Commit Graph

8745 Commits

Author SHA1 Message Date
gsxdsm
d739223de2 FN-7049: collapse model pricing behind modal editor
Collapse the Global Models pricing editor into a compact summary with a modal table for detailed edits.

- Add a View pricing table modal with escape, overlay, and close-button dismissal.
- Keep add, edit, delete, and LiteLLM fetch workflows available while showing override counts in the collapsed settings view.
- Update model pricing tests, styling, and documentation for the collapsed summary and modal editor.

Files changed:
 docs/dashboard-guide.md                            |   2 +-
 docs/settings-reference.md                         |   4 +-
 .../settings/sections/ModelPricingSection.css      |  36 +++++-
 .../settings/sections/ModelPricingSection.test.tsx |  62 ++++++++-
 .../settings/sections/ModelPricingSection.tsx      | 144 +++++++++++++++------
 5 files changed, 198 insertions(+), 50 deletions(-)

Fusion-Task-Id: FN-7049
Fusion-Task-Lineage: 9ba63354-28e7-4717-81e6-805f1f05fd97
2026-06-26 01:04:03 -07:00
gsxdsm
6c94ee069c FN-7023: forward MCP runtime config to AI lanes
Forward enabled MCP server configuration through AI session creation and validation paths.

- Resolve and materialize effective MCP servers with secret values at runtime for triage, execution, review, merge, evaluation, reflection, mission validation, and dashboard chat/planning lanes.
- Add runtime support guards, Claude CLI MCP config generation, and a dashboard validation route for MCP server reachability probes.
- Cover MCP forwarding, provider support behavior, settings resolution, validation, and pi extension config generation with targeted tests and docs.

Files changed:
 .changeset/fn-7023-mcp-runtime-forwarding.md       |   7 +
 docs/agents.md                                     |   2 +
 docs/secrets.md                                    |   1 +
 docs/settings-reference.md                         |   6 +-
 .../src/__tests__/mcp-lane-forwarding.test.ts      |  79 +++++++++++
 .../src/__tests__/mcp-validate-route.test.ts       | 111 ++++++++++++++++
 packages/dashboard/src/chat.ts                     |   3 +
 packages/dashboard/src/planning.ts                 |   5 +
 packages/dashboard/src/routes.ts                   |  90 ++++++++++++-
 .../src/__tests__/mcp-lane-forwarding.test.ts      |  95 ++++++++++++++
 .../engine/src/__tests__/mcp-resolution.test.ts    | 104 +++++++++++++++
 .../src/__tests__/mcp-runtime-support.test.ts      |  44 +++++++
 .../src/__tests__/mcp-validation-service.test.ts   |  69 ++++++++++
 packages/engine/src/__tests__/pi.test.ts           |  65 +++++++++
 packages/engine/src/agent-reflection.ts            |   3 +
 packages/engine/src/agent-runtime.ts               |  36 ++++-
 packages/engine/src/agent-session-helpers.ts       |   2 +
 packages/engine/src/evaluator.ts                   |   3 +
 packages/engine/src/executor.ts                    |  14 ++
 packages/engine/src/index.ts                       |   3 +
 packages/engine/src/mcp-resolution.ts              |  81 ++++++++++++
 packages/engine/src/mcp-runtime-support.ts         |  41 ++++++
 packages/engine/src/mcp-validation-service.ts      | 146 +++++++++++++++++++++
 packages/engine/src/merger-ai.ts                   |   5 +
 packages/engine/src/merger.ts                      |  14 ++
 packages/engine/src/mission-execution-loop.ts      |   3 +
 packages/engine/src/pi.ts                          |  44 +++++--
 packages/engine/src/reviewer.ts                    |   3 +
 packages/engine/src/runtime-resolution.ts          |   8 +-
 packages/engine/src/step-session-executor.ts       |   6 +-
 packages/engine/src/triage.ts                      |   5 +
 packages/pi-claude-cli/index.ts                    |  20 ++-
 packages/pi-claude-cli/src/mcp-config.ts           |  65 ++++++++-
 33 files changed, 1150 insertions(+), 33 deletions(-)

Fusion-Task-Id: FN-7023
Fusion-Task-Lineage: ea09c6c7-b3c3-4af6-9ca6-0ebbfec17139
2026-06-26 00:57:32 -07:00
gsxdsm
93da87dc5d FN-7048: enable mobile detail tab swiping
Allow task detail tabs to keep horizontal touch scrolling on mobile surfaces.

- Add pan-x touch-action overrides on detail tab buttons for modal, embedded, mobile, and tablet contexts.
- Extend responsive CSS coverage to assert each task-detail tab target allows horizontal panning.
- Add a patch changeset documenting the mobile task-detail tab scroll fix.

Files changed:
 .changeset/fn-7048-task-detail-mobile-tab-touch-action.md  |  7 +++++++
 packages/dashboard/app/components/TaskDetailModal.css      | 14 ++++++++++++++
 .../TaskDetailModal.responsive-and-dependencies.test.tsx   | 14 +++++++++++++-
 3 files changed, 34 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7048

Fusion-Task-Lineage: bb07b72f-8843-4ed4-a45b-ea8172bc4957
2026-06-26 00:48:51 -07:00
gsxdsm
f3f20accbb FN-7031: preview binary media files in dock Files viewer
Render browser-native previews for right-dock file selections while preserving text editing behavior.

- Add image, video, audio, and PDF preview rendering through the workspace download URL.
- Keep previewable and known binary files out of the text editor, with a read-only fallback for unsupported binaries.
- Cover compact and two-pane dock preview behavior with tests, docs, and a patch changeset.

Files changed:
 .changeset/fn-7031-dock-files-binary-preview.md    |   7 +
 docs/dashboard-guide.md                            |   2 +-
 .../dashboard/app/components/DockFilesView.css     |  45 ++++++
 .../dashboard/app/components/DockFilesView.tsx     |  85 ++++++++++-
 .../components/__tests__/DockFilesView.test.tsx    | 170 ++++++++++++++++++++-
 5 files changed, 303 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7031

Fusion-Task-Lineage: a52bdd57-bb3e-494e-85a8-81326a1548e2
2026-06-26 00:43:55 -07:00
gsxdsm
2ce208e36d FN-7036: make Automations popup movable and resizable
Render the Automations modal in the shared floating-window shell while preserving embedded and mobile behavior.

- Wrap the modal presentation in FloatingWindow with persisted geometry, drag handle, and resize sizing.
- Update Automations modal CSS so desktop fills the floating shell and mobile remains full-screen with resize handles hidden.
- Cover floating-window rendering, dragging, resizing, mobile CSS contract, and embedded mode in ScheduledTasksModal tests.
- Add a minor changeset for the published Fusion package.

Files changed:
 .changeset/fn-7036-automation-modal-floating.md    |   7 ++
 .../app/components/ScheduledTasksModal.tsx         |  43 +++++----
 packages/dashboard/app/components/ScriptsModal.css |  78 +++++++++-------
 .../__tests__/ScheduledTasksModal.test.tsx         | 103 +++++++++++++++++++--
 4 files changed, 172 insertions(+), 59 deletions(-)

Fusion-Task-Id: FN-7036

Fusion-Task-Lineage: 3d6926b7-ec6d-4179-ad17-573bcfef297f
2026-06-26 00:35:41 -07:00
gsxdsm
98a5052b65 FN-6706: add signal connector incident ingestion
Connect external signal ingestion routes to Command Center incident analytics.\n\n- Add connector status reporting and incident upsert/resolution support for webhook, Sentry, Datadog, and PagerDuty sources.\n- Move signal aggregation through shared activity analytics and expose connector-aware empty states in Command Center.\n- Document connector setup, settings, and dashboard behavior with a published changeset.\n- Extend route, analytics, and Command Center UI tests for scoped signal data and configured-but-quiet connectors.\n\nFiles changed:\n .changeset/fn-6706-signal-connectors.md            |   7 +\n AGENTS.md                                          |   1 +\n docs/dashboard-guide.md                            |   4 +-\n docs/settings-reference.md                         |  16 +\n docs/signals-connectors.md                         | 140 ++++++++\n .../core/src/__tests__/signals-analytics.test.ts   |  37 ++-\n packages/core/src/activity-analytics.ts            | 185 ++++++++++-\n packages/core/src/index.ts                         |  11 +-\n packages/core/src/signals-analytics.ts             | 209 +-----------\n .../command-center/areas/SignalsArea.tsx           |  47 ++-\n .../areas/__tests__/areas.github-signals.test.tsx  | 120 +++++--\n .../command-center/areas/__tests__/areas.test.tsx  |   3 +-\n .../register-command-center-routes.test.ts         |  72 +++-\n .../src/__tests__/register-signal-routes.test.ts   | 369 ++++++++++++++++++++-\n .../src/routes/register-command-center-routes.ts   |  29 +-\n .../dashboard/src/routes/register-signal-routes.ts |  55 +++\n packages/dashboard/src/signal-source.ts            |   8 +\n packages/dashboard/src/signal-sources/datadog.ts   |   8 +\n packages/dashboard/src/signal-sources/pagerduty.ts |  14 +-\n packages/dashboard/src/signal-sources/sentry.ts    |  19 +-\n packages/dashboard/src/signal-sources/webhook.ts   |   8 +\n 21 files changed, 1094 insertions(+), 268 deletions(-)

Fusion-Task-Id: FN-6706

Fusion-Task-Lineage: 09003e9b-481b-4393-926b-44907d13bcf9
2026-06-26 00:28:31 -07:00
gsxdsm
4dab2b6986 FN-7011: Exclude engine downtime from task timing
Exclude proven engine-process downtime from active task duration stats and badges.

- Persist a throttled engineLastActiveAt heartbeat while the scheduler is running and unpaused.
- Reconcile in-progress execution segment anchors on startup when downtime exceeds the poll threshold.
- Emit run-audit events for shifted and no-action downtime reconciliation outcomes.
- Cover core timing, dashboard badge behavior, scheduler heartbeat throttling, and self-healing audit metadata.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7011-engine-downtime-timing.md       |  7 +++
 AGENTS.md                                          |  1 +
 .../src/__tests__/store-execution-timing.test.ts   | 51 ++++++++++++++++++++++
 packages/core/src/settings-schema.ts               |  1 +
 packages/core/src/store.ts                         | 28 ++++++++++++
 packages/core/src/types.ts                         |  5 +++
 .../app/utils/__tests__/taskTiming.test.ts         | 16 +++++++
 .../__tests__/scheduler-workflow-cutover.test.ts   | 29 +++++++++++-
 packages/engine/src/__tests__/self-healing.test.ts | 23 ++++++++++
 packages/engine/src/run-audit.ts                   |  4 ++
 packages/engine/src/scheduler.ts                   | 11 +++++
 packages/engine/src/self-healing.ts                | 21 +++++++++
 12 files changed, 196 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7011

Fusion-Task-Lineage: 44225b0a-0fa1-40be-812d-33fd5e446944
2026-06-25 23:35:31 -07:00
gsxdsm
da69e03d29 FN-7047: remove quick-entry keyboard hint
Remove the retired quick-entry keyboard hint and its empty UI shell from task creation.

- Drop the quick-entry hint element and associated responsive styles.
- Remove the retired quickEntryHint locale key from all app locales and generated resources.
- Add coverage asserting the hint and shell stay absent across expanded and collapsed quick-entry modes.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-7047-quick-entry-hint-removal.md         |  7 +++++++
 packages/dashboard/app/components/QuickEntryBox.css    | 18 ------------------
 packages/dashboard/app/components/QuickEntryBox.tsx    |  4 +---
 .../app/components/__tests__/QuickEntryBox.test.tsx    | 18 ++++++++++++++++++
 packages/i18n/locales/en/app.json                      |  1 -
 packages/i18n/locales/es/app.json                      |  1 -
 packages/i18n/locales/fr/app.json                      |  1 -
 packages/i18n/locales/ko/app.json                      |  1 -
 packages/i18n/locales/zh-CN/app.json                   |  1 -
 packages/i18n/locales/zh-TW/app.json                   |  1 -
 packages/i18n/src/resources.d.ts                       |  1 -
 11 files changed, 26 insertions(+), 28 deletions(-)

Fusion-Task-Id: FN-7047

Fusion-Task-Lineage: c838f25b-a3cf-4c2c-b214-511df12dd423
2026-06-25 23:34:08 -07:00
gsxdsm
ea5e12e8eb FN-7042: stop refocusing quick task input after submit
Prevent successful quick task creation from stealing focus back to the quick-entry textarea.

- Remove the post-submit focus restoration request/effect from QuickEntryBox.\n- Update desktop, mobile, duplicate-confirm, and image-upload tests to assert no automatic refocus.\n- Add a patch changeset documenting the quick-entry focus fix.\n\nFiles changed:\n .changeset/fn-7042-quick-entry-no-refocus.md       |  7 +++\n .../dashboard/app/components/QuickEntryBox.tsx     | 46 +------------------\n .../components/__tests__/QuickEntryBox.test.tsx    | 53 ++++++++++++----------\n 3 files changed, 39 insertions(+), 67 deletions(-)

Fusion-Task-Id: FN-7042

Fusion-Task-Lineage: 7c8e26a6-3a6b-463f-bfb7-8310682584f8
2026-06-25 23:34:08 -07:00
gsxdsm
9bd0853667 FN-7044: split Command Center area tests
Split the oversized Command Center area test suite into focused files while preserving shared fixtures and coverage.

- Extract shared date ranges, fixtures, and helpers into areas.test-harness.tsx.
- Move GitHub and Signals area assertions into a dedicated sibling suite.
- Keep the remaining areas.test.tsx below the 2000-line cap with existing coverage intact.

Files changed:
 .../areas/__tests__/areas.github-signals.test.tsx  | 406 +++++++++++++
 .../areas/__tests__/areas.test-harness.tsx         | 302 ++++++++++
 .../command-center/areas/__tests__/areas.test.tsx  | 648 +--------------------
 3 files changed, 730 insertions(+), 626 deletions(-)

Fusion-Task-Id: FN-7044

Fusion-Task-Lineage: 63886ec9-e9c3-410e-bbc6-c5116cddb168
2026-06-25 23:34:08 -07:00
gsxdsm
07209a4732 FN-7045: capitalize Code Review step name
Normalize the built-in Code Review workflow label and cover the display-name invariant.

- Rename the compound-engineering built-in code-review node to "Code Review".
- Add regression coverage that rejects lowercase built-in Code review node names.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7045-code-review-step-name.md           | 7 +++++++
 packages/core/src/__tests__/builtin-workflows.test.ts | 9 +++++++++
 packages/core/src/builtin-workflows.ts                | 6 +++++-
 3 files changed, 21 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7045

Fusion-Task-Lineage: 4608df38-dc5f-4d16-b33e-9f4ce00fe96d
2026-06-25 23:34:07 -07:00
gsxdsm
429143ecf0 FN-7024: add MCP server management CLI
Add CLI support for managing MCP server configuration without exposing secret values.

- Add fn mcp list/add/edit/remove/enable/disable/import/export/validate subcommands.
- Store MCP env and header values as Fusion secret references, including Claude Desktop import conversion.
- Document MCP CLI usage and add command coverage for scoped configuration, import, export, and validation.
- Add a minor changeset for the published CLI feature.

Files changed:
 .changeset/fn-7024-mcp-cli.md                   |   7 +
 docs/cli-reference.md                           |  49 +++
 packages/cli/src/bin.ts                         | 124 +++++++
 packages/cli/src/commands/__tests__/mcp.test.ts | 182 ++++++++++
 packages/cli/src/commands/mcp.ts                | 455 ++++++++++++++++++++++++
 5 files changed, 817 insertions(+)

Fusion-Task-Id: FN-7024

Fusion-Task-Lineage: e3d98bfa-b883-4b9e-86d5-f0416808d6f2
2026-06-25 23:34:07 -07:00
gsxdsm
2c46cdc1e2 FN-7041: fix optional workflow step descriptions
Fix the workflow results settings display for configured optional steps.

- Treat workflow step definitions as missing only when the lookup has no entry.
- Preserve empty descriptions for found optional-group steps such as Code Review.
- Add regression coverage for configured, checkbox, and ordering displays.
- Add a patch changeset for the published Fusion package.

Files changed:
 .../fn-7041-workflow-step-definition-not-found.md  |  7 +++++
 .../app/components/WorkflowResultsTab.tsx          |  9 +++++-
 .../__tests__/WorkflowResultsTab.test.tsx          | 36 ++++++++++++++++++++++
 3 files changed, 51 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7041

Fusion-Task-Lineage: 84698437-d4d3-4b68-9c5f-c8b15f513230
2026-06-25 23:34:07 -07:00
gsxdsm
c7cbae147c FN-7040: align task-detail model displays
Unify task-detail model resolution so Chat, Agent Log, and Workflow settings display the same effective model.

- Add a shared dashboard resolver for executor, validator, and planning model display precedence.
- Reuse the resolver in TaskDetailModal and WorkflowResultsTab instead of duplicating fallback logic.
- Cover log marker, assigned runtime model, task override, and settings fallback cases with focused tests.
- Add a patch changeset for the published Fusion package.

Files changed:
 .../FN-7040-align-task-detail-model-display.md     |   7 +
 .../dashboard/app/components/TaskDetailModal.tsx   | 158 +--------------------
 .../app/components/WorkflowResultsTab.tsx          |  15 +-
 .../__tests__/WorkflowResultsTab.test.tsx          |  74 +++++++++-
 .../__tests__/effective-model-resolution.test.ts   | 111 +++++++++++++++
 .../app/components/effective-model-resolution.ts   | 149 +++++++++++++++++++
 6 files changed, 354 insertions(+), 160 deletions(-)

Fusion-Task-Id: FN-7040

