diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d253356b..859e7998 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,9 +83,11 @@ jobs: - name: Every test file actually ran run: node scripts/check-test-files-ran.mjs vitest-report.json - # Fixture conformance, README example sync, and the README vocabulary - # table vs SPEC section 3. Documentation claims that can be checked by - # machine are checked by machine. + # Fixture conformance, the docs/protocol.md example sync, and that + # document's vocabulary table vs SPEC section 3. Documentation claims that + # can be checked by machine are checked by machine. The owner is one file + # rather than four READMEs: the same fixture repeated per translation is + # four places for it to drift. - name: Conformance suite run: bash spec/verify.sh diff --git a/README.ja.md b/README.ja.md index 1acf9c49..ad651f32 100644 --- a/README.ja.md +++ b/README.ja.md @@ -406,50 +406,18 @@ lifecycle — `Supersedes:` と `Expires:`、そして上の表の最終行を エージェント向けの指針は `skills/commitlore-commits/` にあり、commit-msg hook はエージェントが追加した record を検証するだけで、record を発明したり黙って追加したりしません。`harvest` の経路、`capture` トランザクション、そして人が trailer を手書きする逃げ道は、いずれも [docs/capture.md](docs/capture.md) にあります。 -## 完全な record +## record プロトコル -record はこれよりずっと小さくできますし、ほとんどは数個の field で足ります。この例が語彙のすべてを使うのは、conformance fixture でもあるからです — Git trailer parser は、すべての翻訳 README で下の code block を同じように読みます。 +record は普通の Git commit trailer の集まりで、たいていは小さいもので足ります: ```text -Prevent silent session drops during long-running operations +Fix expired-token refresh -The auth service returns inconsistent status codes on token -expiry, so the interceptor catches all 4xx responses and -triggers an inline refresh. - -Limit: Auth service does not support token introspection -Record-Id: r-4b7e21 Ruled-out: Extend token TTL to 24h | security policy violation -Ruled-out: Background refresh on timer | race condition -Certainty: firm -Blast: module -Undo: easy -Warn: 4xx handling is intentionally broad - -- do not narrow without verifying upstream behavior -Verified: Single expired token refresh (unit) -Unverified: Auth service cold-start > 500ms behavior -CommitLore-Version: 2.0.0 +Warn: Do not narrow the 4xx handler without verifying upstream behavior ``` -### Protocol vocabulary - -| Trailer | Meaning | -|---|---| -| `Limit:` | External condition that constrained the decision | -| `Record-Id:` | Stable identity across rewritten commit hashes | -| `Ruled-out:` | `alternative \| reason` — the first `\|` separates; there is no escape, so an alternative may not contain one | -| `Certainty:` | `firm` \| `tentative` \| `guess` | -| `Blast:` | `local` \| `module` \| `system` | -| `Undo:` | `easy` \| `costly` \| `permanent` | -| `Warn:` | Warning for a future modifier; trust-graded before delivery | -| `Verified:` / `Unverified:` | What was and was not checked | -| `Follows:` / `Supersedes:` | Decision-chain and lifecycle links | -| `Expires:` | Date or condition that ends a limit | -| `Evidence:` | Path, anchor, or URL supporting a claim | -| `Provenance:` | `authored` \| `inherited ` \| `reconstructed` | -| `CommitLore-Version:` / `X-*:` | Protocol identity and extensions | - -path の履歴は `commitlore context ` で読みます。より小さな例と、Git だけで record を読む方法は [docs/protocol.md](docs/protocol.md) に、規範的な定義は [SPEC §3](spec/SPEC.md) にあります。 +語彙をすべて使う完全な例、すべての trailer key の表、そして CommitLore なしで素の Git から読む方法は [docs/protocol.md](docs/protocol.md) にあります。規範的な定義は [SPEC §3](spec/SPEC.md) です。 ## リポジトリが証明すること diff --git a/README.ko.md b/README.ko.md index 79ce84b2..84b936cc 100644 --- a/README.ko.md +++ b/README.ko.md @@ -400,50 +400,18 @@ lifecycle — `Supersedes:`와 `Expires:`, 그리고 위 표의 마지막 행을 에이전트 지침은 `skills/commitlore-commits/`에 있고, commit-msg hook은 에이전트가 추가한 record를 검증할 뿐 record를 발명하거나 조용히 추가하지 않는다. `harvest` 경로, `capture` 트랜잭션, 그리고 사람이 trailer를 직접 쓰는 탈출구는 모두 [docs/capture.md](docs/capture.md)에 있다. -## 완전한 record +## record 프로토콜 -record는 이보다 훨씬 작을 수 있고, 대부분은 몇 개 field면 충분하다. 이 예시가 어휘 전체를 쓰는 이유는 conformance fixture이기도 하기 때문이다 — Git trailer parser는 모든 번역 README에서 아래 code block을 동일하게 읽는다. +record는 평범한 Git commit trailer 묶음이고, 대개는 작은 것으로 충분합니다: ```text -Prevent silent session drops during long-running operations +Fix expired-token refresh -The auth service returns inconsistent status codes on token -expiry, so the interceptor catches all 4xx responses and -triggers an inline refresh. - -Limit: Auth service does not support token introspection -Record-Id: r-4b7e21 Ruled-out: Extend token TTL to 24h | security policy violation -Ruled-out: Background refresh on timer | race condition -Certainty: firm -Blast: module -Undo: easy -Warn: 4xx handling is intentionally broad - -- do not narrow without verifying upstream behavior -Verified: Single expired token refresh (unit) -Unverified: Auth service cold-start > 500ms behavior -CommitLore-Version: 2.0.0 +Warn: Do not narrow the 4xx handler without verifying upstream behavior ``` -### 프로토콜 어휘 - -| Trailer | Meaning | -|---|---| -| `Limit:` | External condition that constrained the decision | -| `Record-Id:` | Stable identity across rewritten commit hashes | -| `Ruled-out:` | `alternative \| reason` — the first `\|` separates; there is no escape, so an alternative may not contain one | -| `Certainty:` | `firm` \| `tentative` \| `guess` | -| `Blast:` | `local` \| `module` \| `system` | -| `Undo:` | `easy` \| `costly` \| `permanent` | -| `Warn:` | Warning for a future modifier; trust-graded before delivery | -| `Verified:` / `Unverified:` | What was and was not checked | -| `Follows:` / `Supersedes:` | Decision-chain and lifecycle links | -| `Expires:` | Date or condition that ends a limit | -| `Evidence:` | Path, anchor, or URL supporting a claim | -| `Provenance:` | `authored` \| `inherited ` \| `reconstructed` | -| `CommitLore-Version:` / `X-*:` | Protocol identity and extensions | - -경로의 이력은 `commitlore context `로 읽는다. 더 작은 예시와 Git만으로 record를 읽는 방법은 [docs/protocol.md](docs/protocol.md)에, 규범적 정의는 [SPEC §3](spec/SPEC.md)에 있다. +어휘 전체를 쓰는 완전한 예제, 모든 trailer key 표, 그리고 CommitLore 없이 순수 Git으로 읽는 방법은 [docs/protocol.md](docs/protocol.md)에 있습니다. 규범적 정의는 [SPEC §3](spec/SPEC.md)입니다. ## 저장소가 증명하는 것 diff --git a/README.md b/README.md index cdaee80a..46d77886 100644 --- a/README.md +++ b/README.md @@ -501,50 +501,18 @@ Ask the agent to commit normally and preserve only the decision context the diff The agent instructions live in `skills/commitlore-commits/`, and the commit-msg hook validates any record the agent adds — it never invents or silently adds one. The `harvest` route, the `capture` transaction, and the escape hatch of writing trailers by hand are all in [docs/capture.md](docs/capture.md). -## A complete record +## The record protocol -A record can be much smaller than this; most need only a few fields. This one uses the whole vocabulary because it is also a conformance fixture — Git's trailer parser reads the code block identically in every translated README. +A record is an ordinary set of Git commit trailers, and a small one is usually enough: ```text -Prevent silent session drops during long-running operations +Fix expired-token refresh -The auth service returns inconsistent status codes on token -expiry, so the interceptor catches all 4xx responses and -triggers an inline refresh. - -Limit: Auth service does not support token introspection -Record-Id: r-4b7e21 Ruled-out: Extend token TTL to 24h | security policy violation -Ruled-out: Background refresh on timer | race condition -Certainty: firm -Blast: module -Undo: easy -Warn: 4xx handling is intentionally broad - -- do not narrow without verifying upstream behavior -Verified: Single expired token refresh (unit) -Unverified: Auth service cold-start > 500ms behavior -CommitLore-Version: 2.0.0 +Warn: Do not narrow the 4xx handler without verifying upstream behavior ``` -### Protocol vocabulary - -| Trailer | Meaning | -|---|---| -| `Limit:` | External condition that constrained the decision | -| `Record-Id:` | Stable identity across rewritten commit hashes | -| `Ruled-out:` | `alternative \| reason` — the first `\|` separates; there is no escape, so an alternative may not contain one | -| `Certainty:` | `firm` \| `tentative` \| `guess` | -| `Blast:` | `local` \| `module` \| `system` | -| `Undo:` | `easy` \| `costly` \| `permanent` | -| `Warn:` | Warning for a future modifier; trust-graded before delivery | -| `Verified:` / `Unverified:` | What was and was not checked | -| `Follows:` / `Supersedes:` | Decision-chain and lifecycle links | -| `Expires:` | Date or condition that ends a limit | -| `Evidence:` | Path, anchor, or URL supporting a claim | -| `Provenance:` | `authored` \| `drafted` \| `inherited ` \| `reconstructed` \| `unknown` | -| `CommitLore-Version:` / `X-*:` | Protocol identity and extensions | - -Read a path's history with `commitlore context `. Smaller examples, and how to read records with plain Git instead, are in [docs/protocol.md](docs/protocol.md); the normative definitions are in [SPEC §3](spec/SPEC.md). +The worked example that uses the whole vocabulary, the table of every trailer key, and how to read records with plain Git are in [docs/protocol.md](docs/protocol.md). The normative definitions are in [SPEC §3](spec/SPEC.md). ## What the repository proves diff --git a/README.zh-CN.md b/README.zh-CN.md index 584df85f..833e033f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -386,50 +386,18 @@ warnings 代理说明位于 `skills/commitlore-commits/`,commit-msg hook 只验证代理添加的 record,不会凭空创建或静默添加。`harvest` 路径、`capture` 事务,以及手写 trailer 这条逃生出口,都在 [docs/capture.md](docs/capture.md)。 -## 完整 record +## record 协议 -record 可以比这小得多,绝大多数只需要几个 field。这个示例用上全部词汇,是因为它同时也是 conformance fixture —— Git trailer parser 会在所有翻译版 README 中以相同方式读取下面的 code block。 +record 就是一组普通的 Git commit trailer,通常小的就够用: ```text -Prevent silent session drops during long-running operations +Fix expired-token refresh -The auth service returns inconsistent status codes on token -expiry, so the interceptor catches all 4xx responses and -triggers an inline refresh. - -Limit: Auth service does not support token introspection -Record-Id: r-4b7e21 Ruled-out: Extend token TTL to 24h | security policy violation -Ruled-out: Background refresh on timer | race condition -Certainty: firm -Blast: module -Undo: easy -Warn: 4xx handling is intentionally broad - -- do not narrow without verifying upstream behavior -Verified: Single expired token refresh (unit) -Unverified: Auth service cold-start > 500ms behavior -CommitLore-Version: 2.0.0 +Warn: Do not narrow the 4xx handler without verifying upstream behavior ``` -### Protocol vocabulary - -| Trailer | Meaning | -|---|---| -| `Limit:` | External condition that constrained the decision | -| `Record-Id:` | Stable identity across rewritten commit hashes | -| `Ruled-out:` | `alternative \| reason` — the first `\|` separates; there is no escape, so an alternative may not contain one | -| `Certainty:` | `firm` \| `tentative` \| `guess` | -| `Blast:` | `local` \| `module` \| `system` | -| `Undo:` | `easy` \| `costly` \| `permanent` | -| `Warn:` | Warning for a future modifier; trust-graded before delivery | -| `Verified:` / `Unverified:` | What was and was not checked | -| `Follows:` / `Supersedes:` | Decision-chain and lifecycle links | -| `Expires:` | Date or condition that ends a limit | -| `Evidence:` | Path, anchor, or URL supporting a claim | -| `Provenance:` | `authored` \| `inherited ` \| `reconstructed` | -| `CommitLore-Version:` / `X-*:` | Protocol identity and extensions | - -用 `commitlore context ` 读取 path 的历史。更小的示例,以及只用 Git 读取 record 的方法,在 [docs/protocol.md](docs/protocol.md);规范定义在 [SPEC §3](spec/SPEC.md)。 +使用全部语汇的完整示例、所有 trailer key 的表格,以及不用 CommitLore 直接用 Git 读取的方法,都在 [docs/protocol.md](docs/protocol.md)。规范性定义见 [SPEC §3](spec/SPEC.md)。 ## 仓库能够证明什么 diff --git a/docs/protocol.md b/docs/protocol.md index 2fff404f..768a7139 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -29,19 +29,64 @@ verification gap. How a record gets written is in [capture.md](capture.md). ## A complete record -The worked example that uses the full vocabulary stays in the README, in every -language, because it is also a conformance fixture: `spec/verify.sh` compares it -byte for byte with +A record can be much smaller than this; most need only a few fields. This one +uses the whole vocabulary because it is also a conformance fixture: `spec/verify.sh` +compares the block below byte for byte with [`spec/fixtures/valid/11-readme-example.txt`](../spec/fixtures/valid/11-readme-example.txt) -and fails if the two drift apart. See [A complete record](../README.md). +and fails if the two drift apart. A document that violated the spec it teaches +would be this project's most expensive defect, and it has happened — +`Certainty: high`, `Blast: narrow` and `Undo: clean` shipped here once, and all +three are values our own rejection fixtures carry. + +The marker below is what the checker finds. It is deliberately not "the last +`text` block in the file": a positional rule cannot say which block it owns, so +editing anything nearby silently moves the contract. + + + +```text +Prevent silent session drops during long-running operations + +The auth service returns inconsistent status codes on token +expiry, so the interceptor catches all 4xx responses and +triggers an inline refresh. + +Limit: Auth service does not support token introspection +Record-Id: r-4b7e21 +Ruled-out: Extend token TTL to 24h | security policy violation +Ruled-out: Background refresh on timer | race condition +Certainty: firm +Blast: module +Undo: easy +Warn: 4xx handling is intentionally broad + -- do not narrow without verifying upstream behavior +Verified: Single expired token refresh (unit) +Unverified: Auth service cold-start > 500ms behavior +CommitLore-Version: 2.0.0 +``` ## The vocabulary -The summary table of every trailer key also stays in the README, in every -language, and is checked against SPEC §3 by -`spec/schema/readme-vocab-check.mjs`: a key in the table that the spec does not -define is a key a user would write and the validator would reject, and a key the -spec defines but the table omits is a field that effectively does not exist. +Checked against SPEC §3 by `spec/schema/protocol-doc-vocab-check.mjs`: a key in +this table that the spec does not define is a key a user would write and the +validator would reject (`Decision-Id:` actually did that), and a key the spec +defines but this table omits is a field that effectively does not exist. + +| Trailer | Meaning | +|---|---| +| `Limit:` | External condition that constrained the decision | +| `Record-Id:` | Stable identity across rewritten commit hashes | +| `Ruled-out:` | `alternative \| reason` — the first `\|` separates; there is no escape, so an alternative may not contain one | +| `Certainty:` | `firm` \| `tentative` \| `guess` | +| `Blast:` | `local` \| `module` \| `system` | +| `Undo:` | `easy` \| `costly` \| `permanent` | +| `Warn:` | Warning for a future modifier; trust-graded before delivery | +| `Verified:` / `Unverified:` | What was and was not checked | +| `Follows:` / `Supersedes:` | Decision-chain and lifecycle links | +| `Expires:` | Date or condition that ends a limit | +| `Evidence:` | Path, anchor, or URL supporting a claim | +| `Provenance:` | `authored` \| `drafted` \| `inherited ` \| `reconstructed` \| `unknown` | +| `CommitLore-Version:` / `X-*:` | Protocol identity and extensions | For the normative meaning, cardinality, and value grammar of each key, read [SPEC §3](../spec/SPEC.md). diff --git a/scripts/check-exact-head-ci.mjs b/scripts/check-exact-head-ci.mjs index 1ef4a88f..7c67a286 100644 --- a/scripts/check-exact-head-ci.mjs +++ b/scripts/check-exact-head-ci.mjs @@ -40,7 +40,7 @@ const CI_WORKFLOW_FILE_PATH = fileURLToPath(new URL(`../${CI_WORKFLOW_PATH}`, im // shell command; without this lock replacing every job body with `true` would // still look like a real successful run. Update deliberately with the CI // workflow when its reviewed job contract changes. -export const EXPECTED_CI_WORKFLOW_SHA256 = 'f84cd550b55320c433cf53656b27eeb38f906f040795a39862c09f32b47bb4e5'; +export const EXPECTED_CI_WORKFLOW_SHA256 = 'b3cf70f2017579462071980215dcd5ee0a65702a05dde409e25b3a18113751bc'; // Fixed rather than inferred from returned jobs: absence must fail rather // than define itself away. `lint` only runs for pull requests and is therefore diff --git a/spec/schema/readme-vocab-check.mjs b/spec/schema/protocol-doc-vocab-check.mjs similarity index 63% rename from spec/schema/readme-vocab-check.mjs rename to spec/schema/protocol-doc-vocab-check.mjs index 070e50ea..b7213fe6 100644 --- a/spec/schema/readme-vocab-check.mjs +++ b/spec/schema/protocol-doc-vocab-check.mjs @@ -1,17 +1,20 @@ #!/usr/bin/env node -// README의 어휘표가 SPEC §3과 일치하는지 검사한다. +// docs/protocol.md 의 어휘표가 SPEC §3과 일치하는지 검사한다. // -// 이 프로젝트에서 README는 마케팅 문서가 아니라 스펙의 요약본이다. 표에 스펙에 없는 +// 표는 사용자가 실제로 보고 쓰는 요약본이다. 표에 스펙에 없는 // 키가 실리면 사용자가 그걸 쓰고 검증기에 거부당한다(`Decision-Id:` 가 실제로 그랬다). // 반대로 키가 빠지면 그 필드는 존재하지 않는 것과 같다. // -// 사용: node spec/schema/readme-vocab-check.mjs [README.*.md ...] +// 소유자는 네 개의 README가 아니라 docs/protocol.md 하나다 — 같은 표를 네 번 +// 반복하면 번역본마다 드리프트 지점이 하나씩 생긴다. +// +// 사용: node spec/schema/protocol-doc-vocab-check.mjs import fs from 'node:fs'; -const [specPath, ...readmes] = process.argv.slice(2); -if (!specPath || readmes.length === 0) { - console.error('usage: readme-vocab-check.mjs [more...]'); +const [specPath, ...docs] = process.argv.slice(2); +if (!specPath || docs.length === 0) { + console.error('usage: protocol-doc-vocab-check.mjs '); process.exit(2); } @@ -36,8 +39,8 @@ if (specKeys.size === 0) { let failed = false; -for (const readmePath of readmes) { - const md = fs.readFileSync(readmePath, 'utf8'); +for (const docPath of docs) { + const md = fs.readFileSync(docPath, 'utf8'); // 어휘표 행에서만 키를 뽑는다. 산문 속 백틱 언급(`feat:` 등)은 세지 않는다. const found = new Set(); for (const m of md.matchAll(/^\|\s*`([A-Za-z][A-Za-z0-9-]*):`/gm)) found.add(m[1]); @@ -52,11 +55,11 @@ for (const readmePath of readmes) { if (missing.length || extra.length) { failed = true; - console.error(`${readmePath}:`); - if (missing.length) console.error(` SPEC에 있으나 README 표에 없음: ${missing.join(', ')}`); - if (extra.length) console.error(` README 표에 있으나 SPEC에 없음: ${extra.join(', ')}`); + console.error(`${docPath}:`); + if (missing.length) console.error(` SPEC에 있으나 표에 없음: ${missing.join(', ')}`); + if (extra.length) console.error(` 표에 있으나 SPEC에 없음: ${extra.join(', ')}`); } } if (failed) process.exit(1); -console.log(`vocab table matches SPEC (${specKeys.size} keys × ${readmes.length} READMEs)`); +console.log(`vocab table matches SPEC (${specKeys.size} keys, owner: ${docs.join(', ')})`); diff --git a/spec/verify.sh b/spec/verify.sh index bb6b60ee..10160439 100755 --- a/spec/verify.sh +++ b/spec/verify.sh @@ -108,37 +108,46 @@ for txt in "$SCRIPT_DIR"/fixtures/invalid/*.txt; do check_fixture "invalid" "$txt" done -# 4. README의 대표 예제는 픽스처와 바이트 단위로 같아야 한다. -# README가 자기 스펙을 위반한 채 배포되는 것이 이 프로젝트에서 가장 비싼 결함이다 +# 4. 대표 예제는 픽스처와 바이트 단위로 같아야 한다. +# 문서가 자기 스펙을 위반한 채 배포되는 것이 이 프로젝트에서 가장 비싼 결함이다 # (실제로 Certainty: high / Blast: narrow / Undo: clean 이 실려 있었고, 셋 다 우리 # 자신의 거부 픽스처 값이었다). 예제를 픽스처로 승격하고 동기화를 기계로 강제한다. +# +# 소유자는 네 개의 README가 아니라 docs/protocol.md 하나다. 같은 fixture를 네 번 +# 반복하면 번역본마다 드리프트 지점이 생기고, README는 제품 진입 문서가 아니라 +# 스펙 요약본으로 남는다. 위치("마지막 text 블록")가 아니라 마커로 찾는다 — +# 위치 규칙은 자기가 무엇을 소유하는지 말하지 못하므로, 근처를 편집하면 계약이 +# 조용히 옮겨간다. README_FIXTURE="$SCRIPT_DIR/fixtures/valid/11-readme-example.txt" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" -for readme in "$REPO_ROOT"/README.md "$REPO_ROOT"/README.*.md; do - [ -f "$readme" ] || continue - if ! node -e ' - const fs = require("fs"); - const md = fs.readFileSync(process.argv[1], "utf8"); - const fixture = fs.readFileSync(process.argv[2], "utf8"); - const m = [...md.matchAll(/```text\n([\s\S]*?)\n```/g)].at(-1); - if (!m) { console.error("no ```text example block"); process.exit(1); } - if (m[1] + "\n" !== fixture) { - console.error("README example drifted from spec/fixtures/valid/11-readme-example.txt"); - process.exit(1); - } - ' "$readme" "$README_FIXTURE" 2>/tmp/commitlore-readme-err.$$; then - fail "$(basename "$readme"): 대표 예제가 픽스처와 다르다 — README가 스펙을 위반할 수 있다" - cat /tmp/commitlore-readme-err.$$ >&2 - fi - rm -f /tmp/commitlore-readme-err.$$ -done +PROTOCOL_DOC="$REPO_ROOT/docs/protocol.md" +if [ ! -f "$PROTOCOL_DOC" ]; then + fail "docs/protocol.md 이 없다 — 대표 예제의 소유자가 사라졌다" +elif ! node -e ' + const fs = require("fs"); + const md = fs.readFileSync(process.argv[1], "utf8"); + const fixture = fs.readFileSync(process.argv[2], "utf8"); + const marker = ""; + const at = md.indexOf(marker); + if (at < 0) { console.error(`no ${marker} marker`); process.exit(1); } + const m = /```text\n([\s\S]*?)\n```/.exec(md.slice(at)); + if (!m) { console.error("no ```text block after the marker"); process.exit(1); } + if (m[1] + "\n" !== fixture) { + console.error("docs/protocol.md example drifted from spec/fixtures/valid/11-readme-example.txt"); + process.exit(1); + } +' "$PROTOCOL_DOC" "$README_FIXTURE" 2>/tmp/commitlore-readme-err.$$; then + fail "docs/protocol.md: 대표 예제가 픽스처와 다르다 — 문서가 스펙을 위반할 수 있다" + cat /tmp/commitlore-readme-err.$$ >&2 +fi +rm -f /tmp/commitlore-readme-err.$$ -# 5. README의 어휘표가 SPEC §3과 일치하는가. +# 5. docs/protocol.md 의 어휘표가 SPEC §3과 일치하는가. # 표에 스펙에 없는 키가 실리면 사용자가 그걸 쓰고 검증기에 거부당한다. -if ! node "$SCRIPT_DIR/schema/readme-vocab-check.mjs" \ - "$SCRIPT_DIR/SPEC.md" "$REPO_ROOT"/README.md "$REPO_ROOT"/README.*.md \ +if ! node "$SCRIPT_DIR/schema/protocol-doc-vocab-check.mjs" \ + "$SCRIPT_DIR/SPEC.md" "$PROTOCOL_DOC" \ >/tmp/commitlore-vocab.$$ 2>&1; then - fail "README 어휘표가 SPEC §3과 다르다" + fail "docs/protocol.md 어휘표가 SPEC §3과 다르다" cat /tmp/commitlore-vocab.$$ >&2 fi rm -f /tmp/commitlore-vocab.$$ @@ -148,5 +157,5 @@ if [ "$fail_count" -gt 0 ]; then exit 1 fi -echo "OK: $checked_count fixtures + README example sync + vocab table" +echo "OK: $checked_count fixtures + protocol example sync + vocab table" exit 0