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

5.3 KiB

Task 3 report: store many immutable sources in one batch

Next.js 16 documentation read

Before editing route/component code, read the version-matched docs required by the plan from node_modules/next/dist/docs/:

  • 01-app/01-getting-started/15-route-handlers.md (Request/FormData and Route Handler contract)
  • 01-app/02-guides/upgrading/version-16.md (Next 16 runtime and async request API guidance)

RED/GREEN evidence

RED baseline: npx vitest run src/app/api/delivery-notes/upload/route.test.ts failed against the old one-batch-per-file implementation when the new dn_batch_sources contract was exercised (the old test/mocking contract had no source collection and multi-file behavior expected separate batches).

GREEN: npx vitest run src/app/api/delivery-notes/upload/route.test.ts passed: 1 file, 5 tests.

Type verification: npm run typecheck passed (tsc --noEmit).

Diff hygiene: git diff --check passed.

Implementation

  • Changed src/app/api/delivery-notes/upload/route.ts to create one batch per request and immutable dn_batch_sources rows per accepted file.
  • Added per-source capture/session/time/GPS metadata parsing, SHA-256 source deduplication, capture-session idempotency, 20 MB/file and 50-file limits, and mixed-validity per-file outcomes.
  • Retained the legacy single-file response fields and added multi-file batch_id, source_count, status, results, and summary data.
  • Updated src/components/upload-delivery-note-button.tsx to report files in one processing batch and show per-file duplicate/error details.
  • Replaced route tests with focused coverage for one-batch/three-source creation, repeated capture sessions, duplicate hashes, mixed valid/invalid files, metadata, and the legacy response.

Self-review and concerns

  • Only the three scoped Task 3 implementation/test files were changed; report evidence is stored alongside the SDD and Clock/photo-app files were untouched.
  • Invalid files are represented in a 201 multi-file response so valid sources are not lost; a single invalid file retains the legacy 400 validation payload (including max_bytes, got, and allowed where applicable).
  • dn_batches.file and file_hash are populated from the first source for legacy readers while the canonical originals are stored in dn_batch_sources.
  • The route persists dn_batches.source_count after source writes and deletes a newly-created batch when no source can be persisted.

Commit

6e298990e407e3f71085ece4f432259f87c6403d fix(dn): harden bulk upload idempotency

Fix round 2 implementation:

00e82e51a51a17eb4545d2b74bfbf44bc80570b0 fix(dn): close bulk upload consistency gaps

Fix round 1 evidence

  • Preflighted source hashes before batch creation. Single-duplicate and all-duplicate multi-file requests create no new dn_batches row.
  • Replaced capture-session interpolation with PocketBase SDK binding via pb.filter("capture_session_id = {:captureSession}", { captureSession }); the suite covers quotes, backslashes, and control-like newlines.
  • Restored single-file 400 validation payloads for empty, oversized, and unsupported files; retained mixed multi-file partial success behavior.
  • Persisted dn_batches.source_count with the successful source count and deleted the newly-created batch if all source writes fail.
  • Expanded the focused route suite to 11 tests, retaining one-batch/source, metadata, duplicate, mixed-validity, and legacy response coverage.

Exact fix-round verification:

npx vitest run src/app/api/delivery-notes/upload/route.test.ts — passed, 1 file, 11 tests.

npm run typecheck — passed (tsc --noEmit).

git diff --check — passed.

Fix round 3 implementation commit:

05c9b6b8f084ac71e9cc462d521b1c48539855db fix(dn): contain bulk batch lifecycle

Fix round 3 evidence

  • New batches are created as status: "needs_attention" and stage: "preparing", so a surviving batch is never processable while source_count persistence is unresolved. Only the successful final update sets source_count, status: "inbox", and stage: "queued".
  • If that final update fails, deletion is attempted. If deletion also fails, the response/log report both failures and the surviving batch remains in its initial non-processable state.
  • In-request hash grouping now reports deferred copies as duplicates only when the primary source write succeeds; if it fails, all grouped copies report the same visible source-write error.

Exact fix-round 3 verification:

npx vitest run src/app/api/delivery-notes/upload/route.test.ts — passed, 1 file, 15 tests.

npm run typecheck — passed (tsc --noEmit).

git diff --check — passed.

Fix round 2 evidence

  • Oversized single-file validation now returns the exact legacy payload with error, filename, max_bytes, and got.
  • Equal hashes within one request are deduplicated in memory; only one source is written and later duplicate outcomes point at the new batch.
  • A dn_batches.source_count update failure deletes the newly-created batch and returns HTTP 500 with a visible error. If deletion also fails, the response and log include both the update and cleanup failures.

Exact fix-round 2 verification:

npx vitest run src/app/api/delivery-notes/upload/route.test.ts — passed, 1 file, 14 tests.

npm run typecheck — passed (tsc --noEmit).

git diff --check — passed.