feat: add daily manual price review workflow
This commit is contained in:
49
.project-docs/10-decisions/ADR-006-daily-price-review.md
Normal file
49
.project-docs/10-decisions/ADR-006-daily-price-review.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user