Commit Graph

11221 Commits

Author SHA1 Message Date
gsxdsm
dabedcf79c FN-7890: cap report-a-bug GitHub URL by encoded length, not raw body length
Fixes the Report-a-Bug flow producing a GitHub "request URL too long" error by budgeting truncation against the actual encoded URL GitHub receives instead of the raw diagnostics body length.

- Replace the raw BUG_URL_BODY_CAP (5500 chars) with BUG_URL_MAX_ENCODED (8000 chars), measured against the final GitHub issue URL (base URL + ?body= + encodeURIComponent(body)).
- Add buildBugReportIssueUrl() which binary-searches the largest body prefix (by code point) whose encoded URL still fits the budget, appending a truncation marker when needed.
- doReportBug now calls buildBugReportIssueUrl(body) instead of manually slicing the body and encoding it inline.
- Update tests to assert the final URL length stays under BUG_URL_MAX_ENCODED and to exercise a diagnostics bundle whose JSON (quotes/braces) expands significantly under percent-encoding.

Files changed:
 .../__tests__/SystemControlsArea.test.tsx          |  9 +++--
 .../command-center/areas/SystemControlsArea.tsx    | 47 +++++++++++++++++-----
 2 files changed, 44 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7890

Fusion-Task-Lineage: fcadafcd-0bbc-4c06-b2ec-c2b124d736db

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 18:10:55 -07:00
gsxdsm
c341b58bde FN-7889: align task card header action icons to the id's optical centerline
Vertically nudges the card-header-actions cluster (size badge/menu icons) to share the same optical centerline as the mono task id, matching the existing id nudge.

- Add a translateY(calc(var(--space-xs) / 4)) transform to .card-header-actions in TaskCard.css, mirroring the FN-7871 id nudge
- Document the change with an FNXC comment explaining the FN-7889 requirement and its relationship to FN-7871/FN-7862/FN-7837
- Extend the TaskCard.badge-wrap.test.tsx regression test to assert the actions cluster's transform matches the id's transform and stays tokenized (no raw px values)

Files changed:
 packages/dashboard/app/components/TaskCard.css                      | 6 +++++-
 .../dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx | 4 ++++
 2 files changed, 9 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7889

Fusion-Task-Lineage: d4743b8f-c7d7-4c3b-9b68-f632dd6e0b7b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 18:06:38 -07:00
gsxdsm
db9a9453db FN-7887: migrate ChatView copy-response to shared clipboard helper
Fixes ChatView's provider-response copy button falsely reporting failure on non-secure origins (mobile/HTTP) by routing through the shared clipboard utility instead of calling navigator.clipboard directly.

- Replaced direct navigator.clipboard.writeText call in handleCopyResponse with the shared copyTextToClipboard helper (secure-context guard + execCommand fallback)
- Added regression tests covering secure Clipboard API success, execCommand fallback success, and combined failure paths
- Added changeset documenting the fix as the last direct clipboard caller found during the FN-7885 preflight

Files changed:
 .changeset/fn-7887-chatview-clipboard.md           |   7 +
 packages/dashboard/app/components/ChatView.tsx     |  16 +-
 .../__tests__/ChatView.copy-response.test.tsx      | 169 +++++++++++++++++++++
 3 files changed, 183 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7887

Fusion-Task-Lineage: bff1084b-7c64-420d-9479-bb8c4c584175

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:59:19 -07:00
gsxdsm
6ea53966f6 FN-7885: migrate remaining direct clipboard callers to shared copyTextToClipboard helper
Replaces the last direct navigator.clipboard.writeText() call sites across dashboard components and the reports plugin with the shared copyTextToClipboard helper, fixing copy actions that crashed or silently failed on non-secure origins (HTTP/mobile).

- Migrated AgentDetailView, AgentErrorDetailsModal, CliBinaryPanel, GitManagerModal, LoginInstructions, PrPanel, SecretsView, and StashConflictModal to use copyTextToClipboard (secure-context guard + execCommand fallback, boolean result handling) instead of calling navigator.clipboard directly.
- Migrated the fusion-plugin-reports ShareBlocksPanel to the same helper and added a vitest alias so the plugin's subpath import resolves to the dashboard's copyToClipboard util instead of collapsing to its package root.
- Added ./app/utils/copyToClipboard subpath export to @fusion/dashboard's package.json.
- Added/extended tests covering copy success, fallback, and failure paths for AgentDetailView, CliBinaryPanel, AgentErrorDetailsModal, GitManagerModal, SecretsView, StashConflictModal, and ShareBlocksPanel.
- Added a patch changeset documenting the fix for @runfusion/fusion.

Files changed:
 .changeset/fn-7885-clipboard-migration.md          |  7 ++
 packages/dashboard/app/components/AgentDetailView.tsx   | 16 ++++-
 packages/dashboard/app/components/AgentErrorDetailsModal.tsx      |  5 +-
 packages/dashboard/app/components/CliBinaryPanel.tsx    | 16 +++--
 packages/dashboard/app/components/GitManagerModal.tsx   | 16 ++++-
 packages/dashboard/app/components/LoginInstructions.tsx | 21 +++---
 packages/dashboard/app/components/PrPanel.tsx      |  9 ++-
 packages/dashboard/app/components/SecretsView.tsx  | 11 ++-
 packages/dashboard/app/components/StashConflictModal.tsx          | 13 ++--
 packages/dashboard/app/components/__tests__/AgentDetailView.copy.test.tsx        | 56 +++++++++++++++
 packages/dashboard/app/components/__tests__/AgentErrorDetailsModal.test.tsx      | 18 +++++
 packages/dashboard/app/components/__tests__/CliBinaryPanel.copy.test.tsx         | 68 ++++++++++++++++++
 packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx  | 23 ++++++
 packages/dashboard/app/components/__tests__/SecretsView.test.tsx  | 82 ++++++++++++++++++++++
 packages/dashboard/app/components/__tests__/StashConflictModal.test.tsx          | 25 ++++++-
 packages/dashboard/package.json                    |  4 ++
 plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.tsx  |  5 +-
 plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ShareBlocksPanel.test.tsx | 43 +++++++++++-
 plugins/fusion-plugin-reports/vitest.config.ts     |  2 +
 19 files changed, 402 insertions(+), 38 deletions(-)

Fusion-Task-Id: FN-7885
Fusion-Task-Lineage: 122a54ea-962b-4ae1-98ac-c28e03f4f8ca
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:54:21 -07:00
gsxdsm
3a37f48e78 FN-7883: expand Command Center bug report with full diagnostics bundle
Replace the bug-report flow's last-5-error-lines excerpt with the full buildDiagnostics() bundle (health, runtime/system info, recent logs) behind a single confirmation prompt.
- doReportBug now reuses buildDiagnostics() instead of independently fetching a 5-line error excerpt
- Single confirm() prompt covers the whole diagnostics bundle instead of only recent errors
- Diagnostics are embedded as a collapsible <details> JSON code block under a new ### Diagnostics section
- Preserved existing safeguards: fenceSafe() neutralization of embedded code fences and BUG_URL_BODY_CAP truncation
- Added tests covering: full bundle inclusion on confirm, omission on decline, fence-breakout neutralization, and oversized-bundle truncation

Files changed:
 .../__tests__/SystemControlsArea.test.tsx          | 96 ++++++++++++++++++++++
 .../command-center/areas/SystemControlsArea.tsx    | 53 +++++++-----
 2 files changed, 128 insertions(+), 21 deletions(-)

Fusion-Task-Id: FN-7883

Fusion-Task-Lineage: 44dd26de-d0f6-427b-a082-59d580f31674

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:42:39 -07:00
gsxdsm
2e7fce21ae FN-7884: reset durable-agent error state on engine restart
Engine startup now treats itself as an implicit operator retry for durable heartbeat agents stuck in error, clearing eligible error states and re-arming heartbeats instead of waiting for the steady-state sweep's cooldown/exhaustion gates.

