Skip to content

feat(sdui-parser)!: one parser body in @objectstack/sdui-parser (#25712 2g PR 1) - #26211

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-6qe56u
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-6qe56u

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of #25712 (4b 2g PR 1: one parser body; Tier S).
Part of objectstack#25677.

Clause-②: yes (narrowing: validateTree now also judges the nodes held in a manifest entry's declared slots, so a caller whose manifest carries slots gets diagnostics on slot-held nodes it did not get before)
Metadata-protocol: none

维护者速读(草稿)

  • 改了什么:
  • 哪些原来能保存的现在会被拒:没有。 保存门(os validate / build / lint 和 os serve 的保存入口)读的是仓里跟踪的 sdui.manifest.json,也就是 @objectstack/console 随包带的那份。它 106 个条目里 0 个带 slots,所以新增的槽位遍历在保存门上无事可做。只有自己用 slotsFor 生成清单的调用方才会走到它,今天只有渲染器,而渲染器那份解析器本来就在遍历。
  • 为什么改: 两份解析器靠 check:sdui-lockstep 维持同一套语法和诊断码,其余部分各自漂移。本 PR 是 2g 的第 1 步。PR 2 把 ui 的 122 个引用改到本包并删除 ui 那份;PR 3 退役 lockstep 门。
  • 风险与代价(含回滚): 库层面的收窄,带 BREAKING 标注,按 patch 发布(元数据协议不变)。parseJsx 产出的节点多了一个 JSON 看不见的 symbol 标记。回滚 = revert 本 PR。
  • 你要做的: 无(Tier S)。

席位核对

(left for the dispatch seat)

Why

Two copies of the constrained JSX parser existed:

  • @objectstack/sdui-parser (packages/sdui-parser), which the save gate runs;
  • @object-ui/sdui-parser (ui/packages/sdui-parser), which the renderer runs.

check:sdui-lockstep held them to one grammar region, one diagnostic-code set and one containment predicate, and they drifted everywhere else. Tesla's 2g ruling keeps @objectstack/sdui-parser as the one package with no forwarding release, deletes the old package, rewrites its importers and takes @object-ui/sdui-parser out of the fixed group. This PR is step 1: the body becomes one, here. The plan, module by module, is #25712 comment 6419004222.

What changes

Source, module by module

Module Winner What this PR does
input-type.ts n/a Unchanged; the copies were identical.
codegen.ts ui The generated .d.ts header gains /* eslint-disable */ and names @objectstack/sdui-parser. No checked-in output carries the header.
parse.ts ui ParsedSchemaNode. Every element is stamped with markHtmlTierNode at production. The LOCKSTEP comments go; the forbidden-attr rationale no longer leans on the gate.
types.ts ui Adds ParsedSchemaNode, ManifestValidationResult and ManifestComponent.slots. Keeps SchemaNode and ValidationResult as @deprecated aliases.
index.ts ui The renderer copy's superset of exports (below) and manifestFromConfigs' slotsFor option. The binding note keeps this copy's pointer to the single record in types.ts.
validate.ts ui code, os severity Adds the node-slot walk (objectui#11170) and the body replacement in the unknown-prop branch. checkType and checkMemberTypes keep this package's rule: every mismatch is an error. The LOCKSTEP comments go.
dashboard-widget-options.ts os code, ui header The emitted code: stays an inline literal, because of check:dispatcher-error-vocabulary. The header takes the renderer's census derivation, re-rooted to the ui/ paths now in this tree, and keeps the spec-side drift section that check:widget-option-census holds.
body-dialect.ts moved in MIT notice kept.
provenance.ts moved in MIT notice kept. The registry key @object-ui/sdui-parser.html-tier-node is unchanged, because it is the cross-instance contract.

New exports:

  • HTML_TIER_NODE, isHtmlTierNode, markHtmlTierNode;
  • acceptsChildren, CHILD_LIST_KEY, SDUI_BASE_PROPS, SduiBaseProp, SduiBasePropScope;
  • checkRetiredBodyDialect, RETIRED_CHILD_LIST_KEY;
  • NodeSlotDeclarationLike, assertFullyLoaded, RegistryConfigLike.lazy.

These cover every name the 2g-b importer inventory needs.

Licence

  • packages/sdui-parser/LICENSE-MIT is a verbatim copy of ui/packages/sdui-parser/LICENSE (MIT, Copyright (c) 2024 ObjectQL), added to files. npm pack --dry-run lists it. scripts/check-published-files.mjs registers it in EXTRA_ENTRIES with its reason, because the gate admits no unexplained files entry.
  • The two moved modules open with a /*! … */ notice that names the licence, the copyright line and LICENSE-MIT. esbuild keeps it in both dist/index.js and dist/index.mjs.
  • The four ported tests that carried the renderer's MIT header keep it, with its pointer retargeted to LICENSE-MIT.
  • The license field stays Apache-2.0.

Behaviour, measured

  • The save gate's verdicts do not move. scripts/gen-sdui-manifest-node.mjs generates the tracked sdui.manifest.json through manifestFromConfigs without slotsFor, so 0 of its 106 entries carry slots. packages/console/build-dist.sh copies that file over the console's dist/sdui.manifest.json, and that copy is what resolveSduiManifest gives os validate, build, lint and os serve's save door. The slot walk only works for a caller that builds a manifest with slotsFor. Today that is the renderer, whose own copy already walks the slots.
  • body. The message now names children. A body child list under a component that takes no children draws not-a-container, the code the children spelling draws. Both stay warnings.
    • The two sentences drop the renderer copy's objectui#6771, because check:doc-authoring refuses a tracker id in author-facing text (the module header keeps the citation). The renderer copy's text, ("body" was retired by objectui#6771), becomes ("body" is its retired spelling). node-slot-walk-11170 pins the new text.
  • The tree. parseJsx elements carry an enumerable, symbol-keyed marker. JSON.stringify and Object.keys do not see it. The consumers' suites pass unchanged (Verification).

Tests (packages/sdui-parser/src/__tests__)

  • Ported whole (7): base-props-bind-hidden-11008, base-props-one-list-11044, body-dialect-6771, input-type-union, node-slot-walk-11170, provenance, tier. Path pointers in comments are re-rooted to ui/. provenance's "objectui-only" sentence now says what was true until this change.
  • ui-only cases merged into this package's twins (7):
    • containment-declared-slot +3: the base key, the body spelling, and the isContainer projection. The import now comes from ../index.js.
    • dashboard-widget-options +3: the retired-sub-caption shapes and their control.
    • type-attribute-collision +1: the no-type regression guard with provenance. Re-ablated: restoring { type: tag, ...props } keeps all nine green, so the header now says "nine".
    • The plan's estimate was 12; the other five were already here under different titles or quote styles. literal-subset's __proto__ case is covered by this file's it.each, and member-kind's two by member-type-mismatch.
  • Exact twins, so not ported (4): compile, html-tier-manifest-10735, inert-expression-6598, kanban-quick-add-8285.
  • New: deprecated-type-aliases.test.ts (2 cases) pins both aliases at type level. Negative control: re-pointing one fails pnpm typecheck with TS2344.
  • Left for PR 2 (5), because each imports @object-ui/*, React or the spec, or reads ui sources: injected-component-input-6950, inline-whitespace.test.tsx, render.test.tsx, spec-symbol-batch7, dashboard-widget-options-census.
  • Severity. No ported case pinned warning for a non-enum mismatch, so no expectation flipped.

What this leaves for PR 2 and PR 3

  • PR 2 rewrites importers to @objectstack/sdui-parser. Old names are not for new code.
    • Once ui imports this package, a non-enum mismatch is an error in the Console too.
    • ui/scripts/__tests__/unconsumed-widget-option-claim-6186.test.ts reads the ui copy's dashboard-widget-options.ts path, and the census header here points at the census test's current path. Both move with it.
    • The old body message text is pinned in ui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx and quoted in ui/content/docs/components/layout/semantic.mdx.
    • scripts/sdui-manifest.sources/sdui-parser.json records ui/packages/sdui-parser as a registry source.
  • PR 3 retires check:sdui-lockstep. It is green on this head (9 and 9 sources agree), so the PRs can land in either order.

Verification

Head 930df7bda, on main 6b9355299. 24 files, +1794 / −191, in two commits. Tier S: no governed path (each checked with check-governed-merges --test), under 3000 lines.

  • @objectstack/sdui-parser: vitest run, 22 files, 349 tests pass. pnpm typecheck and pnpm build (tsup + check-dts-emitted) exit 0.

  • Consumers, built against this dist: packages/lint 164 files / 7356 tests pass; packages/metadata-protocol 266 files / 21539 tests pass (3 files / 19 tests skipped by their own guards).

  • @objectstack/spec vitest --project repo:

    • On 1951e860e: 1 of 1279 failed, visibility-alias-keys-retirement. It is the same failure CI's Test Core (1/8) showed on that head.
    • Its packages/** walk now reaches the ported base-props-one-list-11044.test.ts, whose fixture gives the SDUI tier's deprecated-but-accepted visibleOn base prop a value. Under ui/ the walk never read it.
    • 930df7bda adds the file to the walk's structural exclusions with its reason. That file then passes 12/12, and main's same shard is green.
  • pnpm check:sdui-lockstep: OK in-tree. The grammar region is byte-identical (214 lines), all 25 codes agree, the containment predicate agrees, across 9 and 9 sources. PR 3 can land before or after this one.

  • Changeset gates:

    • check-changeset-no-major --event with this body's declaration: level axis ✓, patch with yes (narrowing) and none.
    • check-adr-0087-registration: ✓, not-required (no-migration-prescription).
  • Gate sweep: node scripts/pm/dispatch-gates.mjs --commands derives 120 gates on 930df7bda. All 120 ran and exited 0. --ran reconciles 120 derived, 120 run, 0 NOT-MEASURED, 0 UNRUN.

    • Four spec gates that read built declarations (check:api-surface, check:dual-source-exports, check:entry-nameability, check:exported-any) first refused with exit 3 on a stale local spec dist. They were re-run green after pnpm --filter @objectstack/spec build.
    • Earlier passes:
      • Pass 1 (81 gates, pre-fix head) found check-issue-citations, check:doc-authoring and check:published-files red; see Behaviour and Licence.
      • Pass 2 (100 gates, 1951e860e) was 100/100 green.
  • Push battery (GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs) on 930df7bda: 2182 cases pass, 0 fail, EXIT 0. It was also 2182/0 on 1951e860e.

  • CI on 930df7bda: 41 success, 8 skipped, 0 failed; mergeable clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS

Unify the save gate's parser with the renderer copy's additions, per the
2g ruling on #25712: node-slot validation, the retired `body` dialect,
html-tier provenance and the renderer copy's exports join this package.
body-dialect.ts and provenance.ts move in from @object-ui/sdui-parser and
keep their MIT notice (LICENSE-MIT ships with the package).

This package's rules win where the copies disagreed: every type-mismatch
and member-type-mismatch stays an error, and unconsumed-widget-option
stays an inline literal. SchemaNode and ValidationResult stay exported as
deprecated aliases of ParsedSchemaNode and ManifestValidationResult.

The renderer copy's pure tests are ported; exact twins are skipped and
the ui-only cases are merged into this package's twins.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
@github-actions github-actions Bot added size/xl dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Oct 26, 2026
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/sdui-parser, touching 25 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/sdui-parser/LICENSE-MIT, packages/sdui-parser/package.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/quick-reference.mdx (via ValidationResult (symbol, a top-level interface; a top-level type))
What this run could not see
  • 2 changed file(s) yielded no anchor (packages/sdui-parser/LICENSE-MIT, packages/sdui-parser/package.json) — pages documenting those are invisible to this run
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 3d50a43bcb55053e0501d939b304242b41cfde19 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1850004b8b3721c9f04441ca51a3b1bf1b8288e9 — the merge of head f87ef4fdbe825603ff2d92ab85b529b060bfaf33 into base 3d50a43bcb55053e0501d939b304242b41cfde19, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1850004b8b3721c9f04441ca51a3b1bf1b8288e9 && git checkout 1850004b8b3721c9f04441ca51a3b1bf1b8288e9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d50a43bcb55053e0501d939b304242b41cfde19 f87ef4fdbe825603ff2d92ab85b529b060bfaf33 && git checkout -B drift-repro 3d50a43bcb55053e0501d939b304242b41cfde19 && git merge --no-ff f87ef4fdbe825603ff2d92ab85b529b060bfaf33

node scripts/docs-audit/affected-docs.mjs --json 3d50a43bcb55053e0501d939b304242b41cfde19

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3d50a43bcb55053e0501d939b304242b41cfde19 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…alias walk (#25712)

The ported base-props-one-list-11044 test gives every SDUI base prop a
well-typed value, `visibleOn` included: the parser tier still accepts the
deprecated key on every node (SDUI_BASE_PROPS). Under ui/ the walk never
reached it; under packages/ the visibility-alias retirement walk reads it as
a spec authoring. The file's job is to spell the key, so it joins the walk's
structural exclusions beside the other "the key is still read here" pins.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
Resolves packages/sdui-parser/package.json: main's publishing metadata
(#26198, engines) and this branch's LICENSE-MIT `files` entry both kept.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
…ot a minor (#25712)

The changeset is a patch; the guard the comment names refuses a major.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 26, 2026
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 8faba89 Oct 26, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-6qe56u branch October 26, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:ui size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants