A non-AIError from transcribe/summarize (av.InvalidDataError on corrupt
audio, seen live with 'My recording 63') escaped the consumer, left the
row 'running', and got requeued at every engine restart forever. Both
runners now catch it, fail the row with the exception type in the
message, and keep a usable transcript (summarize completes with a
'Summary failed' note). Regression tests pin both paths (proven red
without the fix).
The reprocess endpoint poked transport_enqueue while its request session
was still uncommitted; the inline worker woke instantly, read the job row
with its own session, and summarized with the PREVIOUS click's tone
(TRACE: click Funny -> worker read 'dry, witty'; click Neutral -> ran
'funny'). The UI disabled the voice chips for the whole wrong-voice run,
which read as 'spinner does nothing until I leave and come back'.
Rows are the real queue: commit before the poke. Regression test pins
worker tone to the requested tone and Neutral clearing a prior voice;
verified red without the fix, green with it.
- Engine: when the primary summarizer is out of retries (or misconfigured),
run_summarize now finishes the job on the configured rescue provider
(SHONAR_LLM_FALLBACK_*), tags the summary with the provider that wrote it,
and stores a human note in job.error; success clears stale notes.
- App (auto/lan): passes Ollama as the rescue provider when it is up.
- Detail screen: shows the rescue-swap note in plain words, a red
'Summary failed' line with Settings -> Summarizer fix instructions and a
Retry summary button on hard failure.
- Settings copy explains the fallback. 3 new pytest cases (14/14 pass);
live E2E on 2026-09-18: sarcastic summary v6 via LAN on attempt 2.
POST /reprocess?job=summarize&tone=<voice> persists the voice on the job
row (queue carries only ids, so a sweep re-enqueue keeps it), the LLM
prompt appends 'write every field in a <tone> tone — the tone colors
the wording, never the facts', and the resulting summary records its
tone (SummaryOut.tone). Plain re-summarize clears a previous tone.
Migration tone0000000001 (summaries.tone, processing_jobs.tone).
78 passed, 1 skipped; ruff clean.
- shared/ = portable Android-origin sources vendored from deferred/desktop-server
(app/build.gradle.kts srcDir repointed; PlaybackController.kt excluded as Android-only)
- backend/ = bundled-lite engine (SQLite + inline queue); .venv symlinked from the
old checkout, PYTHONPATH pins THIS backend's code over any editable install
- repoRoot() resolves this project dir (env SHONAR_REPO still wins); desktop-dev.sh
watches shared/ + backend/
- Verified: :app:compileKotlin + :app:test green (23 tests); engine boots on :8010,
self-migrates, /healthz ok