diff --git a/.changeset/dashboard-column-role-browser-export.md b/.changeset/dashboard-column-role-browser-export.md new file mode 100644 index 0000000000..e434a1dd47 --- /dev/null +++ b/.changeset/dashboard-column-role-browser-export.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix the dashboard build after task undo classification began using shared column-role helpers. +category: fix +dev: Export the browser-safe column-roles core leaf and mirror its alias across Vite and Vitest consumers so broad core aliases cannot swallow the subpath. diff --git a/packages/core/package.json b/packages/core/package.json index 22a4407d20..c6ac3b09e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -29,6 +29,11 @@ "source": "./src/task-delete-attribution.ts", "import": "./dist/task-delete-attribution.js" }, + "./column-roles": { + "types": "./src/column-roles.ts", + "source": "./src/column-roles.ts", + "import": "./dist/column-roles.js" + }, "./detect-content-language": { "types": "./src/detect-content-language.ts", "source": "./src/detect-content-language.ts", diff --git a/packages/dashboard/app/utils/taskRevert.ts b/packages/dashboard/app/utils/taskRevert.ts index 360f8da670..70994616d0 100644 --- a/packages/dashboard/app/utils/taskRevert.ts +++ b/packages/dashboard/app/utils/taskRevert.ts @@ -1,5 +1,5 @@ import type { Task } from "@fusion/core"; -import { isTerminalColumnRole, type ColumnRoleTraitFlags } from "@fusion/core"; +import { isTerminalColumnRole, type ColumnRoleTraitFlags } from "@fusion/core/column-roles"; /** * FNXC:TaskRevert 2026-07-04-00:00: diff --git a/packages/dashboard/vite.config.ts b/packages/dashboard/vite.config.ts index f43737f518..93f816e5c4 100644 --- a/packages/dashboard/vite.config.ts +++ b/packages/dashboard/vite.config.ts @@ -141,6 +141,7 @@ export default defineConfig({ Ordered before the `@fusion/core` entry because Vite matches aliases in order and the broader key would otherwise swallow this subpath — the exact failure this line was added to fix (`"FUSION_CLIENT_HEADER" is not exported by ../core/src/types.ts`). */ "@fusion/core/task-delete-attribution": resolve(__dirname, "../core/src/task-delete-attribution.ts"), + "@fusion/core/column-roles": resolve(__dirname, "../core/src/column-roles.ts"), "@fusion/core/detect-content-language": resolve(__dirname, "../core/src/detect-content-language.ts"), "@fusion/core": resolve(__dirname, "../core/src/types.ts"), "@fusion/dashboard/app/components/TaskCard": resolve(__dirname, "app/components/TaskCard.tsx"), diff --git a/packages/dashboard/vitest.config.ts b/packages/dashboard/vitest.config.ts index bfae4aebc7..10b52dfe28 100644 --- a/packages/dashboard/vitest.config.ts +++ b/packages/dashboard/vitest.config.ts @@ -534,6 +534,7 @@ export default defineConfig({ Keep this exact alias before the broader core alias so Vite does not rewrite the subpath. */ "@fusion/core/task-delete-attribution": resolve(__dirname, "../core/src/task-delete-attribution.ts"), + "@fusion/core/column-roles": resolve(__dirname, "../core/src/column-roles.ts"), "@fusion/core": resolve(__dirname, "../core/src/index.ts"), "@fusion/engine": resolve(__dirname, "../engine/src/index.ts"), "@fusion/plugin-sdk": resolve(__dirname, "../plugin-sdk/src/index.ts"), diff --git a/packages/desktop/vitest.config.ts b/packages/desktop/vitest.config.ts index 59e5201251..68650cb8f2 100644 --- a/packages/desktop/vitest.config.ts +++ b/packages/desktop/vitest.config.ts @@ -11,6 +11,7 @@ const fusionAliases = { transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + "@fusion/core/column-roles": resolve(__dirname, "../core/src/column-roles.ts"), "@fusion/core/task-delete-attribution": resolve(__dirname, "../core/src/task-delete-attribution.ts"), "@fusion/core": resolve(__dirname, "../core/src/index.ts"), "@fusion/dashboard": resolve(__dirname, "../dashboard/src/index.ts"), diff --git a/packages/engine/src/__tests__/workflow-scheduler-parked-columns-live-e2e.pg.test.ts b/packages/engine/src/__tests__/workflow-scheduler-parked-columns-live-e2e.pg.test.ts index 3154ad11da..2193b614ec 100644 --- a/packages/engine/src/__tests__/workflow-scheduler-parked-columns-live-e2e.pg.test.ts +++ b/packages/engine/src/__tests__/workflow-scheduler-parked-columns-live-e2e.pg.test.ts @@ -168,7 +168,7 @@ pgDescribe("scheduler parked-column resolution against a live store", () => { it("REGRESSION — a dependent in a RENAMED hold column IS unblocked when its blocker is deleted", async () => { /* - FNXC:WorkflowResolvedColumns 2026-08-01-02:20 (fleet — the flip this test was written to catch): + FNXC:WorkflowResolvedColumns 2026-07-31-06:35 (fleet — the flip this test was written to catch): This was a CHARACTERIZATION of the inert sync read: `resolveTaskParkedColumnsSync` answered `{ hold: "todo" }` for a board whose hold column is `backlog`, so the reconciliation queried a column that does not exist, found no dependents, and left `blockedBy` pointing at a deleted task. diff --git a/packages/engine/src/scheduler.ts b/packages/engine/src/scheduler.ts index 0c945a6e5d..33f3a277fe 100644 --- a/packages/engine/src/scheduler.ts +++ b/packages/engine/src/scheduler.ts @@ -450,7 +450,7 @@ function mergeParkedColumns( } /* -FNXC:WorkflowResolvedColumns 2026-08-01-01:40 (fleet): +FNXC:WorkflowResolvedColumns 2026-07-31-06:35 (fleet): The ASYNC twin. The sync one below cannot answer for a custom workflow in production, so any guard that can reach this one must. @@ -477,7 +477,7 @@ async function resolveTaskParkedColumns(store: TaskStore, taskId: string): Promi archived, terminal: new Set([complete, archived]), /* - FNXC:WorkflowResolvedColumns 2026-08-01-02:05 (fleet): + FNXC:WorkflowResolvedColumns 2026-07-31-06:35 (fleet): The wake set UNIONS the legacy ids rather than replacing them, and that is load-bearing rather than defensive. Post-U11 the default lineage has no `triage` column, so a RESOLVED answer returns `intake: "todo"` where the old inert path fell back to `"triage"`. Converting without the union @@ -1268,7 +1268,7 @@ export class Scheduler { schedulerLog.warn(`Failed to reset dispatch oscillation state for ${task.id} on unpause: ${error instanceof Error ? error.message : String(error)}`); }); } - /* FNXC:WorkflowResolvedColumns 2026-08-01-01:40 (fleet): the answer only gates `schedule()`, + /* FNXC:WorkflowResolvedColumns 2026-07-31-06:35 (fleet): the answer only gates `schedule()`, which is async and fire-and-forget, so resolving it properly costs nothing observable. */ void (async () => { const unpausedParked = await resolveTaskParkedColumns(this.store, task.id); @@ -1299,7 +1299,7 @@ export class Scheduler { this.planningTaskIds.add(task.id); } else if (this.planningTaskIds.has(task.id)) { this.planningTaskIds.delete(task.id); - /* FNXC:WorkflowResolvedColumns 2026-08-01-01:40 (fleet): as with the unpause wake above, the + /* FNXC:WorkflowResolvedColumns 2026-07-31-06:35 (fleet): as with the unpause wake above, the answer only gates `schedule()`. The `planningTaskIds.delete` stays SYNCHRONOUS — it is the edge-trigger bookkeeping, and deferring it would let a second update re-enter this branch. */ void (async () => { diff --git a/packages/engine/vitest.config.ts b/packages/engine/vitest.config.ts index a4ef94013e..57a0e43146 100644 --- a/packages/engine/vitest.config.ts +++ b/packages/engine/vitest.config.ts @@ -14,6 +14,7 @@ export default defineConfig({ transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + "@fusion/core/column-roles": resolve(__dirname, "../core/src/column-roles.ts"), "@fusion/core/task-delete-attribution": resolve(__dirname, "../core/src/task-delete-attribution.ts"), "@fusion/core": resolve(__dirname, "../core/src/index.ts"), "@fusion/test-utils": resolve(__dirname, "../core/src/__test-utils__/workspace.ts"), diff --git a/plugins/fusion-plugin-cli-printing-press/vitest.config.ts b/plugins/fusion-plugin-cli-printing-press/vitest.config.ts index e9f4487190..23474091a6 100644 --- a/plugins/fusion-plugin-cli-printing-press/vitest.config.ts +++ b/plugins/fusion-plugin-cli-printing-press/vitest.config.ts @@ -32,6 +32,7 @@ export default defineConfig({ transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + "@fusion/core/column-roles": fileURLToPath(new URL("../../packages/core/src/column-roles.ts", import.meta.url)), "@fusion/core/task-delete-attribution": fileURLToPath(new URL("../../packages/core/src/task-delete-attribution.ts", import.meta.url)), "@fusion/core": fileURLToPath(new URL("../../packages/core/src/index.ts", import.meta.url)), "@fusion/plugin-sdk": fileURLToPath(new URL("../../packages/plugin-sdk/src/index.ts", import.meta.url)), diff --git a/plugins/fusion-plugin-compound-engineering/vitest.config.ts b/plugins/fusion-plugin-compound-engineering/vitest.config.ts index 4db8348112..bbb1e7d735 100644 --- a/plugins/fusion-plugin-compound-engineering/vitest.config.ts +++ b/plugins/fusion-plugin-compound-engineering/vitest.config.ts @@ -52,6 +52,7 @@ export default defineConfig({ transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + { find: "@fusion/core/column-roles", replacement: fileURLToPath(new URL("../../packages/core/src/column-roles.ts", import.meta.url)) }, { find: "@fusion/core/task-delete-attribution", replacement: fileURLToPath(new URL("../../packages/core/src/task-delete-attribution.ts", import.meta.url)) }, { find: "@fusion/core", replacement: fileURLToPath(new URL("../../packages/core/src/index.ts", import.meta.url)) }, { diff --git a/plugins/fusion-plugin-dependency-graph/vitest.config.ts b/plugins/fusion-plugin-dependency-graph/vitest.config.ts index c39bbb92ec..e065b896a5 100644 --- a/plugins/fusion-plugin-dependency-graph/vitest.config.ts +++ b/plugins/fusion-plugin-dependency-graph/vitest.config.ts @@ -33,6 +33,7 @@ export default defineConfig({ transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + { find: "@fusion/core/column-roles", replacement: fileURLToPath(new URL("../../packages/core/src/column-roles.ts", import.meta.url)) }, { find: "@fusion/core/task-delete-attribution", replacement: fileURLToPath(new URL("../../packages/core/src/task-delete-attribution.ts", import.meta.url)) }, { find: "@fusion/core", replacement: fileURLToPath(new URL("../../packages/core/src/index.ts", import.meta.url)) }, { diff --git a/plugins/fusion-plugin-reports/vitest.config.ts b/plugins/fusion-plugin-reports/vitest.config.ts index dbfa9b84dc..53fe049395 100644 --- a/plugins/fusion-plugin-reports/vitest.config.ts +++ b/plugins/fusion-plugin-reports/vitest.config.ts @@ -32,6 +32,7 @@ export default defineConfig({ transitively — this project aliases `@fusion/dashboard`, and `app/api/client.ts` imports the browser-safe delete-attribution leaf. */ + "@fusion/core/column-roles": fileURLToPath(new URL("../../packages/core/src/column-roles.ts", import.meta.url)), "@fusion/core/task-delete-attribution": fileURLToPath(new URL("../../packages/core/src/task-delete-attribution.ts", import.meta.url)), "@fusion/core": fileURLToPath(new URL("../../packages/core/src/index.ts", import.meta.url)), // FNXC:Clipboard 2026-07-12-00:00: The reports plugin imports the dashboard clipboard helper through its package subpath export; keep this exact alias ahead of the package root alias so vitest does not collapse the subpath to src/index.ts. diff --git a/scripts/lib/dashboard-browser-safe-core-modules.json b/scripts/lib/dashboard-browser-safe-core-modules.json index e256bf5b07..5fd1bd0e3e 100644 --- a/scripts/lib/dashboard-browser-safe-core-modules.json +++ b/scripts/lib/dashboard-browser-safe-core-modules.json @@ -60,6 +60,11 @@ "reason": "2026-07-26: Delete caller-attribution constants and the pure header resolver are shared by the browser client that stamps `x-fusion-client` and the route that reads it; the module has no imports at all.", "verifiedAt": "2026-07-26" }, + { + "module": "column-roles", + "reason": "2026-07-31: Column role predicates depend only on erased trait types and provide browser-safe flags-first lifecycle classification.", + "verifiedAt": "2026-07-31" + }, { "module": "live-agent-count", "reason": "2026-07-21: FN-8453 shares pure workflow-trait-based Running and Waiting predicates with dashboard capacity indicators; its dependency graph has no Node-only modules.",