50 lines
2.5 KiB
Markdown
50 lines
2.5 KiB
Markdown
# ADR-006: PRICE_UNMATCHED uses a frozen, source-replayed manual review
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Date
|
|
|
|
2026-08-06
|
|
|
|
## Context
|
|
|
|
The fixed price table can legitimately lack an exact normalized price key. Treating every such result as a permanent
|
|
failure makes staff re-upload unchanged XML after a separate price decision, while accepting edited spreadsheets or
|
|
mutable price-table changes would create a second intake path and weaken provenance.
|
|
|
|
## Decision
|
|
|
|
Only a non-empty error set entirely composed of `PRICE_UNMATCHED` becomes `review_required`. ARR stores one
|
|
privacy-minimized review case per run, grouped missing keys, revisioned actor/price history and candidate comparisons.
|
|
It creates no Finance version, daily download, rejected Finance version, `arr.processing_failed`, or monthly event.
|
|
|
|
After every key has a non-negative integer value, ARR freezes canonical JSON bound to the job, case, original XML
|
|
SHA-256, business date, processor version, rule hash and complete issue set. The operator/API surface uses integer text
|
|
(including explicit `0`); ARR normalizes that value to exact `numeric(18,2)` and canonical `.00` manifest text before
|
|
replay. Finalization re-materializes the registered source XML and replays it with the fixed price table plus that
|
|
manifest. Independent validation must accept the same manifest before one atomic Finance-version activation, case
|
|
completion and `arr.daily_version_committed` event.
|
|
|
|
## Consequences
|
|
|
|
- `POST /api/jobs` remains XML-only; there is no Excel draft/download/upload-back workflow.
|
|
- Staff can change only a reviewed missing key's non-negative integer price, including explicit `0`; fixed pricing rules and XML fields
|
|
remain immutable.
|
|
- Review mutations require session authentication, CSRF and optimistic revision checks. Frozen item/manifest facts are
|
|
immutable and permanently audited without free-form notes or guest detail.
|
|
- Infrastructure failures after freeze become `generation_failed` and may retry with the identical manifest. A
|
|
deterministic final replay failure closes the review and run as failed. Processor/rule identity drift requires cancel
|
|
then fresh upload.
|
|
- The legacy `direct_mcp` path uses an isolated v3 success/failure projection: it rejects manual overrides and maps
|
|
price misses to normal v3 failures, so it cannot gain review behavior from the active v4 processor.
|
|
|
|
## Related
|
|
|
|
- `database/017_daily_price_review.sql`
|
|
- `database/018_daily_review_manual_override_artifact.sql`
|
|
- `arr_web/programmatic.py`
|
|
- `arr_ingestion/postgres.py`
|
|
- `arr-opera-daily-ingest/scripts/process_daily.py`
|