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

@@ -24,11 +24,16 @@ def test_chat_json_strips_think_and_parses():
{"type": "object"})
assert out == {"a": 1}
body = p.call_args.kwargs["json"]
assert "max_tokens" not in body
assert body["max_tokens"] == 8192
assert body["chat_template_kwargs"] == {"enable_thinking": False}
assert body["response_format"]["json_schema"]["schema"] == {"type": "object"}
def test_extract_schema_caps_requirements_list():
reqs = llm.EXTRACT_SCHEMA["properties"]["requirements"]
assert reqs["maxItems"] == 40
def test_chat_json_strips_markdown_fences():
content = "```json\n{\"a\": 2}\n```"
with mock.patch("projektmatch.llm.requests.post", fake_post([content])):