#179

fix(setup): preserve checkpoints and summaries across process retries

Openemerzonwants to merge intofrom

When setup processing returns failed batches or returns after cancellation, setup can save a successful checkpoint before noticing the failure. The next invocation then skips unfinished processing. Retrying failed batches or rechecking a legacy checkpoint also creates a new processing run, causing the completion summary to omit results from earlier successful batches.

Validate failed batches, cancellation, duration, and cost limits before publishing success. Save the successful process run ID in the same atomic write as its checkpoint. Version the process completion digest so legacy checkpoints are rechecked once while completed earlier setup phases remain reusable.

Build the completion summary from saved workspace records across processing attempts: count currently analyzed files, retain existing findings and their original attribution, and include recorded investigation costs while excluding revalidation costs. The process run ID identifies the latest successful attempt, including an empty attempt when all files were already analyzed. Document these summary semantics.

Seven focused regression cases cover failed batches, cancellation, cost-limit handling, partial retries, legacy checkpoint recovery, retained findings and costs, atomic run-ID publication, and subsequent checkpoint reuse. The fixtures use distinct attempt IDs and update record status to match the processor's retry behavior. The original failure cases and the follow-up recovery/publication regressions were reproduced before their fixes.

Validation:

  • pnpm test: 2,424 passed, 1 live-sandbox test skipped; includes bundle and stub-agent pipeline tests.
  • pnpm bundle: passed.
  • pnpm --filter '!website' -r build: passed for core, scanner, processor, and CLI.
  • pnpm --filter website build: passed.
  • pnpm lint: passed.
  • pnpm knip: passed.

No data migration is required. Existing process checkpoints are rechecked once; the processor retains its normal per-file recovery behavior and stored findings/history are unchanged. Earlier completed setup phases remain reusable.

Activity

4
vercel[bot]1mo ago

@emerzon is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

auto-maintain[bot]1mo ago

🤖 auto-maintain review

Automated, advisory triage for @emerzon's PR. Facts below are read from the GitHub API.

CheckResult
Author's merged PRs (this repo)0 — ⚠️ first-time contributor
Account established✅ (age 3467d · 23 followers · 17 public repos)
Commits signed/verified✅ 1/1

Review panel: 🟡 medium highest severity

deepsec maintainer code review: 🟢 low

The process checkpoint fix correctly validates cancellation, cost limits, and failed batches before publishing success, with appropriate checkpoint invalidation and regression coverage.

General code review: 🟡 medium

The failure checkpoint fix works, but legacy checkpoint migration corrupts completed-run reporting.

  • packages/deepsec/src/setup/coordinator.ts:839 — Bumping the process digest reruns every legacy successful checkpoint, but the real processor creates a new empty run when all records are already analyzed. The coordinator then overwrites `processRunId` and filters history/findings by that empty run, so the resumed setup reports zero investigated files/findings and loses attribution to the completed run. The regression test masks this by returning the same fixed run ID without updating record status.

Scanner and matcher quality: 🟢 low

No actionable scanner, matcher, or processing checkpoint issues found.

Sandbox and credential boundaries: 🟡 medium

The checkpoint-validation fix itself is sound, but the forced digest invalidation plus run-scoped summary counting makes setup report zero investigated files and zero findings on existing workspaces and after recovery; fix the summary accounting before merge.

  • packages/deepsec/src/setup/coordinator.ts:839 — Bumping the process digest with `completionContract: 2` invalidates every existing process checkpoint, so the next `deepsec setup` on an already-completed workspace re-enters the process phase. The processor only selects records with status pending/error (packages/processor/src/index.ts:443-455), so it early-returns with `analysisCount: 0, findingCount: 0` and a fresh runId, which the coordinator then checkpoints as success and stores in `state.processRunId`. The summary is scoped to that runId, so setup prints "Deepsec found 0 potential findings" / Investigated 0 / $0.0000 for a workspace that already has findings on disk — and because the empty runId is persisted, every later skip-path summary reports 0 too. The new tests don't catch this because the mocked `process` always returns `analysisCount: 1`.
  • packages/deepsec/src/setup/coordinator.ts:904 — The same run-scoped counting makes the failed-batch recovery path under-report: after the new error-batch throw, resuming setup reprocesses only the files left in `error` status, so `completedHistory`/`completedFindings` (filtered on the new `processResult.runId`) exclude everything analyzed by the aborted first run. The recovery run now reports far fewer analyses/findings than the previous behavior, which reused the earlier runId. Consider counting the union of runIds that contributed to the phase, or deriving the summary from record state rather than a single runId.

Durability and distributed state: 🟡 medium

The checkpoint validation fix works on ordinary retries, but interrupted publication can retain a stale run ID and resumed partial runs produce incomplete summaries.

  • packages/deepsec/src/setup/coordinator.ts:871 — The successful phase checkpoint is persisted before processRunId. If interrupted between runPhase returning and this write, a stale prior processRunId remains; resume treats the new checkpoint as current and skips processing, attributing results to the wrong run. Persist the checkpoint and run ID atomically or clear the old ID before rerunning.
  • packages/deepsec/src/setup/coordinator.ts:889 — After a partially failed run, recovery processes only failed records under a new run ID, but the completion summary filters history and findings exclusively to that new ID. Findings and costs from batches that succeeded before the retry are omitted from human and JSON completion output.

Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call.