# Production preview UAT and long-running request control

Use this reference when a release requires an exact-commit production Preview against real source files while Confirm remains forbidden.

## 1. Freeze and bind the input

- Build one owner-only Preview payload from immutable source-file bytes and active canonical source mappings.
- Hash every source file before and after Preview.
- For a provider file that mixes authoritative row-level source IDs with blank IDs, include the approved file-level fallback `source_reference` and the full allowed `mapping_ids` set. The fallback is part of the Preview fingerprint; omitting it can create false `account_source_mapping_missing_or_mismatch` findings for otherwise valid blank-reference rows.
- Never substitute a similar account mapping. Row-level source ID wins; fallback applies only where the provider contract leaves it blank.

## 2. Prove read-only behavior

Capture a logical per-table digest before and after the production Preview, not merely file mtime or SQLite byte equality. Also compare:

- `foreign_key_check` and `integrity_check`;
- import-batch count;
- staging/candidate count;
- canonical transaction count;
- source-file hashes.

Require no changed tables and no new batches/candidates. Save detailed rows, tokens, account hints, and merchant identities only in owner-only evidence (`0600`); publish masked aggregates.

## 3. Control long-running HTTP previews

A client timeout or disconnected socket does **not** prove that a synchronous server-side Preview stopped. CPU-bound request work may continue in the application thread pool.

- Do not immediately issue another full Preview after a client timeout.
- First determine whether the original server computation is still active. Prefer request/job correlation when available; otherwise observe service CPU/log completion.
- If cancellation cannot be established and an authorized maintenance window exists, restart the application service once to terminate orphaned computations, verify readiness, and then launch exactly one retry with a timeout longer than the measured Preview duration.
- Run the retry as one tracked bounded background process and wait for its real exit status.
- Never infer success from health endpoint responsiveness alone: a thread-pooled service can answer health while an orphaned Preview still consumes CPU.

For expensive historical comparisons, execute the previous exact commit in a separate detached worktree against the same read-only database and identical payload. Keep it in a separate process so module imports cannot mix versions. Record both commit SHAs in evidence.

## 4. Interpret review numbers correctly

Publish distinct values rather than one overloaded “review” count:

- total rows with review disposition;
- total decision-needed rows;
- rows absorbed into multi-row merchant clusters;
- grouped cluster count;
- singleton individual-review count;
- ordinary-monetary coverage numerator/denominator;
- technical confirmability;
- business readiness.

A useful remaining-decision count is `grouped clusters + singleton rows`, but guardrailed clusters that forbid a shared category must be called out separately.

## 5. Browser and deployed-bundle UAT

Test the actual deployed route at desktop and CSS/mobile widths. Verify:

- import page, file control, confirmed mapping selector, and Preview action are reachable;
- no document-level horizontal overflow;
- no browser console/JavaScript errors;
- the deployed bundle contains and can render technical readiness, business readiness, and grouped merchant-decision controls when a Preview result is present.

Static route reachability plus API success is not evidence that a post-Preview panel rendered. If full browser upload is impractical, state exactly which parts were exercised live and which were verified from the deployed bundle/component contract.

## 6. Closeout ordering

1. Finish Preview and before/after evidence.
2. Verify services, exact `HEAD == origin/main`, and production connection-factory FK enforcement.
3. Re-enable schedulers only after UAT.
4. Stop before Confirm.
5. Report that Previewed real-file rows cannot appear as newly imported portal transactions until a separately authorized Confirm.

Do not confuse raw Python `sqlite3.connect()` defaults with the production connection factory: `PRAGMA foreign_keys` is connection-local. Verify the factory itself and every direct writer/migration path.