Commit Graph

395 Commits

Author SHA1 Message Date
gsxdsm
3bf9bf5f74 collapse the plan-admission-throttle payload to one gate (+ AGENTS.md) (#2562)
The cross-project semaphore is deleted, so
`task:plan-admission-throttled` was describing a gate that no longer
exists. Nothing wires `options.semaphore` any more, which left three
things dead-but-visible:

- `semaphoreAvailable` was permanently `Infinity`, so
`Math.min(projectRoom, …)` was a no-op keeping a deleted limiter in the
arithmetic
- `blockedBy` was a **discriminator** between `"running-agent cap"` and
`"global semaphore"`; only the first can occur
- four `semaphore*` metadata fields were always `undefined`, and two
more terms in the dedupe signature were constant

## `blockedBy` is kept, not dropped

Even though it is now a constant. The event exists (FN-8600) to answer
*“why did this card sit queued to plan?”* after the fact — a named
reason answers that even when there is one gate, whereas a payload with
**no** reason field reads as “unknown”. It costs nothing and preserves
the shape if a second gate is ever added.

The dedupe signature drops the two semaphore terms and keeps the
eligible task IDs — that term is what stops a **new** card’s stall being
swallowed when the counts land on an unchanged tuple, which is the
property the event depends on.

## AGENTS.md

It documented the removed field names verbatim, so it is updated in the
same commit. Leaving docs describing a payload the code cannot emit is
exactly the readable-but-wrong artifact this program keeps deleting.

## Verification

`pnpm lint` clean · engine `tsc` clean · `pnpm test:gate` green · triage
suites **234/234**.

---

**Correction I owe on `concurrency.ts`, measured rather than
estimated.** I earlier told the coordinator ~75% of its 886 lines could
go with the cross-project cap. That was line-range arithmetic and it was
wrong. With the cap now fully removed, `concurrency.ts` is **still 886
lines**, because `AgentSemaphore` has four consumers unrelated to it —
`verification-concurrency` (maxConcurrentVerifications),
`research-orchestrator` (research runs), `experiment-executor`
(maxConcurrentExperiments), `step-session-executor` (parallel steps) —
plus `ProjectAdmissionCoordinator`, which is FN-8453 oldest-first
**ordering**, not a limiter. The real remaining win there is the
pre-held-slot bookkeeping and the idle-semaphore leak recovery, which
existed to service the global instance; I will measure that as its own
slice rather than quote a fraction.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Updated plan admission throttling to consistently use the project’s
running-agent capacity.
* Improved throttle audit events by reporting stable capacity details
and removing obsolete semaphore information.
* Preserved accurate deduplication for repeated throttling events,
including changes in stalled tasks.

* **Documentation**
* Updated run-audit guidance to match the revised throttling event
format.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 00:31:27 -07:00
gsxdsm
d193dcdbb0 chore(lint): ban React components declared inside another component
Adds fusion-react/no-nested-component-definitions, a custom rule in the house
style of the existing detached-spawn guard. A component declared in render is a
new element type every render, so React remounts its subtree on each parent
update and destroys focus, scroll, and local state.

This pattern shipped three times without review or tests catching it: FN-8606's
ModalShell left Planning Mode and Settings untypable, and MailboxModal's
ReplyContextExpandable collapsed expanded reply rows. Tests missed it because
fireEvent.change sets a value without needing the node to stay mounted.

The rule reports PascalCase functions (including memo()/forwardRef()-wrapped)
that return JSX and are declared inside another JSX-returning function.
Lowercase render helpers are deliberately allowed — they are the sanctioned fix.
Escape hatch: // nested-component-allowlist: <reason>.

Scoped to production .tsx, with a vitest guard for the rule itself. Hoists the
two pre-existing violations (ProviderStatusBadge, GitHubStatusBadge in
ModelOnboardingModal) to module scope so the rule lands clean at "error".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 17:34:45 -07:00
gsxdsm
2d263acc49 fix(FN-8600): keep self-healing from pausing live planners and unstick queued planning
Planning moved into the task's own worktree but never published that path to
activeSessionRegistry, so the self-owned-branch reclaim sweep's FN-4819 liveness
guard was blind to a live planner. A zero-commit task branch trivially reads as
tip-already-merged, so the sweep ran `git worktree remove --force` on the tree a
planning session was using, the removal failed, and the failure escalated to
branch-conflict-unrecoverable — parking a healthy card paused with no operator
action.

Planning now claims its worktree through acquireActiveSessionPath (new "planning"
session kind) and releases it only while it still owns the record, so a live
executor that took over the same path mid-teardown is never cleared.

Also fixes planning starvation and its diagnosability:
- admitOldest walks past candidates whose lane declines instead of ending the
  pass on candidates[0], unwinding each declined attempt's pre-held executor slot
  and reservation exactly so a decline cannot leak capacity past maxConcurrent.
- Withheld planning admission emits a deduped task:plan-admission-throttled
  run-audit event (ids/counts only), written fire-and-forget with the dedupe
  marker set only after the write lands. Previously the binding gate lived only
  in a log line that is persisted nowhere, so "why did this card sit queued to
  plan?" was unanswerable after the fact.

Reviewed by 8 review agents; every finding acted on or recorded. A proposed
STALE_SEMAPHORE_EXCESS_REPAIR_MS 600s->180s reduction was reverted under review —
nested runs are already excluded from the reclaim floor, so the window guards
uncounted top-level holders such as a merge body, and shortening it would trade a
bounded visible stall for an unbounded silent cap breach.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 09:26:13 -07:00
gsxdsm
fd073e287f FN-8592: self-heal stranded hold continuations
Restore graph-owned plan-review continuations for eligible hold-column cards stranded after planning cancellation.

- Detect real-spec hold cards with no active workflow continuation and re-seed Plan Review safely.
- Serialize workflow continuation seeding, review-result writes, and lease claims to prevent duplicate recovery.
- Add recovery diagnostics, release warnings, regression coverage, and a patch changeset.

Files changed:
 .changeset/fn-8592-stranded-hold-continuation.md   |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   4 +
 .../workflow-task-serialization-protocol.test.ts   | 119 +++++++++++++
 .../workflow-work-items-conditional-seed.test.ts   | 191 +++++++++++++++++++++
 packages/core/src/store.ts                         |   5 +-
 .../src/task-store/async-workflow-workitems.ts     | 123 +++++++++----
 packages/core/src/task-store/project-store-ops.ts  |  14 ++
 .../src/task-store/workflow-task-create-ops.ts     |  16 +-
 .../src/task-store/workflow-workitems-ops-2.ts     |  91 ++++++----
 .../src/__tests__/pre-release-plan-review.test.ts  |  17 ++
 ...self-healing-stranded-hold-continuation.test.ts | 171 ++++++++++++++++++
 packages/engine/src/hold-release.ts                |  57 +++++-
 packages/engine/src/plan-review-continuation.ts    |  94 ++++++++++
 packages/engine/src/runtimes/in-process-runtime.ts |  30 +---
 packages/engine/src/self-healing.ts                | 100 ++++++++++-
 16 files changed, 945 insertions(+), 95 deletions(-)

Fusion-Task-Id: FN-8592

Fusion-Task-Lineage: fe7ffd34-96e4-4418-a879-7418e6293d30

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-26 00:46:07 -07:00
gsxdsm
b5707318b5 feat(engine): add force to fn_task_promote for agent-native override parity
fn_task_promote can now pass force:true to start execution when a task is still
waiting on planning or plan review, matching the dashboard's promote override.
The rejection message names the flag so a caller that hits the gate can decide,
and a forced release says the pending replan was cancelled rather than burying it.

Force stays opt-in per explicit promote request: the hold-release sweep and the
webhook event release have no force parameter, so FN-7648 still holds for every
automatic surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 20:02:14 -07:00
gsxdsm
41d60f0355 feat(board): explain the unplanned promote rejection and let operators force past it
Promote on a held card printed the raw i18n key `board.rejection.unplannedForExecution`:
FN-8471 added the server-side code without a client case or catalog entry, so
translateRejection fell through to `t(messageKey, messageKey)`.

- Add the explicit rejection case (both translate helpers) plus the en catalog
  entry and secondary-locale stubs.
- promoteHeldTask(..., { force }) waives ONLY the unplanned-for-execution gate;
  hold membership, capacity and slot reservation still arbitrate. It clears a
  needs-replan/plan-review-unavailable status so triage rediscovery cannot pull
  the card back into the waived replan, and emits task:promote-forced-unplanned.
- POST /tasks/:id/promote accepts { force: true }; the board asks for explicit
  confirmation first and only offers the override for this rejection.

Force stays operator-only — the sweep, the webhook release and fn_task_promote
never set it, so FN-7648 still holds for every automatic surface.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:56:54 -07:00
Victor Canô
0c085bf444 fix(engine): pushAfterMerge no longer strands approved merges when the remote diverges (#2407)
## Problem

With `pushAfterMerge` enabled (and `mergeStrategy` other than
`pull-request`), if `origin/<integration-branch>` advances externally
between the local squash-merge and the push, the divergence path opens a
clean-room `git pull --rebase` and an AI agent resolves and stages the
conflicts — but the flow could end there: no `git rebase --continue`, no
push, and no surfaced error.

Because finalize runs *before* the push, the task is already `done`, so
a reviewed, approved merge is silently left container-only, and every
subsequent merge on the project stalls the same way. Separately, an
abort mid-push (`MergeAbortedError`) was swallowed with only a
process-log warning — no task-log entry, no run-audit event.

## Change

- **Deterministic regression coverage** for the conflicting-divergence
path (real-git fixture) proving the rebase runs to completion and the
push lands (refs converge), plus abort/termination scenarios.
- **Recovery-branch safety net:** before the clean-room rebase starts,
the pre-rebase local squash is force-pushed to a per-task remote branch
`fusion/<task-id>-stranded`, so approved content is never container-only
— even across process death or abort. Deleted after a successful target
push; retained on failure/abort as the recovery source.
- **Never-silent outcomes:** every non-pushed outcome (failure or abort)
writes a durable task-log entry and a `push:origin` run-audit event. The
audit contract now documents `push:origin` as polymorphic (dashboard
Smart Push vs. automated post-merge push) and enumerates the automated
path's outcomes, including the new `"aborted"` shutdown case.
- **Cleanup hardening:** `isRebaseInProgress` now probes Git's
worktree-specific `rebase-merge`/`rebase-apply` state directories
(async, timeout-guarded) so a completed rebase can't receive a spurious
second `--continue`; unfinished rebases are cleaned up.

Out of scope by design: withholding the "merge confirmed" state until
the push succeeds — the `FNXC:MergePush` invariant ("a push problem can
never park or roll back a landed merge") is deliberate; the recovery
branch + surfacing satisfy the data-preservation intent without breaking
it.

## Files
`packages/engine/src/merger-ai.ts`, `packages/engine/src/merger.ts`,
`packages/engine/src/run-audit.ts`, new/updated tests under
`packages/engine/src/__tests__/`, `docs/settings-reference.md`,
`docs/dashboard-guide.md`, `AGENTS.md`, and a labeled changeset.

## Validation
`tsc --noEmit` clean; engine divergence + merger suites pass (41 tests);
rebased onto current `main` with no conflicts.

---

_Developed with Claude Code, under human supervision and review._


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Prevented approved post-merge pushes from becoming stranded when the
remote diverges by using a recovery-branch workflow and safer cleanup.
* Improved behavior and reporting when pushes are aborted or fail after
merge, including clearer non-fatal status and audit outcomes.
* **Documentation**
* Expanded push-after-merge and dashboard Smart Push documentation with
recovery-branch and `push:origin`/`push:recovery-branch` outcome
semantics.
* **Tests**
* Added end-to-end regression tests for divergent/conflicting AI
push-after-merge flows, including abort and worktree cleanup
verification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Victor Cano <victortroz@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-23 15:55:03 -07:00
gsxdsm
1dd36ed4c6 fix(FN-8492): mark orphaned pending step results failed instead of deleting them
Code-review follow-up on 4413699de. Deleting an orphaned pending review
entry was a severity inversion: the merge gate blocks on pending/failed
results, not on an enabled step with NO result, so deletion silently
satisfied the gate and the task merged with its review skipped (verified
live: FN-8492 landed on main without Code Review re-running). Orphans are
now rewritten to status:"failed" — the gate stays closed and the
failed-pre-merge-steps recovery / FN-7720 operator-bypass paths own the
re-run decision.

Also from review: the sweep now runs in periodic maintenance too (a step
session can die without a restart), skips executor-owned in-progress rows
(resume is deferred ~30s at startup, so their liveness is unprovable when
startup recovery runs), re-reads the row immediately before the write so
the whole-array update cannot clobber a fresh lease, counts recovery on
the successful mutation rather than after the audit emit, and the new
audit event literal is registered in DatabaseMutationType (cast dropped).
Tests now cover all three liveness-triple legs, >500-row pagination,
in-progress skip, per-task write-failure isolation, and the never-delete
invariant; the needs-replan adoption row moved under a preserve-group
header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:55:22 -07:00
gsxdsm
4413699de0 fix(FN-8492): clear orphaned pending workflow-step results at startup
An engine restart that kills an in-flight pre-merge step session (FN-8492's
Code Review) left its pending workflowStepResult behind with no live session.
The merge gate read it as incomplete pre-merge steps, surfaced an identical
stall every 30 minutes, and the deadlock disposer parked the task failed two
hours later. resolveOrphanedPendingStepResults existed for exactly this but
shipped with no caller (same U9 gap as the adoption table).

Wire it: a startup sweep right after legacy adoption clears pending results
whose task has no live session (activeSessionRegistry / executingTaskLock /
isTaskActive), emitting task:reconcile-orphaned-pending-step-results with
ids/counts-only metadata. User pauses and live resumed sessions are never
disturbed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 16:05:59 -07:00
gsxdsm
ccb7d4e8ff FN-8367: enforce bounded engine shellouts
Enforce bounded synchronous shellout use across the engine.

- Audit every production synchronous shellout against a call-site allowlist.
- Bound data-dependent git diff commands by timeout and output size.
- Document the async shellout invariant and align focused command guards.

Files changed:
 AGENTS.md                                          |   2 +-
 docs/architecture.md                               |   1 +
 .../__tests__/engine-no-blocking-shellout.test.ts  | 135 +++++++++++++++++++++
 .../user-configured-command-no-execsync.test.ts    |   5 +-
 packages/engine/src/merger-git-parse.ts            |  16 ++-
 .../engine/src/merger-workspace-test-commands.ts   |  27 ++++-
 6 files changed, 181 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-8367
Fusion-Task-Lineage: 976384e6-f283-4464-9f74-f328f2be3430
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-19 16:00:28 -07:00
gsxdsm
e0e395a715 FN-8365: enforce dashboard route registrar mount order
Keep dashboard API registration modular while preserving Express route precedence.

- Route all top-level dashboard registrars through a runtime-checked canonical mount sequence
- Add mount-order and inline-route-ratchet coverage with CI enforcement
- Document registrar ownership and mount-order conventions

Files changed:
.github/workflows/pr-checks.yml                    |   3 +
AGENTS.md                                          |   2 +
package.json                                       |   5 +-
packages/dashboard/src/routes.ts                   | 136 +++++-----
packages/dashboard/src/routes/README.md            | 276 ++++++++++-----------
packages/dashboard/src/routes/__tests__/create-api-routes-mount-order.test.ts |  66 +++++
packages/dashboard/src/routes/create-api-routes-mount-sequence.ts |  54 ++++
scripts/__tests__/check-routes-modular.test.mjs    |  28 +++
scripts/check-routes-modular.mjs                   |  65 +++++
scripts/lib/routes-modular-baseline.json           |   3 +
10 files changed, 433 insertions(+), 205 deletions(-)

Fusion-Task-Id: FN-8365

Fusion-Task-Lineage: 9c36a263-ed5e-4524-8ea5-71ed3f3e34d9

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-19 15:53:07 -07:00
gsxdsm
3f7c32c95c refactor(cutover 2/3): engine — graph-owned lifecycle, legacy execution deleted (#2342)
Part **2 of 3** of the IR-driven lifecycle cutover (stacked on #2341;
top is #2335).

**Scope (80 files, packages/engine + cli/pi skill docs +
AGENTS/architecture):** graph-driven column moves via the
column-boundary controller (R1), single-mover scheduler/hold-release
trait cutover (KTD-2/KTD-9), trait re-keyed self-healing + merger with
the R7b confirmed-merge-must-finalize guarantee, graph-exclusive Plan
Review with leased dedup (R4/R5), the executeCore body-lift — zero
legacy re-entry — with fn_review_step + interceptor machinery deleted
and tombstone-ratcheted (R9), builtin workflow runtime fixes (missing
hold handler, unseamed-node column inheritance, no-merge completion
mover), the 6-column benchmark acceptance suite (11 tests) + 12-builtin
lifecycle sweep (94 assertions), and the executor test-harness
modernization. Also retires core's interpreter-cutover scaffolding whose
last consumer (the authoritative driver) dies here.

**Merge order:** #2341 → this → #2335. After #2341 merges, retarget this
to main.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 12:08:10 -07:00
gsxdsm
0dbe67c851 FN-8356: clear stale duplicate decision pauses
Clear inactive duplicate markers so eligible tasks resume planning instead of showing a stranded decision badge.

- Reconcile stale triage-marker duplicate pauses during self-healing and record audit events.
- Clear inactive canonical markers during triage while preserving user and unrelated pauses.
- Cover missing, deleted, completed, and archived canonical states with regression tests.

Files changed:
 .changeset/fn-8356-stale-duplicate-decision.md     |   7 ++
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   1 +
 .../explicit-duplicate-marker-sweep.test.ts        |  43 ++++++--
 .../self-healing-stale-duplicate-decision.test.ts  | 109 +++++++++++++++++++++
 .../triage-explicit-duplicate-marker.test.ts       |  32 ++++--
 packages/engine/src/run-audit.ts                   |   2 +
 packages/engine/src/self-healing.ts                |  87 ++++++++++++++--
 packages/engine/src/triage.ts                      |  41 ++++++--
 9 files changed, 298 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-8356

Fusion-Task-Lineage: 8df8f0ee-d73e-41d6-8abe-a4b33662c9da

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-18 20:06:41 -07:00
gsxdsm
b61311baa8 FN-8305: add durable PostgreSQL symbol locks
Introduce durable project-scoped symbol locks backed by PostgreSQL.

- Add normalized lease-based lock acquisition, renewal, release, and reconciliation APIs with audit events.
- Add PostgreSQL schema migrations and self-healing reconciliation coverage.
- Document the lock model and test migration and lock behavior.

Files changed:
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   1 +
 docs/storage.md                                    |   7 +
 .../src/__tests__/postgres/schema-applier.test.ts  | 115 +++++++++-
 packages/core/src/__tests__/symbol-locks.test.ts   |  91 ++++++++
 packages/core/src/index.ts                         |  17 ++
 .../core/src/postgres/migrations/0000_initial.sql  |  25 +++
 .../src/postgres/migrations/0025_symbol_locks.sql  |  63 ++++++
 packages/core/src/postgres/schema-applier.ts       |  18 +-
 packages/core/src/postgres/schema/project.ts       |  29 +++
 packages/core/src/store.ts                         |  23 ++
 packages/core/src/symbol-lock-types.ts             |  60 +++++
 packages/core/src/task-store/symbol-locks.ts       | 244 +++++++++++++++++++++
 .../__tests__/symbol-lock-reconciliation.test.ts   |  19 ++
 packages/engine/src/self-healing.ts                |  35 +++
 15 files changed, 745 insertions(+), 3 deletions(-)

Fusion-Task-Id: FN-8305

Fusion-Task-Lineage: efd95c73-23e3-4359-8204-dfad374a39bc

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-18 19:31:30 -07:00
gsxdsm
9a37415887 fix(engine): add honest blocked exit to fn_task_done so impossible tasks park failed instead of laundering to done (#2256)
## What & why

FN-8141 ("Update pi SDK to latest and verify Kimi K3 end to end") was
impossible as specced — pi 0.80.x removed `AuthStorage`/`ModelRegistry`
APIs, so every SDK bump broke the build. The executor correctly reverted
its work and filed follow-up FN-8145 — but had **no sanctioned way to
end the task in a blocked state**. `fn_task_done` only expressed
success: the bulk-completion gate refused it, the requeue budget re-ran
the doomed task 5 times, and the only remaining affordance (mark every
step `skipped`, then complete) made `isTaskComplete()` return true.
Self-healing then promoted the "complete" todo to in-review and the AI
merger finalized the empty diff as `done`. **The honest path must be
cheaper than the laundering path.**

This adds a first-class **blocked** outcome to the executor's
`fn_task_done` tool.

## Change

- `fn_task_done` gains `outcome: "completed" | "blocked"` (default
`"completed"`), optional `blockedBy: string[]`, and `reason` (required
when blocked).
- `outcome="blocked"` runs **before** every completion gate (completion
blocker, verdict providers, worktree invariants, bulk-completion
refusal) — blocked is not a completion claim, so none of those gates
apply.
- Parks the task `failed` with `error = "BLOCKED: <reason>"`, following
the FN-7863 `EXECUTION_DISPATCH_LOOP_EXHAUSTED` park convention: **steps
keep their true statuses** (no auto-done, no auto-skip), worktree/branch
preserved. It does **not** call `onDone()`, so the executor's existing
`status === "failed"` post-loop branch honors the park instead of
handing off to review.
- `blockedBy` is recorded as real `task.dependencies` edges (unioned
with existing) so the task requeues behind the blocker.
- Emits run-audit `task:execution-blocked-parked` with ids/outcomes-only
metadata (`taskId`, `blockedBy` ids, `hasReason` boolean — **never** the
reason prose).
- Executor + core prompt guidance and the
`bulk-step-completion-without-review` refusal message now name the
blocked exit as **the** correct action when work cannot proceed,
replacing skip-and-done. `PREMISE STALE:` skip guidance is preserved for
genuinely-stale premises.

## Surface enumeration

- **fn_task_done tool schema + handler**
(`packages/engine/src/executor.ts`): blocked branch added at the top of
`execute`, before all gates.
- **Refusal/requeue machinery**: `formatTaskDoneRefusal` for
`bulk-step-completion-without-review` now points at the blocked exit;
the requeue-budget path is untouched (blocked never enters it).
- **Executor prompt text**: turn-ending rules, the "Cannot proceed"
section, the preflight/stale-premise escape hatch (now explicitly
distinguishes stale-premise skip from blocked).
- **Core prompt mirror** (`packages/core/src/agent-prompts.ts`): same
turn-ending + cannot-proceed guidance.
- **Tool reference doc**
(`packages/cli/skill/fusion/references/engine-tools.md`): `fn_task_done`
params updated. (grep for `fn_task_done` confirmed the only executable
tool schema is in executor.ts; CLI/pi surfaces re-export it, no separate
schema copy.)
- **Self-healing**: verified a blocked-parked row is NOT auto-recovered
by `recoverStrandedCompletedTodoTasks` — its steps are not all
done/skipped and `task.error` is set (both are hard filters in the
sweep).
- **Run Audit inventory** (`AGENTS.md`): documented the new event.

## Test evidence

New `packages/engine/src/__tests__/executor-task-done-blocked.test.ts`
(8 tests) asserts the invariant across surfaces:

```
pnpm --filter @fusion/engine exec vitest run \
  src/__tests__/executor-task-done-blocked.test.ts \
  src/__tests__/executor-task-done-invariant.test.ts \
  src/__tests__/gating-classifications.test.ts \
  src/__tests__/reliability-interactions/execute-requeue-loop-guard.test.ts --reporter=dot
→ Test Files 3 passed | Tests 138 passed (0 failed)
```

Coverage: blocked parks failed with `BLOCKED:` error and does **not**
trip the bulk-completion refusal or requeue to todo; `blockedBy` unioned
into `dependencies`; `task:execution-blocked-parked` emitted with
metadata that excludes the reason prose; steps left untouched; empty
`reason` rejected without parking; `completed` outcome unchanged (still
marks steps done, no blocked audit); and
`recoverStrandedCompletedTodoTasks` never promotes a blocked-parked row.

### Note on `pnpm verify:fast`

`verify:fast` currently fails at the workspace build step due to
**pre-existing** type errors in `packages/engine/src/auth-storage.ts`,
`pi.ts`, and `provider-registration.ts` — the exact FN-8142 pi SDK API
break that FN-8145 will fix. These are present on the base branch and
untouched by this PR. Verified instead that this change introduces
**zero** new type errors (`tsc` diff before/after, engine and core both
clean) and that all scoped tests are green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus <noreply@anthropic.com>
2026-07-16 19:35:37 -07:00
gsxdsm
ca7a5a7106 FN-8144: remove workspace worktrees on archive
Archive workspace task worktrees synchronously and safely across archive entry points.

- Add store-scoped workspace disposal planning, reservations, and quarantine handling.
- Install baseline and executor disposers that remove per-repository worktrees and branches without shell interpolation.
- Cover disposal-plan deduplication and document the archive cleanup behavior.

Files changed:
 .../fn-8144-archive-removes-workspace-worktrees.md |   7 ++
 AGENTS.md                                          |   1 +
 docs/task-management.md                            |   4 +
 .../archive-removes-workspace-worktrees.test.ts    |  59 +++++++++++
 packages/core/src/archive-worktree-disposer.ts     |  52 ++++++++++
 packages/core/src/index.gate.ts                    |   8 ++
 packages/core/src/index.ts                         |   8 ++
 .../core/src/task-store/archive-lifecycle-2.ts     |  29 ++++--
 packages/core/src/task-store/archive-lifecycle.ts  | 114 ++++++++++++++++++++-
 .../src/archive-worktree-disposer-install.ts       |  27 ++++-
 packages/engine/src/executor.ts                    |  25 ++++-
 11 files changed, 319 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-8144

Fusion-Task-Lineage: 1c4b65f3-a1d2-4a5c-a4b6-c263f9e6f61d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 17:05:29 -07:00
gsxdsm
d870878a23 FN-7998: add executor alternate model escalation
Add opt-in executor escalation after same-model tool-failure retries are exhausted.

- Persist escalation settings and one-shot task state across SQLite and PostgreSQL stores.
- Retry once on a configured alternate model or scheduler node and audit escalation outcomes.
- Expose escalation controls, documentation, translations, migration, and regression coverage.

Files changed:
 .changeset/fn-7998-executor-escalation.md          |   7 ++
 AGENTS.md                                          |   1 +
 docs/settings-reference.md                         |  13 ++-
 .../core/src/__tests__/settings-defaults.test.ts   |  23 ++++-
 packages/core/src/in-review-stall.ts               |  29 ++++++
 packages/core/src/index.gate.ts                    |   3 +-
 packages/core/src/index.ts                         |   3 +-
 packages/core/src/manual-retry-reset.ts            |   1 +
 .../0014_executor_escalation_attempt.sql           |   2 +
 packages/core/src/postgres/schema-applier.ts       |  17 ++++
 packages/core/src/postgres/schema/project.ts       |   1 +
 packages/core/src/settings-schema.ts               |   4 +
 packages/core/src/store.ts                         |   2 +-
 packages/core/src/task-store/persistence.ts        |   2 +
 packages/core/src/task-store/remaining-ops-2.ts    |   2 +-
 packages/core/src/task-store/remaining-ops-3.ts    |   2 +-
 packages/core/src/task-store/remaining-ops-6.ts    |   2 +-
 packages/core/src/task-store/serialization.ts      |   1 +
 packages/core/src/task-store/task-update.ts        |   2 +
 packages/core/src/types.ts                         |  13 +++
 .../dashboard/app/components/SettingsModal.tsx     |  12 +++
 .../app/components/settings/section-keys.ts        |   4 +
 .../settings/sections/SchedulingSection.search.ts  |  36 +++++++
 .../settings/sections/SchedulingSection.tsx        |   6 ++
 .../settings-default-descriptions.test.tsx         |   4 +
 .../__tests__/executor-tool-failure-retry.test.ts  |  91 +++++++++++++++++-
 packages/engine/src/executor.ts                    | 104 +++++++++++++++++++--
 packages/i18n/locales/en/app.json                  |   8 ++
 28 files changed, 376 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-7998

Fusion-Task-Lineage: bbce767d-c61a-4667-be62-abc0cc54d8be

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 14:31:59 -07:00
gsxdsm
60b6e3e048 FN-7996: add configurable executor tool-failure retries
Add bounded, durable same-model retry handling for qualifying consecutive executor tool errors.
- Persist retry claims, cursors, and audit markers with PostgreSQL migrations.
- Expose project retry count, backoff, and failure threshold settings in the dashboard.
- Cover retry, exhaustion, reset, and stale-run safety behavior with tests.

Files changed:
 .changeset/fn-7996-executor-tool-failure-retry.md  |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   1 +
 docs/settings-reference.md                         |  10 ++
 .../executor-tool-failure-retry-claim.test.ts      |  17 +++
 .../core/src/__tests__/manual-retry-reset.test.ts  |   3 +
 .../core/src/__tests__/settings-defaults.test.ts   |  15 +-
 packages/core/src/in-review-stall.ts               |  20 +++
 packages/core/src/index.gate.ts                    |   6 +
 packages/core/src/index.ts                         |   6 +
 packages/core/src/manual-retry-reset.ts            |   3 +
 .../0013_executor_tool_failure_retry.sql           |   4 +
 packages/core/src/postgres/schema-applier.ts       |  17 +++
 packages/core/src/postgres/schema/project.ts       |   3 +
 packages/core/src/settings-schema.ts               |   3 +
 packages/core/src/store.ts                         |  10 +-
 packages/core/src/task-store/persistence.ts        |   7 +
 packages/core/src/task-store/remaining-ops-2.ts    |   2 +-
 packages/core/src/task-store/remaining-ops-3.ts    |   2 +-
 packages/core/src/task-store/remaining-ops-6.ts    |  65 ++++++++-
 packages/core/src/task-store/serialization.ts      |   3 +
 packages/core/src/task-store/task-update.ts        |   6 +
 packages/core/src/types.ts                         |  16 +++
 .../dashboard/app/components/SettingsModal.tsx     |  15 ++
 .../app/components/settings/section-keys.ts        |   3 +
 .../settings/sections/SchedulingSection.search.ts  |  27 ++++
 .../settings/sections/SchedulingSection.tsx        |   4 +
 .../settings-default-descriptions.test.tsx         |   3 +
 .../__tests__/executor-tool-failure-retry.test.ts  | 160 +++++++++++++++++++++
 packages/engine/src/executor.ts                    |  87 ++++++++++-
 packages/i18n/locales/en/app.json                  |   6 +
 31 files changed, 523 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7996
Fusion-Task-Lineage: d1682ef8-534c-410e-b74c-1f2cf176eac2
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-16 13:41:14 -07:00
gsxdsm
beae12a4bf FN-8034: enforce component and token reuse
Document standing guidance that keeps UI implementations consistent with existing systems.

- Require reuse of components, primitives, hooks, and helpers before creating alternatives.
- Require dashboard styling to use design tokens and component-scoped CSS.
- Link the styling guide, token source, and documented solution patterns.

Files changed:
 AGENTS.md | 8 ++++++++
 1 file changed, 8 insertions(+)

Fusion-Task-Id: FN-8034

Fusion-Task-Lineage: f0435e22-8e8e-471e-b188-7e1a732c2e41

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 23:01:27 -07:00
gsxdsm
402b3a91fa fix(FN-8004): treat heartbeat soft-delete races as benign instead of stranding agents
A task soft-deleted concurrently with a heartbeat-driven moveTask raised
TaskDeletedError from the engine's own board path, leaving the agent in `error`
with a non-empty lastError and requiring a stop/start cycle to recover.

The race is benign by construction: the task is gone, so the move is a no-op.
The heartbeat now classifies it via isConcurrentSoftDeleteRaceError (matching the
canonical message and serialized/typed forms), keeps the agent active, clears
stale error/recovery state, and emits agent:heartbeat-move-skipped-soft-delete
with ids/counts-only metadata. Concurrent operator pauses are preserved.

Squash-merged by hand from fusion/fn-8004. The engine's AI merge approved this
content twice (squash a3a3cc6a8) but could not land it: main advances every ~8
minutes and each merge cycle took ~10, so every attempt lost to a concurrent
advance and rebuilt. Each cycle also burned a corrective pass on a first-pass
review rejection with no stated reason — the issue #1946 class of bug that this
task's own report cites as a sibling.

Reconciled against #2157, which refactored transient-error-detector.ts: the new
classifier coexists with the extracted transient-error-patterns.ts leaf. Verified
on the merged tree — 123 tests green across FN-8004's suites and #2157's,
engine typecheck clean.

Fusion-Task-Id: FN-8004

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 21:00:45 -07:00
gsxdsm
836e53c6c0 FN-7975: exclude engine-paused wall-clock from task active timing
Reconcile active task segment anchors on full Global/Engine unpause so stopped-engine wall-clock does not inflate execution time, reusing the FN-7011 downtime path with a transition-captured heartbeat.

- Pass optional engineLastActiveAtOverride into reconcileActiveTimingForEngineDowntime so unpause callers freeze the stopped-window proof against racing scheduler heartbeats
- Await downtime reconciliation in resumeAfterUnpauseAndSweepInReview before resuming agentic work or sweeping in-review tasks
- Fold Global/Engine unpause into the unified pause-lifecycle listener (single reconcile when both clear together; no-op while either pause remains)
- Soft-fail reconcile errors so unpause resume still proceeds
- Add store and project-engine coverage for override, await-before-resume, dual-source clear, and fail-soft paths; document FN-7975 in AGENTS.md run-audit notes
- Add patch changeset for the operator-facing timing fix

Files changed:
 .changeset/fn-7975-engine-pause-active-timing.md   |   7 ++
 AGENTS.md                                          |   2 +-
 .../core/src/__tests__/store-active-timing.test.ts |  86 +++++++++++++
 packages/core/src/store.ts                         |  23 ++--
 .../project-engine-unpause-active-timing.test.ts   |  94 ++++++++++++++
 .../engine/src/__tests__/project-engine.test.ts    | 139 +++++++++++++++++++++
 packages/engine/src/project-engine.ts              |  64 +++++-----
 packages/engine/src/self-healing.ts                |   6 +-
 8 files changed, 378 insertions(+), 43 deletions(-)

Fusion-Task-Id: FN-7975

Fusion-Task-Lineage: 84a46e6f-92bf-452a-ab67-c25ba85cbffb

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-15 12:41:05 -07:00
gsxdsm
3748eca073 docs(agents): allow direct main work; use worktrees only for branches
Relax the worktree standing rule: agents may commit on main when the change
belongs there. When work needs a feature branch, create a worktree instead of
switching the primary checkout off main.
2026-07-15 10:17:23 -07:00
gsxdsm
fc37db3701 docs(agents): require worktrees — never switch or commit on main
Standing rule: agents must create an isolated git worktree (prefer Worktrunk)
for all implementation and commits, and must not check out, switch, or mutate
the primary main working tree.
2026-07-15 10:12:22 -07:00
gsxdsm
6dcecb0c34 FN-7926: park completed-but-blocked tasks instead of looping execute-requeue
Stops the execute → pause-abort → re-queue-to-todo infinite loop for tasks whose implementation work is done but a dependency/blockedBy blocker is still live, by diverting them into a dedicated parked state instead of feeding the FN-7863 no-progress backstop or looping forever.

- Add TaskExecutor.parkCompletedBlockedTask(): when work is complete but getTaskCompletionBlocker() still reports a blocker, park the task in todo with pausedReason:"completed-work-blocked", status:"queued", preserved worktree/branch/steps, and a cleared execute-requeue signature.
- Replace shouldFinalizeCompletedTask's boolean with getCompletedTaskFinalizationDecision() returning "finalize" | "blocked" | "incomplete" so both the paused-after-completion and finalization call sites can react to the new "blocked" outcome without re-entering execution.
- Divert completed-but-blocked tasks before the FN-7863 execute-requeue-loop counter increments, so waiting-on-dependency states are no longer misclassified as EXECUTION_DISPATCH_LOOP_EXHAUSTED.
- Add SelfHealingManager.reconcileCompletedBlockedTasks(): a bounded sweep (wired into both startup/maintenance and periodic self-healing passes) that clears the park and advances the task to review once getTaskCompletionBlockerForStore() resolves, guarded by auto-merge eligibility, user-pause, and live-execution checks; failed advances re-park rather than strand the row.
- Add run-audit mutation types task:completed-blocked-parked and task:completed-blocked-advanced (ids/counts/outcomes-only metadata) plus AGENTS.md/docs/architecture.md entries documenting the new lifecycle.
- Extend execute-requeue-loop-guard.test.ts with coverage for the park/advance flow, including the zero-step task edge case.

Files changed:
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   2 +
 .../execute-requeue-loop-guard.test.ts             | 256 ++++++++++++++++++++-
 packages/engine/src/executor.ts                    |  85 ++++++-
 packages/engine/src/run-audit.ts                   |   4 +
 packages/engine/src/self-healing.ts                |  95 ++++++++
 6 files changed, 432 insertions(+), 11 deletions(-)

Fusion-Task-Id: FN-7926
Fusion-Task-Lineage: e47945f4-a816-447e-9ea1-7c13105d0ba9
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-12 23:26:02 -07:00
gsxdsm
a628a13888 feat: stamp store-open provenance into run-audit
Every TaskStore.init() now records a `store:open` run-audit event with
pid/ppid/execPath/entry/cwd/node version. Motivated by the FN-7910 incident:
a stale pre-fix binary opened the shared fusion.db and evacuated Ideas cards,
and the audit trail (agentId:"system", no PID) could not identify the writer.
Any future mystery mutation is now attributable to the process that opened
the store.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 21:57:29 -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
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
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
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
c9d0211bec FN-7844: coordinate heartbeat and self-healing durable-agent error recovery
Unifies the two independent durable-agent error-recovery paths (heartbeat timer and self-healing sweep) so they share one retry budget, eligibility check, and audit surface instead of racing separate counters.

- Share the heartbeatErrorRecovery attempt budget between HeartbeatMonitor's timer-entry recovery and SelfHealingManager.recoverOrphanedAgents(), with self-healing's legacy durableErrorRecovery metadata folded into the same counter via readHeartbeatErrorRetryCount().
- Add isHeartbeatErrorRecoverable() as the single transient/non-operator-actionable eligibility check, used by both the heartbeat timer and self-healing paths (self-healing additionally allows stale-worktree module-resolution errors).
- resetHeartbeatErrorRecoveryMetadata() now strips the legacy durableErrorRecovery field so recovered agents don't retain stale sweep bookkeeping.
- Self-healing emits the shared agent:auto-recover-error-state / agent:error-retry-exhausted run-audit events with source:"self-healing", and parks the agent paused with pauseReason:"error-retry-exhausted" on budget exhaustion, matching the heartbeat-timer behavior.
- Update AGENTS.md, docs/architecture.md, and docs/agents.md to describe the consolidated recovery budget and audit surface.
- Add a patch changeset documenting the fix for @runfusion/fusion.

Files changed:
 .changeset/fn-7844-error-recovery-coordination.md  |  7 ++
 AGENTS.md                                          |  2 +-
 docs/agents.md                                     | 14 ++--
 docs/architecture.md                               |  2 +-
 packages/engine/src/__tests__/heartbeat-error-recovery.test.ts | 13 +++-
 packages/engine/src/__tests__/self-healing.test.ts | 58 ++++++++++++++-
 packages/engine/src/agent-heartbeat.ts             | 35 ++++++---
 packages/engine/src/self-healing.ts                | 85 ++++++++++++++++++----
 8 files changed, 180 insertions(+), 36 deletions(-)

Fusion-Task-Id: FN-7844
Fusion-Task-Lineage: b70dcba5-56b6-412c-8be2-ef827bee9964
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-11 22:31:30 -07:00
gsxdsm
391ff0d269 FN-7835: auto-clear durable agent error state and retry on next heartbeat
Heartbeat-managed durable agents that land in state:"error" now self-recover on the next heartbeat instead of staying stuck until an operator intervenes.

- HeartbeatTriggerScheduler keeps timers armed for durable heartbeat-managed agents in error state when the last error is transient and not operator-actionable (credential/quota/model-access/permanent-config failures stay parked).
- executeHeartbeat clears recoverable errors at run entry (error → active, clears lastError), bounded by MAX_HEARTBEAT_ERROR_RECOVERY_ATTEMPTS (settings-overridable); a successful run resets the counter.
- On budget exhaustion, the agent is parked paused with pauseReason:"error-retry-exhausted".
- Emits new run-audit events agent:auto-recover-error-state and agent:error-retry-exhausted (added to DatabaseMutationType).
- Adds heartbeat-error-recovery.test.ts and extends heartbeat-scheduler.test.ts to cover the recovery/exhaustion paths.
- Adds changeset and documents the new behavior in AGENTS.md and docs/architecture.md.

Files changed:
 .changeset/fn-7835-agent-error-auto-recovery.md    |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   2 +
 .../src/__tests__/heartbeat-error-recovery.test.ts | 323 +++++++++++++++++++++
 .../src/__tests__/heartbeat-scheduler.test.ts      |  89 +++++-
 packages/engine/src/agent-heartbeat.ts             | 209 ++++++++++++-
 packages/engine/src/run-audit.ts                   |   2 +
 7 files changed, 618 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-7835

Fusion-Task-Lineage: 1bbb28a3-8eb9-40e3-8177-6658ec5dae40

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-11 22:16:48 -07:00
gsxdsm
6317fcddb5 FN-7813: add embedded worktree-rooted multi-tab Terminal to Task Detail
Add an interactive, worktree-rooted, multi-tab Terminal tab to the Task Detail
view, distinct from the pre-existing CLI-agent Session tab.

- TaskDetailModal gains a new embedded Terminal tab (single non-workspace
  task with one recorded worktree) that mounts TerminalModal in a new
  `embedded` render mode, rooted at the task's worktree
- Rename the existing agent-session tab label to "Session" to disambiguate
  it from the new Terminal tab
- useTerminalSessions gains task-scoped session storage and a `defaultCwd`
  option so embedded terminal tabs persist separately from footer/global
  project terminal tabs and start in the task worktree
- TerminalModal/CSS updated to support the embedded layout mode
- Update lazy-loaded-views docs test and AGENTS.md exclusion list to cover
  the new `LazyTerminalModal` task-detail-internal surface
- Document the new Session/Terminal tab split in docs/dashboard-guide.md
- Add i18n strings for the new Terminal tab across all locales
- Add a changeset (minor) for @runfusion/fusion

Files changed:
 .changeset/FN-7813-worktree-terminal-tab.md        |   7 +
 AGENTS.md                                          |   2 +-
 docs/dashboard-guide.md                            |   3 +
 .../app/__tests__/lazy-loaded-views-docs.test.ts   |   4 +-
 .../dashboard/app/components/TaskDetailModal.css   |  17 +++
 .../dashboard/app/components/TaskDetailModal.tsx   |  41 +++++-
 .../dashboard/app/components/TerminalModal.css     |  51 +++++++
 .../dashboard/app/components/TerminalModal.tsx     |  71 +++++++---
 .../__tests__/TaskDetailModal.test-helpers.ts      |   3 +
 .../TaskDetailModal.worktree-terminal.test.tsx     | 139 ++++++++++++++++++
 .../components/__tests__/TerminalModal.test.tsx    |  29 ++++
 .../hooks/__tests__/useTerminalSessions.test.ts    | 157 +++++++++++++++++++++
 .../dashboard/app/hooks/useTerminalSessions.ts     |  63 ++++++---
 packages/i18n/locales/en/app.json                  |   3 +-
 packages/i18n/locales/es/app.json                  |   3 +-
 packages/i18n/locales/fr/app.json                  |   3 +-
 packages/i18n/locales/ko/app.json                  |   3 +-
 packages/i18n/locales/zh-CN/app.json               |   3 +-
 packages/i18n/locales/zh-TW/app.json               |   3 +-
 19 files changed, 550 insertions(+), 55 deletions(-)

Fusion-Task-Id: FN-7813

Fusion-Task-Lineage: 4ef86a15-347a-4862-b01c-5063d8004cb8

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-11 00:01:10 -07:00
gsxdsm
21fb8f6786 FN-7802: recover phantom-worktree tasks stuck merge-active with scopeOverride
Fixes phantom-worktree context bleed where the engine refused to start a
coding agent in a missing worktree for in-review/merge-active tasks even
when scopeOverride=1, stranding them past the normal recovery paths and
retry budget.

- Add isMergeActiveMissingWorktreeSessionStartFailure/isInReviewMissingWorktreeSessionStartFailure classifiers and MERGE_ACTIVE_MISSING_WORKTREE_STATUSES (merging/merging-pr/merging-fix) in restart-recovery-coordinator.ts, exported from @fusion/engine.
- Self-healing: reorder missing-worktree-review-failures sweep earlier, extend the in-review sweep to also match merge-active missing-worktree failures with a triple-proof-guarded, bounded (recoveryRetryCount) stale-metadata clear and fresh session-start retry budget reset.
- Self-healing: extend scopeOverride worktree-metadata reconciliation to safely clear phantom worktree/branch/session metadata for in-review tasks stuck in a merge-active sub-status, narrowly scoped to avoid clobbering genuinely live in-progress/mid-step tasks (FN-5256 guard preserved).
- CLI (task.ts), pi extension (extension.ts), and dashboard route (register-task-workflow-routes.ts) retry paths now bypass the merge-active status gate via a signature-only check, clearing worktree/branch/sessionFile and requeuing to todo while preserving progress.
- Add regression coverage across self-healing.test.ts, restart-recovery-coordinator.test.ts, extension.test.ts, task-retry.test.ts, and routes-tasks-ops.test.ts; update mockCoreEngine.ts test scaffolding.
- Update docs/architecture.md, docs/self-healing-backward-move-audit.md, docs/task-management.md, and AGENTS.md to describe the new merge-active missing-worktree recovery behavior.
- Add changeset (patch) for @runfusion/fusion.

Files changed:
 .changeset/fn-7802-phantom-worktree-merge-active-recovery.md      |   7 +
 AGENTS.md                                                          |   1 +
 docs/architecture.md                                               |   4 +-
 docs/self-healing-backward-move-audit.md                           |   5 +-
 docs/task-management.md                                            |   2 +-
 packages/cli/src/__tests__/extension.test.ts                       |  64 +++++
 packages/cli/src/__tests__/task-retry.test.ts                      |  49 ++++
 packages/cli/src/commands/task.ts                                  |  28 +-
 packages/cli/src/extension.ts                                      |  26 +-
 packages/dashboard/src/__tests__/routes-tasks-ops.test.ts          |  52 ++++
 packages/dashboard/src/routes/register-task-workflow-routes.ts     |  25 +-
 packages/dashboard/src/test/mockCoreEngine.ts                      |  11 +
 packages/engine/src/__tests__/restart-recovery-coordinator.test.ts |  20 ++
 packages/engine/src/__tests__/self-healing.test.ts                 | 297 +++++++++++++++++++++
 packages/engine/src/index.ts                                       |  13 +
 packages/engine/src/restart-recovery-coordinator.ts                |  19 +-
 packages/engine/src/self-healing.ts                                | 157 +++++++++--
 17 files changed, 744 insertions(+), 36 deletions(-)

Fusion-Task-Id: FN-7802

Fusion-Task-Lineage: 5897105b-6b5c-49d5-a8e8-519902182861

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 22:25:21 -07:00
gsxdsm
d80cdd2b3b FN-7787: honor assigned agent's runtimeConfig model in task execution sessions
Task execution sessions previously ignored the assigned permanent agent's
runtimeConfig model whenever the executor was handed an agents-less
worktree AgentStore, silently drifting to the pi runtime's built-in
default model instead of the configured one.

- Add TaskExecutor.getAuthoritativeAssignedAgent(): falls back to the
  authoritative project `.fusion` AgentStore when the live executor's
  worktree AgentStore has no record of the assigned agent, so
  runtimeConfig resolution matches chat-session behavior.
- Replace direct `this.options.agentStore.getAgent(...)` lookups across
  step-session, workflow-graph, and legacy execution paths with the new
  authoritative lookup helper.
- Warn and audit (`noModelResolved` / `runtimeBuiltInFallbackModel`) when
  a non-mock, non-test-mode session resolves no provider/model pair and
  falls back to the runtime's built-in default, so the drift is visible
  instead of silent.
- Add regression tests covering assigned-agent runtime-config resolution
  and the new runtime-resolved audit fields.
- Add changeset (patch) and update docs/settings-reference.md and
  AGENTS.md.

Files changed:
 .changeset/fuzzy-fable-fallback.md                 |  7 +++
 AGENTS.md                                          |  1 +
 docs/settings-reference.md                         |  2 +-
 .../executor-assigned-agent-runtime-config.test.ts | 68 ++++++++++++++++++++++
 .../run-audit-session-runtime-resolved.test.ts     | 44 ++++++++++++++
 packages/engine/src/agent-session-helpers.ts       | 31 +++++++---
 packages/engine/src/executor.ts                    | 43 +++++++++-----
 7 files changed, 174 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-7787

Fusion-Task-Lineage: 40fccad5-2e67-4ee2-8199-4548ce9025c6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-10 10:13:38 -07:00
gsxdsm
e4a59f7269 fix: remove over-firing triage release-authorization gate
The triage release-authorization gate (FN-6481/FN-6469) false-flagged any
spec that merely mentioned release tooling (scripts/release.mjs, pnpm release)
and, because non-user sources made the in-band authorization marker inert,
stranded ordinary tasks in awaiting-approval with no exit.

- Delete triage-release-authorization.ts + its test and the finalizeApprovedTask
  parking block; release-class specs now flow through triage normally.
- Remove the dashboard approve/reject-plan API guards and UI gating so tasks
  still carrying the legacy awaitingApprovalReason="release-authorization" hold
  render as ordinary manual plan-approval holds and can be resolved.
- Keep the awaitingApprovalReason field + activity label for backward-compat.
- Replace the engine gate with agent instruction (AGENTS.md -> Releasing):
  agents must never run a release from inside a Fusion task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 19:58:06 -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
5ad8ec8cb6 FN-7528: capture post-task agent performance reflections
Capture deterministic post-task reflection metrics for completed agent tasks.

- Add non-LLM task performance capture with duration, touched files/packages, verification scope, and retry/rework metrics.
- Wire executor completion paths to fire best-effort reflection capture once per completed task when reflections are enabled.
- Extend reflection/run-audit types, docs, changeset, and regression coverage for capture behavior.

Files changed:
 .changeset/fn-7528-task-performance-capture.md     |   7 +
 AGENTS.md                                          |   1 +
 docs/diagnostics.md                                |  12 +-
 .../core/src/__tests__/reflection-store.test.ts    |  96 +++++++++
 packages/core/src/types.ts                         |  28 ++-
 .../engine/src/__tests__/agent-reflection.test.ts  | 202 +++++++++++++++++++
 .../executor-post-task-reflection-capture.test.ts  | 135 +++++++++++++
 packages/engine/src/agent-reflection.ts            | 215 ++++++++++++++++++++-
 packages/engine/src/executor.ts                    |  63 +++++-
 packages/engine/src/run-audit.ts                   |  29 +++
 10 files changed, 776 insertions(+), 12 deletions(-)

Fusion-Task-Id: FN-7528

Fusion-Task-Lineage: 153090e1-681b-4445-83e8-097bc70dcdb4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-04 21:28:15 -07:00
gsxdsm
79ab367547 FN-7514: withhold overseer actions under human control
Add a human-control guard so planner overseer recovery stays inert for paused or human-review tasks.

- Add a pure overseer human-control policy that treats explicit user pauses and autoMerge:false / human-review tasks as full withhold states.
- Thread settings through planner recovery ticks, skip action classification and pending confirmations when withheld, and emit deduped no-action run-audit events.
- Wire ProjectEngine audit recording and document the new guard, run-audit event, exports, and release note.

Files changed:
 .changeset/fn-7514-overseer-human-control-guard.md |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |  39 +++++
 .../overseer-human-control-policy.test.ts          |  86 +++++++++++
 ...anner-recovery-controller-human-control.test.ts | 170 +++++++++++++++++++++
 packages/engine/src/index.ts                       |   7 +
 .../engine/src/overseer-human-control-policy.ts    |  88 +++++++++++
 packages/engine/src/planner-recovery-controller.ts | 107 ++++++++++++-
 packages/engine/src/project-engine.ts              |  44 +++++-
 packages/engine/src/run-audit.ts                   |  15 +-
 10 files changed, 558 insertions(+), 6 deletions(-)

Fusion-Task-Id: FN-7514

Fusion-Task-Lineage: d4d3bd04-3f8e-4a05-9636-f177e050390b

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-04 14:11:25 -07:00
gsxdsm
605e4d7734 FN-7158: emit reflection run-audit telemetry
Record agent reflection outcomes in run audit without persisting reflection prose.\n\n- Emit generated, skipped, and failed reflection telemetry from AgentReflectionService.\n- Add run-audit metadata contracts, diagnostics docs, and operator-facing run-audit guidance.\n- Cover telemetry payloads and best-effort audit failures in agent reflection tests.\n- Add a minor changeset for the published Fusion CLI package.\n\nFiles changed:\n .changeset/fn-7158-reflection-telemetry.md         |  7 ++\n AGENTS.md                                          |  1 +\n docs/diagnostics.md                                | 12 +++\n .../engine/src/__tests__/agent-reflection.test.ts  | 99 ++++++++++++++++++++++\n packages/engine/src/agent-reflection.ts            | 60 ++++++++++++-\n packages/engine/src/run-audit.ts                   | 41 +++++++++\n 6 files changed, 219 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7158

Fusion-Task-Lineage: e8a83fe9-2112-4ce4-9367-a6fa38b6fbb4

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-27 20:33:22 -07:00
gsxdsm
dbe1e2e39f FN-7093: tighten lazy-view inventory guard
Keep the lazy-loaded views documentation guard aligned with every curated chunk source.

- Expand AGENTS guidance to name the plugin and agent detail lazy-import sources.
- Teach the dashboard docs test to scan feature-owned lazy declarations in PluginsSection and AgentsView.
- Assert explicit exclusions for embedded views, terminal/onboarding internals, and duplicate overflow imports.

Files changed:
 AGENTS.md                                          |   4 +-
 .../app/__tests__/lazy-loaded-views-docs.test.ts   | 113 +++++++++++++++++++--
 2 files changed, 108 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-7093

Fusion-Task-Lineage: 25bbe920-529d-4f2b-95d4-4c17b682dc49

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-27 09:28:29 -07:00
gsxdsm
0440ae4bb9 FN-7074: make task ID reservation commits atomic
Task creation now commits or rolls back distributed task ID reservations with the task-row transaction.

- Add transaction-participating reservation commit and rollback helpers.
- Wire create, duplicate, and refinement task paths to commit reservations inside task insertion.
- Record rollback audit events and preserve burned reservation rows after failed creates.
- Cover atomicity, rollback, and allocator behavior with reservation-focused tests and docs.

Files changed:
 .changeset/fn-7074-reservation-atomicity.md        |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   7 +-
 docs/storage.md                                    |   4 +-
 .../core/src/__tests__/distributed-task-id.test.ts |  25 ++-
 .../__tests__/store-reservation-atomicity.test.ts  | 224 +++++++++++++++++++++
 packages/core/src/distributed-task-id.ts           | 208 +++++++++++++------
 packages/core/src/store.ts                         |  93 +++++++--
 8 files changed, 479 insertions(+), 90 deletions(-)

Fusion-Task-Id: FN-7074

Fusion-Task-Lineage: 464a98cf-e903-4257-93ac-424c7129412b
2026-06-26 14:55:01 -07:00
gsxdsm
74d37785d2 FN-7069: reconcile phantom task reservations
Harden task-store startup and maintenance against phantom committed task reservations.

- Reconcile committed reservation phantoms without freeing reserved task IDs.
- Prune orphaned child rows and emit durable run-audit evidence.
- Normalize missing legacy task.json reads to clean not-found errors.
- Cover archive/search and phantom-reservation reconciliation behavior with tests.

Files changed:
 .changeset/fn-7069-phantom-task-reconcile.md       |   7 +
 AGENTS.md                                          |   1 +
 docs/architecture.md                               |   2 +
 .../src/__tests__/store-archive-search.test.ts     |   5 +
 .../store-phantom-reservation-reconcile.test.ts    | 148 +++++++++++++++++++++
 packages/core/src/store.ts                         | 106 ++++++++++++++-
 packages/engine/src/self-healing.ts                |  14 ++
 7 files changed, 282 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7069

Fusion-Task-Lineage: 772aad37-db30-4310-a968-c6e5306c73bb
2026-06-26 13:39:33 -07:00
gsxdsm
29aee2196a FN-7051: restore verify:fast prerequisite builds
Restore verify:fast so fresh worktrees prepare required artifacts before boot smoke.\n\n- Bootstrap missing or stale workspace dist artifacts before scoped verification.\n- Always build @runfusion/fusion for source-checkout boot smoke, even when no package diff maps to the CLI.\n- Update verify:fast tests and docs to capture the prerequisite build flow.\n\nFiles changed:\n AGENTS.md                              |  4 +-\n docs/testing.md                        |  5 +-\n scripts/__tests__/verify-fast.test.mjs | 29 +++++++++--\n scripts/verify-fast.mjs                | 92 ++++++++++++++++++++++++++--------\n 4 files changed, 101 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-7051

Fusion-Task-Lineage: b8980f15-0103-4f40-9f8d-83907461988a
2026-06-26 01:10:00 -07:00
gsxdsm
98a5052b65 FN-6706: add signal connector incident ingestion
Connect external signal ingestion routes to Command Center incident analytics.\n\n- Add connector status reporting and incident upsert/resolution support for webhook, Sentry, Datadog, and PagerDuty sources.\n- Move signal aggregation through shared activity analytics and expose connector-aware empty states in Command Center.\n- Document connector setup, settings, and dashboard behavior with a published changeset.\n- Extend route, analytics, and Command Center UI tests for scoped signal data and configured-but-quiet connectors.\n\nFiles changed:\n .changeset/fn-6706-signal-connectors.md            |   7 +\n AGENTS.md                                          |   1 +\n docs/dashboard-guide.md                            |   4 +-\n docs/settings-reference.md                         |  16 +\n docs/signals-connectors.md                         | 140 ++++++++\n .../core/src/__tests__/signals-analytics.test.ts   |  37 ++-\n packages/core/src/activity-analytics.ts            | 185 ++++++++++-\n packages/core/src/index.ts                         |  11 +-\n packages/core/src/signals-analytics.ts             | 209 +-----------\n .../command-center/areas/SignalsArea.tsx           |  47 ++-\n .../areas/__tests__/areas.github-signals.test.tsx  | 120 +++++--\n .../command-center/areas/__tests__/areas.test.tsx  |   3 +-\n .../register-command-center-routes.test.ts         |  72 +++-\n .../src/__tests__/register-signal-routes.test.ts   | 369 ++++++++++++++++++++-\n .../src/routes/register-command-center-routes.ts   |  29 +-\n .../dashboard/src/routes/register-signal-routes.ts |  55 +++\n packages/dashboard/src/signal-source.ts            |   8 +\n packages/dashboard/src/signal-sources/datadog.ts   |   8 +\n packages/dashboard/src/signal-sources/pagerduty.ts |  14 +-\n packages/dashboard/src/signal-sources/sentry.ts    |  19 +-\n packages/dashboard/src/signal-sources/webhook.ts   |   8 +\n 21 files changed, 1094 insertions(+), 268 deletions(-)

Fusion-Task-Id: FN-6706

Fusion-Task-Lineage: 09003e9b-481b-4393-926b-44907d13bcf9
2026-06-26 00:28:31 -07:00
gsxdsm
4dab2b6986 FN-7011: Exclude engine downtime from task timing
Exclude proven engine-process downtime from active task duration stats and badges.

- Persist a throttled engineLastActiveAt heartbeat while the scheduler is running and unpaused.
- Reconcile in-progress execution segment anchors on startup when downtime exceeds the poll threshold.
- Emit run-audit events for shifted and no-action downtime reconciliation outcomes.
- Cover core timing, dashboard badge behavior, scheduler heartbeat throttling, and self-healing audit metadata.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7011-engine-downtime-timing.md       |  7 +++
 AGENTS.md                                          |  1 +
 .../src/__tests__/store-execution-timing.test.ts   | 51 ++++++++++++++++++++++
 packages/core/src/settings-schema.ts               |  1 +
 packages/core/src/store.ts                         | 28 ++++++++++++
 packages/core/src/types.ts                         |  5 +++
 .../app/utils/__tests__/taskTiming.test.ts         | 16 +++++++
 .../__tests__/scheduler-workflow-cutover.test.ts   | 29 +++++++++++-
 packages/engine/src/__tests__/self-healing.test.ts | 23 ++++++++++
 packages/engine/src/run-audit.ts                   |  4 ++
 packages/engine/src/scheduler.ts                   | 11 +++++
 packages/engine/src/self-healing.ts                | 21 +++++++++
 12 files changed, 196 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7011

Fusion-Task-Lineage: 44225b0a-0fa1-40be-812d-33fd5e446944
2026-06-25 23:35:31 -07:00
gsxdsm
65a2b3bc72 feat: add test-free verify:fast (typecheck + build + boot-smoke) (#1777)
## What this adds

A **test-free verification command** — `pnpm verify:fast`
(`scripts/verify-fast.mjs`) — that gives deterministic, flake-free
signal without running the test suite. It is fully **additive**: `pnpm
test`, the merge gate (`test:gate`), and CI are untouched.

`docs/testing.md` observes the broad test gate "caught no recalled real
bugs while consuming ~70% of shipping time in flake triage."
`verify:fast` is the opt-in path for non-test verification, suitable as
a project `testCommand`/verification command.

## What verify:fast runs

1. **typecheck — scoped to the changed packages** (each package's
`typecheck` script, or `pnpm --filter <pkg> exec tsc --noEmit -p .` when
none exists).
2. **build — scoped to the changed packages** (`pnpm --filter <pkg>
build`, only for packages that declare a build script).
3. **boot smoke once** (`scripts/boot-smoke.mjs`: CLI `--help` + a real
`fn serve` answering `GET /api/health`), after builds so it runs against
fresh artifacts.

Change-detection **reuses `scripts/test-changed.mjs`** (`getBaseBranch`
/ `detectComparisonBase` / `changedFilesSince` /
`resolveAffectedPackages` / workspace resolution — newly `export`ed)
instead of reinventing git-diff, so it scopes to exactly the packages a
changed-only test run would. With no affected package (root/docs-only
diff) it runs the boot smoke only. Each step is bounded by the existing
`runWithWatchdog` (class `changed`) so a hung tsc/build/serve fails
fast; it streams progress and exits nonzero on the first failing step.
`@fusion/desktop` and `@fusion/mobile` are skipped, mirroring the root
`build`/`typecheck` exclusions.

## Measured wall-time

On this branch's diff (which resolves to the heaviest package,
`@fusion/dashboard`), end-to-end:

```
[verify:fast] plan: typecheck:@fusion/dashboard -> build:@fusion/dashboard -> boot-smoke
[verify:fast]    OK typecheck @fusion/dashboard (~44s)
[verify:fast]    OK build @fusion/dashboard (26.2s)
[verify:fast]    OK boot smoke (CLI --help + real serve /api/health) (19.6s)
[verify:fast] PASS — 3 step(s) green in 90.3s (no tests run).
```

**~90s total**, deterministic and flake-free. By contrast a typical
**scoped test run for the same package** is far heavier and flake-prone:
`docs/testing.md` notes a dashboard task "otherwise re-ran all 822
dashboard test files (~5-8 min)", and `pnpm test` additionally runs the
merge-gate suite first. verify:fast trades that test-suite cost (and its
flake-triage tax) for a typecheck+build+boot signal in ~1.5 min.

## Doc additions

- `AGENTS.md` + `docs/testing.md` testing-commands lists now include
`pnpm verify:fast`, described as the recommended **test-free
verification** (typecheck + build + boot-smoke), suitable as a project
`testCommand`/verification command; the full suite stays available and
runs non-blocking.

## Tests / verification

- New `scripts/__tests__/verify-fast.test.mjs` (11 tests) pins the pure
planning / arg-construction logic — scoped typecheck/build selection,
build-script gating, desktop/mobile exclusion, boot-smoke-only fallback,
and reuse of `resolveAffectedPackages`. It never spawns real
tsc/build/vitest.
- `pnpm verify:fast` runs end-to-end and exits 0 (output above).
- Lint clean on all new/changed files; `agents-md-invariants`,
`check-test-inventory`, `verify-fast`, and `test-changed` script tests
all green (132 tests).

No changeset (scripts + docs + CI-tooling, behavior-additive;
`@runfusion/fusion` runtime unaffected).

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1777">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->
2026-06-25 17:24:25 -07:00
gsxdsm
f342761f1b feat: add test-free verify:fast (typecheck + build + boot-smoke)
Adds scripts/verify-fast.mjs + root `pnpm verify:fast`, an opt-in, flake-free
verification path that runs typecheck + build scoped to the changed packages
(reusing test-changed.mjs git-diff / changed-package resolution) plus the
existing boot smoke once, with no test suite. Each step is bounded by the
shared runWithWatchdog (class "changed"); exits nonzero on the first failure.
No default changed: pnpm test, the merge gate, and CI are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:06:08 -07:00
gsxdsm
a9193df997 docs(verification): instruct agents not to use allowFullSuite unless necessary
Strengthen the fn_run_verification allowFullSuite parameter description, add an
AGENTS.md standing rule, and update docs/testing.md so agents default to a
file-scoped verification command and reserve allowFullSuite for genuinely full
runs with no targetable test set. allowFullSuite is the main way verification
balloons past its budget; the thin merge gate is the cross-cutting safety net.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:22:40 -07:00
gsxdsm
3630da0658 feat: Better changelog — structured changesets + distilled release notes (#1750)
## Summary

Replaces dense, agent-authored technical changeset paragraphs with a
**structured, concise changeset schema** (end-user summary + category +
optional dev detail), enforced by a linter. Adds a **deterministic
distillation step** at version time that transforms a release's
collected changesets into clean, grouped, end-user-facing release notes.
Unifies both release paths (local `release.mjs` and CI `version.yml` /
`release.yml`) behind a single distilled artifact so the root
`CHANGELOG.md` and GitHub Release both carry the same user-facing notes.

## Changes

### Changeset format (`scripts/lib/changeset-schema.mjs`)
- Each changeset body now uses labeled fields: `summary` (required,
user-facing, max 120 chars), `category` (required:
feature/fix/breaking/security/performance/internal), `dev` (optional
developer detail)
- Legacy freeform changesets are detected and flagged for the transition
period

### Linter (`scripts/check-changeset-format.mjs`)
- Validates structured schema, summary length, category enum, and
frontmatter package scope
- Wired into `test:gate`, `pretest`, `pretest:full`, and `pr-checks.yml`
- Legacy changesets warn (exit 0) during transition; `--strict` flag
fails on them

### Distillation (`scripts/lib/distill-release-notes.mjs`)
- `distillDeterministic` builds grouped, end-user-facing release notes
by category (New, Fixed, Breaking, Security, Performance, Internal)
- `buildDistillationPrompt` and `DISTILLATION_SYSTEM_PROMPT` ready for
AI distillation via `createFnAgent` when model credentials are available
- Graceful fallback: deterministic bullet list when no model is
configured

### Release integration
- **Local path** (`scripts/release.mjs`): captures changeset entries
before `changeset version` deletes them, distills notes post-version,
replaces the version's CHANGELOG section with curated notes
- **CI path** (`scripts/ci-distill-release-notes.mjs`,
`scripts/run-ci-distill.mjs`): chained into `release:version` so both
flows get distilled notes
- **GitHub Release** (`release.yml`): uses curated CHANGELOG notes
instead of `generate_release_notes: true`

### Documentation
- `AGENTS.md`, `RELEASING.md`, `docs/contributing.md` updated with the
structured format guide
- `.changeset/README.md` template for `pnpm changeset` consumers

## Testing

- 54 new unit tests across changeset-schema, check-changeset-format,
distill-release-notes, and extract-version-notes
- `pnpm lint` clean
- `pnpm test:gate` green (371 tests)
- `pnpm check:changesets` passes (13 legacy warnings expected during
transition)

## Plan

Full implementation plan:
`docs/plans/2026-06-24-001-feat-better-changelog-plan.md`

<!-- stage-review-badge-begin -->

---

<a href="https://stagereview.app/Runfusion/Fusion/pull/1750">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://stagereview.app/assets/gh-open-in-stage-dark.svg">
<img src="https://stagereview.app/assets/gh-open-in-stage-light.svg"
alt="Open in Stage">
  </picture>
</a>

<!-- stage-review-badge-end -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added structured release-note guidance and validation for changeset
entries.
* Release notes now use curated, grouped changelog content instead of
auto-generated text.
* Added a fallback process to keep release notes consistent when
automated distillation isn’t available.

* **Bug Fixes**
  * Improved handling of legacy changesets and malformed entries.
* Ensured version-specific changelog sections are updated without
affecting older releases.

* **Tests**
* Added coverage for changeset validation, note grouping, and changelog
section replacement.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 19:31:18 -07:00
gsxdsm
2019e5a30f feat(changelog): U6 — docs, agent guidance, and structured changeset
Update AGENTS.md, RELEASING.md, contributing.md with the structured
changeset format guide. Add .changeset/README.md template. Add changeset
for this change. Update distill-release-notes.mjs with final version.
2026-06-24 19:25:51 -07:00