- Add SelfHealingManager.resetDurableAgentErrorStateOnStartup(), run first in runStartupRecovery(), which resets shared heartbeatErrorRecovery/legacy durableErrorRecovery metadata, clears lastError/pauseReason, flips eligible error and error-retry-exhausted-parked durable agents to active, and re-arms their heartbeat
- Preserve suppression for operator-actionable, stale worktree/module-resolution, user-paused, error-unrecoverable, ephemeral, disabled-runtime, and actively-executing agents
- Add agent:reset-error-state-on-startup run-audit mutation type with ids/counts/outcomes-only metadata (agentId, priorState, priorPauseReason, source)
- Add changeset FN-7884 (patch) documenting the operator-facing behavior
- Update AGENTS.md and docs/agents.md, docs/architecture.md to describe the new startup reset path alongside existing FN-7835/FN-7844/FN-7859/FN-7878 recovery docs
- Extend self-healing.test.ts with coverage for the new startup reset behavior and its exclusions

Files changed:
 .changeset/fn-7884-restart-error-reset.md          |   7 ++
 AGENTS.md                                          |   1 +
 docs/agents.md                                     |   4 +-
 docs/architecture.md                               |   2 +-
 packages/engine/src/__tests__/self-healing.test.ts | 127 ++++++++++++++++++++-
 packages/engine/src/run-audit.ts                   |   1 +
 packages/engine/src/self-healing.ts                |  88 +++++++++++++-
 7 files changed, 223 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7884
