docs(skills): guard both useAuth members in the auth-permissions example - #9374
Conversation
The `useAuth` fence under "useAuth hook" read `user.name` behind an `isAuthenticated`-only guard. `AuthProvider` hardcodes `isAuthenticated` to `true` in guest mode and in preview mode while `user` stays `null`, so the example threw a TypeError in exactly the two modes a reader without an auth backend is in. Guard both members the way the shipped `UserMenu` does, and add one sentence beside the fence saying why a signed-in-looking context can still carry no user. The fence stays unmarked: it is a fragment by construction (three undeclared placeholders), and marking it would red the gate for reasons that are not this defect. Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewHead: ① derived judgments: the diff makes ONE falsifiable claim on a published skill — in guest mode and preview mode Implemented-by: claude/issue-9350-useauth-guard-both (mode:subagent) Verdict: PASS. Governed surface (published Generated by Claude Code |
维护者速读改了什么 —— 发布技能 为什么改 —— 照抄这个示例的读者在没有后端的 guest / preview 模式下会直接 风险与代价(含回滚) —— 纯文档面,不动任何包;回滚 = revert 单文件 3 行 + 删 changeset。 席位意见 —— 收。本席在 head 你要做的(一个动作) —— 受管面(发布 Generated by Claude Code |
维护者速读(补记:检查全部跑完)读数 —— head 那一红不是本 PR 的 —— 同名检查在 你要做的(一个动作) —— 你已 approve、翻 ready、开过又关了 auto-merge;若关它是因为那一红,现在可以重新挂 auto-merge(队列只看必过集)或直接合;本席不代你挂。 Generated by Claude Code |
…urce` expression root (objectstack-ai#9378) Fixes objectstack-ai#9370 ⛔ **GOVERNED SURFACE** (`skills/**`, `GOVERNED_SURFACES` `skills-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 objectstack-ai#9352 / card objectstack-ai#9311, merged as `28be0786d`.⚠️ **Merge after objectstack-ai#9369, and this PR states which world it measured in.** Re-derived from my own tree at base `69aa9c017`, not assumed: `SchemaRenderer.tsx` still carries the `data: dataSource` binding, and `SchemaRendererContext.tsx` still reads `const dataSource = context?.dataSource` inside `useDataScope`. **Both halves are still LIVE on this base** — objectstack-ai#9369 is open, not merged. So these pages become true the moment objectstack-ai#9369 lands and are ahead of the code until then. Every evaluator measurement below is from that same pre-objectstack-ai#9369 tree, which is the right place to take it: the evaluator itself is what objectstack-ai#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: `SchemaRenderer` stops publishing the injected `DataSource` adapter as the expression root `data` (b1), and `useDataScope` — what a node's `bind` resolves through — reads the ambient scope a host publishes via `PredicateScopeProvider` instead of walking that adapter (b2). The published skill package still taught both halves. Three files, matching the shape objectstack-ai#9369 used for `content/docs/guide/schema-rendering.md` and `packages/react/README.md`: 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. | file | what changed | |---|---| | `guides/schema-expressions.md` | the scope table, a new COMPILED wiring example, the migration callout, the `bind` resolution prose, the bound-list comment, debugging-checklist item 3 | | `guides/data-integration.md` | the architecture diagram's channel split, "Static data" rewritten onto the scope channel as a COMPILED example, the `useDataScope` resolution sentence, the `data` root claim | | `rules/protocol.md` | both measured tables' WIRING restated, the `bind` comment 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 the `data-table` `bind` pothole (objectui#6575 still rules `data-table` does not read `bind`). ## ⭐ 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 the `true` on a missing root belongs to the **predicate layer**, not to the evaluator: | scope, expression | `evaluateExpression` | `evaluateCondition` | |---|---|---| | CONTROL `{ data: { status: 'draft' } }`, `${data.status}` | `"draft"` | `true` | | CONTROL same scope, `Status: ${data.status}` | `"Status: draft"` | `true` | | CONTROL same scope, `${data.nope}` — present root, absent member | `undefined` | `false` | | `{ data: {} }` — adapter-shaped, `${data.status == 'draft'}` | `false` | `false` | | `{ data: undefined }`, same predicate | `false` | `false` | | `{}` — no `data` root, `${data.status}` | `"${data.status}"` | `true` | | `{}` — no `data` root, `Status: ${data.status}` | `"Status: ${data.status}"` | `true` | | `{}` — no `data` root, `${data.status == 'draft'}` | `"${data.status == 'draft'}"` | `true` | The 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: - **predicate layer** — fails soft to `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. - **interpolation layer** — does not fail soft at all. A `content` built 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 `data` restores the OLD always-`false` verdict rather than fixing the gate, and points row gates at `record` — the runtime-layer root under ADR-0089 D3. ## What I decided about the ~two dozen `${data.…}` examples Measured, not estimated: `schema-expressions.md` carries **21** lines spelling `${data.` (one of them is the census's own scope-table row). Repo-wide under `skills/`: 21 here, 14 in `rules/protocol.md`, 5 in `page-builder.md`, 4 in `testing.md`, 2 each in `data-integration.md` and `auth-permissions.md`, 1 in `architecture.md`, plus 4 in `evals/*.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 objectstack-ai#9369's `content/docs` shape: 1. **A pin renders those exact spellings.** `skill-guide-provider-envelope.test.tsx`, as re-derived in objectstack-ai#9369, publishes `scope = { 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. 2. **ADR-0089 D3 does not forbid the name.** `CANONICAL_ROOT_BY_LAYER` puts `data` at the metadata layer. What was retired is the renderer AUTO-publishing the adapter there, not the name. 3. **Most of those examples are about something else** — which text keys carry expressions, type preservation, the troubleshooting section. The root name is scaffolding, and churning 20 lines of scaffolding on a governed surface buys the reader nothing. ⛔ 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/main` `69aa9c017` — **every cited line still lands on the cited text**: | cited | on `origin/main` `69aa9c017` | verdict | |---|---|---| | 112 | `| Top-level data fields | \`SchemaRendererProvider dataSource\` | …` | exact | | 113 | `| \`data\` | Alias for dataSource root | …` | exact | | 302-303 | `dataSource = { customerNames: … }`, `useDataScope("customerNames")` | exact | | 305 | `**Nested paths work:** … resolves \`dataSource.app.settings.users\`` | exact | | 393 | `// ✅ Bound data, already node-shaped: dataSource = { rows: … }` | exact | `premise_still_valid: true`. ## Enumeration beyond the census (⛔ out of scope for this PR) `--measure` judges 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.md`** carries the same retired claim in its own words — a scope table row `| \`data\` | the \`dataSource\` passed to \`SchemaRendererProvider\` |`, the sentence "Keys of the `dataSource` object are reachable only under the `data.` root", and "With no host scope mounted, `data` and `page` are all you get". ⛔ Out of scope here: **PR objectstack-ai#9374 holds that file**, so an in-place edit would collide. Filed as objectstack-ai#9379. - **`guides/testing.md` Pattern 5** asserts `getByText('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 on `main` today. Filed as objectstack-ai#9380. Noted, not filed: the `${data.*}` examples in `page-builder.md`, `architecture.md`, `i18n.md`, `project-setup.md` and `evals/*.json` are 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: ```text $ node scripts/check-skill-examples.mjs --self-test # BEFORE the build EXIT=2 — PRECONDITION NOT MET: the self-test's type-check leg needs the workspace built $ ...os-verify-lock.sh -c 'turbo run build $(--build-filter) --concurrency=2' VERDICT command-exit 0 · held the lock 44s · waited 0s $ node scripts/check-skill-examples.mjs --self-test # AFTER the build EXIT=0 — ✓ 59 cases pass ``` Gate, verbatim, before and after this diff: ```text BEFORE (exit 0) Scanned 20 guide(s) under skills, .claude/skills: 121 ts/tsx/typescript fence(s), 70 json/jsonc fence(s). Marked: 14 ts fence(s) (floor 13), 70 json fence(s) (floor 70) Semantic phase: 14 of 14 ts fence(s) judged, 0 failed. JSON phase: 70 fence(s) parsed, 0 failed. Every marked skill example holds up against the built types. AFTER (exit 0) Scanned 20 guide(s) under skills, .claude/skills: 122 ts/tsx/typescript fence(s), 70 json/jsonc fence(s). Marked: 16 ts fence(s) (floor 13), 70 json fence(s) (floor 70) Semantic phase: 16 of 16 ts fence(s) judged, 0 failed. JSON phase: 70 fence(s) parsed, 0 failed. Every marked skill example holds up against the built types. ``` ⭐ 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.ts` rather than only read. Derived by hand from `package.json` plus `.github/workflows/` (this repo has no `dispatch-gates.mjs`); every one run on this tree: | gate | exit | |---|---| | `check-skill-examples.mjs --self-test` | 0 | | `check-skill-examples.mjs` | 0 | | `check-skills-paths.mjs` | 0 — 88/89 stated paths resolve, 1 pre-existing baselined | | `check-skill-eval-tokens.mjs --self-test` / `check-skill-eval-tokens.mjs` | 0 / 0 | | `check-control-bytes.mjs` | 0 — 7539 tracked text files | | `check-shell-escape-residue.mjs` | 0 — 16/16 skills documents under a declared root | | `check-new-cross-file-line-citations.mjs` | 0 — 0 new citations | | `check-changeset-presence.mjs` | 0 | | `check-doc-links.mjs` | 0 | | `check-governed-queue-guard.mjs --self-test` | 0 — 185 cases | Package tests that READ these three documents (`markdown-test-inputs.mjs --changed` names 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: | reading | before | after | delta | |---|---|---|---| | `guides/schema-expressions.md` | 569 | 635 | +66 | | `guides/data-integration.md` | 485 | 513 | +28 | | `rules/protocol.md` | 341 | 354 | +13 | | whole published package `skills/**` | 5201 | 5308 | +107 (+2.1%) | 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: 1. `node scripts/check-changeset-presence.mjs` — "Compared the working tree with `69aa9c017` (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) 2. Independently: **0 of 43** workspace manifests mention `skills` in `files` / `exports` / `main` / `module` / `types` / `bin`. Positive control on the same scan: 38 manifests list `dist` in `files[]`. Nothing under `skills/` is shipped by any npm package. 3. The merged precedent on this surface, `28be0786d` (objectstack-ai#9352), changed one `skills/` file and carried no changeset.⚠️ One inconsistency worth a maintainer's eye rather than my guess: open PR objectstack-ai#9374, also a single `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) ```text $ node scripts/check-governed-queue-guard.mjs --test skills/objectui/guides/schema-expressions.md skills/objectui/guides/data-integration.md skills/objectui/rules/protocol.md ⛔ GOVERNED — 3 of 3 path(s) are on a governed surface: skills/** x3 — the published skills catalog - skills/objectui/guides/schema-expressions.md - skills/objectui/guides/data-integration.md - skills/objectui/rules/protocol.md One governed path governs the WHOLE pull request — proportion is not a question. ⛔ Do not flip it ready, enqueue it, or arm auto-merge. Park it as a DRAFT and leave the merge to the maintainer; a human merge IS the review record for a governed surface. The merge-queue run of "Governed Surface Queue Guard" refuses this diff unless an APPROVED review by an authorized approver (GOVERNED_APPROVERS: os-zhuang, hotlong) is on the pull request — on whichever commit it was left (maintainer ruling 2026-09-04). exit 3 ``` ## Clause-② — contract review `needs:contract-review` is hung on this PR in the same stroke as opening it, mirroring the card. Published `skills/**` 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 Analysis` is red on `main` (arrived with objectstack-ai#9316; a maintainer decision). - `Doc Snippet Type Check` is red on `main` until objectstack-ai#9369 lands. This diff touches no document that gate scans (`content/docs` + package READMEs + root `README.md`, explicitly not `skills/**`). ## Acceptance notes - ⛔ No test was skipped, quarantined or weakened. No assertion was loosened. The marked-fence floors both hold and the ts population grew. - The card's "the pins are green while the prose beside them is wrong" warning was taken literally: the six `packages/components` pins objectstack-ai#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`, the `hoists every key onto\s+the node` regex, the absence of the two retired sentences, the `no md matches "instead of \`props.\`"` class guard, and the `"bind": "customerNames"` list example in each of the three guides. - Noted, not filed: `rules/protocol.md`'s two measured tables still cite `origin/main` `f1c27f037` as 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 the `skill-guide-provider-envelope` pin 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 名字的迁移说明会让读者踩正这一脚。 ### 风险与代价(含回滚) - **排序风险(主要):** objectstack-ai#9369 还没合。在它合进去之前,这三页描述的是尚未落地的行为。⚠️ 建议排在 objectstack-ai#9369 之后合。 - 保留了全部 `${data.*}` 示例原文,只让接线示例发布一个叫 `data` 的根 —— 因为 `skill-guide-provider-envelope` 这个 pin 正在渲染这些拼写,改写它们会在另一个 PR 的盲区里挪动 pin 的被测主体。 - 回滚代价:纯文档,`git revert` 一笔即可,不影响任何已发布包(本仓 43 个 manifest 无一发布 `skills/`)。 ### 席位意见 (留空,待 `os-zhuang` / `hotlong` 定稿) ### 你要做的 1. 确认合并顺序:先 objectstack-ai#9369,再本 PR。 2. 作为受管面的人工合并方给出 APPROVED review(这一道是 `check-governed-queue-guard` 机械要求的)。 3. clause-② 的 `needs:contract-review` 需要一份书面复核记录后才清 —— ⛔ 两道保障叠加,人工合并不替代它。 4. 顺带定一句话:本仓单文件 `skills/` 改动到底要不要写空 frontmatter changeset(objectstack-ai#9352 没写、objectstack-ai#9374 写了,门禁两边都放行)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-zhuang <jack@objectstack.ai>
Fixes #9350
What changed
skills/objectui/guides/auth-permissions.md, the fence under "useAuth hook", readuser.namebehind anisAuthenticated-only early return.AuthProviderin@object-ui/authcomputesisAuthenticatedasuser !== null && session !== nullonly when auth is enabled and not in preview mode; in guest mode (enabled: false) and in preview mode it hardcodestruewhileuserstaysnull, so the example threw aTypeErrorin exactly the two modes a reader without an auth backend is in. The shippedUserMenucomponent already guards both members.if (!isAuthenticated || !user) return LOGINBUTTON;where LOGINBUTTON stands for the login-button element the fence returns (JSX tag shapes are spelled in words in this body because the platform strips tag-shaped fragments). The returned element is unchanged.UserMenuas the shipped shape. The prose moved with the example, asAGENTS.mdrequires.Not done, by the grading (comment 5651956190): no
os:checkmarker on this fence. It is a fragment by construction (three undeclared placeholders: the spinner, the login button and the button), and marking it would redcheck:skill-examplesfor reasons that are not this defect. Whether to make it self-contained is a separate question, noted below and not filed.Acceptance, both directions
Measured on this branch at
67cccb0with the gate's own scoped build in place:pnpm exec turbo run build $(node scripts/check-skill-examples.mjs --build-filter) --concurrency=2, 29 of 29 tasks successful, run under the shared verify lock (VERDICT command-exit 0, held 141s, waited 0s).Before = the base (
69aa9c0) content of the guide on that same built tree; After =67cccb0.node scripts/check-skill-examples.mjs --measure,TS18047: 'user' is possibly 'null'rows for this file: 1 (auth-permissions.md:49:20) before, 0 after.TS18047rows across the whole corpus: 1 before, 0 after.[semantic]diagnostic rows across the whole corpus (positive control, not a silent zero): 270 before, 269 after.Semantic phasesummary line, both sides:97 of 121 ts fence(s) judged, 79 failed(the fence still fails on its three undeclared placeholders before and after, so the failed-fence count does not move while the diagnostic-row count drops by exactly one).isAuthenticated || !userin this file: 0 before, 1 after.useAuthin this file (lit control): 5 before, 5 after.signed-in-looking context): 0 before, 1 after.The before column is an ablation leg run after the commit:
git checkout 69aa9c0 -- skills/objectui/guides/auth-permissions.mdon the built tree (on-disk proof: 365 lines, old guard 1, new guard 0, prose 0), then--measure, thengit checkout HEAD -- FILE. Restoration is proven bygit hash-objectequal to theHEADblob (dd4e4633…),git diff HEAD --statempty andgit status --porcelainempty. The very first--measureattempt, before the build, exited 2 (PRECONDITION NOT MET) and is not a reading.Line budget (published
skills/**)skills/objectui/guides/auth-permissions.md: 365 → 367 lines (net +2, ceiling +2; 3 insertions, 1 deletion; no re-wrap).skills/**/SKILL.mdin objectui: 137 → 137 (one file, untouched).pnpm check:skill-eval-tokens, before and after identical: exit 0;Scanned 1 skill bundle(s) under skills: 11 eval file(s), 33 eval(s), 125 must_contain token(s), scored against 16 guide file(s).;Red under the chosen oracle: 0 (0 beyond the baseline).Gates run locally (exit captured before any pipe; verdict lines quoted)
Derived by hand for objectui (there is no
dispatch-gates.mjsin this tree): rootpackage.jsoncheck:*scripts whose source readsskills/, plus the.md-scanning and.changeset-scanning workflow steps.pnpm check:skill-examples— exit 0 —Semantic phase: 14 of 14 ts fence(s) judged, 0 failed./Every marked skill example holds up against the built types.pnpm check:skills-paths— exit 0 —OK (88/89 stated path(s) resolve across 20 guide file(s); 1 baselined).pnpm check:skill-eval-tokens— exit 0 —Every must_contain token is taught by its own skill bundle.pnpm check:doc-fences— exit 0 —every TypeScript block in 227 document(s) is fenced ts/tsx/typescript(rest of the line elided).pnpm check:control-bytes— exit 0 —OK (scanned 7540 tracked text file(s); skipped 85 binary).pnpm check:shell-escape-residue— exit 0 —OK (5/5 root(s) resolved … skills: 16 file(s), 210 fence(s) …).pnpm check:doc-types— exit 0 —Every documented component type is registered.pnpm check:upstream-port-parity— exit 0 —11 ported file(s) match objectstack-ai/objectstack modulo their declared divergences.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.(the empty-frontmatter changeset is declared anyway, per the docs-only convention).check-changeset-claims,check-changeset-no-major,check-changeset-fixed,check-changeset-overwrite— exit 0 each.node scripts/check-new-cross-file-line-citations.mjs— exit 0 —0 new citation(s), enforcement report-only.node scripts/check-governed-queue-guard.mjs --test skills/objectui/guides/auth-permissions.md— exit 3 —GOVERNED — 1 of 1 path(s) are on a governed surface: skills/** x1(the path face, not a failure).pnpm check:doc-snippetsexited 2 (PRECONDITION NOT MET: a different 34-package closure is unbuilt), and its own header states thatskills/objectui/**is not claimed by that gate, so it is not a derived gate for this diff.pnpm lintis the repo-wide run CI owns and this diff has no lintable source.Serial check
No open objectui PR touches
skills/**: re-read at 07:58Z today, the 8 PRs updated after the PM's 06:58Z reading (#9367, #9371, #9369, #9349, #9364, #9368, #9360, #9366) list noskills/file.Acceptance notes (noted, not filed)
useAuthfence self-contained (declare the spinner, login-button and button placeholders) and then mark itos:checkis a separate question by the grading; today it is a fragment by construction, like the other unmarked fences in this file that--measurelists. 承接者:无 — no queued PR or person is on this file.useAuthuse in this file (the markedPermissionProvidersetup fence) readsuser?.positionsanduser ?? undefined; it does not have this defect and was not touched.dataSourcemember indata-integration.md; its PR fix(skills): guard the DataSource read in the marked data-integration example #9352 landed 2026-09-13T06:29Z).维护者速读(草稿)
改了什么:发布给使用者的 skills 指南
skills/objectui/guides/auth-permissions.md里,useAuth示例的早退守卫从只看isAuthenticated改为同时看isAuthenticated和user(与仓内已发布的UserMenu组件一致),并在示例上方加一句说明为什么看起来已登录的上下文仍可能没有 user。净增 2 行;另加一个空 frontmatter 的 changeset 声明不发版。为什么改:
AuthProvider在 guest 模式与 preview 模式下把isAuthenticated硬编码为true,而user仍是null。照抄原示例的读者(尤其是没接后端、正在这两种模式下试用的人,以及按指南写代码的 AI)会在运行时撞TypeError。这是发布示例与已发布组件对同一契约的读法不一致,已发布组件是对的。风险与代价(含回滚):只改文档示例与一句散文,不动任何包源码,不发版;
check:skill-examples等门禁本地全绿。回滚就是 revert 这一个 commit。席位意见:
你要做的:受管面(
skills/**),PR 停在 draft;你确认后合并即可,不需要额外的批准点击。Session:
https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8Generated by Claude Code