← ブログに戻る

Claude Code 3セッション並走で8時間ぶん消えた: あの日から追加した5つのハーネス制約

5月末に「Claude Codeを3セッション並列で8時間動かしたら、2回コンテキストを上書きしあった」という事故ログを書きました。あの記事で私が実装した対策は3つ、メモリ名前空間・共有インデックスへのwrite lock・sessions/経由の調整でした。40日運用してみたら、3つでは足りない場所が2箇所ありました。

この記事はその続報です。事故から40日でぶつかった追加の落とし穴と、私が今の環境に足した5つのハーネス制約について、原因と実装コスト付きで書きます。前提として、Anthropicは4月に公式Memory Tool、5月に/memoryファイルシステム、6月に並列Sub-agentsの正式版という順で機能をリリースしていて(Anthropic Engineering: Memory Tool)、これらは「並列トップセッション」より安全な選択肢を提供してくれます。5つのうち3つは、この新機能を前提に組み替えたものです。

40日運用でわかった、3パターンで防げなかった2つの穴

穴1: skills/_index.md のロックは効いたのに、SKILL.mdの本文で衝突した。 flockでインデックスは守れました。ただし6月中旬、セッションAとセッションCがほぼ同時に既存の ~/.claude/skills/tabnews-comment/SKILL.md を編集し、Aの更新をCが上書きする事故が発生しました。ロック対象は「私がロック対象と思っていたファイル」だけで、実際に衝突するファイルはもっと広かった、というだけの話です。

