Files
fusion/docs/run-audit.md
Fusion Agent a786c45bb9 FN-149: converge review cycles before human escalation
Add bounded review convergence so disputed review gates can resolve independently before requiring operator intervention.

- Add staged remediation, arbitration, dispute tooling, and stale-attempt fencing.
- Preserve review history and findings while exposing convergence state in core, dashboard, and audit paths.
- Add migration, configuration, documentation, changeset, and regression coverage.

Files changed:
 .changeset/fn-149-review-convergence.md            |   7 +
 AGENTS.md                                          |   2 +
 docs/architecture.md                               |   4 +
 docs/dashboard-guide.md                            |   4 +
 docs/run-audit.md                                  |   4 +
 docs/settings-reference.md                         |   4 +
 docs/workflow-steps.md                             |   8 +
 .../src/__tests__/default-workflow-hooks.test.ts   |  13 +
 packages/core/src/__tests__/plan-approval.test.ts  |  21 ++
 .../src/__tests__/reopen-semantics-by-role.test.ts |  25 ++
 packages/core/src/__tests__/review-severity-gate.test.ts     |  18 +-
 packages/core/src/__tests__/workflow-step-results.test.ts    |  52 +++-
 packages/core/src/config/settings-schema.ts        |   6 +
 packages/core/src/index.gate.ts                    |   9 +
 packages/core/src/index.ts                         |   9 +
 packages/core/src/planner/plan-approval.ts         |  27 +-
 .../0065_fn_149_review_convergence_stage.sql       |   3 +
 packages/core/src/postgres/schema-applier.ts       |  10 +
 packages/core/src/postgres/schema/project.ts       |   2 +
 packages/core/src/store.ts                         |   2 +-
 packages/core/src/task-store/persistence.ts        |   4 +
 packages/core/src/task-store/reset-lifecycle.ts    |   2 +
 packages/core/src/task-store/serialization.ts      |   2 +
 packages/core/src/task-store/task-mutation-ops.ts  |   2 +-
 packages/core/src/task-store/task-row-mappers.ts   |   2 +-
 packages/core/src/task-store/task-update.ts        |   4 +
 packages/core/src/tasks/in-review-stall.ts         |  21 ++
 packages/core/src/tasks/manual-retry-reset.ts      |   2 +
 packages/core/src/types/settings/settings-scope.ts |   7 +
 packages/core/src/types/task/task-core.ts          |   3 +
 packages/core/src/types/task/task-review.ts        |   2 +-
 packages/core/src/types/workflow/workflow-steps.ts |  24 +-
 .../src/workflows/builtin-workflow-settings.ts     |  43 +++
 .../core/src/workflows/default-workflow-hooks.ts   |  10 +-
 .../core/src/workflows/workflow-step-results.ts    | 185 ++++++++++++-
 .../dashboard/app/components/TaskReviewTab.tsx     |   6 +-
 .../components/__tests__/TaskReviewTab.test.tsx    |   6 +-
 .../dashboard/src/__tests__/routes-tasks.test.ts   |   7 +
 .../src/routes/register-task-workflow-routes.ts    |   4 +-
 .../__tests__/review-arbitration-release.test.ts   |  46 ++++
 .../src/__tests__/review-arbitration.test.ts       |  57 ++++
 .../__tests__/review-convergence-context.test.ts   |  95 +++++++
 .../review-convergence-escalation.test.ts          | 135 +++++++++
 .../src/__tests__/review-dispute-tool.test.ts      | 113 +++++++
 .../__tests__/review-finding-supersession.test.ts  |  55 ++-
 .../review-findings-injection.test.ts              |  19 +-
 .../__tests__/review-history-preservation.test.ts  | 186 +++++++++++++
 .../__tests__/review-ladder-entry-points.test.ts   | 304 +++++++++++++++++++++
 ...un-audit-sink-health-review-convergence.test.ts | 251 +++++++++++++++++
 .../workflow-graph-optional-step-fix.test.ts       |  59 ++--
 packages/engine/src/execution/replan-target.ts     |   6 +-
 .../clear-terminal-step-failures-for-retry.ts      |   8 +-
 .../src/executor/create-review-dispute-tool.ts     | 126 +++++++++
 packages/engine/src/executor/deps-bags.ts          |   2 +-
 .../engine/src/executor/execute-workflow-graph.ts  |  71 ++++-
 packages/engine/src/executor/execute-workflow-step.ts   |  16 +-
 packages/engine/src/executor/free-reexports.ts     |   1 +
 packages/engine/src/executor/impl-bindings.ts      |   1 +
 .../engine/src/executor/optional-step-revision.ts  |  33 ++-
 .../src/executor/recover-failed-pre-merge-step.ts  |  43 ++-
 .../request-pre-merge-optional-step-fix.ts         |  85 +++---
 packages/engine/src/executor/review-arbitration.ts | 233 +++++++++++++++
 .../src/executor/review-convergence-ladder.ts      | 237 +++++++++++++++
 .../route-graph-failure-to-execution-resume.ts     |  16 +-
 .../route-retryable-remediation.ts                 |   9 +-
 .../engine/src/executor/run-graph-custom-node.ts   |   1 +
 packages/engine/src/executor/run-implementation.ts |   2 +
 .../src/executor/task-executor-session-facades.ts  |   3 +-
 .../engine/src/executor/workflow-rerun-bounce.ts   |  13 +-
 .../executor/workflow-step-failure-injection.ts    |  23 +-
 .../engine/src/executor/workflow-step-failures.ts  |  16 +-
 .../engine/src/executor/workflow-step-verdict.ts   |   2 +
 packages/engine/src/self-healing.ts                |   5 +-
 packages/engine/src/util/run-audit.ts              |   9 +-
 .../src/workflows/workflow-graph-executor.ts       |   8 +-
 .../engine/src/worktree/review-diff-fingerprint.ts |  21 +++
 .../src/worktree/workspace-review-evidence.ts      |   9 +-
 77 files changed, 2719 insertions(+), 166 deletions(-)

Fusion-Task-Id: FN-149
Fusion-Task-Lineage: 9330df9d-5086-4b40-8daa-66f0ea31cf3e
Co-authored-by: Fusion <noreply@runfusion.ai>
2026-08-23 01:36:32 +00:00

99 lines
13 KiB
Markdown

# Run-Audit Catalogue
The run-audit catalogue for the S4 **Reliability, Durability & Observability** delivery-pipeline theme — a durable, single-source-of-truth reference for *who did what, when, and why after the fact* across the delivery pipeline's reliability/observability event surface.
## Status / purpose
This document is the **run-audit observability catalogue** for the Core Product Vision & Roadmap mission (Mission **M-MSL4E01A-0001-Y9QC**, Milestone **M2 — Roadmap Definition**, Slice **S4 — Reliability, Durability & Observability roadmap**, feature **F-MSL72J0A-000M-GIJN**), **grounded in the M1 vision theme verbatim**:
> "**Reliability, durability, and observability** of the delivery pipeline — tasks, agents, and their delivery are recoverable and inspectable."
See the north-star grounding: [Core Product Roadmap — §4. Reliability, Durability & Observability](./roadmap.md) and [Product Vision — Strategic Themes](./vision.md).
The S4 roadmap near-term item ("Recoverable and inspectable delivery") calls for the pipeline's run-audit behavior to be **inspectable after the fact**. This catalogue is that surface: it centralizes the delivery-pipeline-focus run-audit event names (finalization, self-healing reconciliation, durable-agent error-state) so an operator or agent can answer *"which run-audit events are emitted for delivery-pipeline finalization / durable-agent error-state / self-healing reconciliation, and when?"* without grepping source. It is kept truthful by a parity test that enforces lock-step with the typed catalogue module.
## How to read / query the run-audit surface
Run-audit events are captured via the run-audit store using the discriminated event union type `DatabaseMutationType` in the engine ([`packages/engine/src/util/run-audit.ts`](../packages/engine/src/util/run-audit.ts)). Metadata follows the **ids/outcomes-only** convention — never description prose. All events named below are literal members of that union; the typed catalogue array [`packages/engine/src/run-audit/run-audit-catalogue.ts`](../packages/engine/src/run-audit/run-audit-catalogue.ts) enforces member-validity at compile time, and the parity test (`run-audit-catalogue.test.ts`) keeps this doc and the module in lock-step so neither can drift from the real union.
Query the stored surface through the audit-store read path (project-scoped historical record of emitted mutation events) filtering by the `mutationType` names below and the ids/outcomes-only structured metadata each event records. This catalogue documents *what each event means*; the audit store records *when each was emitted* in a project's history.
## Delivery-pipeline finalization
Events that close a task's delivery: blocked/advanced completion parks, already-merged / already-on-main no-ops, finalize column-mismatch reconciliation, post-finalize verification, finalize-blocking guards, and stale-merger recovery.
| Event | What it records / when it fires |
| --- | --- |
| `task:completed-blocked-parked` | A fully implemented task is parked instead of advancing to review because a live completion blocker applies. |
| `task:completed-blocked-advanced` | The parked completed task's blocker cleared and its work advances to review. |
| `task:auto-recover-already-merged` | Self-healing finds a task already merged into main and records the no-recovery-needed outcome. |
| `task:auto-recover-finalize-already-on-main` | A finalize attempt is skipped because the task's changes are already present on main. |
| `task:auto-merge-skipped-already-done` | Auto-merge is skipped because the task is already done/landed. |
| `task:auto-merge-finalize-column-mismatch-reconciled` | Finalize found the task in a different live column than its target and reconciled the column. |
| `task:auto-merge-finalize-column-mismatch-no-action` | Finalize found a column mismatch but took no action (e.g. blocked/no-action per triple-proof). |
| `task:post-finalize-verification-no-op` | Post-finalize verification ran and found nothing to verify (no-op), recording the check outcome. |
| `task:no-commits-finalize-blocked-incomplete-steps` | Finalize is blocked for a zero-commit task with incomplete workflow steps (FN-6461 lane). |
| `task:empty-merge-finalize-blocked-no-landed-proof` | The AI empty-merge lane vetoes a zero-diff no-op finalize with no landed proof (FN-8141). |
| `task:finalize-unproven-blocked` | Finalize is blocked because finalization has not been proven against the landing truth. |
| `task:merge-boundary-unproven-parked` | A workflow merge boundary could not be proven and its terminal park is recorded with best-effort, time-bounded telemetry that never blocks or stalls the park. |
| `task:finalize-lost-work-blocked` | Finalize is blocked because it would discard work (lost-work guard). |
| `task:auto-recover-stale-merger-status` | Self-healing clears a stale merger status left on a finalize path. |
## Self-healing reconciliation events
Reconciliation-scoped auto-recover/reclaim events the self-healing sweep surfaces when it repairs board state after the fact.
| Event | What it records / when it fires |
| --- | --- |
| `task:auto-recover-paused-abort-park` | Self-healing clears a benign pause-abort operator park and requeues the task. |
| `task:auto-rebound-paused-scope-decay` | Self-healing rebounds a task whose paused scope decayed past its floor, unblocking followers. |
| `task:auto-archive-failure-budget-exhausted` | Self-healing abandons a repeatedly failing stale-task archive and surfaces it for operator action. |
| `task:no-progress-no-task-done-requeue` | A zero-progress no-task-done failure consumes one bounded self-healing retry and records its backoff. Metadata is task ID, column, attempt, maximum, delay, and fixed outcome only. |
| `task:no-progress-no-task-done-requeue-exhausted` | The bounded no-progress requeue budget parks a task once. Metadata is task ID, column, attempt, maximum, and fixed outcome only; bounded best-effort emission never gates the park. |
| `task:reclaim-phantom-executor-binding` | Self-healing proves an in-memory executor-active binding is stale and requeues the task. |
| `task:reconcile-orphaned-pending-step-results` | Self-healing rewrites orphaned `pending` workflow-step results (no live session) to `failed`. |
| `task:reconcile-stale-duplicate-decision` | Self-healing clears a recurring duplicate-decision pause with no canonical target. |
| `task:reconcile-stale-agent-assignment` | Self-healing clears stale durable Agent.taskId/state drift while preserving file-scope leases. |
| `task:reconcile-engine-downtime-active-timing` | Self-healing shifts active-task anchors to exclude proven stopped-engine wall-clock. |
| `task:reconcile-engine-downtime-active-timing-no-action` | Self-healing finds no active task qualifies for downtime-timing reconciliation (no-action). |
| `task:reconcile-undeclared-column` | Self-healing re-homes a row out of a column its workflow no longer declares. |
| `task:reconcile-wedged-active-merge` | Self-healing reclaims a wedged single-flight merge entry. |
| `task:reconcile-stranded-completed-no-action` | A stranded-completed promoter withholds promotion of an all-steps-done/skipped task with a failure-park provenance (no-action). |
| `task:reconcile-legacy-adoption` | Self-healing startup adopts a pre-cutover legacy task row through the KTD-8 adoption table. |
## Durable-agent error-state
Events that make durable-agent error states and their recovery inspectable.
| Event | What it records / when it fires |
| --- | --- |
| `agent:auto-recover-error-state` | A recoverable, non-operator-actionable durable-agent error is cleared by the heartbeat/self-healing sweep and retried. |
| `agent:reset-error-state-on-startup` | An engine restart clears an eligible durable-agent error/exhaustion park and re-arms the heartbeat (startup-only). |
| `agent:error-retry-exhausted` | A durable-agent error retry budget is exhausted and the agent is parked `paused` with pauseReason `error-retry-exhausted`. |
| `agent:error-parked-unrecoverable` | An operator-actionable durable-agent error parks the agent `paused` with pauseReason `error-unrecoverable` for human repair. |
| `agent:heartbeat-move-skipped-soft-delete` | A heartbeat move races a soft-deleted task and is skipped without parking the durable agent. |
## Maintenance contract
Adding a new catalogued run-audit event requires updating **both** the typed catalogue module (`packages/engine/src/run-audit/run-audit-catalogue.ts`) **and** this doc together — the parity test (`packages/engine/src/__tests__/run-audit-catalogue.test.ts`) fails if the documented event set and the catalogue module's set ever diverge, keeping the observability surface truthful as the real `DatabaseMutationType` union evolves. Removing an event likewise requires updating both in the same change.
### Emit-seam policy
All engine telemetry must use `emitBoundedRunAudit` from `packages/engine/src/util/emit-bounded-run-audit.ts`. It is best-effort and never load-bearing for lifecycle correctness: absent/non-function, synchronously throwing, rejecting, never-settling, and late-settling sinks are absorbed without altering the owning branch. The seam swallow-logs and bounds each write; it intentionally adds no retry, backoff, or queueing.
This applies to executor, run-auditor, self-healing, merger, PR reconciliation, scheduler, project-engine, plugin, mission-loop, hold-release, goal diagnostics, overseer advisor, mesh-lease, in-process runtime, credential rotation, and workflow-column-boundary emitters. `packages/engine/src/merge/merge-write-fence.ts` retains its bespoke non-`RunAuditEventInput` recorder. New engine emitters must ship with a behavioral sink-health regression covering hostile sink states, not only a source-routing assertion.
### Core emit-seam policy
Core best-effort emitters use `packages/core/src/run-audit/emit-bounded-run-audit.ts`. This is a deliberate copy of the engine seam because `@fusion/core` cannot import `@fusion/engine`; it synchronously invokes a valid sink, then absorbs throws, rejection, timeout, and late settlement without making telemetry lifecycle-load-bearing. `emitBoundedRunAudit` is the default void seam. `emitBoundedRunAuditWithOutcome` returns `recorded`, `absent`, `failed` (with the original error), or `timed-out` where a forensic throw ordering or caller-visible skipped payload depends on the audit result; workflow-switch torn reconciliation and phantom committed-reservation reconciliation use it. FN-9181 applies FN-9178's class-A decision to detached recall capture: `memory:capture-recorded` and `memory:capture-failed` are bounded, while the injectable `deps.audit` adapter remains a test seam with its existing bare-metadata contract. Transactional writers and explicitly awaited durability/ordering writers remain unbounded. `packages/core/src/__tests__/core-run-audit-sink-health.test.ts` and `core-run-audit-emitter-isolation.test.ts` respectively enforce hostile-sink behavior and source routing.
### Awaited core exclusion decision
FN-9178 classified awaited sites with hostile-sink characterization tests. FN-9180 routed the class-A `task-deleted-outbox:catch-up`, `:reconciliation-fallback`, `:lease-fenced`, and `:retention-pruned` rows through `emitBoundedRunAudit`; each remains awaited at its post-acknowledgement, post-cursor, or post-DELETE position so bounded telemetry preserves ordering. FN-9181 routed detached recall capture through the same bounded seam. `task:workflow-switch-torn` and `task:reconcile-phantom-committed-reservation` are class B and use the bounded outcome seam because their throw/result payload depends on audit outcome. `task:bypass-review`, `task:resume-step`, and both resurrection-blocked records are class C and intentionally unbounded: they claim persistence before return, destructive cleanup, or a forensic throw.
All `recordRunAuditEventWithinTransaction(tx, ...)` calls and the `recordRunAuditEventBackend(tx, ...)` transactional call are permanently out of scope. Their audit row shares a transaction with the mutation it describes; bounding would split that atomicity. The full matrix and evidence pointers are in the FN-9178 `decision` task document; `excluded-awaited-run-audit-store-sites.test.ts`, `excluded-awaited-run-audit-layer-sites.test.ts`, and the core routing ratchet pin this boundary.
### Review convergence events
`task:review-finding-disputed`, `task:review-convergence-escalation`, `task:review-arbitration`, and `task:review-convergence-human-escalation` record review-cycle progression. Their metadata contains only ids, counts, and fixed outcomes; dispute rationales, findings, reviewer feedback, and arbiter output are never recorded. All five emission sites use the FN-9175 bounded best-effort seam, so hostile telemetry cannot alter or block the ladder, arbitration release, or dispute result.