bc7dfe4bbfcb2b4f2b9d01aead3cb59fadac5bbc
3315 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e72629c251 |
FN-8126: add per-task merger model controls
Enable Quick Add and task editing to select merger models and thinking levels. - Persist merger model and thinking overrides through task APIs, storage, and PostgreSQL migrations. - Add merger-lane selection controls to Quick Add and model settings interfaces. - Apply task merger settings to merger and PR fallback sessions, with regression coverage. - Document the merger lane and include a release changeset. Files changed: .changeset/fn-8126-quick-add-merger-lane.md | 7 ++ docs/dashboard-guide.md | 2 + docs/settings-reference.md | 7 +- .../core/src/__tests__/model-resolution.test.ts | 9 ++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 20 ++++ .../core/src/postgres/migrations/0000_initial.sql | 3 + .../migrations/0017_task_merger_model_lane.sql | 4 + packages/core/src/postgres/schema-applier.ts | 14 ++- packages/core/src/postgres/schema/project.ts | 3 + packages/core/src/store.ts | 2 +- .../core/src/task-store/archive-lifecycle-2.ts | 6 ++ packages/core/src/task-store/persistence.ts | 6 ++ packages/core/src/task-store/remaining-ops-2.ts | 4 +- packages/core/src/task-store/remaining-ops-6.ts | 2 +- packages/core/src/task-store/serialization.ts | 6 ++ packages/core/src/task-store/task-creation.ts | 6 ++ packages/core/src/task-store/task-row-mappers.ts | 4 +- packages/core/src/task-store/task-update.ts | 6 ++ packages/core/src/types.ts | 18 ++++ packages/dashboard/app/api/tasks.ts | 13 +++ .../dashboard/app/components/InlineCreateCard.tsx | 33 ++++++- .../app/components/ModelSelectionModal.tsx | 29 ++++++ .../dashboard/app/components/ModelSelectorTab.tsx | 101 +++++++++++++++++++-- .../dashboard/app/components/QuickEntryBox.tsx | 44 +++++++-- .../__tests__/ModelSelectionModal.test.tsx | 20 ++++ .../components/__tests__/ModelSelectorTab.test.tsx | 37 +++++++- .../src/routes/register-task-workflow-routes.ts | 27 +++++- .../src/__tests__/agent-session-helpers.test.ts | 8 ++ packages/engine/src/agent-session-helpers.ts | 16 +++- packages/engine/src/merger-ai.ts | 14 +-- packages/engine/src/merger.ts | 35 ++++--- packages/engine/src/pr-response-run-ops.ts | 7 +- 34 files changed, 451 insertions(+), 64 deletions(-) Fusion-Task-Id: FN-8126 Fusion-Task-Lineage: 3fc81801-6d77-4e11-9cf0-3af37313930e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b747f51f8b |
FN-8185: use async persistence for reflection rework reads
Resolve workflow rework metrics from the persisted production run. - Resolve workflow selections to definition-backed run IDs - Prefer asynchronous step-instance reads with legacy-store compatibility - Cover PostgreSQL, fallback, and degraded reflection metrics Files changed: .../engine/src/__tests__/agent-reflection.test.ts | 51 +++++++++++++++++++--- packages/engine/src/agent-reflection.ts | 50 ++++++++++++++------- 2 files changed, 80 insertions(+), 21 deletions(-) Fusion-Task-Id: FN-8185 Fusion-Task-Lineage: 2554d5f1-62b7-47a6-bad0-c00a7d649a2d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a136535f15 |
fix(engine): taint steps skipped after a bulk-completion refusal so they cannot auto-promote (#2260)
## What & why
**FN-8141 laundered a failed task into `done` with zero net changes and
no sign-off.** After the executor's
`bulk-step-completion-without-review` refusal fired (steps had no
APPROVE verdicts), the agent used the sanctioned skip affordance
(`fn_task_update status="skipped"`) on the remaining unreviewed steps.
Because every completion check counts `skipped` as complete, the task
then satisfied the exact condition the refusal was protecting, and
downstream **automatic** promotion (implicit `fn_task_done`,
self-healing `recoverStrandedCompletedTodoTasks`) moved it to in-review
— where the AI merger found an empty diff and finalized it as a no-op
`done`.
This PR restores the invariant: **steps skipped while a
bulk-step-completion refusal marker is active on the task are "tainted"
and cannot carry the task to review through any automatic path.** The
taint clears on an honest exit — an accepted `fn_task_done` (explicit or
non-tainted implicit) or an operator manual retry — so the legitimate
`PREMISE STALE` skip-then-done flow is unaffected.
## Design
- **Persisted marker**: new nullable `Task.bulkCompletionRefusalAt` (ISO
timestamp), stamped when the `bulk-step-completion-without-review`
refusal fires (explicit `fn_task_done` handler + implicit
`handleImplicitTaskDoneRefusal`). Survives requeue so a refusal on
attempt N taints attempt N+1's promotion. Full store plumbing (types,
descriptors, serialization, SQLite/PG schema + health self-heal).
- **Pure evaluator** `evaluateSkipBypassTaint(task)` in `@fusion/core`
(next to `evaluateNoCommitsNoOpFinalize`): `blocked` iff the marker is
set AND ≥1 step is `skipped`. Single rule every AUTO-promotion check
calls.
- **Clearing**: accepted explicit `fn_task_done`, accepted
implicit/retry completion (the success-reset `updateTask`s), and
`buildManualRetryResetPatch` (operator retry). A fresh lifecycle that
genuinely re-does the work leaves zero skipped steps, so it is never
blocked even if a marker lingers.
## Surface enumeration (every consumer of "all steps done/skipped" that
gates AUTO-promotion)
- **executor.ts**: `getCompletedTaskFinalizationDecision` (gated on the
`isTaskWorkComplete` branch only, never on an accepted `taskDone`);
`recoverCompletedTask` (shared chokepoint for unpause resume,
completed-task watchdog, orphan resume);
`evaluateImplicitCompletionRefusal` (both implicit-completion loops);
`isTaskAlreadyCompleteForNonContinuableSession`; graph merge-boundary
`getWorkflowMergeImplementationProofFailure`.
- **self-healing.ts**: `recoverCompletedTasks` (stuck in-progress) and
`recoverStrandedCompletedTodoTasks` (the exact FN-8141 promoter).
- **Verified-safe, left as-is**: per-step graph node projections
(executor ~6274/6298) and progress-render checks — they don't gate
whole-task auto-promotion.
## Test evidence
Scoped runs (all green):
```
CORE: pnpm --filter @fusion/core exec vitest run \
src/__tests__/skip-bypass-taint-guard.test.ts \
src/__tests__/skip-bypass-taint-persistence.test.ts \
src/__tests__/manual-retry-reset.test.ts
→ 17 passed
ENGINE: pnpm --filter @fusion/engine exec vitest run \
src/__tests__/executor-skip-bypass-taint.test.ts \
src/__tests__/self-healing.test.ts
→ 401 passed
```
Coverage: pure-evaluator (skip-before-refusal counts, skip-after-refusal
doesn't, taint-clearing, empty-marker/empty-steps edges); store
round-trip of the marker (set→read→clear); executor white-box (implicit
completion refused when tainted, allowed when clean or fully re-done,
graph merge-boundary reports missing proof, and the **explicit
`fn_task_done` PREMISE-STALE honest exit stays accepted**); self-healing
(FN-8141 sequence does not promote from either recovery path; a clean
legitimately-skipped task still promotes); manual-retry clears the
marker.
## Note on `pnpm verify:fast`
`verify:fast` currently fails at the workspace-artifact bootstrap on
**pre-existing** pi-SDK type errors in
`packages/engine/src/{auth-storage,pi,provider-registration}.ts` — the
FN-8145 upstream migration breakage (pi 0.80.x removed
`AuthStorage`/`ModelRegistry.create`). **None of those files are in this
diff.** `@fusion/core` builds clean (`packages/core build: Done`), and
`@fusion/engine` `tsc` reports **no errors in the files this PR
touches** (`executor.ts`, `self-healing.ts`); the only engine build
errors are the FN-8145 files. This base failure is the same condition
FN-8141 describes and is out of scope for this task.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
29543a0aac |
FN-8157: add PostgreSQL workflow step-instance persistence
Persist workflow foreach step-instance state through async PostgreSQL store APIs. - Add async save, load, and stale-run pruning operations backed by Drizzle. - Route executor persistence, recovery, and integration projection through async APIs. - Cover PostgreSQL persistence and migrate foreach wiring coverage to the PG harness. - Quarantine unrelated flaky route and triage tests per the test ledger. Files changed: .../workflow-run-step-instances.pg.test.ts | 100 +++++++++++++++++++ packages/core/src/store.ts | 14 ++- packages/core/src/task-store/remaining-ops-6.ts | 109 ++++++++++++++++++++- .../dashboard/src/__tests__/routes-github.test.ts | 14 +-- .../src/routes/register-task-workflow-routes.ts | 18 ++-- packages/engine/src/__tests__/triage.test.ts | 6 +- .../src/__tests__/workflow-foreach-wiring.test.ts | 59 +++++------ packages/engine/src/executor.ts | 57 ++++++++--- packages/engine/src/triage.ts | 4 +- packages/engine/vitest.config.ts | 2 +- scripts/lib/test-quarantine.json | 7 +- 11 files changed, 315 insertions(+), 75 deletions(-) Fusion-Task-Id: FN-8157 Fusion-Task-Lineage: c359f0d3-9191-4d27-aaed-9912419c5c27 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
aa1e250dd3 |
fix(engine): overseer vetoes no-op merge finalize when the last executor signal was failed-with-incomplete-work (#2258)
## What & why FN-8141 (\"Update pi SDK … verify Kimi K3 end to end\") was **laundered into `done`** despite producing zero net changes. The executor reverted the impossible work 5 times; the planner overseer emitted `stage=executor signal=failed` (\"Executor stage parked failed with work incomplete\") **twice**, then an hour later — because the overseer is **stage-scoped and memoryless** — classified the same task `stage=merger signal=progressing` and let the AI merger's **empty no-op finalize** promote it to `done`. No reviewer ever saw it (skipped steps request no review; the merge-review pass reviews an empty diff). This restores the invariant: **a task whose most-recent executor signal is failed-with-incomplete-work, with no subsequent green completion, must not reach `done` via a zero-diff no-op merge finalize.** ## Change Two pure, unit-testable, never-throw functions (`packages/engine/src/overseer-noop-finalize-veto.ts`), following the FN-7514 `evaluateOverseerHumanControl` precedent (pure predicate + ids/outcomes-only audit metadata): - **`deriveExecutorSignalMemory`** — reconstructs the most-recent executor signal from the **durable `overseer:intervention` timeline the overseer already writes** (no new persisted column / migration; \"the existing oversight state storage the controller uses\"). A later green executor observation supersedes an earlier failure, which is how \"no subsequent execution completed green\" is derived. Keys on the newly-exported `EXECUTOR_FAILED_INCOMPLETE_REASON` constant (already load-bearing for FN-7577 feed dedup) as the single source of truth. - **`evaluateNoOpFinalizeExecutorVeto`** — the veto decision. Wired into the AI **empty-merge lane** (`merger-ai.ts`), composed with (and independent of) the FN-6461 no-commits guard: on veto it sets `error`, writes a durable log entry, emits `overseer:no-op-finalize-vetoed-failed-executor`, and moves the task back to `todo` with progress preserved — mirroring the FN-6461 blocked lane. The move-to-todo transition takes the task out of the merge lane, so the event isn't re-emitted every poll (equivalent to the `overseer:oversight-withheld-human-control` per-(taskId, reason) dedup). Independent of the sibling Task 2 merger-layer lineage guard: both can fire; **either alone stops FN-8141**. ## Surface enumeration - **Empty (zero-diff) no-op merge** — vetoed when memory is failed-incomplete. ✅ - **Non-empty (real squash landed) merge** — **never** vetoed (reviewers/merge review cover real diffs). ✅ - **failed-incomplete then later green execution** — memory superseded → no veto. ✅ - **No executor memory / store lacks the async reader** — fail open (no veto); FN-6461 + sibling guard remain the safety net. ✅ - **user-paused / autoMerge:false / approval-blocked** — defers to FN-7514 human-control; never fights those semantics. ✅ - **Timeline entry filtering** — only `stage=executor` + `action=observe` entries count as signals (retry/escalate messages ignored). ✅ ## Test evidence `pnpm --filter @fusion/engine exec vitest run src/__tests__/overseer-noop-finalize-veto.test.ts src/__tests__/merger-ai.test.ts --silent=passed-only --reporter=dot` ``` Test Files 2 passed (2) Tests 48 passed (48) ``` Covers: derivation (failed→veto, failed-then-green→no-veto, non-executor/non-observe ignored, empty→null); evaluator (veto, green, no-memory, non-empty never-vetoed, user-paused defer, autoMerge:false defer, missing-task fail-open); and an engine integration test driving an **FN-8141-shaped** empty merge through `runAiMerge` → asserts move-to-todo + `overseer:no-op-finalize-vetoed-failed-executor` audit event + main untouched, plus the later-green case finalizing done. `@fusion/core` builds clean (`pnpm --filter @fusion/core build`). **Note on `pnpm verify:fast`:** it currently fails to build `@fusion/engine`, but **only** in `auth-storage.ts` / `pi.ts` / `provider-registration.ts` — the pre-existing pi-SDK breakage that *is* this incident (pi 0.80.x removed `AuthStorage`/`ModelRuntime`/`ModelRegistry`; tracked as FN-8145). Verified identical errors with my changes stashed; **my diff touches none of those files and adds zero new type errors** (tsc reports all program errors before failing — none were in my files). 🤖 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** - Prevented zero-change tasks from being incorrectly finalized when the latest executor attempt failed with unfinished work. - Preserved task progress and returned affected tasks to **Todo** for continued processing. - Allowed finalization to proceed after a subsequent successful executor result. - Maintained existing human-control and non-empty merge behavior. - Added audit visibility for blocked finalization events. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
f079245acc |
fix(engine): defensive getAuth guard on session routing seam + realign pi tests to ModelRuntime seam (#2261)
## What / Why **Reworked after FN-8179 (`fd43a57a4`) landed on `main`.** FN-8179 did most of what the original PR #2261 did — pinned `@earendil-works/pi-ai` / `pi-coding-agent` to `^0.80.10` everywhere, added the `createSessionOptions` `NonNullable` typing in `pi.ts`, and regenerated `pnpm-lock.yaml`. This PR was rebased onto current `origin/main` and reduced to **only the unique residual not covered by FN-8179**. ### Residual change 1 — defensive `getAuth` guard The FN-8142 migration rewrote `attachSessionRoutingHeaders` from the `ModelRegistry.getApiKeyAndHeaders` seam to `ModelRuntime.getAuth`, but dropped the pre-migration defensive invariant: a missing resolution method must **not** break session creation. On `main` the function now calls `modelRuntime.getAuth.bind(...)` unguarded, which throws if `getAuth` is ever absent. This restores the guard: no-op (warn) when `getAuth` is missing, so a future pi rename degrades to un-tagged requests instead of a hard failure at every agent start. ### Residual change 2 — test realignment (needed against current main) FN-8179 aligned the SDK but did **not** update the two `#1675` routing-header test suites, which still asserted the old `getApiKeyAndHeaders` seam. **Verified RED on current `main` before touching them:** - `pi-create-fn-agent.test.ts` — **60 / 104 failing** (mock had no `ModelRuntime` export; `createAgentSession` now receives `modelRuntime`). - `pi-session-routing-headers.test.ts` — **4 / 5 failing** (`attachSessionRoutingHeaders` signature is `getAuth`, not `getApiKeyAndHeaders`). Both are realigned to the `ModelRuntime.getAuth` seam (the mock gains the `ModelRuntime` export) → **109 / 109 green**. Assertions were strengthened to the new behavior, not weakened; the #1675 precedence invariant (taskId > pi session id > no-wrap), header merge, apiKey/provider-header passthrough, failed/undefined passthrough, and absent-method no-op are all still asserted. ## Surface enumeration - **Routing-header seam**: both the `createFnAgent` path and the `attachSessionRoutingHeaders` unit (taskId / pi-session-id / no-id precedence; header merge; apiKey + provider-header passthrough; failed/undefined resolution; absent-method no-op). - **Both mock forms** in `pi-create-fn-agent.test.ts`: the top-level `vi.mock` and all three `vi.doMock` skill-selection blocks now export `ModelRuntime`. ## Test evidence - `pnpm --filter @fusion/engine exec tsc --noEmit` → **exit 0**. - `pnpm --filter @fusion/engine exec vitest run` on both suites → **109/109 pass** (was 64 failing on main). - `pnpm verify:fast` → **PASS** (typecheck + build scoped to changed packages + CLI build + boot smoke `GET /api/health 200`; no tests run). Do not merge without CI. No release performed. 🤖 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** * Improved session creation reliability when authentication support is unavailable. * Preserved existing authentication details while adding session-routing headers when supported. * Prevented session creation from failing when authentication information cannot be resolved. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
ced0e84f5d |
FN-8184: unify heartbeat multiplier timing
Keep scheduler repair and reports health aligned with the effective heartbeat cadence. - Apply heartbeatMultiplier once through shared interval calculations. - Align scheduler repair and reports-health stale thresholds with scaled cadence. - Add multiplier regression coverage and document the effective timing rules. Files changed: .../fn-8184-heartbeat-multiplier-consistency.md | 7 ++ docs/agents.md | 8 +- .../src/__tests__/heartbeat-executor.test.ts | 43 +++++++++ .../src/__tests__/heartbeat-scheduler.test.ts | 46 ++++++++++ packages/engine/src/agent-heartbeat.ts | 101 ++++++++++++++++----- 5 files changed, 176 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-8184 Fusion-Task-Lineage: 05b79c5e-444a-42c3-9073-9c8c05df5def Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
26cb0cc2e1 |
FN-8180: add Kimi K3 catalog and pricing support
Expose Kimi K3 throughout the native pi catalog and token-cost surfaces. - Add K3 pricing and normalized lookup coverage for Dashboard cost calculations. - Verify native K3 registry, API model route, and repeat-stop behavior end to end. - Add a minor release changeset for Kimi K3 model selection support. Files changed: .changeset/fn-8180-pi-sdk-kimi-k3.md | 7 +++ packages/core/src/__tests__/model-pricing.test.ts | 7 ++- packages/core/src/model-pricing.ts | 17 +++++- ...ister-model-routes-kimi-k3-supplemental.test.ts | 70 ++++++++++++++++++++++ .../pi-prompt-session-and-check-recursion.test.ts | 15 +++++ .../src/__tests__/provider-registration.test.ts | 17 ++++++ 6 files changed, 130 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-8180 Fusion-Task-Lineage: 07598d73-c866-4329-b5cf-3aebc1c0a180 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
136958fc60 |
fix(engine): stranded-completed promoters withhold tasks whose last execution ended in a failure park (#2257)
## What & why
FN-8141 laundered a failed task into `done`. The executor correctly
parked the task `failed` ("task parked failed during no-fn_task_done
retry" / "fn_task_done refusal retry budget exhausted"), the pause-abort
machinery bounced it to `todo`, and ~12 minutes later
`recoverStrandedCompletedTodoTasks` promoted it to `in-review` because
every step was done/skipped — overriding the honest failure park. From
there the AI merger found an empty diff and finalized it as a no-op
`done`, with no reviewer ever seeing it.
Existing exclusions (`task.error`,
`evaluateNoCommitsNoOpFinalize().blocked`, active statuses, refreshing
review state) all missed it because the failure provenance lived **only
in the durable task log** by the time the promoter ran — status/error
had been cleared by the pause-abort bounce.
This PR restores the invariant: **a stranded-completed promoter must not
promote a task whose most recent execution lifecycle ended in a
failure/refusal park.**
## Change
- New pure, unit-testable evaluator
`evaluateCompletedPromotionFailureProvenance(task)` in `@fusion/core`
(next to `no-commits-finalize-guard.ts`). It scans the task-log **tail**
(bounded to 250 entries) and lets the **most-recent execution-outcome
marker** decide: a failure/refusal park → `{ blocked: true, reason:
"failure-provenance" }`; a fresh clean completion (`Task marked done by
agent` / `All steps complete — implicit fn_task_done`) that appears more
recently supersedes an earlier park; zero failure markers → not blocked.
Recency is by construction, so a failure that predates a newer clean
execution is never reached.
- Both self-healing sweeps (`recoverCompletedTasks` stuck-in-progress
**and** `recoverStrandedCompletedTodoTasks` stranded-todo) fetch the
full task for candidates that already cleared the cheap slim filters
(slim listings strip `log`) and skip when blocked, emitting a
**deduped** `task:reconcile-stranded-completed-no-action` run-audit
event (ids/outcomes-only: `taskId`, `reason`, `sweep`, `marker?`).
- Defense-in-depth: the shared executor `recoverCompletedTask`
chokepoint — which the sweeps AND the executor's own
unpause/`resumeOrphaned` fast-paths all funnel through — also refuses a
provenance-blocked promotion, so no route can launder a failed park.
**Escape hatch (documented in FNXC comments):** an operator
retrying/moving the task starts a fresh execution whose clean-completion
marker supersedes the failure park, clearing the block with no code
change.
## Surface enumeration
- `recoverCompletedTasks` (stuck-in-progress sweep, self-healing.ts) —
guarded + audited.
- `recoverStrandedCompletedTodoTasks` (stranded-todo sweep,
self-healing.ts) — guarded + audited. FN-8141 shows both columns can
launder.
- `recoverCompletedTask` executor callback (the route both sweeps +
unpause + `resumeOrphaned` share) — verified it did **not** check
log-based provenance; added the guard there as the final chokepoint.
## Test evidence
Pure-evaluator unit tests (`@fusion/core`) — marker detection,
most-recent-outcome recency, supersede-by-clean-completion,
empty/missing log, tail-scan bound:
```
pnpm --filter @fusion/core exec vitest run src/__tests__/completed-promotion-failure-provenance.test.ts
Test Files 1 passed (1) Tests 9 passed (9)
```
Self-healing integration tests (`@fusion/engine`) — FN-8141-shaped todo
(3 done + 2 skipped + refusal-exhaust/park marker) is NOT promoted and
emits the no-action event exactly once (deduped across a second cycle);
same task after a fresh clean execution IS promoted; stuck-in-progress
variant covered:
```
pnpm --filter @fusion/engine exec vitest run src/__tests__/self-healing.test.ts -t "recoverCompletedTasks|recoverStrandedCompletedTodoTasks|FN-8141"
Test Files 1 passed (1) Tests 14 passed | 382 skipped (396)
```
`@fusion/core` builds clean. My engine changes add **zero** new type
errors (verified: all 13 engine build errors are the pre-existing pi-SDK
cluster in `auth-storage.ts`/`pi.ts`/`provider-registration.ts`, none in
`self-healing.ts`/`run-audit.ts`/`executor.ts`/the new file).
## Known environmental blocker
`pnpm verify:fast` cannot go green on this branch: the `@fusion/engine`
build is **already broken at baseline** (confirmed by stashing all my
changes) by the pi 0.80.x SDK migration errors
(`ModelRegistry`/`AuthStorage`/`ModelRuntime`) — the exact FN-8145
upstream breakage described in the FN-8141 incident. That is out of
scope for this task and independent of this diff. Likewise, the 22
pre-existing
`restart.integration.test.ts`/`executor-fast-mode-workflows.test.ts`
failures are identical with and without my changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus <noreply@anthropic.com>
|
||
|
|
1c4fdb7b59 |
fix(engine): require already-landed proof before finalizing an empty AI merge as done (#2259)
## What & why Task FN-8141 laundered a failed task into `done`: its branch had no net changes vs `main` **only because the executor reverted its own work five times**, and the AI empty-merge lane took the "empty means already-landed or nothing-to-do → finalize as no-op done" path, stamping `mergeConfirmed: true` with no reviewer or operator sign-off. This restores the invariant: **a commit-expected task that reaches the empty AI-merge outcome must not finalize `done` without positive evidence the work already landed.** `packages/engine/src/merger-ai.ts` empty-outcome lane now, for a commit-expected task (`noCommitsExpected !== true`), requires one of: 1. Durable recorded landing on the task (`mergeDetails.mergeConfirmed`/`commitSha`). 2. A prior AI no-op finalization proof pair in the task log (`hasPriorAiNoOpFinalizationProof`, FN-7261 shape). 3. The branch tip is an **ancestor of the integration branch** (fast-forwarded / zero-ahead / already-integrated). 4. The already-on-main classifier (`detectAlreadyLandedOnMain`) finds a distinct landing commit via a **strong** strategy (`trailer`/`ancestry`/`patch-id`). The classifier's weak `tree-equal`/`no-diff` strategies are **deliberately rejected**: a branch that committed work then reverted it back to base has a tree equal to `main` (main never advanced), which is exactly the FN-8141 shape and would false-positive. Absent proof, the task gets `task.error` set, emits run-audit `task:empty-merge-finalize-blocked-no-landed-proof` (ids/counts/outcomes-only), and is moved back to `todo` with progress preserved — mirroring the existing FN-6461 blocked lane. `noCommitsExpected === true` tasks are untouched (hardened separately in the sibling Task 1). The non-empty landed path, group/PR routing, and push-after-merge behavior are unchanged. ## Surface enumeration - **Single-repo empty-outcome finalize (primary lane)** — guarded in `runAiMerge`. - **Workspace/multi-repo caller** — `landWorkspaceTask`'s all-empty finalize is a second route. Already-landed sub-repos are proven up front by `findProvenLandedCommit` and marked `status:"landed"`; when `landedCount === 0` the guard re-checks each empty sub-repo's branch and blocks the FN-8141 reverted shape (tip not an ancestor / branch vanished) identically. (Note: the genuinely-integrated all-empty workspace case already throws `missing-merge-confirmation` on `mergeConfirmed:false`, so it never reached `done`; that pre-existing path is left intact.) - **Re-promotion ping-pong** — the blocked path sets `task.error`, and `recoverStrandedCompletedTodoTasks` excludes any task with `task.error`, so the promoter cannot re-promote the unchanged blocked task. Regression-tested. ## Test evidence Scoped tests (all green): ``` vitest run merger-ai.test.ts workspace-merger.test.ts → 46 passed vitest run self-healing.test.ts -t recoverStrandedCompletedTodoTasks → 4 passed vitest run merger.test.ts merger-finalize-unproven.real-git → 20 passed vitest run self-healing-workspace + workspace-merger-lease + workspace-merger-deps-resilient → 26 passed ``` New tests: - merger-ai.test.ts: commit-expected empty (reverted) → blocked to todo + error + audit event, NOT done; empty + prior no-op proof → still no-op done; empty + branch-ancestor-of-main → still no-op done; noCommitsExpected empty → unchanged done path. - workspace-merger.test.ts: all-empty (reverted) workspace → blocked to todo + error, not done / not `task:merged`. - self-healing.test.ts: a task blocked by this guard (all steps done/skipped, `task.error` set) is NOT re-promoted by `recoverStrandedCompletedTodoTasks`. **`pnpm verify:fast` is red on this branch due to the pre-existing pi SDK breakage** (`auth-storage.ts`/`pi.ts`/`provider-registration.ts` — the FN-8142/FN-8145 `AuthStorage`/`ModelRegistry` removal that is the root of the FN-8141 incident). Verified those identical build errors reproduce with my changes stashed; this PR adds **zero** new type errors (no build error is in `merger-ai.ts` or `run-audit.ts`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
19eb179473 |
fix(core): block empty-diff finalize of tasks with skipped steps — generalized FN-6461 guard (#2254)
## What & why FN-8141 (\"Update pi SDK to latest and verify Kimi K3 end to end\") was **laundered into `done` despite producing zero net changes**. The pi SDK bump kept breaking `verify:fast`, the work was reverted 5×, and the agent used the sanctioned skip affordance to mark **Testing & Verification** and **Documentation & Delivery** `skipped`. `isTaskComplete()` counts `skipped` as complete, so: 1. self-healing `recoverStrandedCompletedTodoTasks` promoted the todo task to in-review (all steps done/skipped), 2. the AI merger saw an empty diff vs main → \"finalizing as no-op\" → `done` with `mergeConfirmed:true`, 3. no reviewer ever saw it (skipped steps request no review; the merge-review pass reviews an empty diff). The only existing guard, `evaluateNoCommitsNoOpFinalize` (FN-6461), missed it **twice**: it only fired when `noCommitsExpected === true` (FN-8141 was commit-expected — the branch was empty because work was *reverted*), and even then only blocked when `incomplete >= done` (FN-8141 had 3 done vs 2 skipped). ## The fix Generalize the guard (same exported name/shape — every finalize lane keeps working) so a **zero-diff/no-op finalize is blocked whenever ANY step is `skipped`**: - a **verification-flavored** skipped step (name matching `/test|verif|qa|review/i`) blocks **unconditionally**; - any **other** skipped step blocks **unless** every non-skipped step is `done` **AND** the task is the legacy `noCommitsExpected` ops shape; - the legacy FN-6461 ratio rule (`noCommitsExpected` + `incomplete >= done`) is preserved for pending/in-progress incomplete work; - blocked evaluations return a precise `reason` naming the skipped steps. Legitimate shapes still pass: all-done no-skip empty diffs (left to the lineage-proof work), zero-step tasks, and `noCommitsExpected` ops tasks with a minor non-verification skipped tail. ## Surface enumeration The guard is the single chokepoint used at every zero-diff finalize lane; all already honor `.blocked`/`.reason`, so the core change fixes each surface: - `packages/engine/src/merger-ai.ts` ~1116 — AI empty-merge lane - `packages/engine/src/merger.ts` ~6261 / ~7354 / ~7658 — merger empty-own-diff + no-op lanes - `packages/engine/src/self-healing.ts` ~2851 — stranded-todo promoter pre-check; ~6335 — no-op review finalize Behavior on block is unchanged (error set, durable log entry, `task:no-commits-finalize-blocked-incomplete-steps` run-audit event, move back to todo with progress preserved). ## Test evidence - **Core** `pnpm --filter @fusion/core exec vitest run src/__tests__/no-commits-finalize-guard.test.ts` → **9 passed**. Covers FN-8141 shape (3 done + 2 skipped, not noCommitsExpected → blocked), verification-skip blocks regardless of ratio/`noCommitsExpected`, legacy `noCommitsExpected` shapes, all-done no-skip → not blocked, zero steps → not blocked. - **Engine lanes** — one test per finalize-lane family, all green: - `merger-ai.test.ts` (AI empty lane, incl. new FN-8141 reverted-commit-expected case) → **36 passed** - `merger-finalize-unproven.real-git.test.ts` (merger lanes) → passing - `self-healing.test.ts` (stranded-todo promoter + no-op review finalize, incl. new FN-8141 promoter case) → **394 passed** ### `pnpm verify:fast` — pre-existing engine build breakage (not this PR) `verify:fast` fails at the workspace-dist bootstrap because `@fusion/engine` does **not** typecheck on `main`: `src/auth-storage.ts`, `src/pi.ts`, `src/provider-registration.ts` reference `ModelRuntime` / `AuthInteraction` / `CredentialInfo` / private `ModelRegistry` members removed by pi 0.80.9/0.80.10 (the FN-8142 migration that motivated this incident; upstream fix is FN-8145). Verified this failure reproduces with my changes **stashed** (13 identical tsc errors at clean HEAD). This PR touches only `@fusion/core` (builds clean, `tsc` exit 0) and engine **test** files — no engine source — so it neither causes nor can resolve that breakage. 🤖 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** * Prevented empty or no-op finalization when verification, QA, review, or other required steps are skipped. * Ensured tasks with skipped work are not incorrectly marked complete, merged, or promoted during recovery. * Improved error messages to identify skipped verification steps blocking completion. * **Tests** * Added regression coverage across finalization, merge, and self-healing workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus <noreply@anthropic.com> |
||
|
|
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>
|
||
|
|
fd43a57a41 |
FN-8179: align pi SDK versions with ModelRuntime API
Align workspace pi SDK dependencies with the ModelRuntime API required by the engine. - Pin pi AI and coding-agent packages to 0.80.10 across workspace consumers. - Keep session option typing compatible with the updated SDK contract. - Add a patch changeset and regenerate the dependency lockfile. Files changed: .changeset/fn-8179-pi-sdk-align.md | 7 + packages/cli/package.json | 4 +- packages/core/package.json | 2 +- packages/dashboard/package.json | 2 +- packages/engine/package.json | 4 +- packages/engine/src/pi.ts | 8 +- packages/pi-claude-cli/package.json | 8 +- .../src/thinking-config.ts | 9 +- pnpm-lock.yaml | 947 +++++++++++---------- pnpm-workspace.yaml | 5 + 10 files changed, 520 insertions(+), 476 deletions(-) Fusion-Task-Id: FN-8179 Fusion-Task-Lineage: aef45c2e-f353-4014-93de-44be91f43293 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
b687cc994e |
FN-8174: preserve live triage planning sessions
Keep active planning sessions protected from stale recovery while reclaiming genuinely hung triage work. - Retain stale processing entries that still have a live, non-aborted triage session. - Continue evicting no-session and stuck-aborted tasks so recovery can proceed. - Add triage and self-healing regression coverage, architecture guidance, and a patch changeset. Files changed: .changeset/fn-8174-planning-premature-todo.md | 7 ++ docs/architecture.md | 1 + packages/engine/src/__tests__/self-healing.test.ts | 102 +++++++++++++++++++++ packages/engine/src/__tests__/triage.test.ts | 37 +++++++- packages/engine/src/triage.ts | 48 +++++----- 5 files changed, 168 insertions(+), 27 deletions(-) Fusion-Task-Id: FN-8174 Fusion-Task-Lineage: f6811d72-95b4-4b5f-a71f-212f50e3ecdd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5a60643c0a |
FN-8142: migrate auth storage and model runtime to pi SDK
Migrate Fusion's credential and model integrations to pi SDK 0.80.8+. - Replace legacy AuthStorage initialization with a locked Fusion credential store and ModelRuntime-backed registry. - Wire asynchronous model initialization and refresh through CLI, desktop, dashboard, executor, and provider paths. - Update provider, routing, and registry tests for the new SDK contracts. Files changed: packages/cli/src/commands/__tests__/daemon.test.ts | 2 +- .../cli/src/commands/__tests__/dashboard.test.ts | 9 +- .../cli/src/commands/__tests__/onboard.test.ts | 1 + packages/cli/src/commands/__tests__/serve.test.ts | 2 +- packages/cli/src/commands/daemon.ts | 19 +- packages/cli/src/commands/dashboard.ts | 20 +- packages/cli/src/commands/onboard.ts | 6 +- packages/cli/src/commands/serve.ts | 19 +- packages/cli/src/commands/startup-model-sync.ts | 4 +- packages/core/src/__tests__/openai-models.test.ts | 17 +- ...-model-routes-openai-codex-supplemental.test.ts | 17 +- ...register-model-routes-zai-real-registry.test.ts | 15 +- packages/dashboard/src/routes.ts | 12 +- .../dashboard/src/routes/register-model-routes.ts | 2 +- packages/desktop/src/local-runtime.ts | 2 +- packages/desktop/src/local-server.ts | 2 +- .../custom-providers-openai-completions.test.ts | 16 +- .../custom-providers-openai-responses.test.ts | 16 +- .../engine/src/__tests__/executor-test-helpers.ts | 2 +- .../src/__tests__/pi-create-fn-agent.test.ts | 8 +- .../engine/src/__tests__/pi-layers-wiring.test.ts | 2 +- packages/engine/src/__tests__/pi.test.ts | 47 ++--- .../src/__tests__/provider-registration.test.ts | 17 +- packages/engine/src/auth-storage.ts | 218 ++++++++++++++++++--- packages/engine/src/custom-provider-registry.ts | 14 +- packages/engine/src/executor.ts | 15 +- packages/engine/src/pi.ts | 50 +++-- packages/engine/src/provider-auth.ts | 58 +++--- packages/engine/src/provider-registration.ts | 16 +- 29 files changed, 421 insertions(+), 207 deletions(-) Fusion-Task-Id: FN-8142 Fusion-Task-Lineage: 8ae79064-7820-4976-9645-9431b5a3129e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
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> |
||
|
|
ee28f5e45e |
FN-8155: test triage retry title handling
Cover blank-title behavior across retryable triage failures. - Verify deterministic validation retries retain blank task titles - Verify transient failures retain blank task titles while retries remain Files changed: packages/engine/src/__tests__/triage.test.ts | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) Fusion-Task-Id: FN-8155 Fusion-Task-Lineage: d8b4414d-b105-4251-bc42-d02a6d84bf3a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c6be0b158b |
FN-8129: centralize database backup settings
Move database backup policy and scheduling to shared global configuration. - Split project memory backups from cluster-wide database backup settings. - Migrate legacy backup values and routines safely into central global storage. - Schedule and dispatch one shared PostgreSQL backup routine across project engines. Files changed: .changeset/fn-8129-backup-settings-scope-split.md | 7 + docs/dashboard-guide.md | 2 + docs/settings-reference.md | 10 +- packages/cli/src/commands/backup.ts | 3 +- .../__tests__/backup-settings-migration.test.ts | 50 ++++++ .../src/__tests__/backup-settings-scope.test.ts | 27 +++ packages/core/src/backup-settings-migration.ts | 188 +++++++++++++++++++++ packages/core/src/backup.ts | 77 +++++---- packages/core/src/global-routine-store.ts | 104 ++++++++++++ packages/core/src/index.gate.ts | 6 +- packages/core/src/index.ts | 6 +- .../core/src/postgres/migrations/0000_initial.sql | 19 +++ .../postgres/migrations/0015_global_routines.sql | 19 +++ packages/core/src/postgres/schema-applier.ts | 19 ++- packages/core/src/postgres/schema/central.ts | 21 ++- packages/core/src/postgres/startup-factory.ts | 11 ++ packages/core/src/settings-schema.ts | 14 +- packages/core/src/types.ts | 31 +++- .../dashboard/app/components/SettingsModal.tsx | 10 +- .../settings/__tests__/section-keys.test.ts | 1 + .../app/components/settings/save-split.ts | 2 + .../search/__tests__/settings-search-index.test.ts | 1 + .../settings/search/entries.ts | 2 + .../app/components/settings/section-keys.ts | 4 - .../settings/sections/BackupsSection.search.ts | 40 ----- .../settings/sections/BackupsSection.tsx | 112 +----------- .../sections/DatabaseBackupsSection.search.ts | 51 ++++++ .../settings/sections/DatabaseBackupsSection.tsx | 142 ++++++++++++++++ .../settings-default-descriptions.test.tsx | 1 + packages/dashboard/src/routes.ts | 12 +- .../src/routes/register-settings-memory-routes.ts | 41 ++--- .../engine/src/__tests__/routine-scheduler.test.ts | 55 +++++- packages/engine/src/cron-runner.ts | 4 +- packages/engine/src/routine-runner.ts | 67 +++++--- packages/engine/src/routine-scheduler.ts | 35 +++- 35 files changed, 929 insertions(+), 265 deletions(-) Fusion-Task-Id: FN-8129 Fusion-Task-Lineage: af17f39a-7f1c-40ff-8a4a-cd63895cd532 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
478f226a54 |
test: green full-suite CI after main drift (#2229)
## Summary Restores green **Full Suite (non-blocking)** runs on `main`. Recent main merges left i18n key parity, schema baseline bookkeeping (0011→0012), heartbeat tool inventory (FN-8058 `fn_task_logs_read`), and merger whitespace-classification mocks (execFile `git diff -p -w :2: :3:`) out of date, so all four test shards failed. ## Root causes observed on main - **Shard 4 / `@fusion/i18n`**: missing `skipConfirmationDialogs*` + `reviewBudgetExhausted` in non-en locales; orphan `awaitingApprovalPlanReviewReplanCap` - **Shard 3 / `@fusion/core`**: `SCHEMA_BASELINE_VERSION` advanced to `0012` while tests still equated it with `OWNER_PROJECT_ID_SPLIT_VERSION` (`0011`) and omitted `0012` from applied-migration lists - **Shards 1–2 / `@fusion/engine`**: tool count/snapshot drift for `fn_task_logs_read`; merger tests still mocked `git diff-tree` for trivial classification after the execFile `:2:`/`:3:` cutover; mock provider `updateTask` arity drift ## Changes - Locale catalogs: add missing keys, drop orphan key - Schema applier tests: immutable 0011 identity + baseline 0012 lists - Heartbeat + gating snapshots: include `fn_task_logs_read` - Merger unit mocks: recognize `git diff -p -w :2:path :3:path` - Mock provider: accept optional third `updateTask` arg ## Test plan - [x] `pnpm --filter @fusion/i18n exec vitest run` — 23/23 - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/postgres/schema-applier.test.ts` (immutable + automation upgrade) — pass - [x] `pnpm --filter @fusion/core exec vitest run` project-identity + satellite-fusiondir — pass - [x] Engine suites from failed CI shards (file-scoped, hermes/openclaw/paperclip/grok, reliability post-finalize/mission, heartbeat, gating, merger recovery/prompt, mock-provider, etc.) — pass - [ ] Full Suite workflow green on merge to main <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved project data isolation across backend operations. - Added safer optional toast handling when UI components render outside the full application shell. - Added support for reading task logs during agent heartbeat sessions. - **Bug Fixes** - Prevented runtime probes from hanging and avoided scanning large binary files. - Improved path handling for workspaces with missing descendants. - Corrected task retry state resets and GitHub import/issue-close behavior. - **Style** - Improved chat, terminal, and settings spacing. - Added clearer accessibility labeling for the auto-merge control. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
06a5fd813d |
refactor: package code organization waves 6–7 (#2166)
## Summary Waves 6–7 of package code organization (plan: `docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`), after #2148. ### Wave 6 | New module | Parent | |---|---| | `merger-autostash-labels.ts` | `merger.ts` | | `types/agent-state.ts` | `types.ts` | | `app/api/tasks-lifecycle.ts` | `legacy.ts` | | `task-store/task-row-mappers.ts` | `remaining-ops-3.ts` (rename) | ### Wave 7 | New module | Parent | |---|---| | `self-healing-optional-step-revision.ts` | `self-healing.ts` | | `self-healing-path-utils.ts` | `self-healing.ts` | | `merger-git-parse` (+ `quoteArg`, `getBranchChangedFiles`) | `merger.ts` | | `app/api/settings.ts` | `legacy.ts` | Public import paths stay stable via re-exports. ## Test plan - [x] engine + dashboard typecheck (incl. app) - [x] eslint on touched modules - [x] merger-autostash / parse-porcelain / getBranchChanged / api-tasks - [ ] CI merge gate <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a dedicated task lifecycle API client (task promotion, merge, retry/reset/duplicate, pause/unpause, archive/unarchive, revert, plan approve/reject) plus branch-group and planner oversight actions. * Added a settings/config API service (effective task settings, update check/refresh/install). * Introduced standardized agent lifecycle states with identity/ephemeral detection helpers. * **Bug Fixes** * Improved autostash label compatibility and NUL-delimited changed-file detection for branch diffs. * **Refactor** * Modularized merger labeling/parsing, self-healing helpers, and lifecycle/type wiring while keeping behavior consistent. * **Tests** * Updated merger verification tests for `git diff -z` output handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
edc64138e0 |
feat(worktrees): task-pinned worktrees under worktreeNaming "task-id" (#2233)
## Summary Adds **task-pinned worktrees** for `worktreeNaming: "task-id"`. Under task-id naming, a task is pinned to exactly one derivable directory `<worktreesDir>/<lowercased-task-id>` (e.g. `.worktrees/fn-7996`) for its entire lifecycle — removing the ambiguity that let stale/foreign `task.worktree` pointers strand a task (the FN-7996 shape). `recycleWorktrees` stays fully functional and is **mutually exclusive** with task-id pinning: the two can't be enabled together. ## Behavior - **Pinned acquisition (`worktreeNaming: "task-id"`, recycling off):** `acquireTaskWorktree` runs **derive → validate → reuse-or-recreate** at the derived path — warm-reuse when the dir is a registered, usable worktree on the task's own branch; otherwise reclaim-in-place (`removeWorktree` + recreate at the SAME path, never a sibling name). A disagreeing `task.worktree` cache self-corrects and emits a new `worktree:pin-rederived` audit event, without consuming worktree-session retries. The recycle pool is never consulted in pinned mode. - **Mutual exclusivity:** enabling both `recycleWorktrees` and `worktreeNaming: "task-id"` is rejected at the settings-write boundary — HTTP 400 at `PUT /settings`, and an `Error` backstop in `store.updateSettings` covering the CLI and every other writer (`assertWorktreeNamingRecycleExclusive`). The runtime also gates pinned mode on `!recycleWorktrees`, so a legacy on-disk config carrying both degrades safely to recycling. - **Settings UI:** the Settings → Worktrees panel enforces the exclusivity bidirectionally — the *Recycle worktrees* toggle is disabled while naming is *Task ID*, and the naming select is disabled while recycling is on — so the conflicting state is unreachable, with help text explaining why. - **Byte-inert for the rest:** `random`/`task-title` naming and the recycle pool (incl. `merger.ts` release) are unchanged; worktrunk-managed layouts bypass pinning. ## Acceptance criteria (from the plan) 1. ✅ Pinned task dispatched N times only ever touches `<worktreesDir>/<task-id>` on its own branch 2. ✅ No code path can hand task A's dir to task B (pool bypassed; path derived from task id) 3. ✅ FN-7996 stale/foreign `task.worktree` self-corrects at next dispatch (`worktree:pin-rederived`) without consuming session retries 4. ✅ Non-pinned modes with `recycleWorktrees: true|false` are byte-identical (existing pool tests pass unchanged) 5. ✅ Stale same-name dir (crash leftover / archive→restore) reclaimed in place, never suffixed 6. ✅ Docs updated (settings-reference, architecture, `worktreeNaming` type doc); changeset (`minor`, `feature`); FNXC comments encode the invariant ## Files - `packages/engine/src/worktree-pinning.ts` — new pure helpers (`isTaskPinnedWorktreeNaming`, `pinnedWorktreePathForTask`) - `packages/engine/src/worktree-acquisition.ts` — pinned branch + branch-match reclaim-in-place - `packages/engine/src/run-audit.ts` — `worktree:pin-rederived` audit type - `packages/core/src/settings-validation.ts` (+ `index.ts`, `task-store/settings-ops.ts`) — mutual-exclusion validator + wiring - `packages/dashboard/src/routes/register-settings-memory-routes.ts` — 400 on conflict - `packages/dashboard/app/components/settings/sections/WorktreesSection.tsx` (+ `packages/i18n/locales/en/app.json`) — bidirectional UI exclusivity - `packages/core/src/types.ts`, `docs/*`, `.changeset/*` ## Verification - New tests: engine `worktree-pinning` (5) + `worktree-acquisition-pinned` (7); core `worktree-naming-recycle-exclusive` (2); dashboard settings-route 400 (3) + WorktreesSection UI exclusivity (3) - Regression sweep green: 194 engine worktree/acquisition/pool/executor/merger-release tests, core settings tests, dashboard i18n/settings-section tests - `tsc --noEmit` clean for `@fusion/core` and `@fusion/engine`; changed source files clean; eslint clean - `pnpm verify:fast` PASS (build + scoped typecheck + boot smoke) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Task ID worktree naming, providing each task with a stable, deterministic worktree directory. * Automatically reuses valid pinned worktrees and recreates stale or conflicting ones at the same path. * Added clear settings controls and validation for incompatible Task ID naming and worktree recycling options. * **Documentation** * Updated worktree architecture, settings reference, and in-app guidance to explain pinned worktrees and configuration constraints. * **Bug Fixes** * Improved recovery from stale or incorrect worktree assignments without consuming session retries. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f57dfc03b6 |
FN-8105: remove archived task worktrees safely
Archive task worktrees through a store-scoped, race-safe disposal lifecycle. - Reserve pinned worktree paths during archive cleanup and successor creation. - Reconcile quarantined removals before reusing a pinned path. - Gate PostgreSQL archival before destructive worktree disposal and wire CLI cleanup. Files changed: .changeset/fn-8105-archive-removes-worktree.md | 7 + docs/task-management.md | 4 + .../extension-experiment-finalize.test.ts | 1 + .../src/__tests__/extension-fn-secret-get.test.ts | 1 + .../extension-gitlab-tracking.test.ts | 1 + .../cli/src/__tests__/extension-web-fetch.test.ts | 1 + .../task-command-github-import-tracking.test.ts | 1 + packages/cli/src/commands/__tests__/task.test.ts | 1 + packages/cli/src/commands/task.ts | 8 +- packages/cli/src/extension.ts | 4 + .../__tests__/worktree-path-reservation.test.ts | 58 ++++++++ packages/core/src/archive-worktree-disposer.ts | 21 +++ packages/core/src/index.gate.ts | 13 ++ packages/core/src/index.ts | 13 ++ .../core/src/task-store/archive-lifecycle-2.ts | 8 ++ packages/core/src/task-store/archive-lifecycle.ts | 37 +++++ packages/core/src/worktree-path-reservation.ts | 149 +++++++++++++++++++++ .../src/archive-worktree-disposer-install.ts | 18 +++ packages/engine/src/executor.ts | 16 +++ packages/engine/src/index.ts | 2 + packages/engine/src/runtimes/in-process-runtime.ts | 1 + packages/engine/src/worktree-acquisition.ts | 27 +++- 22 files changed, 388 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-8105 Fusion-Task-Lineage: cabb8f52-093f-4986-bfda-2c7601a72579 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4e4b6be1b4 |
Plan-approval mailbox notification + triage Plan Review convergence (#2237)
## Summary Two related changes around the plan-approval flow, plus a fix for triage's plan-review loop that was stranding tasks at the replan cap. ### 1. Post a mailbox message when a plan needs approval (`feat`) The ntfy push on `awaiting-approval` already existed end-to-end. This adds the missing **durable, in-dashboard record**: a `system`-typed mailbox message linking to the task, written whenever a task enters `awaiting-approval`. - Fires **before** the notifications-enabled gate, so a dashboard-only operator (no ntfy/webhook configured) still gets the record — the whole point of the mailbox channel. - `system` type avoids re-triggering the `message:agent-to-user` ntfy pipeline (no double-notify); idempotent via `sendMessageOnce` (key `plan-approval:<taskId>`). ### 2. Help triage Plan Review converge before the replan cap (`fix`) Investigation of three tasks that burned all 8 plan-review replans without converging (**FN-7996, FN-8105, FN-8108**) found the reviewer surfaced a *new, deeper* issue each cycle instead of confirming its prior ones were fixed (goalpost movement), and reviewed specs at implementation altitude. This addresses the root causes: - **Feed the spec reviewer its own prior REVISE feedback + the 1-based replan attempt** so it verifies prior issues rather than moving goalposts. Gated to `reviewType === "spec"` and `attempt > 1` — **code review and normal plan review are byte-for-byte unaffected** (double-verified). - **Reviewer prompt:** converge-on-re-review rule (don't REVISE for your own earlier miss), severity ratchet (critical-only at attempt ≥ 3), and a **Spec Altitude** guard so exact SQL/lock/CAS protocol design is deferred to code review. - **Planner prompt:** front-load exhaustive surface enumeration before writing File Scope, and a storage-architecture ground-truth note (Postgres-only store, composite PK `(project_id, id)`, `schema-applier` migrations) to stop the repeated stale-fact REVISE rounds. ## Testing - `@fusion/core` + `@fusion/engine` typecheck clean. - Added coverage: reviewer spec-convergence wiring (attempt gating + code/plan exclusion + severity ratchet), triage prior-feedback derivation (incl. empty-output→notes fallback), mailbox decoupling (fires when push disabled) + rejection safety, and assertions for all new prompt sections. - Affected suites green: notification-service, reviewer, triage-plan-review-replan-cap, triage-replan-feedback, agent-prompts. - `pnpm check:changesets` passes (2 changesets: `@runfusion/fusion` minor + patch). ## Review Ran a 6-persona `ce-code-review` (correctness + adversarial on Opus; maintainability, testing, project-standards, api-contract). Guards verified unbreakable; no P0/P1 correctness or security issues. Applied the resulting fixes: decoupled the mailbox write from the push gate (P2), `??`→`||` in the feedback derivation (P3), de-duplicated the `specConvergence` ternary (P3), and closed the test-coverage gap the review flagged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Plans transitioning to **awaiting approval** now write an idempotent, task-linked **dashboard mailbox** message (approval reason + direct task link), even if push/notifications are disabled. - **Bug Fixes** - Plan Review **replan** behavior now better **converges** on prior REVISE feedback (including notes fallback) and stops looping at the replan cap. - At later attempts (attempt 3+), **REVISE** is applied to **critical** issues while lower-severity items shift to suggestions. - **Tests** - Added/expanded coverage for mailbox messaging and spec-convergence prompt wiring. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
2c3a777bca |
FN-8132: recover bare worktree branch collisions
Recover safe worktree creation when a dangling task branch already exists. - Classify bare branch collisions and preserve foreign or mixed commit history. - Reuse task-owned branches or recreate merged branches from the pinned start point. - Audit recovery outcomes and cover native, fallback, and workspace acquisition paths. Files changed: .../fn-8132-worktree-branch-collision-recovery.md | 7 ++ docs/architecture.md | 1 + .../__tests__/worktree-acquisition-backend.test.ts | 69 +++++++++++ .../worktree-acquisition-workspace.test.ts | 21 ++++ .../worktree-backend-branch-collision.test.ts | 132 +++++++++++++++++++++ packages/engine/src/branch-conflicts.ts | 121 +++++++++++++++++++ packages/engine/src/run-audit.ts | 1 + packages/engine/src/worktree-acquisition.ts | 3 +- packages/engine/src/worktree-backend.ts | 94 +++++++++++++++- 9 files changed, 447 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8132 Fusion-Task-Lineage: f09d140f-4fcd-48a6-99b1-a351630f37bd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
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> |
||
|
|
bd4857a44e |
FN-8136: update cron-runner backup error expectations
Align cron-runner backup failure assertions with the current PostgreSQL project-state error wording. - Update legacy command backup failure expectation - Update command-step backup failure expectation Files changed: packages/engine/src/__tests__/cron-runner.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8136 Fusion-Task-Lineage: 8f182491-1ded-43bb-b4ca-02485dce656a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
fdea8a4294 |
FN-8118: document continuation rescue test coverage
Document why the verified post-done continuation rescue suite remains unquarantined. - Record the serialized in-memory reliability coverage and its exclusion rationale. - Preserve the engine-default reliability partition exclusion. Files changed: packages/engine/vitest.config.ts | 2 ++ 1 file changed, 2 insertions(+) Fusion-Task-Id: FN-8118 Fusion-Task-Lineage: 0ac7f24a-888c-470c-b175-98b4e0411061 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
2142841f0c |
FN-8111: restore reliability test coverage
Restore PostgreSQL-compatible reliability coverage and prevent completed tasks from wedging on stale continuation recovery. - Update reliability fixtures and audit assertions for PostgreSQL-backed stores - Prioritize completed-task handling before stale assistant-continuation retries - Unquarantine the restored meta-archive and continuation reliability suites Files changed: .../explicit-duplicate-marker-sweep.test.ts | 4 ++++ .../meta-archive-guard-composition.test.ts | 26 +++++++++++++++++----- .../post-done-continuation-no-wedge.test.ts | 3 ++- packages/engine/src/executor.ts | 7 ++++++ packages/engine/vitest.config.ts | 4 ++-- scripts/lib/test-quarantine.json | 10 --------- 6 files changed, 36 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-8111 Fusion-Task-Lineage: 8b30b5cb-c160-44e1-8e8c-dd58f4877edc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
4a797d3804 |
FN-8117: restore explicit duplicate marker sweep coverage
Configure duplicate-marker PG fixtures with canonical FN task IDs so the sweep coverage exercises real deletion paths. - Set taskPrefix to FN for duplicate-marker reliability fixtures. - Remove the corrected test from the PG quarantine ledger and Vitest exclusions. - Document why valid marker IDs are required for this coverage. Files changed: .../explicit-duplicate-marker-sweep.test.ts | 20 +++++++++++++------- packages/engine/vitest.config.ts | 4 +++- scripts/lib/test-quarantine.json | 5 ----- 3 files changed, 16 insertions(+), 13 deletions(-) Fusion-Task-Id: FN-8117 Fusion-Task-Lineage: 3b09cbbe-924c-4e3c-849b-cf7643b0ac0e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
0b488523c2 |
FN-8104: retire legacy SQLite database fallbacks
Retire legacy SQLite database calls from PostgreSQL-only startup and self-healing paths. - Route plugin schema initialization exclusively through the PostgreSQL executor. - Delegate soft-delete column repair to the PostgreSQL reconciliation seam. - Remove temporary getDatabase allowlist entries and add no-SQLite regression coverage. Files changed: .../postgres/store-safe-defaults.pg.test.ts | 14 +++++- packages/core/src/store.ts | 24 ++++------ .../engine/src/__tests__/plugin-runner.test.ts | 6 --- .../self-healing-fake-overlap-seam.test.ts | 44 +++++++++++++++++++ packages/engine/src/self-healing.ts | 51 +++++----------------- scripts/lib/getdatabase-allowlist.json | 17 +------- 6 files changed, 77 insertions(+), 79 deletions(-) Fusion-Task-Id: FN-8104 Fusion-Task-Lineage: 88dee027-51c3-4c68-94dc-88191fe20330 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
aa07a78f18 |
fix: recover graph-node missing-worktree failures instead of terminal-parking (FN-7996) (#2231)
## Why FN-7996 sat in a dispatch→park loop **all day** (06:06→16:35): its `worktree` metadata pointed at recycled pool worktrees (`coral-badger`, `grand-ridge` — the latter actually belonged to FN-8069), Plan Review refused to start in the missing directory, and the task terminal-parked `failed` every cycle while the planner overseer blindly retried. Root-cause chain: 1. **`graphFailureValue()` couldn't read optional-group results.** `runOptionalGroup` publishes context under the group id (`node:plan-review:value`) and the unqualified template id, but the failed node is recorded as the materialized `plan-review::plan-review-step` — the lookup only understood `#` foreach ids. FN-7977's provider-failure hold *did* classify this failure, but its hold value was invisible to routing. 2. **No graph-failure router handled the `assertValidWorktreeSession` refusal**, so it fell to the terminal sink, which parked the task and *overwrote* `task.error` with a generic message — erasing the signature the missing-worktree self-healing sweep (in-review-only anyway) classifies on. 3. **Plan Review didn't need the worktree at all** — its spec is store-injected (FN-7561) — yet it launched its reviewer in whatever stale `task.worktree` said. ## What - `handleGraphFailure` routes unusable-worktree node failures (any node, any error key, `::`/`#` materialized ids) into the existing bounded worktree-session recovery: clear stale worktree/branch/session metadata, requeue to todo, budgeted by `worktreeSessionRetryCount`. An exhausted budget still falls through to the visible terminal park for human inspection. - `graphFailureValue` resolves `group::template` ids (group value first — it carries post-classification routing intent — then the unqualified template value). Foreach `#` behavior unchanged. - Plan Review falls back to the repo root when its recorded worktree is missing on disk; other read-only gates intentionally keep failing fast into the new recovery (silently retargeting them to root would review the wrong tree). - `recoverMissingWorktreeSessionStartFailure` returns its outcome so the graph router can distinguish requeue from escalate-exhausted; existing truthy callers unchanged. ## Symptom Verification - **Original symptom:** graph-node session-start refusal → `Workflow graph terminated with failure at node 'plan-review::plan-review-step'`, task parked failed with stale metadata intact, no recovery. - **Reproduction:** `graph-node-missing-worktree-recovery.test.ts` drives `handleGraphFailure` with the exact FN-7996 result shape (optional-group materialized id + `Refusing to start coding agent in missing worktree` node error). - **Assertion it is gone:** the task is requeued to `todo` with `worktree`/`branch`/`sessionFile` cleared and retry budget incremented — and is *not* marked `failed`; budget exhaustion still parks visibly. ## Surface Enumeration - Optional-group template nodes (Plan Review — the repro), write-capable review gates, and any custom graph node: covered by the `handleGraphFailure` router (scans exact/materialized/unqualified `:error` keys). - Execute-seam session start: already covered by the pre-existing recovery (unchanged, still passes). - In-review / merge-active columns: already covered by self-healing sweeps (unchanged). - Paused / user-paused / deleted / done tasks: explicitly left to their owning machinery (guard tests). - Budget exhaustion: falls through to the visible terminal park (test). ## Testing - `pnpm --filter @fusion/engine exec vitest run src/__tests__/reliability-interactions/graph-node-missing-worktree-recovery.test.ts` — 13 passed - Adjacent suites (`worktree-incomplete-session-start`, `executor-graph-requeue-gate`, `workflow-graph-optional-group`, `executor-paused-abort-todo-benign`) — 78 passed - `tsc --noEmit` on `@fusion/engine` — clean 🤖 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** * Improved recovery when workflow tasks encounter missing or recycled worktrees. * Automatically retries affected tasks with stale worktree details cleared, up to the configured retry limit. * Escalates tasks after recovery attempts are exhausted. * Improved failure routing for optional workflow groups and template instances. * Plan Review now falls back to the repository root when its recorded worktree is unavailable. * **Tests** * Added regression coverage for recovery, routing, retry limits, and repository-root fallback behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
1a337df5e9 |
FN-8098: add executor model fallback
Add workflow-specific executor fallback configuration and bounded recovery. - Add executor fallback provider, model, and thinking settings across core schemas and settings UI. - Route executor, heartbeat, child, and workflow-step sessions through the executor fallback resolver. - Retry the primary model after fallback failure before reporting terminal exhaustion. Files changed: .changeset/fn-8098-model-fallback.md | 7 +++ docs/settings-reference.md | 7 ++- .../core/src/__tests__/model-resolution.test.ts | 13 ++++ .../core/src/__tests__/settings-parity.test.ts | 5 ++ packages/core/src/builtin-workflow-settings.ts | 24 +++++++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 21 +++++++ packages/core/src/settings-schema.ts | 3 + packages/core/src/types.ts | 11 ++++ .../app/components/WorkflowSettingsPanel.tsx | 8 +++ .../settings/sections/ProjectModelsSection.tsx | 10 ++- packages/engine/src/__tests__/pi.test.ts | 14 ++++- packages/engine/src/agent-session-helpers.ts | 7 ++- packages/engine/src/executor.ts | 48 +++++++------- packages/engine/src/pi.ts | 73 ++++++++-------------- packages/engine/src/step-session-executor.ts | 8 ++- 17 files changed, 180 insertions(+), 81 deletions(-) Fusion-Task-Id: FN-8098 Fusion-Task-Lineage: 61b3103b-357b-431a-8d58-411e7806b87b Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
14953f39e2 |
FN-8075: restore PostgreSQL self-healing test coverage
Restore self-healing coverage for PostgreSQL-backed maintenance. - Update self-healing mocks and assertions for asynchronous audit APIs and PostgreSQL WAL behavior - Align git command expectations and transient recovery budget coverage with current implementation - Remove the repaired self-healing suite from the quarantine ledger and gate exclusion Files changed: packages/engine/src/__tests__/self-healing.test.ts | 70 ++++++++++++---------- packages/engine/vitest.config.ts | 1 - scripts/lib/test-quarantine.json | 5 -- 3 files changed, 39 insertions(+), 37 deletions(-) Fusion-Task-Id: FN-8075 Fusion-Task-Lineage: 7dfce9b0-9d10-4d70-8e92-8100f6595fce Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
40687cb0be |
FN-8050: cover cached dependency reconciliation seeding
Ensure raw dependency-reconciliation fixtures invalidate warmed task caches. - Warm slim task-list caches before corrupt-row seeding in cycle reconciliation tests. - Document the PostgreSQL-only fixture seam and cache invalidation rationale. Files changed: .../engine/src/__tests__/reliability-interactions/_helpers.ts | 11 ++++++----- .../dependency-cycle-reconcile.test.ts | 7 +++++++ 2 files changed, 13 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8050 Fusion-Task-Lineage: 5e708d58-faed-4271-a075-24c36eaa5878 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
50ed689379 |
FN-8044: restore dependency reconcile reliability tests
Restore dependency reconciliation coverage using a PostgreSQL raw-seeding seam. - Add a cache-invalidating raw task-column seeding helper for corrupt fixture states. - Update dependency-cycle and self-defeating reconciliation tests to use the PG seam. - Remove restored suites from the reliability quarantine configuration and ledger. Files changed: .../__tests__/reliability-interactions/_helpers.ts | 29 ++++++++ .../dependency-cycle-reconcile.test.ts | 86 +++++++++++----------- .../self-defeating-dep-reconcile.test.ts | 7 +- packages/engine/vitest.config.ts | 7 +- scripts/lib/test-quarantine.json | 10 --- 5 files changed, 82 insertions(+), 57 deletions(-) Fusion-Task-Id: FN-8044 Fusion-Task-Lineage: 213f606c-1951-4d25-93b4-2ceb97460ace Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
969fce7aa1 |
FN-8047: migrate AgentStore multi-node tests to PostgreSQL
Migrate multi-node AgentStore coverage to shared PostgreSQL-backed fixtures. - Make concurrent central claim insertion resolve unique-key races as checkout conflicts. - Rework claim and owning-node handoff tests to use shared async PostgreSQL layers. - Restore PostgreSQL-compatible tests from the quarantine ledger. Files changed: packages/core/src/async-central-db.ts | 9 ++- .../cross-node-claim-mutex.integration.test.ts | 72 ++++++++++--------- .../distributed-claim-mutex.integration.test.ts | 27 +++---- .../owning-node-handoff.integration.test.ts | 41 +++++------ .../__tests__/reliability-interactions/_helpers.ts | 83 ++++++++++++++++++++-- .../multi-node-claim-mutex-interactions.test.ts | 28 +++----- .../owning-node-unavailable-interactions.test.ts | 36 +++++----- packages/engine/vitest.config.ts | 8 +-- scripts/lib/test-quarantine.json | 25 ------- 9 files changed, 180 insertions(+), 149 deletions(-) Fusion-Task-Id: FN-8047 Fusion-Task-Lineage: 3b7ee21e-0190-4364-a0cb-88aac5e2e1a3 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
19ab7a9e93 |
FN-8068: add input-language task definitions
Add an opt-in setting that localizes planner-authored task-definition prose. - Detect confident Spanish, French, Korean, and Chinese input before instructing triage localization. - Expose and persist the project-level task-definition language toggle with translations and search support. - Preserve English structural markers, code, and unsupported or uncertain input for deterministic parsing. Files changed: .../fn-8068-task-definition-input-language.md | 7 +++ docs/settings-reference.md | 1 + packages/core/src/settings-schema.ts | 7 +++ packages/core/src/types.ts | 10 ++++ .../app/__tests__/settings-sections.test.tsx | 18 ++++++ .../dashboard/app/components/SettingsModal.tsx | 4 ++ .../__tests__/SettingsModal.mobileClose.test.tsx | 12 ++++ .../__tests__/SettingsModal.models-auth.test.tsx | 11 ++++ .../app/components/settings/section-keys.ts | 1 + .../sections/ProjectModelsSection.search.ts | 10 ++++ .../settings/sections/ProjectModelsSection.tsx | 17 ++++++ .../settings-default-descriptions.test.tsx | 1 + packages/engine/src/__tests__/triage.test.ts | 64 ++++++++++++++++++++++ packages/engine/src/triage.ts | 29 +++++++++- packages/i18n/locales/en/app.json | 2 + packages/i18n/locales/es/app.json | 2 + packages/i18n/locales/fr/app.json | 2 + packages/i18n/locales/ko/app.json | 2 + packages/i18n/locales/zh-CN/app.json | 2 + packages/i18n/locales/zh-TW/app.json | 2 + packages/i18n/src/resources.d.ts | 2 + 21 files changed, 205 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8068 Fusion-Task-Lineage: fe0102f5-68cb-443c-8512-2ffa5fe0e2f8 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
5b4ec4c08f |
FN-8042: add merger fallback model lane
Add configurable project fallback models for AI merger retries. - Resolve complete project merger fallback pairs before the shared global fallback. - Expose merger fallback model and thinking controls in Project Models with reset-aware persistence. - Apply the fallback lane across merger, review, PR-response, and recovery sessions with tests and documentation. Files changed: .changeset/fn-8042-merger-fallback-model.md | 7 +++ docs/settings-reference.md | 4 +- .../core/src/__tests__/model-resolution.test.ts | 24 ++++++++++ .../core/src/__tests__/settings-parity.test.ts | 3 ++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/model-resolution.ts | 22 +++++++++ packages/core/src/settings-schema.ts | 4 ++ packages/core/src/types.ts | 13 ++++++ .../app/__tests__/settings-save-split.test.ts | 41 +++++++++++++++++ .../app/__tests__/settings-sections.test.tsx | 36 +++++++++++++++ .../app/components/settings/save-split.ts | 7 +-- .../settings/sections/ProjectModelsSection.tsx | 53 +++++++++++++++++++++- .../settings-default-descriptions.test.tsx | 3 ++ .../src/__tests__/agent-session-helpers.test.ts | 1 + .../__tests__/mcp-pr-response-forwarding.test.ts | 1 + packages/engine/src/agent-session-helpers.ts | 10 +++- packages/engine/src/merger-ai.ts | 17 +++++-- packages/engine/src/merger.ts | 43 +++++++++++++----- packages/engine/src/pr-response-run-ops.ts | 11 +++-- 20 files changed, 276 insertions(+), 26 deletions(-) Fusion-Task-Id: FN-8042 Fusion-Task-Lineage: 31762a72-461e-438c-a12d-2816580283fd Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3f133e0b13 |
FN-8058: add task agent log reader
Expose paginated, filterable persisted agent logs to task-scoped and chat agent sessions. - Add the read-only fn_task_logs_read tool across engine, dashboard chat/planning, heartbeat, step, and CLI extension surfaces. - Filter agent-log entries before pagination, report matching totals, and render complete persisted rows for diagnosis. - Document the tool, add release metadata, regression coverage, and complete affected engine mocks. Files changed: .changeset/fn-8058-task-logs-read.md | 7 ++ docs/agents.md | 4 +- packages/cli/skill/fusion/SKILL.md | 2 +- .../cli/skill/fusion/references/extension-tools.md | 11 +++ .../skill/fusion/references/fusion-capabilities.md | 1 + .../extension-experiment-finalize.test.ts | 2 + .../src/__tests__/extension-fn-secret-get.test.ts | 2 + .../__tests__/extension-gitlab-tracking.test.ts | 2 + .../src/__tests__/extension-integration.test.ts | 1 + .../cli/src/__tests__/extension-web-fetch.test.ts | 2 + packages/cli/src/__tests__/extension.test.ts | 1 + packages/cli/src/extension.ts | 34 ++++++++ .../src/__tests__/agent-logs-backend-mode.test.ts | 28 +++++- packages/core/src/store.ts | 11 ++- packages/core/src/task-store/remaining-ops-7.ts | 16 +++- packages/core/src/types.ts | 1 + packages/dashboard/src/__tests__/chat.test.ts | 1 + .../planning-answered-question-reemit.test.ts | 1 + .../planning-generation-cancellation.test.ts | 1 + packages/dashboard/src/chat.ts | 5 ++ packages/dashboard/src/planning.ts | 3 + .../__tests__/agent-task-logs-read-tools.test.ts | 72 ++++++++++++++++ packages/engine/src/agent-heartbeat.ts | 4 +- packages/engine/src/agent-tools.ts | 99 +++++++++++++++++++++- packages/engine/src/executor.ts | 6 ++ packages/engine/src/gating-classifications.ts | 2 + packages/engine/src/index.ts | 6 ++ packages/engine/src/step-session-executor.ts | 6 +- 28 files changed, 316 insertions(+), 15 deletions(-) Fusion-Task-Id: FN-8058 Fusion-Task-Lineage: 74f198b2-f538-4b39-973f-431f22e68f29 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
c2475b012d |
FN-8064: add proactive task chat status updates
Task-detail chat now narrates engine progress and review outcomes in real time. - Emit bounded, redacted status rows for step lifecycle and review paths. - Present status entries with a distinct task-chat treatment. - Cover status narration and diagnostic sanitization with engine tests. Files changed: .changeset/fn-8064-proactive-chat.md | 7 + docs/architecture.md | 1 + packages/dashboard/app/components/TaskChatTab.css | 16 ++ packages/dashboard/app/components/TaskChatTab.tsx | 9 +- .../engine/src/__tests__/executor-prompt.test.ts | 28 +++- .../engine/src/__tests__/proactive-status.test.ts | 54 +++++++ packages/engine/src/executor.ts | 176 ++++++++++++++++----- packages/engine/src/proactive-status.ts | 117 ++++++++++++++ 8 files changed, 365 insertions(+), 43 deletions(-) Fusion-Task-Id: FN-8064 Fusion-Task-Lineage: c6d0a9b5-0946-4bf4-8338-e982e1cbfd53 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
274318aebf |
FN-8056: enforce task token budgets
Enforce configured task token budgets whenever session usage is persisted. - Apply soft alerts and hard pauses atomically from all executor persistence paths. - Exclude cache-read tokens from budget usage and dispatch budget notifications once. - Document budget semantics and add regression coverage. Files changed: .changeset/fn-8056-token-budget-enforcement.md | 7 ++ docs/settings-reference.md | 2 + packages/core/src/types.ts | 4 +- .../src/__tests__/session-token-usage.test.ts | 101 ++++++++++++++++++++- .../src/__tests__/token-budget-enforcer.test.ts | 81 ++++++++++------- packages/engine/src/executor.ts | 22 ++++- packages/engine/src/session-token-usage.ts | 8 +- packages/engine/src/token-budget-enforcer.ts | 98 +++++++++++++++++--- 8 files changed, 262 insertions(+), 61 deletions(-) Fusion-Task-Id: FN-8056 Fusion-Task-Lineage: 5f5ed522-f950-42ce-b4fd-e0b1d45b5815 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
e46ffebde1 |
FN-7985: surface review budget exhaustion and configure replan cap
Expose exhausted Plan Review replan budgets for operator approval and allow workflows to configure the cap. - Add validated numeric workflow setting support and a Plan Review replan-cap setting. - Route configured cap exhaustion with a distinct approval reason and preserve fallback behavior. - Display the budget-exhaustion state across task cards, lists, and details. - Add tests, localized copy, documentation, and a minor changeset. Files changed: .changeset/fn-7985-review-budget-approval.md | 7 ++++ docs/settings-reference.md | 9 +++-- docs/workflow-steps.md | 2 +- .../builtin-workflow-settings-triage.test.ts | 27 +++++++++++++-- packages/core/src/builtin-workflow-settings.ts | 17 ++++++++++ packages/core/src/index.gate.ts | 1 + packages/core/src/index.ts | 1 + packages/core/src/workflow-ir-types.ts | 4 +++ packages/core/src/workflow-ir.ts | 27 +++++++++++++++ packages/core/src/workflow-settings-resolver.ts | 1 + packages/core/src/workflow-settings.ts | 6 ++++ packages/dashboard/app/components/ListView.css | 19 +++++++++++ packages/dashboard/app/components/ListView.tsx | 22 +++++++++--- packages/dashboard/app/components/TaskCard.css | 18 ++++++++++ packages/dashboard/app/components/TaskCard.tsx | 6 ++-- .../dashboard/app/components/TaskDetailModal.tsx | 4 +-- .../app/components/__tests__/ListView.test.tsx | 30 +++++++++++++++++ .../app/components/__tests__/TaskCard.test.tsx | 17 ++++++++-- .../app/components/workflow-setting-display.ts | 11 ++++++ .../dashboard/app/utils/reviewBudgetApproval.ts | 11 ++++++ .../triage-plan-review-replan-cap.test.ts | 39 +++++++++++++++++++--- packages/engine/src/triage.ts | 21 ++++++++---- packages/i18n/locales/en/app.json | 2 +- packages/i18n/src/resources.d.ts | 19 ++++++++--- 24 files changed, 288 insertions(+), 33 deletions(-) Fusion-Task-Id: FN-7985 Fusion-Task-Lineage: 125f101c-caca-45c2-8b40-996b2a31c019 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
a31c370375 |
FN-8045: add transactional handoff failure-injection seam
Ensure PostgreSQL review handoffs roll back all dependent writes after an injected late failure. - Add a test-only failure injector after transactional handoff writes. - Include workflow work in same-column retry transactions. - Restore PG-backed handoff atomicity coverage and remove its quarantine. Files changed: packages/core/src/store.ts | 24 +++ packages/core/src/task-store/moves.ts | 21 ++- .../in-review-handoff-atomic.test.ts | 172 +++++++++++++-------- packages/engine/vitest.config.ts | 1 - scripts/lib/test-quarantine.json | 5 - 5 files changed, 151 insertions(+), 72 deletions(-) Fusion-Task-Id: FN-8045 Fusion-Task-Lineage: 517e3000-9b88-4b0d-9b25-1a585eb8f322 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9a34862586 |
refactor: package code organization waves 3–5 (#2148)
## Summary Waves 3–5 of package code organization (plan: `docs/plans/2026-07-14-001-refactor-package-code-organization-plan.md`). Behavior-preserving peels after #2139 and #2143. ### Wave 3 — Merger + heartbeat recovery - **`merger-errors.ts`** — verification/abort error classes - **`merger-owned-landed.ts`** — ownership classification + `Fusion-Task-Id` trailer - **`merger-conflict-resolution.ts`** — conflict classify/auto-resolve - **`agent-heartbeat-error-recovery.ts`** — durable error-recovery budget helpers ### Wave 4 — Self-healing + dashboard API - **`self-healing-constants.ts`** — public timing/budget constants - **`self-healing-branch.ts`** — `isBranchAheadOfBase` - **`app/api/client.ts`** — `api` / `ApiRequestError` / `buildApiUrl` / `proxyApi` - **`app/api/health.ts`** — health, engine status, updates + `withProjectId` ### Wave 5 — Types tracking + merger parse + task CRUD - **`types/task-tracking.ts`** — PR/issue/GitHub/GitLab tracking contracts - **`merger-git-parse.ts`** — `parseFailingFilesFromOutput`, `parsePorcelainZ`, `parseShortstatSummary` - **`app/api/tasks.ts`** — task list/detail/create/update/move client surface - Line-count baselines ratcheted down for `merger.ts`, `types.ts`, `legacy.ts` Public import paths stay on parent modules / `legacy.ts` / package barrels via re-exports. ## Test plan - [x] core/engine/dashboard typecheck (including `tsconfig.app.json`) - [x] eslint on touched modules - [x] `parse-porcelain-z` + merger parseFailing/getBranchChanged tests - [x] dashboard `api-tasks` + legacy-prinfo/pr-types (69) - [ ] CI merge gate <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added dashboard API support for task listing/detail, archiving, creation, review updates, duplicate detection, bulk model updates, moving tasks, and overlap repair. - Added health/engine status and refresh/start controls, plus update checking. - **Bug Fixes** - Improved dashboard API handling for non-JSON/HTML responses with clearer errors, better URL routing for remote nodes, and project-scoped queries. - Strengthened automated recovery for heartbeat error/model-unavailable scenarios and safer merge-conflict classification/auto-resolution. - **Tests** - Updated merge-conflict resolution and lifecycle test mocks to match the updated git command behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1d6a0449ce |
fix(engine): keep replan cards plannable when they carry prior steps
Tasks piled up in the Planning column and never moved. hasAdvancedPastPlanning counted steps.length > 0 as proof a card had advanced past planning, but a replan card legitimately retains the steps its previous planning pass materialized. The still-in-planning guard therefore failed for every card Plan Review sent back, so triage's specifyTask claim silently skipped its status:"planning" write and re-claimed the same cards every poll — never planning them, and starving healthy cards out of the maxTriageConcurrent slots they held. Steps are no longer advancement evidence while a card sits in a planner lane: the "triage" column, and the merged "todo" planner lane used by plan-in-place workflows when the card carries a planning status. Worktrees and execution/terminal columns remain durable advancement evidence, preserving FN-7977's protection against a recovery write clobbering a card that raced ahead into execution. The primary claim path now warns instead of returning silently; recovery-write skips stay silent by design. The silence is why this stalled the planner for hours undiagnosed. Regression coverage asserts the invariant across both planner surfaces rather than the reported repro alone: triage cards with and without an explicit needs-replan status, plan-in-place todo replans, every parked-for-planning status, and the advancement signals that must still fire. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7b31d54b98 |
fix(FN-8004): make AI merge rejections actionable and stranded merges retryable (#2160)
Two follow-ups to FN-8004. Both were found by watching FN-8004's *own* merge livelock for 40 minutes — it turned out to be blocked by the very class of bug it was filed to fix. ## 1. AI merge rejections lost their reasons The reviewer prompt said **both** of these: > "**End with a single decision line**: `REVIEW_VERDICT: approve|reject`" > "**Then list each concrete reason as a bullet.**" Those are impossible to satisfy at once. Reviewers obeyed "End with" and wrote their reasoning *above* the verdict — but `extractRejectReasons` only scanned lines *after* it. So every such rejection collapsed to the placeholder `reviewer rejected the merge without a stated reason`, and that placeholder was then handed to the corrective re-merge pass **as its instruction**. The pass got no actionable feedback and just re-rolled the merge. The evidence, from FN-8004's own merge — the pattern repeated across *both* attempts: | | Attempt A | Attempt B | |---|---|---| | review pass 1 | rejected, no reason (03:46) | rejected, no reason (03:57) | | corrective pass | 1/3 | 1/3 | | review pass 2 | **approved** `a3a3cc6a8` (03:49) | approved | A reviewer that rejects and then approves identical content isn't objecting — the reason was being thrown away. Each wasted cycle cost ~7 minutes, stretching the merge past main's ~8-minute churn window so every attempt lost to a concurrent advance and rebuilt. **The livelock was caused by the lost-reason bug.** Fix: the parser recovers reasons from either side of the verdict (inline → after → before, nearest-first so the closing argument leads, capped at 8 so a long transcript can't flood the corrective prompt), skipping severity/verdict/markdown scaffolding. The prompt ordering is now unambiguous — reasons first, verdict last, nothing after it. ## 2. An orphaned merge-active stamp was un-retryable by hand The Retry gate refused **every** merge-active status (`Task is not in a retryable state (current status: landing)`), while self-healing cleared stale stamps automatically minutes later. So a merger killed mid-flight — crash, engine restart, operator SIGTERM — blocked the operator's own escape hatch at exactly the moment they'd reach for it. FN-8004 hit this: a killed merge left `landing` stamped and Retry 400'd for the full sweep delay. `isStaleMergeActiveStatus` now lives in the leaf `merge-active-status.ts`, shared by `recoverStaleMergingStatus` and the Retry gate — so **the manual path can never be stricter than the automatic one**. This is the same one-concept-two-definitions bug as FN-8004's transient classifier, which is why it's worth fixing structurally rather than adding another special case. A live merge stays protected by two independent signals: it holds the in-process lease **and** refreshes `updatedAt` each phase. Staleness fails closed on an unparseable timestamp. One subtlety worth reviewing: the bypass feeds `isInReviewRetry` rather than only the gate. A bare gate bypass would fall through to the generic branch and move fully-executed work to `todo`, **re-running finished work** — a bug this fix could easily have introduced. ## Verification - Gate green (294 + 122 + 63) · lint clean · engine + dashboard typecheck clean · `verify:fast` PASS - 70 merger-suite tests green; all 7 pre-existing verdict-parser tests still pass (backward compatible — none of them covered the verdict-last layout, which is exactly why this shipped) - **The route regression test was confirmed non-vacuous**: neutralizing the fix fails the two "now retryable" cases while the three live-merge-protection cases still pass, proving they guard real behavior rather than the new code. - Regression tests assert the invariant across every surface per *Fix the Invariant, Not the Repro*: all five `ACTIVE_MERGE_STATUSES` (a merger can die in any phase, not just the reported `landing`), both live-merge signals, boundary conditions, fail-closed paths, and that pre-existing retry paths are unchanged. Test files carry the required `## Symptom Verification` and `## Surface Enumeration` sections. 🤖 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** * AI merge rejections now reliably include concrete, correctly ordered reasons, even when provided before the verdict line. * Manual retry can recover tasks stuck in stale merge-processing states. * Retry is still blocked for tasks tied to active merge activity or recently updated/advancing merges. * Existing failed-merge retry behavior remains unchanged. * **Reliability** * Improved shared handling of “orphaned” merge-active detection across the engine and dashboard. * **Tests** * Added/expanded coverage for merge-active staleness, retry eligibility, and verdict/reason parsing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
959a7877c8 |
FN-8020: harden agent heartbeat health classification
Validate the four-interval heartbeat grace window and classify invalid persisted heartbeats safely. - Cover reported field heartbeat ages in dashboard and engine health checks. - Mark unparseable heartbeat timestamps as unresponsive and clamp future timestamps to fresh. - Align dashboard health documentation with the existing four-interval grace window. Files changed: .../app/utils/__tests__/agentHealth.test.tsx | 48 +++++++++++++++++++++- packages/dashboard/app/utils/agentHealth.tsx | 26 ++++++++++-- .../src/__tests__/heartbeat-executor.test.ts | 31 ++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8020 Fusion-Task-Lineage: 2bc0df78-d68c-489b-8bfb-9b09da10cdfa Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
9d791b3bf1 |
FN-8009: preserve approved plans through prompt hygiene
Keep manually approved plans idempotent when deterministic prompt hygiene is applied. - Document normalized fingerprint comparison at the approval gate - Cover approval reuse after Original Description and Frontend UX injection Files changed: packages/engine/src/__tests__/triage.test.ts | 37 ++++++++++++++++++++++++++++ packages/engine/src/triage.ts | 8 ++++++ 2 files changed, 45 insertions(+) Fusion-Task-Id: FN-8009 Fusion-Task-Lineage: 8474bc13-61e0-421a-8e99-080f99382285 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |
||
|
|
3dcb62f40f |
FN-8008: normalize plan approval fingerprints
Keep approval recovery idempotent when deterministic prompt hygiene is injected. - Normalize plan approval fingerprints around Original Description and Frontend UX sections. - Preserve re-approval for operator-authored plan changes and cover recovery behavior. - Document the normalization contract and add a patch changeset. Files changed: .changeset/fn-8008-plan-approval-fingerprint.md | 7 +++ docs/workflow-steps.md | 2 +- packages/core/src/__tests__/plan-approval.test.ts | 53 +++++++++++++++- packages/core/src/plan-approval.ts | 73 ++++++++++++++++++++++- packages/engine/src/__tests__/triage.test.ts | 45 ++++++-------- packages/engine/src/triage.ts | 40 ++----------- 6 files changed, 153 insertions(+), 67 deletions(-) Fusion-Task-Id: FN-8008 Fusion-Task-Lineage: 9c0f415d-662a-455a-a4bd-b873307e53bc Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> |