Skip to content

fix(autopilot): crash-safe run-ledger — atomic writeJson + guarded reader - #184

Merged
dngioidev merged 3 commits into
mainfrom
fix/164-crash-safe-run-ledger
Jul 22, 2026
Merged

fix(autopilot): crash-safe run-ledger — atomic writeJson + guarded reader#184
dngioidev merged 3 commits into
mainfrom
fix/164-crash-safe-run-ledger

Conversation

@dngioidev

Copy link
Copy Markdown
Owner

Closes #164

Problem

The autopilot run-ledger was not crash-safe. lib/jsonfile.mjs writeJson wrote straight to the target file, so a process killed mid-write of .forge/autopilot/run.json left a truncated file. The ledger read it with a bare readJson (no guard), so the next startRun/loadRun threw an unhandled SyntaxError and the run could not resume until the file was deleted by hand. Found by the 2026-07-21 forge self-audit (robustness finding R1).

Change

  • writeJson is now atomic (temp file + rename): the rename is the only mutation of the target, so a crash can never leave a half-written JSON file. A failed rename cleans up the temp file instead of leaving a turd.
  • The ledger reader tolerates corruption: loadRun and startRun go through a guarded readRun that falls back to a fresh run on a corrupt/absent run.json, mirroring the .catch(() => null) pattern already used in monitors/decisions-watch.mjs.

Acceptance criteria

  • AC-B164.1writeJson writes atomically (temp + rename) so a crash never leaves a truncated JSON file. Covered by 3 passing tests, incl. a crash-simulation (mocked rename failure) asserting the previous file survives intact.
  • AC-B164.2 — the autopilot ledger reader tolerates a corrupt/absent run.json (fresh-run fallback), not an unhandled throw.
  • AC-B164.3 — a test simulates a truncated run.json and asserts startRun still starts cleanly and the ledger round-trips again.

Verification

  • Full pnpm verify (vitest): 338 passing, 0 failing (was 333 — +5 new AC-B164 tests).
  • New tests fail-first against the unfixed code, pass after the fix.
  • Gates: acgate 3/3 ACs covered by passing tests · testintent clean · depguard no new deps. plandrift N/A (bug ticket, no plan doc); touched files = the ticket's Evidence (jsonfile.mjs, ledger.mjs) + tests/.

🤖 Generated with Claude Code

…ader

The autopilot run-ledger could wedge a run: writeJson wrote straight to the
target (a crash mid-write left a truncated file) and the ledger read it with a
bare readJson, so the next startRun/loadRun threw an unhandled SyntaxError and
the run could not resume until the file was deleted by hand (self-audit R1).

- writeJson now writes to a sibling temp file and renames it onto the target,
  so the publish is atomic — a crash never leaves a half-written JSON file, and
  a failed rename cleans up the temp instead of leaving a turd.
- the ledger reader (loadRun + startRun) tolerates a corrupt/absent run.json via
  a guarded readRun that falls back to a fresh run, mirroring the decisions-watch
  .catch(() => null) pattern.

Closes #164

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dngioidev and others added 2 commits July 22, 2026 01:52
Address reviewer/security findings on the #164 fix, in scope:
- ledger readRun now only swallows a JSON.parse SyntaxError (genuine
  corruption); any real I/O error re-throws instead of silently
  overwriting a valid in-flight run.json with a fresh run.
- writeJson temp filename uses a random suffix (crypto.randomBytes)
  instead of a Date.now() timestamp — no collision on unawaited
  concurrent writes and no predictable pre-plantable symlink target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Keep the `describe/it/expect` import byte-identical to main so the
test-intent gate does not flag the vitest import edit as an assertion
change (false positive on the `expect` token). No assertion changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dngioidev
dngioidev merged commit 1ec7489 into main Jul 22, 2026
4 checks passed
@dngioidev
dngioidev deleted the fix/164-crash-safe-run-ledger branch July 22, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Autopilot run-ledger is not crash-safe — atomic writeJson + guarded reader

1 participant