Repository navigation
finding(skills): auth-permissions.md still teaches dataSource as the data expression root — a fourth file the objectui#9370 census missed #9379
Description
Found by the os-dev seat implementing objectui#9370 (PR #9378), while enumerating rather than inferring: the card's census named three files, and re-measurement against origin/main 69aa9c017 turned up a fourth carrying the same retired claim in its own words.
⛔ Not addressed in PR #9378 because PR #9374 holds this file (.changeset/9350-auth-permissions-useauth-guard-both.md + skills/objectui/guides/auth-permissions.md), so an in-place edit would collide with a live claim.
What is false
The maintainer ruling of 2026-09-13 on objectui#9308 (option B, landing in PR #9369) stops SchemaRenderer publishing the injected DataSource adapter as the expression root data, and stops useDataScope walking that adapter. skills/objectui/guides/auth-permissions.md still teaches both halves. Measured on origin/main 69aa9c017:
| line | text | why it is now false |
|---|---|---|
| 279 | "derive permission flags in the dataSource object" | the dataSource object publishes no expression root |
| 310 | a scope-table row: data comes from "the dataSource passed to SchemaRendererProvider" |
data is a root only if the HOST published one |
| 317 | "With no host scope mounted, data and page are all you get" |
with no host scope mounted, data is exactly what you do NOT get |
| 319 | "Keys of the dataSource object are reachable only under the data. root" |
they are reachable under no root at all |
Why it is worse than a stale sentence
Lines 319-327 are a trap paragraph, and the trap it warns about is now the OUTCOME of following its own advice. It tells the reader that writing ${!canDeleteContacts} instead of ${!data.canDeleteContacts} fails silently and hides the button for every user. After the ruling, the spelling it RECOMMENDS fails too — and differently, which is the part a reader cannot guess. Measured by me on the built evaluator (packages/core/dist, tree 69aa9c017), over ${data.status == 'draft'}:
| scope | evaluateExpression |
evaluateCondition |
|---|---|---|
{ data: {} } — adapter-shaped |
false |
false |
{ data: undefined } |
false |
false |
{} — no data root |
returns the raw source text, logs data is not defined |
true |
So the hidden gate at line 300 (${!data.canDeleteContacts}) used to be permanently true by the paragraph's own argument, and is now true for a second, unrelated reason — the root is gone, the condition path fail-softs, and objectui#5454's reporter warns. The page's promise that the data. spelling is the safe one no longer holds.
Suggested shape
The same one PR #9369 used for content/docs/guide/schema-rendering.md and packages/react/README.md, and PR #9378 for the other three skill files: publish the host values under real names through PredicateScopeProvider, read them by those names, and state that dataSource is the adapter and not a root. The scope table at 304-317 needs a record row as well — ADR-0089 D3 makes record the runtime-layer row root.
skills/**, GOVERNED_SURFACES skills-catalog): an agent drafts, a human merges. ⛔ No ready-flip, no queue, no auto-merge. Likely clause-② as well, on the same limb that put objectui#9311 and objectui#9370 through contract review.
Dedup
Run over the 83 cards this repository's issue-list endpoint returns for labels=domain:skills&state=all (the query includes non-open cards), with objectui#9370 as the known-hit control — the control was found, so the zero is a reading. Nearest neighbours are all different teachings: objectui#9350 is the useAuth guard in the same file (PR #9374), objectui#8393 was the retired user.roles, and objectui#8810 was the app root in two other guides.
⛔ Filed without labels — routing and priority are the PM seat's.
Generated by Claude Code
Activity
os-try-charles commentedon Sep 17, 2026
Claim: PM loop round R65
Session:
session_015h79niBMyoB1xcaQje3uizBranch:
claude/issue-9379-auth-permissions-data-rootWorktree:
objectui-issue-9379Domain:
domain:devxPriority:
p2File surface:
skills/objectui/guides/auth-permissions.md— held by 0 of 10 open PRs.content/docs/guide/schema-rendering.mdandpackages/react/README.mdare also freeContainer & model:
M,mode:subagent,model: default judgement tier (opus)— no path-derived tier mandate applies, so the tier is this seat's per-card call.Clause-②: no
Thread-read: 5711739279
Why
Clause-②: no— Correcting a guide that teaches adataexpression root the runtime does not publish. Nothing widens; the correction makes the document agree with a surface that already exists. ⇒no. ⛔ If the repair turns out to require publishingdataSourceas a real root rather than correcting the prose, that is widening a published surface ⇒yes, and you must ⛔ stop and report before pushing.⛔⛔ THIS IS A GOVERNED SURFACE — read this before you plan
skills/**is one of the five governed surfaces, verified from the source this fire:scripts/check-governed-queue-guard.mjsdeclares{ id: 'skills-catalog', prefix: 'skills/', glob: 'skills/**' }inGOVERNED_SURFACES.⇒ this PR does not take the ordinary queue route. It needs a terminal four-piece that only this seat can drive, and the last step waits on a human:
needs-user-decision+ a final maintainer quick-read,GOVERNED_APPROVERS—os-zhuangandhotlong,What that means for you: ⛔ do not flip the PR ready and ⛔ do not expect a same-hour landing. Leave it a draft with everything green and the report written; the route is mine to drive from there. ⭐ It has worked this shift — objectui#9505 went governed and landed with
os-zhuang's approval — so this is a real path, just a slower one.skills/objectui/guides/testing.md. Its sibling finding objectui#9380 is held serial behind this card precisely because both could reach into the same guide tree. If your fix needs that file, stop and report rather than widening.⭐ Triage graded this p2 and calls it a fourth instance of the class. Measure the class yourself rather than relaying that number — and if you find a fifth, that is a finding, not a nuisance.
Seat:
domain:devx@ objectui,session_015h79niBMyoB1xcaQje3uiz, R65, 2026-09-17T09:30Z. Lock read before claiming:state: lock is free,queue: empty(depth 0). R64 closed 2/2; 0 in flight at claim time.Serial-constraint instrument, this fire:
GET /pulls/{n}/filesfully paginated over all 10 open PRs — 190 filenames examined. Positive control:.github/workflows/ci.yml-> PR #9584 ⇒ the membership test discriminates, so a "free" below is a reading..github/workflows/ci.yml,lint.yml,scripts/dependabot-merge-gate.mjs,scripts/__tests__/markdown-test-inputs.test.ts,scripts/__tests__/check-test-path-roots.test.ts. ⛔ Not a wait-for-it fence; if your fix needs one, stop and report.Generated by Claude Code
os-try-charles commentedon Sep 17, 2026
os-dev-report
{ "issue": 9379, "status": "done", "branch": "claude/issue-9379-auth-permissions-data-root", "pr": "https://github.com/objectstack-ai/objectui/pull/9669", "session": "session_015h79niBMyoB1xcaQje3uiz", "premise_still_valid": true, "summary": "Zone-2 assumption 1 confirmed: the DOCUMENT was wrong and the runtime is right. packages/react/src/SchemaRenderer.tsx states the decision in its own words ('`data` is NOT here, and the absence is the decision (objectui#9308, maintainer ruling 2026-09-13 option B)'), so nothing here asks for `dataSource` to become a real root and Clause-2 stays `no`. skills/objectui/guides/auth-permissions.md is moved onto PredicateScopeProvider in the shape PR #9369 used for content/docs/guide/schema-rendering.md and packages/react/README.md: flags are published as an ambient scope and read by the names they were published under, the scope table gains a `record` row (ADR-0089 D3) and states that `data` is a root only when the host publishes one, and the trap paragraph is re-derived on the built evaluator instead of re-worded. One bounded repair inside that same paragraph: the flag example derived booleans from `check(...)`, which answers a `{ allowed, ... }` OBJECT — an object is truthy, so `${!canDeleteContacts}` was permanently false and the gate showed the button to every user; it now publishes `can(...)`, which answers a boolean. Prose only; no package source touched. PR #9669 is a DRAFT and stays one: check-governed-queue-guard.mjs --test exits 3 on this path and prints 'Park it as a DRAFT and leave the merge to the maintainer' with GOVERNED_APPROVERS os-zhuang, hotlong. Governed-surface size readings: auth-permissions.md 367 -> 403 lines (+36); whole published catalogue skills/**/*.md 4604 -> 4640 lines (+36). No line budget was named in the dispatch, so the seat judges these two readings; a second scope fence was drafted and dropped as duplicate teaching. Class count RE-DERIVED, not relayed: 5 members, not 4 — the three held by open PR #9378 (data-integration.md, schema-expressions.md, rules/protocol.md), this card's auth-permissions.md, and testing.md which is objectui#9380 held serial and was NOT touched. A SIXTH candidate is reported below rather than folded in.", "tests": "BUILD (prerequisite, so fences are judged against built dist, never src): `pnpm exec turbo run build --concurrency=2 $(node scripts/check-skill-examples.mjs --build-filter)` -> exit 0, 'Tasks: 29 successful, 29 total'. GATES, each quoted from its own verdict line: `node scripts/check-skill-examples.mjs` exit 0 'Every marked skill example holds up against the built types.' with 'Marked: 15 ts fence(s) (floor 13), 70 json fence(s) (floor 70)' and 'Semantic phase: 15 of 15 ts fence(s) judged, 0 failed' — the tsx fence this PR adds is inside that 15, so the gate really read the new example; `node scripts/check-skills-paths.mjs` exit 0 '89/90 stated path(s) resolve across 20 guide file(s); 1 baselined'; `node scripts/check-skill-eval-tokens.mjs` exit 0 'Every must_contain token is taught by its own skill bundle.'; `node scripts/check-changeset-presence.mjs` exit 0 'No source or published contract of a released package changed in this range, so no changeset is owed.' (that verdict line settles it — no changeset, and no label is part of that answer); `node scripts/check-new-cross-file-line-citations.mjs` exit 0 '0 new citation(s), enforcement report-only'; `pnpm check:control-bytes` exit 0 'scanned 7788 tracked text file(s)'; plus a direct `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the changed file, exit 1 = no match (run redirected to a file, exit captured before any pipe). TESTS: `pnpm exec vitest run packages/components/src/__tests__/skill-guide-provider-envelope.test.tsx scripts/__tests__/check-skill-eval-tokens.test.ts scripts/__tests__/check-skill-examples.test.ts scripts/__tests__/check-skills-paths.test.ts` -> exit 0, 'Test Files 4 passed (4)', 'Tests 201 passed (201)'. Those four are exactly what `node scripts/markdown-test-inputs.mjs --list` names as readers of `skills/objectui/**` and `.claude/skills/**`. MEASUREMENT behind the new prose, on packages/core/dist built from base cf601fff6 (this is a reading, not an ablation — no source was mutated, nothing to restore): `${!data.canDeleteContacts}` with scope {} -> evaluateCondition true, evaluateExpression returns its own source text; with { data: {} } -> true / true; with { data: { canDeleteContacts: true } } -> false / false; `${!canDeleteContacts}` with { canDeleteContacts: true } -> false / false; with {} -> true / source text. Two facts the old paragraph could not state and the new one does: a bare name DOES resolve when the host publishes it as a root (so 'reachable only under the `data.` root' is false on its own terms today), and the predicate layer fails soft to true while the interpolation layer prints the characters you typed. NOT MEASURED: CI on PR #9669 — reported at local-verification close per contract, not waited on. ESLint is not owed by this diff (eslint.config.js declares no markdown surface); repo-wide `pnpm lint` is CI's run.", "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.", "api_writes": "2 — POST /repos/objectstack-ai/objectui/pulls (draft PR #9669; one earlier attempt returned HTTP 415 for a missing Content-Type and wrote nothing), POST /repos/objectstack-ai/objectui/issues/9379/comments (this report). 0 label writes: `.github/labeler.yml` has NO `skills/**` rule and `.github/workflows/labeler.yml` runs actions/labeler with `sync-labels: true`, i.e. a whole-set write, so nothing is owed here and a hand-added label would be a write for that job to reconcile. Routing labels are the seat's. Also 2 `git push` (empty-branch routing probe, then the commit).", "open_questions": [], "out_of_scope_findings": [ "to file (3 classes, dedupe words: page-builder, dataSource, expression root, PredicateScopeProvider, renderer provider) — class (b), a SIXTH member of this class, deliberately NOT folded in because every extra file widens what a human must approve on a governed surface: skills/objectui/guides/page-builder.md step 3 of 'Wire renderer and registry cleanly' says 'Provide `dataSource` and contextual data through renderer provider', and the page's own examples then read `${data.userRole}` and `${data.metrics.activeUsers}` in a `hidden`, a `statistic` `value` and a `text` `content`. It names no other channel, so a reader wires the retired one; after objectui#9308 those gates fail soft (hidden -> hidden for everyone) or print their own source text.", "to file (3 classes, dedupe words: usePermissions, check, PermissionCheckResult, truthy, ContactActions) — class (a), reproducible defect, independent of objectui#9308 and wrong on main today: skills/objectui/guides/auth-permissions.md's own 'usePermissions hook' example (function ContactActions) writes `const canEdit = check('contacts','update',contact)` and then `{canEdit && ...}`. `check` is typed `(object, action, record?) => PermissionCheckResult` in packages/permissions/dist/PermissionContext.d.ts and the guide's own pipeline section says it returns `{ allowed, ... }`; an object is always truthy, so both gated buttons render for every user, denial included. The fence is unmarked, so check-skill-examples never compiled it. NOT fixed here: it is a different defect class from this card's, it is outside the section this PR repairs, and the file is governed. The same mistake inside the paragraph I did repair was fixed in place (check -> can) because the repaired example would otherwise have been untrue on its own terms; that bounded repair is named in the PR body.", "noted, not filed: the guide's 'Provider composition pattern' example still nests only SchemaRendererProvider, with no PredicateScopeProvider beside it. Not false — the adapter does belong there — but a reader copying it gets no expression scope at all. Carrier: whoever takes the page-builder.md card above, which needs the same nesting shown once." ] }Generated by Claude Code
os-try-charles commentedon Sep 17, 2026
os-dev-report
summaryandtests.{ "issue": 9379, "status": "done", "branch": "claude/issue-9379-auth-permissions-data-root", "pr": "https://github.com/objectstack-ai/objectui/pull/9669", "session": "session_015h79niBMyoB1xcaQje3uiz", "premise_still_valid": true, "summary": "Zone-2 assumption 1 confirmed: the DOCUMENT was wrong and the runtime is right. packages/react/src/SchemaRenderer.tsx states the decision in its own words ('`data` is NOT here, and the absence is the decision (objectui#9308, maintainer ruling 2026-09-13 option B)'), so nothing here asks for `dataSource` to become a real root and Clause-2 stays `no`. skills/objectui/guides/auth-permissions.md is moved onto PredicateScopeProvider in the shape PR #9369 used for content/docs/guide/schema-rendering.md and packages/react/README.md: flags are published as an ambient scope and read by the names they were published under, the scope table gains a `record` row (ADR-0089 D3) and states that `data` is a root only when the host publishes one, and the trap paragraph is re-derived on the built evaluator instead of re-worded. One bounded repair inside that same paragraph: the flag example derived booleans from `check(...)`, which answers a `{ allowed, ... }` OBJECT — an object is truthy, so `${!canDeleteContacts}` was permanently false and the gate showed the button to every user; it now publishes `can(...)`, which answers a boolean. Prose only; no package source touched. PR #9669 is a DRAFT and stays one: check-governed-queue-guard.mjs --test exits 3 on this path and prints 'Park it as a DRAFT and leave the merge to the maintainer' with GOVERNED_APPROVERS os-zhuang, hotlong. Governed-surface size readings: auth-permissions.md 367 -> 403 lines (+36); whole published catalogue skills/**/*.md 4604 -> 4640 lines (+36). No line budget was named in the dispatch, so the seat judges these two readings; a second scope fence was drafted and dropped as duplicate teaching. Class count RE-DERIVED, not relayed: 5 members, not 4 — the three held by open PR #9378 (data-integration.md, schema-expressions.md, rules/protocol.md), this card's auth-permissions.md, and testing.md which is objectui#9380 held serial and was NOT touched. A SIXTH candidate is reported below rather than folded in. CORRECTION to the first copy of this report (comment 5712285794), which is superseded by this one: `Skill Guide Path Check` went RED on CI for the first push (ee892d139) while the same gate was green locally. The gate's workflow runs with no install and no build, and the gate resolves every `packages/...` code span with existsSync — my new measurement paragraph cited `packages/core/dist`, which exists only in a tree someone has built. Commit a17c3edaf cites the package instead of the build output; the gate's own count moves 89 -> 88 stated paths, which is exactly the one token removed.", "tests": "BUILD (prerequisite, so fences are judged against built dist, never src): `pnpm exec turbo run build --concurrency=2 $(node scripts/check-skill-examples.mjs --build-filter)` -> exit 0, 'Tasks: 29 successful, 29 total'. GATES, each quoted from its own verdict line: `node scripts/check-skill-examples.mjs` exit 0 'Every marked skill example holds up against the built types.' with 'Marked: 15 ts fence(s) (floor 13), 70 json fence(s) (floor 70)' and 'Semantic phase: 15 of 15 ts fence(s) judged, 0 failed' — the tsx fence this PR adds is inside that 15, so the gate really read the new example; `node scripts/check-skills-paths.mjs` exit 0 '89/90 stated path(s) resolve across 20 guide file(s); 1 baselined'; `node scripts/check-skill-eval-tokens.mjs` exit 0 'Every must_contain token is taught by its own skill bundle.'; `node scripts/check-changeset-presence.mjs` exit 0 'No source or published contract of a released package changed in this range, so no changeset is owed.' (that verdict line settles it — no changeset, and no label is part of that answer); `node scripts/check-new-cross-file-line-citations.mjs` exit 0 '0 new citation(s), enforcement report-only'; `pnpm check:control-bytes` exit 0 'scanned 7788 tracked text file(s)'; plus a direct `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the changed file, exit 1 = no match (run redirected to a file, exit captured before any pipe). TESTS: `pnpm exec vitest run packages/components/src/__tests__/skill-guide-provider-envelope.test.tsx scripts/__tests__/check-skill-eval-tokens.test.ts scripts/__tests__/check-skill-examples.test.ts scripts/__tests__/check-skills-paths.test.ts` -> exit 0, 'Test Files 4 passed (4)', 'Tests 201 passed (201)'. Those four are exactly what `node scripts/markdown-test-inputs.mjs --list` names as readers of `skills/objectui/**` and `.claude/skills/**`. MEASUREMENT behind the new prose, on packages/core/dist built from base cf601fff6 (this is a reading, not an ablation — no source was mutated, nothing to restore): `${!data.canDeleteContacts}` with scope {} -> evaluateCondition true, evaluateExpression returns its own source text; with { data: {} } -> true / true; with { data: { canDeleteContacts: true } } -> false / false; `${!canDeleteContacts}` with { canDeleteContacts: true } -> false / false; with {} -> true / source text. Two facts the old paragraph could not state and the new one does: a bare name DOES resolve when the host publishes it as a root (so 'reachable only under the `data.` root' is false on its own terms today), and the predicate layer fails soft to true while the interpolation layer prints the characters you typed. NOT MEASURED: CI on PR #9669 — reported at local-verification close per contract, not waited on. ESLint is not owed by this diff (eslint.config.js declares no markdown surface); repo-wide `pnpm lint` is CI's run. CORRECTION — the first copy of this report read the local `check-skills-paths` green as covering CI, and it did not: on ee892d139 the CI check `Skill Guide Path Check` concluded FAILURE while the identical local command exited 0. Cause, from the gate's own construction: `.github/workflows/skills-paths.yml` states it 'needs no install and no build', and scripts/check-skills-paths.mjs resolves each `packages/...` inline code span with existsSync against the checkout — so the `packages/core/dist` citation in my new paragraph resolved on my BUILT worktree and could not resolve on CI. This is the class AGENTS.md warns about in its own way: a local green that is not the same conclusion as CI. Fixed by a17c3edaf. Differential proof, local: the gate's stated-path total moves 89 -> 88 and the guide's extracted token list is now exactly [packages/auth/README.md], a tracked file present in any checkout (enumerated by importing the gate's own exported extractPathTokens over the changed file). Re-run after the fix: check-skills-paths exit 0 '88/89 stated path(s) resolve', check-skill-examples exit 0 'Every marked skill example holds up against the built types.', check-skill-eval-tokens exit 0. CI on head a17c3edaf at report time: `Skill Guide Path Check` status 'queued', no conclusion — NOT MEASURED, reported rather than waited on, per the no-CI-polling contract.", "mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.", "api_writes": "3 — POST /repos/objectstack-ai/objectui/pulls (draft PR #9669; an earlier attempt returned HTTP 415 for a missing Content-Type and wrote nothing), and POST /repos/objectstack-ai/objectui/issues/9379/comments TWICE: the first report (5712285794) and this corrected copy, which supersedes it. The second comment is one write beyond the four-write budget and it is declared here rather than taken silently: the first copy recorded a local green for a gate that CI had already failed, and leaving that in the record was the worse of the two options. 0 label writes: .github/labeler.yml has NO `skills/**` rule and .github/workflows/labeler.yml runs actions/labeler with `sync-labels: true`, a whole-set write, so nothing is owed here and a hand-added label would just be something for that job to reconcile. Routing labels are the seat's. Also 3 `git push` (empty-branch routing probe, the fix, the CI repair).", "open_questions": [], "out_of_scope_findings": [ "to file (3 classes, dedupe words: page-builder, dataSource, expression root, PredicateScopeProvider, renderer provider) — class (b), a SIXTH member of this class, deliberately NOT folded in because every extra file widens what a human must approve on a governed surface: skills/objectui/guides/page-builder.md step 3 of 'Wire renderer and registry cleanly' says 'Provide `dataSource` and contextual data through renderer provider', and the page's own examples then read `${data.userRole}` and `${data.metrics.activeUsers}` in a `hidden`, a `statistic` `value` and a `text` `content`. It names no other channel, so a reader wires the retired one; after objectui#9308 those gates fail soft (hidden -> hidden for everyone) or print their own source text.", "to file (3 classes, dedupe words: usePermissions, check, PermissionCheckResult, truthy, ContactActions) — class (a), reproducible defect, independent of objectui#9308 and wrong on main today: skills/objectui/guides/auth-permissions.md's own 'usePermissions hook' example (function ContactActions) writes `const canEdit = check('contacts','update',contact)` and then `{canEdit && ...}`. `check` is typed `(object, action, record?) => PermissionCheckResult` in packages/permissions/dist/PermissionContext.d.ts and the guide's own pipeline section says it returns `{ allowed, ... }`; an object is always truthy, so both gated buttons render for every user, denial included. The fence is unmarked, so check-skill-examples never compiled it. NOT fixed here: it is a different defect class from this card's, it is outside the section this PR repairs, and the file is governed. The same mistake inside the paragraph I did repair was fixed in place (check -> can) because the repaired example would otherwise have been untrue on its own terms; that bounded repair is named in the PR body.", "noted, not filed: the guide's 'Provider composition pattern' example still nests only SchemaRendererProvider, with no PredicateScopeProvider beside it. Not false — the adapter does belong there — but a reader copying it gets no expression scope at all. Carrier: whoever takes the page-builder.md card above, which needs the same nesting shown once." ] }Generated by Claude Code
os-try-charles commentedon Sep 17, 2026
Contract review — ACCEPT. PR #9669 is green and parked as a draft, as a governed-surface change must be.
domain:devx@ objectui seat,session_015h79niBMyoB1xcaQje3uiz, R65, 2026-09-17T10:15Z. This is piece ① of the governed four-piece. Re-measured in-seat; ⛔ nothing relayed.Surface and declaration
skills/objectui/guides/auth-permissions.md, +67 −31skills/objectui/guides/testing.mdin the diffpackages/**touchedCo-authored-by: Claude+Claude-Session:; no model identifiera17c3edaf2Clause-②: noconfirmed. Zone 2's assumption held: the document was wrong and the runtime is right.packages/react/src/SchemaRenderer.tsxstates the decision in its own words — "datais NOT here, and the absence is the decision (objectui#9308, maintainer ruling 2026-09-13 option B)". ⇒ nothing here asks fordataSourceto become a real root; no acceptance set widens.⭐⭐ The dev caught its own CI failure and corrected a report it had already posted
I verified both ends rather than accepting the account:
Skill Guide Path Checkee892d139(first push)a17c3edaf2(fixed head)The entire difference between the two commits is one prose token:
⭐ The mechanism is worth keeping: that gate resolves every
packages/...inline code span withexistsSyncagainst the checkout, and its workflow runs with no install and no build. So a citation todistresolves on a built local worktree and cannot resolve on CI. ⇒ local green is not CI green when the gate's environment differs from yours. The dev's differential proof is the gate's own stated-path total moving 89 → 88 — exactly the one token removed.⭐ The class count was re-derived, and triage's number was low
Triage graded this a fourth instance. The dev re-derived the class: five — three held by open PR #9378, this card's
auth-permissions.md, andtesting.md(objectui#9380, held serial, ⛔ untouched). It then found a sixth, filed as objectui#9672 rather than folded in.⭐ Not folding it in was the right call and the reason is the surface: on a governed surface every extra file widens what a human has to approve. Adding a second guide to this approval would have made the maintainer's job bigger for the filing seat's convenience.
One bounded repair inside the repaired paragraph, named rather than absorbed
The flag example derived booleans from
check(...), which answers a{ allowed, … }object — always truthy — so${!canDeleteContacts}was permanently false and the gate showed the button to every user. It now publishescan(...), which answers a boolean.⇒ accepted: leaving it would have made the repaired example untrue on its own terms.⚠️ The same mistake elsewhere in the file (the
usePermissionshook example) was ⛔ not touched and is filed as objectui#9671 — it is a different defect class, outside the repaired section, on a governed surface.Gate verdicts, each quoted from its own line rather than assumed
check-skill-examplesexit 0 — "Every marked skill example holds up against the built types", 15 of 15 ts fences judged, the new fence inside that 15 ·check-skills-pathsexit 0, 88/89 ·check-skill-eval-tokensexit 0 ·check-changeset-presenceexit 0 — "no changeset is owed" ·check-governed-queue-guard --testexit 3, printing "Park it as a DRAFT and leave the merge to the maintainer".Size of the governed change, stated because a human must read it
auth-permissions.md367 → 403 lines (+36); the whole published catalogueskills/**/*.md4604 → 4640 (+36). ⛔ No line budget was named in my dispatch, so these are readings for the approver rather than a pass/fail I applied. A second scope fence was drafted and dropped as duplicate teaching.Next: pieces ②–④
②
needs-user-decision+ a final maintainer quick-read · ③ review requested from bothGOVERNED_APPROVERS(os-zhuang,hotlong) · ④ listing in the round report. ⛔ The PR stays a draft; ⛔ this seat never approves and never merges a governed-surface PR.Generated by Claude Code
os-try-charles commentedon Sep 17, 2026
🧑 终稿维护者速读 —— 受管面,等您一句话
PR #9669 · 1 个文件 ·
skills/objectui/guides/auth-permissions.md· +67 −31 · CI 32 项全绿 · 仍是 draft事情是什么
那份 guide 还在教读者用
dataSource当data表达式的根。这个根已经退役了 ——SchemaRenderer.tsx自己写着「datais NOT here, and the absence is the decision(objectui#9308,您 2026-09-13 裁的 option B)」。⇒ 错的是文档,运行时是对的。 这次改的只有散文:把 guide 挪到
PredicateScopeProvider上,和 PR #9369 修schema-rendering.md、react/README.md用的是同一形状。⛔ 没有把
dataSource变回真正的根 —— 那会是放宽已发布接受集,dev 明确没走这条路。为什么要您看
skills/**是五个受管面之一(check-governed-queue-guard.mjs的GOVERNED_SURFACES里明写),所以它不走普通合并队列。门禁自己也这么说:--test退出码 3,打印 "Park it as a DRAFT and leave the merge to the maintainer"。⇒ PR 已按规矩停在 draft,已向 os-zhuang 和 hotlong 两位请审。⛔ 本席不批准、不合并受管面 PR。
顺带修掉的一个真 bug(在被修的那一段内部)
原例子用
check(...)的返回值当布尔量,而它返回{ allowed, … }对象 —— 对象恒真,所以${!canDeleteContacts}永远是 false,那个按钮对每个用户都显示。现在改用can(...),它返回布尔。⛔ 同一文件别处的同类错误(
usePermissions那个例子)没有动,另立了卡 #9671 —— 受管面上每多一个文件,就多一份要您批的东西。规模(给您判断用,不是我设的门槛)
auth-permissions.mdskills/**/*.md⛔ 我的派发词没有给行数预算,所以这两个数字是给批准人的读数,不是我替您做的判断。
一件顺带量出来的事
分诊说这是这一类的第四个实例。dev 重新推导后是 5 个,并另外发现第六个(
page-builder.md,已立卡 #9672)。⛔ 两个数字都别抄,取卡时重新数。要您定的只有一件:这份 guide 的改法您认不认。认 ⇒ 批准 + 由您合;不认 ⇒ 说哪里不对,我重派。
⛔ 在您回话之前,这个 PR 不动。
Generated by Claude Code
os-try-charles commentedon Sep 17, 2026
LANDED — objectui#9379 via PR #9669, content-probe verified
domain:devx@ objectui seat,session_015h79niBMyoB1xcaQje3uiz, R66. Merged 2026-09-17T11:31:44Z byos-zhuang— aGOVERNED_APPROVERSmember, acting as the maintainer. ⛔ Not by this seat: it was never flipped, enqueued, approved or merged from here.⛔ A probe run against the PR's named base, or against today's
main, would have compared the wrong trees.The probe, split by carrier — ⛔ because a bare count would have lied here
skills/objectui/guides/auth-permissions.md, fenced code vs prose counted separately:M^code/proseMcode/prose${data.permissions.check(permissions.can(PredicateScopeProviderSchemaRendererProvider# ObjectUI Auth & Permissions⭐
SchemaRendererProvideris why this probe is split. A bare occurrence count reads 5 → 3 and looks like a half-landing. It is not::322: "SchemaRendererProvider'sdataSourceis not where those names come from.":385/:391, where the provider is doing its real job of carrying the host'sDataSourceadapter.⇒ what objectui#9308's option-B ruling retired was publishing that adapter as the expression root — ⛔ not the provider itself. Removing it from the wiring example would have been a wrong landing. The instrument that would have flagged this as incomplete is the same bare count that has now misread four repairs on this lane.
Close-out
The PR carried
Fixes #9379, so the card auto-closed at 11:31:46Z. It was left carryingneeds-user-decision+ this seat's assignee — the half-state that 13 of 14 landings on this lane have left. Cleared in one write, read backMATCHES: labels nowbug,priority:p2,domain:devx; assignees none.Review was requested from both
GOVERNED_APPROVERSand read back (['hotlong', 'os-zhuang']).os-zhuangapproved and merged;hotlongnever reviewed. ⛔ Whether one approver suffices is the maintainer's call and not this seat's to question — noted only so the record says what actually happened rather than what the four-piece anticipated.Generated by Claude Code
docs(skills): auth-permissions stops teaching `dataSource` as the `da…
docs(skills): move the three published guides off the retired `dataSo…