Skip to content

docs(gates): record the package-README census and defer check:doc-types' fourth leg (objectui#7896) - #8111

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-7896-doc-types-package-readme-census
Sep 6, 2026
Merged

docs(gates): record the package-README census and defer check:doc-types' fourth leg (objectui#7896)#8111
baozhoutao merged 1 commit into
mainfrom
claude/issue-7896-doc-types-package-readme-census

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Refs #7896

Census-only. The widening does NOT land here, because the census does not read zero.

The domain:ui ruling on objectui#7896 (comment 5556586208) orders this move census-first: walk all 39 packages/NAME/README.md with check:doc-types' own extractor and own registry, report a blind-spot count, and land the widening in the same pull request only if the census reads zero. It reads 26. So this pull request carries the measurement and a proposal per hit, and the fourth leg is left for the follow-up card the PM opens.

STEP 0 — the card's two-way mutation, re-taken on 59a3a233d

Assumption A1 holds. Today's counters have moved from the card's af05c88bb reading (889 literals, 772 registered, was 887 / 770); everything else reproduces exactly.

case mutation disk hash after mutation check:doc-types result
subject "type": "action:button" to action:buttonZZ in packages/app-shell/README.md 0393b247b70563082c817d8d36b4841bf2170571 EXIT=0, counters byte-identical to baseline (188 files / 1105 blocks / 889 literals / 772 registered)
control "type": "object-grid" to object-gridZZ in the root README.md 7d01e3072e8e0d7f78a6a48218f28f78c0a3af50 EXIT=1, README.md:272 [unregistered-doc-type] type 'object-gridZZ' (json), registered 772 to 771

Both mutations proven on disk by counting the injected and the removed text before reading any verdict. Both restores proven by STATE, not by exit code:

path HEAD blob disk hash after restore git diff HEAD --name-only
packages/app-shell/README.md 1304531be8aec7684dbccebda403077ee2914f3e 1304531be8aec7684dbccebda403077ee2914f3e empty
README.md b6c8467fdfe8e5102ceafff83922d9766f6f4f39 b6c8467fdfe8e5102ceafff83922d9766f6f4f39 empty

The control proves the instrument is alive; the byte-identical subject counters prove the package README is not in the scan population at all, rather than judged and forgiven.

STEP 1 — the census (the deliverable)

Instrument. The gate's own packageReadmes / scanDocs / deriveRegistryKeys / analyze, exercised through a thin harness that only groups the gate's own site list per file. To make scanDocs reach the files at all, the fourth leg was applied to a working copy, the census taken, and the leg then reverted — the reverted state is proven below. No hand-written regex judged anything.

Assumption A2 falsified in the useful direction. The extractor is fence-language-AGNOSTIC: scanDocs recognises any ``` fence, records its info string only as a label, and applies the same type matcher inside all of them. So a typescript-fenced literal in a package README is a census HIT, not a blind spot — confirmed by the fence-language mix of the 237 literals read: typescript 149, tsx 44, json 21, ts 13, jsonc 10.

Population control. The widened walk reads 227 documents. check:doc-fences, an independent instrument, reports 227 document(s) and its listDocuments returns the same 39 package READMEs. The two walks agree on the population from different code.

file blocks type literals unregistered (line:value)
packages/app-shell/README.md 21 7 392:start, 393:decision, 395:action, 396:end
packages/auth/README.md 13 0 0
packages/cli/README.md 9 0 0
packages/collaboration/README.md 8 0 0
packages/components/README.md 9 3 0
packages/core/README.md 6 2 0
packages/create-plugin/README.md 8 0 0
packages/data-objectstack/README.md 23 0 0
packages/fields/README.md 3 0 0
packages/i18n/README.md 9 0 0
packages/layout/README.md 8 0 0
packages/mobile/README.md 11 3 138:swipe-left, 142:swipe-right, 167:pinch
packages/permissions/README.md 8 0 0
packages/plugin-ai/README.md 6 1 0
packages/plugin-calendar/README.md 12 8 0
packages/plugin-charts/README.md 7 3 0
packages/plugin-chatbot/README.md 14 3 0
packages/plugin-dashboard/README.md 16 28 226:line, 235:pie, 341:bar, 344:line
packages/plugin-designer/README.md 9 1 0
packages/plugin-detail/README.md 8 13 168:activity-timeline
packages/plugin-editor/README.md 7 3 0
packages/plugin-form/README.md 16 25 0
packages/plugin-gantt/README.md 21 15 608:milestone, 713:fs, 714:ss, 715:ff, 716:sf
packages/plugin-grid/README.md 22 30 177:multiple, 253:count_unique, 380:multiple, 551:multiple
packages/plugin-kanban/README.md 8 4 0
packages/plugin-list/README.md 7 7 0
packages/plugin-map/README.md 6 3 0
packages/plugin-markdown/README.md 6 3 0
packages/plugin-report/README.md 15 15 168:matrix, 189:joined, 247:bar
packages/plugin-timeline/README.md 4 0 0
packages/plugin-tree/README.md 1 2 0
packages/plugin-view/README.md 22 31 171:share, 298:date-range
packages/providers/README.md 5 0 0
packages/react/README.md 12 4 0
packages/react-runtime/README.md 6 0 0
packages/runner/README.md 4 8 0
packages/test-support/README.md 0 0 0
packages/types/README.md 9 12 0
packages/vscode-extension/README.md 6 3 0
TOTAL (39 files) 385 237 26 across 12 files

Blind spots — 4

A zero without a blind-spot reading is not a measured zero, so the blind spots were measured by a deliberately DIFFERENT instrument (the gate's own regexes cannot report what they cannot see). Four categories were counted over the same 39 files:

category count
fence-shaped lines the gate's fence matcher does not accept (info strings with attributes, ~~~ fences) 0
unterminated fences 0
type: keys inside a gate-recognised fence whose value is not a same-line quoted string literal 4
type: keys in 4-space indented (unfenced) code blocks 0

All four are the same site, packages/data-objectstack/README.md:603-606, inside a ```yaml fence:

