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
Conversation
Review statusThanks 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
v2-3 of the B2 change-journal rebuild (epic #1375): the per-task change journal core — a new module
src/core/checkpoints/changeJournal.tsplus the journal hook incheckpointSave.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 singleappendFilesyscall 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) —checkpointSavegains an optionalwrite?: CheckpointWriteInfo | CheckpointWriteInfo[]parameter: after a real commit succeeds (empty/failed saves resolve undefined / reject), each write is appended to the journal in write order withcheckpointId= the commit SHA. Journal failures are logged and never propagate (checkpoints stay enabled). Callers that pass nowrite(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).CheckpointWriteInfois exported for that slice.src/core/checkpoints/__tests__/changeJournal.spec.ts(new, 148 lines, 12 tests) — the pure module:journalPathpath-contract pin test, append shape,diffStatsround-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 structuralTaskdouble: single write, diffStats passthrough, non-write save (task-start baseline), multi-file array, absent provider, no-op commit, and journal-write failure (logged,enableCheckpointsstays true). The negative tests assert the save'sallowEmpty/suppressMessagedefaults and that no journal error path is reached.Scope / non-goals
WriteToFileTool/EditFileTool/ApplyPatchToolpassingwriteinfo and their spec updates) = follow-up PR v2-4, stacked on this one.Gates (local, pre-push)
core/checkpoints→ 45/45 (20 new journal tests + 25 existing checkpoint-suite tests, 3 files).pnpm lintvia pre-commit (11/11 packages,--max-warnings=0);eslint-suppressions.jsoncounts unchanged.pnpm check-types: clean.scripts/stryker-diff.mjs ci, base0ea153dc8= 1404/B1 head, head4e95e380e): 0 surviving / 0 uncovered changed-code mutants. Four equivalent mutants in theloadChangesparse loop are excluded with scopedStryker disable next-linedirectives (each with a concrete reason: theJSON.parsecatch 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 changedindex.tslines are NoCoverage understryker-diff(verified locally: 19 uncovered mutants). The spec is namedindex.checkpointJournal.test.tsso the gate's direct-test filter discovers it for the changedindex.ts.Stacking / merge order
Base:
feat/per-write-checkpoints-b1(head0ea153dc8, the Zoo-Code-Org#1404 B1 branch) — this PR builds only on the B1checkpointSavehook 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 tomain— the diff is identical either way.