MEGAINFRA-WEBAPP/.superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion/final-fix-report.md

4.7 KiB

Final cross-feature fix wave report

Base: 9ace0fb
Code commit: 9ae498a

Addressed blockers

  • Staged assembly now aggregates every ordered page read, uses first-nonempty header evidence, preserves every extracted line, persists line links and the complete canonical check list, and marks a candidate ready only when PO/site/supplier/item/check gates are satisfied.
  • Added a two-page continuation regression proving that distinct lines on both pages survive aggregation.
  • Manual assembly split/join now clears derived document identity, supplier/PO relations, items, checks, match evidence, candidate identity, and posting relation for every affected nonterminal candidate. New candidates start needs_review; posted/dismissed candidates remain protected.
  • Added optional global_page to canonical pages, assigned during staged normalisation, with a batch-wide unique index. Posting and page proxy lookup use it. Legacy rows are remapped only when source ordinal/page identities are contiguous and unique; mixed or gapped rows fail closed.
  • Added a missing-middle-page posting regression: persisted global pages 1 and 3 with candidate [3] selects page 3.

Verification evidence

Commands run from the worktree:

npx vitest run src/lib/dn-pipeline/run-staged.test.ts src/lib/dn-pipeline/page-store.test.ts src/lib/dn-pipeline/normalise-source.test.ts src/lib/dn-pipeline/promote.test.ts src/lib/dn-pipeline/readiness.test.ts src/lib/dn-pipeline/assemble.test.ts "src/app/api/dn-batches/[id]/assembly/route.test.ts" "src/app/api/dn-batches/[id]/notes/[noteId]/post/route.test.ts"
  8 files passed, 89 tests passed
npm run typecheck
  passed
python3 scripts/pb-add-dn-staged-ingestion.py --self-test
  self-test: index replacement and idempotence passed
python3 scripts/pb-add-dn-staged-ingestion.py --dry-run
  offline preview completed; no schema changes written
git diff --check
  passed

Residual risk

The migration remains unapplied and DN_STAGED_INGESTION_ENABLED remains unchanged. Existing rows without global_page and with a source-page gap require manual repair/re-extraction; the posting path intentionally refuses to infer their identity.

Follow-up P1 fixes

  • Exported evaluateRequiredChecks; the staged runner and posting readiness gate now share the exact required/optional policy. A null product_matched, units_convertible, or qty_plausible result cannot produce ready_to_post.
  • Staged checks now load cumulative delivery_note_items.qty_received totals once per run and pass them to qty_plausible. Missing schema, failed reads, or invalid quantities persist a failed delivered_totals_available gate and fail closed. A sequential over-delivery regression proves the current candidate remains review-only.

Follow-up code commit: 199dfba
Follow-up focused verification: 4 files, 92 tests passed; npm run typecheck; git diff --check.

Follow-up 2

delivery_note_items totals are now queried with an escaped OR filter built from the batch's site-scoped purchase-order item IDs. Rows outside that set are ignored before validation. Relevant malformed quantities, including negatives, add a failed persisted gate; zero remains valid. Added regressions for unrelated malformed rows, relevant malformed/negative rows, and cumulative over-delivery.

Follow-up 2 code commit: bb70f33
Follow-up 2 focused verification: 4 files, 95 tests passed; npm run typecheck; git diff --check.

Migration idempotency follow-up

merge_indexes now compares semantic-normalized CREATE INDEX definitions: index names, keywords, quoted identifiers, whitespace, and punctuation are normalized for comparison, while uniqueness, column order/content, and WHERE predicates remain meaningful. Self-tests cover the exact PocketBase uppercased/multiline variants and near-miss definitions that must replace.

Migration code commit: bee2b3d
Verification: migration self-test passed; authenticated live --dry-run against the configured PocketBase reported every collection/index already present and ended with DRY RUN. No schema changes written.; git diff --check passed. No live writes were made.

Migration normalizer follow-up

The comparison now treats backtick-quoted, double-quoted, and safely unquoted SQLite identifiers as equivalent. It tokenizes around double/backtick identifiers and preserves single-quoted literals exactly, so a literal near-miss remains a real replacement. Self-tests cover all identifier styles and a changed single-quoted predicate literal.

Normalizer code commit: 1a97d7d
Verification: migration self-test passed; authenticated live --dry-run reported all indexes already present and no changes; git diff --check passed. No schema apply was performed.