Commit Graph

2203 Commits

Author SHA1 Message Date
gsxdsm
7420abe80e FN-7731: add CLI-level lock retry for task show/move commands
Add bounded exponential-backoff retry above the DB layer so `fn task show`/`fn task move` ride out transient SQLite lock contention instead of failing outright or hanging.

- Add packages/cli/src/lock-retry.ts: retries a thunk on SQLite lock errors (via @fusion/core's isSqliteLockError) with exponential backoff capped by a wall-clock deadline (default 15s, override via FUSION_CLI_LOCK_RETRY_MS); non-lock errors propagate immediately; raises LockRetryExhaustedError on deadline exhaustion.
- Wire lock-retry into packages/cli/src/commands/task.ts for the show/move task-store operations, and close the resolved TaskStore for deterministic exit.
- Export isSqliteLockError from packages/core/src/index.ts for CLI reuse.
- Add packages/cli/src/commands/__tests__/task-lock-retry.test.ts covering retry/backoff/deadline/error-passthrough behavior; extend task.test.ts.
- Document the new behavior/env var in docs/cli-reference.md.
- Add changeset (@runfusion/fusion: patch).

Files changed:
 .changeset/fn-7731-task-cmd-lock-retry.md          |   7 +
 docs/cli-reference.md                              |   9 +
 .../src/commands/__tests__/task-lock-retry.test.ts | 430 +++++++++++++++++++++
 packages/cli/src/commands/__tests__/task.test.ts   |  11 +
 packages/cli/src/commands/task.ts                  | 117 +++++-
 packages/cli/src/lock-retry.ts                     | 117 ++++++
 packages/core/src/index.ts                         |   5 +
 7 files changed, 688 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7731

Fusion-Task-Lineage: 85543164-10d6-4da9-8c3c-a84cd86827aa

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
badb86a965 FN-7726: unify fs.watch/poll logic between TaskStore and AgentStore
Extracted the duplicated file-watch-with-polling-fallback logic from TaskStore and AgentStore into a shared controller.

- Added packages/core/src/fs-watch-poll-controller.ts implementing a reusable fs.watch + polling-fallback controller
- Refactored packages/core/src/store.ts (TaskStore) to use the shared controller instead of its own watch/poll implementation
- Refactored packages/core/src/agent-store.ts (AgentStore) to use the shared controller instead of its own watch/poll implementation
- Added packages/core/src/__tests__/fs-watch-poll-controller.test.ts covering the new controller's behavior
- Updated docs/architecture.md to document the shared controller

Files changed:
 docs/architecture.md                               |   1 +
 .../src/__tests__/fs-watch-poll-controller.test.ts | 187 +++++++++++++++++++++
 packages/core/src/agent-store.ts                   |  66 +++-----
 packages/core/src/fs-watch-poll-controller.ts      | 123 ++++++++++++++
 packages/core/src/store.ts                         |  66 +++-----
 5 files changed, 364 insertions(+), 79 deletions(-)

Fusion-Task-Id: FN-7726
Fusion-Task-Lineage: 69be6dc3-5414-44f2-a3f1-3eb72c2d7391
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
927741a8cf FN-7727: persist failed workflow step history across self-healing retries
Preserves prior failed pre-merge review attempts instead of overwriting them when self-healing re-runs a failed workflow step.

- Add optional bounded `priorAttempts?: WorkflowStepResult[]` field to `WorkflowStepResult` (capped at `MAX_WORKFLOW_STEP_PRIOR_ATTEMPTS`)
- Add shared pure `upsertWorkflowStepResult(existing, incoming, opts?)` helper in `@fusion/core` (packages/core/src/workflow-step-results.ts)
- Route the executor graph adapter's `recordWorkflowStepResult` and triage's `recordPlanReviewWorkflowResult` through the new helper so a self-healing recovery re-run snapshots the prior failed/advisory_failure attempt into `priorAttempts` instead of dropping it
- Selection logic (self-healing, merge-blocker, progress/timing) is unchanged and still reads only the current entry
- Surface prior failed attempts in the TaskDetailModal Summary tab's Workflow results list as a collapsed "previous failed attempts" disclosure
- Add core/engine/dashboard tests covering the upsert helper, self-healing recovery snapshotting, and the UI disclosure
- Document the behavior in docs/workflow-steps.md
- Add changeset for @runfusion/fusion (patch)

Files changed:
 .changeset/fn-7727-persist-failed-step-history.md  |   7 ++
 docs/workflow-steps.md                             |  15 +++
 .../src/__tests__/workflow-step-results.test.ts    | 138 +++++++++++++++++++++
 packages/core/src/index.gate.ts                    |   4 +
 packages/core/src/index.ts                         |   4 +
 packages/core/src/types.ts                         |  19 +++
 packages/core/src/workflow-step-results.ts         |  99 +++++++++++++++
 .../dashboard/app/components/TaskDetailModal.css   |  55 ++++++++
 .../dashboard/app/components/TaskSummaryTab.tsx    |  42 ++++++-
 .../TaskSummaryTab.prior-attempts.test.tsx         |  89 +++++++++++++
 .../clear-terminal-workflow-step-failures.test.ts  |  27 ++++
 packages/engine/src/__tests__/self-healing.test.ts |  50 ++++++++
 ...flow-step-results-self-healing-recovery.test.ts | 115 +++++++++++++++++
 packages/engine/src/executor.ts                    |  29 +++--
 packages/engine/src/triage.ts                      |  14 ++-
 15 files changed, 688 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-7727
Fusion-Task-Lineage: 7316fb18-bc92-426d-91f4-b1a4ad41c9b1
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
5a9f354e9a FN-7730: fix linked-worktree project-root resolution so board writes don't silently land in the wrong .fusion.db
Board mutations (fn_task_update, CEO override, direct SQL) issued from a pi-extension tool session could silently write into a task's throwaway, never-synced worktree-local .fusion/fusion.db instead of the true project root when git CLI resolution failed (missing git binary, Docker "dubious ownership" refusal, or a non-default settings.worktreesDir). This fixes root-cause resolution and adds regression coverage plus a docs writeup.

- getProjectRootFromGitLinkedWorktree now resolves a linked worktree's project root from git's own on-disk .git/commondir metadata via pure filesystem reads before falling back to the git rev-parse CLI, so writes no longer fall through to a local hydrated copy on git-invocation failure.
- Added getMainRepoRootFromGitFile and resolveCommonGitDirFromWorktreeGitFile helpers with FNXC:Storage comments documenting the FN-7730 root cause and fix rationale.
- Added packages/core/src/__tests__/pi-extensions-write-path-durability.test.ts regression coverage for the write-path durability invariant.
- Extended packages/core/src/__tests__/pi-extensions.test.ts with additional resolution-path assertions.
- Documented the failure mode and fix in docs/storage.md ("Silent board-mutation write loss (FN-7730)").
- Added a patch changeset for @runfusion/fusion describing the user-facing fix.

Files changed:
 .changeset/fn-7730-worktree-project-root-resolution.md              |   7 ++
 docs/storage.md                                                     |  54 ++++++++++
 packages/core/src/__tests__/pi-extensions-write-path-durability.test.ts | 98 ++++++++++++++++++
 packages/core/src/__tests__/pi-extensions.test.ts                   |  87 +++++++++++++++-
 packages/core/src/pi-extensions.ts                                  | 114 +++++++++++++++++++++
 5 files changed, 359 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7730
Fusion-Task-Lineage: 00753a2d-a934-42cf-8fde-0f9b8ad98142
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
d44dbaab15 FN-7728: add review_gate_bypass RBAC category for fn_task_bypass_review
Introduces a dedicated review_gate_bypass permission-policy category so operators can govern who may bypass a failed pre-merge review gate independently of ordinary task-mutation permissions.

- Add review_gate_bypass as a new sensitive-action category in packages/core/src/types.ts, distinct from task_agent_mutation, with fn_task_bypass_review as its sole example tool
- Default review_gate_bypass to require-approval even under the unrestricted preset (stricter than the preset's uniform disposition) in packages/core/src/agent-permission-policy.ts, while approval-required/locked-down already cover it uniformly
- Classify fn_task_bypass_review into the new category via a shared REVIEW_GATE_BYPASS_FN_TOOLS set in packages/engine/src/gating-classifications.ts, consumed identically by both evaluateAgentActionGate and the permanent-agent gate to prevent path drift
- Render the new category as its own row in the dashboard's project-default and per-agent AgentPermissionPolicyEditor, surfaced in AgentDetailView
- Update docs/settings-reference.md and add unit tests across core/engine/dashboard covering the new category, its stricter default, and gate-classification alignment
- Add changeset (@runfusion/fusion: minor) documenting the new operator-facing permission category

Files changed:
 .changeset/fn-7728-review-gate-bypass-rbac.md      |  7 +++
 docs/settings-reference.md                         |  8 +--
 .../src/__tests__/agent-permission-policy.test.ts  | 54 ++++++++++++++++++-
 packages/core/src/agent-permission-policy.ts       | 12 ++++-
 packages/core/src/types.ts                         |  8 +++
 .../dashboard/app/components/AgentDetailView.tsx   |  2 +
 .../app/components/AgentPermissionPolicyEditor.tsx |  8 +++
 .../__tests__/AgentPermissionPolicyEditor.test.tsx |  5 ++
 .../engine/src/__tests__/agent-action-gate.test.ts | 45 ++++++++++++++++
 .../src/__tests__/gating-classifications.test.ts   | 63 ++++++++++++++++++++++
 .../src/__tests__/permanent-agent-gating.test.ts   | 41 ++++++++++++++
 packages/engine/src/agent-action-gate.ts           |  7 +++
 packages/engine/src/gating-classifications.ts      |  8 ++-
 packages/engine/src/permanent-agent-gating.ts      |  6 +++
 14 files changed, 266 insertions(+), 8 deletions(-)

Fusion-Task-Id: FN-7728

Fusion-Task-Lineage: 100c8563-2897-4d53-9546-5c2faa6ab7d8

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
1fc615d0da FN-7724: bridge Grok CLI tool_use NDJSON events in GrokRuntimeAdapter
Bridges Grok CLI tool execution events (tool_use start/result) from the NDJSON stream into the runtime adapter's onToolStart/onToolEnd callbacks, alongside existing text bridging.

- GrokRuntimeAdapter.promptWithFallback now parses and bridges tool_use NDJSON events into onToolStart/onToolEnd callbacks
- Tool name/args/result pass through unchanged (no Grok→pi tool-name mapping, since the verified contract doesn't pin a vocabulary)
- step_finish/error remain non-terminal per-step events, not bridged to any callback; only subprocess close/error finalizes (unchanged from FN-7722)
- Extended stream-parser.ts to recognize tool_use event shapes
- Added new types for tool event payloads in types.ts
- Updated docs/grok-cli-contract.md and plugin README to document tool event bridging
- Added changeset for @runfusion/fusion (minor)
- Added/extended tests in runtime-adapter.test.ts and stream-parser.test.ts (fixture-based, no live binary)

Files changed:
 .changeset/fn-7724-grok-cli-tool-bridging.md       |   7 ++
 docs/grok-cli-contract.md                          |  16 ++-
 plugins/fusion-plugin-grok-runtime/README.md       |  12 +++
 .../src/__tests__/runtime-adapter.test.ts          | 120 +++++++++++++++++++++
 .../src/__tests__/stream-parser.test.ts            |  33 ++++++
 .../src/runtime-adapter.ts                         |  83 +++++++++++---
 .../src/stream-parser.ts                           |  10 ++
 plugins/fusion-plugin-grok-runtime/src/types.ts    |  23 ++++
 8 files changed, 287 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7724

Fusion-Task-Lineage: 73abbf2a-6dcd-44fb-86be-71d4788c92d2

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:05 -07:00
gsxdsm
e5c3ffbb39 FN-7725: wire Grok CLI runtime adapter routing end-to-end with tests and docs
Formalizes and tests the existing agent Runtime-mode picker path as the decided Grok CLI routing wiring.

- Add FNXC decision note at the extractRuntimeHint seam documenting the Grok CLI routing chain (agent-session-helpers.ts)
- Add routing test verifying runtimeHint="grok" resolves through resolveRuntime/resolvePluginRuntime to GrokRuntimeAdapter (grok-runtime-routing.test.ts)
- Update docs/grok-cli-contract.md with the wiring decision and documented limitations
- Update plugins/fusion-plugin-grok-runtime/README.md with routing guidance
- Add changeset for the new opt-in Grok CLI streaming runtime routing feature

Files changed:
 .changeset/fn-7725-grok-cli-routing.md             |   7 +
 docs/grok-cli-contract.md                          | 106 ++++++--
 .../src/__tests__/grok-runtime-routing.test.ts     | 275 +++++++++++++++++++++
 packages/engine/src/agent-session-helpers.ts       |  12 +
 plugins/fusion-plugin-grok-runtime/README.md       |  38 ++-
 5 files changed, 414 insertions(+), 24 deletions(-)

Fusion-Task-Id: FN-7725

Fusion-Task-Lineage: f5210793-59d0-4a0c-8152-4c5ef61ca737

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
626e00288c FN-7720: add operator review-lane bypass for stranded pre-merge review failures
Add a policy-gated review-lane bypass primitive so operators can unstick cards stranded by a failed pre-merge review step (e.g. the no-feedback review-engine defect), without exposing it to agent-driven lanes.

- Add `store.bypassFailedPreMergeReviewStep(id, { reason, actor })` in @fusion/core plus `getLatestFailedPreMergeReviewStep` in task-merge.ts, and new `bypassedBy`/`bypassedAt`/`bypassReason`/`bypassedFromStatus`/`bypassedFromVerdict` fields on `WorkflowStepResult`
- Add operator-only `fn_task_bypass_review` CLI/pi-extension tool; explicitly withheld from executor/reviewer/triage agent tool lists
- Add `POST /tasks/:id/bypass-review` dashboard API route and wire it through `register-task-workflow-routes.ts` and legacy API compatibility layer
- Add dashboard UI affordance (context menu action + task detail modal + right-dock controller wiring) to trigger the bypass with a reason
- Add i18n strings for the bypass action/labels across en/es/fr/ko/zh-CN/zh-TW locales
- Update `gating-classifications.ts` to recognize the bypassed state
- Add unit tests: `store-bypass-review.test.ts`, `task-merge-bypass.test.ts`, extension test coverage, and `useTasks` hook test coverage
- Update docs (`docs/workflow-steps.md`, `docs/dashboard-guide.md`, AGENTS.md, fusion skill references) to describe the new bypass tool/route
- Add changeset `.changeset/fn-7720-review-lane-bypass-primitive.md` (minor)

Files changed:
$(git diff --cached --stat)

Fusion-Task-Id: FN-7720

Fusion-Task-Lineage: 590b020a-ae02-4b51-8189-df8f54bf3044

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
e657d3b965 FN-7723: add cross-process agent state change notification bus
Adds opt-in cross-process change detection to AgentStore so the engine reacts to CLI-driven agent stop/start mutations promptly instead of waiting for the periodic audit sweep.

- AgentStore gains fs.watch-based (with poll fallback) cross-process notification, modeled on TaskStore's existing mechanism
- Re-emits existing agent:updated/agent:stateChanged events in the engine process when another process (the fn CLI) mutates an agent row
- HeartbeatTriggerScheduler listeners now fire immediately instead of waiting up to 60s for the auditTimerRegistrations sweep; the sweep remains as durable backstop
- in-process-runtime.ts wires up the new notification bus
- Adds unit tests for agent-store cross-process notifications and heartbeat-scheduler reaction behavior
- Updates docs/agents.md and docs/architecture.md
- Adds changeset (patch) for @runfusion/fusion

Files changed:
 .changeset/fn-7723-cross-process-agent-notify.md   |   7 +
 docs/agents.md                                     |   1 +
 docs/architecture.md                               |   1 +
 packages/core/src/__tests__/agent-store.test.ts    | 177 +++++++++++++++++
 packages/core/src/agent-store.ts                   | 210 ++++++++++++++++++++-
 .../src/__tests__/heartbeat-scheduler.test.ts      | 162 ++++++++++++++++
 packages/engine/src/runtimes/in-process-runtime.ts |  30 +++
 7 files changed, 587 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7723
Fusion-Task-Lineage: d3a7fa05-b40d-4388-8e98-140f9d8861c9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
a24b0fac1a FN-7721: cap heartbeat worktree-acquisition retries and record exhaustion failures
Bounds durable-agent heartbeat worktree acquisition to a fixed retry count instead of requeuing to todo indefinitely across heartbeat cycles.

- Add MAX_HEARTBEAT_WORKTREE_ACQUISITION_RETRIES (3) in agent-heartbeat.ts, reusing Task.recoveryRetryCount as a cross-heartbeat counter (no schema migration)
- On cap exhaustion, terminally mark the task status:"failed" with an explanatory error, log the entry, and reopen to todo with preserveStatus so the failed status isn't wiped by reopen-to-todo semantics
- Add onTaskAcquisitionExhausted callback wired in in-process-runtime.ts to CentralCore.recordTaskCompletion(taskId, false) so exhausted acquisitions count toward totalTasksFailed
- Add regression tests in agent-heartbeat-worktree.test.ts and in-process-runtime.test.ts covering the retry cap and completion recording
- Add changeset (patch) and a docs/solutions/logic-errors writeup documenting the investigation and other worktree-collision sub-gaps found not to reproduce on HEAD

Files changed:
 .changeset/fn-7721-worktree-heartbeat-retry-cap.md |  7 ++
 docs/solutions/logic-errors/heartbeat-worktree-acquisition-unbounded-requeue.md | 84 ++++++++++++++++++++++
 packages/engine/src/__tests__/agent-heartbeat-worktree.test.ts | 58 +++++++++++++++
 packages/engine/src/__tests__/in-process-runtime.test.ts | 11 +++
 packages/engine/src/agent-heartbeat.ts | 72 ++++++++++++++++++-
 packages/engine/src/runtimes/in-process-runtime.ts | 12 ++++
 6 files changed, 242 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7721

Fusion-Task-Lineage: caad671c-f360-4c1c-8aaa-5b48fca5a55b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
171aaa2432 FN-7722: route Grok execution through CLI with NDJSON streaming
Switches the Grok runtime plugin to execute prompts via the Grok CLI, parsing its NDJSON stream output instead of the previous invocation path.

- Add cli-stream.ts to spawn and stream the Grok CLI process
- Add stream-parser.ts to parse NDJSON CLI output into runtime events
- Rework runtime-adapter.ts to route execution through CLI streaming
- Extend types.ts with CLI stream/NDJSON event types
- Add docs/grok-cli-contract.md documenting the CLI streaming contract
- Add/update tests for stream-parser and runtime-adapter
- Update plugin README with CLI streaming details
- Add changeset for the Grok CLI streaming change

Files changed:
 .changeset/fn-7722-grok-cli-streaming.md           |   7 +
 docs/grok-cli-contract.md                          | 200 +++++++++++++++++++++
 plugins/fusion-plugin-grok-runtime/README.md       |  28 +++
 .../src/__tests__/runtime-adapter.test.ts          | 135 ++++++++++++--
 .../src/__tests__/stream-parser.test.ts            |  79 ++++++++
 .../fusion-plugin-grok-runtime/src/cli-stream.ts   |  52 ++++++
 .../src/runtime-adapter.ts                         | 182 ++++++++++++++++---
 .../src/stream-parser.ts                           |  54 ++++++
 plugins/fusion-plugin-grok-runtime/src/types.ts    | 120 +++++++++++++
 9 files changed, 816 insertions(+), 41 deletions(-)

Fusion-Task-Id: FN-7722

Fusion-Task-Lineage: c5f33e9d-0032-432b-88b5-4ad8d786d67e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
cda9532c3b FN-7718: fix zombie heartbeat timers surviving agent stop/start
Ensures stopping and restarting an agent durably clears its heartbeat timer instead of relying on the later FN-7645 watchdog repair.

- HeartbeatTriggerScheduler.auditTimerRegistrations now unregisters lingering timers for non-eligible (stopped/paused/disabled) agents
- syncTimerForAgent force-re-arms a stale present timer on a start transition so no orphaned timer entry lingers
- Added 308 lines of new heartbeat-scheduler regression tests covering the stop/start zombie-timer scenarios
- Added changeset (patch) documenting the fix
- Updated docs/agents.md and docs/architecture.md to describe the new invariant

Files changed:
 .changeset/fn-7718-zombie-timer-invalidate.md      |   7 +
 docs/agents.md                                     |   2 +
 docs/architecture.md                               |   1 +
 .../src/__tests__/heartbeat-scheduler.test.ts      | 308 +++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             |  49 +++-
 5 files changed, 364 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7718

Fusion-Task-Lineage: fc834ccd-495e-4294-805d-325b4cb536a2

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
a4931a4731 FN-7719: derive implicit planning fallback model when no fallback configured
Triage planning-model retries no longer dead-end on "no fallback configured" when a provider primary-model call fails (e.g. a 404 wrapped in a 429 for nvidia/moonshotai/kimi-k2.6).

- Add resolveImplicitPlanningFallbackModel() to agent-session-helpers.ts: derives a fallback from the resolved project/global default (execution) model when neither planningFallback*/global fallback* is set, guarding against self-swap and skipping in test mode.
- Wire the implicit fallback into TriageProcessor.specifyTask() in triage.ts so a retryable primary planner-model failure swaps once via the derived fallback instead of failing triage outright.
- Add unit test coverage in agent-session-helpers.test.ts and triage.test.ts for the new implicit-fallback resolution and its triage integration.
- Document the new implicit-fallback behavior in docs/settings-reference.md.
- Add a patch changeset for @runfusion/fusion describing the fix.

Files changed:
 .changeset/fn-7719-triage-planning-implicit-fallback.md           |   7 +
 docs/settings-reference.md                                       |   2 +
 packages/engine/src/__tests__/agent-session-helpers.test.ts       |  75 +++++++
 packages/engine/src/__tests__/triage.test.ts                      | 237 +++++++++++++++++++++
 packages/engine/src/agent-session-helpers.ts                      |  41 ++++
 packages/engine/src/triage.ts                                     |  31 ++-
 6 files changed, 389 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-7719

Fusion-Task-Lineage: 69e797e1-5bac-47f3-8dce-505b9d64d83c

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:04 -07:00
gsxdsm
c8fcbec94f FN-7717: release active-session locks when a task is archived
Archiving a task from triage/planning/todo (not just in-progress) previously left leaked active-session-registry entries, so a successor task could hit ActiveSessionPathHeldByForeignTaskError and get blocked from Plan Review.

- Add an explicit `to === "archived"` branch in the task-move handler that awaits abort of in-flight task work and sweeps any leftover activeSessionRegistry paths for the task, checked before the narrower `from === "in-progress"` branch so direct in-progress→archived transitions are covered too.
- Deliberately exclude `to === "done"` / `to === "in-review"` from this sweep since those columns legitimately hold ai-merge / workspace-repo-land merge leases that must survive the transition.
- Add regression test coverage for archive releasing active sessions across originating columns.
- Add changeset and architecture doc note.

Files changed:
 .../fn-7717-archive-active-session-release.md      |   7 +
 docs/architecture.md                               |   1 +
 ...xecutor-archive-releases-active-session.test.ts | 167 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |  35 +++++
 4 files changed, 210 insertions(+)

Fusion-Task-Id: FN-7717

Fusion-Task-Lineage: 7cff6821-7bb3-4b75-b502-a26467ca7f51

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
b2613b7132 FN-7714: honor ~/.grok/user-settings.json apiKey when GROK_API_KEY is unset
Fall back to the Grok CLI's user-settings file for the API key so pi's $GROK_API_KEY provider reference resolves even when the env var isn't exported.

- Add hydrateGrokApiKeyFromUserSettings() in grok-provider.ts, called from registerBuiltInGrokProvider(), which hydrates process.env.GROK_API_KEY from ~/.grok/user-settings.json { apiKey } only when the env var is unset/empty
- Env var always wins; a missing (ENOENT), malformed, or empty-apiKey settings file is fail-soft (no throw, no env mutation), mirroring the grok-runtime probe's fallback behavior
- Add regression tests covering env-precedence, fallback hydration, and fail-soft error paths (grok-provider-user-settings.test.ts)
- Document the fallback in docs/settings-reference.md
- Add a patch changeset for @runfusion/fusion

Files changed:
 .changeset/fn-7714-grok-user-settings-apikey.md    |   7 +
 docs/settings-reference.md                         |   2 +-
 .../__tests__/grok-provider-user-settings.test.ts  | 156 +++++++++++++++++++++
 packages/core/src/grok-provider.ts                 |  47 +++++++
 4 files changed, 211 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7714

Fusion-Task-Lineage: 5450b480-3a32-4331-9494-867b84605464

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
7dc271027f FN-7711: add built-in Grok CLI provider to fix pi model registry lookup
Registers a built-in grok-cli provider so Grok CLI model executions no longer hard-fail with "not found in the pi model registry".

- Add packages/core/src/grok-provider.ts: built-in grok-cli provider config (xAI OpenAI-compatible endpoint https://api.x.ai/v1, api openai-completions, apiKey $GROK_API_KEY), mirroring the existing Z.ai provider
- Register the provider in packages/engine/src/pi.ts (registerExtensionProviders) and packages/engine/src/provider-registration.ts (seedDashboardProviders)
- Wire the provider into CLI entrypoints: packages/cli/src/commands/daemon.ts, dashboard.ts, serve.ts
- Export grok-provider from packages/core/src/index.ts and packages/core/src/index.gate.ts
- Add unit tests: packages/core/src/__tests__/grok-provider.test.ts, and extend packages/engine/src/__tests__/pi-create-fn-agent.test.ts and provider-registration.test.ts
- Document the new provider in docs/settings-reference.md
- Add changeset .changeset/fn-7711-grok-cli-model-registry.md (patch, category: fix)

Note: Grok CLI binary remains discovery/probe only; GrokRuntimeAdapter streaming is a stub (tracked follow-up).

Files changed:
 .changeset/fn-7711-grok-cli-model-registry.md      |   7 +
 docs/settings-reference.md                         |   2 +
 packages/cli/src/commands/daemon.ts                |   4 +
 packages/cli/src/commands/dashboard.ts             |   4 +
 packages/cli/src/commands/serve.ts                 |   4 +
 packages/core/src/__tests__/grok-provider.test.ts  | 130 ++++++++++++
 packages/core/src/grok-provider.ts                 | 224 +++++++++++++++++++++
 packages/core/src/index.gate.ts                    |   7 +
 packages/core/src/index.ts                         |   7 +
 .../src/__tests__/pi-create-fn-agent.test.ts       |  78 ++++++-
 .../src/__tests__/provider-registration.test.ts    |   4 +-
 packages/engine/src/pi.ts                          |   4 +
 packages/engine/src/provider-registration.ts       |   4 +
 13 files changed, 476 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7711

Fusion-Task-Lineage: ae90b54f-206e-46fd-8365-b0a4488ceb84

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-09 19:58:03 -07:00
gsxdsm
081dae0e0f FN-7705: Add Grok CLI runtime support as a bundled plugin
Adds a new bundled Grok CLI runtime plugin, wiring it end-to-end into settings, auth routes, model discovery, and the dashboard authentication UI.

- New `fusion-plugin-grok-runtime` package with CLI spawn, probe, provider, process-manager, and runtime-adapter modules plus tests
- Bundled-plugin install list (CLI + core) updated to auto-install the grok-cli plugin
- New `useGrokCli`/`grokCliBinaryPath` settings in `settings-schema.ts` and `types.ts`
- Dashboard: `GrokCliProviderCard` component/styles, `ProviderIcon` grok entry, `AuthenticationSection` wiring
- New `grok-model-cache.ts` for caching `grok models` discovery results, registered model/auth routes for `/auth/grok-cli` and `/providers/grok-cli/status`, merged into `/api/models`
- `runtime-provider-probes.ts` extended with Grok CLI probe/model-discovery delegation
- Docs updated (`PLUGIN_AUTHORING.md`, `settings-reference.md`) and changeset added (minor, feature)
- Workspace config (`pnpm-workspace.yaml`, `pnpm-lock.yaml`) updated to register the new plugin package

Files changed:
 .changeset/fn-7705-grok-cli-runtime.md             |   7 +
 docs/PLUGIN_AUTHORING.md                           |   2 +-
 docs/settings-reference.md                         |   4 +
 packages/cli/src/plugins/bundled-plugin-install.ts |   8 +
 .../cli/src/plugins/staged-bundled-plugin-ids.ts   |   1 +
 packages/cli/vitest.config.ts                      |  12 +
 .../core/src/__tests__/grok-cli-settings.test.ts   |  34 +++
 packages/core/src/index.ts                         |   1 +
 .../core/src/plugins/bundled-plugin-install.ts     |  10 +
 packages/core/src/settings-schema.ts               |   6 +
 packages/core/src/types.ts                         |   9 +
 packages/dashboard/app/api/legacy.ts               |  40 ++++
 .../app/components/GrokCliProviderCard.css         |  65 ++++++
 .../app/components/GrokCliProviderCard.tsx         | 204 ++++++++++++++++
 packages/dashboard/app/components/ProviderIcon.tsx |   5 +
 .../__tests__/GrokCliProviderCard.test.tsx         | 105 +++++++++
 .../app/components/__tests__/ProviderIcon.test.tsx |   8 +
 .../settings/sections/AuthenticationSection.tsx    |   8 +-
 packages/dashboard/package.json                    |   1 +
 .../src/__tests__/grok-model-cache.test.ts         | 152 ++++++++++++
 .../register-model-routes-grok-cli.test.ts         | 214 +++++++++++++++++
 .../dashboard/src/__tests__/routes-auth.test.ts    | 258 ++++++++++++++++++++-
 packages/dashboard/src/grok-model-cache.ts         | 166 +++++++++++++
 packages/dashboard/src/routes.ts                   |   1 +
 .../dashboard/src/routes/register-auth-routes.ts   | 134 ++++++++++-
 .../dashboard/src/routes/register-model-routes.ts  |  51 ++++
 packages/dashboard/src/runtime-provider-probes.ts  |  43 ++++
 packages/dashboard/vitest.config.ts                |  12 +
 packages/desktop/scripts/workspace-tools.ts        |   3 +-
 plugins/fusion-plugin-grok-runtime/CHANGELOG.md    |   7 +
 plugins/fusion-plugin-grok-runtime/README.md       |  54 +++++
 plugins/fusion-plugin-grok-runtime/manifest.json   |   6 +
 plugins/fusion-plugin-grok-runtime/package.json    |  40 ++++
 .../src/__tests__/cli-spawn.test.ts                | 103 ++++++++
 .../src/__tests__/index.test.ts                    |  12 +
 .../src/__tests__/probe.test.ts                    | 135 +++++++++++
 .../src/__tests__/process-manager.test.ts          |  96 ++++++++
 .../src/__tests__/provider.test.ts                 |  57 +++++
 .../src/__tests__/runtime-adapter.test.ts          |  21 ++
 .../fusion-plugin-grok-runtime/src/cli-spawn.ts    |  50 ++++
 plugins/fusion-plugin-grok-runtime/src/index.ts    |  74 ++++++
 plugins/fusion-plugin-grok-runtime/src/probe.ts    | 107 +++++++++
 .../src/process-manager.ts                         |  86 +++++++
 plugins/fusion-plugin-grok-runtime/src/provider.ts |  25 ++
 .../src/runtime-adapter.ts                         |  25 ++
 plugins/fusion-plugin-grok-runtime/src/types.ts    |  12 +
 plugins/fusion-plugin-grok-runtime/tsconfig.json   |  10 +
 .../fusion-plugin-grok-runtime/vitest.config.ts    |  22 ++
 pnpm-lock.yaml                                     |  25 ++
 pnpm-workspace.yaml                                |   1 +
 50 files changed, 2525 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7705

Fusion-Task-Lineage: b8194ea8-c773-4199-a52a-b0e4e7347192

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
55dae49b37 FN-7704: fix fn agent stop/start hanging up to 60s due to unclosed store handles
Fix CLI process exit so `fn agent stop`/`fn agent start` no longer hang up to 60s and eventually time out on repeated retries against the same agent.

- Root cause: `resolveProject()` cached an unclosed `TaskStore`, and `createAgentStore()` never closed the `AgentStore` it opened, leaving SQLite handles alive after the command's real work was done.
- Add `resolveProjectPathOnly`/`closeProjectStore` helpers in `project-context.ts` so path-only callers never leak a `TaskStore`.
- Explicitly close `AgentStore` on every exit/return path in `agent.ts`, since `process.exit()` skips pending `finally` blocks.
- Add a bounded fast-fail timeout around the state-store write (default 10s, overridable via `FUSION_AGENT_CMD_TIMEOUT_MS`) so a genuinely stuck operation fails fast with a clear error and non-zero exit instead of hanging.
- Add regression tests covering process-exit/store-closing behavior and update CLI reference docs.
- Add changeset for the patch release.

Files changed:
 .changeset/fn-7704-agent-cmd-hang-fix.md           |   7 +
 docs/cli-reference.md                              |   3 +
 .../commands/__tests__/agent-process-exit.test.ts  | 114 +++++++++++
 packages/cli/src/commands/__tests__/agent.test.ts  | 111 +++++++++-
 packages/cli/src/commands/agent.ts                 | 223 ++++++++++++++++-----
 packages/cli/src/project-context.ts                |  44 ++++
 6 files changed, 444 insertions(+), 58 deletions(-)

Fusion-Task-Id: FN-7704
Fusion-Task-Lineage: 4679d1a0-3ab8-48ce-86b7-5919bba805fb
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
c9a2b201be FN-7698: update cursor-cli-contract.md with verified model/auth commands
Corrects the FN-3396 preflight's assumed Cursor CLI commands with the real, verified contract captured and implemented in FN-7697.

- Documents model discovery as plain-text `cursor-agent models` (no --json flag), including output shape, empty-account state, and the unreliable --list-models alternative
- Documents the parsing strategy: extract id before first ' - ' per line, filtering header/tip/empty-state lines
- Documents authentication as derived from `cursor-agent status --format json` via `isAuthenticated`, distinct from the --version availability probe
- Updates the Windows shell-backed probe list to include the auth-status probe and the corrected model-discovery command
- Marks the FN-3396 contract-freeze section as superseded by the verified contract, retaining accurate parts (binary candidates, expected failure states, dynamic-first principle)
- Adds an update-history note and FNXC:CursorCli comment documenting the correction

Files changed:
 docs/cursor-cli-contract.md | 43 +++++++++++++++++++++++++++++--------------
 1 file changed, 29 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7698

Fusion-Task-Lineage: ae30b81c-f750-4011-85ae-883b1c5eb48b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:34 -07:00
gsxdsm
7fe18dfc9b FN-7696: surface Cursor CLI models in the model picker
Adds Cursor CLI model discovery to the dashboard's /api/models endpoint so cursor-agent-backed models appear in the picker when the Cursor CLI provider is enabled.

- Add cursor-model-cache.ts: short-TTL, single-flight cache for cursor-agent model discovery (no per-request CLI spawn)
- register-model-routes.ts additively merges cursor-cli models, deduped by provider/id, without displacing existing entries
- runtime-provider-probes.ts adds cursor-cli to configuredProviders when useCursorCli is on so rows survive the final provider filter
- Add unit tests for the cache and for register-model-routes cursor-cli integration
- Update docs/settings-reference.md
- Add changeset (patch/minor: @runfusion/fusion) documenting the fix

Files changed:
 .changeset/fn-7696-cursor-cli-models-in-picker.md  |   7 +
 docs/settings-reference.md                         |   2 +
 .../src/__tests__/cursor-model-cache.test.ts       | 158 +++++++++++++++++++
 .../register-model-routes-cursor-cli.test.ts       | 131 +++++++++++++--
 packages/dashboard/src/cursor-model-cache.ts       | 175 +++++++++++++++++++++
 .../dashboard/src/routes/register-model-routes.ts  |  45 ++++++
 packages/dashboard/src/runtime-provider-probes.ts  |  26 +++
 7 files changed, 527 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7696

Fusion-Task-Lineage: 2f2baf1e-5e4d-4c47-a112-b282a8ee45b6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 23:39:33 -07:00
gsxdsm
0755fc5747 fix(FN-7693): mobile terminal blank — exempt xterm measurement from * { max-width: 100% }
Real root cause of the blank mobile terminal (the FN-7692 remeasure guard did not
fix it and is reverted here). styles.css has a mobile-only reset
`@media (max-width: 768px) { * { max-width: 100% } }` to prevent horizontal
overflow. That universal selector also matches xterm's hidden character-measurement
subtree (`.xterm-helpers` / `.xterm-char-measure-element`). That subtree's containing
block (`.xterm-helpers`) is a 0x0 absolutely-positioned box, so `max-width: 100%`
resolves to `max-width: 0` and hard-caps xterm's character-cell measurement at 0.
FitAddon.fit() then proposes 0 columns/rows and `.xterm-screen` (plus the WebGL
canvas) collapses to 0x0 — the prompt streams in and is written into xterm's row DOM
but paints into a zero-size box, so the terminal is blank. Mobile-only, which is why
desktop always rendered fine.

Reproduced live via mobile emulation: `.xterm-char-measure-element` measured 0 while
an identical monospace span in the same container measured ~295px; `max-width: none`
on the measure element restored ~295px, and reopening the terminal with the exemption
active rendered the prompt with `.xterm-screen` sized 369x760. No amount of
remeasure/refit can fix this — the CSS re-caps the measurement to 0 every time — so
the FN-7692 CharSizeService guard is removed.

- Exempt `.xterm-helpers` / `.xterm-char-measure-element` from the mobile max-width
  reset in styles.css (covers both TerminalModal and SessionTerminal)
- Revert the ineffective FN-7692 remeasure guard and its tests
- Update changeset (patch) and the docs/solutions write-up to the real root cause

Note: root cause + fix validated in the automation browser via mobile emulation
(393px, iPhone UA, forced touch), not a physical device.

Fusion-Task-Id: FN-7693

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:09:26 -07:00
gsxdsm
67cc02750c FN-7692: fix misleading merger confirmation copy under active auto-merge
Correct the planner-oversight confirmation messaging so it no longer claims a hard block when the active auto-merge policy will actually advance the merge/pull-request stage unattended.

- decidePlannerRecovery accepts an additive, messaging-only `autoMergeWillProceed` flag and picks accurate reason wording (advisory vs. genuine human-approval block vs. neutral/unknown) for merger/pull-request await_confirmation decisions
- PlannerRecoveryController.tick threads `allowsAutoMergeProcessing(task, settings)` into decidePlannerRecovery as `autoMergeWillProceed`
- project-engine's requestConfirmation steering comment prefix changed from "confirmation required" to neutral "merge checkpoint" so it doesn't contradict the now-accurate reason text
- added regression tests in planner-recovery.test.ts and planner-overseer-intervention-wiring.test.ts
- added changeset and doc note

Files changed:
 .changeset/fn-7692-merger-confirmation-copy.md     |  7 +++
 docs/architecture.md                               | 10 +++-
 packages/core/src/__tests__/planner-recovery.test.ts    | 66 ++++++++++++++++++++++
 packages/core/src/planner-recovery.ts              | 36 +++++++++++-
 packages/engine/src/__tests__/planner-overseer-intervention-wiring.test.ts | 37 ++++++++++++
 packages/engine/src/planner-recovery-controller.ts | 14 ++++-
 packages/engine/src/project-engine.ts               | 11 +++-
 7 files changed, 176 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7692

Fusion-Task-Lineage: 187684b8-1d24-425d-85d4-627587469908

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 15:09:26 -07:00
gsxdsm
b7b1b71cae fix(FN-7692): recover blank mobile terminal when xterm screen collapses to 0x0
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>
2026-07-08 13:22:10 -07:00
gsxdsm
461a4a2711 FN-7689: add opt-in Anthropic-style prompt caching for custom providers
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>
2026-07-08 13:09:10 -07:00
gsxdsm
6a81871781 chore: refresh weekly test-velocity baseline (2026-W28) — confirm gate regression resolved
Gate wall-time back down to 8.8s (was 36.3s this morning, pre-FN-7667/7669 fix landing). Cycle 2026-W28 re-measured post-fix.
2026-07-08 11:19:47 -07:00
gsxdsm
07507f5264 FN-7688: add slow login-shell profile latency hint and docs
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>
2026-07-08 10:06:42 -07:00
gsxdsm
83e77431e2 FN-7684: move terminal shortcuts/zoom controls into footer on tablet widths
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>
2026-07-08 09:56:32 -07:00
gsxdsm
a832b7979f FN-7686: skip redundant session-list round trip on fresh terminal load
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>
2026-07-08 09:53:39 -07:00
gsxdsm
8ee8f15dc6 FN-7675: add agent runtime self-awareness to system prompts
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>
2026-07-08 08:16:30 -07:00
gsxdsm
aa534c19af FN-7672: recover durable agents stuck in error state despite active manager
Root-causes 4 correlated CTO-report agent failures where durable non-ephemeral agents got stuck in `error` state indefinitely because the heartbeat scheduler stops ticking error-state agents entirely, and self-healing's recovery sweep previously only considered them when their manager row was missing.

- SelfHealingManager: scope the `managerMissing` gate to the "running" orphan-detection path only, so "error"-state durable agents with a present/active manager now fall through to the existing transient/operator-actionable/active-execution/cooldown/retry-budget recovery guards instead of being skipped outright
- Add FNXC:AgentHeartbeat comment documenting the FN-7672 incident and rationale for the scoping change
- Extend self-healing.test.ts with coverage for manager-present durable agents in error state
- Add changeset (patch) describing the fix for release notes
- Update docs/agents.md accordingly

Files changed:
 .changeset/fn-7672-durable-agent-recovery.md       |   7 ++
 docs/agents.md                                     |   2 +
 packages/engine/src/__tests__/self-healing.test.ts | 129 ++++++++++++++++++++-
 packages/engine/src/self-healing.ts                |  24 +++-
 4 files changed, 160 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-7672

Fusion-Task-Lineage: 6676dc9e-66e7-4f70-804a-cccf77e8d337

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 05:38:01 -07:00
gsxdsm
ad9a72176c FN-7669: pre-bundle @fusion/core gate-safe barrel to cut engine-core gate import-phase cost
Prototype and land a rebuilt-every-run esbuild bundle of the @fusion/core gate-safe barrel closure, collapsing the engine-core gate's per-fork Vite SSR import-phase cost (18 forks x ~430-file closure re-resolved from scratch) into a single file load per fork.

- Add scripts/build-engine-core-gate-bundle.mjs: esbuild-bundles packages/core/src/index.gate.ts (220 first-party files, packages:"external" so third-party/node: imports stay external, treeShaking:false to preserve side effects) into packages/core/.gate-bundle/core.mjs + core.meta.json
- Wire the builder into packages/engine/vitest.config.ts's engine-core project globalSetup (alongside the existing vitest-teardown hook) so the bundle is rebuilt fresh before every gate invocation, and repoint the @fusion/core resolve.alias at the bundled output instead of index.gate.ts source
- Place the bundle output at packages/core/.gate-bundle/ as a sibling of packages/core/node_modules/ (not nested inside it) to avoid Vite SSR's external-dep heuristic, which would otherwise silently defeat vi.mock interception for imports nested in the bundle
- Gitignore packages/core/.gate-bundle/ and add a matching ESLint ignore entry so the generated bundle text is never linted or committed
- Add esbuild ^0.25.12 as a root devDependency (pnpm-lock.yaml updated accordingly)
- Document the pre-bundling rationale, placement constraints, and measured A/B wall-time results in docs/testing.md

Verified: pnpm test:gate passes (335/335 engine-core tests, 63/63 CLI ci-shape tests), engine package typecheck clean, eslint clean on touched files.

Files changed:
 .gitignore                                |  11 ++
 docs/testing.md                           |   3 +
 eslint.config.mjs                         |  10 ++
 package.json                              |   1 +
 packages/engine/vitest.config.ts          |  50 ++++++++-
 pnpm-lock.yaml                            |   3 +
 scripts/build-engine-core-gate-bundle.mjs | 174 ++++++++++++++++++++++++++++++
 7 files changed, 247 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7669

Fusion-Task-Lineage: 62b06b2a-4ac6-45ae-ac79-9771132bc303

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 04:24:30 -07:00
gsxdsm
6902077972 FN-7667: add gate-scoped @fusion/core barrel to decouple engine-core gate from full barrel growth
Introduces a project-scoped @fusion/core barrel used only by the engine-core
gate project, so new feature modules added to the full barrel don't silently
inflate the gate's transform/import cost.

- Add packages/core/src/index.gate.ts, a copy of the full @fusion/core barrel
  minus export statements for modules added since the last re-audit baseline
  (i.e. it still re-exports everything the full barrel does except newly
  added, gate-irrelevant feature modules).
- Update packages/engine/vitest.config.ts to add a project-scoped
  resolve.alias mapping @fusion/core -> packages/core/src/index.gate.ts for
  the engine-core project only; engine-default/engine-reliability/engine-slow
  and @fusion/engine continue to resolve the full barrel.
- Document the gate-safe barrel and its audit procedure in docs/testing.md.

Files changed:
 docs/testing.md                  |    3 +
 packages/core/src/index.gate.ts  | 2102 ++++++++++++++++++++++++++++++++++++++
 packages/engine/vitest.config.ts |   17 +
 3 files changed, 2122 insertions(+)

Fusion-Task-Id: FN-7667

Fusion-Task-Lineage: 054ec89a-d973-44dd-b9ac-ad266f553f01

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 03:28:37 -07:00
gsxdsm
845fad2aa4 FN-7666: record weekly test-velocity regression finding (gate 7.7s -> 36.3s)
Narrative: update the 2026-W28 test-velocity baseline report with the newly measured merge-gate/changed-test wall-time regression and document root-cause attribution.
- Bump baseline cycle to 2026-W28 (captured 2026-07-08T09:30:50.606Z)
- Update headline metrics table: gate 7.7s -> 36.3s (+28.6s), boot smoke 17.4s -> 25.3s (+7.9s), pnpm test 9.3s -> 43.4s (+34.0s), quarantine count unchanged at 1
- Add FN-7666 finding note attributing the regression to ~4,305 new re-exported source lines across 14 modules pulled through the @fusion/core / @fusion/engine barrels, inflating per-fork import/transform cost across the 18 curated engine-core gate files; confirms it is a genuine regression (not cache/env noise) via cold-vs-warm reruns; files follow-up FN-7667 for the barrel-decoupling fix
- Update weekly history table (previous/latest/delta rows) and the #leads post text block
- Append new row capturing this week's measurements to scripts/test-velocity-history.json

Files changed:
 docs/test-velocity-baseline.md     |  23 ++++----
 scripts/test-velocity-history.json | 112 +++++++++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7666

Fusion-Task-Lineage: 73ec627c-2fc6-4289-8e6e-3e839ed5c675

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 02:45:47 -07:00
gsxdsm
03161adfb9 FN-7659: paginate and sort the Archived column newest-first
Adds server-side pagination for the Archived task column, sorted by most-recently-archived first, with a Show more control on the dashboard.

- Add ArchiveDatabase.listPage and TaskStore.listArchivedTasks for a bounded SQL LIMIT/OFFSET read ordered by archivedAt DESC
- Add GET /tasks/archived route for paged archive fetches, leaving the legacy merged listTasks({includeArchived}) path unchanged
- Wire useTasks.loadArchivedTasks to fetch page 1 on first Archived-column expand and loadMoreArchivedTasks for subsequent pages
- Add a "Show more" affordance in Column.tsx/Board.tsx/MainContent.tsx to trigger loading additional archived pages
- Extend taskSorting.ts to keep archived task ordering stable with the new paged data
- Add core and dashboard tests covering archive pagination and store/route behavior
- Add changeset for @runfusion/fusion (minor) and update docs/storage.md and docs/dashboard-guide.md

Files changed:
 .changeset/FN-7659-archived-pagination.md          |   7 +
 docs/dashboard-guide.md                            |   4 +-
 docs/storage.md                                    |   7 +
 packages/core/src/__tests__/archive-db-pagination.test.ts    |  94 ++++++++
 packages/core/src/__tests__/store-archive-search.test.ts     |  63 ++++++
 packages/core/src/archive-db.ts                    |  18 ++
 packages/core/src/store.ts                         |  32 +++
 packages/dashboard/app/App.tsx                     |   5 +-
 packages/dashboard/app/api/legacy.ts               |  19 ++
 packages/dashboard/app/components/Board.tsx        |  19 +-
 packages/dashboard/app/components/Column.tsx       |  48 ++++-
 packages/dashboard/app/components/__tests__/Column.test.tsx       |  41 ++++
 packages/dashboard/app/components/__tests__/taskSorting.test.ts   |  27 +++
 packages/dashboard/app/components/dashboard/MainContent.tsx       |   9 +
 packages/dashboard/app/components/dashboard/types.ts    |   6 +
 packages/dashboard/app/components/taskSorting.ts   |  16 ++
 packages/dashboard/app/hooks/__tests__/useTasks.test.ts | 236 ++++++++++++++++++++-
 packages/dashboard/app/hooks/useTasks.ts           | 173 ++++++++++++++-
 packages/dashboard/app/test/mockApi.ts             |   3 +
 packages/dashboard/src/routes/__tests__/tasks-archived-pagination.test.ts |  94 ++++++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts  |  32 +++
 21 files changed, 930 insertions(+), 23 deletions(-)

Fusion-Task-Id: FN-7659

Fusion-Task-Lineage: 7a5a1f62-277c-4f29-883c-62e75b269bc5

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 01:29:56 -07:00
gsxdsm
dd9fa2d3cb FN-7661: expose removeLineageReferences on fn_task_archive/fn_task_delete
Fixes fn_task_archive and fn_task_delete rejecting tasks still referenced as a lineage parent, with no tool-exposed way to clear that reference.

- Add optional removeLineageReferences boolean param to fn_task_archive and fn_task_delete tool schemas, forwarded to store.archiveTask/store.deleteTask
- Update tool descriptions and prompt guidelines to advertise the recovery path (removeLineageReferences:true) when a lineage-parent block occurs
- Add task-lineage-unlink.test.ts covering the new parameter behavior
- Document the change in docs/storage.md
- Add changeset (@runfusion/fusion minor, category: fix)

Files changed:
 .changeset/fn-7661-lineage-unlink-tools.md         |   7 +
 docs/storage.md                                    |   1 +
 packages/cli/src/__tests__/task-lineage-unlink.test.ts | 199 +++++++++++++++++++++
 packages/cli/src/extension.ts                      |  28 ++-
 4 files changed, 232 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7661

Fusion-Task-Lineage: 414c046c-43df-4995-85a5-ff00b345de50

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:33:02 -07:00
gsxdsm
97e46674f4 FN-7660: rename remaining board-column "Triage" labels to "Planning"
Rename remaining "Triage" board-column label references to "Planning" across dashboard editors, locale strings, and docs.

- Fixed BUILTIN_WORKFLOW_COLUMN_LABELS.triage in board-workflows.ts (was still "Triage", clobbering FN-7599's IR rename); column id "triage" stays unchanged
- Updated dashboard components (AgentDetailView, RoutineEditor, ScheduleForm, ScheduleStepsEditor) to use the "Planning" label
- Updated en/app.json locale strings (board.triage, schedule.columnTriage/taskColumnTriage/triageColumn) to "Planning"
- Updated board-workflows tests to assert the new label
- Updated docs/dashboard-guide.md Triage/Planning column references to "Planning", preserving the FN-7653 intake-only gating correction
- Added a patch changeset documenting the label-consistency fix

Files changed:
 .changeset/fn-7660-planning-label-consistency.md               |  7 +++++++
 docs/dashboard-guide.md                                        |  9 +++++----
 packages/dashboard/app/components/AgentDetailView.tsx          |  2 +-
 packages/dashboard/app/components/RoutineEditor.tsx            |  2 +-
 packages/dashboard/app/components/ScheduleForm.tsx             |  2 +-
 packages/dashboard/app/components/ScheduleStepsEditor.tsx      |  2 +-
 .../dashboard/src/routes/__tests__/board-workflows.test.ts     |  7 ++++++-
 packages/dashboard/src/routes/board-workflows.ts               | 10 +++++++++-
 packages/i18n/locales/en/app.json                              |  8 ++++----
 9 files changed, 35 insertions(+), 14 deletions(-)

Fusion-Task-Id: FN-7660

Fusion-Task-Lineage: fc6c94d6-9876-4c0e-80b1-a5518903952e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-08 00:27:52 -07:00
gsxdsm
f7d9509294 FN-7658: gate same-agent duplicate auto-archiving behind opt-in setting
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>
2026-07-08 00:23:32 -07:00
gsxdsm
511bcaf56d FN-7657: persist GitHub issue import modal state across close/reopen
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>
2026-07-08 00:14:52 -07:00
gsxdsm
ebe9b9fa67 FN-7653: restrict plan auto-approve toggle to the intake/planning column
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>
2026-07-07 23:55:50 -07:00
gsxdsm
efabdd6f04 FN-7651: remove chat "Search in title only" toggle
Simplifies chat search UX by always matching both title and message content, removing the now-unneeded title-only toggle button and state.

- Removed the "Search in title only" toggle button and its CSS from ChatView
- Dropped searchInTitleOnly/setSearchInTitleOnly state and logic from useChat; content-search query params are now always-on
- Updated ChatView tests to drop title-only toggle interactions and assertions
- Removed the title-only-search i18n string across all locales
- Updated dashboard-guide.md docs to reflect the simplified search behavior
- Added a patch changeset documenting the removal

Files changed:
 .../fn-7651-remove-chat-title-only-toggle.md       |  7 +++
 docs/dashboard-guide.md                            |  4 +-
 packages/dashboard/app/components/ChatView.css     | 18 --------
 packages/dashboard/app/components/ChatView.tsx     | 26 +++--------
 .../__tests__/ChatView.autosize.test.tsx           |  2 -
 .../__tests__/ChatView.content-search.test.tsx     | 51 ++++++----------------
 .../components/__tests__/ChatView.draft.test.tsx   |  2 -
 .../__tests__/ChatView.hash-mention.test.tsx       |  2 -
 .../__tests__/ChatView.mobile-render.test.tsx      |  2 -
 .../components/__tests__/ChatView.rooms.test.tsx   |  2 -
 .../__tests__/ChatView.scroll-to-top.test.tsx      |  2 -
 .../components/__tests__/ChatView.test-harness.tsx |  2 -
 packages/dashboard/app/hooks/useChat.ts            | 39 ++++++++---------
 packages/i18n/locales/en/app.json                  |  1 -
 packages/i18n/locales/es/app.json                  |  1 -
 packages/i18n/locales/zh-CN/app.json               |  1 -
 packages/i18n/locales/zh-TW/app.json               |  1 -
 packages/i18n/locales/ko/app.json                  |  1 -
 packages/i18n/locales/fr/app.json                  |  1 -
 19 files changed, 47 insertions(+), 118 deletions(-)

Fusion-Task-Id: FN-7651

Fusion-Task-Lineage: 5a31825c-8e6e-441f-800d-6053f5f844ba

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 23:15:53 -07:00
gsxdsm
bec8987ce9 FN-7648: gate hold-release on trait-based unplanned-card check, not literal todo column
Blocks planning/intake column cards from entering processing columns regardless of literal column id, so renamed custom intake/planning columns are covered by the same guard as the legacy todo column.

- Add isUnplannedForExecution() in hold-release.ts: true when task.status==="planning", or when the card sits in the legacy todo column or a column carrying the intake trait AND its PROMPT.md still equals the bootstrap stub.
- Route issueRelease() (used by the sweep, promoteHeldTask, and releaseHeldTaskByEvent) through this guard before releasing into any countsTowardWip processing column.
- Update scheduler.ts's reserveSlot guard to use the same trait-based predicate instead of a hardcoded "todo" column id check.
- Add regression tests in hold-release.test.ts and scheduler-workflow-cutover.test.ts covering renamed intake/planning columns.
- Document the invariant in docs/architecture.md and docs/workflow-steps.md.
- Add changeset (patch) describing the fix.

Files changed:
 .changeset/fn-7648-unplanned-intake-cards-never-execute.md |   7 +
 docs/architecture.md                               |   2 +
 docs/workflow-steps.md                             |   2 +
 packages/engine/src/__tests__/hold-release.test.ts | 238 +++++++++++++++++++++
 packages/engine/src/__tests__/scheduler-workflow-cutover.test.ts |  60 +++++-
 packages/engine/src/hold-release.ts                |  60 ++++++
 packages/engine/src/scheduler.ts                   |  26 +--
 7 files changed, 378 insertions(+), 17 deletions(-)

Fusion-Task-Id: FN-7648

Fusion-Task-Lineage: a4b54d30-f86d-4eb9-9cf2-6ac55b6dbe58

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:21 -07:00
gsxdsm
4e8c621e9c FN-7641: fix cards stranded after out-of-band/workspace merges by allowing proven-merge rehome
Fixes a state-machine bug family where cards got stranded after out-of-band or workspace merges landed: store.moveTask now allows a proven-merge recoveryRehome to cross legacy columns (e.g. todo→done), and nodeId='end' finalize no longer silently no-ops — it finalizes on durable merge proof or returns an explicit error, consistently across the dashboard route, the CLI task-update tool, and store.updateTask.

- packages/core/src/store.ts: allow proven-merge recoveryRehome moves across legacy columns (e.g. todo→done) instead of rejecting them
- packages/core/src/node-override-guard.ts: nodeId='end' finalize now checks for durable merge proof and returns an explicit error instead of silently no-op'ing
- packages/dashboard/src/routes/register-task-workflow-routes.ts: dashboard workflow route surfaces the new explicit finalize error/behavior
- packages/cli/src/extension.ts: CLI task-update tool surfaces the same explicit finalize error/behavior
- docs/task-management.md: documented the updated finalize/rehome behavior
- Added regression tests across core (node-override-guard, store-movement, task-node-override), dashboard (register-task-workflow-routes.nodeid-finalize), engine (merger-merge-lifecycle), and CLI (extension) covering the stranded-card invariant
- Added changeset for @runfusion/fusion (patch)

Files changed:
 .changeset/fn-7641-stranded-cards-after-merge.md   |  7 ++
 docs/task-management.md                            |  2 +
 packages/cli/src/__tests__/extension.test.ts       | 59 ++++++++++++++
 packages/cli/src/extension.ts                      | 10 +++
 .../core/src/__tests__/node-override-guard.test.ts | 93 +++++++++++++++++++++
 packages/core/src/__tests__/store-movement.test.ts | 94 ++++++++++++++++++++++
 .../core/src/__tests__/task-node-override.test.ts  | 73 +++++++++++++++++
 packages/core/src/node-override-guard.ts           | 69 +++++++++++++++-
 packages/core/src/store.ts                         | 69 +++++++++++++++-
 ...er-task-workflow-routes.nodeid-finalize.test.ts | 90 +++++++++++++++++++++
 .../src/routes/register-task-workflow-routes.ts    | 10 +++
 .../src/__tests__/merger-merge-lifecycle.test.ts   | 58 +++++++++++++
 12 files changed, 631 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-7641

Fusion-Task-Lineage: 48ea7851-ee68-48f1-92f9-302d0da5acff

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:20 -07:00
gsxdsm
923bba7082 FN-7645: force re-arm zombie heartbeat timers detected as stale during audit
Fixes the heartbeat timer audit so it repairs not just missing timer registrations but also 'zombie' ones — timer entries that remain present in memory after their underlying interval silently stopped firing. Long-interval (~1h) agents were most affected since a single lost tick compounded into hours of staleness before self-healing noticed.

- HeartbeatTriggerScheduler audit now computes staleness (elapsed vs repair-stale threshold) up front for every timer-eligible agent, not only for agents missing a timer entry
- Present-but-stale timer entries are now treated as non-advancing and force cleared/re-registered via registerAgent() (which already clears any existing timer before re-arming)
- Fresh (non-stale) present timers are left alone so healthy short-interval agents are never force-re-armed or double-ticked
- Repair reason/log messages now distinguish zombie-timer-rearmed repairs from missing-registration repairs, and the summary log reports counts for each
- Added heartbeat-scheduler tests covering the zombie-timer repair path
- Added changeset and a docs/architecture.md note

Files changed:
 .changeset/fn-7645-heartbeat-rearm.md              |   7 +
 docs/architecture.md                               |   1 +
 .../src/__tests__/heartbeat-scheduler.test.ts      | 223 +++++++++++++++++++++
 packages/engine/src/agent-heartbeat.ts             |  42 +++-
 4 files changed, 266 insertions(+), 7 deletions(-)

Fusion-Task-Id: FN-7645

Fusion-Task-Lineage: 652bc2eb-a660-4306-9f85-d2d5f9ca7e38

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:18 -07:00
gsxdsm
6777eea5d2 FN-7631: add content search to Chat sidebar with title-only toggle
Chat sidebar search now matches message content by default, not just the conversation title/agent, with an opt-out toggle to restore title-only filtering.

- Add ChatStore.searchSessionsByMessageContent (parameterized LIKE ... ESCAPE) for server-side content search across sessions
- GET /chat/sessions route (register-chat-routes.ts, legacy.ts) gains q/titleOnly query params, debounced server-side content lookup merged with local title/agent matches
- useChat hook exposes searchInTitleOnly state and wires debounced content search into session list results
- ChatView renders a "Search in title only" toggle beside the search box (desktop + mobile) and shows a "Matched: ..." preview snippet on content-matched rows
- Task-planner sessions remain excluded from content matches via the same common-feed visibility guard used for the normal session list
- Add unit/integration tests: chat-store content-search, chat-routes API test, ChatView content-search test
- Update docs/dashboard-guide.md to document the new content search behavior and toggle
- Add changeset fn-7631-chat-content-search.md (@runfusion/fusion minor)

Files changed:
 .changeset/fn-7631-chat-content-search.md          |   7 +
 docs/dashboard-guide.md                            |   2 +
 .../__tests__/chat-store.content-search.test.ts    | 157 +++++++++++++++++++++
 packages/core/src/chat-store.ts                    |  64 +++++++++
 packages/core/src/chat-types.ts                    |   8 ++
 packages/dashboard/app/api/legacy.ts               |  23 ++-
 packages/dashboard/app/components/ChatView.css     |  30 ++++
 packages/dashboard/app/components/ChatView.tsx     |  26 ++++
 .../__tests__/ChatView.autosize.test.tsx           |   2 +
 .../__tests__/ChatView.content-search.test.tsx     | 114 +++++++++++++++
 .../components/__tests__/ChatView.draft.test.tsx   |   2 +
 .../__tests__/ChatView.hash-mention.test.tsx       |   2 +
 .../__tests__/ChatView.mobile-render.test.tsx      |   2 +
 .../components/__tests__/ChatView.rooms.test.tsx   |   2 +
 .../__tests__/ChatView.scroll-to-top.test.tsx      |   2 +
 .../components/__tests__/ChatView.test-harness.tsx |   2 +
 packages/dashboard/app/hooks/useChat.ts            | 105 ++++++++++++--
 .../dashboard/src/__tests__/chat-routes.test.ts    |  78 ++++++++++
 .../dashboard/src/routes/register-chat-routes.ts   |  39 ++++-
 packages/i18n/locales/en/app.json                  |   2 +
 packages/i18n/locales/es/app.json                  |   2 +
 packages/i18n/locales/fr/app.json                  |   2 +
 packages/i18n/locales/ko/app.json                  |   2 +
 packages/i18n/locales/zh-CN/app.json               |   2 +
 packages/i18n/locales/zh-TW/app.json               |   2 +
 25 files changed, 667 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7631

Fusion-Task-Lineage: bc68b489-26a7-453e-901b-bda816af364e

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:17 -07:00
gsxdsm
1b7bb1fe18 FN-7639: wire message editing into Planner Chat
Adds the ability to edit and resend a prior user message in task-detail Planner Chat, discarding subsequent turns without reverting already-applied steering/refinement side effects.

- Wire FN-7628's editChatMessage + rewindSessionForEdit into TaskPlannerChatTab for synthetic task-planner:<id> sessions
- Add edit affordance/UI flow and message resend handling in TaskPlannerChatTab
- Ensure already-applied steering comments and refinement tasks are not reverted when a turn is discarded
- Expand TaskPlannerChatTab test coverage for edit/resend flows
- Update dashboard guide docs to describe the new Planner Chat edit behavior
- Add changeset for @runfusion/fusion (minor)

Files changed:
 .changeset/fn-7639-planner-chat-edit.md            |   7 +
 docs/dashboard-guide.md                            |   4 +-
 .../app/components/TaskPlannerChatTab.tsx          |  90 +++++++-
 .../__tests__/TaskPlannerChatTab.test.tsx          | 242 ++++++++++++++++++++-
 4 files changed, 328 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-7639

Fusion-Task-Lineage: ed568a81-12fa-42a8-9ecf-29e6c9a2a884

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:17 -07:00
gsxdsm
26f22861fa FN-7637: port bundled-plugin auto-install into @fusion/core for the desktop runtime
Move the host-agnostic bundled-plugin auto-install logic (manifest loading, entry-path
resolution, install/update/enable flow) out of the CLI package into @fusion/core so the
desktop embedded runtime can auto-install bundled runtime plugins without depending on
the CLI package; the CLI module becomes a thin adapter that supplies its own bundle-dir
resolution to the shared helper.

- Add packages/core/src/plugins/bundled-plugin-install.ts with the shared, host-agnostic
  ensureBundledPluginInstalled / ensureBundledDependencyGraphPluginInstalled /
  ensureBundledCursorRuntimePluginInstalled implementation and BUNDLED_PLUGIN_IDS/
  isBundledPluginId/resolvePluginEntryPath, exported from @fusion/core's index.
- Slim packages/cli/src/plugins/bundled-plugin-install.ts to a CLI-specific
  candidate-bundle-dir resolver that delegates to @fusion/core and re-exports the same
  public surface dashboard.ts/serve.ts/daemon.ts already depend on.
- Remove the now-redundant packages/cli/src/plugins/__tests__/resolve-plugin-entry-path-sync.test.ts
  (coverage moved with the implementation to @fusion/core).
- Add packages/desktop/src/bundled-plugin-dirs.ts to resolve each bundled plugin's staged
  package directory via import.meta.resolve, mirroring the CLI's dist/plugins/<id> resolver.
- Wire local-runtime.ts and local-server.ts to call ensureBundledPluginInstalled before
  loadAllPlugins() and expose a lazy-install callback for PUT /api/plugins/:id/settings,
  mirroring the CLI dashboard command's startup auto-install pass.
- Update docs/PLUGIN_AUTHORING.md to describe the shared bundled-plugin-install location.

Files changed:
 docs/PLUGIN_AUTHORING.md                           |  11 +
 .../__tests__/bundled-plugin-install.test.ts       | 619 ++-------------------
 .../resolve-plugin-entry-path-sync.test.ts         |  97 ----
 packages/cli/src/plugins/bundled-plugin-install.ts | 250 +--------
 packages/core/src/index.ts                         |   8 +
 .../__tests__/bundled-plugin-install.test.ts       | 391 +++++++++++++
 .../core/src/plugins/bundled-plugin-install.ts     | 186 +++++++
 .../src/__tests__/bundled-plugin-dirs.test.ts      |  59 ++
 .../desktop/src/__tests__/local-runtime.test.ts    | 183 +++++-
 .../desktop/src/__tests__/local-server.test.ts     |  96 +++-
 packages/desktop/src/bundled-plugin-dirs.ts        |  61 ++
 packages/desktop/src/local-runtime.ts              |  66 ++-
 packages/desktop/src/local-server.ts               |  36 +-
 13 files changed, 1171 insertions(+), 892 deletions(-)

Fusion-Task-Id: FN-7637

Fusion-Task-Lineage: 953c5b82-a079-4600-b3af-45c974cd5014

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:16 -07:00
gsxdsm
42009cfdb9 FN-7628: allow editing sent chat messages and rewinding agent responses
Adds the ability to edit a previously sent message in an agent chat, which rewinds the session/task room and regenerates the response from the edited message.

- Add chat-store support for locating/replacing a message and truncating subsequent history for a rewind
- Add a chat-manager rewind-session flow and a new register-chat-routes endpoint to rewind a room to an edited message
- Add legacy API route wiring and useChat hook support for issuing an edit request
- Add ChatView/StandardChatSurface/TaskPlannerChatTab UI affordances (edit control, styling) to trigger message edits
- Add a changeset documenting the new chat message-edit capability
- Add unit/integration tests covering chat-store rewind logic, chat-manager rewind-session behavior, chat routes, useChat, and ChatView edit UI

Files changed:
 .changeset/fn-7628-chat-message-edit.md            |   7 +
 docs/dashboard-guide.md                            |   4 +
 packages/core/src/__tests__/chat-store.test.ts     | 171 ++++++++++++++
 packages/core/src/chat-store.ts                    |  95 ++++++++
 packages/dashboard/app/api/legacy.ts               |  22 ++
 packages/dashboard/app/components/ChatView.css     |  78 ++++++
 packages/dashboard/app/components/ChatView.tsx     |  14 ++
 .../app/components/StandardChatSurface.tsx         |  87 ++++++-
 .../app/components/TaskPlannerChatTab.tsx          |   8 +
 .../__tests__/ChatView.autosize.test.tsx           |   1 +
 .../__tests__/ChatView.default-model-icon.test.tsx |   1 +
 .../components/__tests__/ChatView.draft.test.tsx   |   1 +
 .../__tests__/ChatView.hash-mention.test.tsx       |   1 +
 .../__tests__/ChatView.message-edit.test.tsx       | 262 +++++++++++++++++++++
 .../__tests__/ChatView.mobile-render.test.tsx      |   1 +
 .../components/__tests__/ChatView.rooms.test.tsx   |   1 +
 .../__tests__/ChatView.scroll-to-top.test.tsx      |   1 +
 .../components/__tests__/ChatView.test-harness.tsx |   1 +
 .../dashboard/app/hooks/__tests__/useChat.test.ts  |  98 ++++++++
 packages/dashboard/app/hooks/useChat.ts            |  60 +++++
 .../__tests__/chat-manager-rewind-session.test.ts  | 185 +++++++++++++++
 .../dashboard/src/__tests__/chat-manager.test.ts   |  12 +
 .../dashboard/src/__tests__/chat-routes.test.ts    | 124 ++++++++++
 packages/dashboard/src/chat.ts                     | 147 +++++++++++-
 .../dashboard/src/routes/register-chat-routes.ts   |  52 ++++
 25 files changed, 1429 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-7628

Fusion-Task-Lineage: 36d98989-1b75-428c-baf1-b2c7e8e78013

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:06:15 -07:00
gsxdsm
5b243f1223 FN-7636: surface Hermes runtime models in /api/models picker
Adds a short-TTL, single-flight Hermes model cache and merges Hermes profile-list models additively into the dashboard's /api/models endpoint under the hermes provider, without displacing existing entries.

- Add packages/dashboard/src/hermes-model-cache.ts: single-flight, short-TTL cache wrapping `hermes profile list` CLI output (no per-request spawn)
- Merge Hermes-provided models into register-model-routes.ts's /api/models response, deduped by provider/id, additive-only
- Add unit tests for the Hermes model cache and the /api/models route's Hermes merge behavior
- Document the new behavior in docs/settings-reference.md
- Add changeset (@runfusion/fusion: minor) describing the feature for release notes

Files changed:
 .changeset/fn-7636-hermes-picker-models.md         |   7 +
 docs/settings-reference.md                         |   2 +
 packages/dashboard/src/__tests__/hermes-model-cache.test.ts       | 158 +++++++++++++++++
 packages/dashboard/src/__tests__/register-model-routes-hermes.test.ts | 186 +++++++++++++++++++++
 packages/dashboard/src/hermes-model-cache.ts       | 168 +++++++++++++++++++
 packages/dashboard/src/routes/register-model-routes.ts  |  33 ++++
 6 files changed, 554 insertions(+)

Fusion-Task-Id: FN-7636

Fusion-Task-Lineage: c5ab3bba-37e4-416b-9325-667a8ef321dc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:55 -07:00
gsxdsm
0f2bfa546c FN-7629: add enable/disable control for built-in runtime plugins
Adds a durable Plugin Manager toggle to enable/disable built-in runtime plugins (Hermes, Paperclip, OpenClaw, Droid) that persists across restarts, replacing the dead-end "Built-in metadata only" CTA.

- renderBuiltinPluginSection now renders an enable/disable toggle for runtime built-ins regardless of installed status
- Disabling a not-yet-installed built-in first installs it (mirroring CLI's ensureBundledPluginInstalled) then immediately disables it, so a plugin_installs row + disabled project state exists with no new persistence primitive needed
- HermesRuntimeCard/OpenClawRuntimeCard/PaperclipRuntimeCard now show "Disabled in Plugin Manager" instead of a stale detected/connected status when disabled
- Added i18n strings across all locales and updated docs
- Added changeset for @runfusion/fusion (minor)
- Expanded plugin-loader and PluginManager test coverage for the new disable/enable flows

Files changed:
 .changeset/fn-7629-builtin-runtime-disable.md      |   7 +
 docs/dashboard-guide.md                            |   1 +
 docs/plugin-management.md                          |   4 +
 packages/core/src/__tests__/plugin-loader.test.ts  |  46 ++++++
 .../dashboard/app/components/HermesRuntimeCard.tsx |  40 ++++-
 .../app/components/OpenClawRuntimeCard.tsx         |  32 +++-
 .../app/components/PaperclipRuntimeCard.tsx        |  32 +++-
 .../dashboard/app/components/PluginManager.css     |  34 +++++
 .../dashboard/app/components/PluginManager.tsx     | 167 +++++++++++++++------
 .../components/__tests__/PluginManager.test.tsx    |  12 +-
 .../__tests__/PluginManager.toggle.test.tsx        | 104 ++++++++++++-
 packages/i18n/locales/en/app.json                  |   4 +
 packages/i18n/locales/es/app.json                  |   4 +
 packages/i18n/locales/fr/app.json                  |   4 +
 packages/i18n/locales/ko/app.json                  |   4 +
 packages/i18n/locales/zh-CN/app.json               |   4 +
 packages/i18n/locales/zh-TW/app.json               |   4 +
 packages/i18n/src/resources.d.ts                   |   4 +
 18 files changed, 440 insertions(+), 67 deletions(-)

Fusion-Task-Id: FN-7629
Fusion-Task-Lineage: 2a25bb1f-3f73-4273-8769-af05c61778f9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-07 22:04:53 -07:00
gsxdsm
ca7c987ab5 FN-7621: fix mobile terminal shortcut bar horizontal scroll defeated by ancestor touch-action lock
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>
2026-07-07 22:04:49 -07:00