Repository navigation
docs(adr): generated README index, one status-line spelling, shared numbers cited by stem - #26503
Conversation
…umbers cited by stem docs/adr/README.md is now generated by scripts/adr-index.mjs from the records themselves: status keyword, number, title, date, supersedes / superseded-by, read from ONE status-line spelling (`**Status**: `). 17 records that spelled the line another way (`- **Status:**`, `## Status`, `- **状态**:`) are normalized to it; nothing else in them changes. The four numbers carried by more than one file (0006 by three versions of one decision; 0010, 0019, 0057 by two unrelated decisions each) are listed side by side in the index and cross-referenced in each record's header. Nothing is renumbered or moved (AGENTS.md Prime Directive 13). check-adr-anchors.mjs becomes unambiguous on those four: a citation may name the record by its filename stem (`ADR-0057-system-data-lifecycle-and-retention`, `ADR-0006-project-environment-split.v4`), which resolves to exactly one record; a stem that names none is an error, a bare number keeps its existing meaning. The 36 stem-qualified citations in the tree all resolve. The index script carries a --self-test (46 assertions, 7 pinned batteries) and is called REPORT-ONLY from the existing lint ADR step via `pnpm adr:index:report`: a stale index or an unreadable record is a warning annotation, never a red (maintainer ruling A: 门禁不加). `pnpm adr:index` regenerates. ui/docs/adr (objectui's own sequence, #26080) is registered in the root index under the `ui-` prefix with its files untouched; a citation keeps the `ObjectUI ADR-NNNN` spelling. docs/adr/PRIORITIZATION.md, self-declared retired, is removed; the one rule it still carried (status hygiene on landing an implementation) lives in the index's Conventions block, and ADR-0110's pointer to it now names the README. Claude-Session: https://claude.ai/code/session_01WGufzgAZv5vcb4H6qvqBAM Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #26076 body and all 7 comments (ruling 6417460375 letter A, 门禁不加; os-dev report 6434816739), PR #26503 body and file list, Check-runs on the headZERO check-runs and zero workflow runs exist on ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL |
…-rgjbqg # Conflicts: # package.json
… alias; versions cite-as; 0071 status on one line Contract-review respin of the ADR index (seat 1, record 6434963378): - scripts/adr-index.mjs --self-test no longer asserts on what the live records say (the one-spelling and ui-parses assertions moved out; they were already --report's warnings). The live-tree battery now proves only that the generator reads and renders the real registry. A record with another status spelling is a warning, never a red — ruling A, 门禁不加. - The lint step is its own, report-only step running the root alias `pnpm check:adr-index` (= --self-test then --report), per the GATE INVOCATION IDIOM that landed in c91544a; the anchors step is back to `pnpm check:adr-anchors` alone. `adr:index` stays the regeneration alias. - Shared numbers: a versions group states that the bare number names the decision and its current text is the highest version; the superseded unversioned file is cited by its stem, never offered as `ADR-0006`. - A `**Decided**:` header line is a date line (ADR-0079 now dates to its decision, 2026-06-28, not its 2026-08-08 reconstruction). - ADR-0071's status paragraph folded onto its one `**Status**:` line. Claude-Session: https://claude.ai/code/session_01WGufzgAZv5vcb4H6qvqBAM Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewVERDICT: PASS — addendum to the FAIL record 6434963378 (head Served-tier: Inputs: card #26076 body and all 7 comments (ruling 6417460375 letter A, 门禁不加; the os-dev report 6434816739 is still the 05:49Z one, written on Check-runs on the headThis head HAS check-runs (the package.json conflict is gone; ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
席位核对 · ACCEPT ·
|
Fixes #26076
Clause-②: no
Metadata-protocol: none
维护者速读
docs/adr/README.md索引(状态、编号、标题、日期、取代/被取代),143 条记录统一为一种状态行写法(**Status**:,改了 17 个文件的那一行,内容不动);四个重号(0006 三个版本,0010/0019/0057 各两条无关决策)在各自文件头互相标注、在索引里并列,不改号不挪文件;check-adr-anchors.mjs支持ADR-0057-system-data-lifecycle-and-retention这种按文件名引用,只指向一条记录;ui/docs/adr以ui-前缀登记进根索引,文件不动;退役的PRIORITIZATION.md删除,其"落地即翻状态"规则并入索引约定。pnpm check:adr-index(= 生成脚本自身的--self-test,只跑 fixture、不断言任何记录的内容,再接永远 exit 0 的--report);记录写错状态行只出 warning,索引过期不判红,不新增必过门禁;回滚 = revert。scripts/check-adr-anchors.mjs新增的resolveQualified/auditQualifiedCitations(裸号语义不变,带 stem 的引用必须指到一条真记录,否则红);2).github/workflows/lint.yml新增的ADR index is current (report-only)步骤与scripts/adr-index.mjs的 Live tree battery(只证明脚本能读真目录,不断言记录);3)docs/adr/README.md的 Shared numbers 节。pnpm adr:index && git diff --quiet docs/adr/README.md && pnpm check:adr-index && pnpm check:adr-anchors—— 预期 README 无 diff,index 打印 "--report: OK",anchors 打印 OK 且含 "37 of them stem-qualified to one record"。docs/adr/**与.github/workflows/**是受管面(Tier H),需 governed approver 审阅后合并;PR 保持 draft,席位不 ready、不排队。What changes
The index —
docs/adr/README.mdis new and generated (node scripts/adr-index.mjs,pnpm adr:index). It carries a visible generated banner, a Conventions block (the one status spelling, the keyword vocabulary, the status-hygiene rule thatPRIORITIZATION.mdused to hold, how shared numbers and the ObjectUI registry are cited), a summary count per status, a Shared numbers section listing each of the four numbers side by side with the id to cite, the records table (number, title, status, date, supersedes, superseded by), and the ObjectUI registry table under theui-prefix.One status-line spelling — the generator reads exactly one line shape,
**Status**: <keyword> …, and lists a record whose line is missing or names no keyword as Unparsed rather than guessing from a heading or a bullet. Today 143/143 root records and 11/11 ui records parse. The 12 files the card's grep found matching none of the four spellings, before normalization:0009-execution-pinned-metadata.md0010-metadata-protection-model.md0014-record-form-field-type.md0091-grant-lifecycle-and-recertification.md0092-sys-user-profile-field-delegation.md0093-tenancy-mode-and-membership-lifecycle.md0098-pinyin-search-companion-column.md- **Status:**(bullet, colon inside the bold)0100-credential-field-channels.md- **Status:**(bullet, colon inside the bold)0103-managedby-write-policy-and-engine-write-guard.md- **Status:**(bullet, colon inside the bold)0104-field-runtime-value-shape-contract.md- **Status:**(bullet, colon inside the bold)0132-multi-organization-runtime-is-open-core.md- **Status:**(bullet, colon inside the bold)0133-org-management-open-basics.md- **Status:**(bullet, colon inside the bold)Five more records had a spelling the card's grep accepted but the one-instrument rule does not (
## Statusheading in 0071;- **状态**:in 0117, 0118, 0121;- **Status**:in 0079). All 17 normalized files, with the spelling each had:0009-execution-pinned-metadata.md0010-metadata-protection-model.md0014-record-form-field-type.md0071-dataset-semantic-layer-depth.md## Statusheading with the value on the next paragraph0079-record-display-name.md- **Status:**(bullet, colon inside the bold)0091-grant-lifecycle-and-recertification.md0092-sys-user-profile-field-delegation.md0093-tenancy-mode-and-membership-lifecycle.md0098-pinyin-search-companion-column.md- **Status:**(bullet, colon inside the bold)0100-credential-field-channels.md- **Status:**(bullet, colon inside the bold)0103-managedby-write-policy-and-engine-write-guard.md- **Status:**(bullet, colon inside the bold)0104-field-runtime-value-shape-contract.md- **Status:**(bullet, colon inside the bold)0117-owning-business-unit-record-stamp.md- **状态**:/ Chinese key0118-non-user-actor-contract.md- **状态**:/ Chinese key0121-declarative-endpoint-routing-namespace-and-channel-split.md- **状态**:/ Chinese key0132-multi-organization-runtime-is-open-core.md- **Status:**(bullet, colon inside the bold)0133-org-management-open-basics.md- **Status:**(bullet, colon inside the bold)Only the status line changed in those files; the value after it is byte-identical.
Shared numbers, cross-referenced and never renumbered (Prime Directive 13) — each of the nine files under 0006/0010/0019/0057 gains one header line directly under its status line. The 0010/0019/0057 pairs get
**Shares its number with**: [sibling](./sibling.md) (title) — an unrelated decision under the same number … cite this record unambiguously as ADR-<stem>; the three 0006 files get**Versions of this number**: v2 · v3 · v4 (current) — one decision revised twice. The index lists all four side by side.check-adr-anchors.mjsis unambiguous on those four — a citation may now be stem-qualified,ADR-NNNN-<slug>[.vN](ADR-0057-system-data-lifecycle-and-retention,ADR-0006-project-environment-split.v4), and such a citation resolves to exactly one record; a stem that names no record is an error (so a typo cannot pass as a bare number), and a bareADR-NNNNkeeps its existing meaning (resolves against the number; the 4 bareADR-0057anchors stay on the existing ambiguity note, unchanged). Decision-letter citations on a stem-qualified id are checked against that one record's decisions. The grammar is the ADR filename grammar, so nothing new is invented. A new battery, Stem-qualified citations name ONE record (#26076), pins 21 cases; the battery floor moves from 6 to 7. 36 stem-qualified citations exist in the tree after this PR (the nine new header lines, the README, and the scripts' own literals) and all resolve.Report-only in CI — a new lint step, ADR index is current (report-only — warnings, never a red on a record), runs the root alias
pnpm check:adr-index(=node scripts/adr-index.mjs --self-test && node scripts/adr-index.mjs --report), in the alias form the GATE INVOCATION IDIOM note (c91544a) asks of a new gate step; the anchors step is unchanged. The self-test runs with the flag becausecheck-self-test-wiredreds a self-test no workflow executes, and it exercises fixtures only: its live-tree battery proves the generator reads and renders the real registry and never asserts on what a record says (the respin moved the two assertions that did — one spelling everywhere, ui records parse — out; they were already--reportwarnings).--reportchecks the committed index against a fresh rendering and every record's status line, prints::warning file=…::annotations (and a$GITHUB_STEP_SUMMARYblock) for anything off, and exits 0 whatever it finds — so the only red this step can show is a broken generator, never a record or a stale index.--checkis the gate's spelling (exit 1 on drift or an unparsed record) for the day a maintainer names one; nothing calls it in CI. No new required check, no branch-protection change.ObjectUI registry (#26080, ruling 6417494426) — the 11 records under
ui/docs/adr/are listed in the root index underui-NNNNwith their files untouched. Their numbers overlap the root sequence (ui 0053, 0057 …) and mean different decisions, so an index row never readsADR-NNNNfor them, and a citation keeps theObjectUI ADR-NNNNspelling the anchor checker already recognises as another registry's. Rows link to../../ui/docs/adr/…, whichcheck-adr-linksresolves.docs/adr/PRIORITIZATION.mdremoved — it declared itself retired; the one live rule it still carried (a PR landing an ADR's implementation flips the status line toAccepted — implementedwith one line of evidence) is now the Status hygiene bullet in the README conventions, and ADR-0110's pointer to it names the README. Non-ADR notes stay out of the index by construction: a filename that is notNNNN-slug[.vN].mdis reported as foreign, never listed.Respin after the contract review (record 6434963378) — (1) the self-test no longer asserts on live records (above); (2) merged
origin/mainas a merge commit to clear thepackage.jsonconflict with c91544a and took its alias form; (3) README: a versions group now says the bareADR-0006names the decision whose current text is v4 and cites the superseded unversioned file by its stem; a**Decided**:line is a date line, so ADR-0079 dates to 2026-06-28; ADR-0071's status paragraph is folded onto its one line.Stale Proposed — listed, not relabelled. 18 records carry
Proposedwith a status date before 2026-07-26 (the card counted 19; ADR-0025 is the 19th, see row 19). Each has an implementation reading from the tree (anchor files that the gate pins to it; files underpackages/naming the number). None of these status lines changed; the reading is for the records' authors and the maintainer, not a verdict.19 stale Proposed records with an implementation reading each
docs/adr/0010-metadata-protection-model.mddocs/adr/0019-app-as-consumer-unit.mddocs/adr/0022-connectors-vs-messaging-channels.mddocs/adr/0026-client-ui-plugin-distribution.mddocs/adr/0027-metadata-authoring-lifecycle.mddocs/adr/0038-build-verification-loop.mddocs/adr/0046-package-docs-as-metadata.mdpackages/rest/src/meta-item-read-gate.ts,packages/rest/src/meta-item-read-routes.ts). Candidate forAccepted — implemented+ one line of evidence, by its author.docs/adr/0050-form-layout-vs-presentation.mddocs/adr/0051-inline-metadata-views-in-docs.mddocs/adr/0052-audit-is-not-the-activity-feed.mddocs/adr/0063-two-kernel-agents-skills-are-the-extension-primitive.mdpackages/cli/src/commands/generate.ts). Candidate forAccepted — implemented+ one line of evidence, by its author.docs/adr/0064-tool-scoping-to-agent.mddocs/adr/0071-dataset-semantic-layer-depth.mddocs/adr/0072-reference-scope-and-resolvability.mddocs/adr/0073-automation-execution-identity.mddocs/adr/0077-authoring-surface-boundary-hook-flow-validation.mddocs/adr/0080-ai-authored-ui-jsx-source.mdsdui-parserpackage (parse≠…packages/sdui-parser/src/parse.ts). Candidate forAccepted — implemented+ one line of evidence, by its author.docs/adr/0096-execution-surface-identity-admission.mddocs/adr/0025-plugin-package-distribution.md.ospluginar…Counts read from
git grep -l '\bADR-NNNN\b' -- packages(non-test vs__tests__/*.test.*) and fromscripts/adr-anchors/*.jsonat this PR's head.One row in
scripts/pm/dispatch-gates.data.mjs—COMPOUND_ANCHOR_LEDGERclassifies every compound self-test name the dispatch-gates anchor matches; the generator'srunSelfTestis a new one, socheck:pm-dispatch-gatesreddened until it was classified (genuine battery, fixtures masked as hints) and the docblock's pinned counts moved 34→35 / 23→24. The ledger forbids dodging the anchor by renaming, so the row is the fix.Out of scope, on purpose: no record is relabelled, renumbered or moved; no citation in the tree is rewritten to the stem form (bare citations keep working); no new required gate.
Tests
node scripts/adr-index.mjs --self-test— 46 assertions over 7 pinned batteries (record parsing incl.**Decided**:as a date line; the one spelling with every other spelling Unparsed; supersession refs extracted not invented; shared-number grouping and the versions cite-as; deterministic rendering of both registries;--checkreds /--reportnever does; live tree: the generator reads and renders the real registry — no assertion on a record's content). Self-test shape copied from a landed one (battery roster, pinned floors, verdict handshake).node scripts/check-adr-anchors.mjs --self-test— 133 assertions (was 112), new battery of 21 for stem-qualified citations: resolves to one record, malformed stem is red, versioned stem checks the.vNfile, bare number unchanged, collision pair cited by stem is unambiguous, decision letters on a qualified id check that record only. The live run on the merged head: 37 stem-qualified citations resolve (the README's versions note is the 37th).pnpm adr:indexthengit diff --quiet docs/adr/README.md— the committed index is byte-equal to a fresh rendering.node scripts/check-self-test-wired.mjsandnode scripts/check-self-test-workflow-commands.mjs— exit 0 with the self-test wired through thecheck:adr-indexalias (the first pass of the gates caught the generator's self-test as unrun; fixed by wiring, not by an allowlist row).pnpm check:adr-index— exit 0 (self-test, then--report: OK);--check— exit 0.Acceptance notes
check-adr-anchors.mjsunambiguous on those four ✔; index script with--self-test, called report-only from the existing lint ADR step, no new required gate ✔ (门禁不加); 19 stale Proposed listed with a reading, not relabelled ✔; 12 no-match files listed before normalizing ✔; non-ADR notes out of the index ✔;PRIORITIZATION.mdremoved ✔.ui/docs/adrregistered in the root index under theui-prefix, files not renamed ✔.docs/adr/**and.github/workflows/**are governed surfaces; this PR stays draft and lands on a governed approver's review.skip-changeset: the diff touchesdocs/adr/**, rootscripts/, rootpackage.jsonand a workflow — nothing any package ships in itsfiles[].Verification
Head:
9e254cb3(merge of origin/main 00874b9 + the respin commit). Commands below were run from the repository root on that head; exit codes captured before any pipe.Scripts and index
Dispatch gates — every command from `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, reconciled with `--ran`
Pass 3 on the pre-merge head
d1a0cfe5ran all 76 derived commands green (--ran: 76 derived / 76 run / 0 unrun). On this head9e254cb3(merge of origin/main + the respin) the full pass 4 (73 derived commands) was still running when this body was written; the quick checks named above pluspnpm check:pm-dispatch-gates(1935 cases) are exit 0 on it, and the complete--ranrecord of pass 4 is posted as the os-dev-report comment on #26076 when it completes. Changed lines 1542 vs the human-merge threshold 3000: under.Generated by Claude Code