fix: retry failed analyses (no silent drops) + bump Bun to 1.4.2 #2

Merged
nick merged 1 commit from bugfix/retry-failed-analyses into main 2026-09-27 17:33:46 +02:00
Owner

A file whose analysis FAILED (LLM timeout, download error, …) was marked processed with NO record row and was never re-analyzed — the 2026-09-23 WhatsApp appointment screenshot was dropped this way (26 files total, root cause: transient LLM TimeoutError).

What this does

  • Marker state machine on processed_files (NULL=done, a:<n>=retry-pending, ae:<n>=exhausted): a failed file is NOT marked done and is re-analyzed on every poll tick until it succeeds or FILE_RETRY_MAX_ATTEMPTS (default 3) is used up.
  • Visible error audit row per failed file (rec_<id>, status error, "analysis failed (attempt n): …") — new Errors filter in the dashboard.
  • Retry queue in onedrive-poll: marker-driven, re-fetches each file's current Graph state by id (delta never re-delivers unchanged items); 404 → done + row retired.
  • Operator actions: "Retry analysis" (POST /records/repending, re-arms the marker; double-clicks don't consume budget) and "Give up" (POST /records/giveup).
  • One-time startup backfill (CAS-gated in kv): processed-but-rowless analyzable files are re-queued — this recovers the 26 silently dropped files, including the WhatsApp screenshot, on deploy.
  • Bun 1.4 → 1.4.2 (Dockerfile + CI + .mise.toml): the 2026-09-23 container segfaults were on musl/alpine; 1.4.2 fixes a rare JIT crash and a GC crash on musl.
  • bun test --parallel in the test script (per-file worker isolation; the process-global mock.module leaked across files).

Tests

  • test/file-retry.test.ts (10 new): marker state machine, repending/giveup, auto-reject sweep skip, backfill, fail → retry-fail → retry-success and exhaustion end-to-end.
  • Full suite: 224 pass / 1 skip / 0 fail; tsc --noEmit clean.

Spec updated: specs/appointment_detection.md ("Failed-analysis retry (no silent drops)").

A file whose analysis FAILED (LLM timeout, download error, …) was marked processed with NO record row and was never re-analyzed — the 2026-09-23 WhatsApp appointment screenshot was dropped this way (26 files total, root cause: transient LLM TimeoutError). ## What this does - **Marker state machine** on `processed_files` (`NULL`=done, `a:<n>`=retry-pending, `ae:<n>`=exhausted): a failed file is NOT marked done and is re-analyzed on every poll tick until it succeeds or `FILE_RETRY_MAX_ATTEMPTS` (default 3) is used up. - **Visible error audit row** per failed file (`rec_<id>`, status `error`, "analysis failed (attempt n): …") — new **Errors** filter in the dashboard. - **Retry queue** in `onedrive-poll`: marker-driven, re-fetches each file's current Graph state by id (delta never re-delivers unchanged items); 404 → done + row retired. - **Operator actions**: "Retry analysis" (`POST /records/repending`, re-arms the marker; double-clicks don't consume budget) and "Give up" (`POST /records/giveup`). - **One-time startup backfill** (CAS-gated in `kv`): processed-but-rowless analyzable files are re-queued — this recovers the 26 silently dropped files, including the WhatsApp screenshot, on deploy. - **Bun 1.4 → 1.4.2** (Dockerfile + CI + `.mise.toml`): the 2026-09-23 container segfaults were on musl/alpine; 1.4.2 fixes a rare JIT crash and a GC crash on musl. - `bun test --parallel` in the test script (per-file worker isolation; the process-global `mock.module` leaked across files). ## Tests - `test/file-retry.test.ts` (10 new): marker state machine, repending/giveup, auto-reject sweep skip, backfill, fail → retry-fail → retry-success and exhaustion end-to-end. - Full suite: 224 pass / 1 skip / 0 fail; `tsc --noEmit` clean. Spec updated: `specs/appointment_detection.md` ("Failed-analysis retry (no silent drops)").
fix: retry failed analyses (no silent drops) + bump Bun to 1.4.2
All checks were successful
CI/CD / Typecheck & test (pull_request) Successful in 4m45s
CI/CD / Build & push app image (pull_request) Has been skipped
CI/CD / Deploy to Dockhand (pull_request) Has been skipped
ed4c394a84
A file whose analysis FAILED (LLM timeout, download error, ...) was marked
processed with NO record row and was never re-analyzed — the 2026-09-23
WhatsApp appointment screenshot was dropped this way (26 files total).

Fix:
- processed_files gains a marker column: NULL = done (legacy), 'a:<n>' =
  retry-pending after n failures, 'ae:<n>' = failed + budget exhausted.
- file-analysis process step is state-driven: on failure the file is NOT
  marked done; a single audit row (stable id rec_<id>, status 'error',
  'analysis failed (attempt n): ...') is written; on success the same row
  is upserted with the real classification and persist FORCES the
  pipeline status (so a retry clears the stale 'error').
- onedrive-poll collects the retry queue (marker 'a:<n>') each tick,
  re-fetches each file's current Graph state by id (delta never
  re-delivers unchanged items) and merges it into the tick; 404 → done +
  row retired.
- Budget: FILE_RETRY_MAX_ATTEMPTS (env, default 3) — a permanently broken
  file stops burning an LLM call every 5 min and is left to the operator.
- Operator UI: Errors filter; 'Retry analysis' (POST /records/repending on
  an error row: row → pending + marker re-armed; double-clicks don't
  consume budget) and 'Give up' (POST /records/giveup: row → rejected).
- auto-reject startup sweep skips retry-queued rows (pending rows whose
  raw summary is 'analysis failed ...').
- One-time startup backfill (backfill-dropped.ts, CAS-gated in kv):
  processed entries that are 'done' but have no rec_<id> row (and an
  analyzable name) are re-queued for re-analysis — recovers the 26 files
  silently dropped before this fix.
- bun test now runs with --parallel (per-file worker isolation; the
  process-global mock.module used to leak across files).

Bun 1.4 → 1.4.2 (Dockerfile + CI + .mise.toml): the 2026-09-23 container
segfaults were on musl/alpine; v1.4.2 fixes 'a rare JIT crash' and 'a GC
crash on musl' — the same failure class.

Spec: specs/appointment_detection.md gains the 'Failed-analysis retry
(no silent drops)' section; the poll-loop pseudocode now shows the retry
queue.

Tests: test/file-retry.test.ts (10) covers the marker state machine,
repending/giveup, the sweep skip, the backfill, and fail→retry-fail→
retry-success / exhaustion end-to-end. Full suite green (225 tests,
bun test --parallel) + tsc --noEmit.
nick merged commit 9d111cc871 into main 2026-09-27 17:33:46 +02:00
nick deleted branch bugfix/retry-failed-analyses 2026-09-27 17:33:46 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
home/agent!2
No description provided.