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.
Add missing --- fences around the YAML frontmatter in
fix-database-recovery-preserve-corrupt.md so the changeset parses.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Document the root cause (offset-less position:absolute sr-only utility
escaping the kanban board's overflow clip and inflating
documentElement.scrollWidth → iOS shrink-to-fit zoom), the top/left:0 fix,
and the CSS-fixture regression guard. Cross-links the sibling mobile
viewport-containment learnings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Ensure workflow-mode boards fill the mobile viewport while preserving internal column scrolling.
- Reassert the mobile flex fill chain for project content, workflow board wrappers, and workflow columns.
- Extend mobile board regression coverage across empty/populated workflow states with and without the toolbar.
- Document the workflow board collapse root cause and add a transient temp-dir isolation guard test.
Files changed:
docs/dashboard-guide.md | 1 +
.../ui-bugs/mobile-workflow-board-fill-chain.md | 61 +++++++++++++
.../__tests__/board-mobile-initial-render.test.tsx | 101 +++++++++++++++++++++
packages/dashboard/app/styles.css | 52 +++++++++++
scripts/__tests__/check-test-isolation.test.mjs | 23 +++++
scripts/check-test-isolation.mjs | 12 ++-
6 files changed, 249 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6397
Fusion-Task-Lineage: ddee773d-6d3a-46d1-b1c7-f601a961365e
Restore the rescued dashboard routes settings test to the active suite.
- Remove routes-settings from the dashboard Vitest quarantine excludes.
- Delete its quarantine ledger entry now that the test is no longer quarantined.
Files changed:
packages/dashboard/vitest.config.ts | 1 -
scripts/lib/test-quarantine.json | 5 -----
2 files changed, 6 deletions(-)
Fusion-Task-Id: FN-6391
Fusion-Task-Lineage: 41068faa-f1e7-4f7b-a8cd-fc123611ba42
Update the non-progress churn reliability test to match the Move-Task contract for never-user-paused tasks.
- Treat undefined userPaused as not user-paused instead of requiring false.
- Document why engine rebounds should not write userPaused for never-user-paused tasks.
Files changed:
.../src/__tests__/reliability-interactions/non-progress-churn.test.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6394
Fusion-Task-Lineage: 59554c89-4e32-490f-88eb-d22ee7fdca31
Clamp long task-detail headings to two lines with an explicit expand control.
- Replace character-count title truncation with measured two-line CSS clamping.
- Add overflow detection so the Show more/Show less toggle only appears when the heading actually wraps past two lines.
- Update task detail rendering tests for triage, non-triage, editing, summarize, chat-expanded, desktop, and mobile title surfaces.
Files changed:
.../dashboard/app/components/TaskDetailModal.css | 10 +
.../dashboard/app/components/TaskDetailModal.tsx | 111 ++++-
.../__tests__/TaskDetailModal.rendering.test.tsx | 441 +++++++++------------
3 files changed, 261 insertions(+), 301 deletions(-)
Fusion-Task-Id: FN-6387
Fusion-Task-Lineage: 8c1b860c-8fe7-4f91-89e1-b387dbd7320e
Workflow context-limit recovery prompts now direct agents to read task attachments from the project root.
- Pass the project root into reduced workflow step prompts so attachment directories can be absolute when available.
- Replace the autonomous-agent "ask for context" wording with direct file-reading guidance for attachments.
- Cover root-aware, fallback, placement, and context-limit recovery attachment prompt behavior.
- Add a patch changeset for the published Fusion package.
Files changed:
.../fn-6375-workflow-attachment-recovery-prompt.md | 5 ++
.../src/__tests__/step-session-executor.test.ts | 70 +++++++++++++++++++---
packages/engine/src/step-session-executor.ts | 8 ++-
3 files changed, 72 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-6375
Fusion-Task-Lineage: ed9a5367-1623-49cb-9c7f-116c59ce5117
App.tsx and TaskCard.tsx already type the handler's initialTab as
"changes" | "retries" | "workflow", but Column.tsx — the intermediary that
forwards the prop — still declared the narrower "changes" | "retries", so the
forward failed to typecheck (TS2322).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves the onLoad conflict in the compound-engineering plugin by keeping both
the bundled ce-* persona-def install (this branch) and main's
recoverStaleSessionsForContext call.
Also addresses PR #1672 review feedback:
- executor: sentinel resume now guards on !live.paused (mirrors
runAwaitInputNode) so a still-paused task can't consume a reply and re-enter
the skill early.
- TaskCard: make the Answer-questions button text/title/aria-label fallbacks
consistent ("Answer questions") for label-in-name a11y; update test.
- ce-work: replace the unshipped `skill: ce-worktree` reference with the real
git worktree commands so Option B can't dead-end.
- ce-resolve-pr-feedback: invoke bundled scripts by absolute path via the new
FUSION_CE_SKILLS_DIR env (sessions run with cwd=projectRoot); add the Fusion
await-input sentinel path instead of AskUserQuestion for workflow steps;
normalize whitespace-only review bodies like PR comments.
- plugin: expose FUSION_CE_SKILLS_DIR (installed skills root) to step sessions.
- plan doc: add language to fenced block (markdownlint MD040).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main's workflow-graph-executor now collapses the merge-policy region
(merge-gate, branch-group-*, merge-attempt) into a single synthetic
`merge` node. Reconcile the S05 work-item-driver test's visitedNodeIds
assertion to match, and union the `observed` recording-primitive fields
from both branches (executedTasks + merge attempt/run/workflow ids).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>