feat(learning): FU-2 — לכידת seed אקטיב-לרנינג בשער-היו"ר הקיים (#133)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 5s
כל הכרעת keep/drop חדה של היו"ר על הלכה שהפאנל כבר שפט (יש לה שורה ב-halacha_panel_rounds) פולטת seed gold-set מתויג-יו"ר — הסיגנל היחיד שמותר ללולאת הלמידה ללמוד ממנו. לימוד מהצבעות-הפאנל-עצמן = echo-chamber ואסור; לכן הזרע נטבע אך-ורק מהכרעה אנושית. - db.seed_goldset_from_chair(): capture-only, idempotent (UPSERT על batch='chair-live', tagged_by='chair'), לעולם לא נוגע ב-halachot ולא זורק שגיאה לתוך השער (INV-G10). ממפה approved/published→keep, rejected→drop; deferred/pending_review = נודניק, בלי seed. - db._chair_seed_label(): שער טהור (בלי DB) → guard echo-chamber unit-testable; מסנן reviewer מכונה (panel:* / corroborated*). - מחובר ב-db layer (update_halacha + update_halachot_batch) כך שכל מסלולי-השער מתכנסים (G1 נרמול-במקור, G2 בלי מסלול מקביל). הפאנל משתמש ב-SQL גולמי ולא ב-update_halacha → אין echo-chamber מבני. - מצריך שורת-פאנל קודמת: ערך-הזרע הוא זוג (הצבעות-פאנל ⋈ הכרעת-יו"ר) שמזין זיקוק-rubric (FU-4) ומדידה (FU-5). - test_chair_seed_gate.py: 10 בדיקות offline על מדיניות-השער + guard. Invariants: INV-G10 (שער-אישור יחיד, capture-only) · INV-LRN1 (propose-only — אין auto-commit) · G1/G2 · anti-echo-chamber (#133). אין UI/שער חדש (INV-IA). תצוגת-הצבעות-הפאנל ב-HalachaReviewPanel (אופציונלי) נדחית — מצריכה שער-עיצוב Claude Design. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
66
mcp-server/tests/test_chair_seed_gate.py
Normal file
66
mcp-server/tests/test_chair_seed_gate.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""Tests for #133 / FU-2 — the chair-decision active-learning seed gate.
|
||||
|
||||
Covers the PURE gate function db._chair_seed_label, which decides whether (and
|
||||
with what is_holding label) a chair decision on a halacha should mint a gold-set
|
||||
seed. The DB write (seed_goldset_from_chair) and the prior-panel-round filter
|
||||
need a live Postgres and are exercised via the integration smoke test in the
|
||||
task's testStrategy; here we lock down the policy offline.
|
||||
|
||||
The critical invariant under test: a seed is NEVER minted from a machine
|
||||
reviewer (echo-chamber guard, #133) — only firm human keep/drop decisions.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from legal_mcp.services import db
|
||||
|
||||
|
||||
# ── firm decisions map to the coarse is_holding axis ──────────────────────────
|
||||
|
||||
def test_approved_is_keep():
|
||||
assert db._chair_seed_label("approved", "דפנה") is True
|
||||
|
||||
|
||||
def test_published_is_keep():
|
||||
assert db._chair_seed_label("published", "דפנה") is True
|
||||
|
||||
|
||||
def test_rejected_is_drop():
|
||||
assert db._chair_seed_label("rejected", "דפנה") is False
|
||||
|
||||
|
||||
# ── non-firm statuses mint no seed (a snooze is not a judgment) ────────────────
|
||||
|
||||
def test_deferred_no_seed():
|
||||
assert db._chair_seed_label("deferred", "דפנה") is None
|
||||
|
||||
|
||||
def test_pending_review_no_seed():
|
||||
assert db._chair_seed_label("pending_review", "דפנה") is None
|
||||
|
||||
|
||||
def test_unknown_status_no_seed():
|
||||
assert db._chair_seed_label("", "דפנה") is None
|
||||
|
||||
|
||||
# ── echo-chamber guard: machine reviewers never seed ──────────────────────────
|
||||
|
||||
def test_panel_reviewer_blocked():
|
||||
"""The 3-judge panel must never label its own ground-truth (echo-chamber)."""
|
||||
assert db._chair_seed_label("approved", "panel:opus+deepseek+gemini 2/3-keep") is None
|
||||
|
||||
|
||||
def test_corroboration_reviewer_blocked():
|
||||
assert db._chair_seed_label("approved", "corroborated (4 judicial citations ≥ 2)") is None
|
||||
|
||||
|
||||
def test_panel_reviewer_blocked_case_insensitive():
|
||||
assert db._chair_seed_label("rejected", "PANEL:opus") is None
|
||||
|
||||
|
||||
# ── empty reviewer is still a human gate (UI sends no reviewer string) ─────────
|
||||
|
||||
def test_empty_reviewer_is_human_gate():
|
||||
"""The /precedents UI patches review_status with no reviewer string; that is
|
||||
still the chair gate (the panel/corroboration use raw SQL, not update_halacha)."""
|
||||
assert db._chair_seed_label("approved", "") is True
|
||||
Reference in New Issue
Block a user