object: sys_user_preference
fields:
  - { name: user_id,    type: lookup(sys_user), indexed: true }
  - { name: key,        type: string,           indexed: true }
  - { name: value,      type: json }
  - { name: updated_at, type: datetime }

Unquoted YAML scalars naming @objectstack/spec object FIELD types, not SDUI component keys. Explained with evidence, and not a candidate for the exemption table either — the extractor never produces a site for them, so there is nothing to exempt. They do not change the STEP 2 decision, which the 26 hits settle on their own.

STEP 2 — the decision: the widening does NOT land

Unregistered = 26, not 0. The ruling's branch is unambiguous, and the cost is concrete: landing the leg today turns main red on 26 sites this card is explicitly not authorised to touch (⛔ no package README content edits). Per the ruling, ⛔ nothing was added to DOC_TYPE_EXEMPTIONS — that table records rulings, not a switch for turning a first run green, and stuffing 25 entries into it would bury the one real defect among them.

What lands instead: a documentation-only note in scripts/check-doc-component-types.mjs recording the measurement, the ⛔ ruling, the one real defect, the 25 candidates, and the two couplings the implementing change will owe. No walk, no counter, no verdict movespnpm check:doc-types prints the same line before and after (188 / 1105 / 889 / 772 / 117).

The 26 hits, one proposal each

1 real defect — a card of its own, ⛔ not repaired here.

site value why it is a defect
packages/plugin-detail/README.md:168 activity-timeline A detail tab's content.type. DetailTabs.tsx:72 renders tab content through SchemaRenderer schema={toRenderableSchema(tab.content)}, so this IS an SDUI node position. Nothing registers activity-timelineplugin-detail registers activity (namespaced record:activity). A reader copying the example gets the registry's OBJUI-001 "Unknown component type" panel. Same shape as the line-chart instance objectui#7896 recorded in packages/plugin-dashboard/README.md, corrected on the docs side by PR #7951.

25 candidate DOC_TYPE_EXEMPTIONS entries — each a genuine non-component vocabulary with a declaration site in source, each owed a written reason naming that vocabulary:

