From 8bd3bf53176f9cd18989e2892541acb37a997f52 Mon Sep 17 00:00:00 2001 From: 128Na Date: Tue, 25 Aug 2026 01:02:43 +0900 Subject: [PATCH 1/4] =?UTF-8?q?docs:=20docs-lint=E5=B0=8E=E5=85=A5?= =?UTF-8?q?=E3=81=A8CI=E6=8E=A5=E7=B6=9A=EF=BC=88Phase=200=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E:/chore/template_repo のドキュメント最小化方式(不変記録/台帳/生きた文書の3分類 + 機械検証)を導入。既存の docs/dependency-debt.md・docs/known-risks.md は 台帳としてそのまま policy に登録し、スキーマは変更しない。 --- .github/workflows/docs-lint.yml | 23 +++ docs/README.md | 83 ++++++++ .../2026-06-22_assurance-audit.md} | 0 tools/docs-lint.mjs | 181 ++++++++++++++++++ tools/docs-policy.json | 23 +++ 5 files changed, 310 insertions(+) create mode 100644 .github/workflows/docs-lint.yml create mode 100644 docs/README.md rename docs/{assurance-audit-2026-06-22.md => records/2026-06-22_assurance-audit.md} (100%) create mode 100644 tools/docs-lint.mjs create mode 100644 tools/docs-policy.json diff --git a/.github/workflows/docs-lint.yml b/.github/workflows/docs-lint.yml new file mode 100644 index 0000000..42fdaa1 --- /dev/null +++ b/.github/workflows/docs-lint.yml @@ -0,0 +1,23 @@ +name: docs-lint + +on: + push: + paths: + - "**.md" + - "tools/**" + - ".github/workflows/docs-lint.yml" + pull_request: + paths: + - "**.md" + - "tools/**" + - ".github/workflows/docs-lint.yml" + +jobs: + docs-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: "22" + - run: node tools/docs-lint.mjs diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..f9bd607 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,83 @@ +# ドキュメント規約 + +このリポジトリのドキュメントは「実装とテストが SSOT(唯一の真実)」を前提に、 +md として残すものを次の3種類に限定する。この規約は `tools/docs-lint.mjs` で機械検証される。 + +## 原則 + +1. コードを読めば分かることは書かない。 +2. 残すのは「意思決定の理由」(ADR)と「過去時点の記録」(`docs/records/`)だけ。どちらも書いたら変更しない。 +3. 変化する事実(ステータス・日付・採番)は台帳に集約し、md 本文には書かない。 +4. 手動維持の索引は持たない。一覧は `ls docs/records/`(日付順に並ぶ)で得る。 + +## 「実装が SSOT」を削除の言い訳にする前に + +「コードを読めば分かる」は思い込みで成立していないことがある。実在するクラス名を +含みながら内部構造・メソッドシグネチャが実装と全く異なる「一見実装がありそうで +実は架空」のケースが、他リポジトリへの実地移行で複数見つかっている。現在形の +spec 相当文書を削除する前に: + +1. **対応する実装が実在するか確認する**。存在しなければ削除しない。`docs/records/` へ + 日付付きで凍結する。 +2. **内容が実装と一致するか確認する**。クラス名が実在するだけでは不十分。文書が挙げる + 具体例のうち最低1つは実際のソースファイルを開き、クラス構成・メソッドシグネチャ・ + データ構造まで突き合わせる。 +3. **テストが仕様の主張を実際に検証しているか確認する**。記述量に対してテストケース数が + 見合っているか照合する。名ばかりのテストしかない場合は、テスト拡充が先か、当面は + 生きた文書として残すかを判断する。 +4. **原理的にテストが検出できない契約でないか確認する**。外部フォーマット/外部APIの + reverse-engineering 文書や、自動検証のない人手同期の契約は、生きた文書 allowlist の + 正当な例外として認める。 + +## 分類と置き場所 + +### 不変記録 — docs/records/ + +- 命名: `YYYY-MM-DD_slug.md`(例: `2026-06-22_assurance-audit.md`) +- 対象: 調査メモ / 作業ログ / postmortem / 実験結果 +- 作成後は変更しない。内容を更新したくなったら**新しい日付で新規作成**し、 + 旧ファイル冒頭に `> Superseded by:` + 新記録への相対リンク、の1行だけを追記する。 + もう1つ許可される編集は、参照先ファイルが移動・削除された際の**リンクパスのみの追従** + (主張・内容は変えず、リンク先を現在の場所や後継 ADR に向け直すだけ)。内容の書き換えは + 一切許可しない。 + +### 意思決定記録 — docs/adr/ + +- 命名: `NNNN-slug.md`(連番4桁、例: `0001-use-sqlite.md`) +- 「なぜ」だけを書く。「どうなっているか」は実装を参照させる。 +- ステータス行 `> ステータス: Accepted (YYYY-MM-DD)` が必須(lint 検査対象)。 + 覆すときは新 ADR を書き、旧 ADR のステータスを `Superseded by ADR-NNNN` に変える。 + +### 台帳 — docs/dependency-debt.md, docs/known-risks.md + +- 変化する事実の SSOT。行の追加・更新・削除が正規の運用。 +- [dependency-debt.md](dependency-debt.md) のスキーマは変更禁止(`/dependabot-maintenance` スキル互換)。 +- [known-risks.md](known-risks.md) は解消しても行を削除せず、Status とテスト欄を更新して + 履歴として残す運用(dependency-debt.md の delete-on-resolve とは異なる)。初回診断の根拠は + [records/2026-06-22_assurance-audit.md](records/2026-06-22_assurance-audit.md) に凍結済み。 +- 新しい台帳を作るには `tools/docs-policy.json` の `ledgers` への登録が必要。 + +### 生きた文書 — allowlist 制 + +- `tools/docs-policy.json` の `livingDocs` に列挙されたファイルのみ許可。現在の一覧: + `README.md` / `CLAUDE.md` / `docs/README.md` +- allowlist へ追加する場合は、追加理由を ADR として残すこと。 +- 生きた文書は「常に現在を反映する義務」を負う。義務を果たせない文書は + records 化(日付を付けて凍結)するか削除する。 + +## 禁止事項(lint がエラーにする) + +- allowlist 外の「現在形 md」を docs/ やルートに置くこと +- `temp` / `tmp` / `draft` / `wip` という名前のディレクトリ(下書きは PR 説明・issue・セッションの scratchpad へ) +- `INDEX.md` / `index.md` / `TODO.md` というファイル名(索引は持たない、タスクは台帳か issue へ) +- リポジトリ外への絶対パスリンク(`~/`、ドライブレター、`file://`) +- 壊れた相対リンク + +## 検証 + +```bash +node tools/docs-lint.mjs +``` + +CI(`.github/workflows/docs-lint.yml`)でも同じものが走る。`composer run docs`(`composer run all` にも +含まれる)からも実行できる。 diff --git a/docs/assurance-audit-2026-06-22.md b/docs/records/2026-06-22_assurance-audit.md similarity index 100% rename from docs/assurance-audit-2026-06-22.md rename to docs/records/2026-06-22_assurance-audit.md diff --git a/tools/docs-lint.mjs b/tools/docs-lint.mjs new file mode 100644 index 0000000..e9f7067 --- /dev/null +++ b/tools/docs-lint.mjs @@ -0,0 +1,181 @@ +#!/usr/bin/env node +// docs-lint: ドキュメント規約(docs/README.md)の機械検証。依存ゼロ・Node 標準モジュールのみ。 +// 使い方: node tools/docs-lint.mjs (リポジトリルートで実行。exit 0 = green / 1 = error あり) +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const toolsDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.dirname(toolsDir); +const policy = JSON.parse(fs.readFileSync(path.join(toolsDir, "docs-policy.json"), "utf8")); + +const errors = []; +const warns = []; +const rel = (p) => path.relative(repoRoot, p).replaceAll("\\", "/"); +const error = (file, msg) => errors.push(`ERROR ${rel(file)}: ${msg}`); +const warn = (file, msg) => warns.push(`WARN ${rel(file)}: ${msg}`); + +// ---- 走査 ------------------------------------------------------------- +const SKIP_DIRS = new Set([".git", "node_modules", ".github", ".claude", ".idea", ".vscode"]); +const mdFiles = []; // { abs, relPath, kind } + +function walk(dir) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const abs = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (SKIP_DIRS.has(entry.name)) continue; + if (policy.forbiddenDirNames.includes(entry.name.toLowerCase())) { + error(abs, `禁止されたディレクトリ名です(${entry.name})。下書きは PR 説明や scratchpad へ、残す価値があるなら docs/records/ に日付付きで置く`); + } + walk(abs); + } else if (entry.name.endsWith(".md")) { + mdFiles.push({ abs, relPath: rel(abs) }); + } + } +} +for (const root of policy.scanRoots.recursive) { + const abs = path.join(repoRoot, root); + if (fs.existsSync(abs)) walk(abs); +} +for (const root of policy.scanRoots.flat) { + const abs = path.join(repoRoot, root); + for (const entry of fs.readdirSync(abs, { withFileTypes: true })) { + if (entry.isFile() && entry.name.endsWith(".md")) { + mdFiles.push({ abs: path.join(abs, entry.name), relPath: rel(path.join(abs, entry.name)) }); + } + } +} + +// ---- 分類(allowlist 検査) ------------------------------------------ +const inDir = (relPath, dir) => relPath.startsWith(dir.replaceAll("\\", "/") + "/"); +for (const f of mdFiles) { + if (policy.forbiddenFileNames.includes(path.basename(f.relPath))) { + error(f.abs, `禁止されたファイル名です。手動索引や TODO.md は持たない(一覧は ls docs/records/、タスクは台帳か issue へ)`); + f.kind = "forbidden"; + } else if (policy.livingDocs.includes(f.relPath)) f.kind = "living"; + else if (policy.ledgers.includes(f.relPath)) f.kind = "ledger"; + else if (policy.recordDirs.some((d) => inDir(f.relPath, d))) f.kind = "record"; + else if (inDir(f.relPath, policy.adrDir)) f.kind = "adr"; + else if (inDir(f.relPath, policy.templateDir)) f.kind = "template"; + else { + f.kind = "unclassified"; + error(f.abs, `分類できない md です。records/ADR/台帳のいずれかに置くか、生きた文書として tools/docs-policy.json の livingDocs に登録(+理由を ADR 化)する`); + } +} + +// ---- records 命名 ----------------------------------------------------- +const RECORD_NAME = /^(\d{4})-(\d{2})-(\d{2})_[a-z0-9-]+\.md$/; +const now = new Date(); +const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`; +// ローカル日付で判定する(UTCだとJST等UTC+では日付が変わる前0〜9時台に today 扱いの記録が「未来」誤判定される) +for (const f of mdFiles.filter((x) => x.kind === "record")) { + const m = path.basename(f.relPath).match(RECORD_NAME); + if (!m) { + error(f.abs, "records の命名は YYYY-MM-DD_slug.md(slug は小文字英数とハイフン)"); + continue; + } + const [, y, mo, d] = m; + const dt = new Date(`${y}-${mo}-${d}T00:00:00Z`); + if (Number.isNaN(dt.getTime()) || dt.toISOString().slice(0, 10) !== `${y}-${mo}-${d}`) { + error(f.abs, `実在しない日付です(${y}-${mo}-${d})`); + } else if (`${y}-${mo}-${d}` > today) { + error(f.abs, `未来の日付です(${y}-${mo}-${d})`); + } +} + +// ---- ADR 命名 + ステータス行 ----------------------------------------- +const ADR_NAME = /^(\d{4})-[a-z0-9-]+\.md$/; +const adrNumbers = new Map(); +for (const f of mdFiles.filter((x) => x.kind === "adr")) { + const m = path.basename(f.relPath).match(ADR_NAME); + if (!m) { + error(f.abs, "ADR の命名は NNNN-slug.md(連番4桁 + 小文字英数とハイフン)"); + continue; + } + if (adrNumbers.has(m[1])) { + error(f.abs, `ADR 番号 ${m[1]} が重複しています(${adrNumbers.get(m[1])})`); + } else { + adrNumbers.set(m[1], f.relPath); + } + const body = fs.readFileSync(f.abs, "utf8"); + if (!/^> ステータス: /m.test(body)) { + error(f.abs, "「> ステータス: Accepted (YYYY-MM-DD)」形式のステータス行が必要です"); + } +} + +// ---- リンク検査 ------------------------------------------------------- +const LINK = /\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g; +for (const f of mdFiles) { + if (f.kind === "template" || f.kind === "forbidden") continue; + const body = fs.readFileSync(f.abs, "utf8"); + for (const m of body.matchAll(LINK)) { + const target = m[1]; + if (/^(https?|mailto):/.test(target) || target.startsWith("#")) continue; + if (target.startsWith("~") || /^[A-Za-z]:[\\/]/.test(target) || target.startsWith("file://") || target.startsWith("/")) { + error(f.abs, `リポジトリ外への絶対パスリンクは禁止(${target})。リポジトリ内の相対リンクにするか、経緯なら records に書く`); + continue; + } + const resolved = path.resolve(path.dirname(f.abs), target.split("#")[0]); + if (!fs.existsSync(resolved)) { + error(f.abs, `リンク切れ: ${target}`); + } + } +} + +// ---- 台帳スキーマ ----------------------------------------------------- +const depDebt = path.join(repoRoot, "docs", "dependency-debt.md"); +if (fs.existsSync(depDebt)) { + const lines = fs.readFileSync(depDebt, "utf8").split(/\r?\n/); + const headerIdx = lines.findIndex((l) => l.trim() === policy.dependencyDebtHeader); + if (headerIdx === -1) { + error(depDebt, `ヘッダ行が規定と一致しません。/dependabot-maintenance 互換のため次を維持: ${policy.dependencyDebtHeader}`); + } else { + for (let i = headerIdx + 2; i < lines.length; i++) { + const line = lines[i].trim(); + if (!line.startsWith("|")) break; + const cells = line.split("|").map((c) => c.trim()); + const type = cells[5]; + if (type && !policy.dependencyDebtTypes.includes(type)) { + error(depDebt, `L${i + 1}: Type「${type}」は不正。許可値: ${policy.dependencyDebtTypes.join(" / ")}`); + } + } + } +} +const consDebt = path.join(repoRoot, "docs", "consistency-debt.md"); +if (fs.existsSync(consDebt)) { + const seen = new Map(); + const lines = fs.readFileSync(consDebt, "utf8").split(/\r?\n/); + lines.forEach((line, i) => { + const m = line.match(/^\|\s*(CD-\d+)\s*\|/); + if (!m) return; + if (seen.has(m[1])) error(consDebt, `L${i + 1}: ${m[1]} が重複しています(初出 L${seen.get(m[1])})`); + else seen.set(m[1], i + 1); + }); +} + +// ---- 生きた文書のパス参照検査(warn) -------------------------------- +const PATHLIKE = /`((?:docs|tools|src|scripts|tests)\/[^`\s]+)`/g; +for (const f of mdFiles.filter((x) => x.kind === "living")) { + const body = fs.readFileSync(f.abs, "utf8"); + for (const m of body.matchAll(PATHLIKE)) { + const token = m[1]; + if (/[*{}<>()?$]|YYYY|NNNN|\.\.\./.test(token)) continue; // プレースホルダはスキップ + if (!fs.existsSync(path.join(repoRoot, token))) { + warn(f.abs, `参照パスが見つかりません: ${token}(リネーム未追随の可能性)`); + } + } +} + +// ---- テンプレマーカー残存(warn) ------------------------------------ +for (const f of mdFiles) { + if (f.kind === "template") continue; + const body = fs.readFileSync(f.abs, "utf8"); + const count = (body.match(/TODO\(template\):/g) || []).length; + if (count > 0) warn(f.abs, `TODO(template) マーカーが ${count} 件残っています`); +} + +// ---- 出力 ------------------------------------------------------------- +for (const line of errors) console.error(line); +for (const line of warns) console.log(line); +console.log(`docs-lint: ${mdFiles.length} files scanned, ${errors.length} error(s), ${warns.length} warning(s)`); +process.exit(errors.length > 0 ? 1 : 0); diff --git a/tools/docs-policy.json b/tools/docs-policy.json new file mode 100644 index 0000000..c652174 --- /dev/null +++ b/tools/docs-policy.json @@ -0,0 +1,23 @@ +{ + "$comment": "docs-lint.mjs が読む機械可読ポリシー。生きた文書を増やす場合は livingDocs に追記し、理由を ADR として残すこと。", + "scanRoots": { + "recursive": ["docs"], + "flat": ["."] + }, + "livingDocs": [ + "README.md", + "CLAUDE.md", + "docs/README.md" + ], + "ledgers": [ + "docs/dependency-debt.md", + "docs/known-risks.md" + ], + "recordDirs": ["docs/records"], + "adrDir": "docs/adr", + "templateDir": "docs/templates", + "forbiddenFileNames": ["INDEX.md", "index.md", "TODO.md"], + "forbiddenDirNames": ["temp", "tmp", "draft", "wip"], + "dependencyDebtHeader": "| Package | Current | Target | Blocker | Type | Revisit condition | Recorded |", + "dependencyDebtTypes": ["temporary", "infra", "behavior-change"] +} From 9fd4ca16e47c4caf534105c109d06c849810adb0 Mon Sep 17 00:00:00 2001 From: 128Na Date: Tue, 25 Aug 2026 01:02:58 +0900 Subject: [PATCH 2/4] =?UTF-8?q?docs:=20assurance-audit=E3=82=92records?= =?UTF-8?q?=E3=81=B8=E7=A7=BB=E5=8B=95=E3=81=97=E3=83=AA=E3=83=B3=E3=82=AF?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=BE=93=EF=BC=88Phase=202=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/assurance-audit-2026-06-22.md は日付付きの凍結記録なので docs/records/2026-06-22_assurance-audit.md へ命名を揃えて移動。 known-risks.md 側とファイル内の相互リンクをパスのみ追従(内容は変更しない)。 --- docs/known-risks.md | 2 +- docs/records/2026-06-22_assurance-audit.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/known-risks.md b/docs/known-risks.md index 1b6bc07..1cb1dc0 100644 --- a/docs/known-risks.md +++ b/docs/known-risks.md @@ -6,7 +6,7 @@ - **制御**: None(無し)/ Detective(事後に検知・通知のみ)/ Preventive(事前に阻止) - **Status**: 🔴 Missing/Stale/Weak ・ 🟡 Structural Weakness/SPOF ・ 🟢 OK - 是正が完了したら Status とテスト欄を更新する(行は削除せず履歴として残す)。 -- 初回診断の根拠は [assurance-audit-2026-06-22.md](assurance-audit-2026-06-22.md) を参照。 +- 初回診断の根拠は [records/2026-06-22_assurance-audit.md](records/2026-06-22_assurance-audit.md) を参照。 ## A. Scrape / Extract パイプライン diff --git a/docs/records/2026-06-22_assurance-audit.md b/docs/records/2026-06-22_assurance-audit.md index f9ddaf8..9a859a8 100644 --- a/docs/records/2026-06-22_assurance-audit.md +++ b/docs/records/2026-06-22_assurance-audit.md @@ -1,7 +1,7 @@ # Assurance Audit スナップショット(2026-06-22) これは初回の Assurance Audit(assurance-audit スキル)の診断記録。 -「現在の状態」は [known-risks.md](known-risks.md) を参照。本ファイルは**初回診断の根拠**を残すための凍結記録。 +「現在の状態」は [../known-risks.md](../known-risks.md) を参照。本ファイルは**初回診断の根拠**を残すための凍結記録。 採点モデル: `Intent → Behavior → Control → Evidence`。 Coverage(量)ではなく **Confidence(守られているか)** を採点する。 @@ -12,7 +12,7 @@ Coverage(量)ではなく **Confidence(守られているか)** を採 ## Step -1 所見(最優先) **脅威モデル・既知リスク・runbook が一切存在しない。** `docs/dependency-debt.md` は依存負債のみ。 -「何を守るべきか」の台帳が無いこと自体が最大の欠陥。本監査を機に [known-risks.md](known-risks.md) を新設した。 +「何を守るべきか」の台帳が無いこと自体が最大の欠陥。本監査を機に [known-risks.md](../known-risks.md) を新設した。 ## New Candidate Risks(脅威探索で発見、既存台帳に無かった項目) From c0164975f04ab4da1d6a6dd709e0834f3ef2a80f Mon Sep 17 00:00:00 2001 From: 128Na Date: Tue, 25 Aug 2026 01:03:00 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs:=20composer=20script=E3=81=A8CLAUDE.md?= =?UTF-8?q?=E3=81=8B=E3=82=89docs-lint=E3=81=B8=E6=8E=A5=E7=B6=9A=EF=BC=88?= =?UTF-8?q?Phase=203=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit composer run docs(composer run all にも追加)で node tools/docs-lint.mjs を 実行できるようにし、CLAUDE.md に docs/README.md の規約への参照を追記。 --- CLAUDE.md | 7 +++++++ composer.json | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3c6c195..68dedc2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,8 +13,15 @@ composer run pint:check # Pint dry-run composer run stan # PHPStan composer run rector # Rector (automated refactoring, dry-run by default) php artisan test # PHPUnit + +composer run docs # docs-lint (node tools/docs-lint.mjs) ``` +## Architecture + +ドキュメントは [docs/README.md](docs/README.md) の規約に従う。 +生きた文書は allowlist 制(`tools/docs-policy.json`)。記録は `docs/records/` に日付付きで不変。 + ## グローバル規約 `~/.claude/CLAUDE.md` のポリシーに従う。パッケージ更新は `/repo-maintenance`、Dependabot PR 整理は `/dependabot-maintenance`、複数エージェント作業は `/orchestrate` スキルを使う。 diff --git a/composer.json b/composer.json index 5b51f07..fba5697 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,8 @@ "@php artisan ide-helper:models -WR", "@rector", "@stan", - "@pint" + "@pint", + "@docs" ], "pint": [ "@php ./vendor/bin/pint" @@ -75,6 +76,9 @@ ], "rector": [ "@php ./vendor/bin/rector --no-diffs" + ], + "docs": [ + "node tools/docs-lint.mjs" ] }, "extra": { From 32e67a33def6923adcc3bd6a98b0d7f0335d7cff Mon Sep 17 00:00:00 2001 From: 128Na Date: Tue, 25 Aug 2026 01:14:21 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20=E3=82=BB=E3=83=AB=E3=83=95?= =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E5=AF=BE=E5=BF=9C=EF=BC=88?= =?UTF-8?q?CI=E9=87=8D=E8=A4=87=E5=AE=9F=E8=A1=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E3=81=A8CLAUDE.md=E8=A8=98=E8=BF=B0=E6=9B=B4=E6=96=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs-lint.yml の push トリガーに branches フィルタを追加し、既存 main.yml の 慣習(push: master、pull_request: master/develop)に合わせて PR ブランチでの push/pull_request 二重実行を防止 - CLAUDE.md の `composer run all` コメントに docs-lint を明記し、 composer run docs が Node.js を要求することを注記 --- .github/workflows/docs-lint.yml | 2 ++ CLAUDE.md | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs-lint.yml b/.github/workflows/docs-lint.yml index 42fdaa1..d3e120b 100644 --- a/.github/workflows/docs-lint.yml +++ b/.github/workflows/docs-lint.yml @@ -2,11 +2,13 @@ name: docs-lint on: push: + branches: [master] paths: - "**.md" - "tools/**" - ".github/workflows/docs-lint.yml" pull_request: + branches: [master, develop] paths: - "**.md" - "tools/**" diff --git a/CLAUDE.md b/CLAUDE.md index 68dedc2..a6a99c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands ```bash -# Run all checks (dump-autoload + ide-helper + rector + phpstan + pint) +# Run all checks (dump-autoload + ide-helper + rector + phpstan + pint + docs-lint) composer run all composer run pint # Laravel Pint (format) @@ -14,7 +14,7 @@ composer run stan # PHPStan composer run rector # Rector (automated refactoring, dry-run by default) php artisan test # PHPUnit -composer run docs # docs-lint (node tools/docs-lint.mjs) +composer run docs # docs-lint (node tools/docs-lint.mjs, requires Node.js) ``` ## Architecture