Skip to content

fix(spec-docs): the in-shape enum marker stops restating vocabulary cardinality — one new error code touches 2 pages, not 11 - #9239

Merged
os-project-manager merged 3 commits into
mainfrom
claude/issue-9182-render-time-rollup
Aug 17, 2026
Merged

fix(spec-docs): the in-shape enum marker stops restating vocabulary cardinality — one new error code touches 2 pages, not 11#9239
os-project-manager merged 3 commits into
mainfrom
claude/issue-9182-render-time-rollup

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes#9182

Phase 1 — the measurement the card commissioned

The card flagged disposition (2) (compute the +N more roll-up at render time) as "disproportionately cheap" but explicitly unmeasured. Measured here, empirically: registered one probe code in the ledger, regenerated, diffed, reverted (ledger restored byte-identical, git hash-object 6bce76bf both sides).

What produces the +N more. Not a roll-up section — elideEnum / elideWithMarker in packages/spec/scripts/lib/format-type.ts, the type-cell renderer. hidden = members.length - shown.length, so the integer is a pure function of the vocabulary's cardinality. ApiError.code is 288 members (StandardErrorCode union the ledger), the in-shape sample shows 3, hence +285 more.

What one added code actually changes — 11 pages, 69 lines:

lineswhereis it about the new code?
2the code's bullet in error-code-ledger.mdx + contract.mdxyes — real content
1contract.mdx top-level count +281 to +282yes — sits directly above its own full list
66the in-shape marker +285 more to +286 more, across all 11 pagesno — pure cardinality echo

9 of the 11 pages carry nothing else. For analytics, auth, automation-api, batch, export, metadata, package-api, protocol and storage, 100% of changed lines are that marker.

Can it move to render time? No — and the reason is structural. The marker sits inside an inline code span in a markdown table cell. Verified against the docs site's own MDX 3.1.1 compiler: inside backticks, both {N} expressions and JSX components survive as literal text and are never evaluated; outside backticks {N} compiles. Moving the count to render time therefore means type cells stop being code spans — a restructuring of every one of the ~8500 type cells the corpus renders, for the benefit of one enum. Linking instead of expanding is also unavailable: the error envelope is fully inlined in the generated JSON Schema (no $ref, no $defs, a bare 288-member enum node), exactly as the module's own comment states.

Phase 2 — the shape the measurement supports

Render time is the mechanism (2) named; "adding a code touches one page" is the outcome it wanted. The measurement says the outcome is reachable far more cheaply than (2) assumed — by not encoding cardinality in the position that cannot substantiate it.

The count is kept wherever the page prints members to check it against, and dropped where it cannot:

  • TOP_LEVEL_ENUM_WIDTH_LIMIT (formatPropertyType) keeps it — its ### Allowed Values list is printed directly below, so the number is verifiable, and that page is rewritten by a vocabulary change regardless because it spells the vocabulary.
  • VARIANT_LIMIT (union arity) keeps it — arity does not grow with a ledger.
  • The in-shape summary copy loses it — the position this module already documents as the cheap one: "Eliding a copy inside a summary is nearly free — the full list is elsewhere, or the JSON Schema is the authority." On 348 of 805 in-shape occurrences no copy of the list is on the page at all.

Result

beforeafter
pages touched by one new error code112
lines693
pages changing with nothing to say about the code90

Both remaining pages (contract.mdx, error-code-ledger.mdx) print the code itself. One-time corpus cost: 39 files, 127 lines, all pure notation.

Two things deliberately held still

The elision SET is unchanged.elideWithMarker's refusal threshold is still measured against the quantified marker. Judged against the bare token instead, the guard gets cheaper to satisfy and three AppearanceConfig.allowedVisualizations cells — a 9-member vocabulary printed in full since #5340 — started truncating. That is an information loss on an enum unrelated to why the count is coming out, and it would have re-decided #5340's measured calibration as a side effect. One change, one effect.

#6226's "one omission style per table" ruling — please read this half. That ruling is the strongest argument against this change, so it is stated rather than buried. I read it as intact: the bare token is not a new notation, it is the one this very cell already uses for its key elision, which has never quantified what it withheld. The same cell renders { code: ...; message: string; category?: string; httpStatus?: integer; ... } — four keys and a countless marker. So the enum inside a summary now reads by the same rule as the summary containing it. What a reader loses is the magnitude of a list the page does not carry; what they keep is that it is a sample, which is what #5340 required against a silent prefix. If the maintainer weighs #6226 the other way, this is a one-line revert (quantify back to true in formatEnum) — the parameter is deliberately positional rather than a rewrite.

