Repository navigation
check-doc-component-types collects only .mdx too — 26 .md guides teach 345 type literals inside fences with nothing checking them #5342
Description
Activity
Triage (triage seat, session
session_014qTKTqjme5Fp5BH9iRmy6t): gradedpm:queue+tooling, type Task.Rationale: concrete, scoped gate-coverage gap with a measured population (26
.mdguides, ~345 fencedtypeliterals) and a proven sibling precedent (#5174 / PR #5341, where the same extension gap yielded 28/28 first-collection failures). The card's own honest framing holds: this gate's docblock states the.mdxsurface, so it is a coverage decision, not a broken promise — the first task is the measurement pass, absorbed byDOC_TYPE_EXEMPTIONSentries plus real key fixes.Serial constraints for the dispatching seat (fold-or-serial is a mandatory answer, not a default):
- finding(docs/gate): check-doc-component-types 的扫描面止于代码围栏 —— 八个 PR 新加的「真实 register 键表」全部在门外,且门从不判 namespace 半边 #5106 (open, queued, this card's parent) proposes changes to the same
scanDocs/walkFilesregion — same hot filescripts/check-doc-component-types.mjs. Sequence or fold; do not run in parallel. - check-doc-snippet-types.mjs collects only
.mdxunder content/docs — 40.mdguides are uncovered with no UNGATED_DOCS entry, contradicting its own "covered by default" rule #5174 / PR fix(scripts): doc-snippet gate collects .md under content/docs, and states its scan surface #5341 (in flight) is the sibling gate — different file, but merge its landed pattern first so the two collectors converge on one.md-walk spelling.
Generated by Claude Code
- finding(docs/gate): check-doc-component-types 的扫描面止于代码围栏 —— 八个 PR 新加的「真实 register 键表」全部在门外,且门从不判 namespace 半边 #5106 (open, queued, this card's parent) proposes changes to the same
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actionsClaim: PM loop round 19
Session:session_01RV6yuVCxymHYE16PL9vQkE
Branch:claude/issue-5342-doc-component-types-md
Worktree:objectui-issue-5342
Domain:repo:objectui(execution seat)
File surface:scripts/check-doc-component-types.mjs(the collector +DOC_TYPE_EXEMPTIONS), its test, and the.mdguides whosetypeliterals need real fixes (stop on breach; explain in the report)
Container & model: L,mode:subagent,model: opus— no path-derived mandate fromdispatch-gates.mjs; tier is this seat's call, taken at opus for the size of the measurement pass.
Clause-②: no — a gate collector widening plus documentation corrections; no runtime contract moves.
Serial constraints — the mandatory fold-or-serial answer is SERIAL, and it is mine, not yours to revisit:- finding(docs/gate): check-doc-component-types 的扫描面止于代码围栏 —— 八个 PR 新加的「真实 register 键表」全部在门外,且门从不判 namespace 半边 #5106 is this card's parent and proposes changes to the same
scanDocs/walkFilesregion of the same file. It stays inpm:queue, undispatched, for exactly this reason. ⛔ Do not fold it in: its two halves (the scan surface stopping at code fences, and the gate never judging the namespace half) are separate questions from the extension gap, and taking them here would make this PR unreviewable. If you find yourself editing for either, stop — that is the breach signal. - check-doc-snippet-types.mjs collects only
.mdxunder content/docs — 40.mdguides are uncovered with no UNGATED_DOCS entry, contradicting its own "covered by default" rule #5174 / PR5341 — discharged. It merged, and its sibling PR5364 (The getting-started guides teach ~14 symbols the packages do not export — one decision per symbol, applied everywhere (the #5160 class, oncontent/docs/guide/**) #5343) merged too. Triage's instruction was "merge its landed pattern first so the two collectors converge on one.md-walk spelling" — that pattern is now onmainatf2e11ae6f. Readscripts/check-doc-snippet-types.mjsfirst and converge on its spelling (DOCS_ROOT,DOC_EXTENSIONS = ['.mdx', '.md'], and a docblock that states the scan surface alongside the coverage rule). Two collectors walking the same tree two different ways is the defect one level up.
The card is honest that this is a coverage decision, not a broken promise
Unlike #5174, this gate's docblock states the
.mdxsurface — it does not claim to cover what it skips. So the first deliverable is the measurement pass, not an indictment. Report the real numbers; the card's ~26 guides / ~345 literals came from an earlier count and the tree has moved.The ratchet, in the form that made the sibling cards safe
Absorb what you measure into
DOC_TYPE_EXEMPTIONSwith the measured mix and a reason string that names what is wrong, and fix the real key errors. ⛔ Never widen an entry, never add one to paper over a fixable error, never relax the gate, never move a threshold. If a page gets worse under your change, that is a defect in the change.⚠️ A registered-key check has a specific failure shape, measured on a sibling card this round: the reader list on another PR saidobject-pivot-table, which is a component file — the key it registers isobject-pivot. Take the key set from the actualregister(...)calls, not from file or component names, and counter-probe your zeros with a key you know is registered.Scan-root caution
Report which roots your widened collector actually walks. If
skills/**is in scope, note it —skills/objectui/guides/i18n.mdhas a concurrent claim (#5081) this round, andskills/objectui/guides/{schema-expressions,data-integration}.mdplusrules/protocol.mdare in PR5371 awaiting maintainer merge. ⛔ Do not edit any of those four files' content here; if the gate flags them, ledger it and say so.Verification
Run the gate and its test — a gate script passing is not its test passing; the tests carry hardcoded ledgers the script never touches. Reverse-verify the collector widening: revert it and show the newly-collected pages stop being seen. Build artifacts sit between the edit and the result whenever the gate resolves
exports.typestodist/— rebuild and say so per leg. All runs from the repo root.Changeset: docs + gate ledger.
⚠️ A sibling card this round measured thatpatchmoves all 39 packages of the fixed group for a change with no runtime effect, and used empty frontmatter instead — this repo's declared "no release" form. Judge which applies to your diff and say why in the PR; ⛔ nevermajor.Draft PR,
Fixes #5342,Part of #5106.
Generated by Claude Code
- finding(docs/gate): check-doc-component-types 的扫描面止于代码围栏 —— 八个 PR 新加的「真实 register 键表」全部在门外,且门从不判 namespace 半边 #5106 is this card's parent and proposes changes to the same
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actions{ "issue": 5342, "status": "done", "branch": "claude/issue-5342-doc-component-types-md", "pr": "https://github.com/objectstack-ai/objectui/pull/5376", "premise_still_valid": true, "summary": "Widened scripts/check-doc-component-types.mjs from an .mdx-only walk to DOC_EXTENSIONS = ['.mdx', '.md'], converging on the exact spelling check-doc-snippet-types.mjs landed in PR5341/PR5364 (DOCS_ROOT + DOC_EXTENSIONS + a docblock stating the scan surface beside the coverage rule). Measured before/after on f2e11ae6f: 143 -> 183 doc files, 632 -> 1052 code blocks, 564 -> 887 `type` literals, 476 -> 742 registered, 88 -> 145 exempted, findings 0 -> 0. First collection produced 67 findings across 15 .md pages (323 new literals, 256 of which already named registered keys); the card's earlier ~26 guides / ~345 literals estimate is close but the tree has moved. Ten of the 67 were real key errors and are fixed: ObjectGrid/ObjectForm/ObjectDetail -> object-grid/object-form/detail-view in building-crud-app.md (PascalCase never resolves, the lookup is case-sensitive), empty-state -> empty in expressions.md and schema-rendering.md, grid-layout -> grid in schema-playground.md, and the Email field's `link` -> `email` twice in api/schema-reference.md. The other 57 sites became 31 (file, value) DOC_TYPE_EXEMPTIONS entries, each with a reason naming the vocabulary and where it is declared: ActionSchema discriminants, ComponentInput.type in register() inputs[], walkthrough keys the pages register themselves, dashboard widget kinds, AppSchema menu kinds, object-metadata field types, and package.json's own \"type\": \"module\". Nothing was widened, nothing relaxed, FLOORS is byte-identical. This gate walks content/docs ONLY, so skills/** is out of scope and none of the four off-limits files is reachable or touched. Serial constraint held: #5106 is untouched — the fence loop is byte-identical to main except for the one walk predicate, and no namespace logic was added.", "tests": "All runs from the repo root; heavy runs held /tmp/os-heavy-verify.lock. (1) GATE: `node scripts/check-doc-component-types.mjs` -> 'Scanned 183 doc file(s) (.mdx + .md), 1052 code block(s), 887 `type` literal(s) against 659 registered key(s) ... 742 registered, 145 exempted. / Every documented component type is registered.' exit 0. (2) ITS TEST (reported separately because the test carries hardcoded ledgers the script never touches): `npx vitest run scripts/__tests__/check-doc-component-types.test.ts` -> 'Test Files 1 passed (1) | Tests 34 passed (34)' exit 0; 7 tests added (2 fixture-level, 1 repo-level walk pin, 4 pinning the key fixes). (3) COUNTER-PROBED THE ZEROS before trusting any of them, since a zero from a broken matcher reads exactly like a clean result: universe = 659 keys taken from the actual register(...) calls; probe returned YES for object-grid / object-form / grid / form and no for ObjectGrid / object-detail / empty-state / grid-layout. (4) REVERSE-VERIFICATION, from the committed state, and the direction is NOT the template's: reverting only the walk to .mdx-only gave files 183 -> 143, literals 887 -> 564, .md pages carrying a scanned site 20 -> 0 (so the new pages do stop being seen) but the gate goes RED, exit 1, 31 problem(s) all `stale-exemption`, because all 31 new ledger entries lose their sites at once; the test went '5 failed | 29 passed'. Restore leg proved by marker ABSENCE (grep for f.endsWith('.mdx') returned nothing) before re-reading, then re-ran green 34/34 and gate green. (5) BUILD, PER LEG: this gate has NO dist between edit and result — every import is a node: builtin, which its own test asserts — so no leg was rebuilt and none needed to be; stating that rather than claiming a rebuild that did not happen. The SIBLING gate does resolve to dist/ and my doc edits touch five pages in its ledger, so it was owed a real run: built its 15 --build-filter packages, then PROVED the artifacts landed in this worktree rather than trusting a turbo FULL-TURBO cache hit that replayed logs from /home/user/objectui-issue-5357 (checked 8 packages' dist/index.d.ts exist with fresh mtimes), and its own resolution control confirms it: \"'@object-ui/types' was successfully resolved to /home/user/objectui-issue-5342/packages/types/dist/index.d.ts\"; result 'Semantic phase: 87 of 87 block(s) judged, 0 failed.' exit 0, ledger still exact. (6) OTHER GATES touched by these paths, all green: check:control-bytes (4761 tracked text files) plus a manual control-byte grep over my own diff (clean), check-doc-links.mjs (13 scan roots), check:skills-paths; and the three docs test files together -> 141 passed. (7) GATE-SET DERIVATION: scripts/pm/dispatch-gates.mjs does NOT exist in objectui (it lives in objectstack), so the set was derived from this repo's 14 check:* scripts against my changed paths instead. Every number above and the whole gate union were taken at the final commit 0a6cba7b9, which is the branch head.", "open_questions": [], "out_of_scope_findings": [ "filed as #5373: `type: 'crud'` has four declaration faces (interface, zod mirror, validator branch, builder) and no registered renderer, yet sits on the render path — api/schema-reference.md documents it as reference material. Ledgered here with a DELETE-when-it-lands note rather than re-spelled, because there is no registered spelling to move to; which of register/retire/demote is right is the contract question #5115 left open after PR5128 landed only its CLI half.", "filed as #5377: guide/building-crud-app.md addresses every renderer with `object` (and `recordId`), but object-grid / object-form / detail-view all declare `objectName` (and `resourceId`), required. The second dimension this gate explicitly excludes. Note the interaction: fixing the `type` keys converts this from a loud OBJUI-001 panel into a silently empty grid (#5349's shape), which argues for doing both, not for reverting either." ] }Draft PR: #5376 — reported at draft-PR time per the 2026-08-10 ruling; CI convergence is the PM's to read.
Generated by Claude Code
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actions{ "issue": 5342, "status": "done", "branch": "claude/issue-5342-doc-component-types-md", "pr": "https://github.com/objectstack-ai/objectui/pull/5376", "premise_still_valid": true, "summary": "FOLLOW-UP ROUND: the coordinator asked me to also correct object -> objectName and recordId -> resourceId in building-crud-app.md so the page renders, with three things to check rather than assume, and an explicit instruction to stop and report if any came back non-mechanical. Check 3 came back non-mechanical, so I stopped: PR5376 is UNCHANGED and nothing was pushed. Check 1 (is `object` an alias?) — NO, confirmed at runtime, not just by grep: rendered through the real registry with a fake adapter, the grid snippet as the page reads today makes find() 0 times and shows no rows, and after the single rename makes find('task', {$top:50,$skip:0}) once with the row rendering; the form goes from findOne 0 / getObjectSchema 0 / no fields to findOne('task','42') with fields rendering. The only `object`-spelled key the system honours is the NESTED spec binding schema.dataSource.object (useElementDataSource.ts:139), which ElementDataSourceGate maps onto objectName — which is exactly why the top-level spelling is a plausible mis-read. I also found one error in my own #5377: `recordId` is CORRECT on object-form (ObjectForm.tsx:539 calls findOne(schema.objectName, schema.recordId)); it is merely undeclared in the registry inputs, a different defect. recordId -> resourceId is right only for detail-view. Check 3 (does it render?) — NO, and this is the blocker: the grid/form results above hold only under SchemaRendererProvider, which this guide never uses. It imports only SchemaRenderer and passes dataSource as a prop at all five call sites. object-grid and object-form read the adapter from CONTEXT via wrappers; detail-view is registered as the raw component and takes dataSource as a PROP. The two wirings are mutually exclusive and I measured both directions: under a provider, grid find=1 and detail findOne=0; under the prop form, grid find=0 and detail findOne=1. So making this page render is not a prop rename — it needs the example App restructured onto a provider, and it exposes a framework-level split that also puts the page in direct contradiction with AGENTS.md commandment #1. Filed as #5378 with the full both-directions table, and #5377 updated with the runtime numbers plus its own correction. Recommendation: a rename landing ALONE is the worst of the three states, because it removes the loud OBJUI-001 signal that PR5376's type fix already converted to silence, while still rendering nothing.", "tests": "All runs from the repo root; every run held /tmp/os-heavy-verify.lock. The evidence this round is four throwaway probe files rendered through the REAL registry with a fake adapter — all four were deleted afterwards and `git status` is clean, `git diff` against the pushed head is empty, so PR5376 is byte-identical to what I reported last round (HEAD still 0a6cba7b9). NO REBUILD WAS NEEDED OR DONE, and the reason is checked rather than assumed: the root vitest.config.mts aliases every @object-ui/* specifier to the package's src/ (lines 245-257), so these probes read source, not dist — there is no stale-dist false-green available on this path. PROBE 1 (grid, 3 legs): LEG A exact current snippet {type:'object-grid', object:'task', view:'all', data:{objectSchema}} -> find calls 0, 'Write docs' present false. LEG B object->objectName only -> find calls 1, [\"task\",{\"$top\":50,\"$skip\":0}], 'Write docs' present true. LEG C objectName with `data` dropped -> identical to B, proving view/data are inert but harmless and do NOT need to move with the rename. PROBE 2 (form + detail, 5 legs): FORM A today -> findOne 0, getObjectSchema 0, Title false; FORM B object->objectName with recordId LEFT ALONE -> findOne 1 [\"task\",\"42\"], getObjectSchema 1, Title true (this is what corrected #5377's recordId claim); DETAIL A/B/C all -> findOne 0, nothing rendered, including with fully correct objectName+resourceId. PROBE 3 (why detail never fetched, 3 legs incl. a control): DIRECT DetailView with dataSource as a prop -> findOne 1, renders; VIA REGISTRY same schema through SchemaRenderer+provider -> findOne 0, nothing; CONTROL object-grid through the same provider -> find 1, which is what proved the harness and provider were fine and isolated the difference to detail-view's missing wrapper. PROBE 4 (the guide's OWN call shape, dataSource as a prop on SchemaRenderer, no provider) — this is the leg that flipped the conclusion and is why I did not stop at probe 3: DETAIL with corrected keys -> findOne 1 [\"task\",\"42\"], row renders; DETAIL as the page reads today -> findOne 0; GRID with corrected key -> find 0, no rows. Cross-checked against the source contract: SchemaRenderer.tsx:371 reads dataSource from context ONLY, and its own comment at :703 states an explicit React dataSource prop 'arrives via ...props, spread last' — which is precisely why the two blocks split. CHECK 2 (sibling gate ledger) was not re-run and did not need to be: no file changed this round, so check-doc-snippet-types.mjs sees the identical tree it was green on at 0a6cba7b9.", "open_questions": [ { "question": "building-crud-app.md now has two independent live axes — the KEY axis (#5377, verified mechanical and verified to work) and the WIRING axis (#5378, not mechanical). How should they be sequenced against PR5376?", "options": [ "A. Accept PR5376 as it stands (your stated fallback) and sequence #5378 and #5377 together as one card that fixes the page end to end — keys plus the provider restructure — verified by a render probe.", "B. Land the key rename now anyway as a partial improvement, and leave the wiring to #5378.", "C. Land the wiring fix first (converge detail-view onto the context wrapper its siblings use), then the keys." ], "recommendation": "A. B is actively worse than doing nothing on this page: PR5376's type fix already converted the failure from a loud OBJUI-001 panel into silence, and a key rename with the wrong wiring removes the last remaining signal while still rendering nothing — it would make the page look verified to the next reader. C inverts the dependency, since the wiring fix is a framework change with existing callers of detail-view's prop form and needs a compatibility judgment, whereas the keys are a two-minute edit once the wiring is settled. A keeps the two axes in one reviewable change whose acceptance test is the render probe, which is the outcome you actually asked for." } ], "out_of_scope_findings": [ "filed as #5373: `type: 'crud'` has four declaration faces and no registered renderer, yet sits on the render path. Unchanged from last round; ledgered in PR5376 with a DELETE-when-it-lands note.", "filed as #5377 (updated this round with the runtime numbers): the key axis on these five snippets. Corrected in place — `recordId` is right on object-form and wrong only on detail-view, which my original filing got wrong.", "filed as #5378: object-grid / object-form read the dataSource from CONTEXT while detail-view takes it as a PROP; the two wirings are mutually exclusive, neither reports anything when it has no adapter (the #5349 silent shape), and the getting-started guide uses the one that breaks the grid — which also contradicts AGENTS.md commandment #1." ] }Follow-up round on PR #5376 — nothing pushed, PR unchanged at
0a6cba7b9, worktree removed. Stopped per the coordinator's own instruction because check 3 came back non-mechanical.
Generated by Claude Code
- added a commit that references this issue
on Aug 24, 2026 - added a commit that references this issue
on Sep 1, 2026
Found while implementing #5174 (PR #5341), which widened the sibling gate's collector. Filed unassigned and deliberately NOT fixed in that PR — #5174 is scoped to
check-doc-snippet-types.mjs, and this is a second gate with its own ledger, its own exemption vocabulary and its own CI-cost profile.Sub-issue of #5106 because it is the same gate's scan surface, on a second axis: #5106 measured that the surface stops at code fences (tables and prose are outside it); this one is that the surface also stops at the
.mdxextension.Measured, on today's
mainscripts/check-doc-component-types.mjs:697:Its own summary line confirms the surface at runtime:
content/docsholds 143.mdxand 40.md. Of those 40, 26 hold atype: 'value'literal inside a fenced code block — 345 literals in total, none of them read by this gate. Largest contributors:(Count is a fence-restricted scan of
typekeys, the same shape the gate matches; treat it as the order of magnitude rather than the gate's exact tally, which only the gate can produce.)Why this is worth a card and not just a note
This is the same class the gate exists to close. objectui#4823 landed it after real recurrences of documented component keys that nothing registers, and objectui#5076 found a whole
#5002family mirror sitting on the docs site. Those guides are the getting-started pages, andapi/schema-reference.mdalone carries 102typeliterals presented as reference material.One honest difference from #5174
This gate is not contradicting itself. Its docblock says, in its first sentence, "Every
typestring literal in acontent/docs/**.mdxcode block" — the surface is stated, and theDOC_TYPE_EXEMPTIONSledger is keyed by.mdxpaths throughout. So this is a coverage gap, not a broken promise, and it should be triaged on whether the coverage is worth the triage pass rather than as a correctness defect.What turning it on would cost, for triage
Unknown until measured, and that measurement is the first task on this card, not a prerequisite for filing. The comparable number from #5174: widening the snippet gate to
.mdcollected 40 more documents, of which 28 held a compilable block and all 28 failed on first collection. Expect a similar triage pass here, absorbed byDOC_TYPE_EXEMPTIONSentries plus real key fixes. Note that #5106 proposes changes to the samescanDocs/walkFilesregion, so the two are best sequenced rather than run in parallel.Reproduce
Related: #5174 / PR #5341 (the same extension gap in
check-doc-snippet-types.mjs, fixed there), #5106 (the fence axis of this same gate), objectui#4823 (this gate's own card).Generated by Claude Code