Uh oh!
There was an error while loading. Please reload this page.
skills(objectui): record the measured envelope reality instead of contradicting it - #5641
Merged
Merged
Conversation
…tradicting it The published rules told authors that the `properties` / `props` envelope belonged to the `element:*` namespace and that every other key "lives on the node". Measured against a real `SchemaRenderer` inside a `SchemaRendererProvider`, only half of that is true, and the wrong half is the half an author hits when wiring provider data into a `data-table`. `props` is evaluated and then spread as React props, so a `ui:*` / `page:*` renderer never sees it. `properties` is evaluated and then hoisted onto the node (`type` / `id` excepted), so it reaches every renderer in every namespace. A node key is read but never evaluated. The consequence the rules got backwards: the one envelope they told an author not to reach for was the only spelling that put a provider's rows into a `data-table`, while the two they endorsed both rendered a correct header over the empty state with nothing thrown and nothing logged. The guides now record that measurement. The taught route is unchanged — keys on the node, values resolved in the host — because whether `properties` is an official `ui:*` authoring channel is an open contract question (#4795 ②); it is recorded, not recommended. Two directions stay explicitly untouched: node-level evaluation is not widened, and `data-table` does not gain a `bind` read (#5125 declined that for its sibling renderer). Swept by claim rather than by phrase, which found two sites carrying the retired `props.*` workaround with none of the envelope tokens: the Common Mistakes bullet in `SKILL.md` and its twin in `page-builder.md`, and the graded eval fixtures, whose `must_contain` marked `props.value` / `props.label` as the correct answer. A new pin renders all three spellings through the real renderer and guards the class: no published guide may prescribe the envelope, and no published eval may require a `props.*` spelling in a correct answer. Part of #5372 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012u2pRjcqAYtoEjgr3wwhnK
Contributor
✅ 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
|
os-sales
marked this pull request as ready for review
August 21, 2026 22:40
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#5372
The published rules told authors that the
properties/propsenvelope belonged to theelement:*namespace and that every other key "lives on the node". Measured against the real renderer, only half of that is true — and the wrong half is the half an author hits when wiring provider data into adata-table. The guides now record the measurement instead of contradicting it.⛔ Draft on purpose, and it should stay one. The seat dispatched this as a governed surface: human merge only, no ready flip, no auto-merge, no queue. See the one conflict note at the bottom before acting on that.
Re-measured first — the card's claim is a runtime statement, and the tip had moved
The card was filed against
99d8721a3; triage annotated it at 2026-08-21T10:33Z. Re-measured onorigin/mainf1c27f037, realSchemaRendererinside aSchemaRendererProviderholding{ customers: [ 2 records ] }, identicalcolumnsin every leg, readingtbody td:{ "type": "data-table", "data": "${data.customers}", "columns": [...] }["No results foundTry adjusting your filters or search query."]{ "type": "data-table", "properties": { "data": "${data.customers}" }, ... }["Ada Lovelace","ada@example.com","Grace Hopper","grace@example.com"]{ "type": "data-table", "props": { "data": "${data.customers}" }, ... }["No results foundTry adjusting your filters or search query."]{ "type": "data-table", "data": [ 2 literal records ], ... }["Ada Lovelace","ada@example.com","Grace Hopper","grace@example.com"]{ "type": "data-table", "bind": "customers", ... }["No results foundTry adjusting your filters or search query."]The card's claim holds on the current tip, unchanged. Exactly one of the three spellings reaches the provider's data, and it is the one the rules told authors not to reach for.
What the measurement actually shows — the rule was wrong about the namespace, not just about
data-tableThe card explains the
data-tablecase. Probing aui:*cardshows the same mechanism is not namespace-scoped, which is what makes the published sentence false rather than merely incomplete (same tip,dataSource = { label: "Evaluated Title" }):{ "type": "card", "title": "Customer Summary" }Customer Summary{ "type": "card", "props": { "title": "Customer Summary" } }{ "type": "card", "properties": { "title": "Customer Summary" } }Customer Summary{ "type": "card", "title": "${data.label}" }${data.label}— read, never evaluated{ "type": "card", "properties": { "title": "${data.label}" } }Evaluated TitleSo there are two envelopes with two different fates, and the rules had merged them into one:
props.*— evaluated, then spread as React props. Aui:*/page:*renderer readsschema.*and never sees it.element:*'sreadProps()does. The old rule is right about this one.properties.*— the spec spelling; evaluated, then hoisted onto the node by the COMPAT hoist (type/idexcepted), so it lands where every renderer reads, in every namespace. The old rule was wrong about this one.Recorded, not recommended — and why that line is where it is
The taught route is unchanged: keys on the node, values resolved in the host before the schema reaches
SchemaRenderer. Whetherpropertiesis an officialui:*authoring channel is #4795's open sub-question ②, still awaiting the maintainer's one-liner (comment 5328302083: "Still awaiting the maintainer's one-liner on the two parked sub-questions: ② whether the diagnostic copy names thepropertieschannel"). Promoting it to a taught spelling here would settle that from a guide edit, so the guides state the measurement and name the open question instead.What is not open, and is now stated plainly: a
${...}on the node is never evaluated, a key underpropsnever reaches aui:*/page:*renderer, and neither failure says anything about whether the provider has data.⛔ Out of scope, and neither was prepared for
SchemaRenderer, no types file is touched — the diff contains zero packagesrc/changes outside one new test.data-tabledoes not gain abindread — declined by the finding(components):table被注册两次,data-display 的 SimpleTableRenderer 被 complex/table.tsx 完全遮蔽 —— 而它是唯一读bind的表格渲染器 #5125 ruling for its sibling renderer.#5125 remains open on its own termsand is not addressed here.Swept by claim, not by phrase — two sites had none of the searchable tokens
Grepping for the envelope wording finds
protocol.md,page-builder.mdandschema-expressions.md. Sweeping for the claim found two more that a phrase grep misses, both prescribing the workaround #4786 retired from the prose:SKILL.md:138and its twinpage-builder.md:481— a Common Mistakes bullet reading "Putting expression values in top-levelvalue/labelfields instead ofprops.*." It names the envelope as the fix.page-builder.mdtherefore contradicted itself: line 188 of the same file already said "Moving it underpropsdoes not help." Measured false above —props.titlerenders no header at all.skills/objectui/evals/schema-expressions.json— the graded form of the same false rule, and the one with no prose tokens at all. Evals 1 and 2 listedprops.value/props.labelinmust_contain, so an assistant answering with the spelling that renders blank was scored correct, and one answering correctly was scored wrong. Both now require the working answer and list the dead spelling undermust_not_contain.Fixing (2) is the in-place bounded exemption, declared: same defect class as this card, mechanical with the correct shape already pinned by the sibling prose and the 2026-08-17 ruling ("Direction 2 permanently rejected"; working channels are
contentor host pre-resolution), no other claim on the file (no open PR touchesskills/, no open issue names the evals), and the same gate family. Full file surface:skills/objectui/{SKILL.md, rules/protocol.md, guides/page-builder.md, guides/schema-expressions.md, guides/data-integration.md, evals/schema-expressions.json}plus one new test and one changeset.Published-skill net-addition budget
Preference honoured: the false teaching is replaced in place, not appended to.
SKILL.mdis +0 — a same-length swap.skills/objectui/SKILL.mdskills/objectui/rules/protocol.mdskills/objectui/guides/schema-expressions.mdskills/objectui/guides/page-builder.mdskills/objectui/guides/data-integration.mdskills/objectui/evals/schema-expressions.json.mdskills/tree incl. eval JSONThe +44 is concentrated in the one file whose central rule was false, and is mostly two measured tables standing in for prose that was wrong. The other four sites are pointers into it rather than restatements, which is what keeps the package delta at +1.2%. No sibling gate defines a token count for
skills/(scripts/carries onlycheck-skills-paths.mjsand its baseline), so lines are the reading.The pin
packages/components/src/__tests__/skill-guide-provider-envelope.test.tsxrenders all five legs through the realSchemaRendererand guards the class, in the same three-part shape as its siblingskill-guide-data-table-binding.test.tsx:instead of \props.`), and **no published eval may require aprops.*` spelling in a correct answer**.Verification — all at the final commit
2c03db447Changeset gate, quoted rather than assumed — it rules that none is owed, and an explicit empty-frontmatter declaration is added anyway (objectui's first-class "publishes nothing" form; this repo has no
skip-changesetlabel):Build, type-check and tests, exit codes captured before any pipe:
The type-check line is quoted because a
pnpm --filterthat matches no script exits 0 having run nothing; the echoed script name shows this one really ran (objectui spells ittype-check, hyphenated).Reverse verification. Both retired sites were re-introduced on the committed tree and the mutation was confirmed on disk by anchored greps before the run —
instead of \props.*`present (1),renders nothing at allabsent (0), and eval 1'smust_containre-parsed from JSON as['props.value', 'props.label', '${data.']`. Predicted direction was red on exactly the two class-guard cases, and that is what happened:The mutation script carried a
trap … EXIT INT TERMrestore; the restore leg is proven bygit status --porcelainreturning empty, i.e. byte-identical to the commit that ran 29/29 green. No rebuild leg is claimed because none applies here: the pin reads the guides throughfs.readFileSyncon source paths, and the renderer legs resolve through vitest's workspacealiasmap topackages/*/src, neverdist.Lint — a measured narrowing, not a skipped run. ESLint's own config is the population: it refuses
skills/outright ("all of the files matching the glob patternskills/are ignored") and reports markdown as "File ignored because no matching configuration was supplied", withfiles: ['**/*.{ts,tsx}']as its only source glob. So exactly one of the eight changed files is in its population, and--format jsonreportsfiles eslint linted: 1 | errors: 0 | warnings: 0.grep -c 'projectService\|project:' eslint.config.jsis 0 — no type-aware linting is configured, so nothing in this diff can move a verdict on a file it does not touch. CI runs the farm regardless.check:doc-snippetsis not quoted: it exits 1 as a broken gauge until the workspace is built, and its scan surface does not includeskills/(#5465).One conflict to relay, not to act on
The dispatch flagged
skills/**as a governed surface.AGENTS.md§受管面 says the opposite in as many words:.claude/skills/**is governed, while **skills/**at the repo root — the published tree, e.g.skills/objectui/— is explicitly not, and it warns that misjudging conservatively is still misjudging, because it leaves work parked. This PR is left as a draft either way, because a draft is where a dev seat's PR ends. Flagging it so the seat can reconcile the two before this sits waiting for a merge nobody is obliged to perform.Out-of-scope finding
colslayout spelling that protocol.md and mobile.md both forbid #5640 —SKILL.md:81still teachescols: { sm: … }for layout responsiveness, the spellingrules/protocol.md:252andguides/mobile.md:100both retire citing examples/schema-catalog: 13 个 grid 示例用了未声明的cols键,列数被静默丢弃 —— 要 3/4 列的示例在 docs 站实际渲染成 2 列 #4001. Different defect class (layout key, already ruled), so filed unassigned rather than ridden in here.Generated by Claude Code