Gateway 502/503/504 non-JSON bodies (for example Traefik "no available server")
were dumped as content-type diagnostics into the Planning error banner.
## The fix
`app/api/client.ts` builds request headers through a `Headers` object —
which lower-cases every key — and stamps `x-fusion-client: dashboard-ui`
on every dashboard-originated request so the server can attribute the
caller (the FN-8609 delete-attribution surface).
This test asserted `"Content-Type"`, and `expect.objectContaining`
compares keys **case-sensitively**, so it failed on casing alone.
Rather than only lower-casing the key, it now pins the **client stamp**
too — that header is the point of the feature and nothing else in this
file covered it.
| Check | Result |
|---|---|
| `plugin-setup-api.test.ts` | 1 failed → **3 passed** |
| client stamp neutered in `client.ts` | **1 failed** / 2 passed —
load-bearing |
| `pnpm lint`, dashboard app `tsc` | clean |
## Method correction — I nearly filed 40 phantom failures
Measuring this package with a raw `vitest run` reports **~40 failing
files**. That number is worthless: `@fusion/dashboard`'s own `test`
script is `node scripts/run-quality-tests.mjs`, which runs the quality
projects as **separate invocations** with per-group heap sizes and
exclusions. Running every project in one process fails en masse for
reasons unrelated to the code.
Correct command — `pnpm --filter @fusion/dashboard test` — gives **9
failing files / 88 failing tests**, exit 1.
## The finding: those 88 failures are never executed in CI
I first concluded "CI shows no dashboard failures, so these are
local-only." **That was wrong, and the reason matters.**
CI does schedule the dashboard quality groups — they are distributed
across all four shards as individual `test:quality:*` invocations. Shard
2's plan, for example:
```
[ci-test-shard] shard 2/4: @fusion/core [2/2], @fusion/dashboard run test:quality:app:components-b,
@fusion/dashboard run test:quality:app:backfill-1, ...
```
But only two invocations ever get a `(watchdog budget 1500s)` start
line: the plugins group and `@fusion/core [2/2]`. `components-b` never
starts, because the shard aborts on the first failing package —
`ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL`, present in shards 1, 2 and 4.
**So the dashboard quality groups are not passing — they are unrun**,
behind a package that fails first. Consequences:
1. **Fixing core/engine/CLI will unmask 88 dashboard failures.** My
merged PRs move shards 1/2/4 toward green; as each earlier package stops
failing, these groups begin executing for the first time. Expect the
shard counts to *rise* before they fall — that is progress, not
regression.
2. **Reading shard conclusions is misleading.** A shard says "core
failed"; it does not say "and everything scheduled after core never
ran."
Where the 88 live (all files currently unowned):
| File | Failures | Lane / shard |
|---|---:|---|
| `TaskDetailModal.inline-editing-and-integrations` | 50 |
`components-b` / shard 2 |
| `auto-merge-toggle-blank.mobile-integration` | 13 | `components-a` /
shard 3 |
| `auto-merge-toggle-blank.mobile` | 8 | `components-a` / shard 3 |
| `TaskDetailModal` | 6 | `components-b` / shard 2 |
| `SecretsView` | 4 | `components-b` / shard 2 |
| `WorkflowNodeEditor` | 3 | `components-b` / shard 2 |
| `TaskCard` + `TaskCard.badge-wrap` | 3 | `components-b` / shard 2 |
| `board-mobile` | 1 | `components-a` / shard 3 |
Dominant symptoms: `Unable to fire a "click" event - please provide a
DOM element` (29), `Unable to find an accessible element with the role
"checkbox" and name "Auto-merge"` (13), `expected null to be truthy` (7)
— consistent with a small number of shared render/affordance causes
rather than 88 independent bugs, but I have not isolated them.
**I am not starting that repair in this PR.** It is a 9-file, 88-test
area needing per-cluster diagnosis, and bundling it behind a one-line
header fix would produce exactly the shallow work this program keeps
rejecting. Filed here with the correct measurement command, the
lane/shard mapping, and the reason CI has been silent about it.
Three related fixes, all originating from a `[api:error] Request failed`
log line showing a 500 on `GET /api/tasks/FN-8610/runtime-fallback`.
1. Missing/deleted tasks now return 404 instead of 500.
`getTaskImpl` signalled a miss with a bare `Error`, and route catches
only mapped errno `ENOENT` to 404 — a leftover from the file-backed
storage era. In Postgres mode nothing sets an errno code, so every
unknown/missing/soft-deleted/wrong-project read returned 500. Adds a
typed `TaskNotFoundError` (message byte-identical) plus a shared
`task-lookup-error` mapper applied across the task, session-diff,
git/GitHub, workflow and file-workspace route registrars. The same
bare throw existed on both archive-lifecycle delete paths, so
`DELETE /tasks/:id` was affected too.
2. 5xx logs now carry the origin stack.
`rethrowAsApiError` constructed a fresh `ApiError` from the message
and discarded the original, so the `FNXC:ApiErrorDiagnostics`
contract logged the rethrow site rather than the throw site — the
reported log entry had no stack at all. Threads `cause` through the
error factories and walks the chain (bounded, cycle-guarded).
3. Task deletions are attributable, and non-operator deletes notify.
`task:deleted` audit rows recorded `agentId: "system"` for every HTTP
delete, making an operator click indistinguishable from a script or
an agent; the calling agent's task id was accepted by the store and
then never persisted. Adds a `callerKind` union recorded in audit
metadata, tags every delete call site, and stamps a self-reported
`x-fusion-client` header from the dashboard client. When the caller
is `agent-tool` or `api-unattributed`, a best-effort notice is sent
to the operator mailbox; operator and engine deletes stay silent.
`x-fusion-client` is attribution, not authentication — anything can send
it. No delete-blocking, gating or permission logic is added here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Summary
Task cards now remove the overseer eye as soon as their workflow's
effective oversight becomes `off`, instead of reusing an active value
cached before the setting changed.
The invalidation covers card remounts as well as mounted cards, and
authoritative writes from the dashboard, agents, and configuration
rollback reach the board through the existing project-scoped SSE stream.
Older in-flight responses cannot restore the eye after a newer `off`
value wins, while unrelated workflow-setting saves leave active
indicators undisturbed.
## Validation
- 99 focused dashboard regression tests passed across selected and
aggregate cards, desktop and mobile, SSE delivery, and out-of-order
responses
- Core and dashboard typechecks passed
- `pnpm lint` and `pnpm check:changesets` passed
- `pnpm verify:fast` passed production builds and the CLI/server boot
smoke
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Planner-overseer eye badges now disappear immediately when workflow
oversight is turned off.
- Prevented stale or out-of-order updates from displaying incorrect
oversight status.
- Oversight indicators now remain hidden when the effective setting
cannot be confirmed.
- Live workflow setting changes now update task cards without requiring
a page refresh.
- **Documentation**
- Clarified eye badge visibility rules, tooltip meaning, and active
oversight states in the dashboard guide.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Follow-up to PR #2086 addressing two Greptile review findings.
## P2 — Missing `psql` binary guard (Greptile P2)
`hasPg` in `_helpers.ts` previously checked only TCP connectivity to
PostgreSQL. But `adminExecAsync()` shells out to the `psql` CLI for DDL
(`CREATE/DROP DATABASE`). On a runner where Postgres is reachable but
`psql` isn't installed, tests would fail with `spawn psql ENOENT`
instead of skipping cleanly.
**Fix**: Added `hasPsql = spawnSync("psql", ["--version"]).status === 0`
to the `hasPg` guard, so tests skip when either Postgres is unreachable
OR `psql` is missing.
## P1 — Expired quarantine entries (Greptile P1)
The 16 dashboard test files quarantined on 2026-06-25 were past the
14-day deletion ratchet (AGENTS.md: "DELETED after 14 days unless
rescued"). Per the ratchet, the test files were deleted and all
references removed:
- **Deleted 16 test files** (CSS drift, mock drift, mobile-render
regressions)
- **Removed 16 entries** from `scripts/lib/test-quarantine.json` (only
the CLI entry remains)
- **Emptied `quarantinedDashboardTests` array** in
`packages/dashboard/vitest.config.ts`
## Verification
| Check | Result |
|---|---|
| Merge gate (`pnpm test:gate`) | ✅ 294 + 99 + 63 = 456 passed |
| Dashboard curated-gate | ✅ passes (891 files, 892 executed, 1
skip-listed, 1 quarantined) |
| Typecheck (engine) | ✅ clean |
| Lint | ✅ exit 0 |
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Removed multiple outdated dashboard UI, CSS/token, theme contrast, and
API/route test suites.
* Updated dashboard test configuration to stop excluding quarantined
tests and to prune the quality shard to the current set.
* Updated the Vitest split/config guard to match the new test fixture
set.
* Improved PostgreSQL test detection by requiring the `psql` CLI before
running database checks.
* Adjusted quarantine tracking by adding a new CLI extension
distribution ledger entry and removing obsolete dashboard quarantine
entries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Make the AI mission planning workspace movable on desktop and safer when streams fail.
- Host the Plan Mission with AI modal in FloatingWindow with desktop drag/resize geometry and mobile full-screen preservation.
- Normalize terminal mission interview stream failures, close SSE/keepalive once, and suppress duplicate late terminal events.
- Cover modal geometry and stream-error behavior with dashboard tests and document the operator-facing behavior.
Files changed:
.changeset/fn-6975-mission-modal-stream.md | 7 ++
docs/dashboard-guide.md | 7 ++
.../api/__tests__/mission-interview-stream.test.ts | 98 ++++++++++++++++++
packages/dashboard/app/api/legacy.ts | 66 ++++++++++---
.../app/components/MissionInterviewModal.css | 51 ++++++++++
.../app/components/MissionInterviewModal.tsx | 37 ++++---
.../__tests__/MissionInterviewModal.test.tsx | 110 ++++++++++++++++++++-
7 files changed, 344 insertions(+), 32 deletions(-)
Fusion-Task-Id: FN-6975
Fusion-Task-Lineage: b2ffa558-8b7e-4a46-8882-f2c4f6189831
Adds a regression test for the PrInfo endpoint to guard against FN-4971, covering the legacy PR info API path in the dashboard package.
Fusion-Task-Id: FN-4971
Fixes chat streaming delta spacing to preserve proper whitespace, adding regression tests for legacy chat stream handlers and the createChatStreamHandlers factory. A changeset was created for the `@runfusion/fusion` package.
Fusion-Task-Id: FN-3817
- Increase the member role text sizing in CreateRoomModal for better readability
- Update CreateRoomModal.css with the adjusted typography value
- Keep the change scoped to create-room role styling only
Fusion-Task-Id: FN-3808
- Add a first-event watchdog in legacy chat SSE handling to prevent hangs before initial stream data
- Cover watchdog behavior with legacy chat stream API tests
- Add hook-level regression tests for useChat and useQuickChat first-send stream scenarios
- Include a changeset for @runfusion/fusion patch release
Fusion-Task-Id: FN-3760
Documentation delivery alignment completing Step 5 of FN-3369, updating the CLI reference, research docs, and research hardening preflight guide with consistent documentation delivery guidance across all three files.
Fusion-Task-Id: FN-3599
This merge splits the monolithic TaskDetailModal test file into six focused test suites (rendering, attachments/tabs, definition/actions, inline-editing/integrations, models/progress/workflow, responsive/dependencies), adds a changeset for the test isolation baseline, fixes chat SSE optimistic echo
Fusion-Task-Id: FN-3609
- Add plugin setup API coverage for migration and sync edge cases
- Expand plugin route tests and implementation safeguards for setup state handling
- Update legacy API glue to align plugin setup responses with route behavior
- Keep test-isolation runtime ignore handling compatible with live app activity
Fusion-Task-Id: FN-3573
This merge adds done-payload snapshot handling across the chat system (FN-3430), normalizing how final assistant messages are preserved and consumed in the dashboard hooks, plus it normalizes dashboard mailbox and user identity for inter-agent messaging (FN-3484) and introduces plugin workflow step
Fusion-Task-Id: FN-3430
Merges a batch of five commits delivering dashboard performance optimizations (new composite indexes on `tasks.column`/`tasks.updatedAt` and debounced detail fetches), hardened chat SSE parsing with new test coverage, restored typecheck gates, split-pane task-link navigation (FN-3191), and research
Fusion-Task-Id: FN-3243