sites values vocabulary declaration site
packages/app-shell/README.md:392,393,395,396 start, decision, action, end flow-graph node kinds app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.tsx
packages/mobile/README.md:138,142,167 swipe-left, swipe-right, pinch gesture kinds packages/mobile/src/useGesture.ts, useSpecGesture.ts
packages/plugin-dashboard/README.md:226,235,341,344 line, pie, bar dashboard WIDGET kinds, dispatched by DashboardRenderer / DashboardGridLayout (dispatch.family === 'series'), not by ComponentRegistry plugin-dashboard/src/DashboardRenderer.tsx:117, WidgetConfigPanel.tsx:91
packages/plugin-gantt/README.md:608 milestone Gantt task kinds plugin-gantt/src/GanttView.tsx
packages/plugin-gantt/README.md:713,714,715,716 fs, ss, ff, sf Gantt dependency-link kinds plugin-gantt/src/GanttView.tsx
packages/plugin-grid/README.md:177,380,551 multiple grid selection modes (selection.type) plugin-grid/src/ObjectGrid.tsx
packages/plugin-grid/README.md:253 count_unique column summary aggregates (summary.type) plugin-grid/src/useColumnSummary family
packages/plugin-report/README.md:168,189,247 matrix, joined, bar report schema kinds (the gate header already names 'matrix' as one of the seven type vocabularies) plugin-report/src/ReportRenderer.tsx, DatasetReportRenderer.tsx
packages/plugin-view/README.md:171 share view action kinds packages/types/src/views.ts
packages/plugin-view/README.md:298 date-range filter field kinds plugin-view/src/FilterUI.tsx, packages/types/src/views.ts

Note the mixed-vocabulary hazard these make concrete: plugin-dashboard's sibling widget kinds metric-card and metric pass today only because some package in the generous union registers those names — the same block holds passing and failing members of one vocabulary, which is exactly why the exemption table is keyed by (file, value) and not by file.

Ruling 3 — the header's ⛔ is untouched

scripts/check-doc-component-types.mjs:244 still reads, byte-identical to HEAD:

⛔ What this is NOT: a precedent for widening onto any other unscanned tree.

The sentence the ruling asks for sits beside it in the new note, in the deferred form this pull request is in: that ⛔ refuses widening onto an ARBITRARY unscanned tree, and packages/NAME/README.md is not one — it is the surface this gate's own two siblings already walk, so the move aligns the third gate to its family rather than reaching into a new tree. Cited to objectui#7896. The ⛔ stays, and this card is not licence for the next widening.

Ruling 5 — the surface-equality pin

Neither (a) nor (b) is needed in this pull request, because no leg lands. scripts/check-doc-expression-carriage.mjs imports APP_DOCS / appDocsDirs / ROOT_PAGES from this gate; none of those constants moved, so the pin's rebuilt walk and the carriage census's walk are still the same three legs. Both were run anyway and are green: the pin suite passes, and node scripts/check-doc-expression-carriage.mjs exits 0.

The divergence hazard is real but is the follow-up card's to carry, so it is written into the gate's note rather than left to be rediscovered: that pin compares against the CONSTANTS, not against check:doc-types' actual walk, so a fourth leg added to this gate alone would leave the pin GREEN while the two surfaces silently diverge — objectui#7115's shape again. The recorded default is (a): the carriage census imports the new leg constant and walks it too, the pin's rebuilt walk gains the same leg, and SURFACE_LABEL plus its enumerating test gain the leg by name. The note also records the leg's required SLOT in scanDocs (before the root pages, which is where both sibling walks append it) — that slot is what keeps the three lists comparable element by element, and the census run confirmed it produces the same 227-document population check:doc-fences reports.

Positive control

Ruling 4 scopes the injected-literal positive control to "only when the widening lands", and it does not land. The equivalent evidence for the census instrument is above and is stronger than an injection, because it is not synthetic: the widened walk named 26 real sites with file and line across 12 of the 39 files, and the control leg of STEP 0 shows the same gate producing EXIT=1 with an unregistered-doc-type diagnostic on a file that IS in its walk. The instrument is alive and it reaches the files.

Revert of the census-time leg, proven by STATE:

path HEAD blob disk hash after revert git diff HEAD --name-only
scripts/check-doc-component-types.mjs (before the note) ddba2f99b570066c4515901fa37951ea2694ca7d ddba2f99b570066c4515901fa37951ea2694ca7d empty

Gates — all pinned to 1d05e86a1

gate exit verdict line
pnpm check:doc-types 0 188 doc file(s) … 1105 code block(s), 889 type literal(s) … 772 registered, 117 exempted — identical to the pre-change baseline
pnpm exec vitest run scripts/__tests__/check-doc-component-types.test.ts check-doc-expression-carriage.test.ts check-doc-fence-languages.test.ts 0 Test Files 3 passed (3) / Tests 121 passed (121)
node scripts/check-doc-expression-carriage.mjs 0 report-only census, exit 0
pnpm check:doc-fences 0 every TypeScript block in 227 document(s) … (sibling control, unmoved)
pnpm exec vitest run scripts/__tests__/ 0 Test Files 112 passed (112) / Tests 3360 passed (3360)
pnpm type-check:scripts 0 clean
pnpm lint:root 0 32 problems (0 errors, 32 warnings) — warnings pre-existing, none in the changed file
pnpm check:control-bytes 0 scanned 6495 tracked text file(s); skipped 85 binary
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' on the changed path 1 (no match) clean
node scripts/check-changeset-presence.mjs 0 1 file(s) changed, 0 of them published source … no changeset is owed
node scripts/check-governed-queue-guard.mjs --test scripts/check-doc-component-types.mjs 0 NOT GOVERNED — 1 path(s) checked against 5 governed surface(s)
pnpm check:entry-guard 0 70 scripts/ file(s) — no entry guard outside the baseline

