Commit Graph

12166 Commits

Author SHA1 Message Date
gsxdsm
12fa85eefb fix(dashboard-tests): finish the FN-8491 mock backlog; contain FN-8557 innerWidth leakage
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>
2026-07-24 02:14:26 -07:00
gsxdsm
16d30ee7b0 fix(dashboard-tests): clear the FN-8491 plugin-MCP mock backlog unmasked by the watchdog fix
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>
2026-07-24 01:33:29 -07:00
gsxdsm
cdc5b7f901 fix(dashboard-tests): settle canvas hydration before the edge-targeted optional-group pick
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>
2026-07-24 01:20:43 -07:00
gsxdsm
973c978f91 FN-8557: preserve terminal geometry on tablets
Keep floating terminal controls available on touch tablets across responsive and keyboard viewport changes.

- Classify known tablet screens separately from phone fallbacks.
- Restore tablet floating and docked geometry at the 768px breakpoint with larger touch resize targets.
- Cover keyboard-shrunken and breakpoint tablet interactions, and document the responsive behavior.

Files changed:
 docs/dashboard-guide.md                            |   2 +-
 .../dashboard/app/components/TerminalModal.css     |  76 +++++++++--
 .../dashboard/app/components/TerminalModal.tsx     |  33 ++---
 .../components/__tests__/TerminalModal.test.tsx    | 150 +++++++++++++++++++++
 .../app/hooks/__tests__/useViewportMode.test.ts    |  14 ++
 packages/dashboard/app/hooks/useViewportMode.ts    |  33 ++++-
 6 files changed, 277 insertions(+), 31 deletions(-)

Fusion-Task-Id: FN-8557

Fusion-Task-Lineage: cd334682-e70a-4531-ab53-c5c0898f0bf1

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-24 00:59:42 -07:00
gsxdsm
c5e9a7956a fix(ci): stop watchdog false-kills of the grown core slice; actually upload timing artifacts
- 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>
2026-07-24 00:55:38 -07:00
gsxdsm
e4fc3d2acb FN-8558: enlarge Quick Chat tablet drag target
Make the floating Quick Chat header easier to drag on tablet viewports.

- Increase only the floating Chat header touch target in the tablet viewport band.
- Cover floating header controls for empty and populated chats and scope the CSS contract.
- Add a patch changeset for the Quick Chat usability fix.

Files changed:
 .changeset/quick-chat-tablet-drag-header.md        |  7 ++++
 packages/dashboard/app/components/ChatView.css     | 13 +++++++
 packages/dashboard/app/components/__tests__/ChatView.mobile.test.tsx  | 41 ++++++++++++++++++++++
 packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx   | 20 +++++++++++
 4 files changed, 81 insertions(+)

Fusion-Task-Id: FN-8558

Fusion-Task-Lineage: 2d2f788b-b417-4fa3-b066-8790858b393a

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-24 00:46:44 -07:00
gsxdsm
c3cebcd56b FN-8556: fix Windows agent target selection
Make direct-chat Agent selection reliable when Windows Electron suppresses the click after pointer activation.

- Switch target mode on primary pointerdown while retaining keyboard activation.
- Expose selected Model and Agent modes with aria-pressed semantics.
- Cover pointer activation and persistence across direct-chat layouts.
- Add a patch changeset for the Windows selection fix.

Files changed:
 .changeset/fn-8556-windows-agent-selection.md      |  7 ++++
 .../app/components/ChatThinkingLevelControl.tsx    | 28 ++++++++++++-
 .../__tests__/ChatThinkingLevelControl.test.tsx    | 27 +++++++++++--
 .../__tests__/ChatView.thinking-level.test.tsx     | 46 +++++++++++++++++-----
 4 files changed, 93 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-8556

Fusion-Task-Lineage: 25b06177-3b59-4d2d-8440-e92fe7172259

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-24 00:35:34 -07:00
gsxdsm
f6795317a8 fix(dashboard-tests): useTasks column normalization asserts the post-cutover contract
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>
2026-07-24 00:28:55 -07:00
gsxdsm
94f1bd9527 fix(dashboard-tests): align remaining ChatView send assertions with FN-8502 delivery callbacks
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>
2026-07-24 00:09:44 -07:00
gsxdsm
fa6e4fef22 fix(dashboard-tests): satisfy FN-8491 plugin-MCP store contract across route suites; align planning create-task envelope
- 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>
2026-07-23 23:51:29 -07:00
gsxdsm
74236211ea fix(dashboard): repaint stalled terminal renderer so first shell prompt shows without a keypress
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>
2026-07-23 23:51:12 -07:00
gsxdsm
d7752931b0 fix(dashboard-tests): align remote-access mock store with FN-8491, ChatView mobile with FN-8502, and de-race planning claim tests
- 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>
2026-07-23 23:09:59 -07:00
gsxdsm
da0d73c853 fix(release): give desktop closure staging 30min in bun binary builds
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>
2026-07-23 22:56:52 -07:00
gsxdsm
f9f2832936 fix(dashboard): dismiss popped-out/main-panel/dock task detail and Quick Chat on project switch
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>
2026-07-23 22:53:04 -07:00
gsxdsm
ec535b5f3c fix(dashboard-tests): stub Element.prototype.scrollIntoView in SystemControlsArea suite
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>
2026-07-23 22:37:38 -07:00
gsxdsm
e17151cba5 fix(cli-tests): align CLI suite with inbox-mail help, beta versions, and planning-session store
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>
2026-07-23 22:35:12 -07:00
gsxdsm
593f38249c chore(release): v0.73.0-beta.6
Version bump via changesets.
2026-07-23 22:16:37 -07:00
gsxdsm
0a6f84bbdf fix(dashboard): index the FN-8348 Report row in settings search; re-green backfill test lane
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>
2026-07-23 22:13:25 -07:00
gsxdsm
1d4cd27c73 fix(tests): give PG-harness plugin suites the 15s timeout the golden-template harness budgets for
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>
2026-07-23 22:09:51 -07:00
gsxdsm
d8b5ef62db fix(dashboard): key Chat, Missions, and GitHub Import by project against cross-project leaks
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>
2026-07-23 21:59:55 -07:00
gsxdsm
0dd34cd306 fix(chat): don't wipe task-bound planner chat history on /new or /clear
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>
2026-07-23 21:58:21 -07:00
gsxdsm
049c36c9ae fix(chat): narrate task-chat step numbers 1-based to match the card's step count
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>
2026-07-23 21:43:58 -07:00
gsxdsm
345dccbe7d fix(dashboard): consume seeded Planning initial plan on auto-start to stop duplicate sessions
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>
2026-07-23 21:43:47 -07:00
gsxdsm
b007de5f94 fix(ci,tests): repair binary release pipeline and re-green the full suite
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>
2026-07-23 21:43:00 -07:00
gsxdsm
336b0cf95d fix(dashboard): key subtask breakdown by project to stop cross-project draft leaks
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>
2026-07-23 21:37:11 -07:00
gsxdsm
86f56b5cdc fix(dashboard): auto-start terminal sessions when the server host is not Windows
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>
2026-07-23 21:33:55 -07:00
gsxdsm
07541f78e2 fix(dashboard): reset project-scoped modals and remount Planning on project switch
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>
2026-07-23 21:29:54 -07:00
gsxdsm
3b55502d48 fix(ci): full-suite bookkeeping v32 — AgentsView heartbeat mock (#2432)
## 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 -->
2026-07-23 21:19:33 -07:00
gsxdsm
26628b356b chore(release): v0.73.0-beta.5
Version bump via changesets.
2026-07-23 20:23:55 -07:00
gsxdsm
0412113de7 fix(dashboard,cli): un-dead-end deleted plan tasks; harden fn task plan per review
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>
2026-07-23 20:19:18 -07:00
gsxdsm
fdd1202328 feat(cli): claim-aware multi-task planning parity for fn task plan
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>
2026-07-23 20:01:50 -07:00
gsxdsm
fbaf3c56d2 fix(dashboard): close terminal bootstrap residuals from the terminal review
- 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>
2026-07-23 19:49:43 -07:00
gsxdsm
faeb491ea4 fix(dashboard): harden terminal paste contract and manual-start button per review
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>
2026-07-23 19:46:02 -07:00
gsxdsm
ca4639bb67 feat(dashboard): allow multiple tasks from one plan via epoch-scoped claims
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>
2026-07-23 19:37:49 -07:00
gsxdsm
907e8d03e6 fix(dashboard): terminal manual-start on Windows clients and single-delivery Ctrl/Cmd+V paste
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>
2026-07-23 19:30:45 -07:00
gsxdsm
0e6108a1a5 fix(dashboard): Stop cancels pending initial planning turns per-session
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>
2026-07-23 18:52:19 -07:00
gsxdsm
370a7a645d fix(dashboard): a validated plan stays readable, editable, and creatable
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>
2026-07-23 18:52:19 -07:00
gsxdsm
ddea2876c4 fix(dashboard): let the planning workspace loader scroll on short viewports
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>
2026-07-23 18:52:19 -07:00
gsxdsm
e2ee8ba276 fix(dashboard): stream AI thinking on every Planning Mode generation step
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>
2026-07-23 18:39:10 -07:00
gsxdsm
7cb87e7687 fix(dashboard): regenerate interview questions in mission/milestone/onboarding too
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>
2026-07-23 18:39:10 -07:00
gsxdsm
64b20c8be3 fix(dashboard): regenerate a planning question instead of "No active question in session"
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>
2026-07-23 18:29:59 -07:00
gsxdsm
b462978e92 fix(dashboard): emit planning terminal error exactly once on stream reconnect
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>
2026-07-23 18:18:04 -07:00
gsxdsm
2bebed18b2 fix(dashboard): pin mobile comment composer to visualViewport above keyboard
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.
2026-07-23 18:16:28 -07:00
gsxdsm
b67dca4178 fix(dashboard): stop Chat Latest button jumping under the cursor
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.
2026-07-23 18:15:03 -07:00
gsxdsm
716e698628 fix(dashboard): stop Planning Mode hanging on provider errors mid-generation
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>
2026-07-23 18:14:29 -07:00
gsxdsm
dcbf923e07 fix(dashboard): keep planning comment editor open after selection collapse
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.
2026-07-23 18:12:52 -07:00
gsxdsm
34a9216509 fix(dashboard): prevent comment-composer keyboard blur without early commit
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.
2026-07-23 18:09:13 -07:00
gsxdsm
b6a930eff0 fix(dashboard): commit planning comments on pointerdown before keyboard blur
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.
2026-07-23 18:03:21 -07:00
gsxdsm
45da5f71d3 fix(dashboard): keep planning comment composer above the keyboard
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).
2026-07-23 17:56:05 -07:00
gsxdsm
f8e6fd5e2b fix(dashboard): stack tablet Add comment above actions with mobile icon
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.
2026-07-23 17:49:53 -07:00