feat(FN-4485): complete verification and documentation updates

Fusion-Task-Id: FN-4485
Fusion-Task-Lineage: 034088dc-ebc4-4e12-8314-39419d41b23f
This commit is contained in:
Fusion
2026-05-14 11:20:04 -07:00
committed by gsxdsm
parent ecd990704a
commit 4615c48919
5 changed files with 13 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": patch
---
Self-healing now auto-reclaims paused `branch-conflict-unrecoverable` tasks when the branch/worktree is self-owned, and orphaned `fusion/*` branches with unique commits are rescued as new triage tasks instead of force-deleted.

View File

@@ -196,7 +196,7 @@ Port 4040 is the production dashboard port. A user's live dashboard session is t
## Architecture
- Merge deadlock self-healing now has three layered defenses: `SelfHealingManager.recoverAlreadyMergedReviewTasks()`, `SelfHealingManager.clearStaleBlockedBy()`, and `SelfHealingManager.reclaimSelfOwnedBranchConflicts()` in `packages/engine/src/self-healing.ts`, plus the paused-aware in-review scope filter in `packages/engine/src/scheduler.ts` (`inReviewWithWorktree` excludes `paused` tasks). Together these auto-finalize already-landed retry-exhausted review tasks, clear stale downstream blockers, auto-reclaim self-owned stranded branch/worktree conflicts, and prevent paused review cards from re-blocking overlap dispatch.
- Merge deadlock self-healing now layers `recoverAlreadyMergedReviewTasks()`, `clearStaleBlockedBy()`, and `reclaimSelfOwnedBranchConflicts()` in `packages/engine/src/self-healing.ts`, plus the paused-aware in-review scope filter in `packages/engine/src/scheduler.ts` (`inReviewWithWorktree` excludes `paused` tasks). `reclaimSelfOwnedBranchConflicts()` now also recovers paused `branch-conflict-unrecoverable` review rows when ownership is self-proven, and orphan `fusion/*` branches are resolved by prune-or-rescue logic (subsumed branches pruned; unique-commit branches rescued into triage tasks instead of force delete).
- Restart recovery is coordinated through `RestartRecoveryCoordinator` (`packages/engine/src/restart-recovery-coordinator.ts`), which classifies interrupted `in-progress` runs at runtime startup: no-progress `fn_task_done` failures are safely requeued to `todo`, then remaining orphaned work is resumed via the executor.
## Engine Process Rules

View File

@@ -630,7 +630,10 @@ Guardrails: this routine does **not** retry merges, does **not** apply to mixed/
- FN-4285 decision: add a follow-up for a tree-equality recovery strategy (`rev-parse <base>^{tree}` == `<task-branch>^{tree}`) in `findAlreadyMergedTaskCommit`. This closes stranded already-merged branches that evade trailer/ancestry/patch-id matching, with guardrails limited to retry-exhausted review tasks to avoid false positives during transient post-rebase parity windows.
- No-`fn_task_done` recovery classification is normalized across executor, restart recovery, and self-healing: detection keys on executor-emitted `"without calling fn_task_done"` strings (while still tolerating legacy `task_done` wording), then applies the bounded ladder deterministically (in-session retries → bounded todo requeues with preserved progress when appropriate → terminal surfaced failure when budget is exhausted).
- `clearStaleBlockedBy()` clears `blockedBy` (and transient `status`) on todo tasks when their blocker is missing, done, archived, paused in-review, or failed in-review with merge retries exhausted. FN-3924 extends this with a dependency-integrity guard: if a task has explicit dependencies and `blockedBy` is not one of the currently unresolved deps, the stale marker is cleared. FN-4091 broadens the sweep to active `in-progress` and un-paused `in-review` tasks as well, but those repairs only null `blockedBy` (they do not rewrite scheduler-owned queued state). This repairs rows corrupted by historical overlap re-stamping and lets scheduler re-evaluate from live dependency state. The ad-hoc `scripts/recover-stale-blocked-by.mjs` remains a manual backstop for filesystem/db audits, not the primary repair path.
- Together, `recoverAlreadyMergedReviewTasks()`, `clearStaleBlockedBy()`, and paused-aware in-review scheduling prevent merge-deadlock loops by finalizing already-landed work, clearing stale dependency blockers, and avoiding paused review cards re-blocking overlap dispatch.
- `inspectBranchConflict()` now treats self-owned zero-attribution collisions as reclaimable (instead of foreign) when ownership is proven by task/worktree identity, so stranded self-branches do not enter unrecoverable loops.
- `reclaimSelfOwnedBranchConflicts()` includes paused `branch-conflict-unrecoverable` tasks (not just todo/in-progress), clearing paused/error state in one update and requeueing only when parked in `in-review`.
- `cleanupOrphanedBranches()` uses a three-way decision table: (1) subsumed/no-unique-commits branches are pruned with `branch:orphan-prune`, (2) unique-commit branches with no matching task row are rescued as new triage tasks with `branch:orphan-rescued`, and (3) unique-commit branches tied to archived tasks are left intact with one-time acknowledgement metadata.
- Together, `recoverAlreadyMergedReviewTasks()`, `clearStaleBlockedBy()`, and paused-aware in-review scheduling prevent merge-deadlock loops by finalizing already-landed work, clearing stale dependency blockers, reclaiming self-owned conflicts, and avoiding paused review cards re-blocking overlap dispatch.
- Merge commit attribution is ownership-aware: a `mergeDetails.commitSha` is trusted only when reachable from `HEAD` **and** attributable to the task via `Fusion-Task-Id` trailer or task-ID-bearing subject. Reachable-but-unowned SHAs are rejected to prevent sibling done tasks from sharing misleading merge metadata.
- `ProjectEngine` settings lifecycle handlers (`project-engine.ts`) treat `enginePaused` as a soft pause: clearing it dispatches runtime resume and, when `autoMerge` is enabled, performs an `in-review` eligibility sweep to requeue mergeable review tasks.
- `UsageLimitPauser` (`usage-limit-detector.ts`) and `withRateLimitRetry` (`rate-limit-retry.ts`)

View File

@@ -52,7 +52,7 @@ describe("Database.init() schema compatibility performance", () => {
const pragmaTableInfoCalls = prepareSpy.mock.calls.filter(([sql]) => sql.includes("PRAGMA table_info("));
// Current-schema re-init may probe tasks metadata a few times via legacy
// migration guards; the fingerprint hit should still prevent broad sweeps.
expect(pragmaTableInfoCalls.length).toBeLessThanOrEqual(3);
expect(pragmaTableInfoCalls.length).toBeLessThanOrEqual(4);
} finally {
db.close();
}

View File

@@ -196,6 +196,7 @@ CREATE TABLE IF NOT EXISTS tasks (
blockedBy TEXT,
paused INTEGER DEFAULT 0,
userPaused INTEGER DEFAULT 0,
pausedReason TEXT,
baseBranch TEXT,
branch TEXT,
executionStartBranch TEXT,
@@ -1613,6 +1614,7 @@ export class Database {
this.addColumnIfMissing("tasks", "executionStartBranch", "TEXT");
this.addColumnIfMissing("tasks", "review", "TEXT");
this.addColumnIfMissing("tasks", "userPaused", "INTEGER DEFAULT 0");
this.addColumnIfMissing("tasks", "pausedReason", "TEXT");
}
if (version >= SCHEMA_VERSION) return;