feat(learning): raise writer lessons cap 15 → 60 (config.WRITER_LESSONS_LIMIT) — closes #157
עכשיו שהלקחים מנוכי-כפילות (סינתזה #158) ומאושרי-יו"ר, התקרה 15 חתכה בשקט הנחיות-סגנון מובחנות ו-vetted (רישוי/סגנון 29>15). מעלים ל-60 (env-tunable WRITER_LESSONS_LIMIT) — מכסה כל shard נוכחי עם מרווח; הסינתזה שומרת על אי-רדודנטיות בצמיחה. ה-WARN על חיתוך נשאר רשת-הביטחון (חוקה §6) ומפנה לסינתזה/העלאת-התקרה. block_writer צורך דרך config. Invariants: INV-LRN1/G10 (עדיין רק approved זורם; superseded מוחרג), G2. closes #157 בפועל. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -293,6 +293,13 @@ LESSON_SYNTH_DRIFT_FLOOR = float(os.environ.get("LESSON_SYNTH_DRIFT_FLOOR", "0.8
|
|||||||
# quality backstop below this.
|
# quality backstop below this.
|
||||||
LESSON_SYNTH_CLUSTER_THRESHOLD = float(os.environ.get("LESSON_SYNTH_CLUSTER_THRESHOLD", "0.68"))
|
LESSON_SYNTH_CLUSTER_THRESHOLD = float(os.environ.get("LESSON_SYNTH_CLUSTER_THRESHOLD", "0.68"))
|
||||||
|
|
||||||
|
# How many CHAIR-APPROVED decision_lessons flow to the writer per practice_area
|
||||||
|
# (get_recent_decision_lessons). Raised 15 → 60 (#158/#157): the lessons are now both
|
||||||
|
# deduplicated (synthesis merges true near-duplicates) AND chair-approved, so the old
|
||||||
|
# cap silently truncated distinct, vetted style guidance. 60 covers every current shard
|
||||||
|
# with headroom; synthesis keeps the set non-redundant as it grows. env-tunable.
|
||||||
|
WRITER_LESSONS_LIMIT = int(os.environ.get("WRITER_LESSONS_LIMIT", "60"))
|
||||||
|
|
||||||
# Mistral OCR (fallback for scanned PDFs — replaces Google Cloud Vision)
|
# Mistral OCR (fallback for scanned PDFs — replaces Google Cloud Vision)
|
||||||
MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY", "")
|
MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY", "")
|
||||||
|
|
||||||
|
|||||||
@@ -1028,7 +1028,8 @@ async def _build_style_context(practice_area: str = "") -> str:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning("methodology overrides not loaded: %s", e)
|
logger.warning("methodology overrides not loaded: %s", e)
|
||||||
try:
|
try:
|
||||||
lessons = await db.get_recent_decision_lessons(limit=15, practice_area=practice_area)
|
lessons = await db.get_recent_decision_lessons(
|
||||||
|
limit=config.WRITER_LESSONS_LIMIT, practice_area=practice_area)
|
||||||
if lessons:
|
if lessons:
|
||||||
learned.append("\n**לקחים מהחלטות קודמות (decision_lessons):**")
|
learned.append("\n**לקחים מהחלטות קודמות (decision_lessons):**")
|
||||||
for ls in lessons:
|
for ls in lessons:
|
||||||
|
|||||||
@@ -3147,19 +3147,18 @@ async def get_style_distance_history() -> list[dict]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
async def get_recent_decision_lessons(limit: int = 15, practice_area: str = "") -> list[dict]:
|
async def get_recent_decision_lessons(limit: int = 60, practice_area: str = "") -> list[dict]:
|
||||||
"""Per-decision learnings the chair/curator attached in /training (decision_lessons),
|
"""Per-decision learnings the chair/curator attached in /training (decision_lessons),
|
||||||
so the writer consumes them too (T15). Prefers style/structure/lexicon, recent first.
|
so the writer consumes them too (T15). Prefers style/structure/lexicon, recent first.
|
||||||
|
|
||||||
Gate (INV-LRN1/G10): returns only CHAIR-APPROVED lessons (review_status='approved').
|
Gate (INV-LRN1/G10): returns only CHAIR-APPROVED lessons (review_status='approved');
|
||||||
Panel-written proposals stay out of the writer's context until the chair approves
|
superseded sources (merged by synthesis, #158) are excluded automatically.
|
||||||
them in /training — the chair's review is the gate, not the panel's 2/2 vote.
|
|
||||||
|
|
||||||
No silent cap (חוקה §6): when more approved lessons match than `limit`, the
|
Cap (#157/#158): callers pass config.WRITER_LESSONS_LIMIT (60). Raised from 15 once
|
||||||
overflow is dropped by ORDER BY created_at DESC — log it so coverage loss is
|
lessons became deduplicated (synthesis) AND chair-approved — the old cap silently
|
||||||
observable, not invisible. The real fix is lesson synthesis (TaskMaster #158):
|
truncated distinct, vetted style guidance. If even 60 is exceeded the overflow is
|
||||||
consolidate overlapping lessons into fewer, richer ones so the set fits without
|
dropped (ORDER BY created_at DESC) and logged (no silent cap, חוקה §6); that WARN is
|
||||||
truncation. Until then this WARN is the audit trail.
|
the signal to run synthesis (lesson_synthesize_pending) or raise WRITER_LESSONS_LIMIT.
|
||||||
"""
|
"""
|
||||||
pool = await get_pool()
|
pool = await get_pool()
|
||||||
async with pool.acquire() as conn:
|
async with pool.acquire() as conn:
|
||||||
@@ -3185,7 +3184,8 @@ async def get_recent_decision_lessons(limit: int = 15, practice_area: str = "")
|
|||||||
if total > limit:
|
if total > limit:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"get_recent_decision_lessons: capped %d→%d approved lessons "
|
"get_recent_decision_lessons: capped %d→%d approved lessons "
|
||||||
"(practice_area=%r) — %d not reaching the writer. See TaskMaster #158 (synthesis).",
|
"(practice_area=%r) — %d not reaching the writer. Run synthesis "
|
||||||
|
"(lesson_synthesize_pending) or raise WRITER_LESSONS_LIMIT.",
|
||||||
total, limit, practice_area or "*", total - limit,
|
total, limit, practice_area or "*", total - limit,
|
||||||
)
|
)
|
||||||
return [dict(r) for r in rows]
|
return [dict(r) for r in rows]
|
||||||
|
|||||||
Reference in New Issue
Block a user