Add a durable proof-point runbook for validating external plugins against released Fusion CLI builds.
- Document release selection, npm integrity capture, scaffold/build/test/install/enable validation, and pass/fail criteria for external plugin proof points.
- Link the runbook from the docs index so it remains discoverable with other plugin authoring docs.
- Extend the docs README index test to require the new proof-point runbook entry.
Files changed:
docs/README.md | 1 +
docs/plugins/external-proof-point-runbook.md | 211 +++++++++++++++++++++
.../cli/src/__tests__/docs-readme-index.test.ts | 1 +
3 files changed, 213 insertions(+)
Fusion-Task-Id: FN-6438
Fusion-Task-Lineage: 5c768d7e-4465-42ac-9535-f16dc42ab6d0
Enable mobile users to swipe across the agent detail tab strip.
- Restores horizontal touch panning on the agent detail tabs while preserving vertical pan behavior.
- Documents the mobile touch-action requirement next to the tab-strip CSS.
- Adds a mobile regression test covering pan-x touch-action and horizontal overflow.
Files changed:
packages/dashboard/app/components/AgentDetailView.css | 6 ++++++
.../__tests__/AgentDetailView.mobile-scroll.test.tsx | 15 +++++++++++++++
2 files changed, 21 insertions(+)
Fusion-Task-Id: FN-6450
Fusion-Task-Lineage: 7006af83-4490-4fe0-a357-7d19b7b522c0
Rescue the session reconnect SSE test by shutting down route-owned background work before temp cleanup.
- Isolate the focused API harness from TaskStore EventEmitter startup workers.
- Dispose API routes and scheduled AI session cleanup before deleting the test temp root.
- Remove session-reconnect from the dashboard quarantine ledger and vitest skip list.
Files changed:
.../src/__tests__/session-reconnect.test.ts | 22 ++++++++++++++++++----
packages/dashboard/vitest.config.ts | 6 ++++--
scripts/lib/test-quarantine.json | 5 -----
3 files changed, 22 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-6447
Fusion-Task-Lineage: a3fef28c-b92b-4f47-9181-a63bb31e4f48
Ensure the Secrets page root expands within the project content flex row on narrow screens.
- Let the Secrets view root flex to available width with a zero min-width.
- Preserve existing height and spacing behavior while avoiding intrinsic card-width collapse.
- Cover the mobile layout contract with a root-container CSS assertion.
Files changed:
packages/dashboard/app/components/SecretsView.css | 7 +++++++
.../dashboard/app/components/__tests__/SecretsView.mobile.test.tsx | 7 +++++++
2 files changed, 14 insertions(+)
Fusion-Task-Id: FN-6446
Fusion-Task-Lineage: 34f55d8e-ee57-432c-b1ce-165cc43333ee
Remove the CLI extension integration suite's hidden Vitest timeout override while keeping its explicit build-only allowance.
- Replace the file-wide 30s Vitest test and hook timeout override with a scoped comment documenting default caps.
- Derive the extension bundle path from the CLI root in the test file.
- Use a SQLite collision trigger instead of a TaskStore prototype spy and fix the agent delete tool payload.
- Clear the timeout appeasement allowlist now that the CLI test exemption is gone.
Files changed:
.../src/__tests__/extension-integration.test.ts | 25 ++++++++++++++++------
.../lib/test-timeout-appeasement-allowlist.json | 8 +------
2 files changed, 20 insertions(+), 13 deletions(-)
Fusion-Task-Id: FN-6436
Fusion-Task-Lineage: 670f41ea-f98f-400c-aa16-6a1cbb1e9bea
Handle logEntry failures after in-place parking without falling back to executor requeue and tighten the reliability assertion for STUCK_LOOP_EXHAUSTED.
Preserve incomplete task progress after stuck-kill budget exhaustion while marking the task failed and paused for manual intervention instead of making it scheduler-runnable again.
A real touch tap dispatches both pointerdown and touchstart, and the quick chat
send (and stop) buttons ran their action on each event. Because React had not
flushed the composer clear between the two synchronous handlers, both saw the
same input and fired handleSendMessage — and the hook's second send closed the
first's freshly-opened stream and re-POSTed, which could drop the agent's
response (notably for the first message after a response completed).
Add a tap-scoped guard (cleared after the current input task) so only the first
of the paired pointerdown/touchstart events performs the action. This is kept
separate from the 700ms onClick latch — which is shared between the send and
stop buttons — so a stop tap right after a send is never swallowed.
The earlier component test masked this because fireEvent flushes React state
between calls; the regression test now dispatches the full tap sequence in one
act() to mirror the device.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The quick chat stop button reused ChatView's `.chat-input-stop` class, which
sizes itself with `--chat-input-control-size` — a variable defined on
`.chat-input-row` in the ChatView composer and undefined in the quick chat DOM.
The invalid var collapsed the button's width (and mobile min-width) toward its
icon, leaving it noticeably narrower than the send button it replaces.
Pin the stop button to the send button's square dimensions with a compound
selector using globally-scoped spacing tokens, so it outranks the single-class
base and mobile rules from both stylesheets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the quick chat send fix. The send-time stale-stream recovery
only fires when a message is queued while the streaming flag is already
stuck, and the pre-session flush effect fires once on session activation and
bails permanently if a stream ref is lingering. Either gap leaves a queued
message stranded idle in the composer — shown locally but never sent to the
agent or persisted (so also absent from regular chat).
Add a delivery watchdog: whenever a message stays pending under an active
session, re-confirm after a short delay and force-deliver it if the server
reports no generation in flight and no live stream is connected. This is a
catch-all backstop independent of which targeted flush trigger bailed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document the current CLI quarantine decision so the deletion ratchet stays explicit.
- Record that all 24 quarantined CLI Vitest suites were re-triaged and kept in-window.\n- Capture the direct-run evidence and package-load rescue rationale in the CLI Vitest config.\n- Preserve the existing quarantine list without adding or removing suites.\n\nFiles changed:\n packages/cli/vitest.config.ts | 4 ++++\n 1 file changed, 4 insertions(+)
Fusion-Task-Id: FN-6427
Fusion-Task-Lineage: 30e4ccc7-97d3-4aa1-838c-dcb35f2734f1
Two mobile send failures with a shared symptom of "nothing happens":
- Regular chat: the send button was dead to touch. The action lived only
in onClick, but iOS suppresses the trailing synthetic click after
preventDefault() in the touch sequence, so taps never sent. Fire the send
from pointerdown/touchstart with a self-clearing dedupe latch (mirroring the
QuickChat send button), keeping a single send per tap.
- Quick chat: a queued message could strand in the composer — shown locally
but never reaching the agent or the persisted session (so it also never
appeared in regular chat). A stream that dropped without onDone/onError
(e.g. mobile tab suspension) left the streaming flag stuck true, so every
later send took the "queue while streaming" branch and was never flushed.
On a queued send, detect the stale flag via the stream's connection state
and the server's generation status, then tear down the dead stream and flush.
Both paths covered by new tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refresh stored Claude OAuth credentials before reporting dashboard auth status or resolving model auth so users do not need to repeatedly re-login after access-token expiry.
Coalesce concurrent refresh attempts, prevent stale refreshes from overwriting newer logins, and route CLI dashboard/serve/daemon/onboard auth wiring through the shared refresh-capable storage.
Correct scheduler dispatch diagnostics so capacity decisions use consistent non-negative slot counts.
- Clamp excess semaphore releases at zero and warn once when a slot is returned without an active holder.
- Recompute dispatch capacity at each queue decision, including tasks started earlier in the same scheduler tick.
- Update scheduler and semaphore tests for true binding gates, non-negative diagnostics, and workflow-step env stability.
- Add a patch changeset for the scheduler capacity fix.
Files changed:
.changeset/fn-6423-scheduler-capacity.md | 5 +
packages/engine/src/__tests__/concurrency.test.ts | 32 ++++++
.../src/__tests__/executor-step-session.test.ts | 10 +-
packages/engine/src/__tests__/scheduler.test.ts | 122 ++++++++++++++++++++-
packages/engine/src/concurrency.ts | 29 ++++-
packages/engine/src/scheduler.ts | 117 ++++++++++----------
6 files changed, 248 insertions(+), 67 deletions(-)
Fusion-Task-Id: FN-6423
Fusion-Task-Lineage: a6b2e668-a822-46e9-9cd8-ac267fbde804
Keep the task chat expand action visible as an icon-only overlay inside the chat view.
- Move the expand/collapse button out of the scrolling transcript and onto the task chat container.
- Style the control as a centered icon-only button with stable overlay positioning.
- Extend TaskChatTab coverage for empty, loading, populated, expanded, and scrolled transcript states.
Files changed:
packages/dashboard/app/components/TaskChatTab.css | 10 +++-
packages/dashboard/app/components/TaskChatTab.tsx | 26 +++++-----
.../app/components/__tests__/TaskChatTab.test.tsx | 58 +++++++++++++++++-----
3 files changed, 68 insertions(+), 26 deletions(-)
Fusion-Task-Id: FN-6425
Fusion-Task-Lineage: 6526eb4a-4be9-472b-9595-c7fcea6971b1
Prevent the terminal symbols font from corrupting ASCII cell measurement on mobile.\n\n- Add unicode-range scoping to the Fusion terminal Nerd Font symbols face.\n- Cover the CSS contract so symbols ranges include Nerd Font blocks and exclude printable ASCII.\n- Document the xterm font-loading regression and required verification path.\n\nFiles changed:\n .../xterm-symbols-nerd-font-unicode-range.md | 63 ++++++++++++++++++++++\n .../dashboard/app/__tests__/terminal-input.test.ts | 49 +++++++++++++++++\n .../dashboard/app/components/TerminalModal.css | 5 ++\n 3 files changed, 117 insertions(+)
Fusion-Task-Id: FN-6424
Fusion-Task-Lineage: 7350c926-1d51-474b-bfc3-a17f922b9322
Tightens the task detail chat layout so the composer sits closer to the transcript without affecting other detail tabs.
- Reduce the chat tab stack gap and composer vertical padding on desktop and mobile.
- Remove the chat section's inherited top margin while keeping shared tab padding intact.
- Document the spacing requirement with FNXC comments near the affected chat styles.
Files changed:
packages/dashboard/app/components/TaskChatTab.css | 10 ++++++----
packages/dashboard/app/components/TaskDetailModal.css | 7 ++++++-
2 files changed, 12 insertions(+), 5 deletions(-)
Fusion-Task-Id: FN-6418
Fusion-Task-Lineage: 4b484de5-9d6f-403b-902d-5df9f0591811
Swiping across the board to scroll horizontally was intermittent when the
gesture started on a task card. Cards render with native HTML5 `draggable`
for desktop drag-to-move, but native DnD does not function via touch — the
attribute only arms the browser's touch-drag heuristic, which non-
deterministically hijacks horizontal swipes meant to pan the board.
Disable native drag on touch-primary devices (`(hover: none) and
(pointer: coarse)`) via a new `useCoarsePointer` hook, folded into the
card's `isDraggable`. Mouse/desktop and hybrid laptops keep drag-to-move;
touch loses nothing (DnD never worked there) and panning is now reliable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable the task-detail Chat composer to submit messages with standard Enter behavior.
- Submit non-empty task chat drafts on plain Enter while preserving Shift+Enter newline entry and IME composition safety.
- Keep Cmd/Ctrl+Enter as a supported send path through the same handler.
- Document the composer keyboard shortcuts and add dashboard tests for steering, refinement, newline, shortcut, and composition behavior.
- Add a patch changeset for the published Fusion package.
Files changed:
.changeset/tiny-tasks-chat-enter.md | 5 +
docs/dashboard-guide.md | 2 +-
packages/dashboard/app/components/TaskChatTab.tsx | 13 ++-
.../app/components/__tests__/TaskChatTab.test.tsx | 120 +++++++++++++++++++++
4 files changed, 136 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-6415
Fusion-Task-Lineage: 7d200bf6-5130-4fb8-b59c-66bccffe4265
Move observed load-sensitive and slow tests out of the default lanes per the deletion-ratchet policy, keep the quarantine ledger in sync, and preserve corrupt databases when recovery fails during verification.
Move the task timing badge out of the header/meta row and into the footer's bottom-right badge cluster.
- Render the time indicator with footer chips so it aligns with retry and GitHub badges.
- Reuse footer badge sizing for the timer across desktop, wrapping, and mobile layouts.
- Update TaskCard layout tests to assert footer placement and add a patch changeset.
Files changed:
.changeset/fn-6413-task-card-timing-footer.md | 5 +
packages/dashboard/app/components/TaskCard.css | 8 +-
packages/dashboard/app/components/TaskCard.tsx | 31 ++---
.../__tests__/TaskCard.footer-alignment.test.tsx | 13 +-
.../__tests__/TaskCard.footer-wrap.test.tsx | 1 +
.../app/components/__tests__/TaskCard.test.tsx | 138 ++++++++++++++-------
.../app/components/__tests__/board-mobile.test.tsx | 5 +-
7 files changed, 130 insertions(+), 71 deletions(-)
Fusion-Task-Id: FN-6413
Fusion-Task-Lineage: a82da5ab-20c6-4dfa-92a3-150306011fd6
Moves the task chat expand control into the transcript as an overlay while preserving accessible labels and state coverage.
- Render the expand/collapse button inside the transcript container instead of a separate toolbar.
- Add tokenized overlay positioning and mobile sizing styles for the transcript expand control.
- Extend TaskChatTab tests across loading, empty, populated, and CSS states to prevent toolbar regressions.
Files changed:
packages/dashboard/app/components/TaskChatTab.css | 34 +++++++++---------
packages/dashboard/app/components/TaskChatTab.tsx | 22 ++++++------
.../app/components/__tests__/TaskChatTab.test.tsx | 42 ++++++++++++++++++++--
3 files changed, 67 insertions(+), 31 deletions(-)
Fusion-Task-Id: FN-6405
Fusion-Task-Lineage: 344d3ad0-bd57-4df2-86dc-deb8cddb76bc
Make the published plugin SDK declaration bundle self-contained for external plugins.
- Resolve private @fusion/core specifiers while emitting plugin-sdk declarations.
- Add a regression test covering built plugin-sdk .d.ts artifacts when present.
- Record a patch changeset for the published @runfusion/fusion package.
Files changed:
.changeset/FN-6409-plugin-sdk-dts-inline.md | 5 +++++
packages/cli/src/__tests__/plugin-sdk-export.test.ts | 9 +++++++++
packages/cli/tsup.config.ts | 11 ++++++++++-
3 files changed, 24 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6409
Fusion-Task-Lineage: b7590e39-763d-48ca-9301-5131d0a94b89
Screen-reader-only spans were position:absolute with no offsets, so inside
the horizontally-scrolled kanban columns they rendered off-screen-right and
ballooned documentElement.scrollWidth to ~1388px on a 390px viewport. That
triggered iOS Safari shrink-to-fit zoom-out (the "cut off, zoomed out" view,
which persisted into list view) and let the whole page pan, dragging columns
off-screen. Pinning the utility to its containing block origin (top/left: 0)
keeps the document locked to the viewport; the span stays 1px and clipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep published CLI standalone installs from omitting the pi runtime packages.\n\n- Remove pi runtime packages from optional peer declarations so npm and pnpm install them as required dependencies.\n- Add a package manifest regression test for source and prepack-transformed publish manifests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6410-standalone-pi-dependency.md | 5 +++\n packages/cli/package.json | 8 ----\n packages/cli/src/__tests__/package-config.test.ts | 47 +++++++++++++++++++++++\n 3 files changed, 52 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-6410
Fusion-Task-Lineage: 579c8a91-a021-4b36-94d7-e4651ce241cb
Restores reliable GitHub import left-pane resizing while keeping it scoped to the two-pane layout.
- Unify resize availability with the side-by-side breakpoint so the handle and fixed pane width only render when dragging is supported.
- Enlarge the divider hit area without changing the visible divider treatment.
- Cover pointer dragging, clamping, responsive hiding, and list/PR tab resize surfaces.
Files changed:
.../dashboard/app/components/GitHubImportModal.css | 20 ++++-
.../dashboard/app/components/GitHubImportModal.tsx | 35 +++++----
.../__tests__/GitHubImportModal.test.tsx | 91 +++++++++++++++++++++-
3 files changed, 127 insertions(+), 19 deletions(-)
Fusion-Task-Id: FN-6403
Fusion-Task-Lineage: 974903f5-63dd-4852-9edf-6b0ade7ca912