穴2: sessions/*.jsonのハートビートを、私自身がgrepしなかった。 ハートビート機構は動いていました。ただ、私が3並列で作業しているとき、書き込み前に他セッションの主張をチェックする「習慣」までは自動化できていなかったのです。人間の規律に依存する対策は、疲れると外れます。

穴1は技術的な仕様漏れ、穴2はプロセス依存の欠陥。両方に対して、新機能で置き換えるか、規律に依存しない構造に組み替える必要がありました。

追加ハーネス制約1: /memory ネイティブへの全面移行

AnthropicのMemory Toolは、/memory フォルダをファイルシステム上のメモリ層として扱います。セッション開始時に読み込まれ、agentが明示的な意思で書き込む設計です。オプションでauto-memoryモードもあります。

私は ~/.claude/projects/<repo>/memory/ という自作構造を捨てて、worktreeごとの <worktree>/.claude/memory/ に完全移行しました。worktree境界の内側なので、gitの通常の衝突検知が働きます。上書き事故は「静かな消失」ではなく「gitコンフリクト」として表面化するようになりました。

コストは移行の半日ぶんだけです。パターン1の「セッションごとの名前空間」は残しますが、置き場所が worktree の内側になったのが変更点です。

追加ハーネス制約2: Context Editingのworktreeスコープ強制

2026年にAnthropicが公開した Context Editing 機能は、agentが自分のworking contextを能動的に整理する仕組みです。古い情報を落とす、冗長な出力を圧縮する、決定に効く情報を前に持ってくる。Anthropic自身のagentic search評価では、memory + context editingで 39%の性能向上 が観測されています (S3P Studios解説)。

問題は、この整理の中でagentが「他worktreeで作った要約」を巻き込む可能性があることでした。私は各worktreeの CLAUDE.md に以下を明記し、hookで検知するようにしました。

## Context Editing のスコープ

context editingの対象は、この worktree 配下のファイルと会話履歴のみ。
`~/.claude/projects/` 配下や他worktreeの `.claude/memory/`
参照も要約も禁止。読む必要がある場合はユーザーに確認する。

hookは20行のシェルスクリプトです。agent が worktree 外のパスを読もうとしたら stderr に出して terminate する。実装コストは30分。以降、他worktreeへの越境は 0 件です。

追加ハーネス制約3: 並列トップセッションから「1親+3子」への構造変更

Anthropicの並列Sub-agentsは、リード agent が仕事を分解して各サブ agent に委譲する構造です。Sub-agentsは並列で共有ファイルシステム上で動き、結果をリード agent のコンテキストにフィードバックします。

事故の日にやっていた「3並列でトップセッションを開く」は、私がマルチエージェントの並列実行で気にしていた「Anthropicの15倍トークン」問題を、コスト面ではなく状態衝突面で踏んだ形でした。並列サブエージェントは共有状態を「親のコンテキスト」に集約するので、~/.claude/ 配下への書き込みを親が制御できます。

移行後の私の構造はこうです。

Claude Code トップセッション (1つだけ)
  ├─ サブエージェント: feat/voice-buffer worktree担当
  ├─ サブエージェント: fix/og-emit worktree担当
  └─ サブエージェント: feat/citations worktree担当

3つの独立したセッションが競合するのではなく、1つの親が各worktreeを直接管理します。実質的に「並列」の恩恵は残しつつ、メモリ/スキルへの書き込みは親経由で直列化される。10日運用して衝突ゼロです。

トップ3並列より子3並列のほうが安全という結論は、Anthropicが3エージェントハーネス(Planner/Generator/Evaluator)を推奨するのと同じ根拠に立っています。状態管理をSupervisorに集中させる設計思想の実践版です。

追加ハーネス制約4: skills書き込みの「セッション中フリーズ」

穴1(SKILL.md本文の衝突)への直接対策です。以前の_index.mdだけをロックする発想を捨てて、~/.claude/skills/ 配下全体を セッション稼働中は書き込み禁止 にしました。

# ~/.claude/hooks/pre-write.sh
#!/usr/bin/env bash
target="$1"
if [[ "$target" == *"/.claude/skills/"* ]] && [[ -n "$CLAUDE_SESSION_ID" ]]; then
  echo "skills書き込みはセッション終了後のバッチで行う。今はscratch/に書け" >&2
  exit 1
fi

セッション中に「このスキル欲しい」と気付いたら、~/.claude/scratch/pending-skills/ に草案を書き溜めます。セッション終了時に手動レビューして ~/.claude/skills/ に反映。手動レビューを挟むことで、typosquattingスキル名や重複命名も検知できます。

セッション中の意思決定を「後回し」に変える設計です。判断を1本のセッション内で完結させないと決めたら、判断疲労の観点でもラクになりました。

追加ハーネス制約5: メモリ書き込みログを別プロセスで残す

穴2(私自身がgrepしない)への構造的対策です。人間の規律を要求する代わりに、書き込みそのものを他プロセスに透明化しました。

~/.claude/memory/ 配下への書き込みは、fswatch経由で監視して ~/.claude/memory-log/YYYY-MM-DD.jsonl に append されます。誰が何をいつ書いたかがログに残ります。

fswatch -0 ~/.claude/memory | while read -d "" event; do
  jq -n --arg t "$(date -Iseconds)" --arg f "$event" \
    '{ts:$t, file:$f}' >> ~/.claude/memory-log/$(date +%F).jsonl
done

Agentが feedback.md を書き換えた瞬間、そのイベントは私が見ようが見まいがログに残る。翌朝ログをtailすれば、夜の間に何が動いたか30秒で把握できる。「memory上書きされたかも」と不安になったときは、grepすれば履歴が出ます。

これは観測 vs コントロールの話に近くて、コントロールを強くしすぎるより、観測を確保しておくと事故発生後の回復コストが劇的に下がります。

5つのハーネス制約が、事故時の3つのパターンをどう補完しているかを示す図

併用他ツールとの比較

Cognition Devinや Cursor Composer の並列実行機能も同じ問題を抱えていて、それぞれ違うアプローチで解いています。Devinは各エージェントが完全に独立したコンテナで動くので状態共有そのものが起きません。ハードウェアレベルの分離です。Cursor Composerは複数のcomposerセッションが同一のプロジェクトファイルで動くとき、私の穴と同じ「edit-collision」を最近のアップデートで検知するようになりました。

Claude Codeの ~/.claude/ 配下は、この2つとは違って明示的に「共有可変状態」の設計です。だから制約を入れる責任は利用者側にあります。ユーザー側の設計責任という点で、Claude Codeは自由度が高い分だけ、こういう制約設計の余地(と義務)が残っています。

いま私が信じていること

3パターン+5制約 = 8個の対策で、40日運用して衝突事故はゼロです。ただし正直に書くと、8個は明らかに多いです。並列3セッションを本当に必要としているタスクは、私の実務では月に2-3回しかありません。それ以外の日は、制約1(memory 移行)と制約3(1親+3子)だけで足りています。

事故の日から学んだことは「並列は無料じゃない」でしたが、40日後の学びは「そのぶんちゃんと制約を入れれば、並列の恩恵は取れる」です。取り出せる恩恵より制約の設計・運用コストが高いなら、そもそも並列を諦める、というのが5個目の制約と同じくらい重要な選択でした。

最後にもう一つ、ハーネスは「入れっぱなし」だと死にます。私はこの記事を書きながら制約4(skillsフリーズ)を実は緩めていて、scratch/ から skills/ への反映を「セッション中でも私が明示的に承認したら通す」形に変えました。ハーネスは自分の運用を写した鏡なので、鏡のほうを毎月拭かないと現実がずれます。次に事故ログを書くとしたら、たぶんそのあたりの話になるはずです。


この話をもっと詳しく: ハーネスレイヤーの設計、6つのモジュール、共有状態の失敗モードと回避策は、ハーネス・エンジニアリング で扱っています。Claude Codeを「開いて動かす」段階を超えて運用したいエンジニア向けのフィールドガイドです。

このブログの関連記事: