Product fixes surfaced by the drift guards:
- GeneralSection.search.ts: FN-8348 added the Report row (bug/feedback/idea/help)
without a search-index entry, so settings search could not find it.
- WorkflowNodeEditor.css: FN-8526 introduced the undefined deprecated alias
--text-secondary; substitute the FN-8043 migration token --text-muted.
Stale-test alignment (no assertions weakened):
- settings-sections: builtin:coding-ideas is no longer deprecated post-#2378.
- TaskDetailModal workflow/oversight suites: mocked workflows gain the columns
array FN-8476 metadata resolution requires; rerender-refetch tests use
persistent mocks since the board-workflows effect now keys on task identity.
- ChatView.chat-commands: sendMessage gained the FN-8502 delivery-callback arg.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The shared pg-test-harness pays the golden schema-template cold start inside
the first PG test of each vitest invocation and is budgeted against core's 15s
testTimeout, but the six PG-consuming plugin packages ran at vitest's 5s
default — on saturated CI runners the first PG test (e.g. whatsapp-chat
persistence.pg) timed out before its assertions ran. Propagate the 15s budget
to all six plugin configs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audit follow-up to the Planning/subtask project-switch fixes — three more
surfaces had the same bug class:
- Embedded ChatView survived a project swap when both projects last used
the chat view: useChat refetched the session list on projectId change
but never reset activeSession/messages or closed the live stream, so
project A's conversation kept rendering (and streaming) under project B.
Now keyed by project like Quick Chat's FN-8257 FloatingWindow key.
- MissionManager's nested always-mounted MissionInterviewModal (and the
interviewTarget-driven MilestoneSliceInterviewModal) reconnected the
previous project's interview session under the new projectId and saved
the goal draft under the new project's kb-mission-last-goal key.
MissionManager is now keyed by project; the interview modal saves an
un-started goal draft on unmount under its own project id.
- Always-mounted GitHubImportModal's persist effect depends on projectId,
so a swap re-fired it with the old project's provider/labels/repo
selections and wrote them under the new project's storage key. Now
keyed by project (the embedded Import Tasks view already unmounted).
Verified SAFE without changes: TaskDetail/Group/Files/WorkflowNodeEditor
(conditionally rendered + closed on swap), Settings/Usage/Schedules/
Agents (cross-project by design or self-correcting projectId-keyed
hydration), and the remaining embedded views (projectId-keyed fetch
hooks, no streams or scoped drafts).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With showTaskChatsInCommonFeed enabled, task-planner sessions appear in the
common Direct feed, so /new//clear there hit ChatView's generic intercept and
createSession orphaned the task's planner history behind a fresh session.
Consume the command with a warning toast for task-bound sessions instead.
Also updates 6 stale ChatView.core.test.tsx sendMessage assertions broken by
FN-8502's onDelivered/onFailed options argument.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The proactive status narration printed the internal 0-based step index, so the
final step of a 13-step task announced "Starting Step 12" next to a card
showing "12/13". Display now uses index + 1 in both the engine builders and
the store-side updateStep narration; the 0-based tool/PROMPT.md contract is
unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The seeded initialPlan lived in modalManager state while embedded Planning
fully unmounts on main-content navigation, wiping its in-component auto-start
guard — so every navigate-back remount (and the project-switch remount key)
auto-started a duplicate planning session and abandoned the one in flight.
The payload is now a one-shot handoff: PlanningModeModal calls the new
onInitialPlanConsumed the moment auto-start fires, useModalManager clears
planningInitialPlan, and remounts take the stored-active-session restore path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Binary Release (v0.73.0-beta.5 was fully red):
- bun compile: mark chromium-bidi external — playwright-core@1.60 (feature-video)
optionally requires it and bun fails closed on unresolvable requires.
- Windows desktop EXE: quote -c.publish.channel=beta in release.yml; PowerShell
tokenizes the bare flag into `-c` + a path and electron-builder ENOENTs on it.
Full suite (all 4 shards red from stale-test drift, no product bugs found):
- engine: align mock stores/assertions with atomic store.moveTaskIf dispatch
(#2371), the fail-closed non-empty PROMPT.md artifact gate (#2390), oldest-
first admission (FN-8453), alreadyClaimed graph routing (#2393), startStep
step projection (#2403/FN-8464), structured retry presentation (FN-8503),
provider-lane pause reasons (#2339), typed column-boundary entry (#2378),
Type.Integer in CAS document schemas (#2375), bounded model-registry refresh.
- engine-no-blocking-shellout: re-pin 17 drifted allowlist line numbers and drop
the stale REBASE_HEAD entry whose execSync was removed.
- core: schema-applier expectations track migrations 0033-0035 (96 tables) and
the synthetic 0000 fixture gains workflow_work_items/mission_contract_assertions;
work-item terminal state is "succeeded" post-#2378.
Known follow-up (not addressed here): self-healing starved-refinement escalation
bumps task.priority, which FN-8453 oldest-first admission no longer consults.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same bug class as the Planning project-switch fix: on a project swap the
SubtaskBreakdownModal survived as the same instance, so the isOpen=false
reset ran with the NEW projectId and persisted the old project's draft
description under the new project's storage key (and kept it in memory),
making the breakdown reopen in project B with project A's draft.
- AppModals keys SubtaskBreakdownModal by project id so a swap remounts it.
- Unmount cleanup now saves the in-progress draft under the instance's own
project key (constant per keyed instance), so the old project's draft is
preserved instead of dropped or misfiled.
- Dismissal on swap was already covered by closeProjectScopedModals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The wt.exe auto-create guard was keyed on the browser UA, forcing Windows
browsers pointed at mac/linux-hosted dashboards through the manual "Start
terminal" screen. Windows-UA clients now probe GET /api/system/info once and
only keep the skip when the SERVER platform is win32 (or the probe fails,
conservatively). Non-Windows browsers never probe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Switching projects left the previous project's modals (task detail, group,
subtask, git manager, ...) open over the new project, and the embedded
Planning view kept the old project's running plan, session list, and
persisted active session (the durable-session effect re-fired with the new
projectId while the old session was selected, contaminating the new
project's storage key).
- modalManager.closeProjectScopedModals() closes project-scoped modals and
clears planning/subtask payloads; settings/schedules/usage/agents/setup
wizard/model onboarding stay open. Called from handleSelectProject (only
on an actual project change), handleViewAllProjects, handleSetupComplete.
- Embedded PlanningModeModal is keyed by project id so a project swap
remounts it: stream closed by unmount cleanup, fresh session list, and
the new project's own persisted draft/active session restored.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
## Summary
Restore main full-suite green after AgentsView started calling
`isAgentHeartbeatEnabled` / `withAgentHeartbeatEnabled`.
- **Root cause:** `agents-view-mobile.test.tsx` wholesale-mocks
`../../api` without those exports → uncaught exception → empty DOM →
missing Board/List/Controls queries.
- **Fix:** add heartbeat helpers to the mock (same contract as
AgentDetailView helpers).
## Context
Failing run:
https://github.com/Runfusion/Fusion/actions/runs/30062695937
4 failures in `agents-view-mobile.test.tsx` (dashboard app quality
backfill shard).
## Test plan
- [x] `app/components/__tests__/agents-view-mobile.test.tsx` (15 tests)
- [x] `pnpm test:gate` ×2
- [ ] CI PR checks / Full Suite
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Tests**
* Improved mobile agents view test coverage by adding heartbeat toggle
behavior to the test setup.
* Documented heartbeat-related dependencies used by bulk and individual
agent controls.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Reported bug (screenshot): deleting the task created from a plan left the
session permanently stuck on PLANNING_CREATED_TASK_MISSING — Retry create
replayed the same 409 forever. A linked task absent from the
include-archived scan (task-row authority; a successful scan proves
deletion, not a flaky read) now clears the stale linkage and creates a
fresh task, in both the create-task route and createTaskFromPlanSession;
a still-listed-but-unreadable task keeps failing closed.
Multi-agent review of fdd120232 (correctness/adversarial/reliability):
- P1: CLI planning sessions were memory-only — setAiSessionStore only ran
in the dashboard server, so --resume could never find a session across
invocations. New ensureDurablePlanningSessionStore wires the durable
AiSessionStore over the board store's public asyncLayer in runTaskPlan.
- P1: resume failures now THROW instead of process.exit (fn_task_plan
runs inside the pi host — an exit killed the whole agent session), and
a no-question resume requires an explicit refine focus (the provided
description) so merely resuming never rotates the epoch.
- P2: claim and finalize CAS gained the same expected-epoch WHERE guard
as reconcile, so a stale-epoch creator can no longer finalize an
old-epoch task onto a rotated session.
- Side-effect failures (documents, logEntry, validate, reconcile) are now
logged instead of swallowed; post-insert failures no longer mislabel
the just-created task alreadyCreated:true; the keep-refining readline
closes on thrown prompts and a failed refine after creation returns the
created task id with a resume hint; cross-process generating guard
added to createTaskFromPlanSession.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes the P1 agent-native gap from the multi-task review: the CLI and
fn_task_plan pi tool created tasks via a raw store.createTask with no
proposalClaimId — no idempotency, no session linkage, and tasks outside
the epoch sequence, so a later dashboard Proceed would duplicate them.
- New shared createTaskFromPlanSession in @fusion/dashboard/planning:
the agent-surface twin of POST /planning/create-task (epoch-derived
claim key, claim/finalize/reconcile/release CAS lifecycle with the 30s
stale-lease takeover, formatPlanningPlanMd task shape, plan/original-
description documents, validate-on-create, generating guard).
- runTaskPlan creates through it (making the FN-7734 retry wrapper
genuinely safe), prints the session id, and offers an interactive
keep-refining loop that creates further tasks from the evolved plan.
- fn task plan --resume <sessionId> / fn_task_plan resumeSessionId reopen
an existing session — even a validated one whose task exists — and the
no-question resume regenerates the interview via a refine turn, which
rotates the creation epoch server-side.
Tests: CLI suite pins claim-aware creation, the continue prompt, and the
resume flow; dashboard suite pins createTaskFromPlanSession idempotent
replay and epoch-aware second creation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Windows clients no longer force isReady(true) on mount: the validation
effect owns isReady on every path, so persisted tabs are server-validated
before xterm connects (previously Windows could attach to a dead session
first and recover via 4004).
- isWindowsBrowserClient() matches 'Windows NT' minus 'Windows Phone' so
phone UAs are not needlessly denied first-tab auto-create.
- normalizeActiveTab() now also collapses MULTIPLE active tabs to the first
active one (zero-active was already handled), and readTabsFromStorage
drops malformed persisted entries individually instead of discarding the
whole payload's valid siblings via the outer catch.
- Regression tests for all four paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up to 907e8d03e (ce-code-review, 8 personas):
- Ctrl/Cmd+V with no clipboard API (or after a denied read) now returns
false WITHOUT preventDefault: xterm skips key handling and the browser's
default paste fires xterm's helper-textarea listener once. Returning true
made non-mac xterm inject \x16 and cancel the native paste (verified
against xterm 5.5.0 _keyDown).
- A denied clipboard read sets a sticky ref so later pastes use the native
path instead of being preventDefaulted into zero delivery.
- Custom-path delivery goes through terminal.paste() to restore bracketed
paste and newline normalization.
- 'Start terminal' surfaces createTab failures in the error banner and
disables while a create is in flight (no duplicate PTY sessions).
- normalizeActiveTab() extracted so storage-read and server-validation
share one all-inactive tie-break; failure-path regression test added.
- Rewrote docs/solutions/ui-bugs/xterm-async-font-remeasure-paste-dedupe.md
to the current paste contract (was prescribing the pre-#1902 behavior).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One planning session can now create multiple tasks. Task-creation claims
are epoch-scoped: proposalClaimId stays planning-session:{id} for epoch 0
and becomes planning-session:{id}#N after the plan is edited past a
created task (rotateTaskCreationEpochOnReopen archives createdTaskId into
createdTaskIds and resets claim state). Unedited Proceed replays stay
idempotent within an epoch; crash-after-insert dedup still reconciles via
the epoch-keyed task row. Complete sessions resume to an editable plan
review with a linked-task banner; the task-created handoff gains a
Continue planning action.
Hardening from the multi-agent code review (9 reviewers):
- Reopen + rotation run only AFTER turn admission, so a rejected request
never burns a phantom rotation (P1, 3 reviewers).
- Claim-lifecycle CAS writes are surgical jsonb merges and reconcile takes
an expected-epoch guard, so a concurrent rotation can never be reverted
or an archived task re-linked to a new epoch.
- create-task 409s while the session is still generating (turn-completion
persist could tear the fresh linkage).
- Durable-read fallback in create-task now logs before trusting the
in-memory epoch.
- linkedTaskId no longer leaks across session switches; the banner
resolves the just-created Task before the tasks prop refreshes and
falls back to the newest archived task after rotation; Continue
planning re-registers the active session.
- Shared applyCompletePlanningResume helper replaces triplicated resume
view-transitions; stale one-task-per-session comment corrected.
Tests: post-rotation replay idempotency and epoch-keyed crash reconcile
(e2e), rewind rotation + rejected-rewind non-rotation + payload
normalization round-trip (unit), Continue planning + banner-leak (UI),
create-task 409 (routes), and a new PG integration suite pinning the
surgical CAS merge and reconcile epoch guard.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GitHub #2121/#2307: Windows browser clients intentionally skip first-tab
auto-create, but TerminalModal showed an endless 'Starting terminal...'
spinner whose only escape was the tab-strip '+'. useTerminalSessions now
exposes autoCreateDisabled and the modal renders an explicit 'Start
terminal' action instead. All-inactive persisted tab payloads are also
normalized on restore so the spinner can't wedge on activeTab=null.
Paste: attachCustomKeyEventHandler returning false does not cancel the
browser's default paste, so Ctrl/Cmd+V delivered the payload twice (custom
clipboard read + xterm helper-textarea paste event). preventDefault() makes
the custom read the single path; when the async clipboard API is missing
(non-HTTPS remote access, older Firefox) the handler returns true so the
native paste path works instead of paste being dead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stopGeneration only aborted generations with an activeGenerations record.
A just-started session whose initial turn was still pending (registered
by start-streaming, not yet consumed by a stream connect) returned false
from Stop and the "stopped" generation sprang back to life on the next
stream connect. Stop now discards the pending turn too, and remains
strictly keyed to its session id so stopping one plan never affects other
concurrently generating sessions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A finished plan must never land on a do-nothing screen:
- submitResponse/rewindSession REOPEN a validated session (clear the
terminal marker; the turn's persistSession durably writes it) instead
of rejecting with "already been validated". validateSession remains the
only terminalizer.
- A complete session with no created task resumes into the full plan
review workspace (read plan, Refine/comments, Proceed) instead of the
create-only retry card; task-linked sessions still resume to the task
handoff, preserving the never-rotated one-task-per-session claim.
- The live create-failure screen gains a Back to plan action.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code-review follow-up to e2ee8ba27: the loader overlay now hosts the
streamed-thinking pane, and on short viewports (landscape phones are in
the mobile breakpoint) the centered column could exceed the overlay and
clip the Stop button and thinking output. The overlay scrolls instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The workspace loader that covers all follow-up turns (next question,
refine, contextual comments, question regeneration) showed only a spinner
and elapsed time — streamed thinking/output was visible only on the first
pre-summary turn. Reuse the initial loading view's thinking container and
toggle there, and mirror the generation-activity label instead of a
hardcoded "Generating plan…".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extends the Planning Mode no-active-question fix to the other three
interview lanes: a LIVE session (no summary yet) that receives a
submission with no active question now reprompts the agent to continue
the interview and ask a fresh question — carrying the submitted input as
context — instead of throwing "No active question in session". Completed
interviews (summary present) still reject late submissions, preserving
the existing contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Refining a plan (or submitting any input) while the session had no active
question — e.g. after a failed retry cleared summary/currentQuestion —
threw InvalidSessionStateError("No active question in session") at the
operator. Now the interview continues instead:
- The refine and contextual-comment branches no longer require
session.summary; they fall back to a running summary rebuilt from
persisted history.
- A submission with no active question reprompts the agent via
formatQuestionRegenerationForAgent to produce a fresh option-driven
question, carrying the submitted operator input along as context.
- The Planning modal forwards no-question submissions to the server
(loading view + SSE) instead of dead-ending with a local error.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code-review follow-up to 716e69862: the terminal-error reconcile ran after
the Last-Event-ID replay, so a reconnecting client received a buffered
error event from the replay AND again from the reconcile block (double
onError, duplicate auto-retry triggers). The reconcile now runs before the
replay and skips it for terminal sessions, writing the newest buffered
error event (or a fresh broadcast when the bounded buffer evicted it)
exactly once, gated on lastEventId.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phone fixed bottom:overlap left the panel under the soft keyboard (layout vs
visual viewport). While open on mobile, remeasure visualViewport and pin with
top/max-height so the entry box stays visible when the keyboard opens.
Center the jump-to-latest chip with margin-inline auto instead of
transform:translateX(-50%) so global .btn transform transitions and
:active scale cannot shift it sideways in Quick Chat and full Chat.
Provider errors thrown between persistSession("generating") and the turn's
own error handling (agent rebuild in ensureSessionAgent, history replay,
legacy sync createSession first turn) escaped to the route and left the
session row "generating" forever with no error, no SSE event, and no
watchdog — the modal hung on "Thinking/Generating plan" because its SSE
reconnect loop and 8s poll both treat a persisted "generating" row as
healthy.
- submitResponse/retrySession/createSession now convert any non-abort
escape after entering "generating" into the standard persisted retryable
error + SSE error broadcast before rethrowing.
- The SSE stream route reconciles settled/stranded sessions on connect
(reconcileStalePlanningGeneration): a terminal error is replayed and the
stream closed; a "generating" session with no live/pending turn past the
watchdog window is converted to a retryable interrupted error.
- Provider failures on the JSON reformat retry now surface as themselves
instead of a misleading "no valid JSON" parse error.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Snapshot the selected quote on Add-comment pointerdown and freeze it for the
composer so mobile selectionchange can no longer clear the quote and unmount
the entry box as soon as it opens.
Add/Submit no longer run on pointerdown (that closed the box immediately).
Match Start Planning: preventDefault keeps focus for the click, then click
commits the action once.
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 -->
The Workflow Steps bullet in README.md links to
`./docs/workflow-steps.md#workflow-declared-optional-steps`, but the
target heading is `#### Workflow-declared optional steps
(`optional-group` nodes)`, which GitHub slugifies (including the
parenthesized text) to
`#workflow-declared-optional-steps-optional-group-nodes`. As a result
the link lands at the top of the page instead of the intended section.
The correct anchor is already used by a self-link elsewhere in the same
file (docs/workflow-steps.md, the "Optional groups and default-on gates"
row), confirming the expected slug.
This is a one-line fix: append `-optional-group-nodes` to the anchor in
README.md.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Updated the Workflow Steps documentation link to direct readers to the
more specific “optional group nodes” section.
<!-- 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>
The root CHANGELOG's beta sections (and the matching GitHub prerelease bodies, now edited in place) carried full-cycle aggregates because every beta distilled all preserved pre-mode changesets. Rebuilt each section from packages/cli/CHANGELOG.md's incremental per-beta entries so each beta lists only its own changes; future releases are handled by the channel-scoped selection in release.mjs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>