Retry incomplete executor work with a fresh session after non-continuable session errors.
- add executor recovery handling that clears session state and requeues incomplete tasks to todo while retry budget remains
- preserve terminal failure behavior once the fresh-session retry budget is exhausted
- extend reliability coverage and AGENTS.md backstop notes for the new retry path
Files changed:
AGENTS.md | 1 +
.../post-done-continuation-no-wedge.test.ts | 48 +++++++++++++++++++++-
packages/engine/src/executor.ts | 40 ++++++++++++++++++
3 files changed, 87 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-5888
Fusion-Task-Lineage: 4ebce260-8923-4599-aced-541533b94543
Implements graceful suppression of transient `task.json` ENOENT errors in the executor, logging a suppression signal and surfacing a banner in the UI, with test coverage for both the executor behavior and notification service. Documentation in `docs/architecture.md` and a changeset for `@runfusion/f
Fusion-Task-Id: FN-5624
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5624
Cold-start dashboard responsiveness went from ~99s to ~6-11s. CPU profiling
identified two synchronous-spawn hotspots blocking the event loop:
- `GitHubTrackingReconciler` scanned up to 200 done tasks per startup,
each call into `getIssue` invoking `isGhAvailable()` + `isGhAuthenticated()`
via `execFileSync`. `gh auth status` makes a network roundtrip, so 400
sync spawns ≈ 71s of pure event-loop blocking (69% of cold-start CPU).
Memoized both checks with a 60s TTL; `resetGhAvailabilityCache()` is
exported for login/logout flows that need immediate invalidation.
- `PRAGMA integrity_check(100)` walks every page of the SQLite file (~7s
per database, multiple DBs × projects). The deferred check was scheduled
3s after init — right in the responsiveness-critical window. Pushed to
60s so the user is already interacting before it runs; check itself is
unchanged.
Also yields the event loop between major InProcessRuntime init phases and
between self-healing recovery steps (34 per project), defers orphan-task
AI agent resumption by 30s (env-overridable, auto-zero under Vitest), and
ships an opt-in `FUSION_TRACE_EL_LAG=/path/to/file` event-loop lag tracer
that diagnosed all of the above.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a static pretest check and a runtime vitest-setup wrapper that block
shell/process calls matching `kill|pkill|killall|fuser|lsof ... <port>` or
`.listen(<port>)` against reserved Fusion ports. Reserved set is dynamic:
default 4040 plus $PORT, $FUSION_SERVER_PORT, $FUSION_RESERVED_PORTS, and any
port responding to /api/health on 4040..4045 at worker startup.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`notice` is `events.find(...)` which returns `undefined` (not `null`)
when no match. `waitFor(() => expect(...).not.toBeNull())` exited
immediately because `undefined !== null` — the test never actually
waited for the api mock to resolve. Sometimes the followup assertions
happened to land after the events fetched (test passed by luck);
sometimes they ran while notice was still undefined and the assertions
failed.
Switched all five waitFor sites to `.toBeDefined()` so they actually
block on the events-fetch resolution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "session runtime resolved" audit event that flows through the engine's main execution lanes — triage, executor, reviewer, merger, heartbeat, step-session-executor, and mission-execution-loop — with runtime mutation support and test coverage, plus a compile-fix for the merger auditor wiring.
Fusion-Task-Id: FN-5544
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5544
Follow-up to bf4428c00 (FF-only ref advance). After the prevention fix
new orphans can't form, but pre-fix orphans like f6358ce4 on
fusion/fn-5419 still need a path back onto the integration branch.
Adds an `orphan-our-advance` classification to contamination recovery:
a "unique" foreign commit whose Fusion-Task-Id trailer points at a
`done` task AND that is unreachable from refs/heads/<integrationBranch>
is treated as a stranded merger output.
For these, the executor attempts a fast-forward rehome onto the
integration branch via advanceIntegrationBranchRef (which still enforces
the FF-only invariant). When successful, the orphan sha is added to the
existing `shasToDrop` set so the same recovery pass that drops
already-upstream/misrouted commits also drops the now-upstream orphan.
Non-FF orphans (diverged from current integration tip) are refused.
Doing a cherry-pick onto the integration branch from inside automated
recovery would introduce conflict-resolution surface that's too high
blast radius for a never-event recovery path. The refusal log line
includes the exact `git cherry-pick <sha>` command an operator can run
manually.
Two new GitMutationType audit events:
- merger:orphan-rehome-ff (successful FF rehome)
- merger:orphan-rehome-refused (non-FF, manual cherry-pick required)
Tests in merger-orphan-rehome.test.ts cover classification (orphan,
not-done, already-reachable, no-trailer) and the rehome operation
(FF success advances the ref + emits the audit event; non-FF refusal
emits the hint and leaves the ref untouched).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the "workflow-step" MockSessionPurpose enum value and the
workflowStepId / workflowStepTemplateId plumbing through
agent-runtime, agent-session-helpers, mock-provider, executor, and
merger. The seeded-workflow-prompts script loses its FN-5205
rationale comment + test (no longer applicable now that workflow
steps run through the regular session purposes).
Also strips the stale FN-5482 architecture-invariant bullet from
AGENTS.md and the corresponding audit-event line from
docs/architecture.md (the self-healing reclaim invariant they
described no longer holds).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address the code-review finding on the prior commit: a natural premise-stale
summary like "PREMISE STALE: the task has no remaining work — implementation
is already done on HEAD" matches /\b(incomplete|not implemented|not done|
not finished)\b/i with 'the task' inside the 40-char first-person window,
refusing fn_task_done with summary-claims-incomplete and deadlocking the
escape hatch.
When summary starts (case-insensitive) with PREMISE STALE:, skip the
dissent-pattern and scoped-incomplete summary checks. Pending-code-review
and bulk-step-completion guards still apply unchanged.
Add executor-task-done-premise-stale.test.ts covering: the deadlock case
now passes; dissent phrasing in a sentinel summary is allowed; case-
insensitive sentinel; sentinel must be at the start (mid-summary doesn't
bypass); REVISE verdict still blocks even with the sentinel.
Root-cause prevention for the FN-5521 failure class where PROMPT.md describes
work that is already done on HEAD and the executor still marches through
plan/review/test/doc, then merger burns hours retrying against unrelated
flakes in @fusion/engine reliability-interactions.
- Executor system prompt: add a Preflight escape hatch. When Step 0
reproduces and finds HEAD already matches the desired state, the agent
marks Step 0 done, marks remaining steps skipped, and calls fn_task_done
with a `PREMISE STALE:` summary. Reuses the existing
evaluateTaskDoneRefusal/skipped semantics and the merger's
empty-own-diff fast-path — no new tools or refusal classes needed.
- packages/engine/vitest.config.ts: split into two projects. engine-default
retains full parallelism; engine-reliability scopes
src/__tests__/reliability-interactions/** to
poolOptions.threads.singleThread so event-ordering assertions stop
flaking under workspace-concurrent merge-gate load (the
`expected 24 to be less than 19` rowid interleaving in
merge-reuse-task-worktree).
Switch commits Fusion produces (both executor step commits and merger squash
commits) from setting `--author="Fusion <noreply@runfusion.ai>"` to appending
`-m "Co-authored-by: Fusion <noreply@runfusion.ai>"`. The user's configured
git identity now stays as the primary author/committer, and Fusion is recorded
as a co-author (recognized by GitHub for shared attribution). The
`commitAuthorEnabled` toggle and `commitAuthorName`/`commitAuthorEmail`
settings keep their existing keys; the dashboard settings UI relabels them
from "Author" to "Co-author" to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contamination on fusion/<id> branches (FN-5233 was the recent example:
two untrailered feat(FN-5353): commits sitting on fusion/fn-5233) used
to be invisible until merge time, days after it happened. The executor
already runs assertCleanBranchAtBase at worktree acquisition and reclaim
— the gap was the active session window itself.
Add reportBranchAttribution(repoDir, branch, baseSha, taskId) which
walks base..branch and bins every commit into ownTrailed (healthy),
ownUntrailed (subject tag but commit-msg hook didn't fire), foreign
(different FN-id), or unattributed (no subject pattern, no trailer —
typically a hand-merge or plumbing commit). Wire it into the executor
right after captureModifiedFiles in the post-session path: when any
anomaly bucket is non-empty, emit a structured branch:attribution-
anomaly audit event and a task log entry. The audit itself is wrapped
in a try/catch so a probe failure never destabilizes a completing
session. New branch:attribution-anomaly and branch:auto-reattach-
authoritative GitMutationType variants accept the structured metadata
(the latter for the handoff re-attach added earlier this session).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added executor logic to skip retries when a review is pending for a task, introducing a `pendingReviewBlockHelper` in the task-done path and updating the retry-gate to consult it; two new reliability-interaction test suites cover the feature behavior and composition with existing retry/backstop laye
Fusion-Task-Id: FN-5436
Close the last fire-and-forget gap from the previous fixes: the task:moved
(away from in-progress) and task:deleted listeners no longer call the
synchronous fire-and-forget `abortInFlightTaskWork`. Instead they track an
awaited disposal promise per task in `pendingTaskDisposals`. The task:moved
(to in-progress) dispatch path awaits any in-flight disposal for the same
task before calling `execute()`, so a fast bounce (in-progress → todo →
in-progress) no longer races the conflict-cleanup path against a still-live
shell.
`awaitAbortInFlightTaskWork` now claims each session surface (activeSessions,
activeStepExecutors, activeWorkflowStepSessions, activeSubagentSessions)
synchronously before awaiting any async abort. This lets concurrent disposal
calls for the same task dedupe naturally — the second call finds the maps
empty and no-ops, preserving the existing single-abort/single-dispose
contract that the soft-delete and user-cancel tests assert.
Adds a regression test in executor-user-cancel covering the re-dispatch
ordering: an immediate task:moved-to-in-progress that follows a still-running
task:moved-away must wait for abort to complete before execute() runs.
The legacy `abortInFlightTaskWork` is removed (no callers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three independent reliability fixes that all surfaced as the same bug:
live tasks losing their worktrees mid-execution and emitting
`wrong_toplevel` errors.
Fix A — executor stale-self-owned classifier:
`reconcileSelfOwnedActiveSessionForRemoval` now takes a process-active
probe (`executingTaskLock.has`) and a minimum-idle window (default 5s)
in addition to the existing in-memory `activeWorktrees` binding probe.
Recently-registered or still-running entries are refused with
`process-active-refuses` / `too-recent-refuses`, with audit-grade
log lines. Both the pre-remove path
(`reconcileSelfOwnedBeforeRemove`), the post-throw retry in
`removeOwnWorktreeWithReconcile`, and the defensive reconcile in
`removeWorktree` route through the same hardened gates.
Fix B — pause synchronously reaps the agent session:
New `awaitAbortInFlightTaskWork` mirrors the existing fire-and-forget
abort but awaits each `session.abort()` /
`stepExecutor.terminateAllSessions()` /
`workflowSession.abort()`. `parkTaskAfterWorkflowStepPause` calls it
before `moveTask("todo")`, and the `task:updated` user-pause handler
routes through it, so a fast re-dispatch can no longer race a still-
live shell.
Fix C — self-healing realpath + active-task skip:
`reconcileTaskWorktreeMetadata` now realpath-normalizes both sides of
the registry comparison (handling macOS `/private/var/...`) and
refuses to clear `worktree`/`branch` on in-progress or in-review
tasks. The skip emits a new
`task:auto-recover-worktree-metadata-skipped-active` audit event;
executor-level recovery paths remain in charge of active tasks.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Added optional dependencies parameter to taskUpdateParams schema
- Implemented validation for self-dependency and non-existent task IDs
- Updates task dependencies via store.updateTask() when parameter is provided
- Preserves existing dependencies when parameter is omitted
- Updated tool description to document the new parameter
The merge adds a post-completion defensive backstop that probes and removes stale same-task `activeSessionRegistry` entries on `done`/`archived` transitions, completing FN-5346 with a shared reconcile helper, a defensive ownership probe wired into paused cleanup, audit event alignment, and regressio
Fusion-Task-Id: FN-5346
Removes the branch-recovery CLI surface, orphan-rescue engine primitives, and their associated tests (over 1,500 lines deleted), while restoring a minimal prune-only orphan branch sweep with proper git audit mutation types. Documentation across `cli-reference.md`, `task-management.md`, and `AGENTS.m
Fusion-Task-Id: FN-5329
Implements in-flight abort for soft-deleted tasks across all three execution lanes: executor, merger, and triage now check for `deletedAt` before proceeding and emit `task:soft-delete-in-flight-abort` audits rather than continuing work on a deleted task. The 829-line addition is dominated by integra
Fusion-Task-Id: FN-5264
Fusion-Task-Lineage: 4ee8e63b-abf0-43e2-9130-39a05434d8f9
The merge introduces an atomic review handoff seam in the core store (`packages/core/src/store.ts`) and migrates executor and self-healing transitions to use it, replacing the previous multi-step mutable-state handoff with a single transactional operation. Extensive reliability backstops and regress
Fusion-Task-Id: FN-5241