Two operator-facing additions to the KB Flask app (app.py, 127.0.0.1:8092) on telep-mainframe: a floating “Tools” button injected on every page (with orphan-review + page-flow endpoints), and a persistent JSONL job queue that survives restarts/power-blips so no curation work is lost across the box’s frequent outages.

For Agents — endpoints & files

  • Per-page Tools buttonapp.py injects a floating “Tools” button on every rendered page.
  • /orphans + /api/orphans — orphan-review dashboard + JSON (over orphans.json, see 2026-08-06-kb-orphan-heading-recovery-subsystem).
  • /api/page-flow — drives the per-page Inspect / Fix-heading / Re-OCR / Apply / Rebuild flow (pageflow.py).
  • Job queuejobstore.py = a persistent JSONL at kb-vectors/jobs.jsonl; GET /api/jobs + a /jobs auto-refresh dashboard.

Why JSONL (not in-memory)

The queue survives restarts and power-blips

telep-mainframe power-cuts under load and on marginal mains (2026-08-06-power-root-cause-gpu-12v-connector-stress-test-pass, 2026-08-05-power-root-cause-nvme-damage-ups-kb-handover). An in-memory queue would lose every pending curation job on each blip. jobstore.py appends to a JSONL file (kb-vectors/jobs.jsonl), so the job history + state survive an app restart or a hard power loss — you can see what was mid-flight and resume.

What registers a job

Every mutating curation operation registers a typed job:

  • apply (orphan heading re-attach + re-embed/re-tag)
  • re-OCR (targeted micro-re-OCR of a destroyed spec-table / code page)
  • fix (heading correction)
  • rebuild (site rebuild via build_content.py)

The /jobs dashboard auto-refreshes and GET /api/jobs exposes the same data for agents.