docs(readme): compile the root README's snippets against the shipped surface - #8312
Conversation
…surface
The root README's five ts/tsx fences held six semantic diagnostics across
three blocks, and the page was the LAST row on `UNGATED_DOCS`. Each block is
its own module in the gate's program, so each repair is local:
* fence 99 (minimal integration) declared `myAPI` and `MySidebar` — the two
pieces a reader brings — as typed stand-ins: `DataSource` from
`@object-ui/types`, which is what `ObjectView.dataSource` REQUIRES further
down the same block, and React's `FC`, because `AppShellProps.sidebar` takes
a `ReactNode` and the page renders `<MySidebar />`.
* fence 382 imports its own `SchemaRenderer` instead of continuing an earlier
block, and declares `schema` as `BaseSchema` — what `SchemaRendererProps`
accepts.
* fence 398's `// ... other methods` elision (TS2420 + TS2355) became an
ambient `declare class … implements DataSource<T>` carrying all six required
members with no bodies: the contract complete instead of hidden, and
re-judged by the gate whenever `DataSource` moves.
* fence 137's `const schema` literal was measured UNBOUND — neither its member
names nor their values were checked by anything — and is now annotated
`ObjectFormSchema`.
The ledger row is deleted, so `UNGATED_DOCS` reaches `{}` and the gate prints
`0 ungated`. Its declaration, header, readers, summary line and export are
untouched, and the strictness region hashes byte-identical to the base. The
objectui#7115 pin flips with the row: from "DECLARED debt" to covered AND
contributing blocks to the compiled tier, so leaving the ledger with no block
left cannot pass for coverage.
Part of #5174
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
ACCEPT (batch 37, the root Verified from the diff and the tree, not the report:
On the dev's open question (the attribution trailer): option A, and it is now measured rather than assumed — the squash path normalises it. Batch 35 ( Next: CI convergence → flip ready → post-flip guard → auto-merge SQUASH → queue confirmation → landing probe ( Generated by Claude Code |
|
Armed: PR #8312 flipped ready at 2026-09-07T10:39:18Z on head Generated by Claude Code |
|
Queue reading, recorded rather than acted on — PR #8312 has been in the merge queue since 10:39:41Z (33 min). Its merge-group head Nothing about this PR is red, and nothing about it changed. Next re-check is patrol 30 at 11:24Z. — recorded so the timeline reads correctly. Generated by Claude Code |
|
Correction to the reading above ( Generated by Claude Code |
|
LANDED: merged at 2026-09-07T11:32:20Z as The attribution question resolved as predicted: the landed commit carries a plain #5174 is re-read and closed in the next stroke. — Generated by Claude Code |
Part of #5174 (batch 37: the root
README.md— the ledger reaches zero)DataSourceat T", "Promise of boolean") rather than with angle brackets, because this repository has measured GitHub silently deleting tag-shaped fragments from stored bodies — inside backticks and fenced blocks alike. See AGENTS.md, "GitHub 会改写你写进 issue/PR 正文的字节".The root
README.mdwas the LAST row onUNGATED_DOCS. Its five ts/tsx fences now compile against the builtdist/*.d.ts, the row is deleted, and the gate prints 0 ungated.Census — measured with the gate's own
scanFences+compileSnippets, never a regexReading 1, on the base
0ea7054f8(the ledger emptied for the measurement so the page is judged):myAPI, TS2304MySidebarSchemaRenderer, TS2304schemafindOne,create,update,delete,getObjectSchema) + TS23555 fences, 0 parse failures, 0 root-bound refusals, 5 of 5 judged, 6 diagnostics across 3 blocks. The ledger row's wording — "4 undefined-name … 2 elided-body (TS2420, TS2355)" — is accurate in count AND classification, the only row on this ledger that stayed both. Harness controls: sentinel 1 (TS2305), positive 0, undeclared 1, root-declared 1, srcLeaks 0, resolution to
packages/types/dist/index.d.ts.Reading 2, after the repairs: 5 fences, 0 diagnostics, 0 failed. Gate summary now reads
Scanned 229 document(s): 229 covered (126 of them hold a ts/tsx block), 0 ungated·631 of 631 block(s) judged, 0 failed.Negative controls — were the two PASSING fences bound?
Length-preserving decimal mutations on the base page, trap/restore discipline, every leg proven on disk before its result was read and proven restored by blob equality plus
git diff HEADat 0 bytes.objectName"object-form"typeNAMEProperty 'type' is missing … but required in type 'BaseSchema'columnsNAMEBaseSchema's index signature absorbs ittypeVALUEType 'number' is not assignable to type 'string'items[].labelNAME⇒ fence 137 was FULLY UNBOUND — an unannotated literal nothing consumes, so all four authored keys and all four values were decoration. Fence 214 is bound at exactly one member:
BaseSchema.type, the interface's only required key, at the OUTER literal only (objectui#7927's index signature is the bound; measured here, not assumed).What changed, and why
Fence 99 — the two ambient names became typed stand-ins:
declare const myAPI: DataSource(from@object-ui/types, which is whatObjectViewProps.dataSourceREQUIRES three lines further down the same block) anddeclare const MySidebar: FC(becauseAppShellProps.sidebartakes aReactNodeand the page renders the value as an element).Fence 382 — imports its own
SchemaRendererinstead of continuing an earlier block (each fence is its own module in this gate's program), and declaresschemaasBaseSchema, which is whatSchemaRendererProps.schemaaccepts.Fence 137 — bound to
ObjectFormSchema. Decided under the four axes: business — this is the landing page's flagship "Stop Writing Repetitive UI Code" literal, the first metadata object most readers copy; long-term — the annotation moves when the type does; anti-AI-error — DECIDING: an unannotated metadata literal is exactly what an AI copies and then edits, andmode: "created"is the plausible edit that nothing could refuse before (leg M2c now refuses it by name); startup focus — oneimport typeand one annotation, no new surface.Fence 398 — the
// ... other methodselision.DataSourcewas counted mechanically off the builtd.ts: 38 members, 6 required, 32 optional, no index signature. Route (c) taken — an ambientdeclare class MyCustomDataSourceat Timplements DataSourceat T carrying all six required members with no bodies:packages/types/examples/rest-data-source.ts, real, on main) implements exactly those six. The doc and the example now teach the same set.implementsIS enforced on adeclare class(TS2416), so this is a checked contract, not a picture of one.// ... other methodsis the elision an AI copies and ships — a class that satisfies nothing and dies at the firstfindOne. Naming every required member is the structural fix; route (a) (bodies that only satisfy the type) would teach fake implementations, which is worse than teaching nothing.⛔ No fragment marker, no
@ts-expect-error, noany, and nothing in this gate's strictness region moved.Fence tags — measured, NOT corrected.
TS_FENCE_LANGUAGESists/tsx/typescript,check:doc-fencestreats all three as TypeScript, andcompileSnippetsparses every block as TSX regardless of the fence label (its own comment states this is deliberate). So thetypescripttag on the two JSX-bearing fences is not wrong by either gate's judgement, and re-tagging would have been an unmeasured change.Fence 214 — deliberately NOT annotated. The precise type of that node is
PageNodeSchema, and annotating the literal with it turns the landing page's own example RED:PageNodeSchema.bodyisSchemaNode[], array-only, whileBaseSchema.bodyisSchemaNodeorSchemaNode[]and the only reader (FlatContent) explicitly accepts a single node behind a cast. The example matches the runtime; the declaration does not. Filed as #8310, not repaired here.Per-key table — every key/prop authored across the five fences
DataSourceProvider.dataSourceproviders/src/types.tsDataSourceProviderPropsDataSourceProviderbodyany— value unchecked (leg M1 GREEN)AppShell.sidebarapp-shell/src/types.ts:19ReactNodeAppShellObjectView.schemaplugin-view/src/ObjectView.tsx:230ObjectViewSchemauseSettledSchematype/objectNametypes/src/objectql.ts:1774/:1779ObjectView.dataSourceObjectView.tsx:268, requireduseDataSource()'sanyreturn, so unchecked in this blockmyAPI's declared typeMySidebar's declared typetype/objectName/modeObjectFormSchema:1190/:1210/:1215fieldsObjectFormSchemafields?: string[]type(outer)BaseSchema.type: stringSchemaRenderertitle,body,columns,items[].type/label/valueBaseSchemaindex signaturePageRendererat runtimeSchemaRenderer.dataForwardedProps, Record of string to anycreateObjectStackAdapterconfigbaseUrl/tokendata-objectstack/dist/index.d.tsfactory paramSchemaRenderer.schemaSchemaRendererProps.schemaSchemaRenderer.dataSourceForwardedPropstypes/src/data.tsDataSourceimplements(leg M3 RED)Phantoms dropped: none — no fence on this page authors an undeclared key.
Positive controls — on the COMMITTED tree, against the real gate
Trap on EXIT INT TERM with absolute paths from
git rev-parse --show-toplevel; restore isgit checkout HEAD -- ABSOLUTE_PATH, never a bare checkout; every mutation proven ON DISK (injected/removed anchor counts plus blob inequality against the HEAD blobd086dd5d0e8383b5bd5133acfa773d432bf8ac52) BEFORE any result was read; every restore proven by blob equality ANDgit diff HEADat 0 bytes, never by an exit code. The harness refuses a mutation that is not length-preserving — it did so once, and that leg was rewritten rather than run.dataSource={myAPI}value to a numberDataSourceProviderProps.dataSourceisanymyAPI's stand-in type to a numeric literal typeobjectNamekey in theobject-viewliteralProperty 'objectName' is missing … but required in type 'ObjectViewSchema'fieldsrenamedBaseSchema's inherited index signature (objectui#7927)objectNamerenamed… required in type 'ObjectFormSchema'— the exact diagnostic NC_B proved IMPOSSIBLE on the basemodevalue to a numberType 'number' is not assignable to type '"create" | "view" | "edit"'delete's return type to a numeric literal typeProperty 'delete' in type 'MyCustomDataSource' … is not assignable to the same property in base type—implementsIS enforced on an ambient classMySidebar'sFCto a numeric literal typebaseUrlkey renamedObject literal may only specify known propertiesschema'sBaseSchemato a numeric literal typeLedger and strictness region
UNGATED_DOCSis now{}. Its declaration,@typetag, the header comment,analyze({ ungated = UNGATED_DOCS }), the summary line and the export are all untouched. One header sentence that went FALSE at zero was rewritten — it claimed "what remains here is.mdxpages undercontent/docsplus package READMEs" — and it now records the terminal state plus the ⛔ rule that an empty object is not an invitation to park the next failing page.Strictness region (from the
── Fence scanningbanner to EOF),awk … | sha256sum:0ea7054f8:2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084b94e842dad:2749d53ae3a8df033a53b8d7a354fa7e22ee2d1a17f6ad0c3d61122e904e084bThe objectui#7115 pin moves with the row: it asserted the root README was DECLARED debt with a reason naming objectui#7417. It now asserts the far end — off the ledger and contributing blocks to the compiled tier, so a page leaving the ledger by having no ts/tsx block left cannot pass for coverage.
Prose claims verified against the tree
@object-ui/componentsdeclaressideEffects: trueand its barrel doesimport './renderers'(packages/components/src/index.ts:36).registerDefaultRenderersexists nowhere in the tree except a pin test asserting its absence.dataSourcereaches the resolved component throughSchemaRenderer's declared pass-through surface (ForwardedProps), so it is forwarded to whatever the schema names; whether that component reads it is the component's business, and the pass-through is unchecked by construction.DataSourceinterface" — TRUE, and the repaired fence now shows exactly which members "implementing" means.Gates — pinned to
94e842dad, redirect-then-capture, exit codes never read through a pipepnpm check:doc-snippets— exit 0: 229 scanned, 229 covered, 0 ungated; 790 covered blocks (631 to compile, 159 declared fragments); 631 of 631 judged, 0 failedpnpm exec vitest run scripts/__tests__/check-doc-snippet-types.test.ts scripts/__tests__/check-doc-fence-languages.test.ts— exit 0: 2 files, 123 testspnpm exec vitest run scripts/__tests__/— exit 0: 118 files, 3491 testspnpm check:doc-types— exit 0: 188 docs, 889 type literals, every documented component type registeredpnpm check:readme-exports— exit 0: 532 self-imports judged, 532 real, 0 fabricated.packages/NAME/README.md, so the root README's import names are read by no gate — the ledger row said so, and that stays true after the row is gone. The compile gate now judges whether those imports RESOLVE and type-check, which is a different question from whether the named symbol is a real export of the package that owns the README.pnpm check:doc-fences— exit 0: 227 documentsnode scripts/check-doc-links.mjs— exit 0: links valid across 17 scan roots (including./packages/types/examples/rest-data-source.ts)pnpm type-check:scripts— exit 0pnpm lint:root— exit 0: 32 problems, 0 errors, 32 pre-existingno-explicit-anywarnings, none in any changed filepnpm check:control-bytes— exit 0: 6602 tracked text files; plus agrep -naPself-scan of all three changed paths, no matchpnpm check:entry-guard— exit 0: 72 scripts/ filespnpm check:comment-mask-corpus— exit 0: 4426 files, 1 disagree, unchanged at objectui#7882's ceilingnode scripts/check-changeset-presence.mjs— exit 0: 3 files changed, 0 published sources, no changeset owednode scripts/check-governed-queue-guard.mjs --teston all three paths — exit 0, NOT GOVERNEDpnpm exec turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2— exit 0, 35/35TURBO_SCM_BASE=0ea7054f8 pnpm exec turbo ls --affected— 0 packages: this diff touches no workspace package, so no packagetest/typecheckis owedCI is not awaited: the report is delivered at draft-PR time per the dispatch contract.
Out-of-scope finding
PageNodeSchema.bodyis array-only while its only reader casts around that to accept a single node — and the root README's flagship example authors the refused form #8310 (finding, unassigned):PageNodeSchema.bodyis array-only while its only reader casts around that to accept a single node — and the root README's flagship example authors the refused form.Dedup method (no
/search: 403 for this seat): REST listing of all 390 open non-PR issues across 4 pages, matched locally. Control termsreadme(15 hits) anddatasource(11 hits) both answered, so the read is real. Two candidate findings were withdrawn as already filed:DataSourceProviderProps.dataSource/useDataSource()typedany, and@object-ui/app-shell's unexported near-duplicateDataSource— both are already named in #8160, with #7912 as the adjacent root-cause card. Nothing new was filed for either.Generated by Claude Code