Fixes fn_task_archive and fn_task_delete rejecting tasks still referenced as a lineage parent, with no tool-exposed way to clear that reference.
- Add optional removeLineageReferences boolean param to fn_task_archive and fn_task_delete tool schemas, forwarded to store.archiveTask/store.deleteTask
- Update tool descriptions and prompt guidelines to advertise the recovery path (removeLineageReferences:true) when a lineage-parent block occurs
- Add task-lineage-unlink.test.ts covering the new parameter behavior
- Document the change in docs/storage.md
- Add changeset (@runfusion/fusion minor, category: fix)
Files changed:
.changeset/fn-7661-lineage-unlink-tools.md | 7 +
docs/storage.md | 1 +
packages/cli/src/__tests__/task-lineage-unlink.test.ts | 199 +++++++++++++++++++++
packages/cli/src/extension.ts | 28 ++-
4 files changed, 232 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7661
Fusion-Task-Lineage: 414c046c-43df-4995-85a5-ff00b345de50
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Move the host-agnostic bundled-plugin auto-install logic (manifest loading, entry-path
resolution, install/update/enable flow) out of the CLI package into @fusion/core so the
desktop embedded runtime can auto-install bundled runtime plugins without depending on
the CLI package; the CLI module becomes a thin adapter that supplies its own bundle-dir
resolution to the shared helper.
- Add packages/core/src/plugins/bundled-plugin-install.ts with the shared, host-agnostic
ensureBundledPluginInstalled / ensureBundledDependencyGraphPluginInstalled /
ensureBundledCursorRuntimePluginInstalled implementation and BUNDLED_PLUGIN_IDS/
isBundledPluginId/resolvePluginEntryPath, exported from @fusion/core's index.
- Slim packages/cli/src/plugins/bundled-plugin-install.ts to a CLI-specific
candidate-bundle-dir resolver that delegates to @fusion/core and re-exports the same
public surface dashboard.ts/serve.ts/daemon.ts already depend on.
- Remove the now-redundant packages/cli/src/plugins/__tests__/resolve-plugin-entry-path-sync.test.ts
(coverage moved with the implementation to @fusion/core).
- Add packages/desktop/src/bundled-plugin-dirs.ts to resolve each bundled plugin's staged
package directory via import.meta.resolve, mirroring the CLI's dist/plugins/<id> resolver.
- Wire local-runtime.ts and local-server.ts to call ensureBundledPluginInstalled before
loadAllPlugins() and expose a lazy-install callback for PUT /api/plugins/:id/settings,
mirroring the CLI dashboard command's startup auto-install pass.
- Update docs/PLUGIN_AUTHORING.md to describe the shared bundled-plugin-install location.
Files changed:
docs/PLUGIN_AUTHORING.md | 11 +
.../__tests__/bundled-plugin-install.test.ts | 619 ++-------------------
.../resolve-plugin-entry-path-sync.test.ts | 97 ----
packages/cli/src/plugins/bundled-plugin-install.ts | 250 +--------
packages/core/src/index.ts | 8 +
.../__tests__/bundled-plugin-install.test.ts | 391 +++++++++++++
.../core/src/plugins/bundled-plugin-install.ts | 186 +++++++
.../src/__tests__/bundled-plugin-dirs.test.ts | 59 ++
.../desktop/src/__tests__/local-runtime.test.ts | 183 +++++-
.../desktop/src/__tests__/local-server.test.ts | 96 +++-
packages/desktop/src/bundled-plugin-dirs.ts | 61 ++
packages/desktop/src/local-runtime.ts | 66 ++-
packages/desktop/src/local-server.ts | 36 +-
13 files changed, 1171 insertions(+), 892 deletions(-)
Fusion-Task-Id: FN-7637
Fusion-Task-Lineage: 953c5b82-a079-4600-b3af-45c974cd5014
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add a path-containment check to fn_task_attach so it can no longer read files outside the task's worktree via traversal or absolute paths.
- Resolve the requested path and confine it to ctx.cwd (the task worktree) before any readFile call, rejecting "../" traversal, absolute paths, and other boundary-escaping inputs
- Add regression tests in extension.test.ts covering traversal/absolute-path attack vectors
- Add changeset (patch, category: security) documenting the fix
Files changed:
.changeset/fn-7619-attach-boundary.md | 7 ++
packages/cli/src/__tests__/extension.test.ts | 133 ++++++++++++++++++++++++++-
packages/cli/src/extension.ts | 23 ++++-
3 files changed, 161 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7619
Fusion-Task-Lineage: d35d9218-d678-4989-945d-6c1e1a322c5c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Task creation surfaces stopped hardcoding column:"triage", so new tasks now land in the selected-or-default workflow's resolved intake column instead of always jumping to Planning/triage.
- Removed hardcoded column:"triage" override in engine's createTaskCreateTool (fn_task_create), letting TaskStore.createTask resolve the landing column from the workflow's intake-trait column.
- Removed the equivalent hardcoded override in the pi extension's fn_task_create, and updated its response text to echo the actual landing column instead of a fixed "Column: triage" string.
- Fixed signal-route, GitHub-import, and planning-subtask-route task creation to stop forcing column when no workflowId is given (or, for planning subtask routes, even when one is provided).
- Custom workflows with a non-triage intake column (e.g. Inbox) now correctly capture new cards inert until released, while the default builtin:coding workflow still resolves to "triage" byte-identically.
- Added regression coverage (agent-tools-intake-column.test.ts, extension-workflow-tools.test.ts) and a patch changeset documenting the fix.
Files changed:
.changeset/fn-7611-intake-column.md | 7 ++
.../src/__tests__/extension-workflow-tools.test.ts | 70 +++++++++++
packages/cli/src/extension.ts | 10 +-
.../src/__tests__/register-signal-routes.test.ts | 8 +-
.../dashboard/src/__tests__/routes-github.test.ts | 2 -
.../dashboard/src/routes/register-git-github.ts | 16 ++-
.../src/routes/register-planning-subtask-routes.ts | 24 +++-
.../dashboard/src/routes/register-signal-routes.ts | 8 +-
.../__tests__/agent-tools-intake-column.test.ts | 138 +++++++++++++++++++++
packages/engine/src/__tests__/agent-tools.test.ts | 1 -
packages/engine/src/agent-tools.ts | 21 +++-
11 files changed, 288 insertions(+), 17 deletions(-)
Fusion-Task-Id: FN-7611
Fusion-Task-Lineage: daf7f755-b1c7-4859-b74f-f15593d5e79e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes workspace-mode (workspaceWorktrees) tasks failing auto-merge under mergeStrategy=pull-request, where processPullRequestMergeTask threw "could not determine repository" because the workspace root is a container of independent git sub-repos, not itself a git repo.
- Hoist an isWorkspaceTask check in ProjectEngine's merge dispatch (project-engine.ts) before the mergeStrategy branch, so workspace tasks always fall through to the existing direct/landWorkspaceTask path regardless of configured mergeStrategy.
- Add processPullRequestMergeTask and syncGroupPrCallback defense-in-depth guards (task-lifecycle.ts) that throw the new named WorkspaceTaskMergeError if a workspace task ever reaches the PR-merge path.
- Add engine tests covering multi-repo, single-repo, and zero-commit no-op workspace tasks under mergeStrategy=pull-request, plus a non-regression test for the legacy single-worktree PR path.
- Add CLI tests asserting the new guards throw WorkspaceTaskMergeError.
- Add a patch changeset describing the fix.
Files changed:
.changeset/fn-7610-workspace-pr-merge-routing.md | 7 ++
.../src/commands/__tests__/task-lifecycle.test.ts | 56 +++++++++
packages/cli/src/commands/task-lifecycle.ts | 33 ++++-
.../engine/src/__tests__/project-engine.test.ts | 140 +++++++++++++++++++++
packages/engine/src/project-engine.ts | 18 ++-
5 files changed, 252 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7610
Fusion-Task-Lineage: 31768b77-d9a9-4a79-a055-bbc6b228a1c4
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Reads for the anthropic-subscription provider now delegate to the real engine authStorage so expired OAuth tokens are refreshed instead of silently failing.
- mergeAuthStorageReads getApiKey("anthropic-subscription") now calls target.getApiKey(providerId) directly, triggering the engine's refresh-token HTTP round trip, instead of a local static Date.now() >= credential.expires check
- Falls back to the read-only fallback storages' local resolution only when the primary engine call yields no key
- Added regression tests exercising the wrapper directly against the engine delegation and fallback behavior
- Added a patch changeset documenting the fix for @runfusion/fusion
Files changed:
.changeset/fn-7576-cli-wrapper-subscription-getapikey-delegation.md | 7 +
packages/cli/src/commands/__tests__/provider-auth.test.ts | 148 +++++++++++++++++++++
packages/cli/src/commands/provider-auth.ts | 12 +-
3 files changed, 166 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7576
Fusion-Task-Lineage: 31e495ec-8487-468b-9b80-36e8d0f53806
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Restore the stable extension suite by quarantining only the dist-barrel recompilation case.
- Move the built @fusion/core dist-barrel extension test into its own file.
- Re-admit extension.test.ts while keeping the isolated dist-barrel file quarantined.
- Update the quarantine ledger and velocity baseline to reflect the narrowed quarantine.
Files changed:
docs/test-velocity-baseline.md | 10 +-
.../src/__tests__/extension-dist-barrel.test.ts | 230 +++++++++++++++++++++
packages/cli/src/__tests__/extension.test.ts | 103 +--------
packages/cli/vitest.config.ts | 5 +-
scripts/lib/test-quarantine.json | 4 +-
5 files changed, 247 insertions(+), 105 deletions(-)
Fusion-Task-Id: FN-7530
Fusion-Task-Lineage: 7b07540f-689b-4133-b590-a39427095397
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Two failures surfaced in the full-suite run on main (28697507894):
1. dashboard session-reconnect.test.ts — real bug. The planning
"replays buffered events" test hung at the 15s timeout because
FN-7444 (planning summary deepening checkpoint) now holds the
completed summary behind a mandatory checkpoint question instead
of finalizing on the agent's "complete" payload. The stream route
never observed session.summary and subscribed forever. Fix: respond
to the deepening checkpoint with the reserved proceed option so
finalizePendingSummary runs, session.summary is set, and the
summary/complete events are buffered for SSE replay. Reproduced
locally (15s hang) and verified green (4/4).
2. cli extension.test.ts — loaded-lane CI flake. The built-dist-barrel
fn_task_list test timed out at 5000ms under 4-shard contention while
passing locally (~1.2s body) and in 3 of the 4 surrounding runs.
Root cause is in-test dist-barrel recompilation inside the default
5s timeout (vi.resetModules + vi.importActual of the full core dist
+ fresh dynamic import), the same signature rescued in
FN-6483/FN-6705/FN-6795/FN-6839. Quarantined on sight per the
flaky-test rule (ledger + matching vitest exclude) rather than
widening the timeout or loosening assertions; the sibling
source-@fusion/core test covers the identical truncation invariant.
Three root causes behind CI run 28695362549 failures:
1. droid-cli: 8 tests fail with 'Failed to resolve entry for
@fusion-plugin-examples/droid-runtime'. The droid-cli vitest config
had no source alias for the droid-runtime plugin, so Vite tried to
resolve non-existent dist/ exports.
2. CLI: multiple tests fail with 'Failed to resolve entry for
@fusion-plugin-examples/roadmap'. The CLI vitest config was missing
source aliases for the roadmap plugin (., /server, /roadmap-suggestions).
3. CLI: 73 bin.test.ts tests fail with 'No runTaskImportFromGitLab export
is defined on the ../commands/task.js mock'. The GitLab import command
was added to bin.ts and task.ts but the bin.test.ts mock was not
updated to include the new export.
runtime-provider-probes.ts imports probeCursorBinary from
@fusion-plugin-examples/cursor-runtime, but neither the dashboard nor CLI
vitest configs had source aliases for this package. Without the aliases,
Vite tried to resolve the package's dist/ exports which don't exist in a
source checkout, causing every dashboard test that transitively imports
the runtime provider to fail with 'Failed to resolve entry for package'.
This broke 48 dashboard test files in CI run 28561416741 after the cursor
runtime plugin was added without updating the vitest configs.
- Regenerate pnpm-lock.yaml for the new electron@^33.4.11 CLI dependency so
--frozen-lockfile stops failing every PR-check job.
- Gate the GPU/sandbox-disabling Electron flags (--no-sandbox, --disable-gpu,
etc.) to Windows only via os.platform(); applying them on macOS/Linux was a
security and rendering regression. This also resolves the unused os import
that failed lint.
- Add FNXC comments for the Windows GPU flags, desktop user-data isolation, and
CLI dashboard-server reuse per project comment convention.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add reports/desktop-release-issues-2026-07-03.md documenting the
regressions observed in the Fusion 0.52.0 Windows desktop release,
including:
- fusion desktop fails to launch because electron is a devDependency
- native launcher walks ancestor dirs and fails on unrelated workspace JSON
- Manage Projects opens Settings instead of overview
- Windows Terminal Help version dialogs on dashboard load
- packaged preload.cjs missing in unpacked release layout
- port collisions and dashboard/gateway drift on Windows
- GPU/sandbox rendering instability on Windows Electron
- isolated user-data path needed
- CLI desktop command not reusing an already-running dashboard server
Also include the two experimental mitigations we applied locally:
- disable GPU/sandbox Electron flags in desktop.ts
- skip embedded local runtime when FUSION_SERVER_PORT is already set
These changes are intended as supporting evidence and starting points
for the Fusion team, not as a final fix.
## Windows: local runtime hung at "Starting local Fusion runtime…"
### Root cause
Desktop startup had two independent persisted sources of truth that
could disagree:
- `desktop-launch-mode.json` — decides whether **main** *starts* the
embedded local runtime
- `shell-connections.json` (`desktopMode`) — decides whether the
renderer **launch gate** *waits* for it
`shell:setDesktopMode` persists shell settings **before** the fallible
`startLocalRuntimeOnce()` / `saveDesktopLaunchMode()`. So a first
"local" selection whose runtime start threw or was interrupted left
`shell=local` / `launch-mode=choose` **permanently**. Every later launch
then sat at "Starting local Fusion runtime…" polling a runtime nobody
started → 30s timeout.
### Fix (defense in depth) — `78f0bc31`
- `initializeApp` reconciles: a completed shell `local` selection is
authoritative → heals the launch-mode file and starts the runtime.
- `onDesktopModeChange` / `onDesktopLaunchModeChange` persist
launch-mode **before** the fallible start so it can't re-desync.
- `DesktopLaunchGate` no longer assumes main started the runtime — if
it's not running/starting it actively `setDesktopMode("local")` before
polling.
- Env-gated startup trace (`FUSION_STARTUP_TRACE`) so packaged builds
(which log nothing) are diagnosable.
- Regression tests: split-brain → runtime starts + file heals;
agreement-on-choose → no start.
**Verified end-to-end under real Electron 35 / Node 22.16**: from the
exact split-brain state the runtime now reaches `RUNNING` and the
launch-mode file heals.
### Also: Windows root-build breakages — `bd24bd4c8`
- `scripts/build-workspace.mjs` "run as main" guard compared
`import.meta.url` to `` `file://${process.argv[1]}` ``, which never
matches on Windows → root `pnpm build` silently no-opped (exit 0, no
dist). Now uses `pathToFileURL(process.argv[1]).href`.
- `spawn('pnpm', …)` without `shell:true` (ENOENT on Windows) in
`build-workspace.mjs` and `packages/cli/tsup.config.ts` → pass `shell`
on win32.
### Notes
- `@fusion/desktop` and `@fusion/dashboard` are private → no changeset.
- Build the Windows installer via the `desktop-windows` workflow
(`electron-builder --projectDir deploy`); local `pnpm deploy` staging
hits an unrelated directory-rename race on managed-workspace
filesystems.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added optional desktop runtime startup tracing (enabled via
environment variable).
* **Bug Fixes**
* Improved local desktop handoff to prevent reload loops and navigate
directly to the embedded local runtime.
* Added “split-brain” healing between persisted launch mode and shell
settings.
* Prevented auto-registration of runtime root/CWD during
desktop/dashboard startup.
* Improved Windows compatibility for CLI/workspace command spawning and
npm install process handling.
* **Tests**
* Expanded local/Electron integration, navigation, and onboarding
regression coverage; improved async flushing for more reliable
initialization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The desktop embedded runtime auto-registered its runtime root (the user's HOME directory) as a
project on first launch, and bare `fusion` / `fn` / `fn dashboard` / `fusion dashboard`
auto-registered the CWD as a project. Both silently created a "cwd-mode" project the operator
never chose and dropped them onto a board for it.
- Desktop: replace ensureDesktopRuntimeProject (which registered home) with
resolveDesktopRuntimePrimaryProject, which only PICKS an already-registered project as the
primary engine target and registers nothing. With no projects the server starts engine-less
(createServer's engine is optional) and the dashboard shows its onboarding empty state.
Applied to both the primary (local-runtime) and legacy (local-server) desktop server paths.
- CLI dashboard command: ensureCwdProjectRegistered now runs with autoRegister:false, so it uses
the CWD project only if already registered, else starts with none and the dashboard onboards.
(serve/daemon keep their existing --no-auto-register flag; the CLI `desktop` launcher unchanged.)
Verified: with zero projects the embedded server starts, /api/health -> 200, /api/projects -> [],
/ serves the client. Unit test asserts resolveDesktopRuntimePrimaryProject registers nothing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The published @runfusion/fusion package ships the desktop runtime assets
in dist/desktop/, but the [title-id-drift] db.ts migration normalized 0 active titles
[done-paused-backfill] db.ts migration repaired 0 done task rows
[title-id-drift] archive-db normalized 0 archived titles
[desktop] Auto-registered project "Fusion" at C:\Users\drewd\Tools\fusion-latest
[server:terminal] WebSocket server mounted { path: '/api/terminal/ws' }
[ai-session-store] Cleanup removed stale sessions {
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0,
maxAgeMs: 604800000,
operation: 'cleanup-stale-sessions',
_emittedAt: '2026-07-03T03:03:31.683Z',
_diagnosticsId: 'bc57bc09-d320-46b1-8717-a139f95bb9b7'
}
[server] AI session cleanup summary {
message: 'Removed stale AI sessions',
source: 'initial',
ttlMs: 604800000,
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0
} command calls require('electron')
to find the Electron binary. Electron was only declared as a built
dependency in pnpm-workspace.yaml, which makes it available during source
builds but does not include it in the published npm package. As a result,
[title-id-drift] archive-db normalized 0 archived titles
[server:terminal] WebSocket server mounted { path: '/api/terminal/ws' }
[ai-session-store] Cleanup removed stale sessions {
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0,
maxAgeMs: 604800000,
operation: 'cleanup-stale-sessions',
_emittedAt: '2026-07-03T03:03:37.491Z',
_diagnosticsId: 'a0c428a8-133d-4673-ab93-82a092646b13'
}
[server] AI session cleanup summary {
message: 'Removed stale AI sessions',
source: 'initial',
ttlMs: 604800000,
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0
} fails or hangs for npm consumers.
Fix: move electron to the dependencies of @runfusion/fusion so it is
installed alongside the CLI, and remove it from onlyBuiltDependencies.
Fixes: the desktop launcher regression where [title-id-drift] archive-db normalized 0 archived titles
[server:terminal] WebSocket server mounted { path: '/api/terminal/ws' }
[ai-session-store] Cleanup removed stale sessions {
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0,
maxAgeMs: 604800000,
operation: 'cleanup-stale-sessions',
_emittedAt: '2026-07-03T03:03:43.460Z',
_diagnosticsId: '325b9f28-e5d3-4937-855e-c5d1b95420b8'
}
[server] AI session cleanup summary {
message: 'Removed stale AI sessions',
source: 'initial',
ttlMs: 604800000,
terminalDeleted: 0,
orphanedDeleted: 0,
totalDeleted: 0
} cannot
resolve the Electron binary from a normal npm install.
Two Windows-only breakages in the build path:
- scripts/build-workspace.mjs: the "run as main" guard compared import.meta.url
to `file://${process.argv[1]}`, which never matches on Windows (import.meta.url
is file:///C:/… with forward slashes and a triple slash; argv[1] is C:\… with
backslashes and no scheme). So `pnpm build` at the repo root silently no-opped
(exit 0, no output, no dist) and packaging shipped empty/stale dist. Compare
against pathToFileURL(process.argv[1]).href instead.
- scripts/build-workspace.mjs runPlannedBuilds and packages/cli/tsup.config.ts
runWorkspaceCommand spawned `pnpm` without shell:true; on Windows pnpm is a
.cmd shim Node refuses to spawn without a shell (ENOENT/EINVAL, CVE-2024-27980),
failing with `spawn pnpm ENOENT`. Pass shell on win32 (args are fixed repo build
invocations with no shell metacharacters).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prevent task-bound callers from soft-deleting the task they are currently executing.
- Add a TaskSelfDeleteError guard in TaskStore.deleteTask before mutation or audit emission.
- Pass the current task id through the fn_task_delete audit context so CLI tool calls inherit the store invariant.
- Cover self-delete rejection and cross-task deletion allowance in core and CLI tests.
- Add a patch changeset for the published CLI package.
Files changed:
.changeset/fn-7411-self-delete-guard.md | 7 +++
.../task-delete-allow-resurrection.test.ts | 48 ++++++++++++++++-
packages/cli/src/extension.ts | 2 +
.../src/__tests__/store-self-delete-guard.test.ts | 60 ++++++++++++++++++++++
packages/core/src/store.ts | 22 +++++++-
5 files changed, 136 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7411
Fusion-Task-Lineage: 50028769-5396-4435-84fb-2ae182315e81
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Add TaskExecutor.blockOuterDispatchWhenEphemeralDisabled, gating all three
workflow dispatch paths (graph / authoritative / work-engine) on
ephemeralAgentsEnabled at the top of execute(). Previously the toggle was
enforced only on the legacy scheduler/EphemeralWorkerManager path — whose
onTaskStart spawn refusal is a fire-and-forget callback that runs after
execution begins — so unassigned tasks reaching execute() off a non-scheduler
path still ran. Unassigned tasks are now re-queued for permanent-agent
assignment; permanent-agent-bound tasks still run. Adds regression coverage
across all three entry points.
Also includes the ephemeralAgentsCanCreateTasks project setting (default on)
gating fn_task_create for ephemeral callers in both the pi extension and the
executor task-worker tool.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
- keep malformed planning-session responses persisted as retryable error
state instead of deleting the session
- add `fn dashboard --supervise` to restart unexpected dashboard exits
with bounded exponential backoff
- document the supervised-dashboard run mode and add a patch changeset
## Test Plan
- `corepack pnpm --filter @runfusion/fusion exec vitest run
src/commands/__tests__/dashboard.test.ts --silent=passed-only
--reporter=dot`
- `corepack pnpm --filter @fusion/dashboard exec vitest run
src/__tests__/session-error-recovery.test.ts --silent=passed-only
--reporter=dot`
- `corepack pnpm --filter @fusion/dashboard exec vitest run
src/__tests__/server.test.ts -t "returns health OK independently"
--silent=passed-only --reporter=dot`
- `corepack pnpm lint`
- `corepack pnpm typecheck`
- `corepack pnpm build`
- `corepack pnpm test:gate`
Note: a full `server.test.ts` run also exposes four existing
routine-runner expectation failures unrelated to this change; the added
health check passes when run by name.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added dashboard supervised mode via a new `--supervise` option, with
automatic restarts using a bounded restart budget and exponential
backoff.
* **Bug Fixes**
* Planning sessions now persist as retryable error states when AI output
can’t be parsed, preventing session loss.
* Improved reliability so `GET /api/health` remains available during
these planning error states.
* **Documentation**
* Documented “Dashboard Availability &amp; Supervised Mode”,
including health-check guidance and operational guardrails.
* **Tests**
* Added test coverage for supervised restart behavior, health during
planning errors, and ensuring no unhandled rejections on parse failures.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Task image artifacts can now complete the full loop: agents and chat
flows can register base64 image bytes, and task details can display
those artifacts with an expandable preview instead of leaving them as
undiscoverable metadata.
This keeps binary payloads on the existing managed artifact storage
path, validates that base64 payloads are real non-empty image data, and
documents the new `dataBase64` field for engine-tool callers. The task
details artifacts gallery now treats image cards as expandable while
leaving document, audio, video, and generic artifact cards on their
existing behavior.
## Validation
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/TaskDocumentsTab.test.tsx
src/routes/__tests__/artifacts-route-integration.test.ts
--silent=passed-only --reporter=dot`
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/agent-artifact-tools.test.ts --silent=passed-only
--reporter=dot`
---
[](https://github.com/EveryInc/compound-engineering-plugin)

<!-- stage-review-badge-begin -->
---
<a href="https://stagereview.app/Runfusion/Fusion/pull/1821">
<picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
</picture>
</a>
<!-- stage-review-badge-end -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added inline base64 (`dataBase64`) support for registering image
artifacts from agent tools, including task-scoped chat registration.
* Task details now allow expanding image media artifacts in a preview
lightbox with accessible keyboard interaction.
* **Bug Fixes**
* Improved verification for media streaming by validating raw binary
responses; added HTTP 200/404 coverage for image endpoints.
* **Documentation**
* Updated `fn_artifact_register` tool documentation to include the
optional `dataBase64` parameter.
* **Tests**
* Added coverage for base64 validation/error handling and lightbox
expand/close behavior (including Escape and Tab focus trapping).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->