buyer/pdf: durable job store + past-work list + resume-on-return

The summarization runs server-side, but the SPA held the job only in
component state and jobs lived only in memory — so navigating away from
the Book Summarizer reset the UI (losing the handle to a still-running
job) and past summaries vanished on restart.

- PdfJobDb: SQLite store (its own file under ARCHIVE_DIR) persisting
  every job on each progress update.
- JobStore: createdAt, persist-on-update, viewOf()/list() that serve
  live jobs first then fall back to the durable record; non-live jobs
  whose worker died surface as a new "interrupted" status.
- routes: GET /v1/pdf-jobs (newest-first list); GET /:id + summary.md
  serve past jobs via viewOf; /:id/events sends a single terminal frame
  for a finished/past job (no hang) and live frames otherwise.
- SPA: RecentJobs list (open any job → reconnect to live progress or
  show result), localStorage resume of the active job on return, and a
  "back to list (keeps running)" affordance.
- mainnet buyer compose: persistent named volume + ARCHIVE_DIR so the
  archive + pdf-jobs.db survive container recreation/redeploys.

Tests: persistence-across-restart, interrupted detection, list, and
past-job serving (incl. terminal SSE). Full buyer suite green (185).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
david
2026-06-03 11:59:20 +02:00
parent c552c4caa3
commit cf77811331
10 changed files with 674 additions and 50 deletions

View File

@@ -21,7 +21,13 @@ services:
environment:
INDEXER_URL: http://marketplace-mainnet-indexer:8090
BUYER_PORT: "8070"
# Pin the archive dir so it lands on the persistent volume below — keeps
# the response archive + PDF job store (past summaries) across container
# recreation/redeploys.
ARCHIVE_DIR: /repo/data/archive
# NETWORK_ID intentionally not pinned — see supplier compose comment.
volumes:
- buyer-mainnet-archive:/repo/data/archive
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8070/healthz >/dev/null || exit 1"]
interval: 20s
@@ -50,3 +56,6 @@ networks:
traefik-net:
external: true
name: traefik-net
volumes:
buyer-mainnet-archive: