Repository navigation
docs(spec): book.json's liveness header names the dead row, not a pair of integers - #16982
Merged
Merged
Conversation
…r of integers The `_note` opened with "15 of 17 live; the two dead entries are both inline `translations` maps". Neither half survives a re-read of the ledger it describes: the file carries 13 live and 1 dead row across all nesting levels, and the gate's own report (`check-liveness.mts --json`, `types.book.byStatus`, published in `liveness/state-counts.md`) reads `live 20 … dead 1 … classified 21`. 15 and 17 appear in neither granularity, and the plural sends a reader looking for a second dead inline `translations` map the ledger does not record. The second dead entry did exist and was NOT lost. `book.translations` was seeded `dead`/`authorWarn` beside `groups[].translations` on 2026-08-01 (#4488); #4667 retired both by two deliberately different routes, stated in that PR's own commit message: the book-level key is a strict deletion, so it left the walked shape and its ledger row was deleted with it, while the group-level key is tombstoned (`retiredKey`) because `BookGroupSchema` is a plain `z.object` with no `.strict()`, where a bare delete would have zod silently strip the key. The liveness README's `book` row records that disposition in writing, and `check:liveness` reports `unclassified: 0` for the type while enforcing that no ledger row outlives its property. The ledger is complete; only the sentence had gone stale, on the day the second key was retired. The replacement states the boundary — which row is dead, and why its twin carries no row — and restates no total, per the ledger README's counting-method section ("The Notes cell is not a place to restate a number"). No `status` value moves, no schema changes, and `state-counts.md` is untouched and reported current. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 131 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
zhuangjianguo
marked this pull request as ready for review
September 8, 2026 22:50
zhuangjianguo
enabled auto-merge
September 8, 2026 22:50
zhuangjianguo
deleted the
claude/issue-16136-book-liveness-note-counts
branch
September 8, 2026 23:12
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.
Fixes #16136
Clause-②: no
The change rewrites one prose sentence in the
_noteheader of a liveness ledger.No accept set moves, no export is added, no schema arm, bound or behaviour changes,
and every per-prop
statusrow stays byte-for-byte as it was.packages/spec'spublished surface changes only in the shipped text of
liveness/book.json.The reading, re-taken on this branch's base (
50dc2141a)The
_noteopened with, verbatim:My own recount, walked at every nesting level of the ledger's own rows, with a live
control proving the parse worked:
The gate's own report beside it —
check-liveness.mts --json,types.book.byStatus,the counting method this ledger family documents and publishes in
liveness/state-counts.md:which is exactly the published row
| book | 20 | 0 | 0 | 1 | 0 | 21 |. So 15 and 17appear in neither granularity — 13 of 14 by ledger rows, 20 of 21 by the gate — and
"the two dead entries" is plural where the file records one. The plural is the half a
reader acts on: it sends them hunting for a second dead inline
translationsmap thatthe ledger does not carry.
The acceptance condition: did a second dead entry ever exist?
Yes — and it was not lost. This decides the deliverable, so it is answered with
evidence rather than assumed.
Route taken: a complete clone. This container's checkout is no longer shallow —
git rev-parse --is-shallow-repositoryreturnsfalse, the root commit1598cabe4("Initial commit", 2026-01-18) is reachable, and 13,316 commits are reachable from
HEAD — so
git loghere is a history reading. Corroborated independently of git bythe ledger README, below.
Recounting
liveness/book.jsonat every revision in its history:4f13be22b(#4488 seeding)7d2158146(#4667)3de535b86,9c249a69b,8cb96ec41At seeding the ledger carried a second dead row,
props.translations(book-level inlinetranslations),
status: deadwithauthorWarn: true. It was removed by7d2158146,whose own commit message states the ledger discipline that governed the removal:
So the row is absent because its key is absent from the walked shape, which is
precisely what this ledger records — not lost, not merged away. Three independent
confirmations:
BookSchemacarries only a comment where the key was(
// translations removed in 17.0.0 (#4667));BookGroupSchema.translationssurvivesas
retiredKey(BOOK_TRANSLATIONS_RETIRED), which is why exactly that one keeps a row.bookrow, in writing, independent of git history:"清空剩余 6 条 authorWarn 死键 —— book ×2 / job.id / translation.validationMessages / app.homePageId / app.areas[].order(ADR-0049,v17 限时) #4667: both inline translation maps retired — book-level row DELETED (BookSchema
is strictObject), group-level row KEPT as a tombstone".
unclassified: 0forbook, andcheck:livenessenforces bothdirections — "every governed-type property at the walk's one-level granularity is
classified … no ledger row outlives its property".
⇒ No row is missing. The clause-② fence does not fire: the ledger was complete all
along and only the sentence had gone stale, on the day the second key was retired. The
prose change is the whole deliverable.
Worth recording for the next reader:
the two dead entrieswas true when written andwent false on 2026-08-03.
15 of 17was never true at any granularity — the same PR thatseeded it published
| book | 13 | – | 2 | – |in the README table. I did not run theseeding-era gate to recover where that pair came from; its provenance is not established
here.
What the replacement says, and deliberately does not
Per the family precedent (#15775) and this ledger README's counting-method section — "The
Notes cell is not a place to restate a number" — the new sentence states the
boundary and restates no total. Writing "13 of 14" would have reproduced this card
with a later date.
The
doc.translations/resolveDocLocalecontrast is kept — it is real, hand-measuredcontent and the reason the trap works. The last clause is the anti-recurrence device: it
tells the next author why no integer is here, so one is not helpfully restored.
Scope held
Everything outside
_noteis byte-identical (asserted mechanically during the edit: theparsed document minus
_noteserializes identically before and after). Nostatusvaluemoves, no schema changes, no other liveness ledger is touched, and
liveness/state-counts.mdis not hand-edited —check:livenessreports it current,so no regeneration was owed.
Changeset — decided with both halves, measured
A changeset is owed, and the deciding tree is the published
liveness/directory, notdist.files[]? Yes. Measured from a realnpm packofpackages/spec(276 files):
liveness/book.jsonPRESENT; positive controlsllms.txt,README.mdPRESENT; negative control
scripts/liveness/check-liveness.mtsABSENT.(
objectstack-spec-17.3.0.tgz) and grepped it: the new sentence is present atliveness/book.json, the old sentence is gone from the whole package. Positivecontrol (pre-existing published text in the same file, "ADR-0046 §6 documentation
spine") present; negative control
proofErrors: [] as string[]— a string that existsin the working tree only inside the unpublished
scripts/liveness/check-liveness.mts—present in the working tree, absent from the packed tree. The probe discriminates.
Verification
Gate families derived from the real change set with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(re-derived after the changeset existed: 54 runnable commands, 6 of them new).
pnpm --filter @objectstack/spec check:liveness— its ownverdict lines: "✓ every governed-type property at the walk's one-level granularity is
classified … no ledger row outlives its property …" and "✓
packages/spec/liveness/state-counts.md is current — the same 36 row(s), no count column
left in the README."
pnpm check:dual-build-cjs-loadsexits 3 with"PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/
… ⛔ This is NOT a pass: nothing was measured" (87 packages unbuilt; that is a
whole-repo build, CI's run).
pnpm check:lean-entry-closurehit the same prerequisite,so
@objectstack/objectqlwas built and it was re-run into a real reading: "✓ 2published condition(s) measured from a real load."
pnpm --filter @objectstack/spec test— 467 files / 13100 tests passed.pnpm --filter @objectstack/spec typecheck— clean (tsc --noEmit+check:scripts-typecheck+check:test-typecheck).pnpm --filter '@objectstack/spec...' build) and bothtest/typecheck runs went through
scripts/pm/os-verify-lock.sh; every one printedVERDICT command-exit 0.pnpm check:nul-bytesgreen, plus a direct control-byte self-scan over both changedfiles (
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', no hits).542b5dbf9— liveness,empty-state, nul-bytes, published-files, merge-driver, doc-authoring, the two changeset
gates, objectui-changeset, lean-entry-closure, adr-0087-registration — all exit 0.
pnpm lint(repo-scan) is CI's run; the local narrowing is declared and measured.(i) eslint's own config declares a JS/TS-only population — every
filesblock is**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}or narrower, with no.jsonand no.mdblock.(ii)
eslint --no-inline-config --format jsonover both changed paths: 2 results, 0errors, both "File ignored because no matching configuration was supplied" — zero files
of this diff are in the linted population. (iii) Type-aware linting is not enabled
anywhere in this repo (no
parserOptions.project, stated ineslint.config.mjsandmeasured there with a positive control), so this diff cannot move the verdict on any
untouched file.
Out of scope, noted and not filed
The liveness README's
bookNotes row still opens with "Dead 2 = BOTH inlinetranslationsmaps" before its own "#4667:" update corrects the disposition two sentenceslater — the same restated-count shape as this card, one file over, in a file this card
fences off. Left for the PM to route: the card's surface is
book.json's header only, andthe README is the carrier of a different family member. Successor: whoever takes the next
liveness/README.mdprose card.Generated by Claude Code