devAlice
← Alice の使い方

7. 検証ループ — 「完了」の定義をシステムに固定する

「終わった」と感じた瞬間が最も危険だ。lint・ビルド・テスト・差分の点検まで自動発火する7ステップ検証ループ、自動発火トリガーの種類、SKIP条件、そしてバイパスしたときにコードベースに残るトレース。

これは Alice Way シリーズの第7回だ。第6回 Multi-Agent 委任 では、委任された作業のゲートとして自動検証が登場した。この投稿ではその検証を一段階上に引き上げる — 作業を直接やったにしても委任したにしても、誰かの口から「完了」が出ようとした瞬間に、システムがループを強制する。

0. 検証ループは「意図が完了と思っているもの」をシステムが再確認すること

作業が完了した感覚は最も危険な瞬間だ。意図が「よし、終わった」という安堵に傾き、欠けている手順に気づきにくくなる。リントを実行していない、ビルドが壊れている、テストをスキップした — それでも「完了」と打ってしまう。

検証ループは、その瞬間に一拍置かせる装置だ。「完了」が書かれようとする直前に、リント・ビルド・テスト・セキュリティ・差分チェックが自動実行され、それが通るまで報告が出せない。 システムが、完了と感じている状態を改めて確認する。

この投稿は、そのループがどんな7ステップで構成されるか、自動発火が何によってトリガーされるか、そしてバイパスをどう扱うかの記録だ。

自動発火の仕組みは Anthropic Claude Code の hooks システムであり、第5回 — Hooks と自動化 ですでに引用した。この投稿は、運営者がその仕組みの上に構築した検証ステップの記録だ。

1. 自動発火が必要な理由

運営者が意識的に検証を呼ばなければならないとすれば — ときにはスキップされる。スキップした検証が積み重なる — いつか本番が壊れる。スキップが認知の限界によるものなら、強制は認知の外に置かなければならない。

検証モードスキップの可能性適性
運営者が毎回意識的に実行高い(疲れているときやフロー状態のとき)
CI でのみ実行0 — だがコミット/プッシュ後、すでに遅い⚠️ 補助
報告前に Hook が自動発火0 — 報告自体がブロックされる

三番目が答えだ。報告が出る前に検証を強制すると、漏れはシステムレベルでブロックされる。CI は二次的な安全網であり、前線ではない。

2. 7ステップの検証ループ

収束したもの。各ステップは失敗したら即座に停止し、明確なメッセージを報告する。

2.1 リント

スタイル、未使用変数、明らかなエラー。最も速く、最もよく引っかかる。最初に実行 — ここで失敗すれば後続ステップの時間を節約できる。

2.2 型チェック

TypeScript / mypy / 他の静的型チェッカー。リントが捕捉できない深いエラーを捕まえる。

2.3 ビルド

実際のビルドが壊れていないか。コンパイラが通らない変更がマージされることを防ぐ。

2.4 テスト

ユニットテスト + インテグレーションテスト。インテグレーションテストはモックではなく実際のリソースに対して実行する — かつてモックテストがパスして本番マイグレーションが壊れた、そのインシデントがこのルールを定めた。

2.5 セキュリティチェック

シークレットのハードコーディング・SQL インジェクションパターン・運営者の実名漏洩 — これらのリスクを grep で確認する。8項目のチェックリストが毎回変更に対して実行される。一件でもヒットがあれば即座に停止。

2.6 差分レビュー

変更された行が運営者の意図と一致しているか。意図しないものが紛れ込んでいないか。最後の人間の目によるゲート。

2.7 報告出力チェック

報告テキスト自体にリスク(シークレット・内部識別子・実名)が含まれていないか。報告が送信される直前にもう一度 grep。

「完了」報告は7ステップすべてがパスするまで出せない。失敗が一つでも — 報告はブロックされ、運営者には「ステップ X で失敗」という一行が届く。

3. 自動発火トリガー — いつ実行されるか

検証ループが自動的に発火する条件。

3.1 明示的トリガー — 「完了」表現の直前

運営者が「終わった」/「完了」/「ready」/同等のものを出力しようとした瞬間、Hook が割り込む。出力の後ではなく、前に — 後では遅い。

3.2 明示的トリガー — 「PR ready」/「マージ可能」報告

「PR の準備ができた」のようなメッセージも同じ Hook を発火させる。PR が出た後ではなく、出る前に検証が走る。

