fix(projekt-matching): break infinite alert loop (issue 2)

Root cause chain (evidence: 25+ failed traces on nproj/3022414):
straight quote in page text ends the guided-decoding JSON string
early -> grammar deadlock -> whitespace loop until context/token
limit -> chat_json fails 3x (~35 min) -> IMAP conn idles out ->
move_to_trash raises -> mail never trashed -> reprocessed forever.

- ingest/mailer: MailBox.ensure_alive() reconnect before finalize;
  move_to_trash failure falls back to flag_checked, run continues
- stages: page_text() feeds only div.content minus skill-tag cloud
  to the LLM and maps quote chars to apostrophes (the actual
  degeneration trigger, live-verified: extract now OK in 21 s)
- llm: requirements maxItems 40; max_tokens 8192 caps degenerate
  runs at ~100 s instead of ~10 min

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
tlg
2026-07-14 10:33:52 +02:00
parent f40a5bb0f8
commit 36eac66abc
8 changed files with 208 additions and 13 deletions

View File

@@ -119,6 +119,10 @@ def run_ingest(cfg, mailbox=None, dispatch=None, espo=None, resolve=None):
or f"unbekannter Status: {result.get('status')!r}")
results.append(result)
summary[result["status"]] += 1
try:
box.ensure_alive() # Flow-2-Dispatches können >30 min dauern — Server-Idle-Timeout
except Exception: # noqa: BLE001
pass # Alert/Trash unten haben eigene Fallbacks
failed = [r for r in results if r["status"] == "failed"]
if failed:
lines = "\n\n".join(
@@ -134,7 +138,14 @@ def run_ingest(cfg, mailbox=None, dispatch=None, espo=None, resolve=None):
"werden:\n\n" + lines + "\n")
except Exception: # noqa: BLE001
summary["alertFailed"] = summary.get("alertFailed", 0) + 1
box.move_to_trash(uid)
try:
box.move_to_trash(uid)
except Exception: # noqa: BLE001
summary["trashFailed"] = summary.get("trashFailed", 0) + 1
try:
box.flag_checked(uid) # nie erneut verarbeiten — Terminal-Zustand
except Exception: # noqa: BLE001
pass # nächster Lauf räumt auf
return summary
finally:
box.close()