docs(skills): move the three published guides off the retired dataSource expression root - #9378
Draft
os-tesla wants to merge 2 commits into
Draft
docs(skills): move the three published guides off the retired dataSource expression root#9378os-tesla wants to merge 2 commits into
dataSource expression root#9378os-tesla wants to merge 2 commits into
Conversation
…ession root
`SchemaRenderer` published `SchemaRendererProvider`'s injected `DataSource`
adapter as the expression root `data`, and `useDataScope` — what a node's
`bind` resolves through — walked that same adapter. The maintainer ruled both
retired (objectui#9308, 2026-09-13, option B): scope now arrives on the
`PredicateScopeProvider` channel a host publishes, and `dataSource` is the
adapter and nothing else. The published skill package still taught both halves.
`schema-expressions.md`, `data-integration.md` and `rules/protocol.md` now
state the scope channel, and two wiring examples carry the `os:check` marker so
they are COMPILED against the built `dist/*.d.ts` rather than only read: the
marked ts population moves 14 -> 16, 0 failed, and the json population is
unmoved at 70.
⭐ Not a renaming, and a note that only swapped provider names would mislead.
Measured on the built evaluator, a MISSING root and a PRESENT-but-empty root
differ: `${data.status == 'draft'}` is `false` against `{ data: <adapter> }` and
against `{ data: undefined }`, while on a scope carrying no `data` key the
condition path fail-softs to `true` and a text key renders the raw source
characters. So a `visible` gate authored from these pages was hidden on every
row and is now shown on every row; the same gate spelled `hidden` flips the
other way. The migration note states that flip, says that re-publishing `data`
restores the old always-false verdict rather than fixing the gate, and points
row gates at `record` — the runtime-layer root (ADR-0089 D3).
Every `${data.*}` example on these pages is kept verbatim and made reachable by
the wiring example publishing a `data` root, rather than rewritten to bare
names: `skill-guide-provider-envelope` renders those exact spellings, and
rewriting them would move a pin's subject inside another pull request's blind
spot.
The `object-*` reader-list paragraph and the `data-table` `bind` pothole
(objectui#6575) are untouched.
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Sep 13, 2026
… the evaluator
The migration callout compressed two different answers into one cell. They are
not the same answer and a reader needs both, because each has its own
reader-visible symptom.
Re-measured on the built evaluator with firing controls. Discriminating control
first: `${data.nope}` against a scope that DOES publish `data` returns
`undefined` — a present root with an absent member — while the same shape
against a scope with no `data` root at all returns something else entirely.
| scope | `evaluateExpression` | `evaluateCondition` |
|---|---|---|
| `{ data: { status: 'draft' } }`, `${data.status}` | `"draft"` | `true` |
| `{ data: { status: 'draft' } }`, `Status: ${data.status}` | `"Status: draft"` | `true` |
| `{ data: { status: 'draft' } }`, `${data.nope}` | `undefined` | `false` |
| `{}`, `${data.status}` | `"${data.status}"` | `true` |
| `{}`, `Status: ${data.status}` | `"Status: ${data.status}"` | `true` |
| `{}`, `${data.status == 'draft'}` | `"${data.status == 'draft'}"` | `true` |
| `{ data: undefined }`, same predicate | `false` | `false` |
| `{ data: {} }`, same predicate | `false` | `false` |
So the `true` on a missing root is the PREDICATE layer failing soft, not the
evaluator's answer. The evaluator hands back the template's own source
characters, in full-string and mixed forms alike — which is what a `content`
key then paints on screen. The callout now carries one column per layer and
says to read both.
The conclusion is unchanged: a `data.*` visibility gate still flips from
constant-hide to constant-show.
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
Co-authored-by: Claude <noreply@anthropic.com>
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 free
to 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 #9370
⛔ GOVERNED SURFACE (
skills/**,GOVERNED_SURFACESskills-catalog). This PR parks as a draft by design and needs an APPROVED review from an authorized approver (os-zhuang,hotlong). ⛔ Not flipped ready, not enqueued, no auto-merge. The precedent on this exact surface is #9352 / card #9311, merged as28be0786d.69aa9c017, not assumed:SchemaRenderer.tsxstill carries thedata: dataSourcebinding, andSchemaRendererContext.tsxstill readsconst dataSource = context?.dataSourceinsideuseDataScope. Both halves are still LIVE on this base — #9369 is open, not merged. So these pages become true the moment #9369 lands and are ahead of the code until then. Every evaluator measurement below is from that same pre-#9369 tree, which is the right place to take it: the evaluator itself is what #9369 stops feeding, and the evaluator's own behaviour does not move. Sequencing is a maintainer decision, not something this PR can enforce.What moved
The maintainer ruled objectui#9308 option B on 2026-09-13:
SchemaRendererstops publishing the injectedDataSourceadapter as the expression rootdata(b1), anduseDataScope— what a node'sbindresolves through — reads the ambient scope a host publishes viaPredicateScopeProviderinstead of walking that adapter (b2). The published skill package still taught both halves.Three files, matching the shape #9369 used for
content/docs/guide/schema-rendering.mdandpackages/react/README.md: publish the host values under real names throughPredicateScopeProvider, read them by those names, and state thatdataSourceis the adapter and not a root.guides/schema-expressions.mdbindresolution prose, the bound-list comment, debugging-checklist item 3guides/data-integration.mduseDataScoperesolution sentence, thedataroot claimrules/protocol.mdbindcomment and nested-path sentence, plus a callout carrying the retirement and pointing at the verdict flip⛔ Untouched, as the card requires: the
object-*reader-list paragraph and thedata-tablebindpothole (objectui#6575 still rulesdata-tabledoes not readbind).⭐ The part that is not a renaming
A migration note that only swaps provider names is wrong here, because the verdict moved. Measured by me on the built evaluator (
packages/core/dist), with firing controls — ⛔ and thetrueon a missing root belongs to the predicate layer, not to the evaluator:evaluateExpressionevaluateCondition{ data: { status: 'draft' } },${data.status}"draft"trueStatus: ${data.status}"Status: draft"true${data.nope}— present root, absent memberundefinedfalse{ data: {} }— adapter-shaped,${data.status == 'draft'}falsefalse{ data: undefined }, same predicatefalsefalse{}— nodataroot,${data.status}"${data.status}"true{}— nodataroot,Status: ${data.status}"Status: ${data.status}"true{}— nodataroot,${data.status == 'draft'}"${data.status == 'draft'}"trueThe third control is the discriminating one: a root that is PRESENT with an absent member yields
undefined, while a root that is MISSING yields the template's own source characters. So the two layers answer a missing root differently, and each has its own reader-visible symptom:true. A"visible": "${data.status == 'draft'}"authored from these pages was hidden on every row and is now shown on every row; spelled"hidden"it flips the other way.contentbuilt from a missing root paints the literal characters${data.status}on screen.The callout carries one column per layer and tells the reader to read both. It also states that re-publishing
datarestores the OLD always-falseverdict rather than fixing the gate, and points row gates atrecord— the runtime-layer root under ADR-0089 D3.What I decided about the ~two dozen
${data.…}examplesMeasured, not estimated:
schema-expressions.mdcarries 21 lines spelling${data.(one of them is the census's own scope-table row). Repo-wide underskills/: 21 here, 14 inrules/protocol.md, 5 inpage-builder.md, 4 intesting.md, 2 each indata-integration.mdandauth-permissions.md, 1 inarchitecture.md, plus 4 inevals/*.json.Decision: keep every one of them verbatim and make them reachable, rather than rewrite them to bare roots. The page's new wiring example publishes a root literally named
data, and the scope table says in a row that every${data.*}example on the page assumes exactly that. Three reasons this is the right divergence from #9369'scontent/docsshape:skill-guide-provider-envelope.test.tsx, as re-derived in feat(react)!: unbind the data-source adapter from the expression scope, and pointbindat the scope channel #9369, publishesscope = { data: PROVIDER }and renders${data.customers}/${data.label}. Rewriting the guide spellings would move that pin's subject from inside a second pull request — the "green alone, red together" shape.CANONICAL_ROOT_BY_LAYERputsdataat the metadata layer. What was retired is the renderer AUTO-publishing the adapter there, not the name.⛔ Nothing was silently rewritten: the only expression text this PR changes is the two
dataSource = {…}comments that named the retired wiring.Re-measurement of the card's per-line census
The card measured on the objectui#9308 branch. Re-measured against
origin/main69aa9c017— every cited line still lands on the cited text:origin/main69aa9c017dataSource = { customerNames: … },useDataScope("customerNames")**Nested paths work:** … resolves \dataSource.app.settings.users``// ✅ Bound data, already node-shaped: dataSource = { rows: … }premise_still_valid: true.Enumeration beyond the census (⛔ out of scope for this PR)
--measurejudges every candidate fence, marked or not, so a page can be wrong where no gate looks. Reading the prose as well turned up two more:guides/auth-permissions.mdcarries the same retired claim in its own words — a scope table row| \data` | the `dataSource` passed to `SchemaRendererProvider` |, the sentence "Keys of thedataSourceobject are reachable only under thedata.root", and "With no host scope mounted,dataandpage` are all you get". ⛔ Out of scope here: PR docs(skills): guard both useAuth members in the auth-permissions example #9374 holds that file, so an in-place edit would collide. Filed as finding(skills):auth-permissions.mdstill teachesdataSourceas thedataexpression root — a fourth file the objectui#9370 census missed #9379.guides/testing.mdPattern 5 assertsgetByText('Secret')for a node gated on${userRole !== "admin"}— a BARE root that no channel publishes, before or after the ruling. Copy the example and the assertion fails. Different defect class from this card, and independent of the ruling — it is wrong onmaintoday. Filed as bug(skills):testing.mdPattern 5 gates on a bareuserRoleroot nothing publishes — copy the example and the assertion throws #9380.Noted, not filed: the
${data.*}examples inpage-builder.md,architecture.md,i18n.md,project-setup.mdandevals/*.jsonare in the same reachable-only-if-the-host-publishes class; they are not falsifiably wrong and the skills lane that takes the auth-permissions card is their natural carrier.Verification
Reproduce-first, as required — the harness is trustworthy before any red or green from it is read:
Gate, verbatim, before and after this diff:
⭐ The marked ts population moves 14 to 16 and the json population is unmoved at 70 — both floors are SHRINK-ONLY and neither is breached. The two new marked fences are the wiring examples, so the thing this card is about is now COMPILED against the built
dist/*.d.tsrather than only read.Derived by hand from
package.jsonplus.github/workflows/(this repo has nodispatch-gates.mjs); every one run on this tree:check-skill-examples.mjs --self-testcheck-skill-examples.mjscheck-skills-paths.mjscheck-skill-eval-tokens.mjs --self-test/check-skill-eval-tokens.mjscheck-control-bytes.mjscheck-shell-escape-residue.mjscheck-new-cross-file-line-citations.mjscheck-changeset-presence.mjscheck-doc-links.mjscheck-governed-queue-guard.mjs --self-testPackage tests that READ these three documents (
markdown-test-inputs.mjs --changednames all three as test inputs, so CI runs the full shards): reported below the fold once the shared verify lock grants a turn.Line readings for the governed surface, as the contract requires:
guides/schema-expressions.mdguides/data-integration.mdrules/protocol.mdskills/**The added lines are the correction itself: one migration callout, two now-compiled wiring examples, and the sentences that replace the retired claims. No re-wrap was used to buy lines.
Changeset
None owed, by measurement rather than by inheritance:
node scripts/check-changeset-presence.mjs— "Compared the working tree with69aa9c017(merge-base with origin/main): 3 file(s) changed, 0 of them published source of a package the release covers, 0 of them a manifest whose published contract moved … No source or published contract of a released package changed in this range, so no changeset is owed." (exit 0)skillsinfiles/exports/main/module/types/bin. Positive control on the same scan: 38 manifests listdistinfiles[]. Nothing underskills/is shipped by any npm package.28be0786d(fix(skills): guard the DataSource read in the marked data-integration example #9352), changed oneskills/file and carried no changeset.skills/guide, DOES carry an empty-frontmatter changeset declaring "no package is released by this change". Both forms pass the gate. If the empty-frontmatter declaration is the house style for this surface, say so and I will add one.Governed-queue-guard verdict on this diff (verbatim)
Clause-② — contract review
needs:contract-reviewis hung on this PR in the same stroke as opening it, mirroring the card. Publishedskills/**making falsifiable contract-semantics claims about which roots a tier binds. ⛔ The governed-surface human merge does not substitute for it; the two stack, and neither limb is mine to clear.Inherited reds — ⛔ not from this PR
Bundle Analysisis red onmain(arrived with chore(deps): runpnpm dedupe --lockfile-onlyon an untouched main — the measurement (objectui#9215) #9316; a maintainer decision).Doc Snippet Type Checkis red onmainuntil feat(react)!: unbind the data-source adapter from the expression scope, and pointbindat the scope channel #9369 lands. This diff touches no document that gate scans (content/docs+ package READMEs + rootREADME.md, explicitly notskills/**).Acceptance notes
packages/componentspins feat(react)!: unbind the data-source adapter from the expression scope, and pointbindat the scope channel #9369 re-derived were read for what they assert about these files before a byte was changed, and every string they pin is intact —Rule: Keys Live on the Node, thehoists every key onto\s+the noderegex, the absence of the two retired sentences, theno md matches "instead of \props.`"class guard, and the"bind": "customerNames"` list example in each of the three guides.rules/protocol.md's two measured tables still citeorigin/mainf1c27f037as the commit they were measured on. This PR restates their WIRING, not their outcomes, and says so inline; a re-measurement on a current commit is a separate piece of work, and theskill-guide-provider-envelopepin is what actually holds those outcomes today. Carrier: whoever next re-derives that pin.维护者速读(草稿)
改了什么
把发布中的
skills/objectui三份指南从「dataSource就是表达式根data、bind走dataSource」改成 ruling 之后的真实通道:宿主用PredicateScopeProvider发布作用域,dataSource只是取数适配器。另外给两个接线示例加上os:check标记,让它们第一次真的被编译校验。为什么改
2026-09-13 的裁决(objectui#9308 option B)已经把这两件事退役,但发布给 AI 读的技能包还在教它们。更要紧的是:这不是改名。同一条
data.*门,旧接线下恒判false、新接线下因为根缺失走 fail-soft 恒判true—— 一个visible门从「每行都藏」变成「每行都显」。只换 provider 名字的迁移说明会让读者踩正这一脚。风险与代价(含回滚)
bindat the scope channel #9369 还没合。在它合进去之前,这三页描述的是尚未落地的行为。bindat the scope channel #9369 之后合。${data.*}示例原文,只让接线示例发布一个叫data的根 —— 因为skill-guide-provider-envelope这个 pin 正在渲染这些拼写,改写它们会在另一个 PR 的盲区里挪动 pin 的被测主体。git revert一笔即可,不影响任何已发布包(本仓 43 个 manifest 无一发布skills/)。席位意见
(留空,待
os-zhuang/hotlong定稿)你要做的
bindat the scope channel #9369,再本 PR。check-governed-queue-guard机械要求的)。needs:contract-review需要一份书面复核记录后才清 —— ⛔ 两道保障叠加,人工合并不替代它。skills/改动到底要不要写空 frontmatter changeset(fix(skills): guard the DataSource read in the marked data-integration example #9352 没写、docs(skills): guard both useAuth members in the auth-permissions example #9374 写了,门禁两边都放行)。Generated by Claude Code
Generated by Claude Code