📚 連載「床で律する生成ループ — 自己検証エンジンを読む」第2回 / 全5回
第 1 回では、生成と検証と再生成を回すループの骨格と、それがどこで止まるかを追った。今回は検証側の底に一枚だけ置かれたファイル、floor/floor_audit.sh を読む。ループの他の層が同一系統のモデルで回るなかで、この一枚は別の役割を負う。機械判定に落とせる合否を、どの入力から、どの形式で返すのか。通らなかったとき何を残し、その残し方がなぜ次の生成に効く形になっているのか。ここを固定コミットのソースで確かめる。
床の入口と出口: 二つのファイルを取り、一行の JSON を返す
floor_audit.sh の入り口は二引数である。
SPEC_FILE="${1:?usage: floor_audit.sh }" CODE_FILE="${2:?usage: floor_audit.sh }"
仕様(SPEC_FILE)と実装(CODE_FILE)を受け取り、両方をモデルに渡して合否を判定する設計だと読める。判定させる側の指示は、出力の自由度を絞る。
output ONLY the one-line JSON, nothing else.
返してよいのは一行の JSON だけ、と明示している。判定という工程を、後段が機械で受け取れる一個の値へ畳んでいる。
出力側も一定の形へ寄せられる。正規化された JSON は verdict / confidence / issues / reason / provider / model の 6 フィールドを持つ。verdict は大文字化される。
verdict:(.verdict|ascii_upcase)
モデルが pass と返そうと Pass と返そうと、下流が受け取るのは大文字に揃った一語になる。判定モデルの表記の揺れを、境界のところで吸収している。
通らないときの既定は FAIL、無言 PASS は無い
合否の倒し方には明確な既定がある。
Refute-by-default: uncertain => FAIL. Offline/garbled => UNAVAILABLE, exit!=0 (NEVER a silent PASS).
疑わしいものは FAIL に倒す。オフラインや応答不正のときは PASS でも FAIL でもなく UNAVAILABLE とし、exit を非ゼロにする。「黙って通す」経路を持たない、という宣言である。応答が JSON として verdict を含まないときも同じ扱いになる。
emit_unavailable "unparseable verdict ($provider:$model)"
解釈できない応答は成功として扱われず、どのプロバイダーとモデルで起きたかを添えて落ちる。判定器が壊れたときに合格が漏れ出ない作りが、既定として置かれている。
落ちた情報は、次に渡せる形で残る
FAIL のとき床が返すのは verdict という一語だけではない。出力 JSON には issues(指摘の配列)と reason(理由)が含まれ、issues はモデル応答にキーが無ければ空配列にデフォルトされる。
issues:(.issues//[])
ここが今回の焦点である。失敗を単なる非ゼロ終了で表さず、何が引っかかったかを構造化した配列として残す。verdict / confidence / issues / reason を持つ一個の JSON は、そのまま再生成の入力材料として使える形をしている。床が保証しているのは、この「渡せる形で残すこと」までである。残した issues と reason をループがどう食い戻すかは、反復の骨格を扱った前回の範囲に属する。床の側は、戻す作りが成立するための入力を構造化して用意している、と読むのが正確である。
誰が判定するか: 系統をずらすために床がある
床がなぜ別枠なのかは、コメントに根拠が書かれている。
The floor exists to catch what same-family review misses (loop-protocol.md: "生成も検品も同一Claude族 # のため、Claudeが系統的に誤る欠陥は両者が共に見逃す").
生成も検品も同じ系統のモデルなら、その系統が構造的に見落とす欠陥は両方が同時に見落とす。それを別系統で拾うために床がある、という位置づけである。
ところが既定のプロバイダーは、その狙いをまだ完全には満たさない。
SPEC_MODEL="${RINNE_FLOOR_MODEL:-claude:claude-opus-4-8}"
環境変数 RINNE_FLOOR_MODEL が未設定なら claude:claude-opus-4-8 が使われる。そして claude を使う経路には警告が置かれている。
rinne floor WARNING: provider=claude is SAME-LINEAGE (no heterogeneity guarantee; interim placeholder until codex:)
パネルと同一系統であり、異種性(heterogeneity)の保証は無い、暫定の置き石だと明記される。床の本来の狙いを満たすプロバイダーは別に想定されている。
Needs OPENAI_API_KEY; UNAVAILABLE (never silent PASS) until that is set.
非 Claude かつ高性能であることが「本来の床」の条件とされる。ただしその経路も、OPENAI_API_KEY が無ければ UNAVAILABLE になり、鍵の欠如を黙って PASS には振り替えない。前節で見た「無言 PASS を持たない」という既定が、プロバイダー選択の層でも一貫して効いている。
テスト用の口もある。
OFFLINE TESTING ONLY: "mock:PASS" / "mock:FAIL" return that verdict; anything else => UNAVAILABLE.
mock:PASS と mock:FAIL だけがその判定を返し、それ以外は UNAVAILABLE になる。オフラインで挙動を確かめる口を用意しつつ、そこでも既定は「不明なら通さない」に揃えられている。
床の設計を一枚で言えば、系統をずらして見落としを拾うために別枠を置き、その判定が壊れても合格が漏れないよう既定を FAIL 側へ倒し、落ちた理由を次へ渡せる構造で残す、という三点になる。既定のプロバイダーがまだ暫定である事実まで含めて、判断がコメントに明示されている点が、このファイルを読む値打ちである。
出典について
本稿の引用は、公開されている参照実装のソースを ある 1 コミットに固定して 逐語照合したものだけです(2026-09-07 検証)。
読んだ対象は floor/floor_audit.sh です。
連載「床で律する生成ループ — 自己検証エンジンを読む」の前後
- ← 第1回: 回すだけでは良くならない — ループの骨格と停止条件
- → 第3回: 同じモデルに採点させない — 異種モデルの合議(公開予定)