Skip to content

docs(spec): re-measure proof 4's two mislabelled census figures, each beside its population - #18863

Merged
os-bill merged 2 commits into
mainfrom
claude/issue-18579-proof4-census-figures
Sep 18, 2026
Merged

os-bill merged 2 commits into
mainfrom
claude/issue-18579-proof4-census-figures

Conversation

@os-bill

@os-bill os-bill commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18579

Clause-②: no

Two census figures in the proof-4 prose of packages/spec/scripts/build-schemas.ts label a population they do not measure. Both are corrected, and both now carry their population — and the tree they were read on — in the same sentence, which is what the triage note on the card asked of whoever took it.

The two figures

(1) The docblock's not-delivered rationale. It said "on the shipped graph 7 of the 8 defs in that state are unions". Measured: that state holds 9 keys on 4 defs, and 3 of those 4 defs are unions, carrying 7 of the 9 keys. The 7 was the KEY count wearing the DEF label.

(2) The closing line of the same docblock, which sent its reader to PR #18529's body "for the census run". That body's census row labels "defs resolving to exactly one declaration that names an undeclared key" with 258 — but 258 is the count of defs resolving to exactly one declaration whether or not it names anything. The labelled population measures 147. That row lives in a merged PR body and is not editable from here, so the docblock now records the census in the tree and tells its reader not to re-derive it from that body.

The argument both figures support is unchanged — unions do dominate the not-delivered set (3 of 4 defs, 7 of 9 keys). Only the arithmetic moved.

Re-measured on this head, not copied from the card

The card's numbers were taken on branch 9e0324f807 and packages/spec/src has moved on main since, so nothing was copied. The whole census was re-derived with this head's own verbatim computeGuidanceRoutes: a byte-identical copy of scripts/build-schemas.ts (prefix proven equal by git hash-object: 0803422391… on both) with a census block appended, run as the real generator so zodByDefKey, generatedSchemas and the declaration registry are the ones the gate itself sees. The copy was deleted before the first commit; it is in no diff.

Tree measured: objectstack-ai/objectstack at 88aa326deb — the merge base of this branch with origin/main.

population count
emitted defs (zodByDefKey, = bundled $defs) 1527
of those, emitted artifact carries additionalProperties: false 1117
of those 1527, defs resolving to exactly one declaration (naming anything or not) 258
of those 258, defs whose declaration NAMES a key the def does not declare 147
keys those 147 defs are promised 779
of those 779, keys the def delivers (prescribed) 770
of those 779, keys not delivered (declared-but-silent) 9
of those 9 keys, keys on shared/RateLimitConfig 2
defs carrying a not-delivered key 4
of those 4 defs, unions 3 (ui/ChartGroupBy, ui/ViewItem, ui/RecordHighlightsField)
of the 9 not-delivered keys, keys on those 3 union defs 7
defs with an empty shape (excluded outright) 6
declarations carrying an empty shape (what each of those 6 would answer to) 9
defs whose match is ambiguous 0
defs with no derivable shape 304

What validates the instrument is the rest matching, not the conclusion agreeing. Reproduced exactly against the card: 1117 · 258 · 147 · 779 · 770 · 9 · 2 · 6 · 9 · 0 ambiguous · 3-of-4 · 7-of-9. Drifted with src, as the card predicted: 1525 → 1527 emitted defs, 303 → 304 no-shape, 408 → 410 not-additionalProperties-false. Independent corroboration of the 1527/1538 pair from the generator's own summary line: check:authorable-surface prints bundled schema: objectstack.json (1527 definitions) / Successfully generated 1538 schemas.

Cross-check tying the partition to the gate's own function: every one of the 779 promised (def, key) pairs the census derived was fed back through the head's verbatim verdictFor0 came back none. A replication that invented a pair the real lookup does not hold would show up there.

Sequencing with PR #18861

PR #18861 (card #18578) repairs the shared/RateLimitConfig open-twin defect and therefore moves this exact partition (its author measures delivered 770 → 772, not delivered 9 → 7). It is absent from the tree measured here, proven twice: git merge-base --is-ancestor 36adecac58 HEAD exits 1 on a non-shallow checkout with a lit control leg (88aa326deb → exit 0), and the census itself still lists shared/RateLimitConfig:keyBy and :store as not-delivered, which is precisely the defect that PR removes.

So both replacement sentences are written as readings of a named commit (at 88aa326deb that state held …), not as claims about whatever main holds today. They stay true when #18861 lands; they become stale, visibly, with the commit that says so right there. The docblock also now tells its next reader that which defs sit in that state is a fact about the graph rather than a property of the proof — "closing an open door moves it" — and to re-measure rather than re-date. Nothing here waits on or depends on that branch.

Not taken: the delivers() fixture

The card's optional fourth item — a fixture for the message-includes leg, the one refusing a strict clone built without the declaration's error map — is handed back, with a reason rather than a shrug. Three routes exist and each is blocked by a rule this repo states out loud:

  1. A synthetic def on the shipped graph. packages/spec/src is published (files[] carries src/**/*.zod.ts), so this ships a fake schema to consumers.
  2. A synthetic def in the test sandbox. build-schemas-check-mode.test.ts builds each sandbox with fs.symlinkSync for src — its own comment says "src/ is the fixture's own, so the population a run observes is the repo's". A fixture def would have to be written into the real src, i.e. route 1. Copying src per sandbox instead is a structural change to a 4466-line harness.
  3. Exporting the leg for a unit test. delivers() is a closure over module-level state, and that file's header rule is no test-only seam.

So it is not a fixture-sized job; it is a restructuring, and the card is explicit that this PR should not become two things. Recommendation for whoever files it: route 2, as its own card, because it also unlocks fixtures for the other fail-closed legs. The census zero that defends the leg today is re-stated above with its tree, so the next reader can see what it rests on.

Verification

  • pnpm --filter @objectstack/spec build — green (runs gen:schema, i.e. the edited generator, end to end)
  • pnpm --filter @objectstack/spec typecheck — green (all three legs: tsc --noEmit, check:scripts-typecheck which compiles the edited file, check:test-typecheck)
  • pnpm --filter @objectstack/spec test — 487 files / 14055 tests passed
  • pnpm --filter @objectstack/spec exec vitest run --project repo scripts/build-schemas-check-mode.test.ts — 1 file / 85 tests passed. This is the suite that spawns the edited generator, and it is not in the package's pnpm test: that script runs --project local only, and vitest's filter guard says so out loud when you aim the wrong project at the file.
  • scripts/file-description.test.ts + scripts/def-key-collisions.test.ts (--project repo) — 128 passed; these are the only other tests that read this script by name.
  • 53 gate families derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack — 51 green. Two exit 3, which is that gate's own NOT-MEASURED code and neither a pass nor a failure: check:dual-build-cjs-loads and check:lean-entry-closure both need a whole-repo pnpm build that this worktree does not carry. Neither can move on a comment in an unpublished script; CI builds fresh.
  • pnpm lint equivalent run whole, not narrowed: npx eslint . --no-inline-config --format json at 8f9a42d8a26846 files, 0 errors, 0 warnings.
  • Control-byte self-scan over the edited file (grep -naP over the C0 range plus DEL) — no match; pnpm check:nul-bytes green.

No changeset (skip-changeset)

Nothing published moves. Measured rather than asserted: @objectstack/spec's files[] is dist · json-schema · liveness · prompts · llms.txt · README.md · src/**/*.zod.ts · CHANGELOG.md · api-surface · spec-changes.jsonno scripts/ entry — and grepping every shipped path for computeGuidanceRoutes returns 0 hits, against a lit positive control (keySetMatches, which is found in dist/shared/index.js and four more). The diff is comment-only inside that unpublished file, so the blast radius is the gate's next reader, not an author and not a consumer.


Generated by Claude Code

…lation

The proof-4 docblock in `packages/spec/scripts/build-schemas.ts` carried two
census figures that name a population they do not measure:

  - "on the shipped graph 7 of the 8 defs in that state are unions" — the
    not-delivered state holds 9 keys on 4 defs, 3 of them unions carrying 7 of
    the 9 keys. The 7 was the KEY count wearing the DEF label.
  - the closing line sent a reader to the census in a PR body whose own row
    labels "defs resolving to exactly one declaration that names an undeclared
    key" with 258 — the count of defs resolving to exactly one declaration
    whether or not it names anything. That population measures 147.

Re-measured on this tree rather than copied: the whole census was re-derived
with this head's own verbatim `computeGuidanceRoutes`, and every figure is now
written beside the population it counts, with the commit it was taken on.

The rationale both figures support is unchanged — unions do dominate the
not-delivered set (3 of 4 defs, 7 of 9 keys). Only the arithmetic moved.

Comment-only; this script ships in no tarball (`@objectstack/spec` `files[]`
has no `scripts/` entry), so the blast radius is the gate's next reader.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
…ead on

Both corrected figures now say WHICH tree they are a reading of, in the same
sentence as the number. A count anchored to a commit stays true when the graph
moves; an unanchored one silently becomes a claim about a tree nobody measured,
which is the same failure mode one level up from the one being fixed here.

The not-delivered partition in particular is a fact about the graph, not a
property of the proof — closing an open door moves it — so the docblock now
tells its next reader to re-measure rather than re-date these counts.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@os-bill os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 18, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 18cc3b1dfcbd080d864cf2372c24112df5cc4b9epackageMentionDocs.

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

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tooling

Projects

None yet

2 participants