fix(cli): a journal states the schema it was written in, and the reader reads it - #776
Merged
blafourcade merged 1 commit intoSep 5, 2026
Merged
Conversation
…er reads it The hook stamps `schema_version` on every `session_start` it writes (`record.cjs`'s own `SCHEMA_VERSION`, 2). The reader dropped the field on the floor: `parseSessionStart` never looked at it, and `RunJournalSessionStart` had nowhere to put it. So a journal written under a schema whose line shapes had changed was read as if it were this one — a silent misreading, which is the one thing a version field exists to prevent. The reader now carries the stated schema and refuses a journal stating one it does not read. Version 1 was a mutable record rather than this append-only line log, so its lines are another shape entirely, and a later version can change any line the same way; reading either would mean guessing that whatever lines this parser still recognises mean what they used to. Absence is not a disagreement. Every journal written before this reader looked at the field states no schema, and refusing those would drop attribution this reader has always given — "an unknown is never a zero", applied to the reader rather than to a figure. A refusal must not cost the fact that the file is there. Dropped from `list()` alone, a refused journal leaves `check` to fall through to a branch that is false about it: "the recorder is declared nowhere" claims no file exists, and "none carry a readable session_start" blames a torn write for a version disagreement. `RunJournalReader.listForeignSchemas` carries the stated versions across, and `hook-fired` gains `journal-in-another-schema`, read ahead of the anchorless one — a build that cannot read a journal's schema cannot tell a missing `session_start` from one shaped differently. Mutations run, all killed: the reader no longer refusing, absence read as a disagreement, and the new reason ordered after the anchorless one. Bundle budget 596 -> 598 KB, measured 594.3 -> 595.8, recorded beside the three raises before it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
5 tasks
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.
Closes two of #657's boxes, and reports the other three as already met. See the audit at the end.
The defect
record.cjsstampsschema_versionon everysession_startit writes:parseSessionStartnever read it, andRunJournalSessionStarthad nowhere to put it. A journal written under a schema whose line shapes had changed was therefore read as if it were this one — the silent misreading a version field exists to prevent. #657 states the box exactly: "a reader givenschema_version: 2behaves predictably and says so, rather than failing or silently misreading."The change
The reader carries the stated schema, and refuses one it does not read. Version 1 was a mutable record rather than this append-only line log, so its lines are another shape entirely; a later version can change any line the same way. Reading either would mean guessing that whatever lines this parser still recognises mean what they used to.
Absence is not a disagreement. Every journal on disk before this reader looked at the field states no schema, and refusing those would drop attribution this reader has always given — "an unknown is never a zero", applied to the reader rather than to a figure.
A refusal does not cost the fact that the file is there. This is the part that needed more than a
return null. Dropped fromlist()alone, a refused journal leavescheckfalling through to a branch that is false about it:recorder-declared-nowhereclaims no run file exists — one does.anchorless-run-fileblames a torn write or a hooks block missingSessionStart— the header parsed perfectly well.RunJournalReader.listForeignSchemascarries the stated versions across, andhook-firedgainsjournal-in-another-schema, read ahead of the anchorless one: a build that cannot read a journal's schema cannot tell a missingsession_startfrom one shaped differently, so blaming a torn write would assert what it just said it cannot see.READABLE_JOURNAL_SCHEMA_VERSIONis pinned against the hook's own exportedSCHEMA_VERSIONby a test, not copied — the same mirrorsanitizePathSegmentalready is. A reader whose constant is a second copy goes on claiming it reads a schema the writer has moved past.Guards
Mutations run, all killed:
anchorless-run-fileThe additive-key box has its own test: a key this reader has never heard of, under a schema it knows, costs it neither the header nor the boundaries.
The skill action
02-check/actions/02-diagnose.mdteaches the new reason in step 6 — required, not optional:telemetry-claim.unit.test.tsfails any reason step 6 does not account for in the same sentence as its verdict token.#657 audit, checked rather than assumed
aidd_docs/runs/tracked or ignored by an explicit rule.gitignorecarries.aidd/*with!.aidd/config.json, andaidd_docs/runs/*with!.gitkeep,!README.md. The issue's "neither tracked nor gitignored" is staleschema_versionbehaves predictably and says soaidd telemetry forgetremoves run files by name throughdeleteRunFile, confined byisBareFileName, with e2e coveragedisablestates what happens to data already writtentelemetry offnames what stays andforgetis what removes itGates
pnpm test3471 passed / 309 files · typecheck ·biome ciexit 0 (2 pre-existing warnings) · knip · jscpd · layering · repository scripts 371 passed · bundle 595.8 KB, budget raised 596 → 598 with the measurement recorded beside the three raises before it.🤖 Generated with Claude Code
https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp