Commit Graph

9540 Commits

Author SHA1 Message Date
gsxdsm
13570e86aa FN-7680: fix Quick Add action row button height parity
Normalizes Quick Add action-row button heights (Save, Attach, Fast, workflow trigger) at desktop and mobile widths so all controls share one box height regardless of icon-only vs text content or .dep-trigger padding.

- Add scoped min-height rule for .quick-entry-actions .btn and .wf-optional-steps-dropdown-trigger in QuickEntryBox.css (desktop base rule, mirrors existing mobile touch-target block)
- Add regression test covering desktop + mobile action-row height parity across button variants
- Add changeset (patch) documenting the fix

Files changed:
 .changeset/FN-7680-quick-add-height-parity.md      |   7 +
 .../quick-entry-action-row-height-parity.test.tsx  | 256 +++++++++++++++++++++
 .../dashboard/app/components/QuickEntryBox.css     |  31 +++
 3 files changed, 294 insertions(+)

Fusion-Task-Id: FN-7680

Fusion-Task-Lineage: 73024883-08f3-41d0-a601-48e223eea57f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 08:59:06 -07:00
gsxdsm
297c7444f5 FN-7676: hide task-card steps breakdown while in Planning column
Task cards previously could show the progress/steps breakdown while still in the Planning (triage) column when a review gate was active; this changes it to only show once a task leaves Planning, matching ListView's behavior.

- TaskCard.showProgressSection now only shows for `in-progress`/`executing` tasks, dropping the special-case `triage` + active-progress-count branch
- Updated FNXC:TaskCardWorkflowProgress comment to document the FN-7676 requirement
- Updated TaskCard tests to cover the new triage-column behavior
- Added changeset for the patch

Files changed:
 .changeset/fn-7676-planning-steps-breakdown.md     |  7 +++
 packages/dashboard/app/components/TaskCard.tsx     |  7 ++-
 .../app/components/__tests__/TaskCard.test.tsx     | 50 ++++++++++++++++++----
 3 files changed, 51 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7676

Fusion-Task-Lineage: 85fcf8f9-1010-4b68-9323-9d8ce03aa66f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 08:20:29 -07:00
gsxdsm
8ee8f15dc6 FN-7675: add agent runtime self-awareness to system prompts
Agents were composing plans (e.g. reboot/wait-and-retry loops) that assumed they could keep acting even after the Fusion platform itself shut down, since prompts never told them they run inside Fusion. This adds a shared, docs-grounded self-awareness preamble prepended to chat, heartbeat, and executor base prompts so agents know their own runtime constraints.

- Added FUSION_RUNTIME_SELF_AWARENESS shared preamble in packages/core/src/agent-prompts.ts, exported via packages/core/src/index.ts
- Prepended the preamble to the chat system prompt (packages/dashboard/src/chat.ts)
- Prepended the preamble to the heartbeat session prompt (packages/engine/src/agent-heartbeat.ts)
- Prepended the preamble to the executor base prompt (packages/engine/src/executor.ts)
- Updated docs/agents.md and CONCEPTS.md to document the new self-awareness/capability-grounding behavior
- Added regression tests across core, dashboard, and engine covering the new prompt content
- Added changeset for @runfusion/fusion (minor, fix category)

Files changed:
 .changeset/fn-7675-agent-runtime-self-awareness.md |  7 ++++
 CONCEPTS.md                                        |  4 +-
 docs/agents.md                                     | 17 ++++++++
 packages/core/src/__tests__/agent-prompts.test.ts  | 41 ++++++++++++++++++++
 packages/core/src/agent-prompts.ts                 | 32 ++++++++++++++-
 packages/core/src/index.ts                         |  1 +
 packages/dashboard/src/__tests__/chat-system-prompt.test.ts | 17 ++++++++
 packages/dashboard/src/chat.ts                     |  6 ++-
 packages/engine/src/__tests__/executor-prompt.test.ts       | 45 ++++++++++++++++++++++
 packages/engine/src/__tests__/heartbeat-session-prompt.test.ts | 35 +++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             | 10 +++--
 packages/engine/src/executor.ts                    |  7 +++-
 12 files changed, 213 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7675

Fusion-Task-Lineage: 126d04a6-2c68-4347-9789-591b274277bf

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 08:16:30 -07:00
gsxdsm
a4fce60b18 FN-7677: fix quick-add workflow dropdown button height mismatch
Aligns the quick-entry workflow-trigger dropdown button height with the neighboring Save/Fast/Subtask buttons and adds a regression test plus changeset.

- QuickEntryBox.css: .quick-entry-workflow-trigger re-asserts .btn-sm's padding: 4px 10px locally so the shared global .dep-trigger padding: 3px 8px no longer shortens it by ~2px
- Add regression test packages/dashboard/app/__tests__/quick-entry-workflow-trigger-height.test.tsx covering the height parity
- Add changeset fn-7677-quick-add-workflow-trigger-height.md (patch, fix)
- Minor doc updates in cli skill fusion references (extension-tools.md, fusion-capabilities.md)

Files changed:
 .changeset/fn-7677-quick-add-workflow-trigger-height.md            |   7 +
 packages/cli/skill/fusion/references/extension-tools.md            |   6 +-
 packages/cli/skill/fusion/references/fusion-capabilities.md        |   4 +-
 packages/dashboard/app/__tests__/quick-entry-workflow-trigger-height.test.tsx | 248 +++++++++++++++++++++
 packages/dashboard/app/components/QuickEntryBox.css                |   5 +
 5 files changed, 266 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7677

Fusion-Task-Lineage: c8cc1b0d-f9c1-4691-a5b4-14ec8ac7e98e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 08:09:53 -07:00
gsxdsm
38d883d83e fix: surface model-lane drift when a workflow's default model changes (#1958)
## Problem

A task's model fields (`modelProvider`/`modelId` and the
planning/validator
equivalents) are snapshotted once at task-creation time from the
workflow's
model-lane default in `workflow_settings`. Nothing re-syncs, flags, or
surfaces drift when that default is later changed.

Concretely: the `builtin:coding` workflow's execution default was
`claude-sonnet-4-6` until it was corrected on 2026-07-05. Every task
created
before that correction stayed permanently, invisibly pinned to the stale
model id — 52 tasks were found silently stuck on it.

## Fix

- `TaskStore.getModelLaneDrift(workflowId, before, after)`
(`packages/core/src/store.ts`):
read-only diff over the three model lanes
(execution/planning/validator).
  For any lane whose provider+modelId actually changed, it lists the
non-terminal (`column` not `archived`/`done`, not soft-deleted) tasks on
  that workflow still pinned to the old value. Never mutates `tasks`.
- Wired into `PATCH /workflows/:id/setting-values`
(`packages/dashboard/src/routes/register-workflow-routes.ts`): captures
a
  `before` snapshot, runs the existing `updateWorkflowSettingValues`
unchanged, then attaches an optional `modelDrift` field to the response
when a lane change orphans existing tasks. Backward compatible — the
field
  is only present when non-empty.
- Operators can act on the surfaced drift via the existing
`POST /tasks/batch-update-models` endpoint; this change intentionally
does
  not auto-rewrite any task (avoids touching tasks mid-execution).

## Testing

- New tests in `packages/core/src/__tests__/workflow-settings.test.ts`
(`TaskStore.getModelLaneDrift`): verifies a task pinned to a changed
lane's
old value is surfaced, a task already on the new value and a
`done`-column
task are excluded, and an unrelated/unchanged lane produces no drift
entry.
- `packages/core`: `npx vitest run
src/__tests__/workflow-settings.test.ts` — 24/24 pass.
- `npx tsc --noEmit` clean in both `packages/core` and
`packages/dashboard`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Workflow setting updates can now return a lane-based model drift
summary (execution, planning, validator) showing task IDs still pinned
to the previous model configuration.
* The PATCH workflow setting response conditionally includes
`modelDrift` when impacted tasks are found.
* **Bug Fixes**
* Drift detection now compares a consistent “before” snapshot with the
updated values to avoid stale pairing.
* “No workflow selection” tasks are handled correctly based on the
default-workflow behavior.
* **Tests**
* Added coverage for lane drift across model changes and null-selection
inclusion rules.
* **Documentation**
  * Added a release note entry for the change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 07:33:26 -07:00
gsxdsm
176222918a fix(dashboard): address model-lane drift review feedback
- getModelLaneDrift now takes an explicit includeNullSelection option; the
  setting-values route passes it when patching the project default workflow so
  no-workflow-selection tasks (which resolve through the default) are counted
  instead of silently dropped (Greptile P1).
- Add updateWorkflowSettingValuesWithPrevious so the drift baseline is captured
  inside the settings write transaction, removing the stale-read race against a
  concurrent patch of the same row (Greptile P2).
- Broaden getModelLaneDrift tests to cover planning and validator lanes and the
  null-selection/default-workflow case (FN-5893 invariant across surfaces).
- Add changeset.

