他の作業を巻き込んでcommitする事故を3つの見張り役で潰した話
困ったこと
夜中に動いていた別の作業が、みんなで使う記録帳を「自分の記録だけが書かれた状態」で上書き保存しました。さらに別の作業がそこに追い書きして保存したため、2日分の記録が消えるところでした。幸い「追記なのに消えている」という保存の統計の違和感で、その日のうちに元通りにできました。
同じ手の事故が6週間で11回。根っこは「保存待ちの置き場」が全作業で共有の1か所だったことです。「自分の分だけ持ってきてね」というマナーでは、夜中に忙しく動いている限り誰かが必ず間違えます。
どう解決したか
3つの見張り役を置きました。
1つ目は送信前の検品。他の人の荷物が混ざったまま送信しようとしたら機械が止めます。導入初日に本物を捕まえました。
2つ目は自動送信を賢くすること。30分ごとの自動送信は「触ると宣言したものだけ」を送ります。そもそも混ざりません。
3つ目は専用の送信コマンド。宣言外は受け付けず、他の人の荷物には触りません。宣言し忘れは拒否されました。
さらに3つのAIに検品してもらい、特別な名前のファイルで壊れる等の弱点を3回指摘され・直しては試験を増やし、ゼロに減りました。
flowchart TB
A["😱 別タブの作業がcommitに混ざった"] --> B["見張り役を3つに"]
B --> subgraph guards["3つの見張り役"]
C["1️⃣ commit前の中身点検"]
D["2️⃣ 触るファイルの事前宣言"]
E["3️⃣ 宣言した分だけcommit"]
end
C --> F["混ざったら止まる 🛑"]
D --> F
E --> F
何が変わったか
夜中に誰かが間違えても、機械が止めてくれます。導入初日に本物の事故を1回防ぎ、記録の保存も新しいコマンドで他の作業を一切巻き込まずに終わりました。
正直に言うと完璧ではありません。古い方法なら通ってしまいますし、別の環境にはまだ見張り役がいません。そこは「1週間動かして実害ゼロか確かめ、だめなら作業ごとに倉庫を分ける方式へ変える」と条件を付けて手帳に書きました。完璧を狙って複雑にするより、止まった時に気づける仕組みと一緒に育てる方が正解でした。
<details><summary>▶ 技術的にどう作ったか</summary>
複数のClaude Codeセッションが同一git worktreeで並行作業する環境で、stage(index)が全セッション共有の単一状態であることが事故の根因。3層の機械防御を実装した。
- 検品hook(git-commit-diff-check.sh の enforce化): paths.json(セッション別の「触るファイル宣言」)とstageの突合で、他🟢セッション宣言一致+自タブ宣言外のcommitをblock(PATHS_BLOCK_MODE=enforce・DRY_RUN除去)。実戦で別セッション残置stage(±126行)をblock実測。
- auto-sync宣言include化(ssot-auto-sync.sh+active-declarations.py): 30分cronの自動commitを「🟢宣言pathに変更がある時だけcommit」に反転。未宣言ファイルは取り込まない。旧exclude方式は絶対/相対パス混在で静かに壊れていたためrepo-root正規化を新設。
- git-commit-scoped.sh(新ラッパー): 宣言検証+
git commit -- <paths>(git内部でHEAD+指定パスのみの一時indexを作り共有indexを参照しない)を同梱。検証はactive-declarations.py--mode check(値をすべて環境変数渡し=インジェクション面なし)で正規化済みpathspecをcommit。untracked新規ファイルはgit add <file>後の2段手順。
9skill+CLAUDE.mdのcommit手順を一斉移行。設計レビュー3ラウンド(3機51件)+実装レビュー2ラウンド(3機32件・critical 3→1→0で収束)。設計正典: obsidian-ssot docs/superpowers/specs/2026-09-05_並行セッション巻き込み再設計-design.md(詳細は01_DECISIONS/claude-code/2026-09-06_並行セッション巻き込み再設計実装.md)。Phase 3(worktree完全分離)は再評価条件付きでバックログ起票済み。
</details>