# System Overview

## Current Architecture

ARR2.0 accepts Opera XML plus a separate Booking workbook recognized through approved exact Tour Code/Group Code and
hotel-detail header aliases, and owns both deterministic processing
lifecycles. It writes immutable private
objects, runs the frozen processor locally, independently validates the complete artifacts, and atomically commits
accepted facts to PostgreSQL. A pure `PRICE_UNMATCHED` result instead enters an authenticated, auditable manual-price
review state and cannot create Finance facts until the original XML is replayed with a frozen canonical manifest.
Downstream analytics and report programs read committed facts.
Successful daily commits also create a durable outbox event. A separate worker derives the affected month and watermark
from committed `ARRIVAL` facts, publishes a validated workbook, and records metadata-only report state in PostgreSQL.

## Main Components

| Component | Responsibility |
|---|---|
| `arr_web.auth` / authenticated `SessionLedger` | Environment-owned operator credentials, bounded login attempts, random server-side sessions, CSRF and logout revocation |
| `arr_web.programmatic` | XML upload plus synchronous frozen-review final replay orchestration and safe public error mapping |
| `arr_processing.local` / `arr_processing.policy` | Isolated frozen processor execution and approved version/rule identity |
| `arr_storage` | Immutable staged/committed OSS workflow; all object ACLs private |
| `arr_ingestion.validation` | Strict v3/v4 delivery/result/structured validation plus independent daily/review-manifest replay |
| `arr_ingestion.postgres` | Job/review lifecycle, artifact registration, atomic final Finance version commit and outbox |
| `booking_ingestion.excel` | Bounded exact-header Booking extraction, latest-row/cancellation semantics and room-item normalization |
| `booking_ingestion.excel_review_postgres` | Durable editable draft state and zero-pending atomic current-source activation |
| `arr_web.booking_uploads` | Private Booking workbook upload, draft review orchestration and privacy-minimized source summaries |
| `arr_web.job_trace` | Read-only programmatic trace from persisted run/attempt/delivery/version/outbox facts |
| `monthly_reports.worker` | Dedicated leased outbox consumer with retry/dead-letter behavior |
| `monthly_reports.repository` / `reporting` schema | ARRIVAL-derived snapshots, report versions, lineage, manifests and artifact identities |
| `monthly_reports`, `channel_analytics`, `company_reports` | Deterministic downstream consumers of committed facts; company reports build official XLSX files in Python/openpyxl inside Web |
| Web + Caddy Compose | Web-owned login/session boundary behind Caddy HTTPS; no browser-native Basic Auth and no MCP service |

## Important Boundaries

- The uploaded source is re-materialized from committed object storage before processing.
- Login documents/assets, the H5 read-only page/assets, purpose-built public H5 aggregate endpoints, `POST /api/login`
  and minimal `/healthz` readiness are anonymous Web routes. The desktop portal redirects to login; generic business
  APIs, detailed health, uploads, traces and downloads return `401` without an authenticated server-side session.
- Operator credentials come only from runtime `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD`; the application fails closed when either is absent. Authenticated mutations also require the per-session CSRF token.
- Processor exit code and JSON status must agree; output paths are confined to an isolated temporary directory.
- Validation completes before the database transaction; any transaction failure leaves no partial active version.
- `review_required` is valid only for a non-empty error set entirely composed of `PRICE_UNMATCHED`. It records neither a Finance version nor an `arr.processing_failed`/monthly event; a final manual value is limited to the verified missing key and is entered as a non-negative integer (including `0`) before exact `.00` storage/manifest normalization.
- All review mutations use the authenticated session plus CSRF protection and optimistic revision checks. Finalization is idempotent; infrastructure failures retain the frozen manifest for retry, while deterministic replay failures close the case and job as failed.
- Booking uploads create review drafts first. Only an explicit activation with no retained pending items can atomically
  create immutable Booking facts and switch `booking.current_source_batch`; an open draft blocks company-report creation.
- Only recognized transient PostgreSQL concurrency errors receive bounded transaction retries.
- PostgreSQL state, not HTTP/console output, is authoritative for success.
- Daily and new report downloads are read from OSS and rechecked; historical local report artifacts remain readable through
  the controlled project-root fallback with the same size and SHA-256 checks.
- Fresh uploads are new jobs/versions; exact delivery replay is idempotent.
- Monthly success is acknowledged only after both artifacts are registered and the publication is active; replay of the same snapshot returns the existing report.

## Related Decisions

- [ADR-004](../10-decisions/ADR-004-arr-owned-programmatic-processing.md)
- [ADR-001](../10-decisions/ADR-001-automatic-monthly-trigger-and-total-price-formula.md)

## Last Updated

2026-08-11