Fusion-Task-Lineage: 05ec3ae7-5063-4219-a515-da16168e9a19
2026-06-25 23:34:07 -07:00
gsxdsm
9a2709dde9 FN-6969: show provider icons beside Command Center models
Show inferred provider icons beside model names across Command Center bar and table surfaces.

- Add dashboard-side provider-icon inference for model/provider strings.
- Render provider icons on Command Center model bar rows and token table rows while keeping pie chart labels text-only.
- Cover overview, token, and ecosystem model-icon surfaces with tests and add a changeset.

Files changed:
 .changeset/fn-6969-provider-icons.md               |   7 ++
 .../components/command-center/CommandCenter.tsx    |  15 ++-
 .../__tests__/CommandCenter.test.tsx               |  33 ++++++-
 .../command-center/areas/EcosystemArea.tsx         |  15 ++-
 .../components/command-center/areas/TokensArea.tsx |  37 +++++++-
 .../areas/__tests__/TokensArea.test.tsx            | 105 +++++++++++++++++++++
 .../command-center/areas/__tests__/areas.test.tsx  |  42 ++++++++-
 .../app/components/command-center/charts/Bar.tsx   |  25 ++++-
 .../components/command-center/charts/charts.css    |  42 +++++++++
 .../app/utils/__tests__/providerIconKey.test.ts    |  32 +++++++
 packages/dashboard/app/utils/providerIconKey.ts    |  45 +++++++++
 11 files changed, 377 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-6969