One declared narrowing. pnpm check:doc-snippets exits 2 with unbuilt-package for 31 packages — a PREREQUISITE NOT MET, not a red gate: it needs a scoped dist/ build this diff has no bearing on. It is left to CI. The reason the narrowing is safe here: that gate reads scripts/check-doc-snippet-types.mjs, which this pull request does not touch, and the only changed file is a block comment.

Files changed

scripts/check-doc-component-types.mjs — 61 added lines, all inside one block comment. Nothing else.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr


Generated by Claude Code

…es' fourth leg (objectui#7896)

`check:doc-types` does not walk `packages/NAME/README.md` while its two sibling
doc gates both do, so a package README's `type` literals are read twice and
judged never. The `domain:ui` ruling on objectui#7896 orders the move
census-first: the widening may land in the same pull request only if the census
reads zero.

It does not. All 39 files walked with this gate's own extractor and registry
read 26 unregistered `type` literals across 12 files, plus 4 blind spots. So
the leg does not land here. This commit records the measurement, the one real
defect it found (`packages/plugin-detail/README.md:168` teaches a detail tab
whose `content.type` is `activity-timeline`, rendered through `SchemaRenderer`
and registered by nothing), the 25 other-vocabulary candidates, and the two
couplings the implementing change will owe — the leg's slot in `scanDocs` and
the expression-carriage census's surface-equality pin.

Documentation only: no walk, no counter and no verdict moves. The header's ⛔
against treating this gate's widenings as a precedent stays verbatim.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr

Copy link
Copy Markdown
Contributor Author

Standing down on Live E2E (informational) for this PR, read 2026-09-06T16:53Z.

Every other check on 1d05e86a1 is judged on its own; this PR flips to ready once the required set converges, then goes to the merge queue by squash.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Armed — squash auto-merge enabled and the PR is in the merge queue, domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

  • Head 1d05e86a1: 28 non-informational checks completed green at 17:03Z (the ci-wait reading); Live E2E (informational) red per the standing-down note above.
  • ready_for_review at 17:03:36Z; the post-flip Governed Surface Queue Guard run started after that timestamp and completed success at 17:03:54Z.
  • added_to_merge_queue at 17:03:59Z on the REST timeline (read 17:04:08Z). PR docs(vscode-extension): state what Export to React actually does #8112 is ahead of it in the queue.

On MERGED: content probe on re-fetched origin/main (the MEASURED, DELIBERATELY NOT WALKED (objectui#7896) note present in scripts/check-doc-component-types.mjs, the ⛔ at the header intact, control: ROOT_PAGES = ['README.md'] unchanged and pnpm check:doc-types' verdict counters unmoved), then the LANDED note here and the release stroke on #7896 (pm:dispatchedpm:blocked, Blocked-by: #8114 and Blocked-by: #8115 in the body, assignee cleared).


Generated by Claude Code

Merged via the queue into main with commit 083e1ab Sep 6, 2026
30 of 31 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7896-doc-types-package-readme-census branch September 6, 2026 17:19

Copy link
Copy Markdown
Contributor Author

LANDED — merged at 2026-09-06T17:19:18Z as 083e1abbf (squash, via the merge queue), domain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46.

Content probe on re-fetched origin/main (083e1abbf, read 17:20:00Z), scripts/check-doc-component-types.mjs:

  • The MEASURED, DELIBERATELY NOT WALKED (objectui#7896) note is present (1 hit).
  • The header's ⛔ (What this is NOT: a precedent for widening onto any other unscanned tree.) is intact at line 244.
  • Control: export const ROOT_PAGES = ['README.md'] unchanged and no package-README leg constant exists, so no walk moved.

#7896 stays open (Refs): released in the same stroke to pm:blocked with Blocked-by: #8114 and Blocked-by: #8115 in the body and the assignee cleared.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants