Commit 0f9245c
docs(skills): pages.md rule 2 states the enforceable reason, not the retired ADR-0048 claim (#19445)
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 both `23f1de0` and this head `127576f`). 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):
```text
2. **Namespace-prefixed filename.** The filename stem becomes the doc
`name` (`^[a-z][a-z0-9_]*$`) and must start with the package namespace
(`crm_…`). 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).
```
After (`127576f`, :337–:341):
```text
2. **Namespace-prefixed filename.** The filename stem becomes the doc
`name` (`^[a-z][a-z0-9_]*$`) and must start with the package namespace
(`crm_…`). `os build` refuses a bare `user_guide`
(`docs/namespace-prefix`) — hygiene, not uniqueness: two packages
coexist on one bare name (ADR-0048 §3.4).
```
## The three false clauses, re-measured on this tree, and the source
line beside each new clause
| # | the old text said | measured refutation (all at `23f1de0`,
unchanged at `127576f`) | the new clause it becomes |
|---|---|---|---|
| 1 | doc names share "one flat, instance-global space with the URL" |
`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` :3452 `const storageKey = packageId
? withDisc(`${packageId}:${baseName}`) : bareKey;` — the storage key is
composite `PACKAGE_ID:NAME`, not one flat space | dropped; replaced by
"two packages coexist on one bare name" |
| 2 | a bare name "would collide across packages" | ADR-0048 :251–:252
「The cross-package **throw is retired**; two distinct packages coexist
on the same bare name by construction」 (§3.4); `registry.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 overlay `console.warn` |
"hygiene, not uniqueness: two packages coexist on one bare name
(ADR-0048 §3.4)" |
| 3 | it "would fail at install" | BUILD door:
`packages/cli/src/utils/collect-docs.ts` :654–:658 `if (namespace &&
!doc.name.startsWith(`${namespace}_`))` pushes `severity: 'error'`,
`rule: 'docs/namespace-prefix'`; `packages/cli/src/commands/compile.ts`
:802–:811 `if (docErrors.length > 0) { … this.exit(1); }`. INSTALL door:
`registry.ts` :4155–:4168 refuses on `manifest.namespace` ownership and
:1456 `constructor(namespace: string, existingPackageId: string,
incomingPackageId: string)` — `NamespaceConflictError` is built from a
namespace, never a doc name | "`os build` refuses a bare `user_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
bare `playbook` → one `docs/namespace-prefix` at `severity '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)`.
- **实际业务需求** — the reader is an AI author of package docs in a customer
project (`skills/**` ships via `npx 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.
- **项目长远合理性** — spec/ADR > implementation > docs. ADR-0048 §3.4 itself
classifies this lint as authoring hygiene (:258–:259), and the source
header (`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.
- **防 AI 写代码/元数据犯错** — A names the door (`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.
- **创业阶段不扩散需求** — both are net 0 lines; A costs +2 ratchet units (see
below), B would shrink. No new capability, no new surface, immediate
correction, no transition text.
Decision: A. The cost is stated honestly: this file's token headroom
goes from 2 to 0, so the next edit to `pages.md` pays 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.md`
with nowhere to carry a package coordinate (`collect-docs.ts` :45–:54
and the check at :737 `if (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)
- Gate: `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]`.
- File, tokens: before 21996 bytes → 5499 units (headroom 2); after
22003 bytes → **5501 units (headroom 0)**. Gate line: `✓
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.
- File, lines: before 448 → after 448 (net 0; the claim's budget was net
≤ +2).
- Package, tokens: bundle total after 140368 (gate output); before
140366 — the unit is per-file additive and only this file changed (+2).
- Package, lines (sum over all `skills/**/SKILL.md`, 10 files): before
6145 → after 6145. Sum over `skills/objectstack-ui/**`: 2155 → 2155.
- ⛔ `pnpm check:pm-skill-ratchet` is the `.claude/**` LINE ratchet and
deliberately excludes the published `skills/` 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 `--ran`
Derived by `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths; change set from merge base
`23f1de0`, 1 path, +3/−3, under the 5000 human-merge threshold).
Reconcile: `Run reconciliation — 23 derived, 23 run, 0 NOT-MEASURED, 0
UNRUN.` (exit 0).
| command | exit |
|---|---|
| `node scripts/check-ci-filter-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `node scripts/check-doc-route-spelling.mjs --advisory` | 0 |
| `node scripts/check-doc-route-spelling.mjs --self-test` | 0 |
| `node scripts/check-skills-token-ratchet.mjs` | 0 |
| `node scripts/check-skills-token-ratchet.mjs --self-test` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
first run 3 (PREREQUISITE NOT MET — `dist/` of `@objectstack/formula` /
`@objectstack/lint` absent in a fresh worktree; NOT MEASURED); after
`pnpm exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint --concurrency=2` under `os-verify-lock.sh`
(`VERDICT command-exit 0`, held 390s): rerun **0** |
| `pnpm check:agent-test-spelling` | 0 |
| `pnpm check:corpus-claim-drift` | 0 |
| `pnpm check:cross-package-test-inputs` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:gitlink-declared` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:pm-governed-merges` | 0 |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:role-word` | 0 |
| `pnpm check:skill-compatibility` | 0 |
| `pnpm check:skill-frame-sync` | 0 |
| `pnpm check:skill-identifier-liveness` | 0 |
| `pnpm check:watch-hint-literal` | 0 |
Outside the derived union, run as the dispatch ordered: `pnpm
check:pm-dispatch-gates` launched detached with its exit captured to a
file; its verdict is reported in the `os-dev-report` comment 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), and
`packages/cli` / `packages/objectql` are cited, not edited. The `##
Contract review` for this Tier H head is the seat's; `node
scripts/pm/check-governed-merges.mjs --pr` on this PR is read after
creation and goes into the report comment.
## PM mechanism assumptions, verified
1. Open PRs touching `skills/objectstack-ui/**`: **0 of 21** open PRs
(read via REST `/pulls?state=open` + `/pulls/{n}/files`). Two open PRs
touch `skills/` at all, neither in this surface: #19378
(`skills/objectstack-platform/SKILL.md`) and #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.
2. `check-governed-merges.mjs --pr` → read after creation; expected
GOVERNED / Tier H. Reported in the report comment.
3. The published-skill token ratchet
(`scripts/check-skills-token-ratchet.mjs`) and
`check:skill-identifier-liveness` are both in the derived union and both
green on this diff. Holds, with the ratchet's name pinned — it has no
`pnpm check:*` alias and is not `check:pm-skill-ratchet`.
## 维护者速读(草稿)
- **改了什么**:对外发布的 `objectstack-ui` skill 里「页面文档」编写规则第 2
条,只改「为什么要加命名空间前缀」那一句;规则本身(文件名必须以包命名空间开头)与 ADR-0048 引用都保留。
- **为什么改**:原句说「裸名字会跨包冲突、安装时失败,依据 ADR-0048」——而 ADR-0048 §3.4
恰恰宣布跨包冲突这一说法已退役(两个包可以同时用同一个裸名字,按包 id 各自解析),真正的拒绝发生在 `os
build`(`docs/namespace-prefix`),不在安装。这份 skill 是客户项目里 AI
写文档时读的权威,错误的理由会被原样抄进客户文档。
- **风险与代价(含回滚)**:纯文案,零代码、零发布产物、零 changeset;净 0 行;该文件的 token 上限余量从 2 变
0(下一次改这个文件须删字付账)。回滚 = revert 这一个 commit。
- **席位意见**:(留空,席位定稿)
- **你要做的**:Tier H,本 PR 保持 draft;席位完成 `## Contract review` 与 ACCEPT
后,由你点合并。
## Acceptance notes
- noted, not filed: `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 touches `registry.ts` for this; not
scanned further).
- noted, not filed: `content/docs/ui/doc-pages.mdx` :76–:77 calls
`docs/namespace-prefix` "the same `namespace-prefix` lint the platform
applies to other named metadata"; in `os lint` the
`naming/namespace-prefix` rule (`packages/cli/src/commands/lint.ts`
:313–:321) is a *warning* on a name declared twice within one package,
while `docs/namespace-prefix` is 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.
- The claim's serial reading "0 of 20 open PRs" is 0 of 21 on this act,
with #19373 as a second `skills/`-touching PR outside this surface —
recorded above, no action.
- ADR-0046 §3.2 (the origin of the retired framing) is #19408's,
untouched here. #19408 is not addressed here.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 60152df commit 0f9245c
1 file changed
Lines changed: 3 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
339 | | - | |
340 | | - | |
341 | | - | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
342 | 342 | | |
343 | 343 | | |
344 | 344 | | |
| |||
0 commit comments