Fusion-Task-Lineage: 6caf8864-1ecc-46e3-b7fa-bfa8e9a70aaa
2026-06-25 23:34:07 -07:00
gsxdsm
433ccce0b5 fix: stop global settings resetting via project-local central DBs (#1787)
## Problem

Operators intermittently saw **"all my global settings reset"** —
including the global concurrency cap surfacing as `gate=semaphore` with
a low value in scheduler queue logs.

## Root cause

`CentralCore` is supposed to live at `~/.fusion/fusion-central.db`, but
`resolveGlobalDir(dir)` returns an explicit dir **verbatim**, and
several production call sites passed the **project** `.fusion/` dir:

- `store.getSecretsStore()` → `new CentralCore(store.getFusionDir())`
- dashboard secrets/proxy/node/secrets-sync/settings-sync routes → `new
CentralCore(store.getFusionDir())`

Each spawned a **stray per-project central DB**
(`<project>/.fusion/fusion-central.db`) seeded with **default** global
state (`globalMaxConcurrent=4`, empty secrets, default
`centralSettings`) that shadowed the real global DB whenever a
read/write hit one of those paths. Confirmed on disk: 14+ stray DBs at
default `4` vs the real `~/.fusion` at the operator's actual value.

## Fix

- Add `TaskStore.getGlobalSettingsDir()` returning the **resolved global
dir** (`string`); route the secrets store + all the affected dashboard
routes through it instead of `getFusionDir()`.
- `getSecretsStore()` also passes the global dir to `MasterKeyManager`
(co-locates the master key; makes the path test-exercisable).
- Add a `resolveGlobalDir()` **guard** that throws on a project-local
`.fusion/` dir (basename `.fusion` with a `.git` parent), with an
explicit `FUSION_ALLOW_PROJECT_LOCAL_GLOBAL_DIR` opt-out for
legitimately version-controlled custom global dirs. Inert under VITEST.

## Tests

- `global-settings-guard.test.ts` — guard rejects project/worktree
`.fusion` dirs, allows home + custom non-repo dirs.
- `store-secrets-store-global-dir.test.ts` — **symptom-based**:
`getSecretsStore()` creates the central DB in the global dir and
**never** spawns a stray project-local `fusion-central.db`.
- Added `getGlobalSettingsDir()` to route-test mock stores (the new
getter is now called by the routes).

## Operator note

Existing stray project-local `fusion-central.db` files (all
default/empty in practice) should be removed; on the affected machine
they were quarantined to `.legacy-central-db-backup-*` folders.

## Verification

- `pnpm --filter @fusion/core run typecheck` / `@fusion/dashboard`
typecheck — clean
- core guard + regression tests pass; previously-affected route suites
(proxy, nodes-sync, browse-directory, secrets-sync) pass (313/313)
- `pnpm check:changesets` — clean; eslint on changed files — clean

Companion to #1786 (global concurrency slider UI), which is independent.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1787">
  <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

* **Bug Fixes**
* Fixed intermittent resets of global settings (including the global
concurrency cap).
* Ensured global settings/secrets are read from and written to the
correct global storage location rather than a project-local one.
* Added a safeguard to prevent using a project-local settings directory
when running inside a repository.

* **Tests**
* Added regression coverage for global directory resolution/guard
behavior.
* Updated route and secrets sync tests to validate the global directory
selection used for proxying and sync.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 23:33:43 -07:00
gsxdsm
e335acc235 feat: global concurrency slider (footer + dashboard) and scoped settings (#1786)
## What

Adds a **Global Max Concurrent** slider to two operator surfaces and
groups the Scheduling settings by scope.

- **Footer engine menu** (`EngineControlMenu`) and **Command Center →
Concurrency card** (`CommandCenterControls`) each get a global-cap
slider.
- **Settings → Scheduling** (`SchedulingSection`) now groups fields
under labeled **"Global — applies to all projects"** and **"This
project"** subheadings with scope badges (clearer on mobile, and
surfaces the existing `globalMaxConcurrent` field that was easy to
miss).

## How

Both sliders are backed by a single shared `useGlobalConcurrency` hook
(module-level store + subscribers), so they read/write **one source of
truth** and **revalidate after every `PUT /api/global-concurrency`**.
This avoids the last-writer-wins / stale-clobber races that two
independent per-component caches would cause (which would ironically
re-create a "global cap resets" symptom). The hook also:

- treats a fetch error as **non-interactive** (slider disabled, not
stuck showing `1` and persistable),
- surfaces a **save-state** indicator (Saving…/Saved/Save failed)
mirroring the project sliders,
- **flushes a pending edit on menu close / unmount** so a quick
drag-then-dismiss isn't silently dropped.

## Review

These race/feedback behaviors were hardened in response to a multi-agent
`/code-review` (correctness, adversarial, reliability, frontend-races
all flagged the per-component-cache approach). Independent of the
companion fix PR (global-settings reset).

## Verification

- `pnpm --filter @fusion/dashboard run typecheck` — clean
- eslint on changed files — clean
- Not yet browser-verified (next step).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1786">
  <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 a global concurrency slider that applies across all projects.
* Global and per-project scheduling controls are now clearly separated
with scope labels and badges.
* The concurrency setting now stays in sync across different dashboard
views and saves automatically after brief pauses.

* **Bug Fixes**
  * Improved feedback for loading, saving, and error states.
* Prevented confusion between global settings and project-specific
settings, especially on smaller screens.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 23:27:23 -07:00
gsxdsm
9fd4dedcae fix(review): resolve PR feedback on resolveGlobalDir guard
- Avoid the legacy-migration side effect on the hot path: run the cheap,
  read-only checks (basename === ".fusion" && parent has .git) FIRST, and
  only call resolveGlobalDirForHome() (which can perform a one-time rename)
  when a dir actually looks project-local.
- Normalize paths before the home-dir comparison (realpathSync when present,
  else resolve) so a trailing slash, doubled separator, or symlinked home
  doesn't make the legitimate home global dir trip the guard.
- Harden the browse-directory regression test: the mock returns a global dir
  DISTINCT from getFusionDir() and asserts CentralCore is constructed with the
  global dir, so a revert to getFusionDir() now fails the test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 23:17:02 -07:00
gsxdsm
1ebb518698 fix(review): resolve PR feedback on global concurrency hook
- Double-commit race: null pendingValueRef synchronously at commit() start
  so the close-flush + unmount-cleanup guards can't both fire and send a
  duplicate PUT for the same value.
- Clamp ceiling: clamp setValue against max(SLIDER_BASE_MAX, currentValue)
  instead of Number(raw) (which had no effective upper bound for
  programmatic callers).
- Out-of-band staleness: force-revalidate on each activate so a Settings-modal
  save (which writes globalMaxConcurrent directly, bypassing this store) is
  reflected when a slider surface reopens.
- Load-error label: add an explicit error branch in both EngineControlMenu
  and CommandCenterControls so a failed initial load shows "Load failed"
  instead of "Ready" while the slider is disabled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 23:05:56 -07:00
gsxdsm
7b19f9796c test: remove real-time waits from the slowest test files (FN-5048) (#1784)
Removes real wall-clock waits and per-test rebuilds from the slowest
test files, replacing them with deterministic seams. **No assertions
weakened, no timeouts widened, no retries added** — this is anti-pattern
removal per FN-5048, verified by re-running each file.

## Changes

| File | What | Result |
|---|---|---|
| `dashboard/.../insights-routes.test.ts` | Boot server+store **once**
in `beforeAll` (was `createServer` + `TaskStore.init` per test ×24);
reset insight tables per test for isolation; drive sweeper via fake
timers | test-exec **~3.7s → ~0.8s** |
| `core/.../db.test.ts` | Fixed 150ms write-lock hold → manual stdin
signal-release (keeps the real OS-lock contention under test); fixed a
real EPIPE on redundant release | 152 pass, non-flaky / 8 runs; −300ms
dead wait |
| `core/.../mission-store.test.ts` | 4 real `setTimeout` sleeps (only
there to force distinct timestamps) → `vi.setSystemTime` controlled
clock | anti-pattern removed |
| `core/.../agent-store.test.ts` | 1 real ordering-sleep → injected
`renewedAt` clock; **assertions strengthened** to pin exact timestamp
values | anti-pattern removed |
| `engine/.../in-process-runtime.test.ts` | Fake the one real 25ms
sleep; drop its inflated 30s per-test timeout | anti-pattern removed |

## Honest accounting
- The **real wins** are `insights-routes` (per-test server boot
eliminated, ~75% execution-time cut) and `db` (dead lock-hold removed).
- The **timestamp-sleep removals** (mission-store, agent-store,
in-process-runtime) are small absolute wins — the headline per-file
durations (16–25s) were **full-suite shard contention, not in-file dead
time** (each runs in 3–10s isolated). But they eliminate the FN-5048
real-wait anti-pattern, so a hub edit no longer drags real sleeps into
every `--changed` selection.
- **`workflow-routes.test.ts` was evaluated for splitting and
deliberately NOT split.** A measured A/B showed the 4-way split
*regressed* wall-clock (6s → 11s): the file is import/transform-bound
(per-file esbuild + `@fusion/core`/express import ≈ 5s > the ~4.3s test
runtime), and per-test store migration was already amortized by
`installInMemoryDbSnapshot`. Splitting only multiplies the dominant
fixed cost. Left intact.

## Verification
- `core` 612/612, `dashboard` 24/24, `engine` 78/78 (file-scoped).
- `tsc --noEmit` clean on all 3 packages; eslint clean.

Follow-up (not in this PR): `scripts/test-timings.json` is stale (its
former #1 file no longer exists) — refresh via `pnpm test:velocity --
--measure --write-report` so the watchdog budgets and velocity report
reflect reality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1784">
  <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

* **Tests**
* Made several test suites more deterministic by replacing real-time
delays with controlled timers and fixed timestamps.
* Improved lock and task checkout tests to use manual release signals,
reducing timing-related flakiness.
* Streamlined route test setup/teardown for faster, more reliable runs.
* Added safer cleanup around timer-based tests to avoid intermittent
failures.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 22:35:53 -07:00
gsxdsm
50f5c36660 feat(core): per-stage column dwell instrumentation (#1781)
## Why

Diagnosing "tasks take too long" showed the dominant end-to-end
wall-clock is **waiting**, not work: tasks sit in `todo` (queue) and
`in-review` (merge wait) far longer than the agent actually runs. Today
only `cumulativeActiveMs` exists, which measures **in-progress time
only** — every other stage's dwell had to be reconstructed by hand from
agent logs.

## What

Adds `columnDwellMs?: Record<string, number>` to `Task` — a per-column
accumulator (column name → cumulative ms), recorded at the **same store
column-transition seam** as `cumulativeActiveMs` (`moveTaskInternal`).
On each move it adds `columnMovedAt(new) − columnMovedAt(prev)` to the
bucket for the column being left:

- clamped `>= 0` (clock skew safe);
- unparseable/missing prior timestamp and zero-dwell moves are skipped
(no spurious buckets);
- second visits **add** to the existing bucket (multi-visit churn is
captured);
- flag-independent — keys off the generic `columnMovedAt` delta, so it
runs for both the workflow-hook and legacy-inline move paths.

This makes per-stage dwell directly queryable, the same way
`productivity-analytics.ts` already consumes `cumulativeActiveMs`.

## Persistence

JSON-text task column, following the v129 `workspaceWorktrees` precedent
exactly: `SCHEMA_SQL` column + `SCHEMA_VERSION` 129→130 + a versioned
`addColumnIfMissing` migration. Additive and behavior-preserving —
pre-existing rows start NULL and accumulate from their next transition.
Survives archive/restore (added to the archive-entry mapping).

## Tests

`src/__tests__/store-execution-timing.test.ts` — new regression asserts
dwell across
`todo→in-progress→in-review→done→todo→in-progress→in-review` accumulates
the right per-column ms (second visits add) and survives a `getTask` DB
round-trip.

```
pnpm --filter @fusion/core exec vitest run src/__tests__/store-execution-timing.test.ts ... --reporter=dot
→ store-execution-timing 5/5, schema suites (goals/secrets) green, 17/17 total
```

Migration chain verified end-to-end (secrets-schema test climbs v11/v82
→ v130). No hardcoded literal version assertions in the suite; schema
tests assert against the `SCHEMA_VERSION` constant.

`@fusion/core` is private — no changeset.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1781">
  <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 per-column dwell timing to tasks, showing how long work spent in
each column across multiple visits.
  * Preserved this timing data when tasks are archived and restored.

* **Bug Fixes**
* Task timing now updates correctly during column moves, including
repeated returns to the same column.
* Existing data can be upgraded to the new timing format without
breaking stored tasks.

* **Tests**
* Added coverage for multi-step task movement and data reloading to
verify timing totals stay accurate.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 22:23:48 -07:00
gsxdsm
1d860ec310 feat: global concurrency slider (footer + dashboard) and scoped settings
Add a "Global Max Concurrent" slider to the footer engine menu and the
Command Center Concurrency card, and group the Scheduling settings by
Global vs Project scope so the global cap isn't mistaken for a per-project
setting (clearer on mobile).

Both sliders are backed by a single shared `useGlobalConcurrency` hook
(module-level store) so they read/write one source of truth and revalidate
after every PUT /api/global-concurrency — fixing the last-writer-wins and
stale-clobber races a per-component cache would cause. The hook treats a
fetch error as non-interactive (slider disabled, not stuck at 1), surfaces
a save-state indicator, and flushes a pending edit on menu close / unmount
so a quick drag-then-dismiss is never silently dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:19:24 -07:00
gsxdsm
84ec10d1a7 fix(review): harden global-dir fix per code review
Addresses code-review findings on the global-settings reset fix:
- getGlobalSettingsDir() now returns a resolved `string` (was `string |
  undefined`), so the project-local `.fusion` guard fires at the getter
  call site instead of leaking CentralCore's undefined-default semantics.
- getSecretsStore() passes the resolved global dir to MasterKeyManager so
  the master key co-locates with the global central DB and the path is
  exercisable under tests (a bare new MasterKeyManager() throws in VITEST).
- resolveGlobalDir() guard gains an explicit FUSION_ALLOW_PROJECT_LOCAL_GLOBAL_DIR
  opt-out so a legitimately version-controlled custom global dir (dotfiles
  repo with a .git parent) is not hard-rejected.
- Add a symptom-based regression test (store-secrets-store-global-dir) proving
  the secrets central DB lands in the global dir and never spawns a stray
  project-local fusion-central.db.
- Add getGlobalSettingsDir() to route-test mock stores (CentralCore is mocked,
  so it mirrors getFusionDir()) and FNXC comments to the remaining route sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:18:56 -07:00
gsxdsm
207d2b899a test: remove real-time waits from slow test files (FN-5048)
Replace real wall-clock waits and per-test rebuilds in the slowest test files
with deterministic seams. No assertions weakened, no timeouts widened, no
retries added — anti-pattern removal only.

- insights-routes.test.ts: boot the server + store ONCE in beforeAll (was a full
  createServer + TaskStore.init per test x24), reset insight tables per test for
  isolation, drive the sweeper via fake timers. Test-execution time ~3.7s -> ~0.8s.
- db.test.ts: convert the fixed 150ms write-lock hold to manual stdin signal-
  release; keeps the real OS-lock contention under test, removes 2x150ms dead
  wait. Fixed a real EPIPE on redundant release. 152 pass, non-flaky over 8 runs.
- mission-store.test.ts / agent-store.test.ts: replace real setTimeout sleeps
  used only to force distinct timestamps with a controlled clock (vi.setSystemTime
  / injected renewedAt). agent-store assertions strengthened to pin exact values.
- in-process-runtime.test.ts: fake the one real 25ms sleep, drop its inflated
  30s per-test timeout.

Honest note: the timestamp-sleep removals are small absolute wins (the headline
per-file durations were full-suite shard contention, not in-file dead time) but
eliminate the FN-5048 real-wait anti-pattern. workflow-routes.test.ts was
evaluated for splitting and deliberately NOT split — measured A/B showed the
split regressed wall-clock (the file is import/transform-bound, already amortized
by installInMemoryDbSnapshot), so splitting only multiplies fixed import cost.

Verified: core 612/612, dashboard 24/24, engine 78/78; typecheck + eslint clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 22:08:41 -07:00
gsxdsm
c20c4b7294 fix: stop global settings resetting via project-local central DBs
Production code constructed `new CentralCore(store.getFusionDir())`, pointing
the central/global DB at the project's `.fusion/` instead of `~/.fusion/`.
`resolveGlobalDir()` returns an explicit dir verbatim, so this spawned stray
per-project `fusion-central.db` files seeded with default global settings
(globalMaxConcurrent=4, empty secrets) that shadowed the real global DB
whenever a read/write hit one of those paths — surfacing as intermittent
"all my global settings reset".

- Add TaskStore.getGlobalSettingsDir() (resolved global dir; undefined→~/.fusion)
- Route the secrets store + secrets/proxy/node/secrets-sync/settings-sync
  dashboard routes through it instead of getFusionDir()
- Add a resolveGlobalDir() guard that throws on a project-local `.fusion/`
  dir (basename `.fusion` with a `.git` parent); inert under VITEST
- Regression tests in global-settings-guard.test.ts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 21:56:27 -07:00
gsxdsm
b6b5583f01 FN-7039: apply execution-lane models to workflow steps
Route workflow and automation prompt steps through the execution-lane model hierarchy.

- Use executor session model resolution for task workflow prompt steps while preserving per-step overrides.
- Use execution settings model resolution for scheduled and manual AI-prompt automation runs.
- Document the model precedence and add regression coverage plus a patch changeset.

Files changed:
 .changeset/fn-7039-workflow-execution-model.md     |   7 +
 docs/settings-reference.md                         |   2 +
 .../core/src/__tests__/model-resolution.test.ts    |  12 ++
 packages/dashboard/src/routes.ts                   |   6 +-
 .../engine/src/__tests__/executor-test-helpers.ts  |  11 +-
 .../__tests__/executor-workflow-step-model.test.ts | 234 +++++++++++++++++++++
 packages/engine/src/cron-runner.ts                 |   7 +-
 packages/engine/src/executor.ts                    |  23 +-
 8 files changed, 285 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7039

Fusion-Task-Lineage: f27c4be8-7793-4212-b9ee-679f50193406
2026-06-25 21:05:33 -07:00
gsxdsm
79c602d9d4 FN-7022: add MCP server configuration foundation
Add core MCP configuration primitives for secure server declarations and resolution.

- Add MCP server setting types, schema entries, validation, and project-over-global resolution.
- Add secret-reference materialization seams plus Claude Desktop import/export helpers.
- Cover MCP config behavior with core unit tests and document settings and secret handling.
- Add a changeset for the published CLI package.

Files changed:
 .changeset/fn-7022-mcp-core-foundation.md      |   7 +
 docs/secrets.md                                |   3 +-
 docs/settings-reference.md                     |  23 ++
 packages/core/src/__tests__/mcp-config.test.ts | 199 ++++++++++++++
 packages/core/src/index.ts                     |  30 +-
 packages/core/src/mcp-config.ts                | 366 +++++++++++++++++++++++++
 packages/core/src/settings-schema.ts           |  90 +++++-
 packages/core/src/settings-validation.ts       | 172 +++++++++++-
 packages/core/src/types.ts                     |  62 +++++
 9 files changed, 947 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7022

Fusion-Task-Lineage: 7bafd0b8-e4a5-4bc2-9a2a-43bfb934845c
2026-06-25 21:01:15 -07:00
gsxdsm
b663eebcb3 FN-7035: split oversized test suites
Split oversized ChatView and notifier suites while updating the line-count baseline.

- Move ChatView core contract and interaction coverage into focused sibling test files.
- Move notifier runtime coverage into its own suite and share setup through a test harness.
- Document the line-count guard decision and ratchet baseline entries for existing growth.

Files changed:
 .../__tests__/ChatView.core-contracts.test.tsx     |  623 ++++++++
 .../__tests__/ChatView.core-interactions.test.tsx  | 1261 +++++++++++++++
 .../components/__tests__/ChatView.core.test.tsx    | 1652 +-------------------
 .../engine/src/__tests__/notifier.runtime.test.ts  |  810 ++++++++++
 .../engine/src/__tests__/notifier.test-harness.ts  |   71 +
 packages/engine/src/__tests__/notifier.test.ts     |  847 +---------
 scripts/check-file-line-count.mjs                  |    3 +
 scripts/line-count-baseline.json                   |   12 +-
 8 files changed, 2778 insertions(+), 2501 deletions(-)

Fusion-Task-Id: FN-7035
Fusion-Task-Lineage: 14cebb57-925b-41c7-9c8b-472f34b76fe2
2026-06-25 20:50:30 -07:00
gsxdsm
6415eed0c1 FN-7034: Align steps dropdown trigger styling
Align the optional steps dropdown trigger with shared task creation button styling.

- Reuse the dashboard btn btn-sm classes for the workflow optional steps trigger.
- Remove bespoke trigger button styling so shared button tokens control padding, border, radius, and states.
- Extend dropdown tests to cover shared classes across empty, selected, and disabled states.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-7034-steps-dropdown-style.md         |  7 +++
 .../components/WorkflowOptionalStepsDropdown.css   | 14 ------
 .../components/WorkflowOptionalStepsDropdown.tsx   |  9 +++-
 .../WorkflowOptionalStepsDropdown.test.tsx         | 50 ++++++++++++++++++++--
 4 files changed, 61 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-7034

Fusion-Task-Lineage: 3ca2a726-2c67-44a0-9a88-467e4fc1ad6b
2026-06-25 20:28:38 -07:00
gsxdsm
4a4e389254 FN-7032: add signed Android release artifacts
Add secret-gated signed Android release packaging while preserving the unsigned fallback.

- Build signed Android release APK and AAB artifacts when keystore secrets are configured.
- Verify signed APKs, generate checksums for APK/AAB outputs, and include AABs in release aggregation.
- Document Android signing secrets, sideload verification, fallback artifacts, and Play upload scope.
- Cover the release and rehearsal workflow wiring with CLI and desktop workflow tests.

Files changed:
 .github/workflows/release.yml                      | 94 ++++++++++++++++++----
 .github/workflows/test-release.yml                 | 94 ++++++++++++++++++----
 MOBILE.md                                          | 18 ++++-
 RELEASING.md                                       | 34 ++++++--
 packages/cli/src/__tests__/ci-workflow.test.ts     | 24 ++++++
 packages/desktop/README.md                         |  2 +-
 .../desktop/src/__tests__/release-workflow.test.ts | 24 +++++-
 7 files changed, 251 insertions(+), 39 deletions(-)

Fusion-Task-Id: FN-7032
Fusion-Task-Lineage: 0334c026-384e-4531-a2b2-f8a0b5bb7ff0
2026-06-25 20:20:33 -07:00
gsxdsm
c0958bfd4f FN-7029: split AI merge prompts and worktree helpers
Split the clean-room AI merger into smaller focused modules while preserving its public behavior.

- Extract prompt builders and review verdict parsing into merger-ai-prompts.
- Extract AI merge worktree lifecycle and cleanup helpers into merger-ai-worktree.
- Re-export the extracted APIs from merger-ai and cover prompt/verdict behavior with tests.
- Remove the merger-ai line-count baseline now that the file is under the guardrail.

Files changed:
 .../engine/src/__tests__/merger-ai-prompts.test.ts |  86 ++++
 packages/engine/src/merger-ai-prompts.ts           | 312 ++++++++++++
 packages/engine/src/merger-ai-worktree.ts          | 287 +++++++++++
 packages/engine/src/merger-ai.ts                   | 555 ++-------------------
 scripts/line-count-baseline.json                   |   1 -
 5 files changed, 723 insertions(+), 518 deletions(-)

Fusion-Task-Id: FN-7029

Fusion-Task-Lineage: 59adc31f-7386-4008-b74f-8fb9bbae078a
2026-06-25 20:12:20 -07:00
gsxdsm
0ae44992df FN-7030: route graph tasks to shared pop-out
Route dependency Graph task opens through the shared movable task pop-out.\n\n- Send dependency-graph plugin task opens and rendered graph task cards to popOutTaskDetail.\n- Preserve fixed modal behavior for non-graph plugin dashboard views.\n- Document the Graph behavior and add regression coverage for desktop, mobile, and pop-out deduping.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-7030-graph-task-popout.md            |   7 +\n docs/dashboard-guide.md                            |   4 +-\n .../app/components/dashboard/MainContent.tsx       |  15 +-\n .../__tests__/MainContent.graph-popout.test.tsx    | 274 +++++++++++++++++++++\n 4 files changed, 296 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7030

Fusion-Task-Lineage: 83008d81-218e-49ce-9aff-cb6b51d84dba
2026-06-25 20:03:53 -07:00
gsxdsm
45727f1459 FN-7019: fix Command Center range filtering
Command Center analytics now honor picker presets and open-ended date bounds.\n\n- Serialize All time with an explicit upper bound and preserve one-sided custom/preset query params.\n- Resolve server analytics ranges as open windows for from-only and to-only requests instead of defaulting them away.\n- Cover picker query serialization and range-consuming Command Center endpoints with regression tests.\n- Document the restored picker contract and add a patch changeset.\n\nFiles changed:\n .changeset/fn-7019-command-center-range.md         |  7 ++\n docs/dashboard-guide.md                            |  3 +-\n .../components/command-center/DateRangePicker.tsx  | 12 ++-\n .../command-center/areas/__tests__/areas.test.tsx  | 54 ++++++++++++-\n .../components/command-center/areas/areaShared.ts  |  8 +-\n .../register-command-center-routes.test.ts         | 92 +++++++++++++++++++++-\n .../src/routes/register-command-center-routes.ts   | 28 ++++---\n 7 files changed, 183 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-7019

Fusion-Task-Lineage: 327f8f45-8ad8-4103-8cc4-efcf2af6a73a
2026-06-25 19:57:49 -07:00
gsxdsm
e48f80b764 FN-7028: split AgentLogViewer tests by concern
Split the large AgentLogViewer suite into focused, line-count-friendly test files.

- Move header, layout, markdown, and rendering coverage into separate test modules.
- Add a shared AgentLogViewer test helper for reusable setup.
- Remove the oversized combined AgentLogViewer test and update the line-count baseline.

Files changed:
 .../__tests__/AgentLogViewer.header.test.tsx       |  490 +++++
 .../__tests__/AgentLogViewer.layout.test.tsx       |  367 ++++
 .../__tests__/AgentLogViewer.markdown.test.tsx     |  797 ++++++++
 .../__tests__/AgentLogViewer.rendering.test.tsx    |  410 ++++
 .../__tests__/AgentLogViewer.test-helpers.ts       |   16 +
 .../components/__tests__/AgentLogViewer.test.tsx   | 2010 --------------------
 scripts/line-count-baseline.json                   |    1 -
 7 files changed, 2080 insertions(+), 2011 deletions(-)

Fusion-Task-Id: FN-7028

Fusion-Task-Lineage: 5ef134c7-0969-4b9b-908c-9713daa9d0d2
2026-06-25 19:51:21 -07:00
gsxdsm
8a03e4fc23 feat(core): per-stage column dwell instrumentation
Adds `columnDwellMs?: Record<string, number>` to Task — a per-column
accumulator (column name -> cumulative ms) recorded at the same store
column-transition seam as `cumulativeActiveMs`. On every move it adds
`columnMovedAt(new) - columnMovedAt(prev)` to the bucket for the column
being left, clamped >= 0; unparseable/missing prior timestamps and 0-dwell
moves are skipped, and second visits add to the existing bucket.

Motivation: `cumulativeActiveMs` only measures in-progress time. Diagnosis
of slow tasks showed the dominant wall-clock is *waiting* (queue time in
todo, review wait in in-review), which previously had to be reconstructed
from agent logs. This makes per-stage dwell directly queryable, like
productivity-analytics already consumes cumulativeActiveMs.

Persisted as a JSON-text task column following the v129 workspaceWorktrees
precedent: SCHEMA_SQL column + SCHEMA_VERSION 129->130 + versioned
addColumnIfMissing migration. Additive and behavior-preserving; pre-existing
rows start NULL and accumulate from their next transition. Survives
archive/restore. @fusion/core is private — no changeset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 19:39:02 -07:00
gsxdsm
ea3cfeeac4 FN-7027: constrain skill list row text
Keep discovered skill rows within the left pane by truncating long text values.

- Wrap skill names in a truncatable text span while keeping chevrons fixed-size.
- Add ellipsis rules and compact font sizing for skill names, paths, and sources.
- Cover long, short, and empty metadata skill rows with a dashboard test.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7027-skill-list-truncation.md        |  7 +++
 packages/dashboard/app/components/SkillsView.css   | 28 ++++++++++
 packages/dashboard/app/components/SkillsView.tsx   |  2 +-
 .../app/components/__tests__/SkillsView.test.tsx   | 64 ++++++++++++++++++++++
 4 files changed, 100 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7027

Fusion-Task-Lineage: ae50f1aa-e58b-4374-abae-38ca28e4073a
2026-06-25 19:35:47 -07:00
gsxdsm
775a1f8e9a FN-7020: compact AI session input banner
Make the dashboard AI session needs-input banner less prominent while preserving visibility rules.

- Reduce the session notification banner spacing, text scale, max heights, and mobile density.
- Cover banner visibility on missions, planning, hidden, empty, and board-session states.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7020-session-banner-compact.md       |   7 +
 .../app/components/SessionNotificationBanner.css   |  47 +++--
 .../dashboard/__tests__/DashboardBanners.test.tsx  | 227 +++++++++++++++++++++
 3 files changed, 263 insertions(+), 18 deletions(-)

Fusion-Task-Id: FN-7020
Fusion-Task-Lineage: bd48da60-29ef-4871-ae27-8a1a48f744ea
2026-06-25 19:35:05 -07:00
gsxdsm
541f1f6533 FN-7017: expose optional workflow steps in task creation
Surface active workflow optional steps on task creation surfaces so users can opt into workflow-specific steps before creating tasks.

- Add optional-step dropdown state and creation payload handling to quick entry.
- Move New Task optional-step controls into the inline quick-button row.
- Cover default-on, workflow selection, duplicate-confirmed create, empty-step rendering, and mobile touch-target behavior with tests.
- Document create-time optional-step controls and add the published package changeset.

Files changed:
 .../fn-7017-optional-steps-quick-dropdown.md       |   7 +
 docs/dashboard-guide.md                            |   4 +-
 docs/workflow-steps.md                             |   2 +
 .../dashboard/app/components/QuickEntryBox.css     |   6 +-
 .../dashboard/app/components/QuickEntryBox.tsx     |  65 ++++++++-
 packages/dashboard/app/components/TaskForm.tsx     |  34 ++---
 .../app/components/__tests__/NewTaskModal.test.tsx |  21 ++-
 .../components/__tests__/QuickEntryBox.test.tsx    | 145 ++++++++++++++++++++-
 .../app/components/__tests__/TaskForm.test.tsx     |   1 +
 9 files changed, 258 insertions(+), 27 deletions(-)

Fusion-Task-Id: FN-7017

Fusion-Task-Lineage: 1b7d21e6-ff7f-4d27-b285-4fd865eafce6
2026-06-25 19:35:05 -07:00
gsxdsm
f5b588cab6 FN-7018: retry transient ntfy notification failures
Ntfy sends now retry bounded transient failures before giving up.

- Add per-attempt timeouts and bounded retries for ntfy network, timeout, 5xx, and 429 failures.\n- Preserve best-effort abort behavior while retrying only retryable publish failures.\n- Cover retry, timeout, abort, payload, and non-retryable failure behavior in notifier tests.\n- Add a patch changeset for the published Fusion package.\n\nFiles changed:\n .changeset/fn-7018-ntfy-retry.md               |   7 +\n packages/engine/src/__tests__/notifier.test.ts | 259 ++++++++++++++++++++++++-\n packages/engine/src/notifier.ts                | 173 +++++++++++++----\n 3 files changed, 399 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-7018

Fusion-Task-Lineage: 0d0b7313-532a-4583-8bb7-67fc4b2e98bf
2026-06-25 19:35:05 -07:00
gsxdsm
f9f767c46a FN-7007: speed up SettingsModal interaction tests
Optimize the split SettingsModal test suite to avoid default user-event overhead while preserving interaction coverage.

- Add a shared no-delay SettingsModal test user with pointer-event tree checks disabled.
- Route split SettingsModal tests through the shared fast user and use fireEvent for simple form mutations.
- Shorten modal readiness checks to wait on settings loading instead of querying Save repeatedly.

Files changed:
 .../__tests__/SettingsModal.general.test.tsx       | 124 +++++++--------
 .../__tests__/SettingsModal.models-auth.test.tsx   | 136 ++++++++--------
 .../SettingsModal.remote-notifications.test.tsx    |  61 ++++----
 .../SettingsModal.scheduling-merge.test.tsx        | 174 ++++++++++-----------
 .../__tests__/SettingsModal.test-harness.tsx       |  32 ++--
 .../__tests__/SettingsModal.testMode.test.tsx      |   5 +-
 .../__tests__/SettingsModal.worktrunk.test.tsx     |   5 +-
 .../__tests__/SettingsModalNodeRouting.test.tsx    |   7 +-
 8 files changed, 273 insertions(+), 271 deletions(-)

Fusion-Task-Id: FN-7007

Fusion-Task-Lineage: 42cf8ce2-3da7-4607-8790-5bf46c07b417
2026-06-25 19:35:05 -07:00
gsxdsm
dba65933e5 FN-7014: publish Android APK release artifacts
Adds Android APK generation to binary release and rehearsal workflows so GitHub releases ship mobile assets.

- Add Android build jobs that sync Capacitor, assemble the debug APK, and upload APK/checksum artifacts.
- Include Android artifacts in release and test-release collection dependencies and file matching.
- Document Android release outputs and extend workflow shape tests for the new asset path.

Files changed:
 .github/workflows/release.yml                      | 86 +++++++++++++++++++++-
 .github/workflows/test-release.yml                 | 85 ++++++++++++++++++++-
 MOBILE.md                                          |  2 +-
 RELEASING.md                                       | 10 ++-
 packages/cli/src/__tests__/ci-workflow.test.ts     |  6 +-
 packages/desktop/README.md                         |  1 +
 .../desktop/src/__tests__/release-workflow.test.ts | 24 +++++-
 7 files changed, 201 insertions(+), 13 deletions(-)

Fusion-Task-Id: FN-7014
Fusion-Task-Lineage: 8ed94f64-399b-433f-add4-0294fc5722d1
2026-06-25 19:35:05 -07:00
gsxdsm
90c0e9936e FN-7009: update dashboard tests for current labels
Refresh dashboard test expectations so they follow current project setup and theme metadata.

- Assert setup wizard registration payloads include workspace and task-prefix fields.
- Resolve default theme assertions through shared theme metadata instead of hardcoded labels.
- Update Command Center theme dropdown coverage to use the current default theme label.

Files changed:
 .../app/components/__tests__/SetupWizardModal.test.tsx   | 16 +++++++++-------
 .../app/components/__tests__/ThemeSelector.test.tsx      | 11 ++++++-----
 .../__tests__/CommandCenterControls.test.tsx             | 12 +++++++-----
 3 files changed, 22 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7009
Fusion-Task-Lineage: 1255f1b5-b63d-4366-afe6-5d72ca15cb1a
2026-06-25 19:35:04 -07:00
gsxdsm
4ea6084322 feat: make Code Review a default-on toggleable optional-group step
Refinement: Code Review is now a DEFAULT-ON but toggleable `optional-group` in the
built-in coding and stepwise coding workflows (defaultOn:true), not a standard
always-on node. It is part of the existing pre-merge flow (execute →
[browser-verification optional] → code-review → review) and runs for every coding
task by default, yet an operator can toggle it off per task by removing `code-review`
from enabledWorkflowSteps; disabled → byte-inert pass-through. Advisory gateMode keeps
it non-blocking (operators can promote to a gate); toolMode readonly.

- Restore the optional-group builder (builtin-code-review-node.ts → -group.ts) with
  config.defaultOn:true; stable group id `code-review`, inner id `code-review-step`.
- Wire the default-on optional-group into both built-in coding IRs.
- Fix store default-workflow seeding: interpreter-deferred built-ins (which carry
  optional-group nodes) previously bailed to `undefined` in
  materializeDefaultWorkflowSteps, dropping default-on group seeding under a
  project-default workflow. Now they seed resolveDefaultOnOptionalGroupIds, mirroring
  the explicit-workflow path, so defaultOn:true actually takes effect (the executor
  enables a group strictly via enabledWorkflowSteps.includes(node.id)).
- Update tests + changeset; full @fusion/core suite green (356 files).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:16:59 -07:00
gsxdsm
a50074d411 feat: make Code Review a standard always-on pre-merge step
Design correction: Code Review is now a STANDARD, default-ON step in the
built-in coding and stepwise coding workflows — not a default-off optional-group
toggle. It is a regular advisory `prompt` node on the pre-merge success path
(execute → [browser-verification optional] → code-review → review), so it runs
for every coding task with no enabledWorkflowSteps gating. Advisory gateMode means
it does not change merge outcomes; operators can promote it to a blocking gate.

- Replace the optional-group module with a standard prompt-node builder
  (builtin-code-review-group.ts → builtin-code-review-node.ts).
- Keep the `code-review` WORKFLOW_STEP_TEMPLATE in the catalog (editor palette).
- Edges unchanged: code-review → review on success, code-review → end on failure
  (mirrors the existing review node, no dead-end).
- Update tests + changeset for the standard always-on (no-toggle) semantics.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:57:08 -07:00
gsxdsm
7772ab3647 feat: add built-in Code Review pre-merge workflow step
Add a configurable "Code Review" diff-review step to the built-in coding and
stepwise coding workflows as a default-OFF optional-group prompt gate. It reuses
the existing workflow-step machinery and the shared trailing-verdict convention
(REVISE blocks, APPROVE/APPROVE_WITH_NOTES pass) — no engine verification code.

- New `code-review` WORKFLOW_STEP_TEMPLATE (toolMode readonly, gateMode advisory,
  phase pre-merge) focused on the correctness value tests miss: logic bugs, edge
  cases, intent-vs-implementation drift, regressions, error handling, contracts.
- New builtin-code-review-group.ts mirroring builtin-browser-verification-group.ts
  (stable group id `code-review`, distinct inner node id `code-review-step`).
- Wired into builtin-coding-workflow-ir.ts and builtin-stepwise-coding-workflow-ir.ts
  on the pre-merge path next to browser-verification, default OFF / opt-in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:34:50 -07:00
gsxdsm
977000ca99 FN-7012: restore mobile task tab scrolling
Restores mobile access to all task detail tabs by making the tab strip the horizontal scroller.

- Allow the task-detail content, body, modal tabs, tablet tabs, and embedded tabs to shrink within narrow containers.
- Preserve touch horizontal panning and momentum scrolling on tab strips without moving horizontal overflow to the detail body.
- Cover the Board modal, List embedded pane, and complete tab label set with responsive CSS regression tests.
- Add a patch changeset for the published Fusion package.

Files changed:
 .../fn-7012-task-detail-mobile-tabs-scroll.md      |  7 +++
 .../dashboard/app/components/TaskDetailModal.css   | 25 +++++++-
 ...etailModal.responsive-and-dependencies.test.tsx | 67 +++++++++++++++++++++-
 3 files changed, 96 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7012

Fusion-Task-Lineage: 06b06a81-b9ec-4058-879f-2f5e187a63a4
2026-06-25 17:26:16 -07:00
gsxdsm
65a2b3bc72 feat: add test-free verify:fast (typecheck + build + boot-smoke) (#1777)
## What this adds

A **test-free verification command** — `pnpm verify:fast`
(`scripts/verify-fast.mjs`) — that gives deterministic, flake-free
signal without running the test suite. It is fully **additive**: `pnpm
test`, the merge gate (`test:gate`), and CI are untouched.

`docs/testing.md` observes the broad test gate "caught no recalled real
bugs while consuming ~70% of shipping time in flake triage."
`verify:fast` is the opt-in path for non-test verification, suitable as
a project `testCommand`/verification command.

## What verify:fast runs

1. **typecheck — scoped to the changed packages** (each package's
`typecheck` script, or `pnpm --filter <pkg> exec tsc --noEmit -p .` when
none exists).
2. **build — scoped to the changed packages** (`pnpm --filter <pkg>
build`, only for packages that declare a build script).
3. **boot smoke once** (`scripts/boot-smoke.mjs`: CLI `--help` + a real
`fn serve` answering `GET /api/health`), after builds so it runs against
fresh artifacts.

Change-detection **reuses `scripts/test-changed.mjs`** (`getBaseBranch`
/ `detectComparisonBase` / `changedFilesSince` /
`resolveAffectedPackages` / workspace resolution — newly `export`ed)
instead of reinventing git-diff, so it scopes to exactly the packages a
changed-only test run would. With no affected package (root/docs-only
diff) it runs the boot smoke only. Each step is bounded by the existing
`runWithWatchdog` (class `changed`) so a hung tsc/build/serve fails
fast; it streams progress and exits nonzero on the first failing step.
`@fusion/desktop` and `@fusion/mobile` are skipped, mirroring the root
`build`/`typecheck` exclusions.

## Measured wall-time

On this branch's diff (which resolves to the heaviest package,
`@fusion/dashboard`), end-to-end:

```
[verify:fast] plan: typecheck:@fusion/dashboard -> build:@fusion/dashboard -> boot-smoke
[verify:fast]    OK typecheck @fusion/dashboard (~44s)
[verify:fast]    OK build @fusion/dashboard (26.2s)
[verify:fast]    OK boot smoke (CLI --help + real serve /api/health) (19.6s)
[verify:fast] PASS — 3 step(s) green in 90.3s (no tests run).
```

**~90s total**, deterministic and flake-free. By contrast a typical
**scoped test run for the same package** is far heavier and flake-prone:
`docs/testing.md` notes a dashboard task "otherwise re-ran all 822
dashboard test files (~5-8 min)", and `pnpm test` additionally runs the
merge-gate suite first. verify:fast trades that test-suite cost (and its
flake-triage tax) for a typecheck+build+boot signal in ~1.5 min.

## Doc additions

- `AGENTS.md` + `docs/testing.md` testing-commands lists now include
`pnpm verify:fast`, described as the recommended **test-free
verification** (typecheck + build + boot-smoke), suitable as a project
`testCommand`/verification command; the full suite stays available and
runs non-blocking.

## Tests / verification

- New `scripts/__tests__/verify-fast.test.mjs` (11 tests) pins the pure
planning / arg-construction logic — scoped typecheck/build selection,
build-script gating, desktop/mobile exclusion, boot-smoke-only fallback,
and reuse of `resolveAffectedPackages`. It never spawns real
tsc/build/vitest.
- `pnpm verify:fast` runs end-to-end and exits 0 (output above).
- Lint clean on all new/changed files; `agents-md-invariants`,
`check-test-inventory`, `verify-fast`, and `test-changed` script tests
all green (132 tests).

No changeset (scripts + docs + CI-tooling, behavior-additive;
`@runfusion/fusion` runtime unaffected).

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1777">
  <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 -->
2026-06-25 17:24:25 -07:00
gsxdsm
55b9c548ad test: quarantine 3 failing CI full-suite tests (#1775)
## Summary

Quarantine 3 test files consistently failing on the non-blocking
full-suite CI on `main`. Per the AGENTS.md deletion-ratchet policy, each
is added to `scripts/lib/test-quarantine.json` with a matching exclude
in its package's vitest config. Tests will be deleted after 14 days
unless rescued with a root-cause fix.

## Quarantined Tests

| File | Shard | Failure | CI Run |
|------|-------|---------|--------|
|
`engine/src/__tests__/self-healing-fn-5488-fast-path-regressions.test.ts`
| 1/4 | `expected +0 to be 1` + `parseFileScopeFromPrompt is not a
function` | [run
28206337202](https://github.com/Runfusion/Fusion/actions/runs/28206337202)
|
| `engine/src/__tests__/in-review-merge-stall-deadlock-recovery.test.ts`
| 2/4 | `expected 'FN-5485' to be null` | [run
28206337202](https://github.com/Runfusion/Fusion/actions/runs/28206337202)
|
| `dashboard/app/components/__tests__/DevServerView.mobile.test.tsx` |
4/4 | `expected +0 to be 1` (mobile CSS structure) | [run
28206337202](https://github.com/Runfusion/Fusion/actions/runs/28206337202)
|

## Verification

- `pnpm test:gate` passes (313 core + 58 ci-shape tests)
- `pnpm lint` clean


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1775">
  <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

* **Chores**
* Updated the test quarantine list to exclude several flaky or failing
tests from routine dashboard and engine test runs.
* Added records for newly quarantined tests, including the date they
were marked and the CI issue they were linked to.
* Continued using the quarantine list across relevant test projects to
keep CI runs more stable.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 17:22:23 -07:00
gsxdsm
0346ea0f6b perf: snapshot migrated in-memory DB to skip per-test migrations (#1774)
## Problem

Hundreds of DB-backed tests build a fresh in-memory store in
`beforeEach` and call `db.init()`, which replays `SCHEMA_SQL` + **~129
migrations every single test** (~40ms each). Across thousands of tests
this is minutes of pure setup. The slowest core store suites are
dominated entirely by this.

## Approach (Option A: serialize/deserialize snapshot)

`node:sqlite` (and `bun:sqlite`) expose `serialize()`/`deserialize()`. I
migrate **one** in-memory DB per test file, serialize it to a byte
buffer, and register it via a test-only hook. Every subsequent in-memory
`Database` deserializes the snapshot at open time, so `init()` finds
`schemaVersion === SCHEMA_VERSION` plus the matching compat fingerprint
and short-circuits `migrate()` + all backfills.

Why this over the existing truncate-based
`createSharedTaskStoreTestHarness`: the snapshot keeps the **exact same
per-test isolation model** — each test still constructs its own
brand-new, fully-isolated DB — so suites that reassign their store/db
inside test bodies (both targets do) need no restructuring. Only the
migration cost is amortized. Disk-backed (production) DBs are never
touched; the hook is `null` in production, so behavior is unchanged.

### Harness API

```ts
beforeAll(() => installInMemoryDbSnapshot());
afterAll(() => clearInMemoryDbSnapshot());
// existing per-test `new <Store>({ inMemoryDb: true }); init()` stays as-is
```

- `packages/core/src/__tests__/store-test-helpers.ts` — core suites
- `packages/dashboard/src/__tests__/db-snapshot-helper.ts` — dashboard
suites (core `__tests__` is a private cross-package dir, so it mirrors
via the new public `setInMemoryTemplateSnapshot` export)

## Before / after

Raw `db.init()` microbenchmark: **43.4ms → 5.4ms (8x)**.

| Suite | Tests | Before | After | Note |
|---|---|---|---|---|
| `agent-store.test.ts` | 199 | 13.12s | **3.32s** | ~4x; init-dominated
|
| `mission-store.test.ts` | 261 | 17.62s | **5.69s** | ~3x; min of 3 |
| `workflow-routes.test.ts` | 53 | tests 4.38s | **tests 2.79s** | min
of 5; not init-dominated, so a smaller (~36%) but real win — most of its
time is express/route logic, not DB init |

All converted suites pass with **0 failures** and every original
assertion preserved. `db.test.ts` (which tests init/migration directly)
is intentionally left unconverted and still passes. Core + dashboard
typecheck clean; lint clean.

> Honest note: `workflow-routes` machine timings were noisy (same config
varied 5–13s under load); the `tests`-portion min-of-5 is the reliable
signal. The snapshot helps every in-memory suite, but the suite-level
win scales with how init-dominated the suite is.

## No changeset

Changes are test-infra only and behavior-preserving for the published
bundle — the snapshot hook is a no-op (`null`) in production. Per
AGENTS.md, no changeset for behavior-preserving/internal changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1774">
  <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

* **Chores**
* Improved in-memory database handling for test runs by reusing a
prepared snapshot instead of rebuilding it repeatedly.
* Added snapshot support to the database layer and SQLite adapter to
speed up initialization in test environments.
* Updated core and dashboard test suites to use shared setup/teardown
for the cached database state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 17:22:08 -07:00
gsxdsm
88a2dbf59b test: split ChatView.test.tsx into 3 parallel files + fix FN-4327 isolation bug (#1773)
## What

Splits the giant
`packages/dashboard/app/components/__tests__/ChatView.test.tsx` (**231
tests, ~24s, one sequential worker**) into 3 parallelizable sibling
files sharing a harness — the same pattern already used for
`SettingsModal` — and fixes a latent test-isolation bug it exposed.

### Split (sum unchanged = 231)
| File | tests |
|------|------|
| `ChatView.core.test.tsx` | 140 |
| `ChatView.sessions-rooms.test.tsx` | 40 |
| `ChatView.mobile.test.tsx` | 51 |

`ChatView.test-harness.tsx` exports fixtures, helpers (`renderWithAct`,
`setupMockChat`, `setupMockRooms`, `mockViewportMode`,
`renderRoomCreation`, …), the `vi.mocked` handles, and
`installChatViewEnv()` (the former file-level `beforeEach`/`afterEach`).
The `vi.mock(...)` factories stay **inline & self-contained** in each
test file — delegating them to a harness export triggers a TDZ
`ReferenceError` because the harness imports `ChatView`/`../../api`.

### FN-4327 isolation bug (root cause + fix)
`Direct/Rooms scope toggle > "FN-4327: switching scope from Rooms to
Direct re-anchors direct thread"` failed standalone/split with `expected
500 to be 1200`.

**Root cause:** the Direct↔Rooms toggle swaps subtrees in ChatView's
render (`chatScope` ternary), so the `.chat-messages` container
**unmounts** on entering Rooms and a **fresh node mounts** on returning
to Direct. The re-anchor effect (`anchorToBottom` on scope change)
correctly targets that remounted node — whose jsdom `scrollHeight` is
`0`. The pre-split file mocked geometry on the *pre-toggle* node and
only passed via a timing race against the remount (confirmed:
`sameNode=false`, `afterScrollHeight=0`).

**Fix (no assertion weakening):** install scroll geometry at the
prototype level (restored in `finally`, per-node `scrollTop` backing) so
whichever `.chat-messages` node is live — including the remounted one —
reports `scrollHeight 1200`, making the re-anchor deterministic.

### vitest.config.ts
- Repoint `dashboard-app-quality-chat` (`qualityAppChatOnlyTests`) to
the 3 new files.
- Drop bare `"ChatView"` from `qualityAppComponentTests` and
`isolatedQualityAppComponentTests`.
- Spread `qualityAppChatOnlyTests` into `backfillAppExclude` (mirroring
the settings split) so the files aren't double-collected by the app
backfill lane.

## Verification
- 3 new files together: **231 passed / 0 failed**, stable across 2 runs.
- FN-4327 passes **standalone** (`-t "FN-4327"`) **and** in the full
split run.
- `vitest list --filesOnly` → each new file collected by exactly **one**
project (`dashboard-app-quality-chat`); harness not collected; no
backfill double-collection.
- ESLint on all 4 new files: exit 0.
- Combined Duration ~**17s** (vs original ~24s); in CI the 3 files
parallelize across workers within the chat project.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1773">
  <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

* **Bug Fixes**
* Improved chat experience reliability across mobile and desktop,
including session switching, room navigation, keyboard behavior,
scrolling, and sidebar interactions.
* Strengthened coverage for room creation, scope switching, and message
refresh behavior to help prevent regressions.
* **Chores**
* Reorganized automated checks to run chat-related scenarios more
consistently and avoid duplicate test collection.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 17:21:33 -07:00