Desktop board scrollers no longer snap: the browser's proximity-snap settle
animation was capturing wheel/trackpad pans and reading as a slow, sticky drag
toward a column center. Base `.board`, `.board-workflow-columns`, and
`.lane-columns` declare `scroll-snap-type: none`; proximity snap is re-declared
in phone-tier media blocks only, where the JS column pager owns paging.
On phones, the hook now owns post-lift motion instead of waiting it out. A
directional lift kills native inertia and animates to its target column via rAF
ease-out (~190-300ms), so the page starts moving on lift rather than after a
native fling that can coast for most of a second. Fling reach is preserved by
deriving a page count (1-3) from release velocity sampled off the board's own
scroll ticks, not from however far inertia happens to travel.
Guards: re-touch cancels the animation and hands the axis back to the finger;
reduced motion, missing rAF, and sub-2px distances fall back to the instant hard
jump; unmount mid-animation restores the frozen inline styles; a fast drag that
rests before lifting is not treated as a flick.
Tap-to-stop-during-momentum is gone as an interaction (no long coast remains to
interrupt). Its regression test is reframed around the equivalent seam: a drag
that interrupts the page animation wins over the pending page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FN-8557 excluded any touch device from mobile mode when its reported physical
screen exceeded the 480px phone threshold, at any CSS width. Large Android
phones report exactly that, so they resolved to tablet: MobileNavBar returns
null off mode "mobile", while MobileNavBar.css still displayed at
(max-width: 768px) — JS and CSS disagreeing about the same device. The board
also fell back to the horizontally-scrollable desktop layout.
No tablet uses a <=600px viewport as its primary layout, so width now
overrides the physical-screen heuristic below that floor; the carve-out
FN-8557 wanted (portrait tablets at the 768 boundary) keeps the 601-768 band.
The floor checks all three width signals the classifier already trusts —
media query, innerWidth, visualViewport — since each can lead on a different
device or update late on rotation. Regression tests cover all three; each
fails against the pre-fix classifier.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A paused engine with nothing running derived executorState "idle", so the
footer badge was indistinguishable from a healthy engine waiting for work.
That is exactly the state a pause settles into once in-flight tasks drain:
triage and planning stall while the board keeps moving, with the pause only
visible by opening the Engine Control menu. Pause state now dominates run
state; the adjacent counters still report throughput.
In the CLI TUI, the global `t` (Git view) branch returned before the Utilities
dispatch in the same key handler, so the advertised "[t] Toggle Engine Pause"
was unreachable dead UI. It now yields when Utilities owns input, and because
the shortcut can stop the board, pausing takes a second `t` within 5s while
resuming stays single-press.
Tests assert the invariant across the whole state matrix (both pause flags x
0/1/5 running), not just the zero-running repro, plus the TUI routing, the
two-press pause, single-press resume, and re-arm behavior.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two auto-save tests waited out the real 500ms debounce via waitFor (up to two
cycles each), unlike the sibling coalesces test which already uses fake timers.
Switch both to vi.advanceTimersByTimeAsync(500) to remove ~1.7s of wall-clock
dead time from the dashboard's slowest feedback-loop suite with identical
coverage (Standing Rule: prefer fake timers over real time waits).
Full Suite shard 4 timed out runTaskShow lock-exhaustion at the default 5s
budget (run 30096660913): each case re-imported the heavy task.js graph under
fake timers, so cold CI workers spent the whole budget on transform and left
unhandled store.getTask / process.exit races after the timeout.
Import task.js once per cached-store describe under real timers via a mutable
store holder, and enable fake timers only around the backoff body. Exhaustion
cases now finish in ~1ms locally while keeping the shipped entry path.
- general: FN-8453 removed the duplicate "Max Triage Concurrent" control
(assert it stays gone); #2400 replaced the per-phase moved-to-workflow note
with the editable "Project workflow model lanes" section.
- scheduling-merge: await the updateSettings call that carries
worktreeCopyFiles instead of assuming calls[0] (CI lane-load ordering).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On GitHub runners headless system Chrome dies at launch without --no-sandbox
and --disable-dev-shm-usage, cascading all five tests as "browser has been
closed" (run 30081843074). The lane had been masked by the shard-4 watchdog
kill since the file landed, so it had never actually executed on CI. Flags
apply only under CI; local launches keep the default sandbox.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nine more api-lane files get the established getProjectScopedPluginMcpServers
stub (includes shared-branch-group-entry-points, whose two current failures
were FN-8491 500s — the historical per-task-derived pair is gone). App lane:
FN-8557 made window.innerWidth<=768 a mobile signal, so leaked innerWidth=375
defineProperty stamps flipped later ListView/settings tests into mobile
layout (reset in beforeEach), and useModalResizePersist's 700px "mobile"
fixture became tablet-class (now a phone-class 375px viewport).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The core watchdog kill had been hiding everything scheduled after it in
shard 4; with the budget fixed, 15 api-lane files surfaced. Thirteen needed
the established FN-8491 recipe (mock stores expose
getProjectScopedPluginMcpServers so the binder short-circuits). Also:
mcp-lane-forwarding tracks resolveManualAiPromptMcpServers' move to
automation-step-execution and FN-8538's getSettings on planning stores;
planning-answered-question-reemit handles PR #2417's synchronous single-turn
admission with a bounded onceAdmitted retry (admission rejections are
side-effect free); routes-system tracks getProjectPluginLoader resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Full-suite run 30077108784 hit an additive insert under CI lane load (group
spliced while merge→end survived) because the toolbar pick ran before the
canvas settled its edge-target state; 5/5 green locally. Same
settle-before-interact class as 5a5796bca; recurrence goes to quarantine.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Shard watchdog floor 25min (was 15): the July PG-cutover test growth pushed
@fusion/core past 900s on contended CI runners; run 30075604930 killed a
healthy core run at exactly the floor because the 27-day-old (still "fresh")
undercounting timings snapshot tightened the budget to it — the same
false-kill class as the 5->15min raise. Floor pin + in-band example updated.
- full-suite.yml timing upload: include-hidden-files — the .timings/ dot-dirs
were silently excluded by upload-artifact@v4, so the step has uploaded
nothing since it was added and the snapshot could never be refreshed from CI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
b2a7425c7 replaced the six-legacy-id whitelist with normalizeColumnId: custom
workflow column ids pass through untouched, only structurally unusable values
(non-string/empty) fall back to triage. The two normalization tests now assert
both halves of that invariant instead of the deleted whitelist behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same drift as chat-commands/mobile: sendMessage gained the {onDelivered,onFailed}
third argument; autosize and draft suites now expect it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- routes-auth/automation/git/github/tasks: FN-8491 binds the plugin-MCP
provider on every project-context resolution; mock stores now expose
getProjectScopedPluginMcpServers so routes stop 500ing.
- routes-github: stub FN-8442 durable planning-claim plumbing whose internal
getSession bypassed the namespace spy; the scoped-store routing contract
is asserted unchanged.
- api-git: POST /planning/create-task now returns the FN-8442
{task, alreadyCreated} envelope; mock matches.
- TaskDetailModal.tab-persistence: settle-then-requery before clicking the
Session tab (detached-node race under CI load).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On some systems the terminal opened blank — buffered prompt present but
never painted (renderer stalled at init: WebGL activation on a zero-sized
canvas or context-loss fallback). Every automatic recovery path fit() but
never refreshed, and fit() with unchanged cols/rows triggers no internal
xterm repaint, so only user input, a font-size change, or a new tab
repaired it. Observer/geometry-driven fits in TerminalModal and
SessionTerminal now always follow fit() with an explicit full-viewport
terminal.refresh(0, rows-1).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- remote-access-routes: FN-8491 made every project-context resolution call the
plugin-MCP binder; the mock store now satisfies the runtime-owned-store guard.
- ChatView.mobile: sendMessage gained the FN-8502 {onDelivered,onFailed} arg.
- PlanningModeModal.planning-flow: settle-then-requery before clicking Proceed
(detached-node race under CI load, same class 5a5796bca fixed for Stop/Refine)
and await the create-dispatch mock signal instead of the DOM alone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The CLI full-package build's nested @fusion/desktop build stages the
production closure via pnpm deploy (~1300+ packages); on cold-store macOS
release runners that exceeded runWorkspaceCommand's default 10-minute
timeout and killed every v0.73.0-beta.* bun-darwin-arm64 release leg with
exit 143. Raise the desktop sub-build budget to 30 minutes and widen the
build-binaries job timeout to 45 minutes to match.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task detail does not always render through modalManager.detailTask: with
Open tasks as popups it opens as FloatingWindows, with the right-sidebar
setting it opens in the right dock, and changes-tab opens use the
main-panel task-detail view. None of those were closed by
closeProjectScopedModals, so switching projects left the previous
project's task windows over the new project — and the Quick Chat
floating window (App state) stayed open the same way.
useProjectActions now receives a composite App-level reset (via a stable
ref, since the dock and main-panel pieces are constructed later in App)
that closes project-scoped modals, all popped-out task windows (new
usePoppedOutTasks.closeAll), main-panel task detail when present, the
right-dock task, and Quick Chat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebuild-job effect schedules jobSectionRef scrollIntoView in a rAF; jsdom
elements lack the method, so a frame firing before unmount threw an unhandled
error that failed the run even with all assertions passing. Module-level stub
follows the ChatView.message-edit convention; the per-test spy still overrides it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stale-test drift, no product changes: FN-8424 inbox reply help text and
fn message inbox --user routing, beta-track prerelease suffixes in version
regexes (0.73.0-beta.N), FN-8399 onMigrationProgress in createTaskStoreForBackend,
#2400 workflow-docs heading, and the durable planning-session store mocks for
fn task plan (0412113de/fdd120232).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>