feat(learning): graduated gate — panel-consensus style lessons auto-flow to writer (P0)
The voice-learning panel produced 81 vetted style lessons — all stuck at review_status='proposed' with 0 ever approved — so the writer (which reads only 'approved') received NONE of them. The system captured learning but never let it flow. Chair decision (2026-06-28): a GRADUATED gate by content risk. - STYLE lessons (categories style/structure/lexicon/tabular) the 2/2 panel kept → created as review_status='approved' → flow to the writer immediately, reversibly (chair vetoes in /training). The deepseek+gemini panel only emits style_method and only on 2/2 consensus, so this is exactly the gate's criterion; substance is already filtered out and skipped, and routes through the strict halacha gate. - SUBSTANCE (halacha/precedent/fact) stays a HARD chair gate — unchanged. style_lesson_panel.py: _review_status_for(category) sets the gate explicitly (approved for _STYLE_CATEGORIES, else proposed); the apply loop passes it to db.add_decision_lesson (which already accepts review_status — no DB change). Spec: INV-LRN1 rewritten as the graduated gate (hard for substance; reversible auto-flow for consensus style — still "under user control" via veto, per NCSC/CEPEJ); §0.6 updated to match. A one-time backfill of the 81 existing panel style lessons to 'approved' runs separately (DB, post-merge). Invariants: INV-LRN1 (amended — graduated), INV-LRN5 (style-only, substance never auto-flows), G10 (human control preserved as reversible veto). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,19 @@ def _category(change: dict) -> str:
|
||||
return "style"
|
||||
|
||||
|
||||
# Graduated gate (INV-LRN1, chair decision 2026-06-28): a STYLE lesson the panel
|
||||
# kept by 2/2 consensus flows straight to the writer (review_status='approved'),
|
||||
# reversibly — the chair can veto it in /training. SUBSTANCE (halacha/precedent/
|
||||
# fact) never reaches here (it's filtered to `substance` and skipped, and routes
|
||||
# through the strict 3-judge halacha gate), so every category this panel emits is
|
||||
# style and auto-approves. The constant keeps the gate explicit and future-proof.
|
||||
_STYLE_CATEGORIES = frozenset({"style", "structure", "lexicon", "tabular"})
|
||||
|
||||
|
||||
def _review_status_for(category: str) -> str:
|
||||
return "approved" if category in _STYLE_CATEGORIES else "proposed"
|
||||
|
||||
|
||||
# ── two judges, one signature: (system, user) -> dict|None ──
|
||||
|
||||
async def judge_deepseek(client: httpx.AsyncClient, system: str, user: str) -> dict | None:
|
||||
@@ -323,18 +336,24 @@ async def main(args: argparse.Namespace) -> int:
|
||||
_lesson_text(r["_change"])])
|
||||
|
||||
written = 0
|
||||
approved = 0
|
||||
for r in fresh:
|
||||
cat = _category(r["_change"])
|
||||
rs = _review_status_for(cat)
|
||||
await db.add_decision_lesson(
|
||||
UUID(corpus_id),
|
||||
lesson_text=_lesson_text(r["_change"]),
|
||||
category=_category(r["_change"]),
|
||||
category=cat,
|
||||
source="panel:deepseek+gemini",
|
||||
created_by="panel",
|
||||
review_status=rs,
|
||||
)
|
||||
written += 1
|
||||
approved += (rs == "approved")
|
||||
|
||||
chair = cc["split"] + cc["incomplete"]
|
||||
print(f"\nAPPLIED (reversible): wrote {written} decision_lesson proposals "
|
||||
print(f"\nAPPLIED (reversible): wrote {written} decision_lessons "
|
||||
f"({approved} auto-approved style → writer; graduated gate) "
|
||||
f"(source=panel:deepseek+gemini) · {skipped_dup} כפילויות דולגו · "
|
||||
f"{chair} escalated to chair · {len(substance)} substance skipped")
|
||||
print(f"backup → {backup}")
|
||||
|
||||
Reference in New Issue
Block a user