Skip to content

feat(checkpoints): per-task change journal core with torn-tail repair (B2 v2-3, epic #1375) - #47

Open
easonLiangWorldedtech wants to merge 1 commit into
feat/per-write-checkpoints-b1from
feat/fws-v2-3-journal-core
Open

feat(checkpoints): per-task change journal core with torn-tail repair (B2 v2-3, epic #1375)#47
easonLiangWorldedtech wants to merge 1 commit into
feat/per-write-checkpoints-b1from
feat/fws-v2-3-journal-core

Conversation

@easonLiangWorldedtech

Copy link
Copy Markdown
Owner

What

v2-3 of the B2 change-journal rebuild (epic #1375): the per-task change journal core — a new module src/core/checkpoints/changeJournal.ts plus the journal hook in checkpointSave.

This PR is the first of a two-part split of the reviewed B2 content (legacy #1406, 1924 lines standalone → v2-3 core + v2-4 tool wiring):

  • src/core/checkpoints/changeJournal.ts (new, 96 lines) — journal format and I/O:
    • ChangeJournalEntry { path, operation: "create" | "update" | "delete", checkpointId, diffStats? } — one line per successful file write, referencing the B1 per-write checkpoint SHA;
    • appendChange() — a single appendFile syscall per entry (minimal torn-write risk), creating parent directories as needed;
    • loadChanges() — order-preserving read with torn-tail repair: a truncated final line is silently discarded, a corrupt middle line is skipped without hiding later valid entries, and an absent/empty journal returns [].
  • src/core/checkpoints/index.ts (+53/-1)checkpointSave gains an optional write?: CheckpointWriteInfo | CheckpointWriteInfo[] parameter: after a real commit succeeds (empty/failed saves resolve undefined / reject), each write is appended to the journal in write order with checkpointId = the commit SHA. Journal failures are logged and never propagate (checkpoints stay enabled). Callers that pass no write (task-start baseline, non-write checkpoints) are untouched — the signature change is source-compatible, and the tool wiring that passes write info lands in the follow-up PR (v2-4). CheckpointWriteInfo is exported for that slice.
  • src/core/checkpoints/__tests__/changeJournal.spec.ts (new, 148 lines, 12 tests) — the pure module: journalPath path-contract pin test, append shape, diffStats round-trip, and every torn-tail/corrupt-line repair case.
  • src/core/checkpoints/__tests__/index.checkpointJournal.test.ts (new, 225 lines, 8 tests) — the hook with a structural Task double: single write, diffStats passthrough, non-write save (task-start baseline), multi-file array, absent provider, no-op commit, and journal-write failure (logged, enableCheckpoints stays true). The negative tests assert the save's allowEmpty/suppressMessage defaults and that no journal error path is reached.

Scope / non-goals

  • Tool wiring (WriteToFileTool / EditFileTool / ApplyPatchTool passing write info and their spec updates) = follow-up PR v2-4, stacked on this one.
  • No UI, no settings, no B1 behavior change (per-write checkpoints unchanged; the journal is additive).

Gates (local, pre-push)

  • vitest: core/checkpoints → 45/45 (20 new journal tests + 25 existing checkpoint-suite tests, 3 files).
  • eslint: full pnpm lint via pre-commit (11/11 packages, --max-warnings=0); eslint-suppressions.json counts unchanged.
  • pnpm check-types: clean.
  • Stryker (scripts/stryker-diff.mjs ci, base 0ea153dc8 = 1404/B1 head, head 4e95e380e): 0 surviving / 0 uncovered changed-code mutants. Four equivalent mutants in the loadChanges parse loop are excluded with scoped Stryker disable next-line directives (each with a concrete reason: the JSON.parse catch makes the mutated parse path behaviorally identical).

Line budget

Standalone diff vs the PR base (0ea153dc8): 522 lines (521+/1-, 4 files) — over the 400-line soft design target. Rationale: the four files are one self-contained unit (module + hook + both specs). Splitting the hook spec into a separate PR fails the mutation gate — with the spec separate, the changed index.ts lines are NoCoverage under stryker-diff (verified locally: 19 uncovered mutants). The spec is named index.checkpointJournal.test.ts so the gate's direct-test filter discovers it for the changed index.ts.

Stacking / merge order

Base: feat/per-write-checkpoints-b1 (head 0ea153dc8, the Zoo-Code-Org#1404 B1 branch) — this PR builds only on the B1 checkpointSave hook that Zoo-Code-Org#1404 introduces. v2-4 (tool wiring) is stacked on this branch. After Zoo-Code-Org#1404 and this PR land on main, the v2-4 PR base switches to main — the diff is identical either way.

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown

Review status

Thanks for contributing. This comment tracks the review sequence and the next action.

Current step: Required CI passed. Waiting for automated review of the latest commit.

If automated review does not start, a maintainer must restart it.

Review-state labels are managed by this workflow; do not edit them manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit coderabbit-review-active Required CI passed; CodeRabbit review is active

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants