Skip to content

feat(hooks): push 前パイプラインハーネスの実装 - #8

Merged
aloekun merged 1 commit into
masterfrom
feat/push-pipeline-harness
Mar 30, 2026
Merged

feat(hooks): push 前パイプラインハーネスの実装#8
aloekun merged 1 commit into
masterfrom
feat/push-pipeline-harness

Conversation

@aloekun

@aloekun aloekun commented Mar 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • Claude Code hooks に存在しない push hook を、PreToolUse ブロック + スタンドアロン exe の2段構成で補完
  • PreToolUse の jj-push-guard プリセットで直接の push をブロックし、pnpm push に誘導
  • hooks-push-pipeline (Rust exe) が hooks-config.toml[push_pipeline] セクションからステップを読み込み、順次実行
  • type = "command" ステップ(テスト等)は即時実行、type = "ai" ステップ(レビュー、コミット整理)は将来実装のプレースホルダー

Changes

ファイル 内容
.claude/hooks-push-pipeline/ パイプラインランナー (Rust, 9 テスト付き)
.claude/hooks-pre-tool-validate/src/main.rs jj-push-guard プリセット追加
.claude/hooks-config.toml [push_pipeline] セクション追加
package.json build:hooks-push-pipeline, push スクリプト追加
docs/adr/adr-008-push-pipeline-harness.md 設計判断の記録

Test plan

  • cargo test (hooks-push-pipeline): 9/9 pass
  • cargo test (hooks-pre-tool-validate): 93/93 pass
  • pnpm push でパイプライン実行を確認 (test pass → AI skip → push 完了)
  • pnpm build:hooks で全 hooks の一括ビルドを確認

🤖 Generated with Claude Code

Summary by CodeRabbit

  • 新機能

    • pnpm push によるプッシュ前パイプラインを追加(テスト実行とレビューステップを順に実行)。
    • 直接的なプッシュ操作をブロックして pnpm push を促す保護機構を導入。
  • ドキュメント

    • ADR-008 を追加し、プッシュパイプラインの設計と動作方針を記載。
  • その他

    • ビルド/デプロイスクリプトと ignore 設定を更新し、パイプライン実行ファイルの扱いを反映。

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
📝 Walkthrough
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed プルリクエストのタイトル「feat(hooks): push 前パイプラインハーネスの実装」は、変更の主な内容(push 前に実行されるパイプラインハーネスの実装)を明確に要約しており、変更内容と完全に一致しています。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
.claude/hooks-pre-tool-validate/src/main.rs (1)

234-265: 環境変数プレフィックスによるバイパスの可能性

preset_git() (line 125) では (?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)* を含めて環境変数プレフィックスや command/env ビルトイン経由の実行をブロックしています。しかし preset_jj_push_guard() にはこの対策がありません。

以下のコマンドがバイパスできる可能性があります:

  • JJ_TRACE=1 jj git push
  • command jj git push
  • env jj git push
