From 8cba8d3e92db0694a2ffce14eee6256e87efc441 Mon Sep 17 00:00:00 2001 From: ischindl Date: Tue, 11 Aug 2026 02:10:26 +0200 Subject: [PATCH] fix(core): make TaskStore.emit override assignable to EventEmitter signature (#3407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Dashboard typecheck fails with **TS2416** in `@fusion/core`'s `TaskStore`: ``` Property 'emit' in type 'TaskStore' is not assignable to the same property in base type 'EventEmitter'. ``` The `override emit(event, ...args)` generic conflicts with the base class's generic `emit(eventName: keyof TaskStoreEvents | K, ...)`. This breaks the dashboard typecheck / CI merge gate. ## Fix Change the override to: ```ts override emit(event: unknown, ...args: any[]): boolean { return EventEmitter.prototype.emit.call(this, event as string, ...args); } ``` `event: unknown` remains assignable to the base's generic signature while still forwarding non-typed runtime keys (`agent:log`, `settings:updated`, …). Internal `EventEmitter.prototype.emit` calls cast `event as string`. Behavior-preserving. ## Verification - `@fusion/dashboard` `tsc --noEmit` → **PASS** (previously failed with TS2416) - `eslint` on touched file → clean - Single-file change (`packages/core/src/store.ts`, +6/−3) ## Scope No behavior change, no changesets required. ## Summary by CodeRabbit * **Bug Fixes** * Improved task event handling to support a broader range of event identifiers. * Preserved cached-lane information for single-argument task update events. * Maintained support for custom and arbitrary event names without disrupting existing behavior. * Improved classification of workflow roles, session purposes, and outcome-related status checks in lifecycle analysis, producing more accurate findings and reducing misleading results. --- packages/core/src/store.ts | 9 ++++++--- scripts/lib/lifecycle-column-census-ast.mjs | 2 +- scripts/lib/lifecycle-column-census.mjs | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/core/src/store.ts b/packages/core/src/store.ts index a64061d3b9..2695889e9c 100644 --- a/packages/core/src/store.ts +++ b/packages/core/src/store.ts @@ -2286,13 +2286,16 @@ export class TaskStore extends EventEmitter { cached answer only. Explicit metadata wins; runtime and process bridge EventEmitters do not call this method and deliberately DROP lanes because absent metadata safely preserves their listener fallback. */ - override emit(event: E, ...args: any[]): boolean { + override emit(event: unknown, ...args: any[]): boolean { + // event: unknown keeps the decorator assignable to EventEmitter's + // generic `emit(name: K|E, ...)` signature while still + // forwarding arbitrary non-typed keys (agent:log, settings:updated, …). if (event === "task:updated" && args.length === 1) { const task = args[0] as Task; const lanes = this.laneCache.get(task.id); - if (lanes !== undefined) return EventEmitter.prototype.emit.call(this, event, task, { lanes }); + if (lanes !== undefined) return EventEmitter.prototype.emit.call(this, event as string, task, { lanes }); } - return EventEmitter.prototype.emit.call(this, event, ...args); + return EventEmitter.prototype.emit.call(this, event as string, ...args); } async updateStep( id: string, stepIndex: number, status: import("./types.js").StepStatus, options?: { source?: "graph" }, ): Promise { diff --git a/scripts/lib/lifecycle-column-census-ast.mjs b/scripts/lib/lifecycle-column-census-ast.mjs index 0dd397f794..b704b80841 100644 --- a/scripts/lib/lifecycle-column-census-ast.mjs +++ b/scripts/lib/lifecycle-column-census-ast.mjs @@ -60,7 +60,7 @@ export const LEGACY_COLUMN_IDS = ["triage", "todo", "in-progress", "in-review", /** Receiver names that denote an agent role / lane rather than a task column. */ export const ROLE_RECEIVER_TOKENS = [ - "role", "agentType", "agent", "lane", "capability", "sessionPurpose", "surface", "purpose", "agentRole", + "role", "agentType", "agent", "lane", "capability", "sessionPurpose", "surface", "purpose", "agentRole", "workflowRole", /* FNXC:LifecycleColumnCensus 2026-07-30-22:00 (fleet phase — the work order was sending workers at non-columns): diff --git a/scripts/lib/lifecycle-column-census.mjs b/scripts/lib/lifecycle-column-census.mjs index 27a9e63900..cd34fa1637 100644 --- a/scripts/lib/lifecycle-column-census.mjs +++ b/scripts/lib/lifecycle-column-census.mjs @@ -49,7 +49,7 @@ export const LEGACY_COLUMN_IDS = ["triage", "todo", "in-progress", "in-review", * the two classes separately instead of silently netting them. */ export const ROLE_RECEIVER_TOKENS = [ - "role", "agentType", "agent", "lane", "capability", "sessionPurpose", "surface", "purpose", "agentRole", + "role", "agentType", "agent", "lane", "capability", "sessionPurpose", "surface", "purpose", "agentRole", "workflowRole", ]; /* @@ -161,7 +161,7 @@ export function findComparisons(filePath, source) { const deliberate = hasDeliberateMarker(originalLines, index); const isRole = ROLE_RECEIVER_TOKENS.includes(receiver) || comparedAgainstSiblingValues(strippedLines, index, receiver, ROLE_ONLY_SIBLING_VALUES); - const isStatus = /status/i.test(receiver) + const isStatus = /status|outcome/i.test(receiver) || comparedAgainstSiblingValues(strippedLines, index, receiver, STATUS_ONLY_SIBLING_VALUES); findings.push({ file: filePath,