Skip to content

docs(adr): a code block inside a record is a specimen, not an example - #8746

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-8363-adr-code-blocks-are-specimens
Sep 9, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-8363-adr-code-blocks-are-specimens

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #8363

Ruling A (director seat, comment 5582376965, decision batch #88, 2026-09-08) lands as text. Quoted verbatim, untranslated:

Ruled. Records are immutable and are not an API surface: teaching examples live in skills/objectui/** and content/docs/** where the gate compiles them. New ADRs and audits write code blocks with a fence language the doc-snippet gate does not compile — chosen from the languages check:doc-fences' header already lists as non-compiled (⛔ no new fence language is invented for this). The convention is one sentence in the ADR template and one line in the contributing text. The four existing records (ADR-0001, ADR-0036, ADR-0057, the 2026-07 objectview audit) are not edited; their UNGATED_DOCS rows are the terminal state and are annotated as such. ⛔ B refused (turns every ADR into a maintained API document, contradicting record immutability). ⛔ C refused (a permanent four-row debt nobody may pay).

The card's four-facet block recommended A with facet ① (long-term architecture) leading at ≥ 50 %: defining a record's block as a specimen keeps the record face and the teaching face apart, so an ADR never becomes an API document that has to be re-edited on every contract change. Facet ③ (making it structurally harder for an AI to write the wrong thing) is served by the same line — a non-compiled fence tells the next author at a glance that the block is a dated sketch, not runnable code to copy.

Two additive edits — 13 inserted lines, nothing removed, nothing reworded

1. CONTRIBUTING.md (+2 lines, 708 → 710). The convention goes in the paragraph that introduces the repo-root docs/ tree as internal engineering material (ADRs, audits, architecture notes) — the contributing text the ruling names. Added, in full:

A code block inside one of those records is a SPECIMEN, not an example to copy. An ADR states what was decided on a date and a dated audit states what was measured on one, so a snippet inside either is part of the record — edited until it compiles, it makes the record say something it never said. Fence such a block plaintext (an unhighlighted spelling scripts/check-doc-fence-languages.mjs already lists, and one scripts/check-doc-snippet-types.mjs does not compile), never ts / tsx / typescript. Code a reader may copy belongs in content/docs/** or skills/objectui/**, where a gate compiles it and a wrong line can be fixed without falsifying a record. Maintainer ruling 2026-09-08 (objectui#8363); the four records that predate it are not edited — they are named, with their measured counts, in that gate's UNGATED_DOCS ledger.

2. scripts/check-doc-snippet-types.mjs (+11 lines, 2722 → 2733). The terminal-state annotation, appended to the comment block that opens UNGATED_DOCS's card-2 section, directly above the first row:

  // ⛔ TERMINAL STATE — these four rows are not a debt anybody may pay down.
  // Maintainer ruling A on objectui#8363 (director seat, decision batch #88,
  // 2026-09-08): a code block inside an ADR or a dated audit is a SPECIMEN of what
  // was decided or measured, never an example to copy. ⇒ ⛔ No record below is
  // edited to make it compile, and ⛔ no row is deleted to "clear" the ledger — a
  // row leaves only with the record it names. New ADRs and audits state the
  // convention instead (CONTRIBUTING.md): a specimen block is fenced `plaintext`,
  // which this gate does not compile, so this list does not grow.
  // ⚠️ Nothing else is relaxed: each of the four still owes its written reason and
  // is still re-derived every run against a document that really holds a block.

⛔ Not touched: no UNGATED_DOCS row is added, removed or reworded; not one byte inside ADR-0001, ADR-0036, ADR-0057 or the 2026-07 objectview audit moves; no code path changes. git show --stat reads 2 files changed, 13 insertions(+), 0 deletions.

The ruling's "one sentence in the ADR template" — measured, that template does not exist

  • git ls-tree --name-only origin/main docs/adr/ returns ten numbered records and nothing else — no README.md, no template file, and no record that presents itself as one.
  • git grep -w -i ADR over the tree, minus docs/adr/** and .changeset/**, reaches exactly three places that could have been a second home: CONTRIBUTING.md:426 (the docs/ paragraph, edited here), AGENTS.md:478 (the governed-surface register) and AGENTS.md:108 (a reference to ADR-0054 from an unrelated commandment). There is no ADR-authoring section anywhere in the repository.
  • docs/audits/ is the same shape: six dated audits, no index and no template.

⇒ The contributing line IS the convention, which is the branch the dispatch anticipated. ⛔ No template file was invented and ⛔ no record was edited — both are explicit terms of the ruling. Giving the convention a second home under docs/adr/** would be a new governed card, not a silent addition here.

Why plaintext is the spelling the line names

  • It is a member of UNHIGHLIGHTED_SPELLINGS (scripts/check-doc-fence-languages.mjs:246 — plaintext, text, plain, txt, plus the empty info string), the set that gate's header already lists as non-compiled. ⛔ No new fence language is invented.
  • scripts/check-doc-snippet-types.mjs compiles TS_FENCE_LANGUAGES = ts / tsx / typescript and nothing else, so a plaintext specimen is never collected for compilation — which is the property the ruling asks for.
  • It is the house spelling: 183 plaintext fences under content/docs/** against 5 text.
  • Ownership, measured from the snippet gate's own map (fences · snippets · types): docs/adr/** and docs/audits/** are read by the snippet gate alone (✗ · ✓ · ✗), so a specimen fence inside a record is invisible to check:doc-fences today. Naming a spelling from that gate's known set is the cheap insurance for the day the two subtrees ever join its walk: a known synonym is classifiable, whereas an unknown spelling such as raw or output fails there on sight and can never be baselined.

Governed-surface reading — taken on the real change set

$ node scripts/check-governed-queue-guard.mjs --test CONTRIBUTING.md scripts/check-doc-snippet-types.mjs
✅ NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched.
   An ordinary pull request: the normal review and merge-queue route applies.

The ruling expected the template edit to land under docs/adr/** and therefore called this governed; because that template does not exist, the change set never reaches the register. The PR is opened as a draft anyway and this seat flips nothing — ready, queue, auto-merge and approval are all the PM seat's call.

Gates — exit codes captured before any pipe; every verdict line is the gate's own

All readings taken on head 4e57601.

command exit verdict
node scripts/check-governed-queue-guard.mjs --test … 0 NOT GOVERNED — 2 path(s) checked against 5 governed surface(s); none matched.
node scripts/check-doc-fence-languages.mjs (check:doc-fences) 0 every TypeScript block in 227 document(s) is fenced ts/tsx/typescript, except 80 declared file(s) carrying 89 block(s) … No unknown fence spelling hides one.
node scripts/check-doc-snippet-types.mjs (check:doc-snippets) 0 Scanned 245 document(s): 241 covered (126 of them hold a ts/tsx block), 4 ungated · Semantic phase: 636 of 636 block(s) judged, 0 failed · Every covered documentation snippet compiles against the built types.
node scripts/check-control-bytes.mjs (check:control-bytes) 0 OK (scanned 6946 tracked text file(s); skipped 85 binary).
node scripts/check-changeset-presence.mjs 0 2 file(s) changed, 0 of them published source … No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-doc-links.mjs (docs:check-links) 0 Links are valid across 17 scan roots.
vitest run --root . scripts/__tests__/check-doc-snippet-types.test.ts 0 Test Files 1 passed (1) · Tests 101 passed (101) — includes the ledger pins (is green, and the ledger is exact, every entry in the real ledger carries a written reason, and the card-2 subtree pins)
vitest run --root . over the nine other scripts/__tests__ files naming the edited gate (fence-languages walk-equality pin included) 0 Test Files 9 passed (9) · Tests 409 passed (409)
vitest run --root . scripts/__tests__/check-doc-links.test.ts scripts/__tests__/ci-cd-pipeline-doc.test.ts (the two suites that read CONTRIBUTING.md) 0 Test Files 2 passed (2) · Tests 179 passed (179)
pnpm exec eslint scripts/check-doc-snippet-types.mjs 0 clean
grep -naP control-character sweep over both changed files 1 (no match) no control byte written

The snippet gate needed a build, and its first answer was not a failure. Unbuilt it printed PRECONDITION NOT MET (exit 2) — The snippet program was NOT run … This is "I could not run", NOT "I ran and found errors", so that reading is recorded as NOT MEASURED, never as red. The scoped closure was then built through the shared verify lock — turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2, 35/35 tasks successful, 2m18s, lock verdict command-exit 0 — and the gate re-ran green above. The 4 ungated documents it reports are the same four rows this PR annotates.

Declared narrowing. The repository-wide pnpm lint and full pnpm test are CI's runs, not this seat's. Locally the change was linted as one file and tested through every scripts/__tests__ suite that names the edited gate plus the two that read CONTRIBUTING.md. No reverse-verification or ablation is reported: this change adds no assertion and no behaviour — both edits are prose and comment text — so there is nothing that could be mutated into a red. The standing measurement in its place is the re-run of the gate itself against the built closure, before and after the edit.

验收备注

  • content/docs/guide/ci-cd-pipeline.md documents the three doc gates and the governed register, and one of its lines names docs/adr/**. Nothing on that page is falsified by this change (it makes no claim about fence conventions inside records), so it is left untouched. Noted, not filed.
  • docs/adr/** has no index, no template and no README.md, so a new ADR takes its shape by copying an existing record. That is an observation about the tree, not a defect of this card, and giving it a home is a governed decision for the maintainer rather than something to add here. Noted, not filed.

维护者速读(草稿)

改了什么 —— 两处纯新增文字,共 13 行,没有删改任何既有内容。一是 CONTRIBUTING.md 介绍仓库根 docs/ 目录那一段后面加一句约定:ADR 与带日期的审计里的代码块是「样本」,写成 plaintext 围栏,不写 ts;可抄的示例仍然放在 content/docs/** 与 skills/objectui/**。二是门禁脚本 check-doc-snippet-types.mjs 里那张账本(UNGATED_DOCS)的头部注释,标明四条记录的账目按裁决 A 就是终态。

为什么改 —— 你在决策批 #88 裁的 A:记录不可改,记录里的代码块是「当天决定/当天测到什么」的样本,不是 API 文档。之前的问题是四份记录里的代码块按字面编译报 29 处错误,门禁只能把它们挂在账上,而这笔账没人有权去付——修了就等于篡改历史记录。这次把裁决写成文字:新记录照约定写,不会再产生新的债;旧的四条明确标为终态,后来的人不会误以为那是待办事项。

风险与代价(含回滚) —— 风险很低:两处都是文字,门禁行为一行未动,账本一行未改,全部门禁与相关测试本地跑绿(见上表)。代价是这条约定目前只有一个家:docs/adr/ 下没有模板文件,而裁决明令不许新造一个,所以写 ADR 的人要从 CONTRIBUTING.md 读到它——实测这是仓里唯一讲 docs/ 是什么的地方。回滚就是 revert 这一个 commit,不牵连任何东西。

席位意见 ——(待补)

你要做的 —— 看一眼那句约定的措辞是否就是你要的意思,特别是「plaintext」这个拼写(它取自门禁自己已列为不编译的集合,没有发明新拼写)。若你希望约定在 docs/adr/ 下也有一份(比如一个 ADR 模板文件),那是另一张受管卡,说一声即可。


Generated by Claude Code

Ruling A on objectui#8363 (director seat, decision batch #88, 2026-09-08)
lands as text.

CONTRIBUTING.md's `docs/` paragraph now states the convention once: a code
block inside an ADR or a dated audit is a SPECIMEN of what was decided or
measured, fenced `plaintext` — an unhighlighted spelling
check-doc-fence-languages.mjs already lists and check-doc-snippet-types.mjs
does not compile — never ts/tsx/typescript. Code a reader may copy stays in
content/docs/** and skills/objectui/**, where a gate compiles it.

The UNGATED_DOCS header comment in check-doc-snippet-types.mjs annotates the
four ledgered rows as the terminal state: no record is edited to make it
compile, no row is deleted to clear the ledger, and new records carry the
convention instead so the list does not grow.

Both edits are additive text. No record is edited, no ledger row is added,
removed or reworded, and no gate behaviour moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants