Repository navigation
feat(scripts): judge JSON doc fences through the strict authoring face (objectui#5250 slice B) - #12127
Conversation
…tui#5250) Every json/jsonc node document under content/docs that the strict authoring face (StrictAnyComponentSchema, the face `objectui validate` judges with) refused is either repaired or declared: - a key nothing reads is removed, or replaced by the declared key its renderer reads (button `action` -> an `action:button` node with its `properties` bag; `empty.message` -> `title`; `card.dataSource` -> a data-bound block; stat-card `value`/`change` -> `children`/`description`); - an off-contract value is corrected (detail field type, button size, grid gap), and teaching shapes no renderer implements are rewritten to the declared ones (form `fields`, select option objects, form `defaultValues`, `data-table` instead of the layout `grid`); - sections that taught capabilities that do not exist are rewritten from the producer (the VS Code extension's shipped `oui-*` snippets and its real validation) or removed (the list item template, an analytics action); - deliberate fragments carry a `doc-json: fragment` declaration. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…e (objectui#5250) The JSON-fence gate the objectui#5250 ruling orders "on top of" the strict face. `scripts/check-doc-json-fences.mjs` judges every json/jsonc node document under content/docs with StrictAnyComponentSchema, read from the built @object-ui/types the CLI imports, so a document it accepts is one `objectui validate` accepts. Non-documents are counted per bucket, deliberate fragments are declared with a `doc-json:` marker the gate re-judges, and RESOLUTION / REFUSES / ACCEPTS / anti-idle controls exit 2 before any verdict when the harness cannot be trusted. Wired as the last step of doc-snippet-types.yml, after the build that produces @object-ui/types; `pnpm check:doc-json`; documented in the CI pipeline guide. The ts/tsx gate exports its blockquote helpers and reason floor so both gates read quoted fences by one rule. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…d (objectui#5250) The JSON-fence repairs removed every snippet eight DOC_TYPE_EXEMPTIONS entries pointed at (the `action` bag discriminants on five blocks pages, the layout-grid column type, and two fictional VS Code snippets), so check-doc-component-types reported them stale. Deleted with their sites. Also: the gate's test imports the face at module scope (the no-dynamic-import-in-test-hook rule), declares its one fixture address, and analyze() carries a JSDoc parameter type; the playground's form note says a form draws no heading rather than no title. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
… unread (objectui#5250) An ablation of the language filter exited 2 as designed, but the summary line above the precondition still printed "anchor ... read". It now reads NOT read whenever the anchor control is what failed. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…input ledger (objectui#5250) `scripts/__tests__/markdown-test-inputs.test.ts` requires every test that reads markdown to be adjudicated, so a markdown-only pull request still starts the shard that runs it. The JSON-fence gate's agreement test walks `content/docs/**`; its `README.md` is a fixture-tree file, recorded as not read. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
|
Dequeued from the merge queue: signature and first diagnosis (2026-10-11T08:17Z). Signature (new; no card or ledger entry carries it)
First diagnosis (a hypothesis, to be confirmed by reproduction): test-order pollution, introduced by this PR.
Action: ⛔ not re-queued, because a new signature is a new problem. A patch round goes to the original dev: reproduce the order, find the root cause, fix it inside this PR. ⛔ No skip, quarantine or re-ordering hack. |
Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…are it (objectui#5250) `requireRecordSource` built its issue `path` once, when the arm was defined, and passed that same array to every `addIssue`. zod 4 copies an added issue shallowly and prefixes its `path` in place (`unshift`) as the issue climbs out of an array slot or object key, so the first refusal reported from inside a container rewrote the path of every later refusal of that arm, for the life of the module. `objectui check` judges many files in one process and read the drift; so did the `unit` vitest project (`isolate: false`): after the JSON-fence gate's agreement test ran the docs corpus through the strict face, a bare `object-kanban` member parse reported its root refusal at `0 -> 0 -> 0 -> 0`, which dequeued PR #12127 from the merge queue. The issue now carries a fresh copy. A pin refuses each gate-wrapped arm from inside a parent's child list and then bare, and asserts the bare path did not move (red on four arms before this change). Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…control through the door (objectui#5250) Every fence the docs corpus refuses is refused by the tolerant face too, so the corpus alone could not tell the strict door from a tolerant one: an ablation that re-pointed `validateAuthoredDocument` at `AnyComponentSchema` stayed green. The REFUSES and ACCEPTS controls now ride with the corpus in the agreement set. Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
…l path (objectui#5250) Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
Red check on head
|
Contract reviewServed-tier: Inputs: card objectui#5250 (body and all 35 comments), PR objectui#12127 (body, 25-file list, net diff against ① Derived judgments
② Semver level
③ Boundary flagsDev reports on this branch:
Implemented-by: VERDICT: PASS |
|
PR body addendum for head Two body statements are no longer true at this head: 「nothing under
The contract-review record for this head is the comment above (PASS). |
|
Re-run requested (2026-10-11T10:59Z): the failed jobs of CI run
|
Part of #5250
Clause-②: no
Slice B of the objectui#5250 programme: the JSON-fence gate the maintainer ruling (comment 5534418546, 2026-09-04, decision batch #25, option 2) orders "on top of" the strict authoring face, plus the docs repairs it cannot land without. Slice A (objectui#11069, objectui#12054, objectui#12050) moved
objectui validateandobjectui checkontoStrictAnyComponentSchema. This PR runs that same face over every json/jsonc node document incontent/docs. The rendering face's.passthrough()is not touched, and nothing underpackages/changes.This card stays open: slice C (the non-docs class (i) documents) is a separate PR.
What changed
scripts/check-doc-json-fences.mjs(pnpm check:doc-json), a sibling script rather than a branch inside the ts/tsx gate, because its only precondition is a built@object-ui/types, not the 34-package closure the ts/tsx gate needs.StrictAnyComponentSchema.safeParse(document).success, read from the BUILT@object-ui/types./zodentry. That is the module@object-ui/cliimports forvalidateAuthoredDocument, whose whole body is the same call. So the docs gate andobjectui validatecannot disagree about a document. A test runs the CLI's own door over the corpus and asserts identical verdicts.type,typenames an arm ofAnyComponentSchema), with two corrections, both toward reading more. Ajsoncfence is read withjsonc-parserand the optionsobjectui checkuses, because the measurement'sJSON.parsedropped every commented jsonc fence. A fence inside a blockquote is read, with the ts/tsx gate's quote rule.type, atypeno node schema declares (with the types).--listprints every fence with its bucket.doc-json: fragmentmarker (the MDX-comment form in.mdx, the HTML-comment form in.md) on the line above its fence. The fragment design follows objectui#5138: an explicit marker, never a silent skip. Every run re-judges the declaration: the fence must be a node document the strict face REFUSES. A marker on an accepted fence, or on a non-document, is stale. A dangling marker, a short reason, or a verification claim (the ts/tsx gate'sVERIFICATION_CLAIM) is a finding. The namespace isdoc-json:, notdoc-snippet:, because the ts/tsx gate reports its own marker above any non-ts fence as stale.packages/types/dist/), REFUSES (one undeclared key: strict refuses it, tolerant accepts it), ACCEPTS (the declared-only twin is accepted), and ANTI-IDLE (at least one judged document, and the anchor pagecontent/docs/blocks/forms.mdxcontributes one).content/docsonly, which is the population the measurement priced. The header and every summary say that pages outside it are not read..github/workflows/doc-snippet-types.yml, after the--build-filterbuild. That closure already carries@object-ui/types..., so no build step is added. Onepackage.jsonscript line.content/docs, re-derived on the dispatch base. Each refused fence is repaired or declared, as tabled below.content/docs/guide/ci-cd-pipeline.md: the command-parity sweep inscripts/__tests__/ci-cd-pipeline-doc.test.tsrequires the workflow's new command to be named on the page. Its table row also said "two gates, one job", which is now three.scripts/check-doc-component-types.mjs: eightDOC_TYPE_EXEMPTIONSentries pointed attypeliterals that only the removed snippets spelled. That gate reports such entries as stale, so they are deleted with their sites, each with a one-line note.scripts/check-doc-snippet-types.mjs: four more exports (quoteDepthOf,stripQuotePrefix,isBlankAtDepthandMIN_REASON_LENGTH), so that both gates read quoted fences and fragment reasons by one rule. Also its header item "schema-key validity ... left unruled on purpose", which this PR makes false for json fences, now points at the sibling.Population on the dispatch base 29b9949
47b1f0b.api/schema-reference.md, both accepted. Four more come from commented jsonc fences:fields/location.mdxis accepted, and three are refused (listed below).typeand 10 with atypeno node schema declares.Instrument: the gate itself, plus a throwaway copy of the measurement predicate for the base reading.
H3: classification of the red-on-both documents
Cited by page and section heading, never by line, per AGENTS.md section 5 rule #11.
api/schema-reference.md, DetailSchemadetailtype: "email"is outside the detail field vocabularytextcomponents/basic/icon.mdx, "nameis identity, not the glyph"iconcomponents/index.md, Example: Buttonbuttonsize: "md"is not a declared sizedefaultguide/dashboard-filters.md, Step 3 (optionsFrom)select(collision)globalFilters[]member whosetypeis the filter's input typeguide/expressions.md, Index in Loopslistitemsmust be an arrayguide/expressions.md, Dependent Fieldsformfields, and select options are{label, value}objectsguide/schema-playground.md, FormformdefaultValue, and formtitleguide/schema-playground.md, Gridgridgridtypedata-tableguide/schema-playground.md, Composing Schemaspagegap: "md"4utilities/runner.mdx, 1. Plugin Developmentpageutilities/runner.mdx, 3. Integration Testingdivutilities/vscode-extension.mdx, Snippets,objectui-formformonSubmitis a runtime slotutilities/vscode-extension.mdx, Snippets,objectui-griddata-tableguide/expressions.md, Scoped Data (jsonc, newly read)listutilities/runner.mdx, Add Custom Schemas (jsonc, newly read)pagemy-componentNone of the 15 is exempted silently. Five are declared with a written reason, and the gate confirms each declared fence is refused. The sixth declaration is the Validation fence in the next table.
Strict-only pairs: per-pair disposition and runtime evidence
Instrument: a throwaway dom-project test, never committed. It renders the node through the real
SchemaRendererand the real registry (packages/components/src/renderers), once with the key and once without it, and compares the normaliseddocument.bodyHTML.Controls: each control changes the output, so the instrument can see a read key:
button.label,empty.title,input.inputType, andform.defaultValues.blocks/authentication.mdxWire Up Submission;blocks/ecommerce.mdxAdd to Cart Action;blocks/forms.mdxAdd Submit Actionbutton.actionaction:buttonnode withproperties.{label, actionType: "api", target, method}(AGENTS.md section 5 rule #4; the same form asguide/schema-rendering.md's api example;executeAPIreadstargetandmethod)blocks/dashboard.mdxAdd Actionsbutton.action(navigate)action:button,actionType: "url",target(executeUrlreadstarget)blocks/marketing.mdxAdd Analyticsbutton.action(analytics)analyticsexecutor exists (the built-ins are script, url, modal, flow, api, form and navigation)blocks/dashboard.mdxAdd Real Data;blocks/ecommerce.mdxAdd Product Datacard.dataSourceelement:numberandobject-gridwith the per-elementdataSourcebinding, verbatim fromguide/data-source.mdandutilities/data-objectstack.mdx).SchemaRendererstripsdataSourcefrom the props it spreadsguide/expressions.mdEmpty State;guide/schema-rendering.mdEmpty Statesempty.messagetitle, which the empty renderer reads (control:titlechanges the heading)messageDOM attribute from the props spreadguide/expressions.mdDynamic Validation (now Validation)input.validationsinputType: "email"(control: it changes the input'stype)guide/schema-playground.mdCardcard.iconiconDOM attributeutilities/runner.mdxDashboard Examplecard.value,card.change(x3)children(a text node) anddescriptionvalue/changeDOM attributesutilities/vscode-extension.mdxSnippets,objectui-appapp.pagespackages/vscode-extension/snippets/objectui.json(the realoui-*prefixes)utilities/vscode-extension.mdxValidation (jsonc, newly read)button.invalidPropadditionalProperties: true, andSchemaValidatorchecks onlyinputType,variantand labels). The fence now shows thevariantwarning it really emits, and is declared as a deliberately refused nodeRed-on-both repairs, probed the same way:
list.itemTemplate: differs only by anitemtemplateDOM attribute.form.title: differs only by the HTMLtitleattribute on theformelement, so the page now says a form draws no heading.gridtitle/rows/actions/pagination: identical.data-tabledataSourceandcolumns[].key/title: identical.form.onSubmit(object): identical.formfields[].defaultValueon a select field: identical. It was replaced by the form'sdefaultValues, which the control shows the form reads.Proofs
29b99490(git checkout 29b99490 -- PAGES), the gate exits 1 with 29 findings. Restored withgit checkout HEAD -- PAGES;git diff HEADis empty afterwards.judgewas pointed atAnyComponentSchemathrough objectstack'sscripts/ablation-replace.mjs(anchor 1 -> 0, blob changed). Result: exit 2, "REFUSES: the strict face accepted a document carrying an undeclared key". Restored: blob == HEAD,git diff HEADempty.JSON_FENCE_LANGUAGESwas emptied ofjson/jsonc. Result: exit 2, 0 judged documents, anchor-unread. Restored: blob == HEAD. The run also showed that the summary line still printed "anchor ... read", which the last commit corrects.Gates (HEAD
3c0d3123)node scripts/check-doc-json-fences.mjspnpm type-check:scriptsplusvitest runover the gate's tests and every test that reads an edited page or the workflow (40 files)pnpm check:doc-typespnpm check:doc-fencespnpm docs:check-linkspnpm check:new-line-citationspnpm check:control-bytesnode scripts/check-doc-expression-carriage.mjspnpm check:test-path-rootspnpm lint:rootnode scripts/check-changeset-presence.mjsnode scripts/check-governed-queue-guard.mjs --testover the 21 pathspnpm check:doc-snippets/pnpm check:doc-examplesAcceptance notes
formfields[].defaultValueis read at runtime on a TEXT field: the probe rendersvalue="zzz"with it and nothing without it. It is inert on a select field, which is the case this PR repaired. The strict face refuses it, soobjectui validatetells an author to remove a key that works on text fields. Reported in the card report; no declaration is made here.content/docsare not read. At this base the strict face refuses four json node documents there: two in the rootREADME.md, one inpackages/plugin-dashboard/README.md, and one in a dated audit underdocs/audits/. Widening the surface needs those repaired or ledgered first. Reported, not done here.guide/dashboard-filters.mdwas listed as a class (i) carrier in the earlier reports. It is not a node at all: itstype: "select"is a filter input type that collides with a node type.The session that produced this change is
https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W.Generated by Claude Code