Fusion-Task-Lineage: fe64f6af-3ff3-4876-8308-8a75591c45f1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:39:29 -07:00
gsxdsm
e92a342b1e FN-7882: fix Copy diagnostics crash on non-secure origins
Route Command Center diagnostics copy through the shared clipboard helper so it no longer throws on non-secure origins (e.g. mobile http://fusionstudio:4040).

- Replace direct navigator.clipboard.writeText call in SystemControlsArea's diagnostics copy handler with copyTextToClipboard, which guards for secure-context clipboard support and falls back to document.execCommand("copy").
- Surface a distinct failure toast ("Could not copy diagnostics to clipboard") when neither clipboard path succeeds, instead of crashing.
- Add regression tests covering the execCommand fallback, the secure-context Clipboard API path, and the failure-toast path when both copy mechanisms are unavailable.
- Add a patch changeset documenting the fix.

Files changed:
 .changeset/fn-7882-copy-diagnostics-fix.md         |  7 ++
 .../__tests__/SystemControlsArea.test.tsx          | 87 +++++++++++++++++++++-
 .../command-center/areas/SystemControlsArea.tsx    | 13 +++-
 3 files changed, 102 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7882
Fusion-Task-Lineage: eceb93bf-b32d-4127-b370-4779cb4e4e27
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:32:41 -07:00
gsxdsm
b84bd11256 FN-7881: fix System-controls refresh button mobile layout
Keeps the Command Center System tab's refresh button inline with the section title and pinned to the far right at all breakpoints, instead of dropping below the title on mobile.

- Add a scoped .cc-area-section-header.cc-system-controls-header CSS override (row + space-between) that wins over the shared mobile column-collapse rule for the System tab only
- Add data-testid="cc-system-refresh" to the refresh button for test targeting
- Add regression tests asserting the header stays row-aligned in the DOM and the CSS override is present at the 768px breakpoint
- Add a patch changeset documenting the fix

Files changed:
 .changeset/fn-7881-system-refresh-inline.md        |  7 +++++++
 .../__tests__/SystemControlsArea.test.tsx          | 23 ++++++++++++++++++++++
 .../command-center/areas/SystemControlsArea.css    | 22 +++++++++++++++++++++
 .../command-center/areas/SystemControlsArea.tsx    |  3 ++-
 4 files changed, 54 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7881

Fusion-Task-Lineage: 80c6fd78-b7e0-4899-b509-b69b8d21e82d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:09:15 -07:00
gsxdsm
c745990aa2 FN-7879: deliver one-time Postgres-migration inbox notice on first 0.59 startup
Adds a best-effort, idempotent dashboard inbox notice announcing the upcoming embedded-Postgres storage migration, delivered once per project on the first engine start under the Fusion 0.59.x release line.

- New `deliverPostgresMigrationNoticeIfNeeded` in `@fusion/engine` (`postgres-migration-notice.ts`) builds and sends a `system` -> `user` inbox message via `MessageStore`, gated to version `0.59.x` by `isPostgresMigrationNoticeVersion`
- Idempotency via existing inbox message `metadata.kind = "postgres-migration-notice"` marker (no new settings key or table), so restarts never duplicate the notice
- Delivery is fully best-effort: any `MessageStore` failure is caught, logged as a warning, and never blocks or fails `ProjectEngine.start()`
- `ProjectEngine.start()` invokes the notice after runtime start, using an injected `cliPackageVersion` threaded from the CLI layer through `EngineManagerOptions` / `ProjectEngineOptions` so the engine never imports CLI/dashboard code directly
- `daemon.ts`, `dashboard.ts`, and `serve.ts` resolve the published `@runfusion/fusion` version via `getCliPackageVersion` / `isUnresolvedCliPackageVersion` and pass it into `ProjectEngineManager`
- Exported new symbols (`POSTGRES_MIGRATION_HELP_URL`, `POSTGRES_MIGRATION_NOTICE_KIND`, `deliverPostgresMigrationNoticeIfNeeded`, `isPostgresMigrationNoticeVersion`, related types) from `@fusion/engine`, and `isUnresolvedCliPackageVersion` from `@fusion/dashboard`
- New unit tests covering version matching and single-delivery/idempotency behavior
- Docs updated (`docs/agents.md`, `docs/dashboard-guide.md`) to describe the one-time notice and its dedup key
- Changeset added for `@runfusion/fusion` (minor, feature)

Files changed:
 .changeset/fn-7879-postgres-migration-inbox-notice.md              |   7 ++
 docs/agents.md                                                     |   1 +
 docs/dashboard-guide.md                                            |   1 +
 packages/cli/src/commands/daemon.ts                                |   6 +-
 packages/cli/src/commands/dashboard.ts                             |   5 +
 packages/cli/src/commands/serve.ts                                 |   6 +-
 packages/dashboard/src/index.ts                                    |   2 +-
 packages/engine/src/__tests__/postgres-migration-notice.test.ts    | 140 +++++++++++++++++++++
 packages/engine/src/index.ts                                       |   9 ++
 packages/engine/src/postgres-migration-notice.ts                   | 107 ++++++++++++++++
 packages/engine/src/project-engine-manager.ts                      |   6 +
 packages/engine/src/project-engine.ts                               |  12 ++
 12 files changed, 299 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7879

Fusion-Task-Lineage: 201877e5-6bdc-4168-a8ac-ae0e50ec8308

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 17:02:33 -07:00
gsxdsm
56c745240a FN-7880: shorten Reset Settings button to "Reset" on mobile
Shortens the Settings footer's Reset Settings button label to Reset at the mobile breakpoint to preserve footer space, while desktop/tablet keep the full label; confirmation dialog and reset behavior are unchanged.

- Add settings.reset.buttonShort i18n key (en, zh-CN) and typed resources.d.ts entry
- SettingsModal reset button now renders buttonShort ("Reset") when viewportMode === "mobile", otherwise the existing "Reset Settings" label
- Update settings-mobile.test.tsx to assert the compact mobile label and add coverage across modal/embedded x mobile/desktop viewport combinations
- Update docs/dashboard-guide.md to document the mobile-only compact label
- Add changeset (patch) for @runfusion/fusion

Files changed:
 .changeset/fn-7880-reset-mobile-label.md           |  7 ++++
 docs/dashboard-guide.md                            |  6 ++--
 packages/dashboard/app/components/SettingsModal.tsx |  7 +++-
 packages/dashboard/app/components/__tests__/settings-mobile.test.tsx | 37 +++++++++++++++++++++-
 packages/i18n/locales/en/app.json                  |  1 +
 packages/i18n/locales/zh-CN/app.json               |  1 +
 packages/i18n/src/resources.d.ts                   |  1 +
 7 files changed, 56 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7880

Fusion-Task-Lineage: e636e73d-172e-4a6e-bb51-078520fd05ab

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 16:48:04 -07:00
gsxdsm
504dc69f02 FN-7878: default heartbeat error recovery to recoverable for generic durable-agent failures
Durable agents were parking as error-unrecoverable on any non-transient-pattern failure, even generic/unknown blips that manual Retry immediately fixed; this changes the default to recoverable and reserves immediate unrecoverable parking for operator-actionable errors.

- isHeartbeatErrorRecoverable now returns true unless the error is operator-actionable (auth/model/billing/scope) or a stale worktree/module-resolution error, instead of requiring a transient-pattern match via classifyError
- Add OAuth scope-requirement and insufficient-scope patterns to the operator-actionable error detector so those still park immediately
- Update heartbeat-error-recovery, heartbeat-executor, self-healing, and transient-error-detector tests to cover the new default-recoverable behavior
- Update AGENTS.md and docs/architecture.md durable-agent error recovery notes to describe the new recoverable-by-default policy
- Add changeset documenting the fix

Files changed:
 .changeset/fn-7878-recoverable-default.md          |  7 ++
 AGENTS.md                                          |  2 +-
 docs/architecture.md                               |  4 +-
 .../src/__tests__/heartbeat-error-recovery.test.ts | 90 +++++++++++++++++++---
 .../src/__tests__/heartbeat-executor.test.ts       | 17 ++--
 packages/engine/src/__tests__/self-healing.test.ts | 45 ++++++-----
 .../src/__tests__/transient-error-detector.test.ts |  7 +-
 packages/engine/src/agent-heartbeat.ts             |  8 +-
 packages/engine/src/transient-error-detector.ts    |  2 +
 9 files changed, 137 insertions(+), 45 deletions(-)

Fusion-Task-Id: FN-7878

Fusion-Task-Lineage: 6f929af9-ceef-404f-95c9-98f26478f020

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 16:25:48 -07:00
gsxdsm
990583261e FN-7877: fix Command Center System tab spacing and canonicalize status colors
Wraps the System tab's controls and stats areas in a shared flex container so vertical rhythm is consistent, and canonicalizes hard-coded status colors to design tokens.

- Wrap SystemControlsArea + SystemStatsArea in a new .cc-system-tab flex container with --space-lg gap, replacing the bare React fragment, so Server logs and Live system health sections get consistent breathing room.
- Add responsive gap rule for .cc-system-tab under the 768px breakpoint.
- Replace hard-coded --danger/--warning fallback colors in SystemControlsArea.css with canonical --color-error/--color-warning tokens.
- Add SystemControlsArea test coverage.
- Add a patch changeset documenting the spacing fix.

Files changed:
 .changeset/fn-7877-system-tab-spacing.md           |   7 +
 .../components/command-center/CommandCenter.css    |  15 ++
 .../components/command-center/CommandCenter.tsx    |   8 +-
 .../__tests__/SystemControlsArea.test.tsx          | 157 +++++++++++++++++++++
 .../command-center/areas/SystemControlsArea.css    |   8 +-
 5 files changed, 189 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7877
Fusion-Task-Lineage: eb4a97ed-5442-43e8-8d38-348fc3e220a8
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 16:11:34 -07:00
gsxdsm
37c0816f57 docs: clarify Homebrew trusted install 2026-07-12 15:37:02 -07:00
gsxdsm
2ffebef022 Address PR feedback: redact TUI/console log path too (#2028)
Move redactSecrets to the log/warn/error entry points so both the
recorded history (served over /system/logs) and the TUI/console output
are masked — previously only the stored entry was redacted while the raw
message still printed to the terminal. Add assertions for both the
console and TUI-target output paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 14:42:09 -07:00
gsxdsm
f1b6a6340c Address review feedback: System panel hardening (#2028)
Correctness/reliability:
- engine restart-all: compensating pause on resume failure so a project
  is never left marked active with a dead engine
- desktop restart: app.quit() (runs before-quit teardown) not app.exit(0)
- supervisor: SIGINT/SIGTERM during crash-backoff exit immediately;
  stopping-latch prevents respawn after intentional shutdown
- coalesce concurrent /system/restart requests (restartScheduled guard)
- rebuild job completion chain given a .catch (no stranded activeJob)

Security:
- same-origin CSRF guard on all mutating /system/* POSTs (safe under
  --no-auth / desktop)
- redact secrets in host-process log history (reuse core redactSecrets)
- report-bug: confirm before including server logs + escape ``` fences
- sanitize restart reason; Object.hasOwn scope-guard (prototype-key 500)

Frontend:
- log tail dedup (drop redundant REST backfill; SSE heartbeat stops 45s
  reconnect churn) + X-Accel-Buffering:no on both SSE routes
- restart-wait 90s timeout re-enables controls
- hydrate buffered rebuild lines on mount (mid-build panel open)

Maintainability + tests:
- engineAvailable reflects centralCore; hoisted systemLogs option; typed
  desktop systemControl DTO
- add tests: SSE routes, exit-86 respawn, compiled-binary respawn,
  engine-restart recovery, log redaction

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 14:42:09 -07:00
gsxdsm
a227b19a22 feat: add Command Center System panel with rebuild/restart controls, Plugins tab, and supervised-by-default dashboard
- pnpm dev / new pnpm start default to the dashboard command
- fn dashboard (and bare fn/fusion/npx, incl. packaged binaries) now runs
  supervised by default via an attached foreground child (TUI-safe);
  --no-supervise opts out; FUSION_RESTART_EXIT_CODE=86 = intentional restart
- New /api/system routes: info, restart, rebuild jobs with SSE output,
  engine restart, agents restart-all, plugins reload-all, log tail
- System tab: rebuild & restart (source checkouts only, hidden elsewhere),
  restart server/engine/agents, backup DB, live server logs, copy
  diagnostics, report bug; new Plugins tab reusing PluginManager
- Desktop restart via Electron app.relaunch(); DashboardLogSink now keeps a
  bounded history + listener feed for the log viewer

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 14:42:09 -07:00
gsxdsm
cbe07ee86b fix(engine): address PR #2027 review — tighten auth exclusions, accurate park accounting
- Exclude revoked/suspended/disabled/deactivated keys, inactive subscriptions,
  and locked accounts from the transient-auth classifier: no retry fixes those,
  so they stay operator-actionable even inside an authentication_error envelope.
- Self-healing sweep logs unrecoverable-error parks separately from
  recovered-to-active agents (return value still counts actions taken).
- Document same-session retry continuation semantics at the heartbeat
  withRateLimitRetry call site (side-effect replay concern).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:57:08 -07:00
gsxdsm
c4fad2d793 fix(engine): auto-recover agents from transient OAuth token-rotation 401s
A routine Claude Max OAuth token rotation (~8h) fails the in-flight call with
401 authentication_error "Invalid authentication credentials" even though
refreshed credentials already exist on disk. Three compounding defects turned
that into a fleet-wide operator-action park:

- The heartbeat prompt path never ran under withRateLimitRetry (executor/
  triage/merger all do), so the 401 immediately failed the run. Now wrapped.
- The 401 matched the operator-actionable /credential/ pattern and defaulted
  to "permanent", so FN-7859 parked agents paused/error-unrecoverable. A new
  shared isTransientAuthCredentialError classifier (also used by
  rate-limit-retry) classifies rotation 401s transient + not operator-
  actionable; OAuth scope-grant and API-key failures still park.
- Heartbeat failure classification ran on the stack-bearing error detail;
  stack frames like "at withRateLimitRetry (.../rate-limit-retry.ts)" match
  the usage-limit /rate[_\s]?limit/ pattern. Classification and
  agent.lastError now use the message; stderrExcerpt keeps the full detail.

Self-healing additionally un-parks agents previously paused with
error-unrecoverable whose lastError now classifies recoverable, bounded by
the shared heartbeat error-recovery budget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:57:08 -07:00
gsxdsm
ee7af2513f fix(MAIN-008): address PR review feedback (#2020)
- Label namespaced mcp__* tools as resourceType "mcp" (not "research") so approvals/audit/dedupe keys describe external MCP actions
- Guard getTask in resumeApprovalAfterUnwindIfNeeded so deferred resume cannot mask execute() finally outcomes
2026-07-12 13:56:36 -07:00
Tchorizo
23c732b2a8 docs(MAIN-008): complete Step 7 — document deterministic MCP approval lifecycle
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
Tchorizo
b688266a02 test(MAIN-008): complete Step 4 — validate shared MCP lifecycle surfaces
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
Tchorizo
555f916ebb fix(MAIN-008): complete Step 3 — resume approved MCP calls once
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
Tchorizo
e977fadda9 fix(MAIN-008): complete Step 2 — stabilize MCP executor bootstrap
Agent: engineer
Fusion-Task-Id: MAIN-008
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-07-12 13:56:36 -07:00
gsxdsm
ee1d978984 FN-7876: add custom terminal shortcut buttons to SessionTerminal mobile key bar
Extends the embedded Task Detail SessionTerminal to surface the shared, user-defined terminal shortcuts (from FN-7872's kb-terminal-preferences localStorage) as tappable buttons in its mobile accessory key bar.

- Read customShortcuts via the shared readTerminalPreferences() store on mount and refresh live on the storage event
- Render each custom shortcut as a mobile-only accessory-bar button that injects decodeTerminalShortcutSequence(value) through the focus-preserving keepFocus + sendInput path, clearing sticky Ctrl like the built-in ^C key
- Suppress the buttons for read-only/replay/idle/ended sessions via the existing canAcceptInput gate; desktop embedded terminals get no key bar
- Add .cli-terminal-key--custom styling for the new buttons
- Update docs/dashboard-guide.md to describe the mobile custom-shortcut key bar behavior
- Add a minor changeset for @runfusion/fusion documenting the feature
- Add SessionTerminal.mobile.test.tsx coverage for rendering, injection, live updates, and read-only suppression

Files changed:
 .changeset/fn-7876-session-terminal-custom-shortcuts.md                     |  7 ++
 docs/dashboard-guide.md                                                     |  2 +-
 packages/dashboard/app/components/SessionTerminal.css                      |  5 ++
 packages/dashboard/app/components/SessionTerminal.tsx                      | 31 +++++++
 packages/dashboard/app/components/__tests__/SessionTerminal.mobile.test.tsx | 94 ++++++++++++++++++++++
 5 files changed, 138 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7876
Fusion-Task-Lineage: 61d62ad2-7357-4eb4-b542-6e17deea1e5e
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 13:22:05 -07:00
gsxdsm
a4dde88ff6 FN-7874: add Discord get-help link and central-DB wording to storage banner
Adds a hardened Discord support link and clarifies that project databases move to the central Fusion database in the storage update notice banner.

- Add a "Get help" button/link to the Fusion Discord (https://discord.gg/ksrfuy7WYR) in StorageMigrationNoticeBanner, with new .storage-migration-notice-banner__actions/__help CSS (desktop + mobile layout)
- Revise the banner body copy to state that project databases will be served from the central Fusion database instead of each project's local .fusion/fusion.db SQLite file
- Add storageMigrationNotice.getHelp/getHelpLabel i18n keys and update en/app.json body copy; sync placeholder keys across es/fr/ko/zh-CN/zh-TW locales and regenerate resources.d.ts
- Extend StorageMigrationNoticeBanner tests to cover the new help link (href/target/rel) and updated body copy, including dismissed/error-path cases
- Add a minor changeset for @runfusion/fusion documenting the banner change

Files changed:
 .changeset/fn-7874-storage-banner-get-help.md      |  7 ++++++
 .../components/StorageMigrationNoticeBanner.css    | 26 ++++++++++++++++++++++
 .../components/StorageMigrationNoticeBanner.tsx    | 16 ++++++++++++-
 .../StorageMigrationNoticeBanner.test.tsx          | 16 +++++++++++--
 packages/i18n/locales/en/app.json                  |  4 +++-
 packages/i18n/locales/es/app.json                  |  7 ++++++
 packages/i18n/locales/fr/app.json                  |  7 ++++++
 packages/i18n/locales/ko/app.json                  |  7 ++++++
 packages/i18n/locales/zh-CN/app.json                |  7 ++++++
 packages/i18n/locales/zh-TW/app.json                |  7 ++++++
 packages/i18n/src/resources.d.ts                    |  7 ++++++
 11 files changed, 107 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7874
Fusion-Task-Lineage: baa88812-18ca-4910-a641-8d6e23f602a6
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 13:17:47 -07:00
gsxdsm
2c7df1bd78 FN-7875: split archived pre-0.50 release notes out of root CHANGELOG.md
Adds a deterministic changelog-archive split so scripts/release.mjs stops regenerating one ever-growing root CHANGELOG.md and instead keeps only current release notes at the root while durably archiving pre-0.50.0 history.

- Add scripts/lib/changelog-archive.mjs with partitionVersionsByCutoff (splits a version-ordered list at the 0.50.0 cutoff, preserving order and treating non-parseable keys as archived) and archivePointerLine (renders the "older releases" pointer appended to the current changelog).
- Rework scripts/release.mjs's syncRootChangelog to build CHANGELOG.md (current versions + archive pointer) and a new CHANGELOG-archive.md (versions before 0.50.0) via a shared buildRootChangelogLines/normalizeChangelogLines helper instead of one monolithic file.
- Add scripts/__tests__/changelog-archive.test.mjs covering cutoff partitioning, boundary/patch handling, non-parseable keys, custom cutoffs, and the archive pointer text.
- Regenerate CHANGELOG.md (now only 0.50.0+) and add CHANGELOG-archive.md containing the pre-0.50.0 history moved out of the root file.

Files changed:
 CHANGELOG-archive.md                         | 10882 +++++++++++++++++++++++
 CHANGELOG.md                                 | 11717 ++-----------------------
 scripts/__tests__/changelog-archive.test.mjs |    58 +
 scripts/lib/changelog-archive.mjs            |    56 +
 scripts/release.mjs                          |    49 +-
 5 files changed, 11710 insertions(+), 11052 deletions(-)

Fusion-Task-Id: FN-7875
Fusion-Task-Lineage: 220e6aa1-54fb-4800-a86e-6d8d21f6bf18
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 13:10:52 -07:00
gsxdsm
b77e12351e FN-7872: add custom terminal shortcut buttons to the Preferences panel
Lets users define, edit, and remove custom terminal shortcut buttons (label + injected key sequence) from the terminal Preferences panel, persisted client-side.

- Add a customShortcuts list to terminalPreferences (kb-terminal-preferences localStorage) with add/edit/remove management
- Add decodeTerminalShortcutSequence to decode \n, \t, \r, \e/\x1b, and \\ escapes for injected sequences
- Render custom shortcut buttons in TerminalModal's shortcut panel, injecting via the focus-preserving sendLiteralShortcut path
- Add management UI (add/edit/remove) for custom shortcuts in the terminal Preferences panel, styled in TerminalModal.css
- Extend TerminalModal and terminalPreferences test coverage for the new custom shortcut behavior
- Document custom terminal shortcuts in docs/dashboard-guide.md
- Add a minor changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7872-terminal-custom-shortcuts.md    |   7 +
 docs/dashboard-guide.md                            |   7 +-
 .../dashboard/app/components/TerminalModal.css     | 106 +++++++++++
 .../dashboard/app/components/TerminalModal.tsx     | 202 ++++++++++++++++++++-
 .../components/__tests__/TerminalModal.test.tsx    | 183 +++++++++++++++++++
 .../utils/__tests__/terminalPreferences.test.ts    |  68 +++++++
 .../dashboard/app/utils/terminalPreferences.ts     | 140 +++++++++++++-
 7 files changed, 708 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7872

Fusion-Task-Lineage: 9b2df0da-0eb7-4cec-a42b-767e23ff4c2c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 13:06:08 -07:00
gsxdsm
ad3d26d365 FN-7873: fix mobile floating-window header drag being intersected by page pan-y lockdown
Restores reliable single-finger dragging of movable FloatingWindow headers on mobile by reasserting touch-action: none at the mobile breakpoint, and adds regression coverage for the touch drag path.

- Reassert touch-action: none on movable FloatingWindow headers within the mobile media query, excluding full-screen sheet variants (chat, task-detail, workflow-editor, automation, mission-interview, file-browser, pr-create, artifacts-gallery), so the drag-handle contract isn't overridden by the global pan-y lockdown on mobile.
- Add a test verifying the movable mobile drag-handle selector keeps touch-action: none alongside the other opted-out draggable selectors (right-dock-expand-modal header, terminal header).
- Add a test exercising the captured pointermove drag path (pointerdown/move/up with pointer capture) confirming the window repositions correctly on touch input.
- Add a patch changeset for @runfusion/fusion documenting the fix.

Files changed:
 .changeset/fn-7873-mobile-modal-header-drag.md                       |  7 +++
 packages/dashboard/app/components/FloatingWindow.css                 |  8 +++
 packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx  | 59 ++++++++++++++++++++++
 3 files changed, 74 insertions(+)

Fusion-Task-Id: FN-7873
Fusion-Task-Lineage: 96f2b199-549e-4cab-a42b-05946a7bae86
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 13:00:56 -07:00
gsxdsm
5de1d9e7b0 FN-7871: center task-id chip with header badge row on TaskCard
Center the task-id chip with the header badge row on TaskCard.

- Add a subtle translateY nudge (calc(var(--space-xs) / 4)) to .card-id in TaskCard.css so the mono task id optically aligns with the first-row badges/icons, while preserving the existing FN-7862 flex-start anchor and FN-7837 badge-wrap contract.
- Add an FNXC:TaskCardLayout comment documenting the FN-7871 requirement and rationale for the nudge.
- Update TaskCard.badge-wrap.test.tsx to assert the new transform value on the rendered .card-id element and to assert the tokenized transform rule (and absence of a raw px translateY) directly in the loaded CSS.

Files changed:
 packages/dashboard/app/components/TaskCard.css                   | 5 +++++
 .../app/components/__tests__/TaskCard.badge-wrap.test.tsx        | 9 ++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7871

Fusion-Task-Lineage: 0c79d482-42d3-4e34-8844-3113c3e75ad7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:55:36 -07:00
gsxdsm
bb86844f8d FN-7869: add hide done toggle to Todo list view
Adds a per-project Hide done / Show done toggle to the Todo list items header so operators can declutter long selected lists while completion counts still reflect all items.
- Add hideDone state persisted per project via localStorage (kb-dashboard-todo-hide-done key registered in projectStorage)
- Filter rendered todo items to hide completed ones when the toggle is active, while keeping list stats/progress counts based on all items
- Adjust up/down item reordering to operate correctly against the visible (filtered) list while still reordering the underlying full item list
- Add an empty-state message when all items are hidden by the toggle, with Eye/EyeOff icon + i18n strings (todo.hideDone, todo.showDone, todo.allDoneHidden)
- Add regression tests covering the toggle, persistence, filtering, and empty state
- Document the Hide done / Show done control in the dashboard guide

Files changed:
 docs/dashboard-guide.md                            |  3 +
 packages/dashboard/app/components/TodoView.css     | 45 ++++++++++
 packages/dashboard/app/components/TodoView.tsx     | 66 +++++++++++++--
 .../app/components/__tests__/TodoView.test.tsx     | 99 ++++++++++++++++++++++
 packages/dashboard/app/utils/projectStorage.ts     |  1 +
 packages/i18n/locales/en/app.json                  |  3 +
 6 files changed, 210 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7869

Fusion-Task-Lineage: 9c334aba-e802-43b8-963c-0f2daf727583

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:52:30 -07:00
gsxdsm
06ec0e606e FN-7866: add auto-save toggle for the workspace file editor (default on)
Adds a shared, persisted auto-save preference for workspace text-file editing, defaulted to on, surfaced as a toolbar toggle in both the Files modal and right-dock Files view.

- Add useAutoSavePreference hook: persists the fn-file-editor-auto-save localStorage preference, broadcasts same-window changes via a custom event (storage events only reach other documents), and defaults to true.
- Extend useWorkspaceFileEditor with an autoSave flag that debounces (800ms) and triggers save() for a loaded, editable file with real pending changes, keyed by workspace+file+content to avoid re-firing on failed writes.
- Add an Auto-save toggle button to FileEditor's toolbar (autoSaveEnabled/onToggleAutoSave/canToggleAutoSave props), hidden for read-only/preview/binary files.
- Wire the shared preference into FileBrowserModal and DockFilesView, disabling auto-save for binary files in the modal.
- Add fileEditor.autoSave / fileEditor.toggleAutoSave i18n strings and document the new default behavior in docs/dashboard-guide.md.
- Add/extend tests covering the new hook, debounced auto-save behavior, and toolbar toggle wiring across FileEditor, FileBrowserModal, and DockFilesView.

Files changed:
 docs/dashboard-guide.md                            |   3 +
 .../dashboard/app/components/DockFilesView.tsx     |   6 +-
 .../dashboard/app/components/FileBrowserModal.tsx  |  20 ++--
 packages/dashboard/app/components/FileEditor.tsx   |  17 +++-
 .../components/__tests__/DockFilesView.test.tsx    |  37 ++++++-
 .../components/__tests__/FileBrowserModal.test.tsx |  86 +++++++++++++---
 .../app/components/__tests__/FileEditor.test.tsx   |  56 +++++++++++
 .../hooks/__tests__/useAutoSavePreference.test.ts  |  66 +++++++++++++
 .../hooks/__tests__/useWorkspaceFileEditor.test.ts | 108 +++++++++++++++++++++
 .../dashboard/app/hooks/useAutoSavePreference.ts   |  79 +++++++++++++++
 .../dashboard/app/hooks/useWorkspaceFileEditor.ts  |  47 ++++++++-
 packages/i18n/locales/en/app.json                  |   2 +
 12 files changed, 500 insertions(+), 27 deletions(-)

Fusion-Task-Id: FN-7866

Fusion-Task-Lineage: 0604de18-666d-4872-abce-2a3886c9ea55

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:49:37 -07:00
gsxdsm
bc30ce8aa1 FN-7857: deliver plugin skill bodies to agent sessions and the Skills view
Plugin-contributed skills previously registered only a name for sessions and the dashboard, so their SKILL.md bodies were never actually loaded — fix threads real body paths through to both session creation and the Skills UI.

- Resolve each enabled plugin skill's body path via @fusion/core's resolvePluginSkillBodyPath and thread its body dir (plus parent dir) into every session-creating lane (executor primary/retry/verification-fix/step/child-agent, triage, reviewer, merger, agent-heartbeat, cron-runner) as additionalSkillPaths, unioned with existing CE skill dirs.
- Add collectPluginSkillNames/mergePluginSkills additionalSkillPaths plumbing in session-skill-context.ts so plugin skill discovery paths flow the same way as native/role-fallback skills.
- Update dashboard skills-adapter.ts to read plugin skill SKILL.md and reference files from disk (via the traversal-guarded reader) instead of returning a runtime-placeholder/"not found" response for plugin-sourced skills.
- Document the plugin skill body delivery mechanism in docs/PLUGIN_AUTHORING.md.
- Add regression coverage: plugin-skill-body-delivery.test.ts, expanded session-skill-context.test.ts and skills-adapter.test.ts.
- Add changeset fn-7857-plugin-skill-body-delivery.md (minor, fix).

Files changed:
 .changeset/fn-7857-plugin-skill-body-delivery.md   |  7 ++
 docs/PLUGIN_AUTHORING.md                           |  3 +
 .../dashboard/src/__tests__/skills-adapter.test.ts | 92 ++++++++++++++++------
 packages/dashboard/src/skills-adapter.ts           | 33 ++------
 .../__tests__/plugin-skill-body-delivery.test.ts   | 75 ++++++++++++++++++
 .../src/__tests__/session-skill-context.test.ts    | 84 +++++++++++++++++++-
 packages/engine/src/agent-heartbeat.ts             |  3 +-
 packages/engine/src/cron-runner.ts                 |  2 +
 packages/engine/src/executor.ts                    | 25 ++++--
 packages/engine/src/merger.ts                      | 10 ++-
 packages/engine/src/reviewer.ts                    |  2 +
 packages/engine/src/session-skill-context.ts       | 43 ++++++++--
 packages/engine/src/step-session-executor.ts       |  5 +-
 packages/engine/src/triage.ts                      |  3 +-
 14 files changed, 318 insertions(+), 69 deletions(-)

Fusion-Task-Id: FN-7857

Fusion-Task-Lineage: 9ba4c305-8b38-4ae8-85b3-4c87205ef767

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:47:38 -07:00
gsxdsm
7fccd73d84 test(engine): update moveTask 3rd-arg assertion undefined -> {} (executor pause-abort always passes options object) 2026-07-12 12:44:05 -07:00
gsxdsm
73172bb45c test(engine): update heartbeat expectations for FN-7835/FN-7859 error-unrecoverable reason + paused state transitions 2026-07-12 12:44:05 -07:00
gsxdsm
8d59df4531 test(engine): add wrapToolsWithRtkRewrite/PermanentAgentGating/ActionGate to pi.js mocks (openclaw + reviewer) 2026-07-12 12:44:05 -07:00
gsxdsm
fb3e843bc2 test(i18n): add storageMigrationNotice keys to all non-en locales 2026-07-12 12:44:05 -07:00
gsxdsm
09e8e71680 FN-7870: remove left accent stripe and row border from Todo sidebar list items
Flattens the Todo sidebar list rows so active/hover states read through background and text color only, without per-row borders or the active-row left accent stripe.
- Removed `border: 1px solid transparent` and hover `border-color` from `.todo-list-item`
- Added hover text color instead of hover border to keep hover state visible
- Removed the `border-color` and inset box-shadow accent stripe from `.todo-list-item--active`
- Added a CSS contract test asserting sidebar list rows stay flat (no left accent stripe, no per-row borders)

Files changed:
 packages/dashboard/app/components/TodoView.css        |  9 +++++----
 .../components/__tests__/TodoView.mobile-css.test.ts  | 19 +++++++++++++++++++
 2 files changed, 24 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7870

Fusion-Task-Lineage: 78db20c0-7e53-427c-95da-7c9d5fa828d7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:33:29 -07:00
gsxdsm
8884f50765 FN-7868: add Commit and Push button to Git Manager changes panel
Adds a one-click commit-and-push affordance to the Git Manager modal's Changes panel, letting users commit staged changes and immediately push the active branch without a separate step.

- Add handleCommitAndPush callback in GitManagerModal that reuses createCommit and pushBranch, refreshes file changes/status on success, and surfaces a distinct toast (with the local commit hash preserved) if the push step fails after a successful commit
- Wire commitAndPush through to ChangesPanel and render a new "Commit and Push" button beside the existing Commit button, disabled while committing, when the message is empty, or when there are no staged files
- Add test coverage for the commit-and-push flow, including the partial-failure case where commit succeeds but push fails
- Add a minor changeset documenting the new Git Manager feature

Files changed:
 .changeset/fn-7868-git-manager-commit-push.md      |  7 +++
 .../dashboard/app/components/GitManagerModal.tsx   | 64 +++++++++++++++++++-
 .../components/__tests__/GitManagerModal.test.tsx  | 68 ++++++++++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7868
Fusion-Task-Lineage: 8b6913db-6f2a-45ab-b99d-a5c11ce53439
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:30:13 -07:00
gsxdsm
b10f823672 FN-7867: make task-card priority badges icon-only to stop meta-badge wrapping
Board TaskCard priority badges no longer render visible priority text, so the .card-meta-badges row cannot wrap onto a new line when a priority label widens it.

- TaskCard.tsx: drop the visible priority-label <span>, add title/aria-label with the full priority label, and keep the label reachable via a visually-hidden span for assistive tech
- TaskCard.css: update the FNXC comment on .card-priority-badge to reflect the icon-only rationale (spacing/geometry rules unchanged, still shared with the Task Detail chip/select)
- TaskCard.test.tsx: assert icon-only rendering (no visible text node/span), title/aria-label correctness, visually-hidden label content, and that the badge is absent for tasks without a priority; loosen the mocked lucide icons to forward arbitrary props
- Add a patch changeset documenting the icon-only priority badge fix

Files changed:
 .changeset/icon-only-priority-badges.md            |  7 +++++
 packages/dashboard/app/components/TaskCard.css     |  2 +-
 packages/dashboard/app/components/TaskCard.tsx     | 12 ++++++---
 .../app/components/__tests__/TaskCard.test.tsx     | 30 +++++++++++++++++-----
 4 files changed, 39 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7867

Fusion-Task-Lineage: 5c845c23-0a9f-47b4-9a75-6c410b507ef4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:26:54 -07:00
gsxdsm
139ae7e4bc FN-7854: fix desktop chat losing messaging tools from stale MessageStore wiring
Project-scoped chat managers can be cached before the project engine boots, so fn_send_message/fn_read_messages were silently dropped for lazily-booted (desktop) sessions while browser sessions kept them; the fix refreshes the cached manager's MessageStore post-construction and surfaces a diagnostic + chat-stream warning when the reduced tool schema condition occurs instead of failing silently.

Key changes:
- ChatManager gains setMessageStore() to refresh a cached manager's MessageStore post-construction, mirroring the existing setPluginRunner() refresh seam
- getOrCreateScopedChatManager()/resolveScopedChatManager() now accept and wire an optional MessageStore, upgrading already-cached managers instead of leaving them stale
- register-chat-routes.ts now passes engine.getMessageStore() through to the scoped chat manager resolver
- ChatManager emits a new 'warning' chat-stream event (code: tool-schema-reduced) plus a diagnostics.warn() call when a bound agent has no MessageStore, so reduced tool schema is agent-visible instead of a silent per-call failure
- Added regression tests covering MessageStore wiring/refresh in chat-project-services and chat-manager, plus a patch changeset documenting the fix

Files changed:
 .changeset/fn-7854-chat-tool-schema-parity.md      |   7 ++
 .../dashboard/src/__tests__/chat-manager.test.ts   | 124 ++++++++++++++++++++-
 .../src/__tests__/chat-project-services.test.ts    |  67 +++++++++++
 packages/dashboard/src/chat-project-services.ts    |  10 +-
 packages/dashboard/src/chat.ts                     |  39 +++++++
 .../dashboard/src/routes/register-chat-routes.ts   |   2 +-
 6 files changed, 245 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7854

Fusion-Task-Lineage: 1d1ee3e7-608b-4b7d-be45-138b38b27f17

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:24:11 -07:00
gsxdsm
fce8c757b8 Updated roadmap 2026-07-12 12:13:41 -07:00
gsxdsm
b8c18becd3 FN-7865: make artifact viewer popups full-screen sheets on mobile
Fixes the artifact viewer FloatingWindow (image/video/PDF/document) so it opens as a full-screen sheet on mobile instead of a small draggable/resizable desktop-style window.

- Add a mobile-breakpoint override for .artifacts-gallery-window that clamps the FloatingWindow to inset:0/100vw/100dvh with no border/radius/shadow
- Hide the FloatingWindow resize handle and disable header drag cursor/touch-action on mobile so the sheet can't be dragged or resized like the desktop window
- Add a CSS-contract regression test (ArtifactsGallery.css.test.ts) asserting the mobile sheet rules exist while desktop keeps the header drag affordance
- Update dashboard-guide.md docs to describe desktop draggable/resizable behavior vs. mobile full-screen sheet behavior
- Add a patch changeset for @runfusion/fusion documenting the fix

Files changed:
 .changeset/fn-7865-artifact-viewer-mobile-sheet.md |  7 +++
 docs/dashboard-guide.md                            |  5 +-
 .../dashboard/app/components/ArtifactsGallery.css  | 28 +++++++++-
 .../__tests__/ArtifactsGallery.css.test.ts         | 63 ++++++++++++++++++++++
 4 files changed, 100 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7865

Fusion-Task-Lineage: 0b5dfaee-3f7b-4a2e-968c-324182ae7953

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:10:38 -07:00
gsxdsm
95a808af6e FN-7864: add inline artifact preview/link to artifact-registered mail messages
Artifact-registration mailbox notifications now render a shared inline preview and open-artifact link instead of plain text metadata.

- Add MailboxArtifactAttachment component rendering an inline image/document preview plus an "open artifact" link from message.metadata (artifactId/artifactType/mimeType) via artifactMediaUrl
- Wire MailboxModal and MailboxView to render the new attachment for artifact-registered messages, with supporting CSS
- Emit metadata.mimeType from notifyArtifactRegistered in agent-tools.ts so mailbox surfaces can pick the right preview affordance without an extra artifact fetch
- Add/extend tests for the new component and for MailboxView/agent-artifact-tools coverage
- Update dashboard guide docs and add a changeset for the feature

Files changed:
 .changeset/fn-7864-artifact-mail-link.md           |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 .../app/components/MailboxArtifactAttachment.tsx   | 103 +++++++++++++++++++++
 packages/dashboard/app/components/MailboxModal.css |  74 +++++++++++++++
 packages/dashboard/app/components/MailboxModal.tsx |  15 +++
 packages/dashboard/app/components/MailboxView.tsx  |  15 +++
 .../__tests__/MailboxArtifactAttachment.test.tsx   |  65 +++++++++++++
 .../app/components/__tests__/MailboxView.test.tsx  |  93 +++++++++++++++++++
 .../src/__tests__/agent-artifact-tools.test.ts     |  32 ++++++-
 packages/engine/src/agent-tools.ts                 |   5 +
 10 files changed, 409 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7864

Fusion-Task-Lineage: a6502e18-5f7f-4c67-80fb-a709e4a52c50

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 12:01:00 -07:00
gsxdsm
9cfb40e137 FN-7863: add bounded execute-node self-requeue loop guard
Bounds the execute->pause-abort->todo dispatch loop so a task can no longer requeue forever with no visible signal or terminal state.

- Track a progress-anchored `executeRequeueLoopCount`/`executeRequeueLoopSignature` pair on the task row (current step + step statuses) so slow no-progress requeue cycles are counted independently of the scheduler's wall-clock `dispatchStormCount` guard.
- Warn visibly in the task log at `EXECUTE_REQUEUE_LOOP_VISIBLE_THRESHOLD` (3) and terminalize non-paused, non-terminal tasks at `MAX_EXECUTE_REQUEUE_LOOP_CYCLES` (6) with `status:"failed"` and an `EXECUTION_DISPATCH_LOOP_EXHAUSTED:` error, preserving worktree/branch/step progress.
- Emit a new `task:execution-dispatch-loop-terminalized` run-audit mutation type with ids/counts/outcomes-only metadata.
- Reset the loop counters on real progress, manual retry, forward moves (in-review/done/archived), and unpause, in both the executor and scheduler.
- Add DB migration 142 (`executeRequeueLoopCount`, `executeRequeueLoopSignature` columns) plus store read/write/reset plumbing.
- Add reliability-interactions coverage for the new loop guard and extend store-persistence tests for the new columns.
- Document the new behavior in AGENTS.md and docs/architecture.md.

Files changed:
 AGENTS.md                                              |   1 +
 docs/architecture.md                                   |   2 +
 packages/core/src/__tests__/store-persistence.test.ts  |  45 +++++
 packages/core/src/db.ts                                |  17 +-
 packages/core/src/manual-retry-reset.ts                |   1 +
 packages/core/src/store.ts                             |  22 ++-
 packages/core/src/types.ts                             |  11 ++
 .../execute-requeue-loop-guard.test.ts                 | 188 +++++++++++++++
 packages/engine/src/executor.ts                        |  67 +++++++-
 packages/engine/src/run-audit.ts                       |   2 +
 packages/engine/src/scheduler.ts                       |   8 +-
 11 files changed, 355 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7863
Fusion-Task-Lineage: db40507f-5851-435e-8854-c1ed695b4154
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:56:54 -07:00
gsxdsm
0c97c161ee FN-7860: honor plugin skillFiles paths for skill body resolution
Plugin skills declared with PluginSkillContribution.skillFiles were silently ignored by the host, forcing plugin authors into a flat skills/<name>/SKILL.md layout instead of category subdirectories.

- Add packages/core/src/plugin-skill-paths.ts with resolvePluginSkillBodyPath (honors skillFiles[0] relative to plugin root, falls back to skills/<name>/SKILL.md, rejects path traversal) and resolvePluginRootFromEntryPath
- Track per-plugin absolute roots in PluginLoader and expose pluginRoot alongside each getPluginSkills() contribution
- Thread pluginRoot/skillFiles through PluginRunner, dashboard server/chat structural types, and skills-adapter so discovered plugin skill path/relativePath resolve via the new traversal-guarded resolver when a pluginRoot is available, keeping the old name-derived path for backward compatibility otherwise
- Export resolvePluginSkillBodyPath/resolvePluginRootFromEntryPath/PluginSkillBodyPath from @fusion/core
- Update docs/PLUGIN_AUTHORING.md and add unit tests covering the new resolver and updated plugin-loader/skills-adapter/plugin-runner behavior
- Add changeset (@runfusion/fusion: minor, category: fix)

Files changed:
 .changeset/fn-7860-plugin-skillfiles.md            |  7 ++
 docs/PLUGIN_AUTHORING.md                           |  4 +-
 packages/core/src/__tests__/plugin-loader.test.ts  | 23 +++++++
 .../core/src/__tests__/plugin-skill-paths.test.ts  | 75 ++++++++++++++++++++++
 packages/core/src/index.ts                         |  5 ++
 packages/core/src/plugin-loader.ts                 | 20 +++++-
 packages/core/src/plugin-skill-paths.ts            | 58 +++++++++++++++++
 .../dashboard/src/__tests__/skills-adapter.test.ts | 75 +++++++++++++++++++++-
 packages/dashboard/src/chat.ts                     |  2 +-
 packages/dashboard/src/server.ts                   |  2 +-
 packages/dashboard/src/skills-adapter.ts           | 19 ++++--
 .../engine/src/__tests__/plugin-runner.test.ts     |  2 +-
 packages/engine/src/plugin-runner.ts               |  4 +-
 13 files changed, 280 insertions(+), 16 deletions(-)

Fusion-Task-Id: FN-7860

Fusion-Task-Lineage: 720cf527-9c6f-4877-838e-5fb64bd86556

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:52:24 -07:00
gsxdsm
7a7c12847e FN-7862: align task-id, badges, and header actions on a shared baseline in board card header
Aligns the task card header so the id chip, first badge row, and right-side size/action controls share a consistent centered vertical baseline, using the existing card chip height tokens for row rhythm on both desktop and mobile while preserving FN-7837 wrapping behavior.

- Give .card-header-badges and .card-id a shared min-height (--card-chip-height) and switch .card-id to inline-flex with centered items and line-height:1 so it lines up with sibling chips instead of sitting on its own baseline
- Add min-height (--card-chip-height) to .card-header-actions so the right-aligned size/action cluster matches the id/badge row height
- Add a mobile-breakpoint override giving .card-id, .card-header-badges, and .card-header-actions a shared --card-chip-height-mobile min-height, extending the existing FN-4365/FN-4351/FN-7837 compact mobile header comment
- Add/extend TaskCard.badge-wrap tests: a shared expectSharedHeaderBaseline() assertion helper, new in-progress and no-badges alignment test cases, and a test asserting the mobile header rhythm CSS rule is present in loaded app CSS

Files changed:
 packages/dashboard/app/components/TaskCard.css     |  23 +++--
 .../__tests__/TaskCard.badge-wrap.test.tsx         | 100 ++++++++++++++++++++-
 2 files changed, 116 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7862

Fusion-Task-Lineage: 1d66fbd6-30bc-4a76-9eb4-3b8734ddadc3

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:39:07 -07:00
gsxdsm
67cc025562 FN-7859: park non-recoverable durable heartbeat errors instead of stalling in bare error
Debug org agents error state recovery regression: durable heartbeat-managed
agents with a non-recoverable error (permanent/credential/model-access/
config, not stale-worktree/module-resolution) were previously left
indefinitely in bare `state:"error"` with no operator-visible reason,
and CLI agent inspection tools did not surface error/pause diagnostics.

- Timer path (`HeartbeatMonitor`) and run-entry recovery now classify
  non-recoverable durable heartbeat errors and park the agent `paused`
  with `pauseReason:"error-unrecoverable"` instead of restart-looping or
  sitting in `error` forever.
- `SelfHealingManager` mirrors the same non-recoverable classification in
  its recovery sweep, parking with the same reason/metadata and skipping
  the exhausted/next-retry gates for that terminal bucket.
- New `agent:error-parked-unrecoverable` run-audit event type emitted by
  both the heartbeat and self-healing paths (ids/counts/outcomes-only
  metadata).
- `fn_agent_show` now prints `Last Error`, `Pause Reason`, and a compact
  `Error Recovery` counter line; `fn_list_agents` prints the same
  diagnostics only for agents currently in `error`/`paused`.
- Updated `AGENTS.md`, `docs/agents.md`, and `docs/architecture.md` to
  document the new terminal-park behavior and CLI diagnostics surface.
- Added a changeset (`@runfusion/fusion` patch) describing the
  operator-facing fix.

Files changed:
 .changeset/fn-7859-org-agent-error-diagnostics.md  |  7 ++
 AGENTS.md                                          |  2 +-
 docs/agents.md                                     |  3 +-
 docs/architecture.md                               |  4 +-
 packages/cli/src/__tests__/extension.test.ts       | 68 ++++++++++++++++
 packages/cli/src/extension.ts                      | 64 +++++++++++++++
 .../src/__tests__/heartbeat-error-recovery.test.ts | 47 ++++++++++-
 packages/engine/src/__tests__/self-healing.test.ts | 94 ++++++++++++++++++----
 packages/engine/src/agent-heartbeat.ts             | 71 +++++++++++++++-
 packages/engine/src/run-audit.ts                   |  1 +
 packages/engine/src/self-healing.ts                | 46 +++++++++--
 11 files changed, 375 insertions(+), 32 deletions(-)

Fusion-Task-Id: FN-7859

Fusion-Task-Lineage: 09b2035d-e8a0-438f-b1ab-1b0048b35c76

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:34:48 -07:00
gsxdsm
e559b2b538 FN-7853: preserve chat thread during active streaming turns
Fix useChat so already-rendered user/assistant messages no longer flicker away while an agent turn is actively streaming.

- useChat.ts: during an active streaming turn for the current session, treat stale/empty/cross-session loadMessages responses as append-only against the visible thread instead of replacing it, merging any genuinely new same-session messages in and skipping the session-cache write when the active thread is being preserved.
- ChatView.streaming-thread.test.tsx: add coverage asserting the rendered thread stays visible across mid-turn session-update/tool-call/stale-reload churn.
- useChat.test.ts: add hook-level regression tests for the append-only/merge/cache-skip behavior during active streaming.
- docs/architecture.md, docs/dashboard-guide.md: document the append-only mid-turn thread-stability behavior.
- Add changeset (patch) for @runfusion/fusion describing the user-facing fix.

Files changed:
 .../fn-7853-chat-mid-turn-message-stability.md     |   7 +
 docs/architecture.md                               |   1 +
 docs/dashboard-guide.md                            |   1 +
 .../__tests__/ChatView.streaming-thread.test.tsx   | 130 +++++++++++++
 .../dashboard/app/hooks/__tests__/useChat.test.ts  | 208 +++++++++++++++++++++
 packages/dashboard/app/hooks/useChat.ts            |  35 +++-
 6 files changed, 380 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7853

Fusion-Task-Lineage: d9909469-082c-4eeb-81fb-b36d1a9e4705

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:26:53 -07:00
gsxdsm
20c6db9534 FN-7861: make pause/unpause task state update the board immediately
Patch useTasks pauseTask/unpauseTask to update local hook state and the project SWR task cache immediately on API success, instead of waiting for SSE/poll to reconcile paused state.

- pauseTask/unpauseTask now bump fetchVersionRef, patch the in-memory tasks list, and patch/clear the project SWR cache the same way retryTask/bypassReview already do
- guards against stale in-flight fetches clobbering the just-applied paused/unpaused state and against missing-id cache entries
- adds regression tests covering immediate local+cache reflection for pause and unpause, stale in-flight fetch ordering, and missing-id stability
- adds a patch changeset documenting the user-facing fix

Files changed:
 .changeset/fn-7861-immediate-pause-state.md        |   7 +
 .../dashboard/app/hooks/__tests__/useTasks.test.ts | 151 +++++++++++++++++++++
 packages/dashboard/app/hooks/useTasks.ts           |  66 ++++++++-
 3 files changed, 222 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7861

Fusion-Task-Lineage: fefbaf4f-8eb7-44a7-a1e2-ac8471a726bd

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:23:23 -07:00
gsxdsm
9bb74595c2 FN-7852: add one-time SQLite→embedded Postgres storage notice banner
Adds a dismissible, one-time dashboard banner announcing the upcoming SQLite→embedded-Postgres storage backend change.

- New self-contained StorageMigrationNoticeBanner component with title/body copy and a dismiss control that persists via localStorage key fusion:storage-migration-notice-dismissed
- Wire the banner into DashboardBanners alongside the CLI binary install banner for project-scoped views
- Add en locale strings (storageMigrationNotice.title/body/dismissLabel) in app.json
- Add component test coverage for render/dismiss/persistence behavior
- Document the notice in docs/dashboard-guide.md
- Add a minor changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7852-storage-migration-notice.md     |  7 ++
 docs/dashboard-guide.md                            |  2 +
 .../components/StorageMigrationNoticeBanner.css    | 73 +++++++++++++++++++
 .../components/StorageMigrationNoticeBanner.tsx    | 65 +++++++++++++++++
 .../StorageMigrationNoticeBanner.test.tsx          | 82 ++++++++++++++++++++++
 .../app/components/dashboard/DashboardBanners.tsx  | 11 ++-
 packages/i18n/locales/en/app.json                  |  5 ++
 7 files changed, 242 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7852

Fusion-Task-Lineage: e3235cce-9ca3-4830-8733-a2ec46c53246

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 11:19:52 -07:00