Verification — all at a68ddebea, the final commit

  • check:docs green — 228 generated files in sync
  • check:generated green — all 13 spec generated artifacts
  • pnpm --filter @objectstack/spec exec vitest run407 files, 10849 tests passed
  • check:scripts-typecheck, typecheck (incl. check:test-typecheck) green
  • Derived with node scripts/pm/dispatch-gates.mjs against the actual 41 changed paths, then run: check:nul-bytes, check:merge-driver, check:docs-redirects, check:quick-reference-counts, check:role-word, check:docs-audit-scope, check:cross-package-test-inputs, check:type-check-coverage, check:type-source-resolution — all green. check:scripts-typecheck is an addition I re-derived from the diff (the wrapper lists it as not-run, but the diff edits a script).
  • check:type-check-debtnot evaluable locally: it refuses to re-measure without the full workspace build closure on disk (54 unbuilt dependencies) and tells you to build it first, as lint.yml does. Precondition, not a finding: spec is not in the DEBT ledger, and both non-docs files are packages/spec/scripts/**, covered green by check:scripts-typecheck and check:test-typecheck.

Neighbour control (the card's central hazard — a total cannot detect a code dropped from some pages). main landed #9234 on exactly these 11 pages mid-flight; merged with scripts/pm/os-regen-merge.sh, regenerated as its own commit. Page-set comparison against the source-of-truth enum: contract.mdx288/288, error-code-ledger.mdx288/288, zero missing, zero extra, zero pairwise divergence, and main's declaredCode present on all 11 pages — main's side survived the regen.

Changeset

None, and that is a decision: nothing published moves. packages/spec's files does not include scripts/, so the generator and its test do not ship; content/docs belongs to @objectstack/docs, which is private: true. Labelled skip-changeset.


Generated by Claude Code

…ardinality
`+N more` is a function of a vocabulary's cardinality, so every reference page
carrying the marker was rewritten whenever that vocabulary grew by one.
Measured on `ApiError.code` (288 members, StandardErrorCode union the
error-code ledger) by registering a single code and regenerating: 11 pages, 69
lines, of which 66 are this marker — and 9 of the 11 pages contain nothing else
at all, 100% of their changed lines being `+285 more` -> `+286 more`. The ledger
is a per-PR append, so any two PRs registering a code were mutually exclusive by
construction, and the generated pages carry no conflict markers when a merge
drops one side.
The count is kept wherever the page prints members to check it against: the
top-level vocabulary row (its `### Allowed Values` list is directly below, and
that page is rewritten by a vocabulary change regardless) and the union variant
cap (arity does not grow with a ledger). Only the in-shape summary copy loses
it — the position this module already documents as the cheap one, "the full list
is elsewhere, or the JSON Schema is the authority".
Not a second omission style: the bare `…` is the token the same cell already
uses for its key elision, which has never quantified what it withheld.
The refusal threshold is still measured against the quantified marker, so this
changes the notation and never which bodies elide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26DJEHSBhhAQ6wwfsHNza
Discharges the os-regen deferral recorded by the merge commit. Carries main's
#9234 vocabulary change (`error.code` closed at every door) through the
generator together with this branch's unquantified in-shape marker.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26DJEHSBhhAQ6wwfsHNza
@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.

@os-project-manageros-project-manager added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/m labels Aug 17, 2026 — with Claude
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation size/m tests tooling labels Aug 17, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 17, 2026 05:56
@os-project-manager
os-project-manager added this pull request to the merge queueAug 17, 2026
Merged via the queue into main with commit 49a49a7Aug 17, 2026
33 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-9182-render-time-rollup branch August 17, 2026 06:15
os-steve pushed a commit that referenced this pull request Aug 17, 2026
…al clear)
Merge of origin/main (2abb66b) into the #9220 branch; regenerated
gen:schema + gen:docs + gen:openapi from the merged tree per
scripts/pm/os-regen-merge.sh step 4. Survival asserted: step-18 block,
six ElementFilterProps retired-keys entries, element-filter-removed
conversion, fixture control, and the reference pages under the merged
(#9239) generator.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs18A2DdXLVN2h8PaaFBcP
os-steve pushed a commit that referenced this pull request Aug 17, 2026
…_LEDGER (#9246)
Registers the ADR-0120 D5e posture-gate refusal the marketplace install seam
answers (409, isolated tenancy posture, installation-wide uniques) under
@objectstack/cloud-connection — already on the wire with a live reader
(packages/cli/src/commands/package/install.ts branches on the literal), found
by the #9223-widened dispatcher-vocabulary gate (objlitconst, plugin-route
door), and ratchets the discharged pending-registration row out of
packages/runtime/src/dispatcher-error-vocabulary.ts in the same change (the
gate reconciles in both directions).
Reference docs regenerated via check:generated --fix (gen:docs): 2 pages,
per the post-#9239 generator.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs18A2DdXLVN2h8PaaFBcP
os-zhuang pushed a commit that referenced this pull request Aug 17, 2026
…h main
Discharges the os-regen deferral recorded by the merge commit. Wholesale
`gen:schema && gen:docs && gen:openapi` from the merged tree, per
scripts/pm/os-regen-merge.sh step 4.
The regenerated diff against main is now 2 pages, not 11: #9239 landed on
main since round 2 and stopped the in-shape enum marker from restating
vocabulary cardinality, so a single new error code no longer bumps a
roll-up count on nine unrelated pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTKPDRoynY8i3HmdSFUxFj
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gateteststooling

Projects

None yet

2 participants

@os-project-manager@claude