Re-review fixes R2, R3, R4, R5 and R6 (job side):
- R3: Apply refuses a plan with nothing to rewrite (409 nothing-to-do, no
job, no backup). Only the first apply keeps a pre-workflow-storage
backup; later applies (after a Cancel, a rerun) take a pooled safety
backup, forced on whatever the policy, so one copy is never pruned.
- R4: a second-connection writer test runs against the live job loop; with
its pause removed the writer hits "database is locked" (4 of 5 links).
- R5: the digest tool loads only database/workflow_documents.py by path (no
package start-up, no log handler) and opens a stopped database with
immutable=1, so it leaves no -wal/-shm; a live one is read through its WAL.
- R6: a backup that fails on a damaged document says which one in the job
line.
- R2: README's changelog says images recorded by this version use the new
form from the first start, and the way back is the earliest
pre-workflow-storage backup (or a pre-update backup if the job never ran).
Final review fixes I1, I2 and I3 for the workflow storage dedupe:
- I1: the migration always takes a verified backup, whatever
maintenance.safety_backup says; the plan reports backup.policy
"required" and the disk check counts it.
- I2: that backup is prompts.db.pre-workflow-storage_<stamp>, outside the
pruned prompts.db.backup_* pool and kept until the user removes it; the
job's last line and the preview name it, and the legacy restore accepts
it. README's changelog warns that an older version opens and restores a
rewritten database but shows no workflows, and says how to go back.
- I3: tools/workflow_text_digests.py writes the sha256 of every record's
stored texts (read-only; inline before the rewrite, through the accessor
after), so a diff before and after proves byte identity.
- A row the migration could not rewrite is listed in the job's errors and
named in its last line, and Reclaim space still runs.