♻️ 環境変数プレフィックス対策の追加
 BlockedPattern {
-    pattern: Regex::new(r#"(?im)(^|&&|;|\|\||\||&)\s*jj\s+git\s+push(\s|$)"#).unwrap(),
+    pattern: Regex::new(r#"(?im)(^|&&|;|\|\||\||&)\s*(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)*jj\s+git\s+push(\s|$)"#).unwrap(),
     message: r#"**jj git push がブロックされました**

同様に jj push のパターンにも適用してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/hooks-pre-tool-validate/src/main.rs around lines 234 - 265, The jj
push guard patterns in preset_jj_push_guard() lack the
environment-variable/command/env prefix protection present in preset_git();
update both Regex::new(...) patterns (the ones matching `jj\s+git\s+push` and
`jj\s+push`) to include the same prefix group
`(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)*` at the start of the
pattern so invocations like `JJ_TRACE=1 jj git push`, `command jj git push`, or
`env jj push` are blocked the same way as in preset_git().
docs/adr/adr-008-push-pipeline-harness.md (1)

35-53: コードブロックに言語指定を追加してください。

markdownlint により、fenced code block に言語指定がないことが検出されています(MD040)。アーキテクチャ図として text または明示的な指定を追加してください。

📝 言語指定の追加
-```
+```text
 Claude が "jj git push" を実行しようとする
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/adr/adr-008-push-pipeline-harness.md` around lines 35 - 53, The fenced
code block in adr-008-push-pipeline-harness.md that contains the architecture
diagram (the block beginning with the line "Claude が \"jj git push\" を実行しようとする")
must include a language spec to satisfy markdownlint MD040; update the opening
triple-backtick to include a language such as text (e.g., ```text) so the
diagram block is explicitly marked and the linter error is resolved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/hooks-push-pipeline/src/main.rs:
- Around line 196-197: The current unwrap_or_else on pipeline.push_cmd accepts
empty or whitespace-only strings; update the logic that builds push_cmd (and the
similar cmd handling at the other block) to trim whitespace and treat an empty
result as missing so the DEFAULT_PUSH_CMD is used: when reading
pipeline.push_cmd, trim() the string and if the trimmed string is non-empty use
it, otherwise fall back to DEFAULT_PUSH_CMD (same change for the other variable
named cmd/ pipeline.cmd), ensuring both whitespace-only and empty strings are
replaced by the default.

In `@package.json`:
- Line 16: The "push" npm script currently uses Windows-only backslash and .exe
(".\\.claude\\hooks-push-pipeline.exe"), which breaks on Unix; update the "push"
script in package.json to invoke a cross-platform path or wrapper (for example
"./.claude/hooks-push-pipeline" without backslashes/extension) or implement
platform-specific scripts like "push:win" and "push:unix" and have "push"
dispatch to the correct one (or use a small Node wrapper that selects the
correct binary), referencing the "push" script name and the
".claude/hooks-push-pipeline" binary to locate the change.

---

Nitpick comments:
In @.claude/hooks-pre-tool-validate/src/main.rs:
- Around line 234-265: The jj push guard patterns in preset_jj_push_guard() lack
the environment-variable/command/env prefix protection present in preset_git();
update both Regex::new(...) patterns (the ones matching `jj\s+git\s+push` and
`jj\s+push`) to include the same prefix group
`(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+|command\s+|env\s+)*` at the start of the
pattern so invocations like `JJ_TRACE=1 jj git push`, `command jj git push`, or
`env jj push` are blocked the same way as in preset_git().

In `@docs/adr/adr-008-push-pipeline-harness.md`:
- Around line 35-53: The fenced code block in adr-008-push-pipeline-harness.md
that contains the architecture diagram (the block beginning with the line
"Claude が \"jj git push\" を実行しようとする") must include a language spec to satisfy
markdownlint MD040; update the opening triple-backtick to include a language
such as text (e.g., ```text) so the diagram block is explicitly marked and the
linter error is resolved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8eefad1c-2540-404e-b3ed-db1abdc1742f

📥 Commits

Reviewing files that changed from the base of the PR and between cdd1551 and 9d9bcb9.

⛔ Files ignored due to path filters (1)
  • .claude/hooks-push-pipeline/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .claude/hooks-config.toml
  • .claude/hooks-pre-tool-validate/src/main.rs
  • .claude/hooks-push-pipeline/Cargo.toml
  • .claude/hooks-push-pipeline/src/main.rs
  • .gitignore
  • CLAUDE.md
  • docs/adr/adr-008-push-pipeline-harness.md
  • package.json
  • scripts/deploy-hooks.ts

Comment thread .claude/hooks-push-pipeline/src/main.rs Outdated
Comment thread package.json
"build:hooks-settings": "node -e \"const fs=require('fs');const t=fs.readFileSync('.claude/settings.local.json.template','utf8');const p=process.cwd().replace(/\\\\/g,'\\\\\\\\');fs.writeFileSync('.claude/settings.local.json',t.replace(/\\{\\{PROJECT_DIR\\}\\}/g,p))\" && echo settings.local.json generated",
"build:hooks": "pnpm build:hooks-pre-tool-validate && pnpm build:hooks-post-tool-linter && pnpm build:hooks-stop-quality && pnpm build:hooks-settings",
"build:hooks": "pnpm build:hooks-pre-tool-validate && pnpm build:hooks-post-tool-linter && pnpm build:hooks-stop-quality && pnpm build:hooks-push-pipeline && pnpm build:hooks-settings",
"push": ".\\.claude\\hooks-push-pipeline.exe",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

クロスプラットフォーム互換性の確認が必要です。

push スクリプトで Windows 固有のパス構文 (.\) を使用しています。Unix 系システム(Linux/macOS)では ./ が必要であり、現在の構文では動作しません。

.exe 拡張子から Windows 専用を意図している可能性がありますが、クロスプラットフォーム対応が必要な場合は以下の修正を検討してください:

🔧 クロスプラットフォーム対応の提案
-    "push": ".\\.claude\\hooks-push-pipeline.exe",
+    "push": "node -e \"require('child_process').execSync(process.platform === 'win32' ? '.\\\\.claude\\\\hooks-push-pipeline.exe' : './.claude/hooks-push-pipeline', {stdio: 'inherit'})\"",

または、Unix 用にはシンボリックリンクや拡張子なしのバイナリを用意する方法もあります。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 16, The "push" npm script currently uses Windows-only
backslash and .exe (".\\.claude\\hooks-push-pipeline.exe"), which breaks on
Unix; update the "push" script in package.json to invoke a cross-platform path
or wrapper (for example "./.claude/hooks-push-pipeline" without
backslashes/extension) or implement platform-specific scripts like "push:win"
and "push:unix" and have "push" dispatch to the correct one (or use a small Node
wrapper that selects the correct binary), referencing the "push" script name and
the ".claude/hooks-push-pipeline" binary to locate the change.

PreToolUse の jj-push-guard プリセットで直接の push をブロックし、
hooks-push-pipeline (スタンドアロン Rust exe) で push 前パイプラインを
実行する2段構成で、Claude Code hooks に存在しない push hook を補完する。

- PreToolUse: jj-push-guard プリセット追加
- hooks-push-pipeline: command 型/ai 型ステップの順次実行 + 最終 push
- hooks-config.toml: [push_pipeline] セクション追加
- ビルド・配布統合: package.json, .gitignore, deploy-hooks.ts 更新
- ADR-008: 設計判断を記録

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

fix: CodeRabbit レビュー指摘4件を修正

- push_cmd/cmd の空文字バリデーション追加 (#1 Major)
- jj-push-guard に環境変数プレフィックスバイパス対策 (#3 Nitpick)
- ADR-008 コードブロックに言語指定追加 (#4 Nitpick)
- hooks-push-pipeline 全関数に docstring 追加 (#5 Pre-merge)
@aloekun
aloekun force-pushed the feat/push-pipeline-harness branch from 9d9bcb9 to 9150913 Compare March 30, 2026 13:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.claude/hooks-push-pipeline/src/main.rs (1)

106-114: Windows 固有の cmd /c を使用しているため、Linux/macOS では動作しません

Command::new("cmd").args(["/c", cmd]) は Windows 専用です。クロスプラットフォーム対応が必要な場合は、OS に応じてシェルを切り替える必要があります。

現在の設計が Windows 専用で意図的であれば問題ありませんが、将来的な移植性を考慮する場合は修正を検討してください。

♻️ クロスプラットフォーム対応案
-    let mut child = match Command::new("cmd")
-        .args(["/c", cmd])
+    #[cfg(windows)]
+    let mut child = match Command::new("cmd")
+        .args(["/c", cmd])
+        .stdout(std::process::Stdio::piped())
+        .stderr(std::process::Stdio::piped())
+        .spawn()
+    {
+        Ok(c) => c,
+        Err(e) => return (false, format!("Failed to execute {}: {}", cmd, e)),
+    };
+
+    #[cfg(not(windows))]
+    let mut child = match Command::new("sh")
+        .args(["-c", cmd])
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
         .spawn()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/hooks-push-pipeline/src/main.rs around lines 106 - 114, The code
currently spawns a process with Windows-only Command::new("cmd").args(["/c",
cmd]) which breaks on Linux/macOS; change the spawn logic to choose the shell
based on the target OS (e.g., if cfg!(windows) use
Command::new("cmd").args(&["/c", cmd]) else use Command::new("sh").args(&["-c",
cmd])) and then call .stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() the
same way; keep the same error handling (the Err(e) => return ...) and the
variable names (child, cmd) so the rest of the function remains unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.claude/hooks-push-pipeline/src/main.rs:
- Around line 106-114: The code currently spawns a process with Windows-only
Command::new("cmd").args(["/c", cmd]) which breaks on Linux/macOS; change the
spawn logic to choose the shell based on the target OS (e.g., if cfg!(windows)
use Command::new("cmd").args(&["/c", cmd]) else use
Command::new("sh").args(&["-c", cmd])) and then call
.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() the same way; keep the
same error handling (the Err(e) => return ...) and the variable names (child,
cmd) so the rest of the function remains unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b2e6b497-f318-43af-8055-f6b0e1b1abc3

📥 Commits

Reviewing files that changed from the base of the PR and between 9d9bcb9 and 9150913.

⛔ Files ignored due to path filters (1)
  • .claude/hooks-push-pipeline/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .claude/hooks-config.toml
  • .claude/hooks-pre-tool-validate/src/main.rs
  • .claude/hooks-push-pipeline/Cargo.toml
  • .claude/hooks-push-pipeline/src/main.rs
  • .gitignore
  • CLAUDE.md
  • docs/adr/adr-008-push-pipeline-harness.md
  • package.json
  • scripts/deploy-hooks.ts
✅ Files skipped from review due to trivial changes (3)
  • CLAUDE.md
  • .claude/hooks-push-pipeline/Cargo.toml
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (4)
  • scripts/deploy-hooks.ts
  • package.json
  • .claude/hooks-config.toml
  • docs/adr/adr-008-push-pipeline-harness.md

@aloekun
aloekun merged commit 3984686 into master Mar 30, 2026
1 check passed
@aloekun
aloekun deleted the feat/push-pipeline-harness branch March 30, 2026 13:44
aloekun added a commit that referenced this pull request Apr 18, 2026
…プト化 + prepare-pr-body helper 新設 (PR-B)

ADR-028 の二次防衛層を実装。`permissions.ask` の "ask > allow" precedence により、auto mode でも毎回確認プロンプトが発火するようになる。

- `.claude/settings.json`: `permissions.ask` に 4 パターン追加
  - `Bash(pnpm create-pr*)`, `Bash(pnpm merge-pr*)`
  - `Bash(*cli-pr-monitor.exe*)`, `Bash(*cli-merge-pipeline.exe*)`
- `.claude/settings.local.json.template`: `pnpm create-pr` / `merge-pr` の allow 登録を削除 (ADR-028 の二層防衛と矛盾する誤解を招くため)
- `scripts/prepare-pr-body.ps1` 新規: stdin から PR body を受け取り `.tmp-pr-body.md` に UTF-8 (BOM なし) で書き出す helper。`-Cleanup` で削除
- `package.json`: `prepare-pr-body` / `prepare-pr-body:cleanup` スクリプト追加
- `.gitignore`: `.tmp-pr-body.md` を除外 (一時ファイル)
- `docs/adr/adr-028-pnpm-create-pr-gate.md`: PR-B 実装済みに反映、PR-D 参照番号を更新
- `docs/todo.md`: PR-B タスク削除 + 後続リナンバー (PR-C → #7, PR-D → #8)

smoke test:
- `echo "日本語" | pnpm prepare-pr-body` で UTF-8 が保持されることを確認
- `pnpm prepare-pr-body:cleanup` で `.tmp-pr-body.md` 削除動作を確認

refs: ADR-028, PR #56 (PR-A, ADR 集約)
aloekun added a commit that referenced this pull request Apr 18, 2026
…プト化 + prepare-pr-body helper 新設 (PR-B)

ADR-028 の二次防衛層を実装。`permissions.ask` の "ask > allow" precedence により、auto mode でも毎回確認プロンプトが発火するようになる。

- `.claude/settings.json`: `permissions.ask` に 4 パターン追加
  - `Bash(pnpm create-pr*)`, `Bash(pnpm merge-pr*)`
  - `Bash(*cli-pr-monitor.exe*)`, `Bash(*cli-merge-pipeline.exe*)`
- `.claude/settings.local.json.template`: `pnpm create-pr` / `merge-pr` の allow 登録を削除 (ADR-028 の二層防衛と矛盾する誤解を招くため)
- `scripts/prepare-pr-body.ps1` 新規: stdin から PR body を受け取り `.tmp-pr-body.md` に UTF-8 (BOM なし) で書き出す helper。`-Cleanup` で削除
- `package.json`: `prepare-pr-body` / `prepare-pr-body:cleanup` スクリプト追加
- `.gitignore`: `.tmp-pr-body.md` を除外 (一時ファイル)
- `docs/adr/adr-028-pnpm-create-pr-gate.md`: PR-B 実装済みに反映、PR-D 参照番号を更新
- `docs/todo.md`: PR-B タスク削除 + 後続リナンバー (PR-C → #7, PR-D → #8)

smoke test:
- `echo "日本語" | pnpm prepare-pr-body` で UTF-8 が保持されることを確認
- `pnpm prepare-pr-body:cleanup` で `.tmp-pr-body.md` 削除動作を確認

refs: ADR-028, PR #56 (PR-A, ADR 集約)
aloekun added a commit that referenced this pull request Apr 18, 2026
…プト化 + prepare-pr-body helper 新設 (PR-B) (#57)

ADR-028 の二次防衛層を実装。`permissions.ask` の "ask > allow" precedence により、auto mode でも毎回確認プロンプトが発火するようになる。

- `.claude/settings.json`: `permissions.ask` に 4 パターン追加
  - `Bash(pnpm create-pr*)`, `Bash(pnpm merge-pr*)`
  - `Bash(*cli-pr-monitor.exe*)`, `Bash(*cli-merge-pipeline.exe*)`
- `.claude/settings.local.json.template`: `pnpm create-pr` / `merge-pr` の allow 登録を削除 (ADR-028 の二層防衛と矛盾する誤解を招くため)
- `scripts/prepare-pr-body.ps1` 新規: stdin から PR body を受け取り `.tmp-pr-body.md` に UTF-8 (BOM なし) で書き出す helper。`-Cleanup` で削除
- `package.json`: `prepare-pr-body` / `prepare-pr-body:cleanup` スクリプト追加
- `.gitignore`: `.tmp-pr-body.md` を除外 (一時ファイル)
- `docs/adr/adr-028-pnpm-create-pr-gate.md`: PR-B 実装済みに反映、PR-D 参照番号を更新
- `docs/todo.md`: PR-B タスク削除 + 後続リナンバー (PR-C → #7, PR-D → #8)

smoke test:
- `echo "日本語" | pnpm prepare-pr-body` で UTF-8 が保持されることを確認
- `pnpm prepare-pr-body:cleanup` で `.tmp-pr-body.md` 削除動作を確認

refs: ADR-028, PR #56 (PR-A, ADR 集約)
aloekun added a commit that referenced this pull request Apr 19, 2026
ADR-028 (外部可視成果物の生成コマンドの実行ゲート) の運用フローを skill として明文化。

`pnpm push` 完了後の PR 作成で以下の 7 ステップを標準化する:

1. jj status + jj log -r master..@ で差分/bookmark 確認
2. commit description から PR title 初稿生成 (70 文字超は短縮、conventional prefix 維持)
3. diff + commit log から PR body 初稿生成 (Summary/Context/Validation/References)
4. Claude が提示 → AskUserQuestion で明示承認 (auto mode でも必須)
5. `pnpm prepare-pr-body` 経由で `.tmp-pr-body.md` に書き出し
6. `pnpm create-pr --title ... --body-file ...` foreground 実行 (permissions.ask で再確認)
7. `pnpm prepare-pr-body:cleanup` で一時ファイル削除

## 設計ハイライト

- **ADR-028 二層防衛の活用**: skill 内の AskUserQuestion (一次) + permissions.ask (二次)
- **user-supplied text の尊重** (ADR-022): 承認済 draft の二重書き換えを禁止
- **body は必ず一時ファイル経由**: `--body "..."` 引数経由の切り詰めリスク回避 (PR #51 / memory `feedback_pnpm_create_pr_body.md`)
- **automated actor から独立**: takt / cli-* の自律ループはこの skill を呼ばない

## ステータス

試験運用 (2026-04-19〜)。発火頻度・UX を半年観察して正式採用 / 改良 / 廃止を判断する。

## ファイル

- `.claude/skills/prepare-pr/SKILL.md` 新設 (178 行)

## docs/todo.md

- PR-D 完了に伴い「セッション 247510ea 由来: 整備タスク群」ブロック全体を削除
- 雑務 task をリナンバー (#8#7)

refs: ADR-028, ADR-022, PR #57 (PR-B body helper), memory `feedback_bookmark_auto_naming.md`
aloekun added a commit that referenced this pull request Apr 19, 2026
ADR-028 (外部可視成果物の生成コマンドの実行ゲート) の運用フローを skill として明文化。

`pnpm push` 完了後の PR 作成で以下の 7 ステップを標準化する:

1. jj status + jj log -r master..@ で差分/bookmark 確認
2. commit description から PR title 初稿生成 (70 文字超は短縮、conventional prefix 維持)
3. diff + commit log から PR body 初稿生成 (Summary/Context/Validation/References)
4. Claude が提示 → AskUserQuestion で明示承認 (auto mode でも必須)
5. `pnpm prepare-pr-body` 経由で `.tmp-pr-body.md` に書き出し
6. `pnpm create-pr --title ... --body-file ...` foreground 実行 (permissions.ask で再確認)
7. `pnpm prepare-pr-body:cleanup` で一時ファイル削除

## 設計ハイライト

- **ADR-028 二層防衛の活用**: skill 内の AskUserQuestion (一次) + permissions.ask (二次)
- **user-supplied text の尊重** (ADR-022): 承認済 draft の二重書き換えを禁止
- **body は必ず一時ファイル経由**: `--body "..."` 引数経由の切り詰めリスク回避 (PR #51 / memory `feedback_pnpm_create_pr_body.md`)
- **automated actor から独立**: takt / cli-* の自律ループはこの skill を呼ばない

## ステータス

試験運用 (2026-04-19〜)。発火頻度・UX を半年観察して正式採用 / 改良 / 廃止を判断する。

## ファイル

- `.claude/skills/prepare-pr/SKILL.md` 新設 (178 行)

## docs/todo.md

- PR-D 完了に伴い「セッション 247510ea 由来: 整備タスク群」ブロック全体を削除
- 雑務 task をリナンバー (#8#7)

refs: ADR-028, ADR-022, PR #57 (PR-B body helper), memory `feedback_bookmark_auto_naming.md`
aloekun added a commit that referenced this pull request Apr 19, 2026
ADR-028 (外部可視成果物の生成コマンドの実行ゲート) の運用フローを skill として明文化。

`pnpm push` 完了後の PR 作成で以下の 7 ステップを標準化する:

1. jj status + jj log -r master..@ で差分/bookmark 確認
2. commit description から PR title 初稿生成 (70 文字超は短縮、conventional prefix 維持)
3. diff + commit log から PR body 初稿生成 (Summary/Context/Validation/References)
4. Claude が提示 → AskUserQuestion で明示承認 (auto mode でも必須)
5. `pnpm prepare-pr-body` 経由で `.tmp-pr-body.md` に書き出し
6. `pnpm create-pr --title ... --body-file ...` foreground 実行 (permissions.ask で再確認)
7. `pnpm prepare-pr-body:cleanup` で一時ファイル削除

## 設計ハイライト

- **ADR-028 二層防衛の活用**: skill 内の AskUserQuestion (一次) + permissions.ask (二次)
- **user-supplied text の尊重** (ADR-022): 承認済 draft の二重書き換えを禁止
- **body は必ず一時ファイル経由**: `--body "..."` 引数経由の切り詰めリスク回避 (PR #51 / memory `feedback_pnpm_create_pr_body.md`)
- **automated actor から独立**: takt / cli-* の自律ループはこの skill を呼ばない

## ステータス

試験運用 (2026-04-19〜)。発火頻度・UX を半年観察して正式採用 / 改良 / 廃止を判断する。

## ファイル

- `.claude/skills/prepare-pr/SKILL.md` 新設 (178 行)

## docs/todo.md

- PR-D 完了に伴い「セッション 247510ea 由来: 整備タスク群」ブロック全体を削除
- 雑務 task をリナンバー (#8#7)

refs: ADR-028, ADR-022, PR #57 (PR-B body helper), memory `feedback_bookmark_auto_naming.md`
aloekun added a commit that referenced this pull request Apr 19, 2026
ADR-028 (外部可視成果物の生成コマンドの実行ゲート) の運用フローを skill として明文化。

`pnpm push` 完了後の PR 作成で以下の 7 ステップを標準化する:

1. jj status + jj log -r master..@ で差分/bookmark 確認
2. commit description から PR title 初稿生成 (70 文字超は短縮、conventional prefix 維持)
3. diff + commit log から PR body 初稿生成 (Summary/Context/Validation/References)
4. Claude が提示 → AskUserQuestion で明示承認 (auto mode でも必須)
5. `pnpm prepare-pr-body` 経由で `.tmp-pr-body.md` に書き出し
6. `pnpm create-pr --title ... --body-file ...` foreground 実行 (permissions.ask で再確認)
7. `pnpm prepare-pr-body:cleanup` で一時ファイル削除

## 設計ハイライト

- **ADR-028 二層防衛の活用**: skill 内の AskUserQuestion (一次) + permissions.ask (二次)
- **user-supplied text の尊重** (ADR-022): 承認済 draft の二重書き換えを禁止
- **body は必ず一時ファイル経由**: `--body "..."` 引数経由の切り詰めリスク回避 (PR #51 / memory `feedback_pnpm_create_pr_body.md`)
- **automated actor から独立**: takt / cli-* の自律ループはこの skill を呼ばない

## ステータス

試験運用 (2026-04-19〜)。発火頻度・UX を半年観察して正式採用 / 改良 / 廃止を判断する。

## ファイル

- `.claude/skills/prepare-pr/SKILL.md` 新設 (178 行)

## docs/todo.md

- PR-D 完了に伴い「セッション 247510ea 由来: 整備タスク群」ブロック全体を削除
- 雑務 task をリナンバー (#8#7)

refs: ADR-028, ADR-022, PR #57 (PR-B body helper), memory `feedback_bookmark_auto_naming.md`
aloekun added a commit that referenced this pull request May 9, 2026
…必須 follow-up (順位 91 + 92 + 93) (#135)

* feat(cli-finding-classifier, cli-push-runner): Bundle i — Phase d 着手前必須 follow-up (順位 91 + 92 + 93)

PR #132 (Phase c MVP land) の post-merge-feedback で採用された 3 件を 1 PR にまとめて land。

## 順位 91 (Tier 2 #4): [lint_screen] config parse test
- src/cli-push-runner/src/config.rs に 5 tests を追加
- silent field rename / 追加で None fallback する failure mode を unit test で防止
- full fields / minimal only enabled / absent yields None / numeric defaults / string defaults の 5 軸独立検証

## 順位 92 (Tier 2 #5): scale-aware eval fixtures (200+ 行)
- eval13-large-refactor-real.diff (5 file / 280 行) — context 限界 + JSON 完全性
- eval14-mid-mixed.diff (3 file / 153 行) — mid-scale recall 安定性
- eval15-syntax-stress.diff (1 file / 208 行) — 単 file 長尺の schema 完全性
- lint-screen-evals.json に id 13/14/15 baseline (auto_fix lane × 13 findings 合計) 追加
- count test を rename + 上限緩和 (eval_set_loads_and_has_at_least_phase_b_prime_baseline_count)
- Bundle i 実体スモーク test (eval_set_includes_bundle_i_scale_aware_fixtures) 追加

### dogfood 結果 (mistral:7b / temperature=0)

agreement = 11/15 = 73.3% (Phase b' 75% から marginal 劣化 = fixture が設計通り failure mode を再現)

eval13 (280 行): JSON parse error 'missing field screen_decision' → fallback path 作動
  = PR #132 smoke (868 行 diff) で観測した failure mode を decisive に再現
eval15 (208 行): JSON parse error 'missing field severity at line 38'
  = nested field omission の別 failure mode を新規捕捉
eval14 (153 行): JSON 完全だが recall 33% (3 baseline 中 1 件のみ TP)

aggregate precision=76.2% recall=51.6% latency p50=4591ms p95=8370ms verdict CONDITIONAL-GO

agreement < 75% 未達理由は eval13/15 の fallback (= fixture が設計通り作動した結果) で
mechanical に説明可能。Phase d 投入前の必須 measurement を取得 (todo6.md L164 「未達理由が
文書化される」branch を満たす)。§8.D v4 prompt 改訂は別 bundle に切り出し。

## 順位 93 (Tier 3 #8): coding-style.md partial fix anti-pattern codify
- ~/.claude/rules/common/coding-style.md § Cross-File Reference Lifecycle に
  「変更差分外への partial fix 再発」anti-pattern を追加
- PR #94 / #111 / #132 を inline cite (実証ベース)
- family_tag を grep -rn で全 path 検索する対処手順、partial fix の意図的切り出しを明記

## Phase d 着手の前提条件 update

Bundle i land で以下が揃った:
- (a) [lint_screen] config silent failure 防止 (順位 91)
- (b) scale-aware fixtures による failure mode の reproducible measurement (順位 92)
- (c) cross-file partial fix anti-pattern の global rule 化 (順位 93)

次は §8.D v4 prompt 改訂で大規模 diff の JSON 完全性を改善するループ (Phase d 着手前の最終 gate)。

* fix(cli-finding-classifier): CodeRabbit Major #r3213115045 — eval count 下限を Bundle i baseline 15 に固定

>=12 だと既存 fixture 削除を検出できないため >= 15 に変更し regression 防止。
将来の fixture 追加 (>15) は許容。
aloekun added a commit that referenced this pull request May 25, 2026
…jj split の -m 必須化 (順位 144) (#172)

* docs(todo): 順位 143 + 144 新規追加 — PR #171 post-merge-feedback 採用 2 件 (Bundle 171: T2-#4 fixture helper + T3-#8 jj hook)

* refactor(hooks-pre-tool-validate): BlockedPattern に exception field 追加 (順位 144 Phase 1)

* feat(hooks-pre-tool-validate): jj-message-required preset 追加 (順位 144 Phase 2)

* test(hooks-pre-tool-validate): block/allow/non-regression test 拡充 (順位 144 Phase 3)

* chore(hooks): build + deploy hooks-pre-tool-validate exe + 順位 144 entry 削除 (Phase 4)
aloekun added a commit that referenced this pull request Aug 6, 2026
…DR-071) (#361)

* feat(autonomy-policy): 未マージ draft 数の背圧を判定コアの入力にする

WP-18 PR 1 (1/5)。ADR-052 原則 5 が自動実行可クラスの前提条件とする背圧を、
判定コア `lib-autonomy-policy` が入力として受け取れるようにする。

## 変更の要点

`Operation::backpressure_connected()` の `DraftPr => false` 固定を廃し、背圧の
**指標の要求**と**指標の実測値**を別の層へ分けた:

- `Operation::requires_draft_backpressure()` — 操作クラスが「未マージ draft 数」を
  要求するかだけを持つ静的分類。状態は持たない
- `GateInputs::open_draft_prs` / `max_open_draft_prs` — 実測値と閾値。どちらか一方でも
  `None` なら `backpressure-unavailable` で deny (fail-closed)

背圧の状態を enum 側にも持たせると `GateInputs` と二重管理になり判定経路が分岐するため、
状態の保持先は `GateInputs` 1 箇所に限定した (計画書 WP-18 PR 1 の明示要件)。

`FixPush` が draft 数を要求しないのは背圧が無いからではなく、その背圧が cli-pr-monitor の
有界 retry (`max_retries`) で、呼び出しごとに gate へ渡す状態を持たないため。この非対称を
doc コメントとテスト (`fix_push_is_unaffected_by_draft_backpressure_inputs`) の両方で固定した。

## 追加した deny 理由

`DenyReason::BackpressureSaturated { open, limit }` (code = `backpressure-saturated`)。
飽和判定は `>=`。閾値は「これ以上は積まない」上限であり、`limit = 0` は「draft を 1 件も
作らない」= 実質停止を意味する。

`describe_sources` の背圧表記は `structural` / `ok(N/M)` / `saturated(N/M)` / `unavailable`
の 4 状態で、実数を必ず併記する。run log 1 行で「数え損ねて止まった」と「積み過ぎて
止まった」を切り分けられるようにするため。

## テスト

判定コアのテストを 16 → 22 件へ。網羅走査テストは背圧 4 パターンを軸に加えて 54 → 216
組合せになり、許可される組合せ数も式で固定した (truthy 表記数 × (fix-push 4 + draft-pr 1))。
境界 (`open == limit` で止まる)、`limit = 0`、kill-switch が背圧より先に効くこと、
背圧入力の欠落 3 パターンをそれぞれ独立に pin している。

## 本コミットの範囲

呼び手 2 件 (`cli-autonomy-gate` / `cli-fix-push-gate`) は本コミットでは `None` を渡す。
`draft-pr` は従来どおり deny のままで、運用挙動は変わらない。config からの閾値読み取りは
2/5、実測 draft 数の受け口は 3/5 で接続する。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(autonomy-policy): 背圧閾値 max_open_draft_prs を config から 1 回の read で取り込む

WP-18 PR 1 (2/5)。背圧の閾値を `autonomy-config.toml` の `[autonomy]
max_open_draft_prs` に置き、kill-switch フラグと同じ信頼境界 (master ref の写し) と
同じ「欠損 → 停止」極性に乗せる。

## read を 1 回に集約した理由

`read_repo_config_enabled(path) -> Option<bool>` を `read_repo_config(path) -> RepoConfig`
へ置き換えた。キーごとに読み取り関数を生やすとファイルを 2 回読むことになり、2 回の read の
間に config が差し替わると「kill-switch は旧世代・閾値は新世代」という混成状態で判定しうる。
1 回の read から派生した値だけを使う。

## 半壊 config は全フィールドが停止側へ倒れる

toml の parse はファイル単位なので、`max_open_draft_prs` が型違い (文字列 / 負値 / 小数) だと
`enabled` も含めて全フィールドが `None` になる。これは意図した挙動で、config の一部が壊れた
状態を「kill-switch だけ有効」で運転させない (ADR-043 fail-closed)。テストで明示的に固定した。

閾値キー欠落時に既定値 (3 など) へ倒さないのも同じ理由。書き忘れが「勝手に 3 件まで作る」
という fail-open にならないよう、欠落は背圧未接続 = draft PR deny とする。

一方で未知キーは無視する (`unknown_keys_are_ignored`)。config に新しい設定を足したときに、
未更新のバイナリが parse 失敗で全停止するのは安全側に振りすぎるため。

## テスト

sources のテストを 6 → 11 件へ。閾値の正常読み取り (0 を含む)、キー欠落が既定値でないこと、
型違い 3 種が kill-switch フラグごと停止側へ倒すこと、未知キー無視を追加した。

## 本コミットの範囲

`cli-autonomy-gate` は閾値を `GateInputs` へ渡すようになったが、実測 draft 数の受け口
(`--open-draft-prs`) はまだ無いため `draft-pr` は `backpressure-unavailable` で deny のまま。
接続は 3/5 で行う。`cli-fix-push-gate` は `enabled` しか使わないため呼び出し形の追従のみ。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat(autonomy-gate): --open-draft-prs で実測 draft 数を受け取り背圧を接続する

WP-18 PR 1 (3/5)。判定コア (1/5) と閾値 (2/5) に続き、背圧の残り 1 入力である
**実測件数**の受け口を CLI に開ける。これで `draft-pr` が構造的 deny を脱し、
ADR-052 原則 5 の契約を満たした状態でのみ許可されるようになる。

## 引数の扱い

`--open-draft-prs <count>` は省略可能。`fix-push` では判定に使わないためで、`draft-pr` で
省略した場合は `None` = 背圧未接続として deny に倒れる。**省略が許可へ倒れる経路は無い**。

値のパースは `u32` で、空文字 / 負値 / 小数 / 非数値 / 末尾空白は引数不正 (exit 2) として
弾く。呼び手の `gh api` が失敗したときの出力 (空文字など) を 0 件と読み違えて「draft が
1 件も無いので作ってよい」に倒れるのが最悪の failure mode なので、ここは黙って `None` へ
潰さず loud に落とす。

`--config` / `--operation` の必須性は従来どおり。引数ループは値取得を各 arm へ寄せ、
未知フラグの判定を値の有無より先に行う既存の順序を保った。

## テスト

引数解析のテストを 4 → 7 件へ。省略時が `None` であること、`0` が正しく `Some(0)` として
読まれること (「0 件」と「数えられなかった」を型で区別する契約)、不正値 5 種が引数不正で
あることを固定した。

## 本コミットの範囲

exe 単体では背圧が接続された。実測値を渡す呼び手 (夜間 workflow の `gh api` step) は
**本 PR には含まれず PR 3 で追加する** (ADR-069 chain 宣言 → 計画書 § WP-18 と ADR-071 を
5/5 で更新)。実 exe による drill は 4/5。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(adr-071): 未マージ draft PR 数による背圧を起票し drill 12 シナリオを記録

WP-18 PR 1 (4/5)。1/5〜3/5 で実装した背圧の設計判断・試験運用条件・実測を永続化する。

## 記録した決定 5 件

1. 指標は「未マージ draft PR 数 (claude/ prefix)」。run 回数や経過時間のような代理指標
   ではなく、止めたい事象 (人間が捌けない量の未処理成果物) を直接数える
2. 背圧の状態は GateInputs だけが持つ。enum 側にも状態を置くと判定経路が二股に分かれ、
   「テストは通るが実運用では別の枝を通る」drift を生む
3. 閾値は autonomy-config.toml の max_open_draft_prs、判定は >=。0 は draft-pr クラス
   だけの停止、キー欠落は既定値ではなく deny
4. 数えるのは呼び手 (workflow step の gh api)、判断するのは gate。exe が gh に依存すると
   ローカル drill が GitHub 到達性に依存し、安全装置の再現可能な検証ができなくなる
5. 数えられなかったことは 0 件ではない。不正値は exit 2 で loud に落とす

決定 4 の「呼び手が数を偽れる」問題は、schedule イベントが default branch の workflow 定義を
使うという GitHub の仕様で担保される (ADR-066 の config master ref 契約と同じ信頼境界)。

## 外部 SaaS の課金・上限事実を移管 (計画書 § 2 の退役条件 2)

2026-08-06 に最新値を再確認して永続化した。

- public リポジトリ + standard runner の Actions 実行は無料・分数無制限 (GitHub 公式 docs で
  原文確認)。GitHub Free の 2,000 分/月は private のみ
- claude-code-action@v1 は claude_code_oauth_token で Max 枠を消費。OAuth token は個人
  サブスクに紐づき、自動化の消費が対話作業のレート枠を圧迫しうる

要約: Actions の実行時間は無料だが Max 枠は有限。背圧の経済的根拠はここにある。

## 検証記録

release build の実 exe で drill 12 シナリオを実施し全て設計どおりを確認した。#6/#7 の対で
>= 境界、#8 で limit=0 の停止、#9 で fix push が draft 数から独立していること、#10 で
半壊 config が enabled ごと停止することを実バイナリ上で確認している。

unit test は判定コア 22 / sources 11 / 引数解析 7 件。網羅走査 1 件が 216 組合せを走査する。

## bounded lifetime

decision trigger (b)「閾値到達で実際に次の run が止まること」は本 ADR 固有の観測点で、
exe 単体 drill では作れない状態 (夜間ループが実際に 3 件積む必要がある)。期限は 2026-11-06。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(harness-plan): WP-18 PR 1 の完了を記帳し PR 3 への chain 宣言と SaaS 事実の移管を行う

WP-18 PR 1 (5/5)。計画書側の 3 種類の更新。

## 1. PR 1 完了の記帳

- WP-18 節の見出しと全体像表を「着手可」→「着手中(PR 1 実装済み)」へ
- PR 構成 1 に実装時の設計判断を確定記録: 閾値判定の層は (a) gate 内を採用。
  backpressure_connected() は廃し requires_draft_backpressure()(指標の要求のみ)と
  GateInputs の 2 フィールド(状態)へ分離した
- 着手前決定 2 の記述を過去形へ(backpressure_connected() は既に存在しない名前のため、
  現在形のまま残すと ADR-069 決定 1 の「名前一致」要件に反する)
- WP-19 ステップ 2 を「前倒し済み」→「land 済み」へ

## 2. PR chain 宣言(ADR-069 決定 1)

PR 1 が導入した 3 点(--open-draft-prs フラグ / max_open_draft_prs キー / DraftPr の許可
経路)は PR 3 まで呼び手を持たない。抽出↔呼び手のペアリングを表で具体名指定した。

順序を逆にできない根拠も明記した — ADR-052 原則 5 が背圧の接続を draft-pr クラス有効化の
前提条件としているため、背圧が先に land する必要がある(WP-17 の kill-switch 先行と同構造)。

なお PR 1 単体では draft-pr は deny のままで運用挙動は変わらない。

## 3. SaaS 課金・上限事実の移管(退役条件 2 / 順位 117 の 3 ステップ原則)

permanent 側(ADR-071 § 外部 SaaS の課金・上限事実)を先に作成 → 本ファイルの § 2 から
GitHub Actions 課金 2 点と claude-code-action の OAuth 認証を削除し ADR-071 への参照へ
置き換えた。cloud routines の事実群は WP-19 / ADR-070 の担当範囲のため本節に残している。
runner 単価の相対比も private 化時のみ関係するため移管対象外とした。

pnpm lint:docs / markdownlint ともに green。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: CodeRabbit 指摘 2 件に対応 (主張の適用範囲を限定 / 移管済み方針の更新)

PR #361 への CodeRabbit レビュー (Minor 2 件) の反映。どちらも「書いた内容が実態より
広い/古い」型の指摘で、妥当。

## 1. 「draft-pr は deny のまま」の適用範囲を限定 (2 箇所)

指摘は「**両文書**が PR 3 まで draft-pr に許可経路が無いように読める」というもの。実際には
ADR-071 § 検証記録の drill #6 が示すとおり、--open-draft-prs と有効な config を渡せば exe は
allow になる。

正確には**リポジトリ内の自動化経路に --open-draft-prs を渡す呼び手が 1 つも無いため** deny に
なる、が正しい。「運用挙動は変わらない」の根拠を exe の挙動ではなく呼び手の不在へ置き直した。
根拠を取り違えたまま PR 3 で呼び手が入ると、主張だけが stale に残る。

修正箇所は 2 つ:

- docs/adr/adr-071-*.md § 残課題
- docs/harness-improvement-plan.md § WP-18 の PR chain 宣言

計画書側から ADR-071 § 残課題への参照も付け、同じ限定が 1 箇所に集約されるようにした。

## 2. 計画書 § 2 preamble: 移管済みの事実と矛盾する旧方針を更新

旧文は「現時点では ADR 化せず本ファイルに保持する」と書いていたが、同じ blockquote の
次の行で「ADR-071 へ移管済み」と述べており矛盾していた。

方針文を「担当 WP の ADR 起票時に移管し本節から削除する」という恒常ルールへ書き換え、
移管済み / 未移管を別項目に分けた。未移管の内訳 (cloud routines の daily cap / webhook
上限 / GitHub App 必須 / 緑ステータスの意味) も具体名で列挙し、次の担当 WP が何を移管
すべきか読み取れるようにした。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
aloekun added a commit that referenced this pull request Aug 8, 2026
* docs(todo): WP-18 で検出した問題を 4 エントリへ登録 (順位 374-377)

WP-18 (夜間 todo 消化ループ、#361 / #362 / #363) の実装中に pre-push review・
CodeRabbit・ユーザー指摘で検出した問題 9 件のうち、todo 登録が要る 8 件を **実装時の
PR 粒度**で 4 エントリへまとめる。切り分けは 2026-08-06 にユーザー確認済み。

## 評価時の #1 を検証し、todo 登録が不要になった

#1 は「Bash prefix 許可の悪用可能性検証と全経路への横展開」として登録予定だった。
ユーザー指示により登録前に検証したところ、**前提が誤りだった**。

#363 の security review は「`Bash(cargo test:*)` は前方一致でシェルを解釈しないため
`cargo test` に任意コマンドを連結すると通過する」と主張していた。公式ドキュメントは
これを明確に否定している:

  Claude Code is aware of shell operators, so a rule like `Bash(safe-cmd *)` won't give
  it permission to run the command `safe-cmd && other-cmd`. The recognized command
  separators are `&&`, `||`, `;`, `|`, `|&`, `&`, and newlines. A rule must match each
  subcommand independently.

`--allowedTools` も同じルール体系に属する (managed settings の deny を --allowedTools で
上書きできない、と明記されている)。

結果:

- `pr-monitor.yml` の Phase A 分析 agent に**当該の穴は無く、対処不要**。production の
  live な穴という当初の見立ては誤りだった
- 残作業は `ADR-072` 決定 5 の根拠記述の訂正のみで、#363 が open のうちに同 PR へ直接
  反映する。よって todo エントリを立てない

検証結果と経緯はセクション冒頭の対応表に残した。

## この一件自体を教訓として取り込んだ

「レビュー指摘への対応時チェックリスト」エントリに 4 項目目を追加した — **指摘が技術的
前提 (ツールの挙動・仕様) に依拠しているなら、対処より先にその前提を検証する**。とくに
設計変更や他経路への横展開を伴う場合。

今回は未検証の前提のまま (a) agent から Bash を落とす設計変更を行い、(b) それを ADR の
決定として記録し、(c) さらに「同じ形が production にもある」と横展開の警告まで出していた。
一次情報に当たれば 1 回の WebFetch で否定できた。

## 内訳

- 順位 374: WP-18 夜間ループの実走スモーク実施 (Tier 1、評価時の #2/#3)
- 順位 375: レビュー指摘への対応時チェックリスト (Tier 2、評価時の #4/#5/#6 + 今回の #1)
- 順位 376: push-runner の bookmark 自動前進がスタック境界を壊す (Tier 2、評価時の #7)
- 順位 377: 夜間ループの防御を検知から防止へ格上げする判断 (Tier 3、評価時の #8/#9)

## まとめ方の方針

リポジトリの既存バッチ登録 (#350#357 の 24 件を 8 エントリへ) と同じく実装時の PR 粒度で
まとめた。評価時の番号との対応はセクション冒頭に表で残してある。

順位 374 (スモーク) の観測項目は `ADR-072` の実走スモーク節に表があるため、todo 側は
スケジューリングの掛かりだけを持ちチェックリストを複製しない。同じ表を 2 箇所で管理すると
必ず drift する (#362 の post-merge feedback が指摘した single source-of-truth 問題と同型)。

ADR-033 (絶対番号は table のみに保持) に従い、エントリ本文には順位番号を書いていない。
todo20.md は 50KB 閾値内。pnpm lint:docs / markdownlint ともに green。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(todo): #363 post-merge feedback の採用 6 件を登録 (順位 378-383)

WP-18 最終 PR (#363、ADR-072) のマージ後 feedback が Tier 1 に 4 件・Tier 2 に 2 件を
採用候補として挙げた。ユーザー承認 (2026-08-07) を得て登録する。

## 6 件は 1 本の根から出ている

台帳 (docs/claude-code-web-tasks.md) の 内容 / 対象ファイル / 注意 は自由記述のまま
無人 agent のプロンプトへ流入する。agent は $GITHUB_WORKSPACE 全体に書き込め、その
出力は draft PR 本文という公開面に出る。ADR-054 の信頼境界そのもの。

- 378 (XS) 台帳を ADR-035 の docs-only 除外パス表へ追加 — 他 3 件の前提。台帳だけを
  変える PR が緩い評価経路に乗ると、対策そのものを迂回する台帳 PR が通りうる
- 379 (S)  tool scope を work/** へ限定 — ADR-072 決定 7 の改ざん検知が必要になって
  いる根本原因。実装後も検知層は残す (防御を 1 枚に減らす変更ではない)
- 380 (M)  台帳フィールドを untrusted data として明示 framing
- 381 (S)  台帳由来 SUMMARY の draft PR 本文出力に screening
- 382 (M)  injection payload の regression test (380 に依存)
- 383 (S)  is_separator_row のパイプ検証欠落

## 期限を「定常運用開始前」に固定する

実効リスクは現時点では低い — 悪意ある台帳行を master へマージするのはユーザー自身で、
単独運用では外部からの注入経路が無い。ただし夜間ループが定常運用に入り draft PR の
流量が増えると前提が変わるため、無期限の Tier 積みにしない。

順位 374 (実走スモーク) は dry_run で PR を作らないため本件の実害が無く、待たせない。

## 383 は実コードで確認済み

is_table_row は行頭 | を要求するが、is_separator_row は split_cells の結果しか見ない。
split_cells("---") は ["---"] を返し全セルが '-' のみなので真になる。markdown の
水平線がセパレータ行として通る (todo ファイル自身が --- を使っている)。
ADR-072 決定 2 の fail-closed 設計の coverage hole。

## 併せて

順位 374 のスモーク観測項目数を 4 → 8 へ修正した (ADR-072 側の実測と不一致だった)。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(harness-plan): WP-18 を 3 PR マージ済へ更新し残作業 2 系統を明示する

#363 のマージ (2026-08-07) で WP-18 の実装 3 本がすべて land した。

## 「実装完了 = WP 完了」ではないことを表で残す

コードは全部 master にあるが、**夜間ループはまだ 1 度も走っていない**。この状態を
「実装済」の一語で片付けると、次のセッションが受け入れ基準を満たしたものと誤読する。
残作業を 2 系統に分けて表にした:

1. 実走スモーク (順位 374) — 受け入れ基準の中核
2. prompt injection 対策 4 件 (順位 378-381) — 定常運用開始前に必須

## スモークの前提が充足したことを記録

受け入れ基準の表は「(a) workflow が master にある (b) 台帳に無人可マークがある」を
未充足として書いていたが、**両方ともマージで解消した**。残る操作は GitHub UI 側の
AUTONOMY_ENABLED 設定のみなので、その 1 点へ書き換えた。

## 依存関係を明示する

378-381 は 1 本の根 (台帳の自由記述が無検証で agent プロンプトへ流入) から出ている。
一方スモークは dry_run で PR を作らないため本件の実害が無い。したがって
**スモークは 378-381 を待たずに着手してよい**と明記した。次セッションが順序で
迷わないようにするため。

## 未 push の改善 3 点の所在を残す

#363 の最終 push が security REJECT で止まったため、改ざん検知の red 化 / 決定 10 の
色分け表 / 決定 6 の列挙基準が master に載っていない。ローカル bookmark
wp18/unpushed-improvements (fc22403c) に保持していることを記録した。いずれも
可観測性と文書の改善で、fail-closed 自体は master 版でも成立している。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(todo): 外部設定 (GitHub App / variables / secrets) の実体記録を登録 (順位 384)

新セッションでの指摘 (2026-08-07): workflow は vars.NIGHTLY_APP_ID /
secrets.NIGHTLY_APP_PRIVATE_KEY を参照するが、App の作成・インストール・登録を
記録した文書がリポジトリ内に無い。

## 欠けているのは設計根拠ではなく運用実体

ADR-072 決定 8 は「なぜ App token か」「なぜ PAT ではないか (オーナー PAT は
Repository admin として ADR-067 の ruleset backstop を素通りする)」「どの権限を
付けるか (Workflows は付けない)」「なぜ publish 直前に発行するか (token 寿命
1 時間)」を厚く残している。

記録が無いのは以下:

- App を実際に作成した事実・日付・名称・インストール範囲
- NIGHTLY_APP_ID = variable / NIGHTLY_APP_PRIVATE_KEY = secret という登録先の別
- 既存の Claude GitHub App との区別 (あちらは Workflows を含む広い権限を持つ別物)
- 再構築手順 (鍵ローテーション・派生プロジェクト展開)

NIGHTLY_APP の文字列はリポジトリ全体で workflow の 2 行と ADR 残課題の 1 行にしか
現れない。

## これは ADR-051 違反

ADR-051 (クロスシステム設定 coupling) は内部設定と外部 SaaS 設定が論理結合する場合に
(1) 両設定ファイルへの相互参照コメント (2) 期待値の組み合わせ表の ADR 必須記載
(3) 変更は両側を同一 PR、の 3 点を規律として定めている。workflow ↔ GitHub App +
repository variables/secrets はこの型で、3 点とも未実施。

前例として ADR-067 段 0 は repository ruleset を ruleset 名つきで「設定済み」と
記録している。ADR-072 は同じ扱いをしていない。同型の欠落が AUTONOMY_ENABLED にも
あり (ADR-066 は「Actions variable を使う」とは書くが現状値を記録していない)、
本エントリで一緒に扱う。

## 順位 374 と同時実施にする理由

スモークでは AUTONOMY_ENABLED の設定と App token の実動確認のため GitHub UI を
触るので、その過程で実値がすべて揃う。先行して記録しようとすると値が確定せず
二度手間になる。

## 教訓を残す

App の作成手順・Expire user authorization tokens の扱い・既存 App との違いは
2026-08-07 のセッションでユーザーへ提示したが、リポジトリへ残さなかった。会話は
次のセッションに残らないが workflow は残る。参照だけが残って由来が消える状態を
作った。順位 375 と同じクラスの失敗としてエントリ本文に記録した。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(review): CodeRabbit Major 4 件を妥当性判定のうえ 3 件へ対応する (#364)

自動 fix 経路 (f698d19) が 1 件 (#4 台帳への非記録ルール追加) を対応済みで、
本コミットはその内容を含んだうえで残りを手で対応した結果である。同一ファイルの
近接行のため path 単位で分離できず 1 コミットに畳み込まれている。

## 妥当性の判定

severity ラベルではなく、プロジェクトの設計方針に照らして 1 件ずつ判定した。

- #1 adr-072:335 (秘密値を ADR に記録しない) — **妥当**。「実走スモークで実値を
  確認し ADR へ追記する」は秘密鍵本文まで書くと読める。ADR-051 が記録を課すのは
  結合の存在と期待値の組み合わせであって秘密の実値ではない。設定メタデータに
  限定し、鍵本文と token は ADR にも git 履歴にも残さないことを明記した
- #2 harness-improvement-plan:223 (受け入れ基準が成功経路だけ) — **妥当**。本
  プロジェクトは背圧 12 シナリオ・kill-switch 8 シナリオと停止側を drill で
  固めてきたが、夜間ループの停止側は実走未観測。WP-17 の残課題 (明示的 false と
  config 側 deny が実走未観測) と同じ穴。AUTONOMY_ENABLED の 3 状態を受け入れ
  基準へ追加した。指摘本文が名指しした 2 箇所 (計画書 L223 / todo20 L293-304) の
  両方に反映している
- #3 todo20:480 (prompt injection の回帰 fixture) — **妥当**。順位 382 の payload
  例 "; echo PWNED; #" は shell injection であって prompt injection ではない。
  台帳テキストが流れ込む先は shell ではなく LLM プロンプトなので、テストが目的と
  噛み合っていなかった。自然言語 adversarial payload (本命) と shell/パース形式
  payload (堅牢性) の 2 系統へ分離した

## 自動 fix の実測確認

f698d19 は 1 ファイル 2 行追加のみで範囲外の編集ゼロ。内容も妥当だったため
そのまま採用した (ADR-068 の後退検知の趣旨に沿って diff を実測で確認済み)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant