The mobile terminal rendered blank even though the WebSocket was Connected and
the shell prompt had already streamed in. Root cause (reproduced live): on the
mobile fullscreen layout xterm's CharSizeService can measure a 0-width character
cell, so FitAddon.fit() proposes 0 columns/rows and .xterm-screen (plus the WebGL
canvas) collapses to 0x0 — prompt bytes arrive and are written into xterm's row
DOM but paint into a zero-size box. Renderer-independent and mobile-layout-
specific; not fixed by resize/font-size re-fits because prior guards only validate
the container width and font load, never the resulting measured screen/cell width.
Add guardAgainstCollapsedTerminalScreen (app/utils/terminalPreferences.ts) and arm
it from both terminal surfaces (TerminalModal + SessionTerminal) right after their
initial fit. While the container has a width but .xterm-screen does not, it forces
a genuine DOM-strategy remeasure (forceTerminalFontRemeasure) + fit, re-driven by a
ResizeObserver until the screen has a real width. It waits (does not give up) while
the container is not yet measurable, is bounded so it never spins, and is disposed
on every re-init/close path. Recurrence of FN-7620/FN-7686.
- Add isTerminalScreenCollapsed + guardAgainstCollapsedTerminalScreen with tests
- Wire + dispose the guard across all xterm (re)init/close paths in both surfaces
- Add changeset (patch) and a docs/solutions write-up
Note: reproduced via mobile emulation (393px, iPhone UA, forced touch), not a
physical device; the guard is the structural fix — confirm on a real device.
Fusion-Task-Id: FN-7692
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Custom providers previously never enabled prompt-cache control, so agent turns re-billed the full context every request even on cache-capable backends.
- Add `CustomProvider.anthropicPromptCaching` opt-in flag in @fusion/core types
- Set pi-ai `compat.cacheControlFormat="anthropic"` on opted-in models in both registration paths: custom-provider-registry `toProviderConfig` and pi.ts `createFnAgent`
- Expose the new toggle in the dashboard CustomProvidersSection UI (with supporting CSS) and thread it through the legacy API + custom-provider routes
- Update docs (dashboard-guide, settings-reference) to document the new setting
- Add engine test coverage for the caching flag across provider registration and pi-create-fn-agent paths
- Add changeset for the fix
Files changed:
.changeset/fn-7689-custom-provider-prompt-caching.md | 7 +
docs/dashboard-guide.md | 1 +
docs/settings-reference.md | 2 +-
packages/core/src/types.ts | 15 ++
packages/dashboard/app/api/legacy.ts | 12 ++
packages/dashboard/app/components/CustomProvidersSection.css | 24 +++
packages/dashboard/app/components/CustomProvidersSection.tsx | 58 +++++-
packages/dashboard/src/routes/register-custom-provider-routes.ts | 16 ++
packages/engine/src/__tests__/pi-create-fn-agent.test.ts | 71 +++++++
packages/engine/src/__tests__/provider-registration.test.ts | 204 ++++++++++++++++++++-
packages/engine/src/custom-provider-registry.ts | 71 +++++--
packages/engine/src/pi.ts | 27 ++-
12 files changed, 473 insertions(+), 35 deletions(-)
Fusion-Task-Id: FN-7689
Fusion-Task-Lineage: b4f88f32-50da-4651-a546-432a95a1ab1c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Investigated whether --login in TerminalService first-prompt latency is a meaningful contributor and added a one-time diagnostic hint plus documentation of findings.
- Add SLOW_LOGIN_PROFILE_HINT_MS (2000ms) threshold and one-time, non-blocking console.info hint in createSession()'s PTY onData handler when a login shell is slow to produce first output
- Track spawnStartedAt and loginProfileHintLogged per session, and whether the succeeding spawn attempt used --login, without altering spawn args, timeouts, or the retry-without-login fallback
- Add regression tests covering the slow-login-profile hint behavior in terminal-service.test.ts
- Document the investigation and findings in docs/solutions/developer-experience/login-shell-profile-latency.md and link it from docs/dashboard-guide.md
- Add a patch changeset for @runfusion/fusion describing the new server-log hint
Files changed:
.changeset/fn-7688-login-shell-profile-latency.md | 7 ++
docs/dashboard-guide.md | 17 +++
.../login-shell-profile-latency.md | 79 ++++++++++++++
.../src/__tests__/terminal-service.test.ts | 121 +++++++++++++++++++++
packages/dashboard/src/terminal-service.ts | 57 ++++++++++
5 files changed, 281 insertions(+)
Fusion-Task-Id: FN-7688
Fusion-Task-Lineage: 08d5dd47-ea9f-4973-9f0e-a8d5fdeae111
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
On tablet-width viewports (769-1024px) the terminal header was overcrowded, so shortcut/zoom/preference controls now move into the bottom status-bar footer instead, mirroring the existing FN-7560 mobile layout while true desktop keeps the header controls.
- Add an isTabletTerminal detection flag (769-1024px, non-mobile) alongside the existing mobile flag
- Render the shared terminalActionControls fragment in the .terminal-status-bar footer for tablet widths, keeping desktop pin/pop-out toggles available there too
- True desktop (>1024px) continues to render font size / clear / shortcuts / preferences controls in the header
- Update TerminalModal.css with footer layout styles for the tablet action controls
- Update TerminalModal tests to cover the new tablet breakpoint rendering
- Update docs/dashboard-guide.md to describe the tablet footer control location
- Add a changeset (@runfusion/fusion: patch) documenting the fix
Files changed:
.changeset/fn-7684-tablet-terminal-footer.md | 7 ++
docs/dashboard-guide.md | 6 +-
.../dashboard/app/components/TerminalModal.css | 34 ++++++
.../dashboard/app/components/TerminalModal.tsx | 103 ++++++++++++-----
.../components/__tests__/TerminalModal.test.tsx | 126 ++++++++++++++++++---
5 files changed, 231 insertions(+), 45 deletions(-)
Fusion-Task-Id: FN-7684
Fusion-Task-Lineage: 627c40af-a152-451a-a045-65ad8babea9e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Speed up initial terminal load by short-circuiting the no-op server session list call when there are no persisted local tabs to validate.
- useTerminalSessions: when readTabsFromStorage returns zero tabs, skip the listTerminalSessions HTTP call entirely and mark bootstrap ready immediately, unblocking auto-create/WebSocket connect instead of serializing behind a provably-discarded round trip
- Reload-with-persisted-tabs path is unchanged and still awaits the list call since its result is decision-relevant there
- Add regression tests covering the fresh-load fast path and the persisted-tabs path
- Add changeset (patch) and a docs/solutions write-up of the bootstrap-list-serialized-before-auto-create issue
Files changed:
.changeset/fn-7686-slow-terminal-initial-load.md | 7 ++
...bootstrap-list-serialized-before-auto-create.md | 87 ++++++++++++++++++++++
.../hooks/__tests__/useTerminalSessions.test.ts | 73 ++++++++++++++++++
.../dashboard/app/hooks/useTerminalSessions.ts | 23 +++++-
4 files changed, 189 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7686
Fusion-Task-Lineage: 9c708329-6362-4c2e-967f-aea12849c47c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes the mobile top header wrapping onto a second line after a foldable phone is unfolded then refolded (a live resize, not a reload).
- .header now explicitly sets flex-wrap: nowrap instead of relying on the flex default
- .header-left gets flex: 1 1 auto; min-width: 0 promoted from the mobile-only media query to the base rule, so it shrinks/truncates during the resize before the width media query re-settles
- .header-actions gets flex: 0 0 auto; min-width: 0 so the action icon cluster stays at intrinsic size and is never squeezed off-row
- Added Header.test.tsx coverage asserting the nowrap/shrink contract across populated and empty header states, on mobile/tablet/desktop
- Added useViewportMode.test.ts regression reproducing a fold->unfold->refold visualViewport resize cycle, confirming mode resolves back to mobile
- Added changeset for @runfusion/fusion (patch/fix)
Files changed:
.changeset/fn-7687-mobile-header-single-line-refold.md | 7 ++
packages/dashboard/app/components/Header.css | 14 ++++
packages/dashboard/app/components/__tests__/Header.test.tsx | 78 ++++++++++++++++++++++
packages/dashboard/app/hooks/__tests__/useViewportMode.test.ts | 59 ++++++++++++++++
4 files changed, 158 insertions(+)
Fusion-Task-Id: FN-7687
Fusion-Task-Lineage: 2b88a26e-d380-458c-b602-b6496e39311d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Upgrades Quick Add action-row height parity from a bare min-height floor to a true fixed box height, then adds a mobile-only Save correction per operator feedback so Save matches its siblings at the <=768px breakpoint without touching desktop/tablet sizing.
- Pair min-height with an equal max-height (plus tokenized line-height and centered alignment) on `.quick-entry-actions .btn, .quick-entry-actions .wf-optional-steps-dropdown-trigger` at both the desktop base rule and the <=768px touch-target media block (FN-5751: never a breakpoint-only fix)
- Add a mobile-only override scoped to `[data-testid="quick-entry-save"]` inside the <=768px media query (zero vertical padding, line-height:1) so Save's text+icon content fits the same fixed box as its siblings, leaving desktop/tablet Save sizing untouched
- Add regression coverage asserting the fixed min-height==max-height contract at both breakpoints, that shared .btn-sm/.btn-icon/.dep-trigger rules are untouched, and that the Save-only override exists only inside the mobile media query
- Add a patch changeset documenting the fix and follow-up for @runfusion/fusion
Files changed:
.changeset/FN-7683-quick-add-height-parity.md | 7 +
.../quick-entry-workflow-trigger-height.test.tsx | 181 ++++++++++++++++++++-
.../dashboard/app/components/QuickEntryBox.css | 53 ++++++
3 files changed, 234 insertions(+), 7 deletions(-)
Fusion-Task-Id: FN-7683
Fusion-Task-Lineage: d3df638a-812e-4fc2-aada-cfee58d29f2b
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Summary: The Planner Chat idle send button now renders icon-only, dropping its visible "Send" text span to match TaskChatTab's regular chat send button, while keeping the accessible name "Send" via aria-label.
- Removed `showSendText` prop from the planner chat send/stop button so the idle send button no longer shows a visible text span.
- Updated the CSS FNXC comment documenting that the send-text-hiding rule is now solely load-bearing for the streaming Stop button's icon-visibility contract, not the idle Send button.
- Updated the corresponding test to assert the send button has no visible text span (icon-only) while still asserting the accessible name resolves to "Send".
Files changed:
packages/dashboard/app/components/TaskPlannerChatTab.css | 7 +++++++
packages/dashboard/app/components/TaskPlannerChatTab.tsx | 5 ++++-
.../dashboard/app/components/__tests__/TaskPlannerChatTab.test.tsx | 4 +++-
3 files changed, 14 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-7685
Fusion-Task-Lineage: 5ec896c5-7ca3-415b-bd5f-99a49f6a21ec
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Narrative: swaps a hardcoded px padding value for token-based spacing on the quick-entry workflow-selector trigger so it complies with the tokenized-CSS lint rule, while preserving visual sizing parity.
- Replace `padding: 4px 10px` with `padding: var(--space-sm) var(--space-md)` on `.quick-entry-workflow-trigger`.
- Add FNXC:QuickAddWorkflow comment explaining the token substitution and confirming height parity is governed by the existing FN-7680 min-height normalization, not this padding.
Files changed:
packages/dashboard/app/components/QuickEntryBox.css | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7682
Fusion-Task-Lineage: e2213e7d-5c38-460b-9727-57199d86b153
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Task cards previously could show the progress/steps breakdown while still in the Planning (triage) column when a review gate was active; this changes it to only show once a task leaves Planning, matching ListView's behavior.
- TaskCard.showProgressSection now only shows for `in-progress`/`executing` tasks, dropping the special-case `triage` + active-progress-count branch
- Updated FNXC:TaskCardWorkflowProgress comment to document the FN-7676 requirement
- Updated TaskCard tests to cover the new triage-column behavior
- Added changeset for the patch
Files changed:
.changeset/fn-7676-planning-steps-breakdown.md | 7 +++
packages/dashboard/app/components/TaskCard.tsx | 7 ++-
.../app/components/__tests__/TaskCard.test.tsx | 50 ++++++++++++++++++----
3 files changed, 51 insertions(+), 13 deletions(-)
Fusion-Task-Id: FN-7676
Fusion-Task-Lineage: 85fcf8f9-1010-4b68-9323-9d8ce03aa66f
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Agents were composing plans (e.g. reboot/wait-and-retry loops) that assumed they could keep acting even after the Fusion platform itself shut down, since prompts never told them they run inside Fusion. This adds a shared, docs-grounded self-awareness preamble prepended to chat, heartbeat, and executor base prompts so agents know their own runtime constraints.
- Added FUSION_RUNTIME_SELF_AWARENESS shared preamble in packages/core/src/agent-prompts.ts, exported via packages/core/src/index.ts
- Prepended the preamble to the chat system prompt (packages/dashboard/src/chat.ts)
- Prepended the preamble to the heartbeat session prompt (packages/engine/src/agent-heartbeat.ts)
- Prepended the preamble to the executor base prompt (packages/engine/src/executor.ts)
- Updated docs/agents.md and CONCEPTS.md to document the new self-awareness/capability-grounding behavior
- Added regression tests across core, dashboard, and engine covering the new prompt content
- Added changeset for @runfusion/fusion (minor, fix category)
Files changed:
.changeset/fn-7675-agent-runtime-self-awareness.md | 7 ++++
CONCEPTS.md | 4 +-
docs/agents.md | 17 ++++++++
packages/core/src/__tests__/agent-prompts.test.ts | 41 ++++++++++++++++++++
packages/core/src/agent-prompts.ts | 32 ++++++++++++++-
packages/core/src/index.ts | 1 +
packages/dashboard/src/__tests__/chat-system-prompt.test.ts | 17 ++++++++
packages/dashboard/src/chat.ts | 6 ++-
packages/engine/src/__tests__/executor-prompt.test.ts | 45 ++++++++++++++++++++++
packages/engine/src/__tests__/heartbeat-session-prompt.test.ts | 35 +++++++++++++++++
packages/engine/src/agent-heartbeat.ts | 10 +++--
packages/engine/src/executor.ts | 7 +++-
12 files changed, 213 insertions(+), 9 deletions(-)
Fusion-Task-Id: FN-7675
Fusion-Task-Lineage: 126d04a6-2c68-4347-9789-591b274277bf
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
## Problem
A task's model fields (`modelProvider`/`modelId` and the
planning/validator
equivalents) are snapshotted once at task-creation time from the
workflow's
model-lane default in `workflow_settings`. Nothing re-syncs, flags, or
surfaces drift when that default is later changed.
Concretely: the `builtin:coding` workflow's execution default was
`claude-sonnet-4-6` until it was corrected on 2026-07-05. Every task
created
before that correction stayed permanently, invisibly pinned to the stale
model id — 52 tasks were found silently stuck on it.
## Fix
- `TaskStore.getModelLaneDrift(workflowId, before, after)`
(`packages/core/src/store.ts`):
read-only diff over the three model lanes
(execution/planning/validator).
For any lane whose provider+modelId actually changed, it lists the
non-terminal (`column` not `archived`/`done`, not soft-deleted) tasks on
that workflow still pinned to the old value. Never mutates `tasks`.
- Wired into `PATCH /workflows/:id/setting-values`
(`packages/dashboard/src/routes/register-workflow-routes.ts`): captures
a
`before` snapshot, runs the existing `updateWorkflowSettingValues`
unchanged, then attaches an optional `modelDrift` field to the response
when a lane change orphans existing tasks. Backward compatible — the
field
is only present when non-empty.
- Operators can act on the surfaced drift via the existing
`POST /tasks/batch-update-models` endpoint; this change intentionally
does
not auto-rewrite any task (avoids touching tasks mid-execution).
## Testing
- New tests in `packages/core/src/__tests__/workflow-settings.test.ts`
(`TaskStore.getModelLaneDrift`): verifies a task pinned to a changed
lane's
old value is surfaced, a task already on the new value and a
`done`-column
task are excluded, and an unrelated/unchanged lane produces no drift
entry.
- `packages/core`: `npx vitest run
src/__tests__/workflow-settings.test.ts` — 24/24 pass.
- `npx tsc --noEmit` clean in both `packages/core` and
`packages/dashboard`.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Workflow setting updates can now return a lane-based model drift
summary (execution, planning, validator) showing task IDs still pinned
to the previous model configuration.
* The PATCH workflow setting response conditionally includes
`modelDrift` when impacted tasks are found.
* **Bug Fixes**
* Drift detection now compares a consistent “before” snapshot with the
updated values to avoid stale pairing.
* “No workflow selection” tasks are handled correctly based on the
default-workflow behavior.
* **Tests**
* Added coverage for lane drift across model changes and null-selection
inclusion rules.
* **Documentation**
* Added a release note entry for the change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- getModelLaneDrift now takes an explicit includeNullSelection option; the
setting-values route passes it when patching the project default workflow so
no-workflow-selection tasks (which resolve through the default) are counted
instead of silently dropped (Greptile P1).
- Add updateWorkflowSettingValuesWithPrevious so the drift baseline is captured
inside the settings write transaction, removing the stale-read race against a
concurrent patch of the same row (Greptile P2).
- Broaden getModelLaneDrift tests to cover planning and validator lanes and the
null-selection/default-workflow case (FN-5893 invariant across surfaces).
- Add changeset.
CodeRabbit's effective-values suggestion is intentionally skipped: model-lane
declarations carry no declaration-level default (KTD-7), so effective == raw for
these keys and comparing effective values is a no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes/relates to Runfusion/Fusion#1956.
## Summary
Surfaces silent runtime-resolution fallback in the dashboard, and
threads the real `FallbackReason` ("not_found" vs "factory_error")
through instead of hardcoding `"not_found"` for every fallback.
## Changes
- `packages/engine/src/runtime-resolution.ts`: `resolvePluginRuntime()`
now returns a tagged miss result (`{ ok: false, reason }`)
distinguishing "not found" from "factory/instantiation error" instead of
collapsing both to `null`. `resolveRuntime()` threads the real reason
through to `logRuntimeFallback(...)` and returns it via
`ResolvedRuntime.fallbackReason`.
- `packages/engine/src/agent-session-helpers.ts`:
`createResolvedAgentSession()` includes `fallbackReason` in the
`session:runtime-resolved` audit event metadata when present.
- `packages/dashboard/src/routes/register-task-workflow-routes.ts`: new
`GET /api/tasks/:id/runtime-fallback` endpoint, returning the most
recent `session:runtime-resolved` event normalized for UI consumption
(`wasConfigured`, `runtimeHint`, `reason`, `showFallbackBadge`).
- `packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts` (new):
polls the endpoint, dedupes toast firing per audit-event-id.
- `packages/dashboard/app/components/RuntimeFallbackBadge.tsx` (new):
renders the badge + fires the toast; wired into `TaskCard.tsx`,
`ActiveAgentsPanel.tsx`, and `AgentsView.tsx` (board and list variants).
## Test plan
- `pnpm --filter @fusion/engine exec vitest run
src/__tests__/runtime-resolution.test.ts` — 24/24 pass (21 pre-existing
+ 3 new, none weakened)
- `pnpm --filter @fusion/dashboard exec vitest run
src/routes/__tests__/register-task-workflow-routes.runtime-fallback.test.ts`
— 5/5 pass
(empty/configured-ok/fallback-with-hint/fallback-blank-hint/stale-superseded
states)
- `pnpm --filter @fusion/dashboard exec vitest run
app/components/__tests__/RuntimeFallbackBadge.test.tsx` — 8/8 pass (all
data states + mobile breakpoint + toast-fires-once)
- `pnpm --filter @fusion/dashboard run typecheck` and `pnpm --filter
@fusion/engine run typecheck` — both clean
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added runtime-fallback warning badges across task and agent views
(including board and “working on” sections) with automatic toast
notifications.
* Introduced a new backend API to surface the latest runtime-fallback
state for a task.
* Added runtime-fallback status polling and UI messaging to reflect the
most recent state.
* **Bug Fixes**
* Prevented repeated toasts by deduplicating notifications across
polling updates.
* Improved fallback reporting so the UI reflects the latest
runtime-resolved audit event.
* Enhanced diagnostics by distinguishing fallback reasons (e.g., missing
runtime vs factory failure) for clearer user guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
The repo eslint config omits react-hooks/exhaustive-deps, so a disable
directive for it is itself a lint error (rule-not-found). Replace with a
plain comment documenting the intentional dep omission.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the Command Center SDLC funnel's intake stage display text from "Triage" to "Planning" to match the renamed board column, updating English locale copy and tests; the underlying aggregator/i18n key names stay "triage".
- SdlcFunnel.tsx: stage label fallback, enteredInRange, and completionRateAria copy changed to Planning/Entered planning/in-range planning entrants; added FNXC comment explaining the key-vs-label distinction
- packages/i18n/locales/en/app.json: synced English strings for the same three keys
- SdlcFunnel.test.tsx: updated assertions to match new Planning copy
- Added changeset (patch) documenting the label rename
Files changed:
.changeset/fn-7665-funnel-planning-label.md | 7 +++++++
.../app/components/command-center/SdlcFunnel.tsx | 16 ++++++++++++----
.../command-center/__tests__/SdlcFunnel.test.tsx | 6 +++---
packages/i18n/locales/en/app.json | 6 +++---
4 files changed, 25 insertions(+), 10 deletions(-)
Fusion-Task-Id: FN-7665
Fusion-Task-Lineage: d8aec609-6b8b-4f36-bb43-61d7c7c421f9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Removes duplicated SSE streaming logic between the /automations/:id/run/stream and /routines/:id/run/stream endpoints by extracting a single generic makeRunStreamHandler factory.
- Add makeRunStreamHandler<TStore, TEntity> factory in routes.ts, parameterized by store resolver, entity getter, and not-found message
- Replace the two near-identical inline SSE handlers (connect headers, buffered replay, run subscription, auto-attach, teardown, error handling) with calls to the shared factory
- Import ScopeValue type from ./routes/types.js to type the factory's scope parameter
- Add regression tests in routes-automation.test.ts covering the consolidated handler behavior for both routes
Files changed:
.../src/__tests__/routes-automation.test.ts | 35 +++
packages/dashboard/src/routes.ts | 246 +++++++++------------
2 files changed, 137 insertions(+), 144 deletions(-)
Fusion-Task-Id: FN-7663
Fusion-Task-Lineage: 55ede468-c4ed-4a72-a428-7d19686e8ea5
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Duplicate tasks created by the same agent are no longer auto-archived by default; they are flagged for review instead, controlled by a new opt-in project setting.
- Add project setting `autoArchiveDuplicateTasksEnabled` (default false) gating the FN-4892 same-agent duplicate intake path
- Add `flagSameAgentDuplicate` path and `nearDuplicateOf` metadata used when auto-archive is disabled; tombstone-resurrection blocking is unchanged
- Wire the setting through core settings schema/types/store, dashboard SchedulingSection UI, and i18n strings
- Update docs (settings-reference.md, task-management.md) to describe the new default-off behavior
- Add a changeset for the @runfusion/fusion minor release
- Extend duplicate-intake, tombstone-window, store-parent-task-dedup, and reliability-interaction tests to cover both flag states
Files changed:
$(cat /tmp/fn7658_stat.txt)
Fusion-Task-Id: FN-7658
Fusion-Task-Lineage: 7d0d1074-1020-48a8-b96f-186154c2c408
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Retains the GitHub Import Tasks modal's provider/tab/filter/selection state so returning to Import Tasks doesn't reset the user's in-progress import setup.
- GitHubImportModal now persists provider, active tab, label filter, remote, and issue selection per project via a new modalPersistence hook, restoring them on remount instead of always defaulting.
- Added packages/dashboard/app/hooks/modalPersistence.ts to encapsulate the persisted-state read/write logic backed by projectStorage.
- projectStorage.ts gains the `kb-dashboard-github-import-state` storage key.
- Falls back to the existing default-remote auto-detect behavior when no persisted state exists.
- Added extensive test coverage in GitHubImportModal.test.tsx for the new persistence behavior.
- Updated docs/dashboard-guide.md to describe the retained state.
- Added a patch changeset for @runfusion/fusion.
Files changed:
.changeset/fn-7657-github-import-state-retained.md | 7 +
docs/dashboard-guide.md | 2 +
.../dashboard/app/components/GitHubImportModal.tsx | 204 +++++++++++++--
.../__tests__/GitHubImportModal.test.tsx | 282 +++++++++++++++++++++
packages/dashboard/app/hooks/modalPersistence.ts | 72 ++++++
packages/dashboard/app/utils/projectStorage.ts | 1 +
6 files changed, 547 insertions(+), 21 deletions(-)
Fusion-Task-Id: FN-7657
Fusion-Task-Lineage: c8086340-368c-4efd-a12a-4cddeeb0aa26
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Task model fields (execution/planning/validator provider+modelId) are
snapshotted once at task-creation time from the workflow's model-lane
default and never re-synced. Changing a workflow's default (e.g. fixing
a stale model id) silently leaves already-created tasks pinned to the
old value with no visibility — this is exactly how 52 tasks stayed
pinned to a stale claude-sonnet-4-6 default after it was corrected.
Add TaskStore.getModelLaneDrift(workflowId, before, after), a read-only
diff over the three model lanes that lists non-terminal tasks still
pinned to a lane's old value. Wire it into
PATCH /workflows/:id/setting-values so the response includes
`modelDrift` whenever a lane change orphans existing tasks. Operators
can then act via the existing POST /tasks/batch-update-models.
Fixes silent runtime fallback visibility (dashboard never read wasConfigured
or session:runtime-resolved) and threads the real FallbackReason
(not_found vs factory_error) through resolveRuntime()/logRuntimeFallback
instead of hardcoding "not_found" for every fallback.
- packages/engine/src/runtime-resolution.ts: resolvePluginRuntime() now
returns a tagged miss result distinguishing not_found from factory_error;
resolveRuntime() threads the real reason through and returns it as
ResolvedRuntime.fallbackReason
- packages/engine/src/agent-session-helpers.ts: includes fallbackReason in
the session:runtime-resolved audit event metadata
- packages/dashboard/src/routes/register-task-workflow-routes.ts: new
GET /api/tasks/:id/runtime-fallback endpoint
- packages/dashboard/app/hooks/useRuntimeFallbackStatus.ts +
packages/dashboard/app/components/RuntimeFallbackBadge.tsx: new polling
hook + badge/toast component wired into TaskCard, ActiveAgentsPanel, and
AgentsView
Ref: Fusion task FUX-022, investigations/FUX-017-hermes-runtime-fallback.md
recommendation #1
Fixes chat sessions not showing the working/"Thinking…" indicator when returning to a session whose generation was already in flight but hadn't emitted its first delta yet.
- useChat.ts: selectSession's authoritative fetchChatSession refresh now reattaches whenever refreshedSession.isGenerating===true, instead of also requiring a populated inFlightGeneration snapshot (which is null pre-first-delta)
- Added a guard so the reattach only proceeds if the user hasn't navigated away from the session while the refresh was in flight (activeSessionRef.current?.id === id)
- Calls attachIfGenerating(id, refreshedSession.inFlightGeneration, { silent: true }) when no stream is already attached, reusing existing double-attach guarding
- Added regression tests in useChat.test.ts covering the reattach-on-isGenerating-alone behavior and the stale-session navigation guard
- Added a patch changeset documenting the fix
Files changed:
.changeset/fn-7656-chat-reattach-working-state.md | 7 ++
.../dashboard/app/hooks/__tests__/useChat.test.ts | 113 +++++++++++++++++++++
packages/dashboard/app/hooks/useChat.ts | 22 +++-
3 files changed, 141 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7656
Fusion-Task-Lineage: c923c16a-391f-4475-aab8-6194bbc675f7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fix the Board Auto-approve plan shortcut leaking onto hold (Todo-like) columns; it must render only on the intake/planning column.
- Board.tsx: gate the planAutoApproveEnabled/onTogglePlanAutoApprove prop pair on columnDef.flags.intake only (dropped the || columnDef.flags.hold clause) for both the aggregate and single-workflow column renders
- Column.tsx: update the FNXC comment to document that Board.tsx is the single source of truth for this intake-only gating; Column.tsx just renders whatever prop it receives
- docs/dashboard-guide.md: correct the Board guide to describe the toggle as intake/planning-column-only, not hold columns
- Board.test.tsx: fix the existing intake/hold test expectation to assert the hold column does NOT get the toggle, and add a regression test reproducing the built-in Coding workflow's Todo (hold) column symptom
- add a patch changeset documenting the fix
Files changed:
.changeset/FN-7653-plan-auto-approve-intake-only.md | 7 +++++++
docs/dashboard-guide.md | 5 +++--
packages/dashboard/app/components/Board.tsx | 6 ++++--
packages/dashboard/app/components/Column.tsx | 5 ++++-
.../app/components/__tests__/Board.test.tsx | 20 +++++++++++++++++---
5 files changed, 35 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7653
Fusion-Task-Lineage: 10c5f01a-b00c-4039-9b83-a8a8965fc56c
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Reconcile the automation live-run panel's terminal status with the authoritative run result so successful runs no longer flash "Run failed".
- ScheduledTasksModal/RoutineCard now reconcile SSE terminal status against the POST/registry result instead of trusting raw stream teardown.
- Gate benign SSE teardown (post-terminal close, reconnect exhaustion) from being surfaced as a failure state.
- Apply the same reconciliation to both /routines/:id/run/stream and /automations/:id/run/stream routes in routes.ts.
- Add regression coverage in RoutineCard, ScheduledTasksModal, and routes-automation tests.
- Add a patch changeset documenting the fix.
Files changed:
.changeset/fn-7652-automation-live-run-false-failure.md | 7 ++
packages/dashboard/app/components/ScheduledTasksModal.tsx | 66 ++++++++++++++--
packages/dashboard/app/components/__tests__/RoutineCard.test.tsx | 37 +++++++++
packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx | 78 +++++++++++++++++++
packages/dashboard/src/__tests__/routes-automation.test.ts | 87 ++++++++++++++++++++++
packages/dashboard/src/routes.ts | 52 ++++++++++++-
6 files changed, 319 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-7652
Fusion-Task-Lineage: 61fc89f5-d25d-44de-9699-bc8ad2a0dea6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Removes the visible "Stop generation" text label from the planner chat's stop button while keeping it accessible via aria-label.
- Add showStopText prop to StandardChatActionButton (defaults to showSendText) to independently control Send vs Stop visible text
- Set showStopText={false} in TaskPlannerChatTab so the streaming stop button renders icon-only
- Update TaskPlannerChatTab test to assert no visible text span on the stop button while aria-label is retained
- Add changeset for @runfusion/fusion (patch)
Files changed:
.changeset/fn-7655-planner-stop-icon-only.md | 7 +++++++
.../dashboard/app/components/StandardChatSurface.tsx | 16 ++++++++++++++--
packages/dashboard/app/components/TaskPlannerChatTab.tsx | 3 +++
.../app/components/__tests__/TaskPlannerChatTab.test.tsx | 4 +++-
4 files changed, 27 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-7655
Fusion-Task-Lineage: f68a8bfa-30ba-439e-97d0-28654a614c54
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes project-switch hydration so a persisted "settings" view resolves to the Board instead of re-opening Settings.
- Extend resolveLandingTaskView() in useViewState.ts to treat "settings" the same as "command-center", resolving both to "board" for the auto-restored/hydrated landing view only
- Add regression tests covering the settings->board landing resolution in useViewState.test.ts
- Add changeset documenting the patch-level fix
Files changed:
.changeset/fn-7649-project-switch-board-landing.md | 7 ++
.../app/hooks/__tests__/useViewState.test.ts | 74 ++++++++++++++++++++++
packages/dashboard/app/hooks/useViewState.ts | 5 +-
3 files changed, 85 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-7649
Fusion-Task-Lineage: 7179efd6-bb1b-4ea4-a062-479b9b1fffa3
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Follow-through on FN-5048: the four residual fixed 100ms sleeps in the
websocket badge integration tests (3 subscription-establishment waits + 1
cross-instance pub/sub propagation wait) are replaced with deterministic
awaits on the server-side WebSocketManager subscription:changed event and the
shared pub/sub message event. Removes ~400ms of unconditional real-time waiting
and closes the ordering races the sleeps papered over.
Fusion-Task-Id: FN-5048
Fix the Planner Chat composer's streaming Stop button rendering narrower than the idle Send button by giving both variants a shared width floor.
- Declare a locally-scoped --chat-input-control-size on .task-planner-chat-composer (same formula as ChatView.css's .chat-input-row so the shared .chat-input-send/.chat-input-stop classes no longer read an undefined custom property and fall back to width: auto.
- Add a min-inline-size floor bound to that property on .task-planner-chat-send (present on both send and stop variants) so neither button renders narrower than the other on desktop.
- Add a regression test asserting the desktop control-size floor, the pre-existing mobile square sizing, and the FN-7594 stop-icon visibility contract.
- Add a changeset documenting the fix as a patch release.
Files changed:
.changeset/fn-7634-planner-stop-button-width.md | 7 +++++
.../app/components/TaskPlannerChatTab.css | 8 ++++++
.../__tests__/TaskPlannerChatTab.test.tsx | 33 ++++++++++++++++++++++
3 files changed, 48 insertions(+)
EOF
)
Fusion-Task-Id: FN-7634
Fusion-Task-Lineage: e73c836f-3c2d-4871-ac93-fdf5e52de5f6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Align the Priority chip, Execution-mode toggle, and Oversight dropdown trigger to the exact same box height in the task detail metadata cluster, closing a gap where a shared min-height floor still let controls diverge.
- Add explicit `height: var(--detail-priority-control-min-height)` to `.detail-priority-chip`, `.detail-execution-mode-toggle`, and `.detail-oversight-menu-trigger` alongside the existing `min-height`, so none can outgrow or undershoot the others regardless of flex stretch/content differences
- Keep `min-height` as a safety-net floor for edge cases like font scaling
- Add regression test asserting all three controls share the same fixed height token on desktop and mobile, and that the Oversight popover itself remains unaffected
- Add changeset documenting the fix as a patch-level bug fix
Files changed:
.../fn-7633-priority-execution-oversight-height.md | 7 ++++
.../dashboard/app/components/TaskDetailModal.css | 44 ++++++++++++++++---
...etailModal.responsive-and-dependencies.test.tsx | 49 ++++++++++++++++++++++
3 files changed, 95 insertions(+), 5 deletions(-)
Fusion-Task-Id: FN-7633
Fusion-Task-Lineage: 7a89743e-117f-4032-9614-a9a15f2a9b08
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Authentication settings previously enumerated providers straight from pi AuthStorage's live runtime registry, so connecting a runtime plugin (e.g. Hermes Runtime) could narrow/collapse the visible provider list. This adds a static, hand-maintained supported-provider catalog and unions it with storage-reported providers so presence in the list is deterministic while status stays live.
- Add packages/dashboard/src/routes/auth-provider-catalog.ts with STATIC_OAUTH_PROVIDER_CATALOG, STATIC_API_KEY_PROVIDER_CATALOG, and unionProviderCatalog() (catalog always wins on presence; runtime-only extras still surface; runtime name wins on name conflicts).
- Update register-auth-routes.ts's GET /api/auth/status to union the static catalogs with storage.getOAuthProviders()/getApiKeyProviders() instead of relying solely on runtime-reported providers.
- Extend routes-auth.test.ts coverage for the new catalog-union behavior (provider presence stable across narrowed runtime registries, extras preserved, name precedence).
- Add changeset fn-7625-static-auth-provider-catalog.md (patch, fix).
Files changed:
.changeset/fn-7625-static-auth-provider-catalog.md | 7 +
.../dashboard/src/__tests__/routes-auth.test.ts | 180 +++++++++++++++++++--
.../dashboard/src/routes/auth-provider-catalog.ts | 93 +++++++++++
.../dashboard/src/routes/register-auth-routes.ts | 25 ++-
4 files changed, 291 insertions(+), 14 deletions(-)
Fusion-Task-Id: FN-7625
Fusion-Task-Lineage: be984497-b881-4a8f-9860-544617e2b5f7
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes the onboarding/settings GitHub step so it never offers a dashboard OAuth login for github (no github OAuth provider is ever registered; pi only ships anthropic/github-copilot/openai-codex), replacing the broken Connect OAuth button with gh CLI guidance and a clearer server-side error.
- Remove the "Connect OAuth (optional)" button and its login-instructions panel from the onboarding branch that runs when hasGithubProvider is false (ModelOnboardingModal.tsx), since it always called handleLogin("github") against a non-existent provider
- Update ModelOnboardingModal tests to cover the new gh-CLI-only flow
- Make POST /api/auth/login return a clear, actionable 400 naming the requested provider, the registered dashboard OAuth providers, and that GitHub integration uses gh CLI/token auth instead of a generic "Unknown provider" / model-not-found error
- Add routes-auth.test.ts coverage for the improved unknown-provider error message
- Add a patch changeset documenting the fix
Files changed:
.changeset/fn-7624-github-onboarding-auth.md | 7 +++++
packages/dashboard/app/components/ModelOnboardingModal.tsx | 32 +++++++---------------
packages/dashboard/app/components/__tests__/ModelOnboardingModal.test.tsx | 32 +++++++++++++++++++++-
packages/dashboard/src/__tests__/routes-auth.test.ts | 22 +++++++++++++++
packages/dashboard/src/routes/register-auth-routes.ts | 14 +++++++++-
5 files changed, 83 insertions(+), 24 deletions(-)
Fusion-Task-Id: FN-7624
Fusion-Task-Lineage: e7be1b0a-6e50-479d-b56e-1284e8a94b7f
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Adds a close affordance to the embedded Settings header on mobile, since only a bottom nav bar (no sidebar) is available to exit there.
- Render a mobile-only `modal-close` button in the embedded Settings header when `isEmbedded && viewportMode === "mobile"`, wired to the existing `onClose` prop.
- Leave desktop/tablet embedded and the standalone modal presentation unchanged.
- Add regression tests covering the new mobile close button.
- Add a patch changeset documenting the fix.
Files changed:
.changeset/fn-7627-mobile-settings-close.md | 7 ++
.../dashboard/app/components/SettingsModal.css | 16 +++
.../dashboard/app/components/SettingsModal.tsx | 17 +++
.../__tests__/SettingsModal.mobileClose.test.tsx | 125 +++++++++++++++++++++
4 files changed, 165 insertions(+)
Fusion-Task-Id: FN-7627
Fusion-Task-Lineage: b39c7172-bce3-4fb2-9095-58ae376c43ef
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Root-caused and fixed the third recurrence of the mobile terminal shortcut bar not scrolling horizontally: styles.css's mobile lockdown resets touch-action to pan-y across ancestors, and touch-action's used value is the intersection of the touched element's and every ancestor's value, so the leaf .terminal-shortcut-panel's pan-x was silently defeated even though it was already correct.
- Opt the terminal overlay and modal ancestors (.modal-overlay.terminal-modal-overlay, .modal.terminal-modal--mobile, plain-media-query mobile modal, and the shortcut/status footer) into touch-action: pan-x pan-y so descendant leaf touch-action values can take effect
- Add FNXC:Terminal comments documenting the ancestor-intersection root cause and recurrence history (FN-7550/FN-7560)
- Add a documented solution note under docs/solutions/ui-bugs/ for the ancestor-intersection touch-action pattern
- Add regression tests asserting the modal/overlay/footer ancestors carry the pan-x pan-y opt-in
- Add a changeset for the fix
Files changed:
.../fn-7621-mobile-terminal-shortcut-scroll.md | 7 ++
...on-ancestor-intersection-defeats-leaf-scroll.md | 57 +++++++++++
.../dashboard/app/components/TerminalModal.css | 38 ++++++++
.../components/__tests__/TerminalModal.test.tsx | 106 +++++++++++++++++++++
4 files changed, 208 insertions(+)
Fusion-Task-Id: FN-7621
Fusion-Task-Lineage: 771fd79e-e193-43b0-908b-0e8fe2fc2c70
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fixes the mobile dashboard terminal sometimes rendering completely blank on open by having TerminalModal recover from a zero/collapsed container box.
- TerminalModal now attaches a persistent ResizeObserver directly on the xterm container, mirroring SessionTerminal's existing pattern
- When the container reports a zero/collapsed box on the first post-open fit, it now re-fits once the real box settles instead of staying stuck at FitAddon's degenerate 2x1-cell floor
- Added regression tests covering TerminalModal and SessionTerminal zero-geometry recovery
- Documented the root cause and fix in docs/solutions/ui-bugs/mobile-terminal-blank-render-zero-geometry-container.md
- Added a patch changeset for @runfusion/fusion
Files changed:
.changeset/fn-7620-mobile-terminal-blank-render.md | 7 +
docs/solutions/ui-bugs/mobile-terminal-blank-render-zero-geometry-container.md | 112 +++++++
packages/dashboard/app/components/TerminalModal.tsx | 49 +++
packages/dashboard/app/components/__tests__/SessionTerminal.test.tsx | 66 ++++
packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 358 +++++++++++++++++++++
5 files changed, 592 insertions(+)
Fusion-Task-Id: FN-7620
Fusion-Task-Lineage: 103f5b17-9a6e-4e9a-ab61-65ccb2203a8d
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Update five dashboard app test files whose assertions drifted from
intentional product changes that landed on main without updating them:
- graph-workflow-header: FN-7057 treats stale/missing workflow ids as the
default workflow, so FN-unknown now shows under the default selection.
- EngineControlMenu.css: FN-7340 added a 768px range-thumb touch-target
block; narrow the popover-breakpoint assertion to that selector.
- MissionManager.delete-confirm: FN-7156 removed first-mission auto-select;
explicitly select the mission before the detail-delete flow.
- board-mobile-initial-render: FN-7342 preserves board column scroll during
stabilization; FN-6825 renders the workflow toolbar on options, not callbacks.
- workflow-auto-layout: FN-7265 removed the stepwise review node (per-step
review lives in the foreach); the connected run ends at completion-summary.
No assertion was loosened or deleted to force a pass; each change cites the
breaking commit via an FNXC comment. packages/dashboard is private (no changeset).