Resolves the actionable CodeRabbit threads on the workspace-mode foundation:
- project-resolver: defer saveWorkspaceConfig until after the user confirms init
and store.init() succeeds (no partial .fusion/ on a declined/non-interactive run).
- git-repository: validate each candidate with a real `git rev-parse` work-tree
probe before counting it (no false-positive repos from stray .git markers);
loadWorkspaceConfig now rejects absolute paths, `..` escapes, and non-string
entries so a corrupt/malicious config can't resolve outside the workspace root.
- executor: gate workspace mode on repos.length > 0 at all three sites so an
empty { repos: [] } can't bypass the git-repo guard or enable an empty workspace.
- worktree-acquisition: thread the configured-command runner through the workspace
acquire path (sub-repos run their init setup); validate repoRelPath as an in-root
relative path before joining; liveness-check a remembered worktree before
reporting it ready (pruned paths fall through to re-acquire); clear the singular
task.worktree/branch after persisting per-repo state (per-repo state lives only
in workspaceWorktrees).
- agent-tools: forward runContext into acquireWorkspaceRepoWorktree for log attribution.
The executor-workspace test's mock-the-subject pattern is left for the
session-scoping follow-up that rewrites it with a real two-repo fixture (FN-5048).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Applies ce-code-review (autofix) feedback — 5 reviewers, P1s corroborated.
F1 [P1, ×4 reviewers] Guard the merge chokepoint, not just the 4 doors. The
per-caller `getTask().catch(()=>null); if(t) assert` pattern failed open on a
transient read, and runAiMerge re-read the task unguarded — so a workspace
task could reach git work against the non-git root. Added a named
WorkspaceTaskMergeError and call assertNotWorkspaceTaskMerge inside runAiMerge
(the sole merge path) and the deprecated aiMergeTask body; door guards remain
as fast-fail defense-in-depth.
F2 [P1] The dispatch catch treated the guard throw as a merge failure and set
mergeRetries=MAX, permanently blocking manual retry. It now recognizes
WorkspaceTaskMergeError and parks without burning retries.
F3 [P2] Deprecation-warning test asserted toBeLessThanOrEqual(1) — vacuously
true on zero emissions. Now resets the per-project flag and asserts the
warning fires exactly once and not again on a second deterministic merge.
F6 [P2] The once-per-process warning flag suppressed the notice for all other
projects in a multi-project host; now keyed per project (Set by cwd).
F5/F7/F8 [P3] @deprecated propagated to the aiMergeTask barrel re-export; CLI
runTaskMerge guard moved inside the formatted try/catch; FNXC placeholder
timestamps corrected; test .at(-1) -> length index.
Documented as residual (deferred to master-plan U8, not bugs in U0's window):
self-healing auto-finalize + store.mergeTask are additional merge-completing
paths not hardened here — workspace tasks are not end-to-end runnable until
master-plan Phase A, and U8 makes self-healing workspace-aware.
Gate green: typecheck (29 projects), lint, build, test:gate (649+58),
affected tests (206+4).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make runAiMerge (the FN-5633 clean-room AI merge path, already the default)
the sole merge path; soft-deprecate the legacy aiMergeTask pipeline. Phase 0
of the workspace-mode master plan — a standalone merge-consolidation refactor
that lands first so downstream workspace work targets one merge function.
U1 — collapse the dispatch: project-engine.ts now calls runAiMerge
unconditionally; the two direct callers that bypassed the dispatch
(dashboard.ts onMergeImpl in --no-engine mode, task.ts runTaskMerge /
`fn task merge`) now route to runAiMerge too. Export runAiMerge from
@fusion/engine.
U2 — soft-deprecate: aiMergeTask is @deprecated (body retained for a later
deletion pass; shared helpers runAiMerge imports, e.g.
captureSingleCommitLandedMetadata, left intact). merger.mode "deterministic"
is annotated deprecated and made inert (type + field kept — published
@runfusion/fusion surface); the dispatch logs a one-time deprecation warning
and routes to runAiMerge. Changeset added (minor).
U3 — R7 workspace merge-boundary guard: shared @fusion/core predicate
assertNotWorkspaceTaskMerge(task) rejects tasks with populated
workspaceWorktrees at all four merge entry points (dispatch, store.mergeTask,
onMergeImpl, runTaskMerge) with an error naming master-plan U6. Covers the
window until per-repo merge support lands; U6 removes it.
U4 — deterministic-mode blast-radius audit: no production project, CI config,
or seeded setting pins merger.mode "deterministic"; only four engine tests
used it to drive the dispatch to aiMergeTask as a mockable seam — migrated to
mock runAiMerge instead. Other module-level aiMergeTask mocks were dead under
the default "ai" mode or test aiMergeTask directly (body retained), so they
are unaffected.
Also removes an unused acquireWorkspaceRepoWorktree import inherited from the
foundation branch (executor.ts) that was failing lint; master-plan U1 re-adds
it with its per-repo usage.
Merge gate green: lint, typecheck (29 projects), build, test:gate
(649 + 58), plus the migrated (114) and new predicate (4) tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Allow registering a non-git parent directory that contains multiple git
repositories as a single Fusion project. The agent acquires per-repo
worktrees on demand via a new `fn_acquire_repo_worktree` tool as it
discovers it needs to work in each sub-repo.
This commit lays the foundation:
- detectWorkspaceRepos / loadWorkspaceConfig / saveWorkspaceConfig in
@fusion/core (config persisted to .fusion/workspace.json)
- Task.workspaceWorktrees data model + store plumbing (per-repo
worktree/branch map, distinct from the singular task.worktree)
- acquireWorkspaceRepoWorktree wraps acquireTaskWorktree per sub-repo,
clearing the singular worktree/branch fields so each sub-repo gets a
fresh worktree instead of resuming a sibling repo's worktree
- fn_acquire_repo_worktree agent tool + workspace prompt injection
- executor git-repository validation bypassed when a workspace config
is present
- CLI `fn init` detects a non-git dir containing sub-repos and writes a
workspace config
Known gap (intentionally left for design discussion, see PR): the
executor's main worktree-acquisition path still assumes a single git
root and is not yet workspace-aware. End-to-end execution (skipping the
root acquisition, per-repo merge, per-repo session scoping) is a
follow-on once the execution model is agreed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dashboard's discovered-skills catalog was built only from the
disk-scanning package manager, so plugin-contributed skills (e.g.
compound-engineering ce-*) — which the engine materializes for executor
sessions separately — never appeared in the editor. Built-in workflow
nodes that reference them (builtin:compound-engineering) showed
"— select skill —" / unresolved.
- skills-adapter: merge plugin skill contributions into the discovered
list (deduped by bare name) via an optional getPluginSkills thunk;
add shared bareSkillName normalizer.
- wire getPluginSkills into all three server entry points: serve,
daemon, and dashboard (the UI-serving command — verified via live
end-to-end that omitting it left the editor catalog empty).
- node-summary + WorkflowNodeEditor: resolve namespaced skillNames
(compound-engineering:ce-work) against the catalog's two-segment
names (ce-work/SKILL.md) so nodes display and select the right skill.
Verified: dashboard + CLI typecheck, 136 dashboard tests, and a live
dashboard E2E (discovered skills 0→11; Plan node resolves to "ce-plan"
in both the canvas label and the inspector dropdown).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pnpm dev/local dashboard prebuild now rebuilds @fusion/core and @fusion/engine alongside the dashboard UI (was client-only), and startup warns loudly when built dist/ is older than src/. Prevents the FN-6638 class where landed engine fixes silently never run because the process loads stale dist.
Close cached TaskStore handles so CLI extension task-tool tests shut down cleanly.\n\n- Add deterministic cached-store shutdown for the CLI extension and invoke it from extension teardown.\n- Expose the shutdown helper for tests and call it after canonical-project-root task-tool cases.\n- Add a patch changeset for the published @runfusion/fusion package.\n\nFiles changed:\n .changeset/fn-6626-close-cached-stores.md | 5 +++++\n .../cli/src/__tests__/extension-task-tools.test.ts | 8 ++++++++\n packages/cli/src/extension.ts | 19 ++++++++++++++++++-\n 3 files changed, 31 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6626
Fusion-Task-Lineage: 65791502-8992-4037-8ca0-fe21c8283506
Add regression coverage that exercises fn_task_list through the built @fusion/core dist exports.
- Expand core task-list formatting tests from export checks to a runtime-shaped fn_task_list execution path.
- Add a CLI extension regression test that mocks @fusion/core to the built dist barrel before executing fn_task_list.
- Cover broad and filtered task-list truncation paths with dependency rendering to catch missing clampTaskListText exports.
Files changed:
packages/cli/src/__tests__/extension.test.ts | 82 ++++++++++++++-
.../core/src/__tests__/task-list-format.test.ts | 110 ++++++++++++++++++++-
2 files changed, 190 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-6535
Fusion-Task-Lineage: dd405811-5655-406d-92e6-53f0e35ac217
Add a canonical dashboard workflow editor guide and connect it from related docs.
- Add a new Workflow Editor guide covering entry points, canvas anatomy, authoring panels, settings values, templates, AI design, import/export, and mobile behavior.
- Link the guide from the docs index, settings reference, and Workflow IR overview.
- Require the workflow editor guide in the docs README index coverage test.
Files changed:
docs/README.md | 1 +
docs/settings-reference.md | 2 +-
docs/workflow-editor.md | 158 +++++++++++++++++++++
docs/workflow-steps.md | 2 +-
.../cli/src/__tests__/docs-readme-index.test.ts | 1 +
5 files changed, 162 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-6520
Fusion-Task-Lineage: 9a2525a5-6ff8-48de-ae13-a7c7c5844f99
Keep the Agents view focused when starting the selected agent from the dashboard TUI.
- Treat `s` as the selected-agent start command inside the Agents interactive view.
- Preserve `m` as the universal Main/status shortcut and keep the `s` alias outside Agents.
- Add regression coverage for Agents, non-Agents, status, and empty Agents shortcut behavior.
- Add a patch changeset for the published CLI package.
Files changed:
.changeset/fn-6491-tui-agents-start-key.md | 5 +
.../commands/dashboard-tui/__tests__/app.test.tsx | 102 ++++++++++++++++++++-
packages/cli/src/commands/dashboard-tui/app.tsx | 9 +-
3 files changed, 112 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-6491
Fusion-Task-Lineage: 82a9acab-6e4a-400c-80ee-54d051245b57
Rescue the same-day core and CLI flaky quarantines by fixing their fixture seams instead of appeasing timeouts.
- Make the core concurrent-write lock helper release synchronously inside its child process so package load cannot delay the transient lock release.
- Close real CLI TaskStore fixtures before removing temp roots and switch mock cleanup to non-hoisted unmocking.
- Remove both test files from package quarantine excludes and clear the quarantine ledger while documenting the rescue pattern.
Files changed:
docs/testing.md | 2 ++
packages/cli/src/__tests__/extension-task-tools.test.ts | 17 +++++++++++++----
packages/cli/vitest.config.ts | 4 +++-
.../core/src/__tests__/store-concurrent-writes.test.ts | 8 +++++++-
packages/core/vitest.config.ts | 4 +++-
scripts/lib/test-quarantine.json | 13 +------------
6 files changed, 29 insertions(+), 19 deletions(-)
Fusion-Task-Id: FN-6486
Fusion-Task-Lineage: c90358c4-0549-4c68-8d17-2e2a336433b5
Index the upstream ACP MCP forwarding sponsorship doc so the docs README and CLI index test keep it discoverable.
- Add the upstream ACP MCP passthrough and permission forwarding sponsorship doc link under Audit Reports.
- Add the upstream doc to the required docs README index coverage.
- Guard that CLI Printing Press docs remain indexed in Audit Reports only, not duplicated under Plugins.
Files changed:
docs/README.md | 5 +++++
.../cli/src/__tests__/docs-readme-index.test.ts | 21 +++++++++++++++++++++
2 files changed, 26 insertions(+)
Fusion-Task-Id: FN-6479
Fusion-Task-Lineage: f23afeba-a989-4552-8857-fe2984df6081
Add a durable proof-point runbook for validating external plugins against released Fusion CLI builds.
- Document release selection, npm integrity capture, scaffold/build/test/install/enable validation, and pass/fail criteria for external plugin proof points.
- Link the runbook from the docs index so it remains discoverable with other plugin authoring docs.
- Extend the docs README index test to require the new proof-point runbook entry.
Files changed:
docs/README.md | 1 +
docs/plugins/external-proof-point-runbook.md | 211 +++++++++++++++++++++
.../cli/src/__tests__/docs-readme-index.test.ts | 1 +
3 files changed, 213 insertions(+)
Fusion-Task-Id: FN-6438
Fusion-Task-Lineage: 5c768d7e-4465-42ac-9535-f16dc42ab6d0
Remove the CLI extension integration suite's hidden Vitest timeout override while keeping its explicit build-only allowance.
- Replace the file-wide 30s Vitest test and hook timeout override with a scoped comment documenting default caps.
- Derive the extension bundle path from the CLI root in the test file.
- Use a SQLite collision trigger instead of a TaskStore prototype spy and fix the agent delete tool payload.
- Clear the timeout appeasement allowlist now that the CLI test exemption is gone.
Files changed:
.../src/__tests__/extension-integration.test.ts | 25 ++++++++++++++++------
.../lib/test-timeout-appeasement-allowlist.json | 8 +------
2 files changed, 20 insertions(+), 13 deletions(-)
Fusion-Task-Id: FN-6436
Fusion-Task-Lineage: 670f41ea-f98f-400c-aa16-6a1cbb1e9bea
Refresh stored Claude OAuth credentials before reporting dashboard auth status or resolving model auth so users do not need to repeatedly re-login after access-token expiry.
Coalesce concurrent refresh attempts, prevent stale refreshes from overwriting newer logins, and route CLI dashboard/serve/daemon/onboard auth wiring through the shared refresh-capable storage.
Make the published plugin SDK declaration bundle self-contained for external plugins.
- Resolve private @fusion/core specifiers while emitting plugin-sdk declarations.
- Add a regression test covering built plugin-sdk .d.ts artifacts when present.
- Record a patch changeset for the published @runfusion/fusion package.
Files changed:
.changeset/FN-6409-plugin-sdk-dts-inline.md | 5 +++++
packages/cli/src/__tests__/plugin-sdk-export.test.ts | 9 +++++++++
packages/cli/tsup.config.ts | 11 ++++++++++-
3 files changed, 24 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6409
Fusion-Task-Lineage: b7590e39-763d-48ca-9301-5131d0a94b89
Keep published CLI standalone installs from omitting the pi runtime packages.\n\n- Remove pi runtime packages from optional peer declarations so npm and pnpm install them as required dependencies.\n- Add a package manifest regression test for source and prepack-transformed publish manifests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6410-standalone-pi-dependency.md | 5 +++\n packages/cli/package.json | 8 ----\n packages/cli/src/__tests__/package-config.test.ts | 47 +++++++++++++++++++++++\n 3 files changed, 52 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-6410
Fusion-Task-Lineage: 579c8a91-a021-4b36-94d7-e4651ce241cb
Expose the docs README index suite as a focused CLI test lane and document its deterministic invocation.
- Add a package script for running only the docs README index Vitest file.
- Lock the package-config contract around the new script and dashboard quality runner expectations.
- Clarify the CI shard docs to pass Vitest shard flags without a bare separator.
Files changed:
docs/contributing.md | 2 +-
packages/cli/package.json | 1 +
packages/cli/src/__tests__/package-config.test.ts | 37 ++++++++++++++++++-----
3 files changed, 31 insertions(+), 9 deletions(-)
Fusion-Task-Id: FN-6355
Fusion-Task-Lineage: 2a02bc5f-2407-4bad-8aed-afa519826ce7