CodeRabbit's effective-values suggestion is intentionally skipped: model-lane
declarations carry no declaration-level default (KTD-7), so effective == raw for
these keys and comparing effective values is a no-op.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 07:26:26 -07:00
gsxdsm
be94f630ea Surface runtime-resolution fallback in dashboard; thread real FallbackReason (#1957)
Closes/relates to Runfusion/Fusion#1956.

## Summary

Surfaces silent runtime-resolution fallback in the dashboard, and
threads the real `FallbackReason` ("not_found" vs "factory_error")
through instead of hardcoding `"not_found"` for every fallback.

## Changes

- `packages/engine/src/runtime-resolution.ts`: `resolvePluginRuntime()`
now returns a tagged miss result (`{ ok: false, reason }`)
distinguishing "not found" from "factory/instantiation error" instead of
collapsing both to `null`. `resolveRuntime()` threads the real reason
through to `logRuntimeFallback(...)` and returns it via
`ResolvedRuntime.fallbackReason`.
- `packages/engine/src/agent-session-helpers.ts`:
`createResolvedAgentSession()` includes `fallbackReason` in the
`session:runtime-resolved` audit event metadata when present.
- `packages/dashboard/src/routes/register-task-workflow-routes.ts`: new
`GET /api/tasks/:id/runtime-fallback` endpoint, returning the most
recent `session:runtime-resolved` event normalized for UI consumption
(`wasConfigured`, `runtimeHint`, `reason`, `showFallbackBadge`).
- `packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts` (new):
polls the endpoint, dedupes toast firing per audit-event-id.
- `packages/dashboard/app/components/RuntimeFallbackBadge.tsx` (new):
renders the badge + fires the toast; wired into `TaskCard.tsx`,
`ActiveAgentsPanel.tsx`, and `AgentsView.tsx` (board and list variants).

## Test plan

- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/runtime-resolution.test.ts` — 24/24 pass (21 pre-existing
+ 3 new, none weakened)
- `pnpm --filter @fusion/dashboard exec vitest run
src/routes/__tests__/register-task-workflow-routes.runtime-fallback.test.ts`
— 5/5 pass
(empty/configured-ok/fallback-with-hint/fallback-blank-hint/stale-superseded
states)
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/RuntimeFallbackBadge.test.tsx` — 8/8 pass (all
data states + mobile breakpoint + toast-fires-once)
- `pnpm --filter @fusion/dashboard run typecheck` and `pnpm --filter
@fusion/engine run typecheck` — both clean


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added runtime-fallback warning badges across task and agent views
(including board and “working on” sections) with automatic toast
notifications.
* Introduced a new backend API to surface the latest runtime-fallback
state for a task.
* Added runtime-fallback status polling and UI messaging to reflect the
most recent state.

* **Bug Fixes**
* Prevented repeated toasts by deduplicating notifications across
polling updates.
* Improved fallback reporting so the UI reflects the latest
runtime-resolved audit event.
* Enhanced diagnostics by distinguishing fallback reasons (e.g., missing
runtime vs factory failure) for clearer user guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-08 07:24:51 -07:00
gsxdsm
ec2aeed2bf fix(dashboard): drop unknown-rule eslint-disable in RuntimeFallbackBadge
The repo eslint config omits react-hooks/exhaustive-deps, so a disable
directive for it is itself a lint error (rule-not-found). Replace with a
plain comment documenting the intentional dep omission.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 07:16:53 -07:00
gsxdsm
fd11280ce3 FN-7673: document closure of single combined-entry engine-graph gate bundle experiment
Narrative: FN-7673 re-attempted the engine-core gate bundle lever with a single combined-entry engine-graph design (all 14 mock-safe roots redirected through a resolveId plugin to one synthetic packages/engine/.gate-bundle/engine.mjs) after FN-7670's 14-separate-root attempt was inconclusive. This update records the negative A/B result and closes the lever.

- Documented that the combined-entry design achieved its structural goal (149 first-party inputs -> 1 output file) and full 335/335 coverage parity
- Recorded a true interleaved A/B (5 warm + 1 cold pair) showing the combined-entry bundle is consistently slower than the @fusion/core-only baseline (warm median +29.1%, import-phase aggregate +74.0%)
- Captured the working theory: funnelling 14 relative-import sites through a resolveId-plugin redirect to one large synthetic export-* file adds more transform/resolution overhead than it saves, unlike @fusion/core's plain resolve.alias
- Noted the experiment was NOT landed; wiring (engine-graph scans, combined-entry builder, resolveId plugin) was fully reverted
- Marked this lever (bundling the @fusion/engine relative-import graph for the engine-core gate, in either 14-file or single-combined-entry shape) as CLOSED absent new evidence

Files changed:
 packages/engine/vitest.config.ts | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7673

Fusion-Task-Lineage: 46951e5f-e7dc-4f7c-9601-0cfa0b082d70

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 06:35:22 -07:00
gsxdsm
b1b4735111 FN-7671: remove stale merger-post-merge entry from engine-core gate include
Removes a dead test-file reference from the engine-core vitest gate include list, with a code comment documenting why.

- Remove the nonexistent `src/__tests__/merger-post-merge.test.ts` entry from packages/engine/vitest.config.ts's engine-core include list (retired by FN-7039; graph is now sole post-merge owner)
- Add FNXC comment noting the entry matched zero files and that graph post-merge coverage lives in workflow-graph-post-merge.test.ts (engine-default)

Files changed:
 packages/engine/vitest.config.ts | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7671

Fusion-Task-Lineage: 73447412-7b8a-4578-a2b8-07f83e381548

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 05:56:12 -07:00
gsxdsm
aa534c19af FN-7672: recover durable agents stuck in error state despite active manager
Root-causes 4 correlated CTO-report agent failures where durable non-ephemeral agents got stuck in `error` state indefinitely because the heartbeat scheduler stops ticking error-state agents entirely, and self-healing's recovery sweep previously only considered them when their manager row was missing.

- SelfHealingManager: scope the `managerMissing` gate to the "running" orphan-detection path only, so "error"-state durable agents with a present/active manager now fall through to the existing transient/operator-actionable/active-execution/cooldown/retry-budget recovery guards instead of being skipped outright
- Add FNXC:AgentHeartbeat comment documenting the FN-7672 incident and rationale for the scoping change
- Extend self-healing.test.ts with coverage for manager-present durable agents in error state
- Add changeset (patch) describing the fix for release notes
- Update docs/agents.md accordingly

Files changed:
 .changeset/fn-7672-durable-agent-recovery.md       |   7 ++
 docs/agents.md                                     |   2 +
 packages/engine/src/__tests__/self-healing.test.ts | 129 ++++++++++++++++++++-
 packages/engine/src/self-healing.ts                |  24 +++-
 4 files changed, 160 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7672

Fusion-Task-Lineage: 6676dc9e-66e7-4f70-804a-cccf77e8d337

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 05:38:01 -07:00
gsxdsm
b8fa2a6652 FN-7670: document negative result of extending engine-core pre-bundle to @fusion/engine relative-import graph
Prototyped extending the @fusion/core pre-bundle alias lever to @fusion/engine's relative-import production graph reached by the 18 gate files, but an A/B showed no clear win over the @fusion/core-only bundle, so the change was not landed and only the rationale is recorded.

- Added an FNXC:EngineTests comment block in packages/engine/vitest.config.ts documenting the FN-7670 prototype (171 first-party files → 35 output files via esbuild multi-entry splitting)
- Recorded the negative A/B result: byte-size growth of 14 separate large root bundles offset per-file-dispatch savings, with no clear win beyond host run-to-run noise
- Left the vitest alias wiring unchanged at the @fusion/core-only bundle state, pointing future attempts to FN-7670's task docs for full analysis and to consider a single combined engine-graph entry instead of 14 separate root entries

Files changed:
 packages/engine/vitest.config.ts | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Fusion-Task-Id: FN-7670

Fusion-Task-Lineage: efd27f94-a6c4-49c7-a78e-50213fd42a24

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 05:20:47 -07:00
gsxdsm
ad9a72176c FN-7669: pre-bundle @fusion/core gate-safe barrel to cut engine-core gate import-phase cost
Prototype and land a rebuilt-every-run esbuild bundle of the @fusion/core gate-safe barrel closure, collapsing the engine-core gate's per-fork Vite SSR import-phase cost (18 forks x ~430-file closure re-resolved from scratch) into a single file load per fork.

- Add scripts/build-engine-core-gate-bundle.mjs: esbuild-bundles packages/core/src/index.gate.ts (220 first-party files, packages:"external" so third-party/node: imports stay external, treeShaking:false to preserve side effects) into packages/core/.gate-bundle/core.mjs + core.meta.json
- Wire the builder into packages/engine/vitest.config.ts's engine-core project globalSetup (alongside the existing vitest-teardown hook) so the bundle is rebuilt fresh before every gate invocation, and repoint the @fusion/core resolve.alias at the bundled output instead of index.gate.ts source
- Place the bundle output at packages/core/.gate-bundle/ as a sibling of packages/core/node_modules/ (not nested inside it) to avoid Vite SSR's external-dep heuristic, which would otherwise silently defeat vi.mock interception for imports nested in the bundle
- Gitignore packages/core/.gate-bundle/ and add a matching ESLint ignore entry so the generated bundle text is never linted or committed
- Add esbuild ^0.25.12 as a root devDependency (pnpm-lock.yaml updated accordingly)
- Document the pre-bundling rationale, placement constraints, and measured A/B wall-time results in docs/testing.md

Verified: pnpm test:gate passes (335/335 engine-core tests, 63/63 CLI ci-shape tests), engine package typecheck clean, eslint clean on touched files.

Files changed:
 .gitignore                                |  11 ++
 docs/testing.md                           |   3 +
 eslint.config.mjs                         |  10 ++
 package.json                              |   1 +
 packages/engine/vitest.config.ts          |  50 ++++++++-
 pnpm-lock.yaml                            |   3 +
 scripts/build-engine-core-gate-bundle.mjs | 174 ++++++++++++++++++++++++++++++
 7 files changed, 247 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7669

Fusion-Task-Lineage: 62b06b2a-4ac6-45ae-ac79-9771132bc303

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 04:24:30 -07:00
gsxdsm
6902077972 FN-7667: add gate-scoped @fusion/core barrel to decouple engine-core gate from full barrel growth
Introduces a project-scoped @fusion/core barrel used only by the engine-core
gate project, so new feature modules added to the full barrel don't silently
inflate the gate's transform/import cost.

- Add packages/core/src/index.gate.ts, a copy of the full @fusion/core barrel
  minus export statements for modules added since the last re-audit baseline
  (i.e. it still re-exports everything the full barrel does except newly
  added, gate-irrelevant feature modules).
- Update packages/engine/vitest.config.ts to add a project-scoped
  resolve.alias mapping @fusion/core -> packages/core/src/index.gate.ts for
  the engine-core project only; engine-default/engine-reliability/engine-slow
  and @fusion/engine continue to resolve the full barrel.
- Document the gate-safe barrel and its audit procedure in docs/testing.md.

Files changed:
 docs/testing.md                  |    3 +
 packages/core/src/index.gate.ts  | 2102 ++++++++++++++++++++++++++++++++++++++
 packages/engine/vitest.config.ts |   17 +
 3 files changed, 2122 insertions(+)

Fusion-Task-Id: FN-7667

Fusion-Task-Lineage: 054ec89a-d973-44dd-b9ac-ad266f553f01

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 03:28:37 -07:00
gsxdsm
03161adfb9 FN-7659: paginate and sort the Archived column newest-first
Adds server-side pagination for the Archived task column, sorted by most-recently-archived first, with a Show more control on the dashboard.

- Add ArchiveDatabase.listPage and TaskStore.listArchivedTasks for a bounded SQL LIMIT/OFFSET read ordered by archivedAt DESC
- Add GET /tasks/archived route for paged archive fetches, leaving the legacy merged listTasks({includeArchived}) path unchanged
- Wire useTasks.loadArchivedTasks to fetch page 1 on first Archived-column expand and loadMoreArchivedTasks for subsequent pages
- Add a "Show more" affordance in Column.tsx/Board.tsx/MainContent.tsx to trigger loading additional archived pages
- Extend taskSorting.ts to keep archived task ordering stable with the new paged data
- Add core and dashboard tests covering archive pagination and store/route behavior
- Add changeset for @runfusion/fusion (minor) and update docs/storage.md and docs/dashboard-guide.md

Files changed:
 .changeset/FN-7659-archived-pagination.md          |   7 +
 docs/dashboard-guide.md                            |   4 +-
 docs/storage.md                                    |   7 +
 packages/core/src/__tests__/archive-db-pagination.test.ts    |  94 ++++++++
 packages/core/src/__tests__/store-archive-search.test.ts     |  63 ++++++
 packages/core/src/archive-db.ts                    |  18 ++
 packages/core/src/store.ts                         |  32 +++
 packages/dashboard/app/App.tsx                     |   5 +-
 packages/dashboard/app/api/legacy.ts               |  19 ++
 packages/dashboard/app/components/Board.tsx        |  19 +-
 packages/dashboard/app/components/Column.tsx       |  48 ++++-
 packages/dashboard/app/components/__tests__/Column.test.tsx       |  41 ++++
 packages/dashboard/app/components/__tests__/taskSorting.test.ts   |  27 +++
 packages/dashboard/app/components/dashboard/MainContent.tsx       |   9 +
 packages/dashboard/app/components/dashboard/types.ts    |   6 +
 packages/dashboard/app/components/taskSorting.ts   |  16 ++
 packages/dashboard/app/hooks/__tests__/useTasks.test.ts | 236 ++++++++++++++++++++-
 packages/dashboard/app/hooks/useTasks.ts           | 173 ++++++++++++++-
 packages/dashboard/app/test/mockApi.ts             |   3 +
 packages/dashboard/src/routes/__tests__/tasks-archived-pagination.test.ts |  94 ++++++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts  |  32 +++
 21 files changed, 930 insertions(+), 23 deletions(-)

Fusion-Task-Id: FN-7659

Fusion-Task-Lineage: 7a5a1f62-277c-4f29-883c-62e75b269bc5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 01:29:56 -07:00
gsxdsm
7cf70b3c59 FN-7665: rename Command Center SDLC funnel intake stage label to Planning
Rename the Command Center SDLC funnel's intake stage display text from "Triage" to "Planning" to match the renamed board column, updating English locale copy and tests; the underlying aggregator/i18n key names stay "triage".

- SdlcFunnel.tsx: stage label fallback, enteredInRange, and completionRateAria copy changed to Planning/Entered planning/in-range planning entrants; added FNXC comment explaining the key-vs-label distinction
- packages/i18n/locales/en/app.json: synced English strings for the same three keys
- SdlcFunnel.test.tsx: updated assertions to match new Planning copy
- Added changeset (patch) documenting the label rename

Files changed:
 .changeset/fn-7665-funnel-planning-label.md              |  7 +++++++
 .../app/components/command-center/SdlcFunnel.tsx         | 16 ++++++++++++----
 .../command-center/__tests__/SdlcFunnel.test.tsx         |  6 +++---
 packages/i18n/locales/en/app.json                        |  6 +++---
 4 files changed, 25 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-7665

Fusion-Task-Lineage: d8aec609-6b8b-4f36-bb43-61d7c7c421f9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:50:11 -07:00
gsxdsm
d2e38ba920 FN-7663: consolidate /automations and /routines run-stream SSE handlers into a shared factory
Removes duplicated SSE streaming logic between the /automations/:id/run/stream and /routines/:id/run/stream endpoints by extracting a single generic makeRunStreamHandler factory.

- Add makeRunStreamHandler<TStore, TEntity> factory in routes.ts, parameterized by store resolver, entity getter, and not-found message
- Replace the two near-identical inline SSE handlers (connect headers, buffered replay, run subscription, auto-attach, teardown, error handling) with calls to the shared factory
- Import ScopeValue type from ./routes/types.js to type the factory's scope parameter
- Add regression tests in routes-automation.test.ts covering the consolidated handler behavior for both routes

Files changed:
 .../src/__tests__/routes-automation.test.ts        |  35 +++
 packages/dashboard/src/routes.ts                   | 246 +++++++++------------
 2 files changed, 137 insertions(+), 144 deletions(-)

Fusion-Task-Id: FN-7663

Fusion-Task-Lineage: 55ede468-c4ed-4a72-a428-7d19686e8ea5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:41:58 -07:00
gsxdsm
dd9fa2d3cb FN-7661: expose removeLineageReferences on fn_task_archive/fn_task_delete
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>
2026-07-08 00:33:02 -07:00
gsxdsm
97e46674f4 FN-7660: rename remaining board-column "Triage" labels to "Planning"
Rename remaining "Triage" board-column label references to "Planning" across dashboard editors, locale strings, and docs.

- Fixed BUILTIN_WORKFLOW_COLUMN_LABELS.triage in board-workflows.ts (was still "Triage", clobbering FN-7599's IR rename); column id "triage" stays unchanged
- Updated dashboard components (AgentDetailView, RoutineEditor, ScheduleForm, ScheduleStepsEditor) to use the "Planning" label
- Updated en/app.json locale strings (board.triage, schedule.columnTriage/taskColumnTriage/triageColumn) to "Planning"
- Updated board-workflows tests to assert the new label
- Updated docs/dashboard-guide.md Triage/Planning column references to "Planning", preserving the FN-7653 intake-only gating correction
- Added a patch changeset documenting the label-consistency fix

Files changed:
 .changeset/fn-7660-planning-label-consistency.md               |  7 +++++++
 docs/dashboard-guide.md                                        |  9 +++++----
 packages/dashboard/app/components/AgentDetailView.tsx          |  2 +-
 packages/dashboard/app/components/RoutineEditor.tsx            |  2 +-
 packages/dashboard/app/components/ScheduleForm.tsx             |  2 +-
 packages/dashboard/app/components/ScheduleStepsEditor.tsx      |  2 +-
 .../dashboard/src/routes/__tests__/board-workflows.test.ts     |  7 ++++++-
 packages/dashboard/src/routes/board-workflows.ts               | 10 +++++++++-
 packages/i18n/locales/en/app.json                              |  8 ++++----
 9 files changed, 35 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7660

Fusion-Task-Lineage: fc6c94d6-9876-4c0e-80b1-a5518903952e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:27:52 -07:00
gsxdsm
f7d9509294 FN-7658: gate same-agent duplicate auto-archiving behind opt-in setting
Duplicate tasks created by the same agent are no longer auto-archived by default; they are flagged for review instead, controlled by a new opt-in project setting.

- Add project setting `autoArchiveDuplicateTasksEnabled` (default false) gating the FN-4892 same-agent duplicate intake path
- Add `flagSameAgentDuplicate` path and `nearDuplicateOf` metadata used when auto-archive is disabled; tombstone-resurrection blocking is unchanged
- Wire the setting through core settings schema/types/store, dashboard SchedulingSection UI, and i18n strings
- Update docs (settings-reference.md, task-management.md) to describe the new default-off behavior
- Add a changeset for the @runfusion/fusion minor release
- Extend duplicate-intake, tombstone-window, store-parent-task-dedup, and reliability-interaction tests to cover both flag states

Files changed:
$(cat /tmp/fn7658_stat.txt)

Fusion-Task-Id: FN-7658

Fusion-Task-Lineage: 7d0d1074-1020-48a8-b96f-186154c2c408

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:23:32 -07:00
gsxdsm
511bcaf56d FN-7657: persist GitHub issue import modal state across close/reopen
Retains the GitHub Import Tasks modal's provider/tab/filter/selection state so returning to Import Tasks doesn't reset the user's in-progress import setup.

- GitHubImportModal now persists provider, active tab, label filter, remote, and issue selection per project via a new modalPersistence hook, restoring them on remount instead of always defaulting.
- Added packages/dashboard/app/hooks/modalPersistence.ts to encapsulate the persisted-state read/write logic backed by projectStorage.
- projectStorage.ts gains the `kb-dashboard-github-import-state` storage key.
- Falls back to the existing default-remote auto-detect behavior when no persisted state exists.
- Added extensive test coverage in GitHubImportModal.test.tsx for the new persistence behavior.
- Updated docs/dashboard-guide.md to describe the retained state.
- Added a patch changeset for @runfusion/fusion.

Files changed:
 .changeset/fn-7657-github-import-state-retained.md |   7 +
 docs/dashboard-guide.md                            |   2 +
 .../dashboard/app/components/GitHubImportModal.tsx | 204 +++++++++++++--
 .../__tests__/GitHubImportModal.test.tsx           | 282 +++++++++++++++++++++
 packages/dashboard/app/hooks/modalPersistence.ts   |  72 ++++++
 packages/dashboard/app/utils/projectStorage.ts     |   1 +
 6 files changed, 547 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-7657
Fusion-Task-Lineage: c8086340-368c-4efd-a12a-4cddeeb0aa26
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:14:52 -07:00
fusion-merge-train
83a9cf15d0 fix: surface model-lane drift when a workflow's default model changes
Task model fields (execution/planning/validator provider+modelId) are
snapshotted once at task-creation time from the workflow's model-lane
default and never re-synced. Changing a workflow's default (e.g. fixing
a stale model id) silently leaves already-created tasks pinned to the
old value with no visibility — this is exactly how 52 tasks stayed
pinned to a stale claude-sonnet-4-6 default after it was corrected.

Add TaskStore.getModelLaneDrift(workflowId, before, after), a read-only
diff over the three model lanes that lists non-terminal tasks still
pinned to a lane's old value. Wire it into
PATCH /workflows/:id/setting-values so the response includes
`modelDrift` whenever a lane change orphans existing tasks. Operators
can then act via the existing POST /tasks/batch-update-models.
2026-07-08 09:10:49 +02:00
Fusion
0bed997af8 feat: surface runtime-resolution fallback in dashboard, thread real FallbackReason
Fixes silent runtime fallback visibility (dashboard never read wasConfigured
or session:runtime-resolved) and threads the real FallbackReason
(not_found vs factory_error) through resolveRuntime()/logRuntimeFallback
instead of hardcoding "not_found" for every fallback.

- packages/engine/src/runtime-resolution.ts: resolvePluginRuntime() now
  returns a tagged miss result distinguishing not_found from factory_error;
  resolveRuntime() threads the real reason through and returns it as
  ResolvedRuntime.fallbackReason
- packages/engine/src/agent-session-helpers.ts: includes fallbackReason in
  the session:runtime-resolved audit event metadata
- packages/dashboard/src/routes/register-task-workflow-routes.ts: new
  GET /api/tasks/:id/runtime-fallback endpoint
- packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts +
  packages/dashboard/app/components/RuntimeFallbackBadge.tsx: new polling
  hook + badge/toast component wired into TaskCard, ActiveAgentsPanel, and
  AgentsView

Ref: Fusion task FUX-022, investigations/FUX-017-hermes-runtime-fallback.md
recommendation #1
2026-07-08 03:09:29 -04:00
gsxdsm
e29fea38e0 FN-7656: restore chat working indicator when reattaching to an active generation
Fixes chat sessions not showing the working/"Thinking…" indicator when returning to a session whose generation was already in flight but hadn't emitted its first delta yet.

- useChat.ts: selectSession's authoritative fetchChatSession refresh now reattaches whenever refreshedSession.isGenerating===true, instead of also requiring a populated inFlightGeneration snapshot (which is null pre-first-delta)
- Added a guard so the reattach only proceeds if the user hasn't navigated away from the session while the refresh was in flight (activeSessionRef.current?.id === id)
- Calls attachIfGenerating(id, refreshedSession.inFlightGeneration, { silent: true }) when no stream is already attached, reusing existing double-attach guarding
- Added regression tests in useChat.test.ts covering the reattach-on-isGenerating-alone behavior and the stale-session navigation guard
- Added a patch changeset documenting the fix

Files changed:
 .changeset/fn-7656-chat-reattach-working-state.md  |   7 ++
 .../dashboard/app/hooks/__tests__/useChat.test.ts  | 113 +++++++++++++++++++++
 packages/dashboard/app/hooks/useChat.ts            |  22 +++-
 3 files changed, 141 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7656

Fusion-Task-Lineage: c923c16a-391f-4475-aab8-6194bbc675f7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:05:29 -07:00
gsxdsm
ebe9b9fa67 FN-7653: restrict plan auto-approve toggle to the intake/planning column
Fix the Board Auto-approve plan shortcut leaking onto hold (Todo-like) columns; it must render only on the intake/planning column.

- Board.tsx: gate the planAutoApproveEnabled/onTogglePlanAutoApprove prop pair on columnDef.flags.intake only (dropped the || columnDef.flags.hold clause) for both the aggregate and single-workflow column renders
- Column.tsx: update the FNXC comment to document that Board.tsx is the single source of truth for this intake-only gating; Column.tsx just renders whatever prop it receives
- docs/dashboard-guide.md: correct the Board guide to describe the toggle as intake/planning-column-only, not hold columns
- Board.test.tsx: fix the existing intake/hold test expectation to assert the hold column does NOT get the toggle, and add a regression test reproducing the built-in Coding workflow's Todo (hold) column symptom
- add a patch changeset documenting the fix

Files changed:
 .changeset/FN-7653-plan-auto-approve-intake-only.md  |  7 +++++++
 docs/dashboard-guide.md                              |  5 +++--
 packages/dashboard/app/components/Board.tsx          |  6 ++++--
 packages/dashboard/app/components/Column.tsx         |  5 ++++-
 .../app/components/__tests__/Board.test.tsx          | 20 +++++++++++++++++---
 5 files changed, 35 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7653

Fusion-Task-Lineage: 10c5f01a-b00c-4039-9b83-a8a8965fc56c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 23:55:50 -07:00
gsxdsm
7c7b22e511 FN-7652: fix live-run status falsely showing Run failed for successful automations
Reconcile the automation live-run panel's terminal status with the authoritative run result so successful runs no longer flash "Run failed".

- ScheduledTasksModal/RoutineCard now reconcile SSE terminal status against the POST/registry result instead of trusting raw stream teardown.
- Gate benign SSE teardown (post-terminal close, reconnect exhaustion) from being surfaced as a failure state.
- Apply the same reconciliation to both /routines/:id/run/stream and /automations/:id/run/stream routes in routes.ts.
- Add regression coverage in RoutineCard, ScheduledTasksModal, and routes-automation tests.
- Add a patch changeset documenting the fix.

Files changed:
 .changeset/fn-7652-automation-live-run-false-failure.md           |  7 ++
 packages/dashboard/app/components/ScheduledTasksModal.tsx         | 66 ++++++++++++++--
 packages/dashboard/app/components/__tests__/RoutineCard.test.tsx  | 37 +++++++++
 packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx | 78 +++++++++++++++++++
 packages/dashboard/src/__tests__/routes-automation.test.ts        | 87 ++++++++++++++++++++++
 packages/dashboard/src/routes.ts                                  | 52 ++++++++++++-
 6 files changed, 319 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7652
Fusion-Task-Lineage: 61fc89f5-d25d-44de-9699-bc8ad2a0dea6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 23:35:05 -07:00
gsxdsm
dfb084c59e FN-7655: make planner chat stop button icon-only
Removes the visible "Stop generation" text label from the planner chat's stop button while keeping it accessible via aria-label.

- Add showStopText prop to StandardChatActionButton (defaults to showSendText) to independently control Send vs Stop visible text
- Set showStopText={false} in TaskPlannerChatTab so the streaming stop button renders icon-only
- Update TaskPlannerChatTab test to assert no visible text span on the stop button while aria-label is retained
- Add changeset for @runfusion/fusion (patch)

Files changed:
 .changeset/fn-7655-planner-stop-icon-only.md             |  7 +++++++
 .../dashboard/app/components/StandardChatSurface.tsx     | 16 ++++++++++++++--
 packages/dashboard/app/components/TaskPlannerChatTab.tsx |  3 +++
 .../app/components/__tests__/TaskPlannerChatTab.test.tsx |  4 +++-
 4 files changed, 27 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7655
Fusion-Task-Lineage: f68a8bfa-30ba-439e-97d0-28654a614c54
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 23:26:55 -07:00
gsxdsm
efabdd6f04 FN-7651: remove chat "Search in title only" toggle
Simplifies chat search UX by always matching both title and message content, removing the now-unneeded title-only toggle button and state.

- Removed the "Search in title only" toggle button and its CSS from ChatView
- Dropped searchInTitleOnly/setSearchInTitleOnly state and logic from useChat; content-search query params are now always-on
- Updated ChatView tests to drop title-only toggle interactions and assertions
- Removed the title-only-search i18n string across all locales
- Updated dashboard-guide.md docs to reflect the simplified search behavior
- Added a patch changeset documenting the removal

Files changed:
 .../fn-7651-remove-chat-title-only-toggle.md       |  7 +++
 docs/dashboard-guide.md                            |  4 +-
 packages/dashboard/app/components/ChatView.css     | 18 --------
 packages/dashboard/app/components/ChatView.tsx     | 26 +++--------
 .../__tests__/ChatView.autosize.test.tsx           |  2 -
 .../__tests__/ChatView.content-search.test.tsx     | 51 ++++++----------------
 .../components/__tests__/ChatView.draft.test.tsx   |  2 -
 .../__tests__/ChatView.hash-mention.test.tsx       |  2 -
 .../__tests__/ChatView.mobile-render.test.tsx      |  2 -
 .../components/__tests__/ChatView.rooms.test.tsx   |  2 -
 .../__tests__/ChatView.scroll-to-top.test.tsx      |  2 -
 .../components/__tests__/ChatView.test-harness.tsx |  2 -
 packages/dashboard/app/hooks/useChat.ts            | 39 ++++++++---------
 packages/i18n/locales/en/app.json                  |  1 -
 packages/i18n/locales/es/app.json                  |  1 -
 packages/i18n/locales/zh-CN/app.json               |  1 -
 packages/i18n/locales/zh-TW/app.json               |  1 -
 packages/i18n/locales/ko/app.json                  |  1 -
 packages/i18n/locales/fr/app.json                  |  1 -
 19 files changed, 47 insertions(+), 118 deletions(-)

Fusion-Task-Id: FN-7651

Fusion-Task-Lineage: 5a31825c-8e6e-441f-800d-6053f5f844ba

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 23:15:53 -07:00
gsxdsm
ac719d1203 fix: add usage_events to operational-log retention
usage_events was absent from Database.pruneOperationalLogs, so the
per-tool telemetry log grew unbounded (~187k rows / ~28MB observed) and
became a dominant driver of .fusion DB bloat once runAuditEvents was
already 30-day capped. Prune it on the same operationalLogRetentionDays
cadence, keyed off its `ts` column (not `timestamp`), alongside the other
column-name exceptions. Adds a regression test and changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 22:58:11 -07:00
gsxdsm
ce4f173d8f FN-7649: resolve settings landing view to board on project switch
Fixes project-switch hydration so a persisted "settings" view resolves to the Board instead of re-opening Settings.

- Extend resolveLandingTaskView() in useViewState.ts to treat "settings" the same as "command-center", resolving both to "board" for the auto-restored/hydrated landing view only
- Add regression tests covering the settings->board landing resolution in useViewState.test.ts
- Add changeset documenting the patch-level fix

Files changed:
 .changeset/fn-7649-project-switch-board-landing.md |  7 ++
 .../app/hooks/__tests__/useViewState.test.ts       | 74 ++++++++++++++++++++++
 packages/dashboard/app/hooks/useViewState.ts       |  5 +-
 3 files changed, 85 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7649

Fusion-Task-Lineage: 7179efd6-bb1b-4ea4-a062-479b9b1fffa3

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:23 -07:00
gsxdsm
60bfb66c8c test(FN-5048): replace fixed WS badge subscription sleeps with deterministic event waits
Follow-through on FN-5048: the four residual fixed 100ms sleeps in the
websocket badge integration tests (3 subscription-establishment waits + 1
cross-instance pub/sub propagation wait) are replaced with deterministic
awaits on the server-side WebSocketManager subscription:changed event and the
shared pub/sub message event. Removes ~400ms of unconditional real-time waiting
and closes the ordering races the sleeps papered over.

Fusion-Task-Id: FN-5048
2026-07-07 22:06:22 -07:00
gsxdsm
563a8c6b7c FN-7647: route dashboard sync/mesh AuthStorage writes through createFusionAuthStorage
Route node settings-sync and mesh credential writes through the coordinated @fusion/engine auth store to prevent concurrent clobbers of ~/.fusion/agent/auth.json.

- register-settings-sync-routes.ts, register-settings-sync-inbound-routes.ts, and register-mesh-routes.ts now persist received credentials via createFusionAuthStorage() instead of raw AuthStorage.create(getFusionAuthPath())
- Shares FN-7646's reload-before-persist + per-provider locked-merge path, avoiding lost writes from concurrent Fusion processes
- Uses a static top-level import of @fusion/engine (not dynamic import) per FN-3049's bundler-safety rule
- Adds route-level regression coverage in mesh-routes.test.ts, routes-nodes-sync-contract.test.ts, and routes-nodes-sync.test.ts
- Adds a patch changeset for @runfusion/fusion

Files changed:
 .../fn-7647-auth-storage-routes-coordination.md    |   7 ++
 .../dashboard/src/__tests__/mesh-routes.test.ts    | 118 +++++++++++++++++++++
 .../__tests__/routes-nodes-sync-contract.test.ts   |  27 ++++-
 .../src/__tests__/routes-nodes-sync.test.ts        | 110 ++++++++++++++++++-
 .../dashboard/src/routes/register-mesh-routes.ts   |  14 ++-
 .../register-settings-sync-inbound-routes.ts       |  15 ++-
 .../src/routes/register-settings-sync-routes.ts    |  15 ++-
 7 files changed, 290 insertions(+), 16 deletions(-)

Fusion-Task-Id: FN-7647

Fusion-Task-Lineage: cfab9679-0baa-4eca-8c7a-23db017aa54f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:22 -07:00
gsxdsm
bec8987ce9 FN-7648: gate hold-release on trait-based unplanned-card check, not literal todo column
Blocks planning/intake column cards from entering processing columns regardless of literal column id, so renamed custom intake/planning columns are covered by the same guard as the legacy todo column.

- Add isUnplannedForExecution() in hold-release.ts: true when task.status==="planning", or when the card sits in the legacy todo column or a column carrying the intake trait AND its PROMPT.md still equals the bootstrap stub.
- Route issueRelease() (used by the sweep, promoteHeldTask, and releaseHeldTaskByEvent) through this guard before releasing into any countsTowardWip processing column.
- Update scheduler.ts's reserveSlot guard to use the same trait-based predicate instead of a hardcoded "todo" column id check.
- Add regression tests in hold-release.test.ts and scheduler-workflow-cutover.test.ts covering renamed intake/planning columns.
- Document the invariant in docs/architecture.md and docs/workflow-steps.md.
- Add changeset (patch) describing the fix.

Files changed:
 .changeset/fn-7648-unplanned-intake-cards-never-execute.md |   7 +
 docs/architecture.md                               |   2 +
 docs/workflow-steps.md                             |   2 +
 packages/engine/src/__tests__/hold-release.test.ts | 238 +++++++++++++++++++++
 packages/engine/src/__tests__/scheduler-workflow-cutover.test.ts |  60 +++++-
 packages/engine/src/hold-release.ts                |  60 ++++++
 packages/engine/src/scheduler.ts                   |  26 +--
 7 files changed, 378 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7648

Fusion-Task-Lineage: a4b54d30-f86d-4eb9-9cf2-6ac55b6dbe58

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:21 -07:00
gsxdsm
009ce26fd0 FN-7646: prevent OAuth credential clobbering across concurrent Fusion processes
Fix API keys/OAuth credentials in ~/.fusion/agent/auth.json being clobbered when the desktop app and CLI-served web app run concurrently on one machine.

- Reload primary auth storage from disk (primary.reload()) before persisting a refreshed OAuth credential, so a concurrent process's newer login/refresh for the same provider isn't overwritten by this process's stale in-flight refresh.
- Re-check credential identity against the freshly reloaded disk state before writing the refreshed token back.
- Add cross-process regression coverage exercising concurrent auth.json read-modify-write scenarios.
- Add changeset documenting the fix and its dependency on the pi-coding-agent locked per-provider merge (>=0.80.x).

Files changed:
 .changeset/fn-7646-auth-storage-coordination.md    |   7 +
 .../src/__tests__/auth-storage-concurrency.test.ts | 234 +++++++++++++++++++++
 packages/engine/src/auth-storage.ts                |  27 +++
 3 files changed, 268 insertions(+)

Fusion-Task-Id: FN-7646

Fusion-Task-Lineage: de39f08d-2d9f-46ff-b293-c603e3268ecf

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:21 -07:00
gsxdsm
4e8c621e9c FN-7641: fix cards stranded after out-of-band/workspace merges by allowing proven-merge rehome
Fixes a state-machine bug family where cards got stranded after out-of-band or workspace merges landed: store.moveTask now allows a proven-merge recoveryRehome to cross legacy columns (e.g. todo→done), and nodeId='end' finalize no longer silently no-ops — it finalizes on durable merge proof or returns an explicit error, consistently across the dashboard route, the CLI task-update tool, and store.updateTask.

- packages/core/src/store.ts: allow proven-merge recoveryRehome moves across legacy columns (e.g. todo→done) instead of rejecting them
- packages/core/src/node-override-guard.ts: nodeId='end' finalize now checks for durable merge proof and returns an explicit error instead of silently no-op'ing
- packages/dashboard/src/routes/register-task-workflow-routes.ts: dashboard workflow route surfaces the new explicit finalize error/behavior
- packages/cli/src/extension.ts: CLI task-update tool surfaces the same explicit finalize error/behavior
- docs/task-management.md: documented the updated finalize/rehome behavior
- Added regression tests across core (node-override-guard, store-movement, task-node-override), dashboard (register-task-workflow-routes.nodeid-finalize), engine (merger-merge-lifecycle), and CLI (extension) covering the stranded-card invariant
- Added changeset for @runfusion/fusion (patch)

Files changed:
 .changeset/fn-7641-stranded-cards-after-merge.md   |  7 ++
 docs/task-management.md                            |  2 +
 packages/cli/src/__tests__/extension.test.ts       | 59 ++++++++++++++
 packages/cli/src/extension.ts                      | 10 +++
 .../core/src/__tests__/node-override-guard.test.ts | 93 +++++++++++++++++++++
 packages/core/src/__tests__/store-movement.test.ts | 94 ++++++++++++++++++++++
 .../core/src/__tests__/task-node-override.test.ts  | 73 +++++++++++++++++
 packages/core/src/node-override-guard.ts           | 69 +++++++++++++++-
 packages/core/src/store.ts                         | 69 +++++++++++++++-
 ...er-task-workflow-routes.nodeid-finalize.test.ts | 90 +++++++++++++++++++++
 .../src/routes/register-task-workflow-routes.ts    | 10 +++
 .../src/__tests__/merger-merge-lifecycle.test.ts   | 58 +++++++++++++
 12 files changed, 631 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7641

Fusion-Task-Lineage: 48ea7851-ee68-48f1-92f9-302d0da5acff

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:20 -07:00
gsxdsm
923bba7082 FN-7645: force re-arm zombie heartbeat timers detected as stale during audit
Fixes the heartbeat timer audit so it repairs not just missing timer registrations but also 'zombie' ones — timer entries that remain present in memory after their underlying interval silently stopped firing. Long-interval (~1h) agents were most affected since a single lost tick compounded into hours of staleness before self-healing noticed.

- HeartbeatTriggerScheduler audit now computes staleness (elapsed vs repair-stale threshold) up front for every timer-eligible agent, not only for agents missing a timer entry
- Present-but-stale timer entries are now treated as non-advancing and force cleared/re-registered via registerAgent() (which already clears any existing timer before re-arming)
- Fresh (non-stale) present timers are left alone so healthy short-interval agents are never force-re-armed or double-ticked
- Repair reason/log messages now distinguish zombie-timer-rearmed repairs from missing-registration repairs, and the summary log reports counts for each
- Added heartbeat-scheduler tests covering the zombie-timer repair path
- Added changeset and a docs/architecture.md note

Files changed:
 .changeset/fn-7645-heartbeat-rearm.md              |   7 +
 docs/architecture.md                               |   1 +
 .../src/__tests__/heartbeat-scheduler.test.ts      | 223 +++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             |  42 +++-
 4 files changed, 266 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7645

Fusion-Task-Lineage: 652bc2eb-a660-4306-9f85-d2d5f9ca7e38

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:18 -07:00
gsxdsm
f1db31374a FN-7642: emit diagnostic output for dispatch/infra failures in optional-group and CE gate nodes
Fixes the code-review/plan-review/CE gate workflow node failing with a blank "(no feedback captured)" message when a dispatch or infra exception (not a reviewer verdict) causes the step to fail.

- WorkflowGraphExecutor now synthesizes a non-blank WorkflowStepResult.output when an enabled optional-group (code-review, plan-review, browser-verification) or CE source:"node" skill-gate template node fails via dispatch/infra exception
- Diagnostic output is derived from the node:<id>:error context-patch key, falling back to the failure value, then a stable sentinel
- status, verdict extraction, edge routing, and self-healing's latestFailedPreMergeStep selection are unchanged
- Added regression test coverage: workflow-graph-optional-group-no-feedback.test.ts
- Added changeset (patch) documenting the fix for Runfusion/Fusion#1946

Files changed:
 .changeset/fn-7642-code-review-no-feedback-diagnostic.md          |   7 +
 packages/engine/src/__tests__/workflow-graph-optional-group-no-feedback.test.ts | 246 +++++++++++++++++++++
 packages/engine/src/workflow-graph-executor.ts                    | 104 ++++++++-
 3 files changed, 355 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7642

Fusion-Task-Lineage: 1329e907-652f-4230-a945-5a9d7040ae69

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:18 -07:00
gsxdsm
6777eea5d2 FN-7631: add content search to Chat sidebar with title-only toggle
Chat sidebar search now matches message content by default, not just the conversation title/agent, with an opt-out toggle to restore title-only filtering.

- Add ChatStore.searchSessionsByMessageContent (parameterized LIKE ... ESCAPE) for server-side content search across sessions
- GET /chat/sessions route (register-chat-routes.ts, legacy.ts) gains q/titleOnly query params, debounced server-side content lookup merged with local title/agent matches
- useChat hook exposes searchInTitleOnly state and wires debounced content search into session list results
- ChatView renders a "Search in title only" toggle beside the search box (desktop + mobile) and shows a "Matched: ..." preview snippet on content-matched rows
- Task-planner sessions remain excluded from content matches via the same common-feed visibility guard used for the normal session list
- Add unit/integration tests: chat-store content-search, chat-routes API test, ChatView content-search test
- Update docs/dashboard-guide.md to document the new content search behavior and toggle
- Add changeset fn-7631-chat-content-search.md (@runfusion/fusion minor)

Files changed:
 .changeset/fn-7631-chat-content-search.md          |   7 +
 docs/dashboard-guide.md                            |   2 +
 .../__tests__/chat-store.content-search.test.ts    | 157 +++++++++++++++++++++
 packages/core/src/chat-store.ts                    |  64 +++++++++
 packages/core/src/chat-types.ts                    |   8 ++
 packages/dashboard/app/api/legacy.ts               |  23 ++-
 packages/dashboard/app/components/ChatView.css     |  30 ++++
 packages/dashboard/app/components/ChatView.tsx     |  26 ++++
 .../__tests__/ChatView.autosize.test.tsx           |   2 +
 .../__tests__/ChatView.content-search.test.tsx     | 114 +++++++++++++++
 .../components/__tests__/ChatView.draft.test.tsx   |   2 +
 .../__tests__/ChatView.hash-mention.test.tsx       |   2 +
 .../__tests__/ChatView.mobile-render.test.tsx      |   2 +
 .../components/__tests__/ChatView.rooms.test.tsx   |   2 +
 .../__tests__/ChatView.scroll-to-top.test.tsx      |   2 +
 .../components/__tests__/ChatView.test-harness.tsx |   2 +
 packages/dashboard/app/hooks/useChat.ts            | 105 ++++++++++++--
 .../dashboard/src/__tests__/chat-routes.test.ts    |  78 ++++++++++
 .../dashboard/src/routes/register-chat-routes.ts   |  39 ++++-
 packages/i18n/locales/en/app.json                  |   2 +
 packages/i18n/locales/es/app.json                  |   2 +
 packages/i18n/locales/fr/app.json                  |   2 +
 packages/i18n/locales/ko/app.json                  |   2 +
 packages/i18n/locales/zh-CN/app.json               |   2 +
 packages/i18n/locales/zh-TW/app.json               |   2 +
 25 files changed, 667 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7631

Fusion-Task-Lineage: bc68b489-26a7-453e-901b-bda816af364e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:17 -07:00
gsxdsm
1b7bb1fe18 FN-7639: wire message editing into Planner Chat
Adds the ability to edit and resend a prior user message in task-detail Planner Chat, discarding subsequent turns without reverting already-applied steering/refinement side effects.

- Wire FN-7628's editChatMessage + rewindSessionForEdit into TaskPlannerChatTab for synthetic task-planner:<id> sessions
- Add edit affordance/UI flow and message resend handling in TaskPlannerChatTab
- Ensure already-applied steering comments and refinement tasks are not reverted when a turn is discarded
- Expand TaskPlannerChatTab test coverage for edit/resend flows
- Update dashboard guide docs to describe the new Planner Chat edit behavior
- Add changeset for @runfusion/fusion (minor)

Files changed:
 .changeset/fn-7639-planner-chat-edit.md            |   7 +
 docs/dashboard-guide.md                            |   4 +-
 .../app/components/TaskPlannerChatTab.tsx          |  90 +++++++-
 .../__tests__/TaskPlannerChatTab.test.tsx          | 242 ++++++++++++++++++++-
 4 files changed, 328 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-7639

Fusion-Task-Lineage: ed568a81-12fa-42a8-9ecf-29e6c9a2a884

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:17 -07:00
gsxdsm
26f22861fa FN-7637: port bundled-plugin auto-install into @fusion/core for the desktop runtime
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>
2026-07-07 22:06:16 -07:00
gsxdsm
42009cfdb9 FN-7628: allow editing sent chat messages and rewinding agent responses
Adds the ability to edit a previously sent message in an agent chat, which rewinds the session/task room and regenerates the response from the edited message.

- Add chat-store support for locating/replacing a message and truncating subsequent history for a rewind
- Add a chat-manager rewind-session flow and a new register-chat-routes endpoint to rewind a room to an edited message
- Add legacy API route wiring and useChat hook support for issuing an edit request
- Add ChatView/StandardChatSurface/TaskPlannerChatTab UI affordances (edit control, styling) to trigger message edits
- Add a changeset documenting the new chat message-edit capability
- Add unit/integration tests covering chat-store rewind logic, chat-manager rewind-session behavior, chat routes, useChat, and ChatView edit UI

Files changed:
 .changeset/fn-7628-chat-message-edit.md            |   7 +
 docs/dashboard-guide.md                            |   4 +
 packages/core/src/__tests__/chat-store.test.ts     | 171 ++++++++++++++
 packages/core/src/chat-store.ts                    |  95 ++++++++
 packages/dashboard/app/api/legacy.ts               |  22 ++
 packages/dashboard/app/components/ChatView.css     |  78 ++++++
 packages/dashboard/app/components/ChatView.tsx     |  14 ++
 .../app/components/StandardChatSurface.tsx         |  87 ++++++-
 .../app/components/TaskPlannerChatTab.tsx          |   8 +
 .../__tests__/ChatView.autosize.test.tsx           |   1 +
 .../__tests__/ChatView.default-model-icon.test.tsx |   1 +
 .../components/__tests__/ChatView.draft.test.tsx   |   1 +
 .../__tests__/ChatView.hash-mention.test.tsx       |   1 +
 .../__tests__/ChatView.message-edit.test.tsx       | 262 +++++++++++++++++++++
 .../__tests__/ChatView.mobile-render.test.tsx      |   1 +
 .../components/__tests__/ChatView.rooms.test.tsx   |   1 +
 .../__tests__/ChatView.scroll-to-top.test.tsx      |   1 +
 .../components/__tests__/ChatView.test-harness.tsx |   1 +
 .../dashboard/app/hooks/__tests__/useChat.test.ts  |  98 ++++++++
 packages/dashboard/app/hooks/useChat.ts            |  60 +++++
 .../__tests__/chat-manager-rewind-session.test.ts  | 185 +++++++++++++++
 .../dashboard/src/__tests__/chat-manager.test.ts   |  12 +
 .../dashboard/src/__tests__/chat-routes.test.ts    | 124 ++++++++++
 packages/dashboard/src/chat.ts                     | 147 +++++++++++-
 .../dashboard/src/routes/register-chat-routes.ts   |  52 ++++
 25 files changed, 1429 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7628

Fusion-Task-Lineage: 36d98989-1b75-428c-baf1-b2c7e8e78013

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:15 -07:00
gsxdsm
5b243f1223 FN-7636: surface Hermes runtime models in /api/models picker
Adds a short-TTL, single-flight Hermes model cache and merges Hermes profile-list models additively into the dashboard's /api/models endpoint under the hermes provider, without displacing existing entries.

- Add packages/dashboard/src/hermes-model-cache.ts: single-flight, short-TTL cache wrapping `hermes profile list` CLI output (no per-request spawn)
- Merge Hermes-provided models into register-model-routes.ts's /api/models response, deduped by provider/id, additive-only
- Add unit tests for the Hermes model cache and the /api/models route's Hermes merge behavior
- Document the new behavior in docs/settings-reference.md
- Add changeset (@runfusion/fusion: minor) describing the feature for release notes

Files changed:
 .changeset/fn-7636-hermes-picker-models.md         |   7 +
 docs/settings-reference.md                         |   2 +
 packages/dashboard/src/__tests__/hermes-model-cache.test.ts       | 158 +++++++++++++++++
 packages/dashboard/src/__tests__/register-model-routes-hermes.test.ts | 186 +++++++++++++++++++++
 packages/dashboard/src/hermes-model-cache.ts       | 168 +++++++++++++++++++
 packages/dashboard/src/routes/register-model-routes.ts  |  33 ++++
 6 files changed, 554 insertions(+)

Fusion-Task-Id: FN-7636

Fusion-Task-Lineage: c5ab3bba-37e4-416b-9325-667a8ef321dc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:55 -07:00
gsxdsm
51e389199a FN-7634: widen Planner Chat stop button to match send button
Fix the Planner Chat composer's streaming Stop button rendering narrower than the idle Send button by giving both variants a shared width floor.

- Declare a locally-scoped --chat-input-control-size on .task-planner-chat-composer (same formula as ChatView.css's .chat-input-row so the shared .chat-input-send/.chat-input-stop classes no longer read an undefined custom property and fall back to width: auto.
- Add a min-inline-size floor bound to that property on .task-planner-chat-send (present on both send and stop variants) so neither button renders narrower than the other on desktop.
- Add a regression test asserting the desktop control-size floor, the pre-existing mobile square sizing, and the FN-7594 stop-icon visibility contract.
- Add a changeset documenting the fix as a patch release.

Files changed:
 .changeset/fn-7634-planner-stop-button-width.md    |  7 +++++
 .../app/components/TaskPlannerChatTab.css          |  8 ++++++
 .../__tests__/TaskPlannerChatTab.test.tsx          | 33 ++++++++++++++++++++++
 3 files changed, 48 insertions(+)
EOF
)

Fusion-Task-Id: FN-7634

Fusion-Task-Lineage: e73c836f-3c2d-4871-ac93-fdf5e52de5f6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:54 -07:00
gsxdsm
413ef1dfa2 FN-7633: pin equal explicit heights for Priority, Execution-mode, and Oversight controls
Align the Priority chip, Execution-mode toggle, and Oversight dropdown trigger to the exact same box height in the task detail metadata cluster, closing a gap where a shared min-height floor still let controls diverge.

- Add explicit `height: var(--detail-priority-control-min-height)` to `.detail-priority-chip`, `.detail-execution-mode-toggle`, and `.detail-oversight-menu-trigger` alongside the existing `min-height`, so none can outgrow or undershoot the others regardless of flex stretch/content differences
- Keep `min-height` as a safety-net floor for edge cases like font scaling
- Add regression test asserting all three controls share the same fixed height token on desktop and mobile, and that the Oversight popover itself remains unaffected
- Add changeset documenting the fix as a patch-level bug fix

Files changed:
 .../fn-7633-priority-execution-oversight-height.md |  7 ++++
 .../dashboard/app/components/TaskDetailModal.css   | 44 ++++++++++++++++---
 ...etailModal.responsive-and-dependencies.test.tsx | 49 ++++++++++++++++++++++
 3 files changed, 95 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7633

Fusion-Task-Lineage: 7a89743e-117f-4032-9614-a9a15f2a9b08

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:54 -07:00
gsxdsm
d54ab80395 FN-7623: wire pluginStore/pluginLoader into desktop embedded dashboard server
Fixes the desktop app's plugin subsystem, which was never wired into createServer(), breaking Settings > Plugins Browse registry and plugin install.

- local-runtime.ts: construct a PluginStore + PluginLoader (mirroring the CLI dashboard command), load enabled plugins, run plugin schema-init hooks, and pass pluginStore/pluginLoader/pluginRunner into createServer()
- local-server.ts: apply the same wiring to the legacy desktop local server path for consistency
- Both paths fail soft: a broken plugin subsystem (e.g. corrupt manifest) is logged/traced but no longer blocks embedded dashboard startup
- Extend local-runtime.test.ts and local-server.test.ts to cover plugin wiring and the fail-soft path
- Add changeset (patch) documenting the fix

Files changed:
 .changeset/fn-7623-desktop-plugin-wiring.md        |   7 ++
 .../desktop/src/__tests__/local-runtime.test.ts    | 123 ++++++++++++++++++++-
 .../desktop/src/__tests__/local-server.test.ts     |  69 +++++++++++-
 packages/desktop/src/local-runtime.ts              |  50 ++++++++-
 packages/desktop/src/local-server.ts               |  41 ++++++-
 5 files changed, 286 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7623

Fusion-Task-Lineage: c6f291fb-e6aa-4ac1-a3f3-4189fc831c60

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:53 -07:00
gsxdsm
0f2bfa546c FN-7629: add enable/disable control for built-in runtime plugins
Adds a durable Plugin Manager toggle to enable/disable built-in runtime plugins (Hermes, Paperclip, OpenClaw, Droid) that persists across restarts, replacing the dead-end "Built-in metadata only" CTA.

- renderBuiltinPluginSection now renders an enable/disable toggle for runtime built-ins regardless of installed status
- Disabling a not-yet-installed built-in first installs it (mirroring CLI's ensureBundledPluginInstalled) then immediately disables it, so a plugin_installs row + disabled project state exists with no new persistence primitive needed
- HermesRuntimeCard/OpenClawRuntimeCard/PaperclipRuntimeCard now show "Disabled in Plugin Manager" instead of a stale detected/connected status when disabled
- Added i18n strings across all locales and updated docs
- Added changeset for @runfusion/fusion (minor)
- Expanded plugin-loader and PluginManager test coverage for the new disable/enable flows

Files changed:
 .changeset/fn-7629-builtin-runtime-disable.md      |   7 +
 docs/dashboard-guide.md                            |   1 +
 docs/plugin-management.md                          |   4 +
 packages/core/src/__tests__/plugin-loader.test.ts  |  46 ++++++
 .../dashboard/app/components/HermesRuntimeCard.tsx |  40 ++++-
 .../app/components/OpenClawRuntimeCard.tsx         |  32 +++-
 .../app/components/PaperclipRuntimeCard.tsx        |  32 +++-
 .../dashboard/app/components/PluginManager.css     |  34 +++++
 .../dashboard/app/components/PluginManager.tsx     | 167 +++++++++++++++------
 .../components/__tests__/PluginManager.test.tsx    |  12 +-
 .../__tests__/PluginManager.toggle.test.tsx        | 104 ++++++++++++-
 packages/i18n/locales/en/app.json                  |   4 +
 packages/i18n/locales/es/app.json                  |   4 +
 packages/i18n/locales/fr/app.json                  |   4 +
 packages/i18n/locales/ko/app.json                  |   4 +
 packages/i18n/locales/zh-CN/app.json               |   4 +
 packages/i18n/locales/zh-TW/app.json               |   4 +
 packages/i18n/src/resources.d.ts                   |   4 +
 18 files changed, 440 insertions(+), 67 deletions(-)

Fusion-Task-Id: FN-7629
Fusion-Task-Lineage: 2a25bb1f-3f73-4273-8769-af05c61778f9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:53 -07:00
gsxdsm
a6c60e1592 FN-7625: make auth provider list a static catalog, not runtime-derived
Authentication settings previously enumerated providers straight from pi AuthStorage's live runtime registry, so connecting a runtime plugin (e.g. Hermes Runtime) could narrow/collapse the visible provider list. This adds a static, hand-maintained supported-provider catalog and unions it with storage-reported providers so presence in the list is deterministic while status stays live.

- Add packages/dashboard/src/routes/auth-provider-catalog.ts with STATIC_OAUTH_PROVIDER_CATALOG, STATIC_API_KEY_PROVIDER_CATALOG, and unionProviderCatalog() (catalog always wins on presence; runtime-only extras still surface; runtime name wins on name conflicts).
- Update register-auth-routes.ts's GET /api/auth/status to union the static catalogs with storage.getOAuthProviders()/getApiKeyProviders() instead of relying solely on runtime-reported providers.
- Extend routes-auth.test.ts coverage for the new catalog-union behavior (provider presence stable across narrowed runtime registries, extras preserved, name precedence).
- Add changeset fn-7625-static-auth-provider-catalog.md (patch, fix).

Files changed:
 .changeset/fn-7625-static-auth-provider-catalog.md |   7 +
 .../dashboard/src/__tests__/routes-auth.test.ts    | 180 +++++++++++++++++++--
 .../dashboard/src/routes/auth-provider-catalog.ts  |  93 +++++++++++
 .../dashboard/src/routes/register-auth-routes.ts   |  25 ++-
 4 files changed, 291 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7625

Fusion-Task-Lineage: be984497-b881-4a8f-9860-544617e2b5f7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:52 -07:00
gsxdsm
6bf0090a47 FN-7630: harden Hermes Runtime as additive-only to provider/model/auth catalogs
Confirms and locks in that the Hermes Runtime plugin cannot suppress independently-configured custom providers, models, or auth options, closing out GitHub #1931's remaining audit items.

- Add FNXC documentation comments to register-model-routes.ts and the Hermes Runtime plugin explaining why the plugin structurally cannot mutate AuthStorage/ModelRegistry (no reference in PluginContext) and why configuredProviders only ever grows.
- Add regression coverage proving a connected Hermes runtime never narrows the model picker (/api/models), custom-provider CRUD routes, or auth-status surfaces.
- Add changeset documenting the fix and remaining follow-up items (FN-7625, FN-7636).

Files changed:
 .changeset/fn-7630-hermes-runtime-additive.md      |   7 +
 .../register-auth-routes-hermes-additive.test.ts   | 108 ++++++++++
 .../register-model-routes-hermes-additive.test.ts  | 152 +++++++++++++
 .../custom-provider-routes-hermes-additive.test.ts | 236 +++++++++++++++++++++
 .../dashboard/src/routes/register-model-routes.ts  |  12 ++
 plugins/fusion-plugin-hermes-runtime/src/index.ts  |  12 ++
 6 files changed, 527 insertions(+)

Fusion-Task-Id: FN-7630
Fusion-Task-Lineage: 651cb242-cabf-487b-bf19-21c51b2e91d3
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:51 -07:00
gsxdsm
fe5a595984 FN-7622: unify desktop and CLI provider seeding to fix truncated provider list
The Electron desktop app's in-process dashboard server skipped the CLI's provider seeding sequence, so /api/providers and /api/models returned a truncated catalog (missing built-in API-key providers and user customProviders[]) compared to the identical config on the web build.

- Move provider-auth.ts and custom-provider-registry.ts from @fusion/cli into @fusion/engine as the single shared implementation
- Add engine/src/provider-registration.ts exposing seedDashboardProviders(), mirroring the CLI's exact startup order (built-in Zai provider registration -> wrapAuthStorageWithApiKeyProviders -> model merge/refresh -> registerCustomProviders -> settings:updated resubscription)
- Update desktop/src/local-runtime.ts and local-server.ts to call the shared seedDashboardProviders() helper instead of constructing a raw authStorage/modelRegistry
- Convert packages/cli/src/commands/provider-auth.ts and custom-provider-registry.ts into re-export shims preserving unchanged observable behavior
- Add engine/src/__tests__/provider-registration.test.ts and expand desktop local-runtime/local-server tests to cover the shared seeding path
- Add changeset for @runfusion/fusion (patch)

Files changed:
 .changeset/fn-7622-desktop-provider-parity.md      |   7 +
 .../cli/src/commands/custom-provider-registry.ts   | 122 +----
 packages/cli/src/commands/provider-auth.ts         | 517 +--------------------
 .../desktop/src/__tests__/local-runtime.test.ts    |  93 ++++
 .../desktop/src/__tests__/local-server.test.ts     |  62 ++-
 packages/desktop/src/local-runtime.ts              |  33 +-
 packages/desktop/src/local-server.ts               |  21 +-
 .../src/__tests__/provider-registration.test.ts    | 192 ++++++++
 packages/engine/src/custom-provider-registry.ts    | 117 +++++
 packages/engine/src/index.ts                       |  18 +
 packages/engine/src/provider-auth.ts               | 513 ++++++++++++++++++++
 packages/engine/src/provider-registration.ts       | 105 +++++
 12 files changed, 1172 insertions(+), 628 deletions(-)

Fusion-Task-Id: FN-7622
Fusion-Task-Lineage: fb6fbbf3-745e-4623-b7af-11471e13f138
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:51 -07:00
gsxdsm
a4f5fbc0e9 FN-7624: fix onboarding GitHub sign-in erroring with model-not-found
Fixes the onboarding/settings GitHub step so it never offers a dashboard OAuth login for github (no github OAuth provider is ever registered; pi only ships anthropic/github-copilot/openai-codex), replacing the broken Connect OAuth button with gh CLI guidance and a clearer server-side error.

- Remove the "Connect OAuth (optional)" button and its login-instructions panel from the onboarding branch that runs when hasGithubProvider is false (ModelOnboardingModal.tsx), since it always called handleLogin("github") against a non-existent provider
- Update ModelOnboardingModal tests to cover the new gh-CLI-only flow
- Make POST /api/auth/login return a clear, actionable 400 naming the requested provider, the registered dashboard OAuth providers, and that GitHub integration uses gh CLI/token auth instead of a generic "Unknown provider" / model-not-found error
- Add routes-auth.test.ts coverage for the improved unknown-provider error message
- Add a patch changeset documenting the fix

Files changed:
 .changeset/fn-7624-github-onboarding-auth.md       |  7 +++++
 packages/dashboard/app/components/ModelOnboardingModal.tsx | 32 +++++++---------------
 packages/dashboard/app/components/__tests__/ModelOnboardingModal.test.tsx | 32 +++++++++++++++++++++-
 packages/dashboard/src/__tests__/routes-auth.test.ts | 22 +++++++++++++++
 packages/dashboard/src/routes/register-auth-routes.ts | 14 +++++++++-
 5 files changed, 83 insertions(+), 24 deletions(-)

Fusion-Task-Id: FN-7624
Fusion-Task-Lineage: e7be1b0a-6e50-479d-b56e-1284e8a94b7f
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:50 -07:00