On phone/tablet the first tap of Add/Submit blurred the composer, closed the
keyboard, and shifted the fixed panel so the click was lost. Commit the action
on pointerdown (same pattern as Refine Apply) so one tap is enough.
Pin the selection comment editor as a fixed panel on tablet and phone, and
lift it with visualViewport keyboard metrics so the first focus no longer
hides the form under the OS keyboard (or off-screen on tablet).
Tablet plan-actions no longer use flex nowrap (which put Add comment beside
Refine/Proceed). Keep the two-column grid so the selection control is a
full-width first row, and pin MessageSquarePlus to the same 16px/token size
as mobile.
Phone no longer pins Add comment under the action rail as a fixed bar. It
uses the same full-width in-flow footer row as tablet, above Refine and
Proceed. The composer stays fixed when open.
Reopening a complete plan no longer shows "still being prepared" when the
validated payload marker is missing. Generation Retry that hits
"already been validated" refreshes into create-retry or plan review.
Tablet (≤1024px) now uses the action-rail Add-comment control as a full-width
row above Refine/Proceed instead of the document-end trigger. Phone keeps the
fixed bar above the mobile nav; desktop keeps the in-document control.
Align MissionAutopilot expectations with autonomy-audit attribution: updateMission
carries the system actor options, and autopilot_disabled is recorded on the
mutation instead of a separate logMissionEvent mirror.
- usage-limit-detector + provider-health-monitor: make three bare listTasks()
callers explicit with { slim: true }, restoring the architecture-hot-paths
contract (they only read scalar pause/column/model-provider fields).
- pg-test-harness beforeEach: wipe <rootDir>/.fusion/tasks after TRUNCATE ...
RESTART IDENTITY so filesystem isolation matches the id reset; stale task
dirs from prior tests no longer collide with reused IDs (fixes
store-reservation-atomicity rollback assertions).
Plan review Add-comment controls now track document-level selectionchange so
they appear as soon as text is selected and dismiss when the selection ends.
On mobile the trigger and composer are fixed above the nav (with width auto)
so operators no longer need to scroll to reach them.
## Problem
With `pushAfterMerge` enabled (and `mergeStrategy` other than
`pull-request`), if `origin/<integration-branch>` advances externally
between the local squash-merge and the push, the divergence path opens a
clean-room `git pull --rebase` and an AI agent resolves and stages the
conflicts — but the flow could end there: no `git rebase --continue`, no
push, and no surfaced error.
Because finalize runs *before* the push, the task is already `done`, so
a reviewed, approved merge is silently left container-only, and every
subsequent merge on the project stalls the same way. Separately, an
abort mid-push (`MergeAbortedError`) was swallowed with only a
process-log warning — no task-log entry, no run-audit event.
## Change
- **Deterministic regression coverage** for the conflicting-divergence
path (real-git fixture) proving the rebase runs to completion and the
push lands (refs converge), plus abort/termination scenarios.
- **Recovery-branch safety net:** before the clean-room rebase starts,
the pre-rebase local squash is force-pushed to a per-task remote branch
`fusion/<task-id>-stranded`, so approved content is never container-only
— even across process death or abort. Deleted after a successful target
push; retained on failure/abort as the recovery source.
- **Never-silent outcomes:** every non-pushed outcome (failure or abort)
writes a durable task-log entry and a `push:origin` run-audit event. The
audit contract now documents `push:origin` as polymorphic (dashboard
Smart Push vs. automated post-merge push) and enumerates the automated
path's outcomes, including the new `"aborted"` shutdown case.
- **Cleanup hardening:** `isRebaseInProgress` now probes Git's
worktree-specific `rebase-merge`/`rebase-apply` state directories
(async, timeout-guarded) so a completed rebase can't receive a spurious
second `--continue`; unfinished rebases are cleaned up.
Out of scope by design: withholding the "merge confirmed" state until
the push succeeds — the `FNXC:MergePush` invariant ("a push problem can
never park or roll back a landed merge") is deliberate; the recovery
branch + surfacing satisfy the data-preservation intent without breaking
it.
## Files
`packages/engine/src/merger-ai.ts`, `packages/engine/src/merger.ts`,
`packages/engine/src/run-audit.ts`, new/updated tests under
`packages/engine/src/__tests__/`, `docs/settings-reference.md`,
`docs/dashboard-guide.md`, `AGENTS.md`, and a labeled changeset.
## Validation
`tsc --noEmit` clean; engine divergence + merger suites pass (41 tests);
rebased onto current `main` with no conflicts.
---
_Developed with Claude Code, under human supervision and review._
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Prevented approved post-merge pushes from becoming stranded when the
remote diverges by using a recovery-branch workflow and safer cleanup.
* Improved behavior and reporting when pushes are aborted or fail after
merge, including clearer non-fatal status and audit outcomes.
* **Documentation**
* Expanded push-after-merge and dashboard Smart Push documentation with
recovery-branch and `push:origin`/`push:recovery-branch` outcome
semantics.
* **Tests**
* Added end-to-end regression tests for divergent/conflicting AI
push-after-merge flows, including abort and worktree cleanup
verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Victor Cano <victortroz@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
- synchronize all five secondary app locale catalogs with the authored
English key structure
- restore fallback entries for heartbeat controls, release-channel
settings, report targeting, and task provenance labels
- add a patch changeset for the catalog parity fix
## Test Plan
- `pnpm i18n:status`
- `pnpm --filter @fusion/i18n test` (29 tests)
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/AgentsView.test.tsx --silent=passed-only
--reporter=dot` (138 tests)
- `pnpm check:changesets`
- `pnpm build`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **Bug Fixes**
- Improved localization consistency for heartbeat controls, capability
settings, release-channel configuration, reporting guidance, and task
provenance details.
- Added missing translation entries across Spanish, French, Korean,
Simplified Chinese, and Traditional Chinese locales.
- Untranslated values now fall back cleanly to the authored English text
structure, preventing missing or inconsistent labels in supported
interfaces.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Every PR's blocking checks were dominated by redundant full rebuilds,
not by tests. Measured on recent runs: the Gate job spent ~6 of its ~7.5
min on a cold `pnpm build` (the exact-key dist cache missed on virtually
every PR) for ~45s of actual boot smoke + gate tests; Build ran ~8 min
and Typecheck ~4 min, both fully cold every time. Expected end state
once the warm job has run on main: all four blocking checks in roughly
2–4 min wall-clock.
### Gate job
- New `gate-dist-*` cache namespace with `restore-keys`, additionally
caching `.fusion/cache/plugin-build-cache.json` (build-workspace's
per-package content-hash skip cache) and `packages/cli/dist`. The
always-run `pnpm build` reconciles a near-match restore by content hash
and rebuilds only the packages the PR touched. This is safe *because*
the gate builds after restoring — the shard jobs' "no restore-keys" rule
(FN-4232/FN-4605 stale-dist incidents) still stands there, since they
consume dist without building.
- `FUSION_CLI_FULL_PACKAGE=0` on the gate build: skips the multi-minute
CLI desktop/plugins/DTS packaging tail nothing in the gate consumes
(same shape `pnpm verify:fast` proves locally). Full CLI packaging
coverage stays blocking in the Build job.
### Build job
- Restore-only tap (`actions/cache/restore`) of the same warmed cache.
Restore-only because this job runs FULL CLI packaging (`CI=true`) and
saving that shape would swap the cache's canonical fast-CLI contents out
from under the Gate job. Its distinctive coverage is preserved:
`ensureFullPackageCliPlanned` force-plans the CLI in full mode
regardless of cache state.
### Typecheck job
- Caches per-package tsc incremental buildinfo — self-validating (tsc
hashes every input against it and re-checks whatever changed), so
`restore-keys` is correctness-neutral by construction.
- **Fixes a real incrementality bug:** `tsconfig.json` and
`tsconfig.app.json` in the dashboard both inherited
`${configDir}/dist/.tsbuildinfo` from `tsconfig.base.json`, so the two
typecheck programs clobbered each other's buildinfo and re-checked the
full program every run — incremental typechecking never worked for the
dashboard, in CI or locally. `tsconfig.app.json` now writes
`dist/.tsbuildinfo-app`. Measured: dashboard typecheck 44s cold → 5.6s
warm.
### Warm job (full-suite.yml, push to main)
- New `warm-gate-build-cache` job saves both caches from main on every
push. Caches saved on a PR merge ref are invisible to other PRs, so
without this every PR's *first* run would still build/check cold.
## Guardrails
`ci-workflow.test.ts` pins the coupled invariants so they can't drift
apart silently:
- restore-keys requires the reconciling `pnpm build` after restore,
before boot smoke
- the mtime-defeating seed step stays exact-hit-only
- byte-identical cache path lists between the Gate/Build/warm blocks
(actions/cache versions caches by path list — a drifted list makes
caches mutually invisible)
- Build stays restore-only and must NOT opt out of full CLI packaging
- Typecheck cache shape + the distinct dashboard app buildinfo path
## Notes
- First PR runs after this lands still build cold until the warm job has
run once on main.
- No changeset: CI config + test-only per AGENTS.md.
## Verification
- `ci-workflow.test.ts` + `package-config.test.ts`: 106 tests pass
- Dashboard typecheck run twice locally: 44s cold → 5.6s warm, both
`.tsbuildinfo` and `.tsbuildinfo-app` written, exit 0
- Cache block path/key parity verified programmatically across both
workflow files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Performance**
* Improved CI build and type-check performance through incremental
caching.
* Added cache warming from the main branch to speed up pull request
checks.
* Enabled faster CLI packaging during gate validation while retaining
full packaging coverage elsewhere.
* **Bug Fixes**
* Prevented dashboard TypeScript build information from being
overwritten, preserving incremental type-checking reliability.
* **Tests**
* Added coverage to verify CI cache behavior, build ordering, cache
paths, and packaging modes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Proceed with plan called /api/planning/create-task without the legacy
/validate step, so the persisted AI session stayed awaiting_input and the
session list/needs-input banner kept advertising a finished session. The
create-task route now terminalizes the session via validateSession on every
path that ends with a created task, including alreadyCreated reconciliation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep all selected GitHub issue actions touch-safe and visible on a single mobile row.
- Group detail action controls separately from the comment composer and make their mobile tracks shrinkable.
- Add responsive browser-smoke coverage at 320px, 390px, and 412px plus modal structure tests.
- Document the mobile behavior and add a patch changeset.
Files changed:
.changeset/fn-8548-mobile-github-import-actions.md | 7 +
docs/dashboard-guide.md | 8 +-
.../dashboard/app/components/GitHubImportModal.css | 42 +++++-
.../dashboard/app/components/GitHubImportModal.tsx | 80 ++++++------
.../__tests__/GitHubImportModal.test.tsx | 50 ++++++++
packages/dashboard/app/styles.css | 11 +-
.../dashboard/scripts/browser-layout-smoke.mjs | 142 ++++++++++++++++++++-
7 files changed, 290 insertions(+), 50 deletions(-)
Fusion-Task-Id: FN-8548
Fusion-Task-Lineage: 8fab6a1d-0ca9-4246-9707-e1fc84ca58e5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Render complete mission hierarchy details from the fn_mission_show agent tool.
- Format mission, linked-goal, milestone, slice, and feature metadata with IDs and statuses
- Link features to their tasks and bound verbose acceptance and verification text
- Cover populated and empty hierarchy responses with regression tests
- Add a patch changeset for the agent lookup fix
Files changed:
.changeset/fn-8540-mission-show-hierarchy.md | 7 ++
.../src/__tests__/agent-mission-tools.test.ts | 68 +++++++++++++++++++
packages/engine/src/agent-tools.ts | 77 +++++++++++++++++++++-
3 files changed, 150 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-8540
Fusion-Task-Lineage: f2282226-3f4e-4d9f-bd8e-d18ad9639c03
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Keep Planning Refine and Proceed controls visible across mobile plan-review hosts.
- Make the plan document pane the responsive scroll owner while preserving its action rail.
- Cover portrait and short-landscape embedded and modal layouts with CSS and browser tests.
- Add a patch changeset for the mobile planning action fix.
Files changed:
.changeset/fn-8537-mobile-planning-actions.md | 7 ++++++
.../dashboard/app/components/PlanningModeModal.css | 25 ++++++++++++++++++++++
.../__tests__/PlanningModeModal.css.test.ts | 17 +++++++++++++++
.../src/__tests__/planning-browser-e2e.test.ts | 20 +++++++++++++++--
4 files changed, 67 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-8537
Fusion-Task-Lineage: 7a53aa74-859d-4c76-bf26-ab6ea72873dd
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Port the planning turn-admission invariant (FNXC:PlanningTurnAdmission,
2026-07-22) into the Compound Engineering orchestrator: at most one turn
(opening/answer/resume-rehydration) is admitted per CE session, reserved
synchronously and held until the turn settles — a re-entered mobile view
re-submitting a turn now gets CeTurnInProgressError (HTTP 409) instead of
displacing the in-flight turn's live agent, which surfaced as "Failed to
parse agent response: AI returned no valid JSON". cancel()/discard()
force-clear the reservation; releases are token-scoped so a stale release
can't drop a newer turn's slot.
In the engine interactive-ai-session seam: bump the reformat retry from
one to two attempts (non-Anthropic default models comply less reliably
with the JSON-only protocol), and log every failed parse with a bounded
raw-response snippet plus resolved provider/model — including a distinct
empty-assistant-message marker — so support can diagnose these reports
without a repro.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Release a settled retry token so a later planning error can start the next bounded attempt.
- Clear only the matching retry owner before scheduling its successor.
- Cover distinct stream errors after retry settlement.
- Add a patch changeset for the recovery fix.
Files changed:
.changeset/fn-8536-planning-retry.md | 7 +++++++
packages/dashboard/app/components/PlanningModeModal.tsx | 10 ++++++++++
.../__tests__/PlanningModeModal.planning-flow.test.tsx | 5 +++++
3 files changed, 22 insertions(+)
Fusion-Task-Id: FN-8536
Fusion-Task-Lineage: cccb0793-390e-4bdb-b459-939760e644bb
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
After a hard host crash (SIGKILL, power loss), postmaster.pid survives with no
postmaster behind it. The optimistic join handed every subsequent boot a URL to
the dead port, so the dashboard could never start again without a manual pid
delete. Probe the recorded pid with signal 0: provably dead (ESRCH) rebuts the
live-lock presumption and the boot takes an owned start — PostgreSQL itself
re-validates and reclaims the stale lock file, so a recycled live pid keeps the
old join-then-fail behavior and a genuinely live postmaster still surfaces the
lock collision we already join on. EPERM counts as alive (fail-closed).
Verified end to end: real cluster started, postmaster SIGKILLed leaving the pid
file + interrupted WAL, fresh lifecycle detected the stale lock, ran an owned
start, and crash recovery preserved the marker row.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reloading Planning re-entered the generation view ("Generating initial
plan…", Stop button, elapsed timer, 8s watchdog) while merely fetching a
persisted session. A new session_loading view state renders a neutral
"Loading session…" spinner during hydration; the generating view is
reserved for sessions the server reports as generating. Unrecognized
persisted session shapes now land in the retryable error view instead of
spinning forever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
beta.4 follow-up from the issue thread: on an interrupted (not-cleanly-shut-down)
cluster, the elevated Windows launcher declared readiness on a bare TCP accept
while crash recovery still rejected every connection with 57P03, so
ensureDatabase failed and the start cleanup fast-shutdown the recovering
postmaster ~0.2s after launch; the retry then joined the instance it had just
told to stop, got ECONNREFUSED, and parked the dashboard in a dead shell. The
30s ".pgrunner sharing violation" stall was recovery's SyncDataDirectory fsync
walk hitting Fusion's own pgctl log inside the data dir.
- Move the pgctl runner dir to a sibling .pgrunner-<dataDirName> outside the
data dir (and sweep the legacy in-dataDir .pgrunner), so recovery's fsync
walk can never contend with the postmaster's inherited log handle.
- Ignore 57P03 recovery rejections in the elevated readiness fatal scan.
- Owned starts wait for the cluster to genuinely accept connections (retrying
57P03/socket errors, bounded by the start timeout) before ensureDatabase —
never stop a postmaster that is still in recovery.
- Join-path database verify retries the 57P03 recovery signal for up to 15s;
socket errors keep the instant optimistic-join contract for stale pids.
- startup-factory's joined-instance-unreachable retry backs off across ~15s
instead of a single 500ms attempt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary
Wave 15 of package code organization.
### Peels
- `types/settings-scope.ts` — global/project settings (~2.2k lines)
- `types/archive-planning.ts` — archive, mesh/multi-project, planning
sessions
- `task-store/project-store-ops.ts` — rename of `remaining-ops-1` (last
numbered ops module)
### LOC
- `types.ts` ~5872 → ~3074
## Test plan
- [x] `@fusion/core` typecheck
- [ ] CI merge gate
**Stack:** this PR → #2397 → #2398
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Reorganized and expanded the core public type surface into dedicated
modules for settings, archive/planning, board, tasks, todo lists, plugin
activation, and multi-project setup.
* Improved the browser-safe type exports to keep the public contracts
consistent.
* Updated internal project-level operation wiring to use the correct
project implementations.
* **Bug Fixes**
* Fixed a workflow creation test hook to inject the correct pre-insert
behavior for workflow-definition collision/allocator scenarios.
* **Chores**
* Refreshed internal headers and updated line-count baselines.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
Restore full-suite green after latest `origin/main` landings. PR #2392
already merged; this branch starts from current main.
- **ExecutorStatusBar mobile utility test:** assert `Waiting` (product
rename from Queued) and that the In Review segment is gone.
- **AppearanceSection task-popup help:** assert FN-8478 fallback copy
(board deep-tab chips + List row/card + right-dock → movable popup).
- **Changeset format gate:** shorten
`mobile-board-pointercancel-settle.md` summary to ≤120 chars (was
blocking `pnpm test:gate` on main).
## Context
Latest main Full Suite run failed primarily on dashboard app quality
backfill:
- https://github.com/Runfusion/Fusion/actions/runs/29970703115
## Test plan
- [x] `pnpm test:gate` ×2
- [x] `utility-mobile.test.tsx` + `AppearanceSection.test.tsx`
- [x] `settings-default-descriptions.test.tsx`
- [ ] CI PR checks / Full Suite on this branch
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved mobile board column snapping when resting mid-screen,
flinging past columns, or making unintended swipes.
* Updated task status labels to show “Waiting,” “Running,” and
“Blocked.”
* Refined help text for opening tasks as popups, including click-target
and subsequent-task behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Add `CHAT_CODEBASE_ACCURACY_GUIDANCE` so agent chat (direct +
multi-agent rooms) investigates the live checkout with tools before
answering code/architecture questions.
- Soften chat’s pure brevity default for repo questions: still lead
short, but keep real path/symbol evidence (parity with the investigation
pressure that makes Planning Mode more accurate).
- Cover the constant and assembly via unit tests; include a patch
changeset for `@runfusion/fusion`.
## Why
Users reported Planning Mode was more accurate about the codebase than
agent chat. Plan mode inherits the triage seam’s “read/grep first, name
real files” contract; chat only had a short helpful-assistant persona
plus a brevity default, so models often answered from priors.
## Test plan
- [x] `pnpm --filter @fusion/dashboard exec vitest run
src/__tests__/chat-system-prompt.test.ts
src/__tests__/chat-manager.test.ts`
- [ ] Manually ask agent chat a project-specific architecture question
and confirm it greps/reads before answering with real paths
- [ ] Confirm non-code chat still stays short/crisp
- [ ] Confirm multi-agent room responders also receive the new guidance
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Improvements**
* Improved repository/code answers by investigating the live codebase
first and prioritizing verified paths and symbols over speculation.
* Refined response-length behavior so code questions stay
evidence-focused, while non-code questions remain concise.
* Applied consistent accuracy guidance across both direct and room-based
conversations.
* **Bug Fixes**
* Prevented chat instructions from depending on unavailable mailbox
functionality, improving reliability for agentless/room flows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Symptom
Users reported Planning Mode regularly failing with **"AI returned no
valid JSON.. Retry this planning session or start a new one."**, and
that leaving and returning to the interface mid-generation **duplicates
the generation — infinitely**. Every app-tab switch unmounts the
Planning view, so the leave/return path is the *normal* path, not an
edge case.
## Root causes
1. **Check-then-act turn admission.** `activeGenerations.has()` was
checked at turn entry, but the record was only created inside
`runGenerationWithTimeout`, after several awaits. Overlapping turn
entries (remounted view re-submitting, racing auto-retries, duplicate
start of an existing session) both passed the guard; the second
displaced the first, and the displaced teardown disposed the
**session-shared agent the surviving turn was actively prompting** —
which then read an empty assistant message and failed parse with "AI
returned no valid JSON".
2. **Per-mount auto-retry budget.** The client reset its 3-attempt
auto-retry budget on every mount, so each return to an errored session
re-ran a full-turn regeneration (agent rebuild + complete history
replay) — forever.
3. **SSE replay appended onto existing output.** Fresh stream
connections replay buffered thinking; the client pre-seeded from
persisted `thinkingOutput` (or kept prior output on silent reconnect)
and then appended the replay — visibly doubling the generation on every
reconnect. The 100-event buffer only held a suffix of a turn, forcing
that pre-seed.
4. **Raw `session.prompt()` at context limits.** A long interview that
overflowed the model's context window errored terminally, and auto-retry
replayed the full history into a fresh agent — overflowing again,
unrecoverably.
## Fix
- Synchronous per-session **turn reservation** shared by
`submitResponse`, `retrySession`, `startExistingSession`, and the
initial turn; losers get `GenerationInProgressError` instead of
displacing the winner. Duplicate starts of a generating session are
no-ops. Rewind aborts an active generation through its own teardown
first.
- Client auto-retry budget is **module-scoped per session** (survives
remounts); exhausted budget shows the error view instead of a stuck
spinner. Retry rejections for "already in progress" rejoin the live run.
- Fresh SSE connections **clear streamed output before the buffered
replay**; buffer deepened to a full turn (2000 events); rejoin paths
reconnect cleanly instead of seeding persisted thinking.
- All six planning prompt sites route through the engine's
**`promptWithFallback`**, recovering context-window overflows via
prompt/memory compaction and `session.compact()`.
- Cosmetic: no more doubled period in the retryable parse error message.
## Symptom Verification
- **Original symptom:** "AI returned no valid JSON" after answering
questions; generations duplicating on leave/return.
- **Exact reproduction:** concurrent turn entries on one session
(submit×2, retry×2, start-while-generating) — previously
displaced/disposed the live agent mid-prompt.
- **Assertion it is gone:** `planning-turn-admission.test.ts` asserts
exactly one turn is admitted per race, the winner completes with a
question and no session error, and the shared agent is never disposed;
`planning-context-compaction.test.ts` asserts every planning prompt
routes through `promptWithFallback` (signal forwarded) and that a
recovered context overflow leaves the turn healthy.
## Verification
- `vitest run` on all 7 planning server test files + the 2 new
regression files: **40/40 pass**.
- `routes-planning*` failures at main tip are pre-existing (identical
103/126 + 3/6 counts with and without this diff; main is mid-refactor on
route wiring). `planning-answered-question-reemit` 3 timeouts also
reproduce on clean main.
- `pnpm verify:fast` green; dashboard `tsconfig.json` +
`tsconfig.app.json` typechecks clean; eslint clean on touched files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Prevented Planning Mode from duplicating generations and triggering
“AI returned no valid JSON” errors when leaving and re-entering mid-run.
* Made planning turn handling concurrency-safe and idempotent across
submit, retry, rewind, and duplicate start actions.
* Improved SSE reconnect recovery: clearer replay after reconnect, no
duplicated “thinking” output, and preserved auto-retry limits across
remounts.
* Improved long-context recovery via fallback prompting and cleaned up
retry error formatting.
* **Tests**
* Expanded coverage for concurrent Planning actions, reconnect replay,
context compaction, rewind behavior, and retry formatting; improved
parallel test-harness reliability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
Two-part fix for #2411 — Windows embedded PostgreSQL backends dying with
exception `0xC0000142` and taking the whole dashboard down.
### 1. Crash hardening + recovery (FN-8522)
- Child-only native `PATH` hardening so forked backends can always
resolve their runtime DLLs.
- Non-blocking `.pgrunner` log monitoring (shared read), eliminating the
self-inflicted ~30s `sharing violation` retry window at boot.
- Detection of the ordered 0xC0000142 shutdown sequence with a single
automatic restart of owned clusters on their resolved port, plus
operator diagnostics.
### 2. Platform-aware `max_connections` default (follow-up from
[operator
report](https://github.com/Runfusion/Fusion/issues/2411#issuecomment-5054900702))
On Windows every PostgreSQL connection is a separate process; the
embedded cluster's unconfigured `max_connections=500` cap lets backend
spawn bursts exhaust the non-interactive desktop heap, which kills
forked backends with exactly `0xC0000142`. The reporter confirmed
stability after lowering the cap.
- `embeddedPostgresMaxConnections` is now schema-unset so the server can
distinguish "operator never set it" from an explicit choice
(`getSettings()` merges schema defaults, which previously pinned 500
unconditionally and made the runtime fallback dead code).
- New `resolveEmbeddedMaxConnections()` resolves the unset default
platform-aware: **150 on win32, 500 elsewhere**. Explicit settings are
honored on every platform, clamped to [32, 2000] as before.
- Settings UI renders the cap empty ("auto") with platform-aware help
copy across all six locales.
- Fixed a latent reset bug this exposed: global "Reset this menu" wrote
`undefined` for undefined-default keys, which JSON serialization drops —
the stored value silently survived reset. Now uses null-as-delete.
## Testing
- New unit tests for `resolveEmbeddedMaxConnections` (platform defaults,
clamping, non-integer handling).
- Updated settings-defaults, default-descriptions, and SettingsModal
tests; embedded lifecycle + recovery coverage from FN-8522.
- `@fusion/core` builds clean; changesets included for both parts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary
Gives dashboard integrators (plugin views, embedded panels, theming
tools) a supported way to match the dashboard's look and to layer
overlay UI correctly — instead of scraping computed styles and guessing
z-index values. This implements the CSS-token bridge slice of
`docs/proposals/2026-07-01-dashboard-theme-plugin-system.md`.
Two additions, both inert unless used:
1. **Documented theme-token contract.** A "Theme tokens" section in
`docs/dashboard-guide.md` (referenced from `docs/PLUGIN_AUTHORING.md`)
declares the stable set of CSS custom properties — colors, surfaces,
status colors — that integrators may read. Tokens resolve to raw color
strings (e.g. `#161b22`) in every theme, including the newer ones. A
sync test (`theme-token-contract-docs.test.ts`) parses the doc's token
table and asserts each documented token has a real definition in the
dashboard CSS, so the contract cannot silently drift from the code.
2. **Overlay layering surface.** Overlay-style UI (palettes, pickers,
floating panels) currently has no supported way to sit above the
floating-window stack — the effective max z-index is runtime state
inside `floatingWindowStack.ts`. This PR exposes it:
- `--fusion-max-z` on `:root` — kept in sync by `floatingWindowStack`
(written at module load and after every `nextFloatingZ()` claim), so it
always reflects the true top of the dashboard-managed stack. Boot/floor
value is `11001`, chosen to clear the highest statically-declared layer
(the body-portaled model-combobox dropdown at `z-index: 11000`).
- `#plugin-overlay-root` — an empty, `pointer-events: none` sibling of
`#root` stacked at `calc(var(--fusion-max-z) + 1)`. React never renders
into it, so it is hydration-safe; integrators portal into it and
re-enable pointer events on their own elements.
- The layer bands (base UI / floating windows / toasts / dropdown /
overlay root) are documented in `styles.css` and the guide, and a guard
test (`dashboard-max-z-guard.test.ts`) scans the structural + component
CSS and fails if any static `z-index` is ever introduced above the floor
— keeping the contract honest as the codebase evolves.
## Behavior
No visual or behavioral change for existing users: `floatingWindowStack`
still returns the same values from `nextFloatingZ()`; the overlay root
is empty and click-through; tokens were already defined — this only
documents and guards them.
## Tests
- `theme-token-contract-docs.test.ts` — docs ↔ CSS sync
(non-tautological: anchored matching against real definitions).
- `floatingWindowStack.max-z.test.ts` — `--fusion-max-z` boot value and
live tracking as the stack claims z-indexes.
- `dashboard-max-z-guard.test.ts` — no static dashboard z-index above
the floor (decorative `public/theme-data.css` INT_MAX scanline overlay
deliberately excluded; it's non-interactive grain, documented in the
test).
- Changeset included (`minor`, `category: feature`). Typecheck clean.
## Open question for maintainers
The token is named `--fusion-max-z`. The existing scale uses `--z-*`
names (`--z-dropdown`, `--z-modal`) on a lower band — happy to rename to
`--z-max` / `--z-plugin-overlay` or anything that fits your convention;
the name is the only bikeshed here, the sync mechanism is independent of
it.
## AI assistance disclosure
Parts of this change were authored with AI assistance (Anthropic's
Claude); the commit carries a `Co-authored-by` trailer accordingly.
Everything was human-reviewed before submission, and the test suite and
typecheck were run locally against the current `main`.
If squash-merging with a rewritten message, please keep the attribution:
```
Co-authored-by: Claude <noreply@anthropic.com>
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added stable dashboard theme tokens for consistent plugin/integration
styling.
- Introduced a dedicated plugin overlay mount point with click-through
defaults and an overlay stacking ceiling.
- Overlay z-index now stays in sync with floating window layering
automatically.
- **Documentation**
- Added an explicit stable “theme token contract” and “overlay layering
contract,” including interaction and z-index usage rules and deprecation
expectations.
- **Bug Fixes**
- Improved reliability of plugin overlay stacking so overlay content
renders above intended dashboard layers.
- **Tests**
- Added guards validating CSS z-index ceilings and enforcing the
documented theme token contract.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude <noreply@anthropic.com>
The jump-to-latest button is centered via transform: translateX(-50%), and
the global .btn:active scale transform replaced it wholesale on mousedown,
shifting the button out from under the cursor mid-click. Compose both
transforms on :active (same fix as the DevServer new-logs button).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>