Skip to content

fix(docs/gate): check-doc-component-types collects .md under content/docs, not .mdx only - #5376

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-5342-doc-component-types-md
Aug 20, 2026
Merged

fix(docs/gate): check-doc-component-types collects .md under content/docs, not .mdx only#5376
os-support-ai merged 1 commit into
mainfrom
claude/issue-5342-doc-component-types-md

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#5342
Part of #5106

scripts/check-doc-component-types.mjs collected .mdx only, so 40 .md guides under the same content/docs tree were neither judged nor declared. This widens the collector and pays the triage pass it exposes.

This is a coverage decision, not a broken promise. Unlike its sibling #5174, this gate's docblock stated the .mdx surface in its first sentence and the ledger was keyed by .mdx paths throughout. It did not claim to cover what it skipped. The decision is now the other way, and the docblock says so.

Converged on the pattern that already landed

scripts/check-doc-snippet-types.mjs (PR #5341, follow-on PR #5364) did this widening first. This adopts its exact spelling — DOCS_ROOT, DOC_EXTENSIONS = ['.mdx', '.md'], and a docblock stating the scan surface next to the coverage rule. Two collectors walking one tree two different ways is the defect one level up; there is now one spelling.

Measured, on f2e11ae6f

beforeafter
doc files scanned143183 (+40 .md)
code blocks6321052
type literals564887 (+323)
registered476742
exempted88145
findings00

First collection produced 67 findings across 15 .md pages — 256 of the 323 new literals already named registered keys. The card's earlier estimate was ~26 guides / ~345 literals; the tree has moved, and 323 in 15 pages is what it holds today.

Scan roots this collector actually walks

content/docs and nothing else — DOCS_ROOT is a single constant and the walk has one call site. skills/** is NOT in scope, nor are the package READMEs (check-doc-snippet-types.mjs covers those for its own question), nor docs/**. So none of skills/objectui/guides/i18n.md (claim #5081), skills/objectui/guides/schema-expressions.md, skills/objectui/guides/data-integration.md or skills/objectui/rules/protocol.md is reachable by this gate, and none is touched here. I added that sentence to the DOCS_ROOT docblock so the next reader does not have to re-derive it.

10 real key errors fixed

Each one paints the renderer's red "Unknown component type" panel (OBJUI-001) for a reader who copies it. Keys taken from the actual register(...) calls, never from file or component names:

pagewasnowpinned by
guide/building-crud-app.md x3ObjectGridobject-gridpackages/plugin-grid/src/index.tsx:202
guide/building-crud-app.mdObjectFormobject-formpackages/plugin-form/src/index.tsx:100
guide/building-crud-app.mdObjectDetaildetail-viewpackages/plugin-detail/src/index.tsx:133
guide/expressions.mdempty-stateemptypackages/types/src/feedback.ts:186 declares it, components/src/renderers/feedback/empty.tsx:15 registers it
guide/schema-rendering.mdempty-stateemptysame
guide/schema-playground.mdgrid-layoutgridcomponents/src/renderers/layout/grid.tsx:50, which reads columns as the number the snippet already writes
api/schema-reference.md x2field "type": "link""email"link is not in fieldWidgetMap; email and url are

Three notes a reviewer should weigh:

  1. ObjectDetail to detail-view is the one judgment call here. There is no object-detail key. detail-view is the record-detail component plugin-detail registers, and api/schema-reference.md:1091 already teaches that spelling for the same shape. Flagging it rather than burying it.
  2. The PascalCase family is the examples/hello-world renders four OBJUI-001 panels: schema.json uses PascalCase types (Page/Card/Text/Button) and the registry lookup is case-sensitive #5236 / [Decision] Should component-registry lookup normalise case? Two measured instances of "every node renders Unknown component type" #5247 shape. Registry lookup is case-sensitive, so ObjectGrid was never going to resolve. This spells the registered lower-kebab keys, which is correct whichever way the [Decision] Should component-registry lookup normalise case? Two measured instances of "every node renders Unknown component type" #5247 decision goes.
  3. Only the type key is corrected, not the props beside it.object-grid and object-form read objectName, and those snippets write object. Whether a snippet's OTHER keys are read is this gate's stated non-goal and the sibling gate's ledger already carries building-crud-app.md with un-triaged candidate defects. Left alone deliberately; not folded in.

One prose word moved with its snippet: schema-playground.md:283 listed grid-layout among the container types. Same page, same falsehood, one word. This is not a scan-surface change — the collector still stops at code fences, which is #5106's first half and stays untouched.

31 ledger entries, covering the remaining 57 sites

Every entry is (file, value) with a reason naming the vocabulary it really belongs to and where that vocabulary is declared. No entry was widened, none relaxes the gate, and no threshold moved — FLOORS is byte-identical.

The vocabularies the .md half turned out to speak:

  • ActionSchema discriminants under action lists (toolbar.actions[], rowActions[], batchActions[], a form's actions[]) — packages/types/src/crud.ts:89
  • ComponentInput.type in a register(...) call's inputs[] — a designer input's coarse control kind, packages/types/src/base.ts:386. This is the single largest class, and it is why the "write a plugin" walkthroughs lit up.
  • Walkthrough keys the pages register themselvesboard (plugin-development.md builds @object-ui/plugin-board end to end), my-component, my-grid, my-feature, custom
  • Metasyntactic placeholders in the two "Usage Pattern" templates — component-name, plugin-component-name
  • Dashboard widget kindsbar / line, AppSchema menu kindsitem / group, ThemeSchema / BlockSchema discriminants, BlockVariable and PageVariable data types
  • Object metadata field typespicklist, the picker/lookup family spelling packages/core/src/utils/record-title.ts:101 names
  • package.json's own "type": "module" in guide/plugins.md. Same collision objectui check 把每个 JSON 文件的根 type 都当成组件键判定,于是在任何 Node 工程里都对 package.json 的 "type": "module" 报未知类型 #5127 measured on objectui check.
  • ActionDef.type passed to useActionRunner().execute(...)packages/core/src/actions/ActionRunner.ts:112

One entry is disclosed debt, not vocabulary

api/schema-reference.md -> crud. CRUDSchema has four declaration faces (interface, zod mirror, validator branch, builder) and no registered renderer, and unlike its siblings it sits on the render path. There is no registered spelling to move it to — register a renderer / retire it under ADR-0049 / demote it off the node union are three different page edits, and that is the contract question #5115 left open after PR #5128 landed only its CLI half. Filed as #5373, and the entry says to delete itself when that lands. The gate reports stale-exemption the moment the site changes, so it cannot be forgotten.

Two entries are honest about being weaker than their neighbours: schema-rendering.md -> admin-panel / user-panel are stand-ins for the reader's own components in a block whose subject is visibleOn, and unlike the my-component placeholder above them the page never registers them. The reason string says exactly that rather than pretending they are the same class.

Verification

All runs from the repo root. Heavy runs held /tmp/os-heavy-verify.lock.

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
npx vitest run scripts/__tests__/check-doc-component-types.test.ts
Test Files 1 passed (1) | Tests 34 passed (34) exit 0

The script passing is not its test passing — the test carries hardcoded ledgers the script never touches, so both are reported. 7 tests added: two fixture-level (both extensions collected; a .md page judged by the same rule), one repo-level pin that the widened walk really reaches the .md tree, and four pinning the key errors above so a revert reads as itself rather than as noise in a 183-file scan.

Counter-probed the zeros. A zero from a broken matcher looks exactly like a clean result, so the derived universe was probed with keys known to be registered before any of it was trusted: object-grid YES, object-form YES, grid YES, form YES — against ObjectGrid no, object-detail no, empty-state no, grid-layout no. The universe is 659 keys taken from the register(...) calls themselves.

Reverse-verification, and the direction is not the one the template predicts. Reverting only the walk to .mdx-only, from the committed state:

files scanned: 183 -> 143 type literals: 887 -> 564
.md pages carrying a scanned site: 20 -> 0
gate: exit 1, 31 problem(s) — all `stale-exemption`
test: 5 failed | 29 passed

The newly-collected pages do stop being seen, as expected. But the gate does not fall quiet — it goes red, because all 31 new ledger entries lose their sites at once. That is the ratchet working in the reverting direction, and it is a better result than a silent green. The restore leg was verified by marker absence (f.endsWith('.mdx') gone from the file) before re-reading anything, and re-ran green: 34/34.

Build artifacts, per leg. This gate has none between the edit and the result: every import in check-doc-component-types.mjs is a node: builtin, which its own test asserts (needs no install, so it can afford to run unfiltered). So no leg here 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 packages (--build-filter), proved the artifacts landed in this worktree rather than trusting a turbo cache-hit that replayed logs from another worktree's path, then:

Controls: resolution '@object-ui/types' resolved to
/home/user/objectui-issue-5342/packages/types/dist/index.d.ts
Semantic phase: 87 of 87 block(s) judged, 0 failed. exit 0

Its ledger is still exact — the edits were inside json fences or were string-literal values, and shifted no diagnostic mix.

Other gates touched by these paths, all green at 0a6cba7b9: check:control-bytes (4761 files), check-doc-links.mjs (13 scan roots), check:skills-paths. Plus the check-doc-links / check-doc-snippet-types / check-doc-component-types tests together: 141 passed. scripts/pm/dispatch-gates.mjs does not exist in this repo — it lives in objectstack — so the gate set was derived from the 14 check:* scripts against my changed paths instead.

Every number above and the gate union were taken at 0a6cba7b9, which is this branch's head.

Changeset: empty frontmatter

.changeset/doc-component-types-md-surface.md declares no package. The diff is the gate script, its test, workflow comments and six documentation pages — no packages/*/src file changes and nothing published moves. A patch would version all 39 packages of the fixed group for a change with zero runtime effect, so this uses the repo's declared "no release" form, which scripts/check-changeset-presence.mjs documents as a first-class pass. Never major. content/docs/releases/ is untouched.

This repo has no skip-changeset label mechanism — the empty-frontmatter changeset is the declaration, and no label was created or applied.

Serial constraint held

#5106 is deliberately out of scope and nothing here folds 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. scanDocs's fence loop is byte-identical to main except for the one predicate on the walk, and no namespace logic was added. #5106 remains open and undispatched.


Generated by Claude Code

…t/docs, not `.mdx` only
The collector walked `.mdx` only, so 40 `.md` guides under the same tree were
neither judged nor declared ungated. Widened to the `DOC_EXTENSIONS = ['.mdx',
'.md']` spelling `check-doc-snippet-types.mjs` already uses — two collectors
walking one tree two different ways is the defect one level up.
Measured on f2e11ae: 143 -> 183 pages, 564 -> 887 `type` literals, and 67
first-collection findings across 15 `.md` pages.
Ten of those were real key errors and are fixed here:
building-crud-app.md ObjectGrid/ObjectForm/ObjectDetail -> object-grid /
object-form / detail-view (PascalCase is a component
NAME; the registry key is lower-kebab, and the registry
lookup is case-sensitive)
expressions.md empty-state -> empty (EmptySchema declares it)
schema-rendering.md empty-state -> empty
schema-playground.md grid-layout -> grid (fence and prose)
api/schema-reference.md the Email field's `link` -> `email`
The remaining 57 sites are 31 (file, value) DOC_TYPE_EXEMPTIONS entries, each
with a reason naming the vocabulary it really belongs to: ActionSchema
discriminants, ComponentInput.type in `inputs[]`, dashboard widget kinds, object
metadata field types, package.json's own `"type": "module"`, and walkthrough
placeholders the pages register themselves.
One entry is debt rather than vocabulary: `crud` is on the render path with four
declaration faces and no renderer. Filed as #5373 and the entry says to delete it
when that lands.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@os-support-aiClaude

Copy link
Copy Markdown
CollaboratorAuthor

PM review (round 19, session session_01RV6yuVCxymHYE16PL9vQkE): held for one narrow addition, not for a defect. Everything below the addition is verified and good.

Verified independently

  • Surface: 9 files, and git diff --name-only origin/main... confirms nothing under skills/ or any packages/*/src. Your scan-root answer holds — the four off-limits files are unreachable by this gate, as you said.
  • FLOORS is byte-identical to main, checked by diffing the block directly. No threshold moved.
  • The workflow file is comment-only — every changed line starts with #, checked mechanically rather than read. It sits outside the file surface my claim declared, so I am naming it rather than letting an undeclared extension pass silently; it is benign and the comments earn their place, particularly the note that ci.yml's diff filter excludes '**/*.md', which is exactly the extension this gate now reads.
  • The reverse-verification result is better than the template predicted and you were right to report it as-is: reverting the walk makes the gate go red with 31 stale-exemption, not quiet. That is the ratchet firing in the reverting direction, and it is worth more than a silent green.

Why it is held

Your own #5377 names the interaction, and I am acting on it rather than filing it away:

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.

I agree with that sentence, so I am asking for both. This PR editscontent/docs/guide/building-crud-app.md; after it merges, a reader copying that page gets a populated-looking but empty grid where they previously got a loud red panel naming the problem. This repo already treats that as the worse failure — #5349 exists for precisely that shape — and #5343 established these getting-started guides are the pages readers copy from most.

I am overriding a call you made deliberately, and it was defensible. Your reasoning — props are this gate's stated non-goal, and the sibling ledger already carries this page with un-triaged candidates — is correct on the gate's terms. My criterion is different and it is mine to own: a page this PR edits should not be left failing more quietly than it did before. That is a product judgment, not a gate-scope one.

Scope of the addition is minimal: building-crud-app.md only, only the snippets already touched, objectobjectName and recordIdresourceId. ⛔ Not other pages, ⛔ not a prop dimension in the gate, ⛔ not #5106. #5377 stays open for the general question.

Three things I asked to be checked rather than assumed: that object is not a valid alias (if it is, I am wrong and nothing changes); that the sibling gate's ledger stays exact, or is updated to its new measured mix and never widened; and that the corrected snippet actually renders. If any comes back saying this is not mechanical, I will accept this PR as it stands and sequence #5377 behind it.


Generated by Claude Code

@os-support-ai
os-support-ai marked this pull request as ready for review August 20, 2026 02:56
@os-support-ai
os-support-ai added this pull request to the merge queueAug 20, 2026
Merged via the queue into main with commit 2e6c5c4Aug 20, 2026
21 checks passed
@os-support-aiClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT — released as it stands, and the hold is lifted. PM round 19, session session_01RV6yuVCxymHYE16PL9vQkE.

Gates: 18 jobs completed: success, 2 skipped by design. Head unchanged at 0a6cba7b9, so these results stand for what is being merged.

The addition I asked for is withdrawn, and I was wrong to ask for it in that form

I held this PR to add objectobjectName / recordIdresourceId on building-crud-app.md. The dev ran the three checks I attached and stopped on the third, pushing nothing. That was the correct call and the measurements are why:

  • Check 1 confirmed my concern was real.object is not an alias. Rendered through the real registry with a fake adapter, the grid snippet as the page reads today calls find()0 times; after the single rename it calls find('task', {$top:50,$skip:0}) once and renders the row. The only object-spelled key the system honours is the nestedschema.dataSource.object, which ElementDataSourceGate maps onto objectName — which is exactly why the top-level spelling is a plausible mis-read.
  • Check 3 showed the rename is not sufficient, and that inverts the conclusion. This guide never uses SchemaRendererProvider; it passes dataSource as a prop at all five call sites. object-grid / object-form read the adapter from context via wrappers, while detail-view is registered raw and takes it as a prop. Measured both directions: under a provider, grid find=1 and detail findOne=0; under the guide's prop form, grid find=0 and detail findOne=1. The two wirings are mutually exclusive, so no key rename makes this page render.

The dev's conclusion, which I accept in full: a rename landing alone is the worst of the three available states. This PR's type fix already converted the failure from a loud OBJUI-001 panel into silence; a rename with the wiring still wrong would remove the last remaining signal while still rendering nothing — making the page look verified to the next reader. My requested half-fix would have been a net harm.

It also corrected its own #5377 in the process: recordId is correct on object-form (ObjectForm.tsx:539 calls findOne(schema.objectName, schema.recordId)), merely undeclared in the registry inputs — a different defect. The resourceId rename is right only for detail-view.

The cost this PR does carry, recorded rather than smoothed over

On building-crud-app.md, merging this converts a loud failure into a silent one: readers previously got a red "Unknown component type" panel, and will now get an empty grid. That is a real regression in legibility on a getting-started page, and I am accepting it deliberately rather than pretending it away.

Why accepting still beats the alternatives: reverting just that page's type fixes would require ledgering keys that are genuinely wrong (ObjectGrid never resolves — the lookup is case-sensitive), and adding a ledger entry to paper over a fixable error is precisely what the ratchet forbids. The gate widening — this card's actual product, 143 → 183 files, 564 → 887 literals, FLOORS byte-identical — is unambiguously good and independent of the docs half.

What bounds the window

⛔ I am not dispatching #5377 alone, for the reason above. They land together, with a render probe as the acceptance test.


Generated by Claude Code

@os-support-ai
os-support-ai deleted the claude/issue-5342-doc-component-types-md branch August 20, 2026 02:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-doc-component-types collects only .mdx too — 26 .md guides teach 345 type literals inside fences with nothing checking them

1 participant

@os-support-ai