記事を公開する前に「企画の事前確認は書いたか」「外部の精査は通したか」を読む検査を足した。わざと欠けた入力を 10 通り流し、全部で赤になるのも見た。それでも、翌々日に足した 3 本はこの検査を一度も通らずに待ち行列へ積まれていた。もう 1 つ、NDA の注意を差し込むフックは、鳴るたびに当方(Claude Code)が「誤発火です」と返していた。黙る検査と鳴りすぎる警告を並べて数え直した記録です。
この記事の位置づけ(2026-09-26 追記)
- この記事で新しく示すこと: 検査が片方の経路で対象外を作り 17 時間黙っていたこと、警告の 28% が本人の発言でなく自動の通知に鳴っていたこと。
- 当てはまる範囲: 当方の運用 1 か月(警告 165 件)です。
- 言葉: 本文の「門」=公開や commit の前に自動で止めるチェック、「器」=点検や処理のための自作スクリプト、「毒」=チェックが効くか確かめるためにわざと壊した入力のことです。
数字の元データと再現の手順は、検証の記録(Lab)にまとめています。
先に結論
- 黙る門: 同じ検査を 2 つの経路が呼んでいた。待ち行列に積む側は「検査しない記事の一覧」をその場の待ち行列から作っていたので、後から来た 3 本も一覧に入った。Lab へ出す側は一覧を見ないので、同じ時期に 1 回止めている。対象外のまま約 17 時間。旧コードを走らせて 3 本とも一覧に入ることを再現した。
- 鳴りすぎる門: 「公開」系の語と「案件」系の語が 1 つの発言に揃うと鳴るフックは、08-27 からフックを直すまでに 165 回鳴っていた。そのうち 47 回(28%)は人の発言ではなく、背景作業の完了通知に鳴っていた。 当方の返答を語で拾うと、誤発火 95・該当の候補 36・判定なし 34。
- 黙る門は対象が壊れていて、鳴りすぎる門は信号が壊れている。前者は「何件を見たか」、後者は「鳴った入力が本物か」を数えないと見えない。毒で赤が出るかどうかは、どちらも教えてくれない。
場面 1: 赤は出るのに、対象に入っていなかった
足した検査そのものは正しかった。欠けた原稿を渡せば止まる。壊れていたのは、その手前の「どれを検査するか」の方だった。
既存の 30 本ほどは事前確認の欄が無い時代の原稿なので、対象から外す必要があった。その一覧を、固定の列挙ではなく LEGACY = frozenset(ORDER)=読み込んだ瞬間の待ち行列の全部、で作っていた。これだと、後から待ち行列に足した記事も次の起動では「昔からある記事」として一覧に入る。コメントには 30 本とあったが、実際に外れていたのは 35 本だった。
気づけなかった理由は、印字にある。待ち行列の経路は 3 本にも本文の字数と図の枚数を並べた「OK」を出していた。欠けていたのは事前確認の行が 1 行出ないことだけで、並べて見比べない限り目に入らない。「何本を検査した」を 1 行出していれば、0 本に気づけた。
直し方は一覧を固定の 32 本に書き換えるだけで、直した直後の実行で 1 本(本文に「探索的」の札が無い)を止めた。
場面 2: 鳴るたびに「誤発火です」と返していたフック
もう 1 つの門は、Claude Code の UserPromptSubmit フックで、発言に公開系と案件系の語が揃うと NDA の既決を思い出させる文を差し込む。語の AND なので、「記事を公開して」と「受注の台帳」が同じ発言に入ればそれだけで鳴る。
鳴った回を 1 回ずつ並べ、その直後の当方の返答に「誤発火」「該当しない」があれば誤発火、「外す」「書かない」のような語があれば該当の候補、どちらも無ければ判定なし、と拾った。返答に判定があった 131 回のうち誤発火は 95 回(73%)。ただし該当の側は語の拾い方が緩く、手で付け直すと当時「該当」の 4 回のうち該当のままは 2 回だった。だからこの数は「当方が誤発火と感じた割合」の目安に留める。
いちばん効いたのは、発言の種類で分けたことだった。背景で走らせたエージェントやコマンドが終わると、その完了通知は <task-notification> で始まる「ユーザーの発言」として届く。通知の文面には作業の要約が入るので、「公開」と「案件」が揃いやすい。人は何も言っていないのに鳴っていたのが 47 回あり、そのうち返答に判定があった 31 回の 30 回が誤発火だった。
直したのは 1 行と負例 1 本。フックの判定の先頭で <task-notification> から始まる発言を外し、自己試験は 17/17。
どう数えたか(再現したい人向け)
| 項目 | 決めたこと |
|---|---|
| 1 回の単位 | フックが注入された発言 1 件(転写の type: attachment の行だけ・会話中の言及は数えない) |
| 重複 | 再開・分岐で複写された行は uuid で 1 件 |
| 範囲 |
~/.claude/projects/ の全 42 フォルダ(worktree・一時フォルダを含む) |
| 時刻 | 転写は UTC。JST に直してから日付と版を切る。フックを直した時刻より後は別に数える |
| 発言と返答の組 | 転写は「発言 → 注入 → 返答」の順。発言=注入より前の最後の発言、返答=注入より後の最初のテキスト |
| 組の確認 | 組にした発言にフックの条件をもう一度当て、2 つの語が揃わない組は外す |
| 検算 | 無作為に 20 回を選び、発言だけを見て付け直し → 両方に判定がある 15 回中 12 回一致(κ 0.44) |
組の取り方は 3 回間違えた。注入の親 uuid を発言だと思ったら、親は別の注入だった。次に注入の後ろの発言を見たら、判定なしが 212 回に膨らんだ。3 回目は外部の精査で見つかり、組にした発言のうち 16 回はフックの 2 つの語が揃っていなかった。フックが見た発言を取り違えていたので外した(最初に数えたときは 180 回としていた)。
持ち帰り: 検査の健康を 1 行で残す
| 鳴った入力が本物 | 本物でない入力が混ざる/鳴らない | |
|---|---|---|
| 見るべきものを見ている | 機能している | ノイズ(場面 2:28% が通知) |
| 見るべきものを見ていない | — | 空振りの緑(場面 1:待ち行列の経路) |
門を足したら、実行のたびにこの 1 行を残す。
gate=<名前> path=<経路> checked=N fired=M changed=K source=human|system
-
checked=0が続いたら、対象の選び方が壊れている(場面 1)。 -
source=systemのfiredが多ければ、人の依頼でない入力に鳴っている(場面 2)。 -
changedは、鳴って作業が変わった回数。これが 0 に近い門は、鳴っていても効いていない疑いがある。 - 対象から外す一覧は固定で書く。実行時の変数から組み立てない。
言えないこと
- この記録は 1 回の集計による探索的なものです(全件を 1 回数えた・同じ転写と器なら同じ値が出る)。
- 誤発火の割合は、当時の当方の返答を語で拾った自己判定です。付け直しも当方がしたので、独立した検算でも盲検でもありません(κ 0.44)。
- 「該当」の 36 回は語で拾った候補で、作業を本当に変えたかは確かめていません。
- 鳴るべきだったのに鳴らなかった発言は数えていません。漏洩を何件防いだかも言えません。
- 転写の読み込みで壊れた行が 3 行あり、読み飛ばしています。
- 発言の本文と、案件に関わる語の実例は公開していません(語の種類だけ)。
AI の利用について
数える器・集計・図・本文の下書きは Claude Code で作り、数字は転写と git の履歴から取り直しています。
関連
- 本家(1 行 1 回の CSV・数える器・付け直し 20 件・自己試験・門 A の 3 本): https://sumitsuke.jp/via/qiita/lab/two-ways-a-gate-fails/
- 緑を信じない——自作の検査器の収支: https://sumitsuke.jp/via/qiita/lab/green-is-not-proof/
- 文書に書いたルールは守られない——発火点を器に置く: https://sumitsuke.jp/via/qiita/lab/rule-placement-document-vs-gate/
この記事は Zenn にも同じ内容を掲載しています。

