feat(learning): chair-feedback style corrections auto-flow to the writer (P1 #6)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 3s
Lint — undefined names / undefined-names (pull_request) Successful in 10s

The chair-feedback chain was DEAD: 27 feedback rows captured, 0 ever became a
lesson the writer reads (no weekly-analysis job ever existed). The chair's own
corrections — the most authoritative signal of all, and exactly "learn from every
returned draft" — went nowhere.

decision_lessons can't carry them: it's FK-coupled to a style_corpus row (a signed
final), which a case-in-progress doesn't have. So chair STYLE feedback instead
rides the discussion_rules channel that already reaches the writer for all blocks —
the same path /training promote uses.

- db.append_global_rule: the locked read-modify-write append, extracted from web
  `_append_methodology_override` into one shared impl (G2). The web function is now
  a thin wrapper that seeds defaults; chair-feedback calls it directly.
- record_chair_feedback (MCP tool): a STYLE-category correction (style/wrong_tone/
  wrong_structure) with a lesson_extracted flows immediately to discussion_rules.
  The chair IS the gate — no separate approval (INV-LRN1 graduated gate). SUBSTANCE
  feedback (missing_content/factual_error/other) is case-specific → recorded only.

Invariants: INV-LRN1 (chair-authored style = highest authority, flows; substance
not auto-flowed), G2 (single append impl shared by promote + feedback), INV-LRN5
(style channel only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 22:06:58 +00:00
parent b0a6c2fe01
commit 8b23542dec
3 changed files with 72 additions and 31 deletions

View File

@@ -394,13 +394,35 @@ async def record_chair_feedback(
lesson_extracted=lesson_extracted,
)
# Auto-flow chair-authored STYLE feedback to the writer (closes the dead
# chair_feedback→lesson chain — 27 feedback rows had produced 0 lessons). The
# chair is the highest authority, so a style correction she writes flows
# immediately — the chair IS the gate. It rides the SAME discussion_rules channel
# promote uses (db.append_global_rule, G2), reaching every block, without the
# style_corpus coupling decision_lessons require. SUBSTANCE feedback
# (missing_content/factual_error/other) is case-specific → recorded only.
# (INV-LRN1 graduated gate; 07-learning §1.2.)
_STYLE_FB = {"style", "wrong_tone", "wrong_structure"}
flowed = 0
if lesson_extracted.strip() and category in _STYLE_FB:
try:
flowed = await db.append_global_rule(
"discussion_rules", "universal", [lesson_extracted.strip()],
)
except Exception as e:
logger.warning("chair-feedback auto-flow failed for %s: %s", case_number, e)
msg = f"הערה נרשמה בהצלחה. קטגוריה: {category}."
if flowed:
msg += " הלקח (סגנון) זרם אוטומטית לכותב."
return ok({
"feedback_id": str(feedback_id),
"flowed_to_writer": bool(flowed),
"next_steps": [
"כדי להפיק לקח מההערה, הפעל: analyze_chair_feedback",
"כדי לסמן כמטופל: resolve_chair_feedback",
],
}, message=f"הערה נרשמה בהצלחה. קטגוריה: {category}.")
}, message=msg)
_CURATOR_FINDING_CATEGORIES = {"style", "structure", "lexicon", "tabular", "general"}