diff --git a/.superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion/task-8-report.md b/.superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion/task-8-report.md index 4ba86a0..1d45de3 100644 --- a/.superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion/task-8-report.md +++ b/.superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion/task-8-report.md @@ -156,3 +156,53 @@ failure handler rewriting a candidate. month-end/receipt/invoice payable semantics remain unchanged. - Clock/photo/image-quality/nearest-site behaviour and migration scope remain unchanged. No production PocketBase writes were performed. + +## Fix Round 2 + +Status: complete. + +### RED evidence + +The new regression tests were run before the production changes. The focused +promotion suite reported **3 failed, 23 passed**: candidate read failures were +treated as absence, concurrent promoters both proceeded, and a second +promoter could enter while the first was persisting a failure marker. + +### GREEN evidence + +- Focused promotion/runner tests: **67 passed** (`promote.test.ts`, + `run.test.ts`). +- Full delivery-note pipeline: **211 passed** across 15 files. +- Isolated Poppler split test: **6 passed**. +- Full repository suite, serial workers: **1,100 passed** across 93 files. +- `npm run typecheck`: passed. +- `python3 scripts/pb-add-dn-staged-ingestion.py --dry-run`: passed without + credentials or writes. +- `git diff --check`: passed. + +### Fixes and regression coverage + +- `promoteNote` now acquires the shared acquisition-specific batch lease before + reading the candidate. Candidate read errors are fail-closed and cannot be + interpreted as candidate absence; no delivery row or item mutation occurs. +- The complete promotion attempt—terminal candidate check, bound delivery-note + lookup/create/reconciliation, item replacement, posted marker, and durable + failure persistence—runs under the same `withBatchLease(pb, batchId, ...)`. + Lease losers receive retryable `promotion_in_progress` and perform no writes. +- The runner no longer performs an unprotected candidate `needs_review` update + after promotion failure. Promotion owns failure marking while holding the + lease, and the runner records the batch/check failure only. +- Added interleaving tests proving a second promoter cannot post while the + first owns the lease during delivery creation or failure-marker persistence, + plus fail-closed candidate-read coverage. + +### Fix Round 2 self-review + +- Code commit: `c37821d232259be314fb78928f2d71e21f71c0f4` + (`fix(dn): serialize promotion under batch lease`). +- Create-race recovery, supplier-scoped duplicate identity, unknown-supplier + review semantics, zero unmatched quantity, authoritative MIME/name handling, + post-after-all-line-writes ordering, and posted immutability remain intact. +- Parallel full-suite runs consistently hit the pre-existing Poppler split + fixture's 5-second timeout under load (1,099 passed, 1 timeout); the isolated + and serial full-suite runs pass. No production PocketBase writes were made.