Found by the os-dev seat implementing objectui#9370 (PR #9378) while enumerating every ${data.…} and every expression example under skills/. This one is independent of objectui#9308: it is wrong on main today, and it stays wrong after that ruling lands.
The example
skills/objectui/guides/testing.md, "Pattern 5: Component rendering with SchemaRenderer", measured on origin/main 69aa9c017, lines 130-169. The fence builds a provider bag and then asserts the gated node is on screen:
const dataSource = { users: [...], userRole: 'admin' };
...
schema={{ type: 'text', content: 'Secret', hidden: '${userRole !== "admin"}' }}
...
// admin should see it
expect(screen.getByText('Secret')).toBeDefined();
userRole is spelled as a bare root. Nothing publishes it as one. SchemaRendererProvider's dataSource never spread its keys as bare names — the guide's own sibling skills/objectui/guides/auth-permissions.md documents exactly that trap at lines 319-327 ("Keys of the dataSource object are reachable only under the data. root — they are not also spread as bare names").
Measured, not inferred
On the built evaluator (packages/core/dist, tree 69aa9c017), evaluateCondition('${userRole !== "admin"}', scope):
| scope |
hidden verdict |
node on screen |
what the OLD renderer built: { data: { userRole: 'admin' } } |
true |
NO |
| what the renderer builds after objectui#9308: nothing published |
true |
NO |
what the example ASSUMES: { userRole: 'admin' } |
false |
YES |
So the node is hidden, getByText('Secret') throws, and the published test pattern fails for a reader who copies it verbatim. It is the fail-soft leg: userRole is not in scope, the expression is unevaluable, and the condition path returns true.
Why no gate sees it
--measure on scripts/check-skill-examples.mjs puts this fence in the root-bound refusal set — it imports vitest and @testing-library/react, which only the repository ROOT manifest declares, so the bound keeps it out of the semantic program and it is never type-checked. It is also unmarked, so it is outside the opt-in population either way. And type-checking would not catch this regardless: the defect is a runtime scope fact, not a type.
This is the class the gate's own header names — a fence can be wrong in a way no gate sees — so it needs a human edit, not a marker.
Suggested shape
Either publish userRole as a real root through PredicateScopeProvider (the channel objectui#9308 makes canonical, and the one a reader should be learning in a testing guide), or spell the gate against a root the example actually publishes. ⛔ Do not "fix" it by asserting the node is absent — the comment // admin should see it says what the pattern is meant to demonstrate, and inverting the assertion would publish a test pattern that teaches the wrong wiring while passing.
⚠️ Governed surface (skills/**, GOVERNED_SURFACES skills-catalog): an agent drafts, a human merges. ⛔ No ready-flip, no queue, no auto-merge.
Dedup
Run over the 83 cards this repository's issue-list endpoint returns for labels=domain:skills, non-open cards included, with objectui#9370 as the known-hit control — the control was found, so the zero is a reading. The two prior testing.md cards are different subjects: objectui#7557 retired the KNOWN_ROOT_DEVDEP_EXAMPLES rows and objectui#7494 the KNOWN_BARE_ANY_EXAMPLES rows. Neither touches what this fence asserts.
⛔ Filed without labels — routing and priority are the PM seat's.
Generated by Claude Code
Found by the
os-devseat implementing objectui#9370 (PR #9378) while enumerating every${data.…}and every expression example underskills/. This one is independent of objectui#9308: it is wrong onmaintoday, and it stays wrong after that ruling lands.The example
skills/objectui/guides/testing.md, "Pattern 5: Component rendering with SchemaRenderer", measured onorigin/main69aa9c017, lines 130-169. The fence builds a provider bag and then asserts the gated node is on screen:userRoleis spelled as a bare root. Nothing publishes it as one.SchemaRendererProvider'sdataSourcenever spread its keys as bare names — the guide's own siblingskills/objectui/guides/auth-permissions.mddocuments exactly that trap at lines 319-327 ("Keys of thedataSourceobject are reachable only under thedata.root — they are not also spread as bare names").Measured, not inferred
On the built evaluator (
packages/core/dist, tree69aa9c017),evaluateCondition('${userRole !== "admin"}', scope):hiddenverdict{ data: { userRole: 'admin' } }truetrue{ userRole: 'admin' }falseSo the node is hidden,
getByText('Secret')throws, and the published test pattern fails for a reader who copies it verbatim. It is the fail-soft leg:userRoleis not in scope, the expression is unevaluable, and the condition path returnstrue.Why no gate sees it
--measureonscripts/check-skill-examples.mjsputs this fence in the root-bound refusal set — it importsvitestand@testing-library/react, which only the repository ROOT manifest declares, so the bound keeps it out of the semantic program and it is never type-checked. It is also unmarked, so it is outside the opt-in population either way. And type-checking would not catch this regardless: the defect is a runtime scope fact, not a type.This is the class the gate's own header names — a fence can be wrong in a way no gate sees — so it needs a human edit, not a marker.
Suggested shape
Either publish
userRoleas a real root throughPredicateScopeProvider(the channel objectui#9308 makes canonical, and the one a reader should be learning in a testing guide), or spell the gate against a root the example actually publishes. ⛔ Do not "fix" it by asserting the node is absent — the comment// admin should see itsays what the pattern is meant to demonstrate, and inverting the assertion would publish a test pattern that teaches the wrong wiring while passing.skills/**,GOVERNED_SURFACESskills-catalog): an agent drafts, a human merges. ⛔ No ready-flip, no queue, no auto-merge.Dedup
Run over the 83 cards this repository's issue-list endpoint returns for
labels=domain:skills, non-open cards included, with objectui#9370 as the known-hit control — the control was found, so the zero is a reading. The two priortesting.mdcards are different subjects: objectui#7557 retired theKNOWN_ROOT_DEVDEP_EXAMPLESrows and objectui#7494 theKNOWN_BARE_ANY_EXAMPLESrows. Neither touches what this fence asserts.⛔ Filed without labels — routing and priority are the PM seat's.
Generated by Claude Code