← 기사 목록
日本語https://zenn.dev/topics/ai/feed

消したはずの claude -p が残っていた ── C3 v2.6.1〜v2.7.0

추출된 키워드

40
claude -p·5C3·5v2.6.1·4v2.7.0·4Claude Code·4security-audit·4ヒューマンインザループ·4consolidate_memory.py·3開発プロセスの知識管理·3llm_summary.md·3exit 2·3stderr·3run_in_background=True·3summarize-memory agent·3permission_handler·3permission_rules.json·3auto_allow·3Stop hook·3Parallel Orchestra·3PO·3Agent·3Skill·3worktree·2sqlite3.connect·2TDD wave·2--dangerously-skip-permissions·2worktree_guard.py·2git check-ignore·2parallel-agents·2base64.b64encode·2WebFetch·2Bash(git status *·2prompt-history·2LLM 子プロセス·2-EncodedCommand·2os.path.getmtime·2Codex CLI·2PowerShell インジェクション·2OpenHands·1AutoCodeRover·1

원문

12,414
消したはずの claude -p が残っていた ── C3 v2.6.1〜v2.7.0

消したはずの claude -p が残っていた ── C3 v2.6.1〜v2.7.0

前回記事:

https://zenn.dev/satoh_y_0323/articles/db51bb73a97c19
C3 GitHub:https://github.com/satoh-y-0323/claude-code-conductor/PyPI:https://pypi.org/project/claude-code-conductor//公式ドキュメント:https://satoh-y-0323.github.io/claude-code-conductor/
本記事のスコープ:v2.6.1 の定期 security-audit フィックスと、v2.7.0 で claude -p を完全除去した話。おまけで permission_handler の使い勝手改善も紹介します。

はじめに

前回の記事(v2.5.0〜v2.6.0)では、Codex CLI との並列レビューで「Claude が見逃した本物の脆弱性を Codex が見つけた」という話をしました。

今回は少し趣が違います。

C3 は v2.5.0 で Parallel Orchestra(PO)という別プロセス方式を廃止し、

claude -p
(ヘッドレス起動)をほぼ使わない構成に移行していました。「これで claude -p は消えた」と思っていたところ、Stop hook の深いところにひっそりと生き残っていた ── そういう話です。

さらに「Agent として実装し直してほしい」と依頼したら、Claude が Skill として実装してしまったというオチもあります。

v2.6.1: 定期 security-audit の結果を適用

v2.7.0 の話に入る前に、直前リリースを軽く紹介します。

v2.6.1 は「API 変更なし・定期 security-audit の結果をフィックスするだけ」のパッチリリースです。

セキュリティ修正 3 件

[SR-INJ-002] PowerShell の EncodedCommand 化

前回 Codex が指摘した PowerShell インジェクション対策の延長です。Windows 通知を送る箇所で

safe_msg
を f-string に直接埋め込んでいた方式を廃止し、
base64.b64encode
で変換してから
-EncodedCommand
に渡すよう変更しました。
'
・バッククォート・
$
を含むメッセージでもインジェクション不可能になっています。

[SR-AI-001] LLM 子プロセスの攻撃面縮小

consolidate_memory.py
claude -p
でサブプロセスを起動する箇所で、
--dangerously-skip-permissions
フラグを除去し
--tools ""
で全ツール無効化しました。この時点ではまだ
claude -p
自体は残っていたわけですが、権限は最小化しています。

[SR-V-001] prompt-history の秘密情報マスク

select_tier.py
がプロンプト先頭 200 文字を
.claude/logs/prompt-history.jsonl
に保存する際、API キー・トークン・パスワード相当の 7 パターンを
***
でマスクするようにしました。

コード品質修正 14 件

sqlite3.connect
を直接書いていた箇所の
_BUSY_TIMEOUT_MS
統一、アトミック書き込み漏れの補完、型分岐の明示化など。いずれも security-audit エージェントによる定期スキャンで検出したものです。

v2.7.0 メインストーリー: 消したはずの claude -p が残っていた

背景: PO 廃止で消したつもりだった

C3 は v2.1.0〜v2.5.0 の 4 日間 5 リリースで Parallel Orchestra(PO)を完全廃止しました。PO は別プロセスでエージェントを起動する方式で、中心的な実装が

claude -p
の呼び出しでした。

廃止後は Claude Code のインタラクティブセッション内で Agent ツールを使う方式に完全移行。

claude -p
の出番はなくなったはずでした。

Stop hook に潜んでいた残骸

ところが、セッション終了時に動く Stop hook の中に、まだ生きている

claude -p
がありました。

.claude/hooks/consolidate_memory.py
は、セッション終了時に過去 7 日分のセッションファイルを集約して
llm_summary.md
を更新する処理を担っています。この「集約」の部分が LLM を呼ぶ必要があり、その実装として
claude -p
でサブプロセスを起動していたのです。

PO を廃止したとき、この hook は「エージェントオーケストレーションではなく単純な LLM 呼び出し」として扱われていたため、見落とされました。

# consolidate_memory.py の旧実装(簡略)
def run_llm_summary(prompt: str) -> str:
    result = subprocess.run(
        ["claude", "-p", prompt, "--dangerously-skip-permissions"],
        capture_output=True, text=True
    )
    return result.stdout

Agent として実装してほしいと依頼したら、Skill で実装された

「Stop hook から

claude -p
を廃止して、代わりに Agent として LLM 要約を実行するよう実装してほしい」と依頼しました。

Claude が Wave 0 で実装したのは .claude/skills/summarize-memory/SKILL.md でした。

Skill として実装されてしまったわけです。

一見それっぽいのですが、Skill は動きません。Skill ツールは LLM のコンテキスト内でマークダウンを読み込む仕組みで、子プロセスを起動しません。Stop hook は Python スクリプトとして Claude Code と別プロセスで動くため、「Skill を呼ぶ」という概念が成立しないのです。

Stop hook (Python サブプロセス)
   ↓ 呼べない
Skill ツール(Claude Code セッション内の LLM 読み込み)

作り直し: exit 2 + Agent(run_in_background=True)

正しい実装は、Stop hook が Claude Code 本体に対して「Agent を起動してくれ」と指示を渡すことです。

Claude Code の hook 仕様には

exit 2
+
stderr
で LLM へフィードバックを返す機能があります。これを使うと、hook が終了した後に Claude が stderr の内容をシステムメッセージとして受け取り、次のアクションを取ります。
# session_stop.py の新実装(簡略)
def main():
    if not _needs_summary():
        sys.exit(0)
    
    instruction = """
    summarize-memory agent を背景で起動してください:
    Agent(subagent_type="summarize-memory", run_in_background=True, ...)
    """
    print(instruction, file=sys.stderr)
    sys.exit(2)  # exit 2 で Claude へフィードバック

そして

.claude/agents/summarize-memory.md
を新規作成し、LLM 要約の手順を Agent 定義として移植しました(旧 SKILL.md の内容がそのまま使えます)。
Stop hook (Python) → exit 2 + stderr
   ↓ Claude Code が受け取る
Claude → Agent(run_in_background=True, subagent_type="summarize-memory") を起動
   ↓ 非同期実行
summarize-memory agent が llm_summary.md を更新

この方式のメリットは、Stop hook がブロッキングなしで終了できることです。旧実装は

claude -p
の完了を待ってから hook が返っていたため、セッション終了のたびに数秒〜十数秒の待機が発生していました。新実装では hook は即座に終了し、要約は background で実行されます。

要約の必要性を機械的に判定

あわせて、毎回 LLM を呼ばないための仕組みも整備しました。

旧実装はクールダウン(60 分)でスロットリングしていましたが、「60 分以内に 2 回セッションを終了したら要約が更新されない」という問題があります。

新実装では

os.path.getmtime()
でファイルの更新時刻を比較します。
def _needs_summary() -> bool:
    """session ファイルが llm_summary.md より新しければ要約が必要"""
    summary_mtime = _safe_mtime(SUMMARY_PATH)
    session_mtimes = [_safe_mtime(p) for p in SESSION_DIR.glob("*.tmp")]
    if not session_mtimes:
        return False
    return max(session_mtimes) > summary_mtime

「セッションファイルが更新されていれば要約が必要、そうでなければ不要」という判断を LLM なしで行えます。クールダウンという恣意的な時間制限が消えて、シンプルになりました。

結果: C3 から claude -p が消えた

この変更で

consolidate_memory.py
の LLM 関連関数・定数 11 個が削除され、ファイルが -400 行以上スリムになりました。C3 の全コードベースから
claude -p
の呼び出しが完全になくなっています。

その他の改善: permission_handler の使い勝手向上

ボタン付きトースト通知でワンクリック auto_allow

Claude Code が「このコマンドを実行してもいいですか?」と権限確認ダイアログを出す場面があります。毎回承認するコマンドがあれば

permission_rules.json
auto_allow
に追加しておくと確認がスキップされます。

これまでは

permission_rules.json
を手で開いてパターンを書き込む必要がありました。
Bash(git status*)
のようなワイルドカードパターンを自分で考えて書くのは地味に面倒です。

v2.7.0 からは Windows 通知に 「自動承認に追加: Bash(git status *)」ボタンが付くようになりました。

[権限確認] Bash: git status --short
[承認] [拒否] [自動承認に追加: Bash(git status*)]
               ↑ クリックで permission_rules.json に追記

permission_handler.py
がツール名と引数からワイルドカードパターンを自動推定します。
ツール推定パターンの例
Bash
コマンド先頭 1〜2 トークンにワイルドカード付加
Write
/
Edit
/
Read
親ディレクトリ
/**
WebFetch
domain:hostname

ボタンクリックで

permission_rules.json
への書き込みはアトミック(mkstemp + os.replace)なので、並走する他のプロセスがファイルを読んでいても壊れません。

auto_allow リストに上限 100 件

auto_allow
に際限なくパターンを追加できると、意図しないコマンドまで自動承認されるリスクが高まります。100 件を超えるとエラーになるサイズ制限を設けました。日常的な開発用途で 100 件を超えることはほぼないはずです。

worktree 並列実行時のファイル取り込み修正

parallel-agents
スキルで worktree を使った並列実行をすると、main ブランチへのファイル取り込み(merge)が一部ケースで失敗する問題がありました。

git check-ignore -q
による .gitignore チェックを入れた分岐ハイブリッド方式に変更し、構造的に修正しています。あわせて
worktree_guard.py
の CWD ベース自動有効化で、worktree 外への Write/Edit を
exit 2
でブロックする保護が一貫して動くようになりました。

C3 が目指しているもの

ここで少し C3 の設計思想について話します。

C3 は「業務アプリケーション開発チームが使うヒューマンインザループ型の開発フレームワーク」を目指して作っています。

キーワードは 2 つです。

ヒューマンインザループ

C3 のワークフローには、各フェーズの間に必ず人間の確認ポイントがあります。

ヒアリング → [承認] → 設計 → [承認] → 計画 → [承認]
→ 実装(TDD wave)→ [承認] → レビュー → [承認] → 完了

「AI が自律的にどんどん動いてほしい」という用途には向いていません。各フェーズで AI が出した成果物を人間が確認・修正できる構造を意識的に維持しています。

これは趣味的な選択ではなく、業務での利用を考えたときの必然です。業務アプリケーションには「なぜこの設計にしたか」「このトレードオフをなぜ選んだか」という判断ポイントがあり、そこは AI に任せっきりにできません。レポートファイル(architecture-report / plan-report / code-review-report / security-review-report / test-report)を生成して人間が読み、判断してから次へ進む設計はそのためです。

自動化フレームワークではない

「C3 で CI/CD を自動化したい」「コードを書いて自動でデプロイしたい」という用途には、より適したツールがあります(OpenHands や AutoCodeRover など)。

C3 が得意なのは「開発サイクルの中で人間と AI が協調しながら品質を上げていく」場面です。

レポートの蓄積、パターンの学習、定期的な security-audit、過去の失敗アプローチの記録 ── こういった開発プロセスの知識管理を機械的にサポートするのが C3 の役割です。

バージョンまとめ

バージョン日付主な変更
v2.6.1 2026-05-15security-audit 定期監査フィックス(セキュリティ 3 件・品質 14 件)
v2.7.0 2026-05-16
claude -p
完全除去(Stop hook を background Agent 方式に移行)、permission_handler ボタン付き通知、auto_allow 上限、worktree 取り込み修正

おわりに

「消したはずだった

claude -p
」は、定期的な security-audit と地道なコード棚卸しで発見できました。

Claude に「Agent として実装して」と依頼したら Skill で実装されてしまったのは、Skill と Agent の仕組みの違いを C3 自体が正確に伝えられていなかったということでもあります。SKILL.md と agents/*.md の使い分けを CLAUDE.md に明記したことで、再発は防げる見込みです。

こういった「AI が間違える場面を記録・修正していく」プロセス自体が、C3 を使った開発のサイクルになっています。

リンク