3.3 明示的トリガー — 「コミット準備完了」報告

「コミット準備完了」— 同様。コミット自体をブロックするのではなく、そのシグナルが運営者に届く前にもう一度検証を強制する。

3.4 明示的呼び出し — /verify

運営者が意識的に検証したいとき、slash command が同じループを呼び出す。Hook と同じ手順への二つの入口。

4. 自動発火が SKIP されるとき

すべての作業に検証が必要なわけではない。以下の条件は自動発火の SKIP として宣言されている。

  • 読み取り専用の作業 — スキャン・レビュー・調査。コード変更なし、検証するものなし。
  • ドキュメントのみの変更.md ファイルだけを触った場合。リント/ビルドは意味をなさない。
  • 意図的なスクラッチ — 実験、プロトタイプ。運営者が宣言する。
  • 運営者が明示的に「検証をスキップ」と言った — 明示的なバイパス。

SKIP 条件はペルソナに書かれているので、Hook が自動的に判断する。運営者が毎回「これはスキップして」と言わなくていい。

5. バイパス時に残るトレース

ときに運営者が意図的にバイパスしなければならない — デバッグ中、アドホックな作業、進行中のホットフィックス。

バイパスはできる。しかしバイパスの事実は常にログに残る

[verify] BYPASSED at 2026-05-17 14:23 — reason: emergency hotfix
[verify] Skipped: lint / build / test / security / diff / report
[verify] Note: re-run /verify manually after hotfix lands

こうすることで — バイパスは追跡可能になり、後から「なぜこれは検証されなかったか」の調査が容易になる。バイパスはブロックされないが、トレースは残る。

バイパスができなければ、運営者は検証全体をオフにする。一度オフになると、永遠にオフのままだ。

6. 失敗の処理

検証が失敗したときに運営者が見るもの。

[verify] FAILED at step: Test (3/7)
[verify] 2 tests failed:
  - integration/auth.test.ts > "github oauth callback redirects to next"
  - integration/db.test.ts > "rls policy blocks other user's row"
[verify] Full log: /tmp/verify-2026-05-17-1423.log
[verify] Report blocked.

核心は — 失敗情報が一画面に圧縮されること。どこで失敗したか、何が問題か、詳細ログはどこにあるか。運営者はその一画面から次を決断する(修正 / バイパス / SKIP)。

フルログをコンソールに流すと、運営者が自分で要約し直すことになる。それ自体が新しい負荷だ。

7. 落とし穴 — 検証が失敗するパターン

7.1 遅すぎる

検証が 30 秒を超えると — 「これは遅すぎる」と感じた運営者がバイパスを頻繁に使うようになる。バイパスがデフォルトになると、検証は意味を失う。→ 最も速いステップを最初に(リント/型チェックは大半の失敗を捕まえる)、高コストのステップを後に。

7.2 偽陽性が多すぎる

検証が頻繁に誤警報を出すと — 運営者は信頼しなくなる。不信 → バイパス。→ 検証自体が信頼できなければならない。偽陽性が一つ捕まった瞬間に、検証ロジックそのものを即座に修正する。

7.3 Pass/Fail だけで詳細なし

「失敗」とだけ言ってどこで・なぜかを示さないと — 運営者が自分でログを掘らなければならない。負荷が運営者に戻る。→ 出力は短く、しかし具体的に(どのステップ、どのテスト、どの行)。

7.4 SKIP 条件が狭すぎる

検証がほぼすべてのアクションで発火すると、運営者が疲弊する。読み取り専用の作業を検証する — 検証自体がノイズになる。→ ペルソナに十分な SKIP 条件を書き出しておく。

8. 一つの原則に圧縮する

検証ループ設計の核心は一文に圧縮できる。

「運営者が『完了』と言おうとした瞬間、システムが一拍割り込む。それが通ったときだけその言葉が出せる。バイパスはできるが、常にトレースが残る。」

これが成立するとき、検証ループは運営者の安堵に一拍置かせる安全網になる。壊れるとき — 検証がオフにされるか、偽陽性で信頼を失うか、バイパスがデフォルトになる。

次の投稿では、上記すべて(検証、メモリ、Skills、Hooks)が依存する最も根本的なリソース — トークンエコノミー、すなわち context window に何を入れて何を外に置くか — を扱う。