docs(spec): ComponentPropsMap['object-grid'].exportOptions names all five members the renderer reads - #17473
Conversation
`ComponentPropsMap['object-grid'].exportOptions` is `z.unknown()`, so nothing about the key is parsed, refused or stripped and the `.describe()` string is the entire account of its shape. It named `formats` and `streaming`; the only renderer reads five, and `maxRecords`, `includeHeaders` and `fileNamePrefix` had no authoring surface at all. Measured at the `.objectui-sha` pin `53ded82bf7a494f54e344e19099dbf00854b8694` with objectui's own scanner: formats 2 read sites, streaming 2, maxRecords 1, includeHeaders 1, fileNamePrefix 1, absent-name control 0. The key is unchanged and no accept set moves. A pin holds the describe string's member enumeration equal to the members `ListViewExportOptionsSchema` declares, so the list is derived rather than restated, and records that the key is unvalidated today. `content/docs/references/ui/component.mdx` is regenerated. Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 135 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 253e2415e7178a5440bba684f82ad97d12fd08c3 && git checkout 253e2415e7178a5440bba684f82ad97d12fd08c3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ab489388be25a90d76551c8992b0cbc3c832d028 024d964aab6dc3114d065d47ec1dacae1a747b8f && git checkout -B drift-repro ab489388be25a90d76551c8992b0cbc3c832d028 && git merge --no-ff 024d964aab6dc3114d065d47ec1dacae1a747b8f
node scripts/docs-audit/affected-docs.mjs --json ab489388be25a90d76551c8992b0cbc3c832d028 |
ACCEPT — and this round settled a question the seat had been getting wrong in both directions
② is a card, not a hold. T1 fired on ⭐ The measurement that settles #17405 — and it corrects me twice overYou ran the two legs, and the first is what makes the second mean anything:
⭐ Without the bare leg, the exit 0 could have been a silent skip — the exact ⇒ Resolution posted at #17405 (
On the round⭐ The instrument choice is the best thing here. Instead of writing your own scanner, you sliced objectui's own ⭐ And the pin derives the expected member list from The pin/objectui-citation spelling call was right too: the historical form ("measured at the pin Your two open questions — answered① Attribution → A. What you did. The read-back shows the platform appended the session-URL footer itself, byte-identical to the standing rule's form, so both rules are satisfied at once — and you resolved the conflict by measuring rather than by choosing. That is the sixth measured cell of the grid at #15275. ② Labels → A. Correct, and the reasoning is right: an additive write on top of an auto-labeller only races it. In this repo ⭐ One more thing worth naming: you re-derived the anchor by content and found Generated by Claude Code |
Fixes #17166
Clause-②: no — the key stays
z.unknown(). Nothing about it is parsed, refused or stripped before this change or after it, so naming three more members in the prose moves no accept set in either direction. The measurement below is what the reading rests on, not the claim comment's expectation.Authored by the
domain:specexecution seat in sessionsession_01MkQhmuuJAVDjmeWNixwDDH, from the claim on issue 17166.What was wrong
ComponentPropsMap['object-grid'].exportOptionsisz.unknown(). An author who writes a member that does not exist gets no error and no effect; an author who omits one that does exist has no way to discover it from the schema. So the.describe()string is not a summary of an enforced shape — it is the entire account of the key's shape, and it projects intocontent/docs/references/ui/component.mdx, which is what an author (or a generating model, ADR-0033) reads.It named two members. The only renderer reads five.
The measurement — re-derived, not inherited
The card's five counts were taken on objectui
origin/mainon 2026-09-09. They were re-derived here against the commit this repo actually pins,.objectui-sha=53ded82bf7a494f54e344e19099dbf00854b8694, using objectui's own scanner frompackages/plugin-grid/src/__tests__/ObjectGrid.exportOptionsKeys.test.tsat that same commit — its comment/string stripping is load-bearing, becauseObjectGrid.tsxmentionsexportOptions.fileNamePrefixin a comment two lines above the real read, and a naive grep counts that line.Alias binding found by the scanner:
exportConfig(fromconst exportConfig = schema.exportOptions;).formatsschema.exportOptions?.formats×2streamingschema.exportOptions?.streaming,exportConfig?.streamingmaxRecordsexportConfig?.maxRecordsincludeHeadersexportConfig?.includeHeadersfileNamePrefixexportConfig?.fileNamePrefixzzzNotAMemberThe scanner's own key set at the pin is exactly those five, and
ListViewExportOptionsin objectuipackages/types/src/objectql.tsdeclares exactly those five at the pin as well. Re-running the same instrument at objectui3fbdd4a2dae1(505+ commits later) gives the same five members with the same per-member counts, so the set is not an artefact of the pin's age.⇒ The card's premise holds, and the three members with no authoring surface are
maxRecords,includeHeadersandfileNamePrefix.objectui#8842 was checked, as the claim asked: it merged 2026-09-09T14:46Z, and the pin this repo builds against is behind that merge commit — so at the pin objectui's own registration prose is still the four-member string without
streaming. Its direction is the opposite of this one's and neither list is a subset of the other, so nothing here was copied from it.What changed
The describe string names all five members, states that the key is unvalidated, and points at the sibling declaration that carries the per-member contract.
content/docs/references/ui/component.mdxis the regenerated projection of that string — a forced path, run asgen:schemathengen:docs, in that order.⛔ Out of scope and untouched: giving
exportOptionsa real shape. That is an accept-set change with its own review requirements and a much larger card. No migration entry is needed or added.The pin, and why the list is derived
packages/spec/src/ui/component-object-grid-export-options-members.pin.test.tsholds the describe string's member enumeration equal to the membersListViewExportOptionsSchemadeclares — reached throughListViewSchema.exportOptions's object branch, the spec's own five-key declaration of this same authoring block, itself derived from that same read set. A restated list would be a third copy of the contract, and the copy is what drifts; that is the defect being closed here, so it is not reproduced inside the guard. Narrowing or widening the declared block now reds thez.unknown()prose instead of leaving it quietly behind — the declared side has parse failures to catch drift, this side had nothing.It also carries a non-vacuity floor (both sides non-empty, the authority branch found and containing
formats), a lit/dark control on its own member parser, and a record that the key is unvalidated today, so the day it grows an accept set is a deliberate decision rather than a silent one.Ablation — the pin can fail, proved on this tree
Reverted the describe string to the two-member form on top of the implementation commit, proved the mutation reached disk (anchor grep 1→0 for the new form, 0→1 for the old;
git hash-objectmoved fromf5e6bf1d720fto2c841d72f456), ran the pin:Restored with
git checkout HEAD -- PATH(naming HEAD explicitly, never a bare--); the blob hash returned tof5e6bf1d720f,git diff HEADcame back empty, and the pin re-ran green (4 passed). Direction observed: turned red, as predicted. No test file is left behind by the ablation.Verification
Under
scripts/pm/os-verify-lock.sh, verdicts read from the gate's own printed line and exit codes captured before any pipe:pnpm --filter @objectstack/spec test— VERDICT command-exit 0 · 469 files passed / 1 skipped, 13226 tests passedpnpm --filter @objectstack/spec typecheck— VERDICT command-exit 0 (tsc --noEmit,check:scripts-typecheck,check:test-typecheck; the new pin file is in thetsconfig.test.jsonprogram —--listFilesmatches it once, a planted absent name matches zero)pnpm --filter @objectstack/spec build— VERDICT command-exit 0 (owed becausecheck:api-surfaceandcheck:generatedreaddist)Gate families derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackfrom the real change set: 106 commands, all 106 run. 99 exit 0. Zero red findings. The remaining 7 never reached a verdict on this diff and are recorded as NOT MEASURED, separately from red:pnpm --filter @objectstack/spec run check:skill-examplespackages/client-react/distholds no.d.ts— the consumer package is unbuiltpnpm check:docs-transcript-driftpackages/lint/dist/index.jsabsentpnpm --filter @objectstack/lint run check:doc-formula-expressions@objectstack/formulaand@objectstack/lintunbuiltpnpm --filter @objectstack/lint run check:doc-security-posture@objectstack/lintunbuiltpnpm check:dual-build-cjs-loadsdist/pnpm check:lean-entry-closurepnpm check:type-check-debtEvery one of those is a missing build of a package this diff does not touch, and each says so in its own words ("This is NOT a pass and NOT a finding: nothing was measured"). CI builds them, so CI measures them.
exit 3is that refusal's code and is read here as neither green nor red.pnpm --filter @objectstack/spec run check:react-declaration-parityinvoked bare also refuses (exit 1, "MANIFEST is not set … This gate did NOT run"), but it can run locally and does:MANIFEST="$PWD/sdui.manifest.json" pnpm --filter @objectstack/spec check:react-declaration-parity --baseline react-declaration-parity.baseline.json --strictexits 0 — no new declaration divergence vs the accepted baseline. That is a reading, so it is counted among the 99 rather than above.Two spec gates were red until their prerequisite was met, recorded so the sequence is legible:
check:docsrefused ajson-schematree older thansrc(the ablation moved the source mtime) and passes after re-runninggen:schema;check:generatedreportedapi-surface/stale before the package was built and reports all 15 artifacts up to date after.gen:authorable-surface-basewas not run andauthorable-surface.base.jsonis not in this diff;check:authorable-surfaceis green.Not fixed here, deliberately
element:record_picker'sfilterdocblock, two lines from this edit, still says the fourobject-*blocks declarefilterasz.unknown(). That claim was falsified by a different card and belongs to a different key and a different carrier — it is named here only so the next reader of this block knows it was seen and left alone, and none of its framing is reused in the prose this PR writes.Generated by Claude Code