docs(skills): pages.md rule 2 states the enforceable reason, not the retired ADR-0048 claim - #19445
Conversation
…retired ADR-0048 claim Authoring rule 2 of skills/objectstack-ui/rules/pages.md told an AI author that doc names "share one flat, instance-global space with the URL, so a bare `user_guide` would collide across packages and fail at install (ADR-0048)". Each clause is refuted by the source it cites: - routing keys on the package id and packaged items are stored under the composite `<packageId>:<name>` key (ADR-0048 §3.1/§3.3; packages/objectql/src/registry.ts storageKey line); - the cross-package throw is retired — two distinct packages coexist on the same bare name by construction (ADR-0048 §3.4); - a bare doc name is refused at BUILD by `docs/namespace-prefix` (packages/cli/src/utils/collect-docs.ts, severity error; `os build` exits 1 on doc errors), not at install — the install gate refuses on `manifest.namespace` ownership and `NamespaceConflictError` is constructed with a namespace, not a doc name. The rule (the stem must start with the package namespace) and the ADR citation stay; the REASON now names where the refusal happens and what the prefix is for. Net 0 lines; published-skills token ratchet 5499 -> 5501 of 5501 (paid within the same span). Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgments
② Semver levelNone — a published skill's prose, no package version; ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #19358
Clause-②: no
Seat
domain:skills#2(seat post #19287), dispatched from the claim on #19358. Tier H:skills/**— this PR stays DRAFT; the seat reviews and ACCEPTs, the maintainer's own click lands it.What changed
skills/objectstack-ui/rules/pages.md, authoring rule 2 only (:337–:341 at both23f1de0and this head127576f). The rule (the stem must start with the package namespace) and the ADR-0048 citation stay; the REASON sentence is replaced with the facts the source enforces. Net 0 lines (+3 / −3), one file.Before (
23f1de0, :337–:341):After (
127576f, :337–:341):The three false clauses, re-measured on this tree, and the source line beside each new clause
23f1de0, unchanged at127576f)docs/adr/0048-cross-package-metadata-collision.md:182–:183 「The route/container coordinate for an installed package's UI is its package id」 (§3.1); :222–:223 「resolves a bare name within the current package first, keyed on the package id」 (§3.3);packages/objectql/src/registry.ts:3452const storageKey = packageId ? withDisc(${packageId}:${baseName}) : bareKey;— the storage key is compositePACKAGE_ID:NAME, not one flat spaceregistry.ts:3472–:3477 「ADR-0048 §3.4 — the per-item CROSS-package throw is retired … two installed packages shipping the same bare name (e.g.page/home) legitimately COEXIST under distinct composite keys」 — and no throw exists in :3458–:3511, only the ADR-0005 overlayconsole.warnpackages/cli/src/utils/collect-docs.ts:654–:658if (namespace && !doc.name.startsWith(${namespace}_))pushesseverity: 'error',rule: 'docs/namespace-prefix';packages/cli/src/commands/compile.ts:802–:811if (docErrors.length > 0) { … this.exit(1); }. INSTALL door:registry.ts:4155–:4168 refuses onmanifest.namespaceownership and :1456constructor(namespace: string, existingPackageId: string, incomingPackageId: string)—NamespaceConflictErroris built from a namespace, never a doc nameos buildrefuses a bareuser_guide(docs/namespace-prefix)"Reference wording the dispatch pointed at, unchanged and consistent with the new sentence:
content/docs/ui/doc-pages.mdx:68–:69 「The build lint still requires every doc name to be namespace-prefixed, as a same-package authoring-hygiene rule」 and :77–:82 「It is no longer load-bearing for uniqueness … two installed packages may each ship a doc with the same bare name and coexist」. The build-check pin:packages/cli/src/utils/collect-docs.package-docs.test.ts:420–:424 (a bareplaybook→ onedocs/namespace-prefixatseverity 'error').Restate vs drop — the card's "First act", decided on the four axes
Option A (taken): restate — the enforceable fact plus one corrected clause naming where the refusal happens and what the prefix is not for, with the ADR section. Option B: drop to the bare enforceable fact ("must start with the package namespace; enforced by
os build") and a bare(ADR-0048).skills/**ships vianpx skills add/npm create objectstack). Both options let it comply. What the old sentence did in practice was get copied as a reason into the author's own docs; B leaves the "why" blank, and a blank "why" beside an ADR number is the shape an author fills with the retired claim. A gives the one fact that closes it. Neither option adds any capability.collect-docs.ts:32–:54) says the same. A keeps the skill's sentence tied to the ADR clause it cites (§3.4), so a future change to that clause has one sentence to update; B cites a document that then says nothing about the sentence.os build) and the rule id (docs/namespace-prefix), so an author hitting the refusal can find it, and states the negative ("not uniqueness: two packages coexist on one bare name") that stops an AI from re-deriving the retired collision claim or designing around a constraint that does not exist (e.g. inventing cross-package-unique names). B is silent on exactly the claim this card is about.Decision: A. The cost is stated honestly: this file's token headroom goes from 2 to 0, so the next edit to
pages.mdpays by deletion.Also considered and not written, for the byte budget: the second true reason the source states — the prefix is what separates a same-package link from a cross-package one, because a doc link is a bare
./NAME.mdwith nowhere to carry a package coordinate (collect-docs.ts:45–:54 and the check at :737if (namespace && !target.startsWith(${namespace}_)) continue;). It is measured and true; the sentence had room for one clause, and the §3.4 framing is the direct refutation of the retired claim. "authoring hygiene" was trimmed to "hygiene" for the same reason: the full spelling lands at 5504 units against a 5501 ceiling.Published-skill ratchet readings (both readings, per os-dev.md)
node scripts/check-skills-token-ratchet.mjs(scripts/check-skills-token-ratchet.mjs, invoked directly in.github/workflows/lint.yml:5527–:5528; unit =ceil(utf8 bytes / 4)). Ceiling row :473['skills/objectstack-ui/rules/pages.md', 5501].✓ check-skills-token-ratchet: skills/objectstack-ui/rules/pages.md is 5501 tokens (ceiling 5501; headroom 0).Paid within the replaced span (148 → 155 bytes); ceiling row untouched.skills/**/SKILL.md, 10 files): before 6145 → after 6145. Sum overskills/objectstack-ui/**: 2155 → 2155.pnpm check:pm-skill-ratchetis the.claude/**LINE ratchet and deliberately excludes the publishedskills/root (its header says so); it is not the gate this file answers to.Gates — derived union, every exit captured before any pipe, reconciled with
--ranDerived by
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; change set from merge base23f1de0, 1 path, +3/−3, under the 5000 human-merge threshold). Reconcile:Run reconciliation — 23 derived, 23 run, 0 NOT-MEASURED, 0 UNRUN.(exit 0).node scripts/check-ci-filter-parity.mjsnode scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/check-doc-route-spelling.mjs --advisorynode scripts/check-doc-route-spelling.mjs --self-testnode scripts/check-skills-token-ratchet.mjsnode scripts/check-skills-token-ratchet.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionsdist/of@objectstack/formula/@objectstack/lintabsent in a fresh worktree; NOT MEASURED); afterpnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2underos-verify-lock.sh(VERDICT command-exit 0, held 390s): rerun 0pnpm check:agent-test-spellingpnpm check:corpus-claim-driftpnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:gitlink-declaredpnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:refd-timer-probepnpm check:role-wordpnpm check:skill-compatibilitypnpm check:skill-frame-syncpnpm check:skill-identifier-livenesspnpm check:watch-hint-literalOutside the derived union, run as the dispatch ordered:
pnpm check:pm-dispatch-gateslaunched detached with its exit captured to a file; its verdict is reported in theos-dev-reportcomment on #19358 (it was still in its self-test when this body was written). No package build or test is owed: the diff touches no package (① empty), andpackages/cli/packages/objectqlare cited, not edited. The## Contract reviewfor this Tier H head is the seat's;node scripts/pm/check-governed-merges.mjs --pron this PR is read after creation and goes into the report comment.PM mechanism assumptions, verified
skills/objectstack-ui/**: 0 of 21 open PRs (read via REST/pulls?state=open+/pulls/{n}/files). Two open PRs touchskills/at all, neither in this surface: fix(skills): enumerate every authorable top-level stack key in the platform skill, and gate it #19378 (skills/objectstack-platform/SKILL.md) and fix(spec,objectql): declare the inert-JSON artifact and registry-record package body stages, and stop the record under-reporting functions #19373 (skills/objectstack-platform/references/_index.md) — the claim named only the first; the second is a refinement of the reading, not a conflict. Holds.check-governed-merges.mjs --pr→ read after creation; expected GOVERNED / Tier H. Reported in the report comment.scripts/check-skills-token-ratchet.mjs) andcheck:skill-identifier-livenessare both in the derived union and both green on this diff. Holds, with the ratchet's name pinned — it has nopnpm check:*alias and is notcheck:pm-skill-ratchet.维护者速读(草稿)
objectstack-uiskill 里「页面文档」编写规则第 2 条,只改「为什么要加命名空间前缀」那一句;规则本身(文件名必须以包命名空间开头)与 ADR-0048 引用都保留。os build(docs/namespace-prefix),不在安装。这份 skill 是客户项目里 AI 写文档时读的权威,错误的理由会被原样抄进客户文档。## Contract review与 ACCEPT 后,由你点合并。Acceptance notes
packages/objectql/src/registry.ts:3458–:3463 still opens with the pre-§3.4 framing ("refuse it loudly if a DIFFERENT code package already owns the same (type, name)") immediately above the :3472 retirement note — a stale lead-in comment, no behaviour behind it (no throw in :3458–:3511). Observation, outside the three filing classes. 承接者: none known (no open PR touchesregistry.tsfor this; not scanned further).content/docs/ui/doc-pages.mdx:76–:77 callsdocs/namespace-prefix"the samenamespace-prefixlint the platform applies to other named metadata"; inos lintthenaming/namespace-prefixrule (packages/cli/src/commands/lint.ts:313–:321) is a warning on a name declared twice within one package, whiledocs/namespace-prefixis an error on a missing prefix — same name stem, different predicate and severity. Docs imprecision, not a false claim about behaviour; out of this card's surface (the dispatch marked that page read-only). 承接者: none.skills/-touching PR outside this surface — recorded above, no action.Generated by Claude Code