Stage-2 quality judge + improve loop with convergence (speca#88 eval) - #22
Merged
Conversation
The eval step of the #88 confirmed direction: eval is LLM-as-judge quality scoring (five axes, blind rubric), NOT recall — the verdict compares score distributions against the vendored solodit reference bar (data/solodit_checklist.csv, provenance pinned incl. git blob sha); the vuln dataset is improve-step teaching material only. Convergence needs BOTH the reference bar met AND a 3-round plateau; unconverged runs end honestly with converged=false. LLM access is injected (prompt->response subprocess seam): no API key in the repo, default CI runs the deterministic mock end to end, the real LLM run is dispatch-only on self-hosted (judge-dispatch.yml). recall.py untouched; generation (CHK-15) reused, not re-implemented.
Observed on the first real run (local authenticated claude -p, sonnet): one intermittent rc=1 with empty stderr killed a 67-call judge run at item 20, and an immediate same-second retry also failed. judge_item's retries now cover adapter failures as well as bad responses, with a --retry-wait pause between attempts (CLI defaults: --retries 2, --retry-wait 5). An item still failing after the retries aborts the run — never silently skipped, so a mass-refusing or dead adapter cannot produce a thin-but-green report.
Second real-run lesson (local claude -p): one adapter call hung for 19+ minutes; subprocess.run's TimeoutExpired then escaped judge_item's retry net entirely and killed the run. subprocess_llm now converts the timeout into JudgeError (kill + retry like any transient failure) and the CLI exposes --llm-timeout (default 600s) so a flaky adapter recycles fast.
Closed
17 tasks
grandchildrice
approved these changes
Jul 22, 2026
grandchildrice
left a comment
Contributor
There was a problem hiding this comment.
Approve → マージします。#88 確定方針の judge harness + improve ループを、合意した設計どおり忠実に実装しています。load-bearing な主張を実コードで裏取りしました。
検証(すべて実コードで確認)
- judge は blind:
checklist_items_from_01e/_from_soloditがtext/assertionだけをcheck/detailに落とし、id・provenance(x_dataset_evidence)を渡さない。build_judge_promptは RUBRIC + CHECK + DETAIL のみで reference/生成側が同一骨格。内容一致は構造的に評価不能。✓ - dataset は improve 専用:
build_improve_promptにのみ vuln 行が入る(select_evidence: 同 label 優先→Critical/High fallback→最大3、決定論順)。judge 側には一切入らない。✓ - no-clamp:
parse_judge_responseは全軸 present + integer + [1,5] + 非空 critique を要求、欠損/範囲外は retry 後エラー。clamp/default で握らない。✓ - バー判定:
meets_reference_bar= overall ≥ 参照 かつ 全軸が参照−tolerance(0.25) 以上。1軸盛りで通過できない。分布のみ、内容類似は不関与。✓ - 収束:
converged=meets_reference_barANDplateaued(直近3ラウンド、伸び≤0.05、3ラウンド未満は plateau 主張不可)。両方必須をループ本体で確認。max_rounds到達時はconverged:false+stop_reasonで正直終了。✓ - improve の honesty ガード:
apply_improvementは MUTABLE_FIELDS(text/assertion)のみ採用、client 名 regex で reject(generality lint)、validate_property再検証、immutable フィールドは byte-identical、無視した非可変キーはログ。lean_status/severity/label の自己昇格を構造的に禁止。出力は proposal で theorem_map は手作業反映。✓ - LLM 注入: コアは純ロジック、LLM は
judge_fn/improve_fn注入、repo は API キー無し。ユニット45 + モック end-to-end + self-hosted dispatch の3層。✓
solodit の 52 vs 53: 私が #88 で「53」と書いたのは行数(52 data + header)の数え間違いで、52 が正。meta に正直に記録し blob sha を CI で再計算照合、という対応が正しい。
CI 全緑・mergeable 確認。マージします。ただし「meets_reference_bar: True」を「#88 完了」と読むのは早いので、解釈上の留保3点を #88 に上げます(下記)。ツールの正しさとは別の、結果の妥当性の話です。
留保(このPRの欠陥ではなく、結果解釈の判断材料)
- improve ループが実 LLM で未完走: bar 達成済みでも round 1 で low-axis 項目があれば improve に入り、sonnet/opus とも cyber safeguard に refuse(rc=1)して中断。judge 単体は実走したが、自己改善の「改善」半分はモックのみ。今回は bar 到達済みで改善不要なので実害はないが、機構は実 LLM で未実証。
- judge の self-preference: CHK-15 は Claude 由来(#21 authoring)、judge も Claude(sonnet)。blind でも文体の自己選好は消えない。4.04 vs 2.50 の差に自己贔屓が混じる余地。「同等品質」を主張するなら別系統モデル or 人手アンカーでの校正が要る。
- バーが弱い: solodit がこのルーブリックで 2.50(specificity 1.8)なので「バー到達」のハードルが低い。CHK-15 が最初から超えている = ループが品質を押し上げていない。done 基準としてこれで良いかは要判断。
3点とも #88 で gohan に上げます。
This was referenced Jul 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
speca#88 確定方針(方針コメント)の ② eval ステップ: judge harness + improve ループ + 収束判定。生成 (CHK-15, plugin #21) は再実装せずそのまま入力に使う。
recall.pyは不変更(0.556 は参考値として残置、judge の合否には一切入らない)。eval は recall ではない(方針の「重要な訂正」に構造で対応)
text/assertionのみを見せ、id・出典コーパス・provenance(x_dataset_evidence等)・dataset 行は一切渡さない。reference と生成側は同一プロンプト骨格(tests/test_judge.pyがプロンプト衛生を固定)。data/ethereum_vulns.csv) は improve の教材専用(improve プロンプトにのみ入る。これもテストで固定)。5軸採点の仕様
judge.RUBRICに固定(specificity / implementation_readiness / generality / actionability / granularity、各 1-5、1/3/5 のアンカー記述付き)。応答は strict JSON で全軸必須・範囲内・integer のみ受理。欠損や範囲外は 1 retry 後にエラー — clamp や default で握らない。集計(軸平均・overall 平均/中央値/最小)は純ロジック。参照バーと合否
data/solodit_checklist.csv(52項目)を specabenchmarks/knowledge/solodit_checklist.csvから byte-identical に vendor。provenance はdata/solodit_checklist.meta.jsonに pin:8b7da09eaf87(取得時), blob sha17810eef5e78f0ad939906590b712013e3ee8dc4(テストと CI が vendored バイト列から git blob sha を再計算して照合)meets_reference_bar: 生成側 overall 平均 >= 参照 overall 平均、かつ全軸平均が参照軸平均 − tolerance (0.25) 以上(1軸だけ盛って通過することを防ぐ)。校正するのは品質水準のみで、DeFi ドメインの内容は合否に関与しない。収束判定
converged= 参照バー同等以上 AND 直近3ラウンド頭打ち(overall 平均の伸び <= 0.05) の両方。片方だけでは止まらないことをテストで固定:max_roundsまで継続し、converged: false+stop_reason: max_rounds_reached_without_convergenceで正直に終了ラウンドごとの分布・改善結果・overall 推移は
score_log.jsonに全記録。improve ループ
低スコア項目(overall < 参照平均 or いずれかの軸 <= 3)に (a) 項目 (b) judge 批評+スコア (c) 該当 label の vuln dataset 行(label 外は severity 順 fallback、最大3行)を渡して研ぎ直し。決定論ガード:
text/assertionのみ。lean_status・severity・label 等の書き換え要求は無視して記録(honesty メタデータの自己昇格を構造的に禁止)schema.validate_propertyを通ること出力
improved_01e.jsonは proposal。checklist の正本はtheorem_map.jsonのままで、反映はレビュー付きの手作業。LLM 呼び出しの設計判断
コア(プロンプト構築・パース・分布・バー判定・収束・ログ)は LLM 非依存の純ロジック、LLM は
judge_fn/improve_fn: prompt -> responseとして注入。リポジトリは API キーを一切持たない。バインディングは3層:tests/fixtures/mock_llm.pyを実際の--llm-cmdsubprocess 経路で end-to-end 実行(キーレスで緑)judge-dispatch.yml(dispatch 専用・self-hosted・認証済み Claude CLIclaude -p、speca 03/04 と同型・actor guard 付き)。検証ステップは artifact の整形のみを assert し、バー到達や収束は assert しない(未達も正当な報告結果)。実測(ローカル、認証済み claude CLI / sonnet で実走)
実 judge(ローカルの認証済み Claude CLI,
claude -p --model sonnet, blind 5軸ルーブリック, 各項目 retry 3 回):meets_reference_bar: True(bar_gaps 空)。CHK-15 の分布は overall・全軸とも solodit 参照バーを上回る(現時点で参照バー同等以上に到達)。最低スコアは CHK-JF-02(2.6, 「span within slot budget」がやや抽象的)と CHK-LV-01/CHK-JF-04(3.2)。参照バーが 2.5 と低いのは、solodit が DeFi 汎用チェックリストで多くが「Is X followed?」式の高レベル問い(specificity 平均 1.8)である一方、CHK-15 は Lean 定理×dataset 失敗クラスで算術幅・境界に踏み込んでいるため。これは方針が意図した「品質水準での校正」がそのまま出た結果で、内容一致は一切見ていない。
実 improve ループ: sonnet では improve プロンプト(監査項目を実際の攻撃面に研ぎ直す指示+vuln dataset 行)が Claude の cyber safeguard に refuse され rc=1 で中断。これは speca
03-04-lean-pilot.ymlが記録している「cwd/内容依存の sonnet refusal」と同型。重要なのは、refusal が 握り潰されず run を停止したこと(honesty ガードが意図通り作動)。opus に切り替えても同じく refuse(rc=1, 中断)。judge 側は同じ CHK 内容でも refuse されなかった(採点プロンプトは攻撃指示を含まないため)。improve ループのロジック自体はユニットテスト(決定論モック)と既定 CI のモック end-to-end で緑に検証済み。実 LLM improve は self-hosted dispatch(judge-dispatch.yml)または refusal を受けない環境が要る。テスト
ローカル 147 passed(既存 102 + 新規 45)。CI: python job にsolodit provenance honesty check とモック judge/improve の end-to-end を追加。
refs NyxFoundation/speca#88