# Delivery-report investigation notes

Phase 3 milestone 20, built locally on **2026-10-19–20** during **Phase 6 pilot preparation**. Administrators can record investigation progress against an outbound attempt's linked delivery reports on web, iOS and Android. All staff can inspect retained notes. This does not verify delivery, resolve the original uncertain SMTP attempt, remove conflicts or authorize another send.

## Staff workflow

Open a conversation's **Outbound attempt history**. Attempts with linked reports show **Delivery investigation**, the current report evidence, status and paginated note history. The available statuses are **Investigating** and **Reviewed — delivery unverified**. Add a meaningful note, confirm the review boundary, then save. Each save appends a revision with the administrator's name, time and the number of reports present; existing notes cannot be edited or deleted through the application.

The review covers the exact stored report set. A new report changes that set and makes the case show **Delivery reports need investigation**, even after a previous reviewed note. The earlier note and its original scope stay in history. Recording a new reviewed note does not clear a conflicting report summary. Status is derived from the latest note and current evidence, rather than a mutable delivered/failed flag.

Refresh the investigation to load both current evidence and note history. The form clears confirmation when notes/status change or data reloads. It preserves an unsaved note through a failed save or refresh; navigation/remount can discard local unsaved text. A failed read hides stale records and disables saving. Viewers and traders have read access only. A stale save explains that reports or notes changed and requires a fresh review. No outgoing message or automatic note retry is triggered.

## Server boundary

`GET /v1/inbox/{identity}/outbound/investigations?attempt_id=UUID` returns current status, version, evidence digest/count, the latest report evidence, role-dependent `can_record`, and note history. History uses `offset`/`limit` (default ten, maximum fifty). Existing staff authorization and no-store responses apply; the attempt must belong to the named conversation.

`POST` requires the attempt ID, current investigation version, exact `evidence_token`, one of the two statuses, a 10–2,000-character note, strict `confirmed: true` and a UUID retry key. Extra fields and unsupported control characters are rejected. There is no delivered/verified action. The web proxy retains same-origin enforcement.

The route follows existing conversation/account locking and rechecks the current administrator and session after acquiring the attempt lock. It compares both the note version and a digest of the full linked report set before appending the note and audit record in one transaction. The digest includes retained report identity/content and source hash; the event also stores the exact report-ID list and count for later audit. Reads that observe an evidence-count change during loading disable recording until refreshed.

Report import uses the same attempt-row lock before attaching new evidence and rechecks submission evidence after waiting. This orders report insertion and note creation without changing the attempt lifecycle version. If import commits first, a stale note fails; if a note commits first, the subsequent report makes that note no longer current. Report summary/count/display now come from one statement snapshot, keeping contradictory summaries consistent with the evidence returned by that query.

Identical per-administrator request retries return the current ledger without another note or audit entry. A reused key with different content conflicts. An old command retried after new evidence arrives cannot review that new evidence. Separate competing commands based on the same version allow one winner. Transactional failures leave no partial note/audit entry. Original SMTP outcome, attempt recovery state, approvals and send permissions remain unchanged.

The form supports at most **1,000 linked reports per attempt** for a new note; larger cases stay readable but require operator investigation outside this form. The report panel shows its latest twenty sources and directs staff to the shared inbox for older evidence. This milestone does not add case ownership, due dates, a central attention queue, alerts, notes on unmatched/unsupported reports, or independent provider verification.

## Migration and operation

Migration `53eac242f8c3`, following `42d9c131e7b2`, creates the append-only `delivery_investigations` table and index. No historical notes or reviewed states are inferred. A nonempty table blocks destructive downgrade. Back up the target, use the normal migration/grant procedure, and restart matching API/worker/web/native versions before use. Existing live/preview business databases and credentials were not changed during this work.

No activation setting or provider connection is added. Live receiving/sending remains disabled or deferred. Local validation uses synthetic reports and disposable databases; see [VALIDATION.md](VALIDATION.md). Hosted load/recovery, provider acceptance and signed physical-device tests remain open.

The subsequent [central email attention queue](EMAIL-ATTENTION.md) indexes linked report work and unresolved attempts across conversations. The investigation rules above remain unchanged.
