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

@@ -83,6 +83,28 @@ def test_send_mail_survives_append_failure():
mailer.send_mail("u@x", "pw", "to@x", "Subj", "Body")
def test_ensure_alive_keeps_healthy_connection():
conn = fake_conn()
conn.noop.return_value = ("OK", [b""])
box = mailer.MailBox("u", "p", conn=conn)
box.ensure_alive()
assert box.conn is conn
conn.noop.assert_called_once()
def test_ensure_alive_reconnects_on_dead_connection():
conn = fake_conn()
conn.noop.side_effect = OSError("connection closed")
box = mailer.MailBox("u", "p", conn=conn)
fresh = fake_conn()
with mock.patch("projektmatch.mailer.imaplib.IMAP4_SSL",
return_value=fresh):
box.ensure_alive()
assert box.conn is fresh
fresh.login.assert_called_once_with("u", "p")
fresh.select.assert_called_once_with("INBOX")
def test_append_sent_uses_special_use_folder():
conn = mock.Mock()
conn.list.return_value = ("OK", [