feat: add daily manual price review workflow

This commit is contained in:
Wyndham ARR
2026-08-06 22:40:18 +08:00
parent aae3d8e1db
commit ca3e8e18fa
77 changed files with 7750 additions and 602 deletions

View File

@@ -4,6 +4,7 @@
- 未登录用户只能看到 ARR 登录页或无详情 readiness页面、API、上传、日志和下载均由服务端会话保护退出后立即失效。
- XML 上传、确定性处理、完整结果验证和 Finance 提交不依赖 Agent/MCP并得到终态任务回执。
-`PRICE_UNMATCHED` 上传得到 `needs_review` 而非失败初始阶段没有日报、Finance 版本、失败事件或月报事件;填写全部键后原 XML + 冻结清单的独立重放才可产生一次最终成功。
- 用户上传 XML 后无需再提交月报参数;结构化结果成功入库会自动触发相应月报处理。
- 生成的月报包含 `TOTAL PRICE` 字段,并在每个数据行使用 `REAL PRICE × NIGHTS × NO_OF_ROOMS` 的 Excel 公式。
- 月报发布完成后,打开的月报页面自动出现新版本,不要求用户点击刷新。
@@ -17,10 +18,14 @@
- 测试必须覆盖错误/正确登录、尝试限流、安全回跳、CSRF 退出、会话撤销、桌面/H5 会话过期处理,以及 375/768/1024/1440 响应式无横向溢出。
- 测试必须证明月报触发发生在数据库成功提交之后,且失败提交不会触发。
- 测试必须覆盖合并重复价格键、候选价、整数格式、显式 `0`(冻结清单规范化为 `0.00`、未完成禁止确认、revision 冲突、重复最终化、技术重试、取消留痕、认证/CSRF和隐私字段隔离。
- `待人工处理` 状态与复核进度必须作为同一个操作入口,点击或键盘激活后定位并聚焦人工价格面板,不能误开任务日志;复核面板、动态消息、确认弹窗和安全 API 错误必须完整支持中文、英文和泰文。
- 人工确认请求在途时,上传卡片必须显示本地化的“日报生成中”;请求结束后按成功、仍在生成或可重试复核状态收敛,不能继续显示过期的“等待人工定价”。
- 最终人工成功必须证明日报行序/数值可重放、Finance 只有一个 active 版本、`manual_review` 行数和冻结清单 SHA-256 可追踪、月报事件仅一次。
- 重新打开生成的 XLSX确认 `TOTAL PRICE` 为公式单元格并逐行引用同一行的 `REAL PRICE``NIGHTS``NO_OF_ROOMS`
- 用户入口不得要求提交月报年份、月份或截止日期。
- 页面自动同步必须避免重叠请求;页面隐藏或离开月报页时停止轮询,恢复可见时立即重新读取。
## Last Reviewed
2026-07-30
2026-08-06

View 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`

View File

@@ -5,6 +5,7 @@
| ID | Decision | Status | Date | Applies To | Detail |
|---|---|---|---|---|---|
| ADR-005 | 手机 H5 看板通过专用只读聚合接口公开访问;桌面及运营接口继续认证 | Accepted | 2026-08-03 | Web access boundary, H5, Channel BI | [ADR-005](ADR-005-public-read-only-h5-dashboard.md) |
| ADR-006 | 仅纯 `PRICE_UNMATCHED` 进入冻结人工价格复核;最终原 XML 重放才可提交 Finance | Accepted | 2026-08-06 | Daily XML, review lifecycle, Finance/outbox, Web | [ADR-006](ADR-006-daily-price-review.md) |
| ADR-004 | ARR2.0 owns deterministic processing, artifact validation and terminal ingestion; no Agent/MCP in production XML flow | Accepted | 2026-07-30 | Upload, processing runtime, OSS ACL, validation, deployment | [ADR-004](ADR-004-arr-owned-programmatic-processing.md) |
| ADR-001 | 月报由独立 worker 自动触发;“更新至”取最新纳入的 `ARRIVAL`;发布后页面自动新增;`TOTAL PRICE` 为公式字段 | Implemented | 2026-07-29 | 月报触发、持久化、页面同步、XLSX 输出 | [ADR-001](ADR-001-automatic-monthly-trigger-and-total-price-formula.md) |

View File

@@ -18,15 +18,17 @@
|---|---|---|---|
| 1 | Browser XML upload | Uploaded-basename task provenance + private canonical source object + queued DB job | Filename/content/size and immutable object identity |
| 2 | Committed source object | Isolated processor input | Materialization rechecks stored bytes |
| 3 | Fixed `process_daily.py` | Daily/result/structured or failure artifacts | Timeout, confined paths, exit/JSON agreement |
| 3 | Fixed `process_daily.py` | Final daily/result/structured, pure-price review JSON, or failure artifacts | Timeout, confined paths, exit/JSON agreement |
| 4 | Validated processor/report artifacts | Private committed OSS objects | Role, MIME, size and SHA-256 |
| 5 | Canonical `DeliveryEnvelope` | `DeliveryValidator` | Strict Schema, reconciliation, source/artifact hashes and independent validation |
| 6 | Verified delivery | PostgreSQL Finance facts | Serializable atomic commit/version activation |
| 7 | Accepted/failed run | Task trace and outbox | Persisted state is authoritative |
| 8 | `arr.daily_version_committed` | Dedicated monthly worker | Lease, `SKIP LOCKED`, bounded retry/dead-letter |
| 9 | Retained Finance `ARRIVAL` facts | Monthly snapshot request | Affected month from event version; “更新至” is max included `ARRIVAL` |
| 10 | Monthly snapshot | XLSX/result + `reporting` metadata | Formula reopen-validation, current-pin recheck, atomic activation |
| 11 | Registered active report | Web list/download + outbox acknowledgement | Real report ID; visible monthly tab polls automatically; path/size/SHA-256 recheck before download |
| 5 | Canonical `DeliveryEnvelope` | `DeliveryValidator` | Strict v4 Schema, reconciliation, source/artifact hashes and independent final/review replay |
| 6 | Pure `PRICE_UNMATCHED` delivery | PostgreSQL review case/items/events | No daily XLSX, Finance version, failure event or monthly event; task remains active as `awaiting_review` |
| 7 | Frozen complete review manifest + registered original source | Same processor and independent validator | UI/API accepts only a non-negative integer for a missing key; storage and the manifest normalize it to exact `.00`, and the manifest binds job/case/source/date/processor/rules/full key set |
| 8 | Final verified delivery | PostgreSQL Finance facts | Serializable atomic version activation, review completion and one commit outbox event |
| 9 | Accepted/failed run | Task trace and outbox | Persisted state is authoritative; retryable final infrastructure failure returns to `generation_failed` with manifest retained |
| 10 | `arr.daily_version_committed` | Dedicated monthly worker | Lease, `SKIP LOCKED`, bounded retry/dead-letter |
| 11 | Retained Finance `ARRIVAL` facts | Monthly snapshot request | Affected month from event version; “更新至” is max included `ARRIVAL` |
| 12 | Monthly snapshot | XLSX/result + `reporting` metadata | Formula reopen-validation, current-pin recheck, atomic activation |
| 13 | Registered active report | Web list/download + outbox acknowledgement | Real report ID; visible monthly tab polls automatically; path/size/SHA-256 recheck before download |
## State Ownership
@@ -36,6 +38,7 @@
- `processing_runs.uploaded_filename` owns the user-facing browser basename; the source artifact remains canonically
named `source.xml` for processing and validation.
- Temporary processor/validator paths are deleted after each request.
- `daily_review_cases`/items/events retain only normalized price keys, aggregate impact, actor/revision and before/after price; they deliberately contain no guest names, comments or raw traces. Frozen manifests and items are immutable.
- New monthly/company XLSX and `result.json` bytes live in immutable private OSS objects; the database stores provider,
bucket alias, object key and identity. The controlled local reader remains available for historical local records,
while `/app/outputs` is only staging/cache and local `.web-jobs` state.
@@ -65,4 +68,4 @@ equals its persisted `as_of_date`. Every data-row `TOTAL PRICE` cell is `=R[row]
## Last Updated
2026-07-31
2026-08-06

View File

@@ -3,28 +3,30 @@
| Path | Responsibility | Notes |
|---|---|---|
| `arr_web/auth.py` | Login credential verification and bounded attempt ledger | Environment-owned single-operator identity; dependency-free constant-time comparison; no secret logging |
| `arr_web/programmatic.py` | Upload-to-terminal coordinator | Registers the validated browser basename separately from canonical `source.xml`; no remote/model transport |
| `arr_web/programmatic.py` | XML upload and manual-price finalization coordinator | Registers the validated browser basename separately from canonical `source.xml`; pure price misses create no Finance version, while a frozen manifest replays the original source for final commit |
| `arr_web/booking_uploads.py`, `booking_ingestion/excel.py` | Authenticated Booking source upload and bounded raw XLSX parsing | Parser 2.0 reads Tour Code plus exact `โรงแรม`, latest-row cancellations and extracted room items with review state; coordinator/backend route contracts target drafts |
| `booking_ingestion/excel_postgres.py` | Earlier direct Booking full-source import/activation path | Its one-item immediate-accept implementation does not represent parser 2.0 review and must not be treated as the completed review repository |
| `booking_ingestion/excel_review.py`, `booking_ingestion/excel_review_postgres.py` | Editable extraction drafts and atomic reviewed-source activation | Single-operator repository uses advisory locking and a zero-pending gate; real PostgreSQL transaction/rollback acceptance passes; actor/reason/revision history is not implemented |
| `arr_web/processing_runtime.py` | PostgreSQL + OSS + processor composition | Active production processing composition |
| `arr_processing/local.py` | Bounded subprocess and output manifest extraction | Never exposes stdout/stderr |
| `arr_processing/policy.py` | Frozen processor/rule identity | Neutral, reusable loader |
| `arr_ingestion/validation.py` | Strict artifact/result validation | Runs `validate_daily.py` on success |
| `arr_ingestion/postgres.py` | Atomic Finance commit and lifecycle state | Four retries only for transient SQLSTATEs |
| `arr_processing/policy.py` | Frozen processor/rule identity | Active v4 loader plus isolated legacy direct-MCP v3 compatibility projection |
| `arr_ingestion/validation.py` | Strict artifact/result validation | Dispatches v3/v4; independently replays review and frozen-manifest final results |
| `arr_ingestion/postgres.py` | Job/review lifecycle and atomic Finance commit | `recorded_review` has no Finance/outbox write; final success is the only activation boundary; four retries only for transient SQLSTATEs |
| `arr_storage/aliyun_oss_v2.py` | Encrypted/unversioned OSS adapter | Writes all objects private |
| `arr_web/downloads.py` | OSS daily/report + controlled local legacy download routing | Rechecks provider metadata, size and SHA-256 |
| `arr_web/job_trace.py` | Programmatic persisted-fact trace | No external trace store |
| `arr_web/app.py`, `arr_web/repository.py`, `arr_web/company_jobs.py` | Authenticated portal routes plus public H5 aggregate routes, paged history reads and read-only history-month discovery | Default-deny login gate protects the desktop/API/download surface; purpose-built `/api/public/h5/*` exposes only sanitized aggregate metrics; daily/monthly counts and rows share a repeatable-read snapshot; `/api/history-months` merges daily/monthly database counts with company job-state counts; company totals/slices share one lock |
| `arr_web/server.py` | Standard-library HTTP transport | Dispatches GET/POST/PATCH/DELETE with one bounded body reader; real socket tests cover review update/delete and missing/oversized lengths |
| `arr_web/static/login.html`, `login.css`, `login.js` | Responsive ARR login gateway | Labeled form, password visibility, generic inline failures, safe desktop/H5 return target and reduced-motion support |
| `arr_web/static/app.js`, `arr_web/static/h5.js` | Authenticated desktop and anonymous-capable H5 client state, rendering and polling | Desktop daily/monthly/company histories own independent viewing-month state and default to the latest non-empty month; company generation month remains separate. H5 reads only public aggregate endpoints and may retain optional logout for an authenticated session. Desktop also includes 50-row Booking draft review/edit, the draft's validated uploaded filename below the review title, individual/all-visible selection, count-aware in-page delete confirmation and activation; company generation keeps the fixed five-company context beside the page title, a four-card upload/period setup row, cumulative CO display labels, short centered period actions and an in-page generation confirmation dialog; session-expiry redirect and CSRF logout remain shared; monthly versions auto-refresh the selected viewing month every four seconds |
| `arr_web/static/app.js`, `arr_web/static/i18n.js`, `arr_web/static/h5.js` | Authenticated desktop and anonymous-capable H5 client state, rendering, localization and polling | Desktop daily/monthly/company histories own independent viewing-month state and default to the latest non-empty month; needs-review status opens/focuses the paged privacy-safe price panel, finalization projects a generating upload state, and review controls/errors follow the shared Chinese/English/Thai catalog. H5 reads only public aggregate endpoints; session-expiry redirect and CSRF logout remain shared |
| `monthly_reports/worker.py` | Dedicated outbox consumer | Lease/reclaim, retry/dead-letter, success acknowledgement after activation |
| `monthly_reports/repository.py` | Monthly snapshot and publication repository | Derives scope from `ARRIVAL`; persists metadata/lineage/artifact identities |
| `monthly_reports/publishing.py` | Python/openpyxl monthly builder and atomic/OSS publisher | Reopens sheets, headers, row counts, semantic hash and exact row-relative `TOTAL PRICE` formulas |
| `monthly_reports/`, `company_reports/`, `channel_analytics/` | Downstream reports/BI | Consume accepted Finance facts |
| `database/012_monthly_report_publication.sql` | Additive metadata-only publication schema | Applied after immutable 008011 baseline |
| `database/016_monthly_report_oss_artifacts.sql` | Monthly publication provider compatibility | Allows new OSS/S3 identities while retaining legacy local records |
| `database/017_daily_price_review.sql` | Daily missing-price review lifecycle | Applied to controlled `booking_test` on 2026-08-06 after exact 016 semantic verification and corrected up/down probe; adds protected case/item/event audit, `awaiting_review`, manual lineage and guarded rollback |
| `database/018_daily_review_manual_override_artifact.sql` | Frozen manual-manifest artifact compatibility | Applied as a forward-only correction after rollback probing; permits only `manual_override_json` in the existing artifact-kind constraint and refuses destructive rollback once such an immutable artifact exists |
| `database/014_booking_current_source_batch.sql` | Booking full-workbook current-source pointer and view scoping | Formally applied on 2026-07-31; batch 1 remains selected |
| `database/015_booking_excel_review_drafts.sql` | Item-level Booking extraction draft state | Formally applied and empty; basic latest-state review only, with no actor/reason/revision history and no DB-enforced zero-pending activation |
| `compose.yaml`, `deploy/` | Web-login + Caddy-HTTPS template and worker deployment boundary | Requires Web credentials for the desktop/operational surface; public H5 aggregate routes and `/healthz` remain anonymously reachable; no MCP port/domain/service |
@@ -35,4 +37,4 @@ by the active ARR2.0 entrypoint.
## Last Updated
2026-08-02
2026-08-06

View File

@@ -5,7 +5,9 @@
ARR2.0 accepts Opera XML plus a separate Booking Tour Code/`โรงแรม` workbook 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. Downstream analytics and report programs read committed facts.
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.
@@ -14,11 +16,11 @@ from committed `ARRIVAL` facts, publishes a validated workbook, and records meta
| 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` | Terminal upload orchestration and safe public error mapping |
| `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 delivery/result/structured validation plus independent daily validation |
| `arr_ingestion.postgres` | Job lifecycle, artifact registration, atomic Finance version commit and outbox |
| `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 Tour Code/`โรงแรม` 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 |
@@ -37,6 +39,8 @@ from committed `ARRIVAL` facts, publishes a validated workbook, and records meta
- 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.
@@ -53,4 +57,4 @@ from committed `ARRIVAL` facts, publishes a validated workbook, and records meta
## Last Updated
2026-08-03
2026-08-06

View File

@@ -19,6 +19,84 @@ preserves its no-formula contract. Both publishers upload new workbook and `resu
immutable OSS adapter, while download routing retains controlled-local compatibility for historical records and keeps
`.web-jobs` queue state local.
## Runtime Recovery On 2026-08-06
- Restored the local ARR login page after a reported opening failure. No Web process was listening and the retained
Keychain-backed 8766 launcher still passed two removed Node/artifact-tool flags. Removed only those obsolete flags,
then started one detached `arr2-web-8766` Screen session. `/healthz` returns 200 and root redirects to login with no
browser console error. No login, upload, database, Finance or object-store mutation was performed. The existing
reboot-persistent supervision follow-up remains open.
## Deployed And Accepted On 2026-08-06
- Reconciled the live migration-016 function/comment semantics without rerunning 016, created a privacy-minimized
pre-017 checkpoint, and ran a transaction-only up/down probe. The first probe exposed a real legacy
`artifact_callback`/`direct_mcp` run-constraint drift and rolled back cleanly; corrected 017 preserves that contract.
Formally applied migration 017 SHA-256 `22a0578e…67b5b` under an advisory lock with all original business table counts
unchanged. The guarded down migration SHA-256 is `9e85af0c…29101`.
- Closed incomplete runs 6163 through the normal repository failure transition as
`REVIEW_SCHEMA_MIGRATION_MISSING`, retaining all three source artifacts and adding only failure-audit events. The
first post-migration upload, run 64, then exposed a 16-value/15-placeholder delivery INSERT bug and closed safely as
`DATABASE_WRITE_FAILED`; the placeholder and a focused regression were corrected.
- Restarted Web as PID 24225 with all six authenticated readiness flags true. A subsequent exact-hash `0805.XML`
upload is now run 65 / open review case `dailyreview-878e45ae132b73c131560299b3b2dcb3`: the live page shows the two
expected Lian Tai keys and candidate prices at `0 / 2`, while confirmation remains disabled. PostgreSQL has one open
revision-0 case, two unset items and one required event, with no daily XLSX, manual manifest, Finance version, run
outbox or new monthly event. Historical daily version 28 remains rejected and unchanged. Finance-priced finalization
is the remaining authorized business step.
- Subsequent employee interactions created two independent frozen cases: run 66 stores GRP1/LBLT as `2300/0`, and
latest run 67 stores `200/0`. Both stopped safely as retryable `generation_failed` with no Finance version or run
outbox event. Diagnosis found two finalization defects: the live artifact-kind constraint omitted
`manual_override_json`, and the Finance-version INSERT had 21 placeholders for 20 values. Migration 018
(`5600d825…cd00`) was rollback-probed and formally applied as the additive artifact-kind correction; the surplus SQL
placeholder was removed. A real PostgreSQL outer-transaction review-to-Finance slice now reaches one active version,
one manual row and one commit event before rolling back to zero residue.
- Web is now PID 26286 with all six readiness flags true. The review UI/API displays and accepts only non-negative
integers while preserving exact `.00` database/manifest normalization. At the finalization-repair handoff, run 67
was revision 2 `generation_failed`, read back as `200` and `0`, and had not been retried by that repair; run 66 held a
different frozen GRP1 value. That checkpoint is historical and the later browser-visible state is recorded below.
Historical version 28 remained rejected and unchanged at the repair checkpoint.
- The manual-review UI follow-up is runtime-active without a Web restart because static files are read on each request.
`待人工处理` plus progress are one operation button that focuses/scrolls the price panel without opening the task
log; retryable cases omit the removed frozen-list sentence; finalization projects `日报生成中`; and the complete
review panel/dialog/error surface follows the Chinese/English/Thai selector. Authenticated read-only browser QA
verified both a retryable and an editable case. During that QA, history had advanced outside this task: jobs
`arrjob-e36d8c4c133b48dbb81a57a002e41d76` and `arrjob-cce736734c5e4731b733f03c4a9a3b76` displayed succeeded, while
`arrjob-d3bbbb435cae4559b5f69cb7ea331cee` and `arrjob-dddc27119bea43b19588c78eac087763` still displayed needs-review.
This UI task issued no price save, finalize/retry/cancel submission or upload, so it does not attribute those external
lifecycle changes to itself.
## Diagnosed On 2026-08-06
- Diagnosed the user's fresh post-v4 `0805.XML` upload read-only. The page shows `请求未完成`, while PostgreSQL has
three new v4 run/attempt shells (6163 / 5355) stuck `running`, three exact 948,683-byte source artifacts, no
delivery, Finance version or outbox event. Live database effects match 016 but migration 017 is absent: review tables
do not exist and lifecycle checks permit only success/failed. An isolated same-hash v4 replay plus independent
validation succeeds as `review_required` with exactly `LIAN TAI / LBLT / 0` and `LIAN TAI / GRP1 / 1150`. The Web
transaction therefore fails on the missing review table and its best-effort closure fails at the same dependency.
No live migration, cleanup, re-upload, review edit or business write was performed.
- Diagnosed deployed daily job `arrjob-f1cd01f5fddd458cbdafada0ec7dd14d` read-only. A local XML with the exact logged
948,683-byte/SHA-256 identity reproduces the processor-3.0.0 failure: Lian Tai `LBLT + 0` and `GRP1 + 1150` are absent
from the frozen price reference. All 151 `validation_failed` outcomes are `BATCH_NOT_VALIDATED` fallout; they are not
separate source-field errors. Finance version 28 remains rejected, and no price, code, server, database or job state
was changed. Finance must approve the missing totals or an explicit zero-night rule before rebuild, exact replay and a
fresh upload.
## Implemented Locally On 2026-08-06
- Added the v4 `PRICE_UNMATCHED`-only manual-price-review path for the active fixed-processor/artifact-callback flow.
A pure missing-price result records an audited, privacy-minimized `awaiting_review` case and returns `needs_review`
without a daily XLSX, Finance version, rejected version, failure outbox or monthly event. Mixed errors remain failures.
- Authenticated/CSRF review APIs and the Daily Report panel page grouped keys, candidate fixed prices, aggregate impact
and revision-locked non-negative integer prices (including `0`), then normalize exact `.00` storage/manifest values. Finalization freezes canonical provenance, rematerializes
the registered source XML and independently replays before its one atomic Finance/outbox commit. Infrastructure
retries retain the manifest; deterministic final errors close the review/run.
- Migrations 017/018 and their guarded down migrations, v4 processor package/schema, trace v4 and package checksum parity were
locally verified. After the navigation/progress/localization follow-up, the dependency-complete `.venv` suite passes
393 tests with three intentional private-fixture skips; direct-MCP remains an explicit v3 no-review compatibility
projection. The controlled rollout and later browser-visible user activity supply live review/success state, while
this follow-up itself remains UI-only and produced no Finance or monthly mutation.
## Completed On 2026-08-04
- Repaired deployment availability for monthly and company report artifacts. Removed the monthly Node/private-package

View File

@@ -4,6 +4,13 @@
| Date | Task | Outcome | Docs Updated |
|---|---|---|---|
| 2026-08-06 | Repair manual-review navigation/progress and add English/Thai | Merged the status chip and progress into one review action that focuses the operation panel while row clicks still own logs; removed the frozen-case sentence; set upload status to `日报生成中` before finalization; localized the complete review UI and review API errors. Live browser QA covered zh/en/th and dialog copy without submitting a business action. Full suite: 393 passed, 3 intentional skips | Current state, success criteria, module map, [UI evidence](../50-evidence/topics/2026-08-06-daily-review-ui-navigation-i18n.md), scoped planning record |
| 2026-08-06 | Use integer review prices and repair `请求未完成` finalization | Changed review display/PATCH input to non-negative integer text while retaining canonical `.00` storage and replay. Bound the visible failure to runs 66/67, repaired a 21-placeholder/20-value Finance INSERT and the missing `manual_override_json` artifact kind through additive migration 018, then proved the full PostgreSQL finalization path in an outer rollback. Web PID 26286 is healthy; latest run 67 remains explicitly retryable with frozen `200/0` and no Finance/outbox mutation. Full suite: 387 passed, 3 intentional skips | ADR/domain/architecture/current state, migration/deployment ledgers, [repair evidence](../50-evidence/topics/2026-08-06-integer-review-finalize-repair.md), scoped planning record |
| 2026-08-06 | Deploy migration 017 and restore live `0805.XML` manual review | Reconciled already-live 016 semantics, corrected a legacy run-constraint drift found by rollback probe, formally applied 017 with unchanged business counts, closed runs 6163 as infrastructure failures, then fixed a 16-value/15-placeholder delivery INSERT exposed by the first acceptance upload. Restarted Web and proved run 65 opens the exact two-key review panel at revision 0 with no prices, daily XLSX, Finance version or daily-commit/monthly event. Historical version 28 remains rejected | Migration ledger, current state, [live rollout evidence](../50-evidence/topics/2026-08-06-live-daily-price-review-rollout.md), SHA-protected runtime checkpoints, scoped planning record |
| 2026-08-06 | Diagnose fresh `0805.XML` v4 upload not entering review | Proved the exact file correctly yields independently valid v4 `review_required` with two keys, but live `booking_test` lacks migration 017 tables/states. Three new runs remain `running` after source registration because review commit and best-effort closure both depend on the missing table; no delivery, Finance or outbox fact exists. Live 016 effects are present despite the checked-in ledger saying pending. No database/runtime/business mutation was performed; 017 deployment and explicit shell cleanup require operator authorization | Current state, [live mismatch evidence](../50-evidence/topics/2026-08-06-live-0805-review-migration-mismatch.md), evidence index, stale item, scoped planning record |
| 2026-08-06 | Restore local ARR page opening | Found no listener or Screen session and traced startup failure to the owner-local Keychain launcher passing retired Node/artifact-tool arguments. Removed only those flags, started one detached 8766 Web session, verified `/healthz` 200 and the login redirect with no browser console errors. No login, upload, database, Finance or object-store mutation occurred; reboot-persistent supervision remains open | Current state, evidence/index, scoped planning record |
| 2026-08-06 | Implement Daily `PRICE_UNMATCHED` manual-price review | Added active processor/result v4, canonical frozen override replay, independent review/manual validation, migration 017/down, review lifecycle/repository/API/trace/UI, package parity and focused browser QA. Pure price-only output is `needs_review` without Finance/outbox; final source replay is the first Finance/monthly event boundary. The complete `.venv` suite passes 384 tests with three intentional private-fixture skips; the retired direct-MCP path is an explicit v3 no-review projection. No live database, OSS or job was mutated; deployment requires 016 → 017 backup/disposable rollback probe | ADR-006, data-flow/business rules/success criteria, deployment runbook, migration ledger, [implementation evidence](../50-evidence/topics/2026-08-06-daily-price-review-implementation.md), stale deployment item |
| 2026-08-06 | Diagnose deployed 2026-08-05 Daily `PRICE_UNMATCHED` | Verified the Desktop XML exactly matches the logged 948,683-byte/SHA-256 identity and reproduced the failure with processor 3.0.0. The only actionable misses are Lian Tai `LBLT + 0` and `GRP1 + 1150`; all 151 validation-failed rows are atomic `BATCH_NOT_VALIDATED` fallout. Both checked-in price references are identical and lack the keys. No price, code, runtime, database or job state changed; Finance approval is required before rebuild and fresh upload | Current state, price-unmatched evidence/index, stale item, scoped planning record |
| 2026-08-04 | Fix false monthly XLSX output-validation failure on 440-row reports | Replaced read-only workbook random-cell validation with sequential `iter_rows` scanning and changed Excel-decimal readback checks to an explicit `0.000001` tolerance. Sheet/header/dimension checks, semantic SHA-256, formula count and exact `=R[row]*C[row]*G[row]` formulas remain strict. Added a 440-row, three-date, five-sheet decimal regression; the XLSX test and 18-case monthly builder/publishing/worker/service/repository suite pass. No outbox, database or live runtime state was changed | Current state, report-artifact evidence, scoped planning record |
| 2026-08-04 | Remove private monthly XLSX runtime and make report artifacts deployment-safe | Replaced monthly and company report builders' production path with Python/openpyxl; preserved the exact monthly `TOTAL PRICE` formula and workbook semantic checks; published new monthly/company XLSX and `result.json` artifacts through the existing OSS adapter while retaining legacy local reads; added migration 016 for OSS monthly artifacts, removed Node builders/flags, and added OSS/local download routing tests. Targeted report/deployment tests pass; the full local suite has 307 passes, 3 skips and 8 environment-only errors (`httpx`/Aliyun test setup). Docker and live OSS were intentionally not run in this development environment | Current state, architecture, deployment runbook, migration ledger, evidence/index, stale item |
| 2026-08-03 | Make company-report XLSX generation deploy without private npm | Replaced the company-report builder's Node/private `@oai/artifact-tool` runtime dependency with Python/openpyxl workbook generation and self-validation, removed the private package dependency, enabled `--enable-company-reports` in Compose Web, and documented that only the monthly worker still needs Node/artifact-tool packaging. Focused company-report and deployment-entrypoint tests pass 19/19; no migration or live deployment was performed | Current state/history, architecture, deployment evidence/index, stale item, runbooks |

View File

@@ -16,6 +16,12 @@
recent month containing that record type. Viewing-month state is read-only: it resets list paging but never changes
monthly derivation or a company generation request. Company `生成月份` and history `查看月份` are separate inputs.
- ARR's frozen processor generates complete structured data, which must be independently validated and successfully committed to the database before downstream monthly processing starts.
- A non-empty set of errors made entirely of `PRICE_UNMATCHED` is the sole exception to immediate failure: it becomes a pending manual-price review and remains active without a Finance version, daily download, failure event or monthly trigger.
- The review key is normalized company + Rate Code + Opera price. Staff may set only its non-negative integer price
(including explicit `0`); they may not change XML/company/rate/Opera fields or the fixed reference table. ARR stores
the accepted integer as exact `numeric(18,2)` and freezes canonical `.00` manifest text for deterministic replay.
- Finalization freezes one canonical manifest bound to job, case, source SHA, business date, processor/rule identity and the complete key set, then replays original XML plus the fixed table. Only independently accepted final replay creates `manual_review` retained facts and one daily-commit event.
- If the processor/rule identity changes while a case is open, staff must cancel and re-upload. Retryable infrastructure errors preserve the frozen manifest; deterministic final errors formally fail the case/run.
- The production XML path must not require Agent, MCP, prompts, model-generated JSON, fetch grants, or a public source URL.
- A successful database commit automatically triggers the monthly-report processing program through the business system.
- Automatic monthly processing runs in a dedicated worker process, not inside the Web request process.
@@ -73,4 +79,4 @@
## Last Reviewed
2026-07-31
2026-08-06

View File

@@ -4,6 +4,13 @@ Use this index for searchable, traceable evidence records.
| Date | Topic | Status | Source | Detail |
|---|---|---|---|---|
| 2026-08-06 | Daily review navigation, progress and three-language UI | Implemented and runtime-active | [Evidence topic](topics/2026-08-06-daily-review-ui-navigation-i18n.md) | The whole needs-review status now opens/focuses the operation panel without opening logs; the unwanted retry sentence is removed; finalization sets `日报生成中` before its request; review UI/API errors are localized in Chinese, English and Thai. Live read-only browser QA and 393 tests pass; no price/finalize/cancel/upload mutation was invoked. |
| 2026-08-06 | Integer review prices and finalization repair | Deployed repair; run-67 checkpoint is historical | [Evidence topic](topics/2026-08-06-integer-review-finalize-repair.md) | Review amounts now display/accept integers and normalize to canonical `.00`. Migration 018 fixes the omitted manual-manifest artifact kind and the Finance INSERT now has 20 binds; rollback probes, live readiness and the then-current 387-test suite passed. Run 67 was still frozen at `200/0` when that repair ended; later browser-visible lifecycle state is recorded in the UI follow-up topic. |
| 2026-08-06 | Live Daily price-review rollout and `0805.XML` acceptance | Historical initial-review snapshot; superseded by repair topic | [Evidence topic](topics/2026-08-06-live-daily-price-review-rollout.md) | Reconciled live 016 semantics without rerunning it, corrected and applied 017 after an exact up/down rollback probe, closed runs 6163, fixed a delivery-placeholder runtime bug, and proved run 65 reached the two-key live panel with no Finance/report/outbox effect. Later runs and current retry instructions are in the integer/finalization repair topic. |
| 2026-08-06 | Fresh `0805.XML` did not enter v4 manual review | Historical diagnosis; resolved by live rollout | [Evidence topic](topics/2026-08-06-live-0805-review-migration-mismatch.md) | Exact-source v4 replay isolated the absent-017 failure. The authorized follow-up applied corrected 017, closed the three shells, fixed the delivery INSERT and proved a fresh run enters the two-key review; see the live rollout topic above. |
| 2026-08-06 | Local ARR page-opening recovery | Restored locally; supervision follow-up unchanged | [Evidence topic](topics/2026-08-06-web-launcher-compatibility-recovery.md) | No ARR Web listener was present. The owner-local Keychain launcher still supplied two removed Node/artifact-tool flags; deleting only those flags restored 8766, `/healthz` 200 and the login page with no console error. No business write occurred. |
| 2026-08-06 | Daily `PRICE_UNMATCHED` manual-price review implementation | Implemented; repair and UI follow-ups active | [Evidence topic](topics/2026-08-06-daily-price-review-implementation.md) | v4 creates a no-Finance `needs_review` receipt only for pure missing fixed-price keys, freezes a manifest, replays original XML for final success, and preserves retry/audit/privacy boundaries. Integer/finalization and navigation/i18n behavior are covered by the two newer evidence topics. |
| 2026-08-06 | 2026-08-05 Daily `PRICE_UNMATCHED` | Historical diagnosis and frozen-case checkpoint | [Evidence topic](topics/2026-08-06-daily-price-unmatched.md) | The original v3 diagnosis isolated the two missing Lian Tai keys. Its later run-67 `200/0` generation-failed statement is a historical pre-retry snapshot; current browser-visible lifecycle changes are recorded in the UI follow-up, while rejected version 28 remains immutable in the last authoritative database audit. |
| 2026-08-04 | Report artifact deployability repair | Implemented locally; CentOS/Docker acceptance pending operator execution | [Evidence topic](topics/2026-08-04-report-artifact-deployability.md) | Monthly XLSX now uses Python/openpyxl with exact row-relative formulas and semantic validation. New monthly/company workbook and result artifacts use the existing immutable OSS adapter; download routing supports OSS plus legacy local records, and migration 016 permits OSS monthly identities. Focused report/storage/Web/migration tests pass; Docker/real OSS were intentionally not run locally. |
| 2026-08-04 | Deployed monthly-report download diagnosis | Superseded for implementation; optional authenticated remote capture pending | [Evidence topic](topics/2026-08-04-deployed-monthly-download-diagnosis.md) | The pre-repair Node/local-output diagnosis remains useful as historical cause evidence. The implementation now uses Python/openpyxl plus OSS-backed report identities and legacy local fallback; remote acceptance still belongs on the CentOS deployment. |
| 2026-08-03 | Company-report openpyxl builder for deployment | Implemented; focused deployment checks pass | [Evidence topic](topics/2026-08-03-company-report-openpyxl-builder.md) | CentOS Docker build failed because public npm cannot install private `@oai/artifact-tool`. Company-report XLSX generation now runs through Python/openpyxl in Web, while monthly worker packaging remains the only Node/artifact-tool concern. Focused company-report/deployment tests pass 19/19. |

View File

@@ -0,0 +1,62 @@
# Daily PRICE_UNMATCHED manual-price review implementation
## Metadata
- Date: 2026-08-06
- Status: Implemented; integer-input/finalization repair deployed, explicit employee retry pending
- Scope: `artifact_callback/fixed_processor` daily XML path only; legacy `direct_mcp` remains an explicit v3,
no-review compatibility projection
- Confidence: High; local regression plus the later controlled migration/runtime and exact-XML initial-review acceptance agree
- Last verified: 2026-08-06
- Stale trigger: a later migration, processor/rule change, successful run-67 retry, or new 0805 re-upload
## Implemented boundary
- Active processor/result schemas are v4. A result becomes `review_required` only when its non-empty actionable error
set is entirely `PRICE_UNMATCHED`; it exits technically successfully with `result.json` and
`structured-result.json` only, no daily/exception XLSX.
- The review result includes candidate auto-priced rows, grouped normalized `(company, Rate Code, Opera price)` issues,
existing fixed-price comparisons and aggregate room/room-night impact. It contains no guest name, comment or raw
trace in the review API/read model.
- Staff PATCH only a verified missing key using a non-negative integer string, including `0`. ARR normalizes it to
exact `numeric(18,2)` and canonical `.00` manifest text. Case revision, session username, old/new price and timestamps
are permanently recorded without a free-form note field.
- Finalization freezes canonical JSON bound to job/case/source SHA/business date/processor/rules/full issue key set and
re-materializes the registered original XML. Processor and independent validator both replay that exact manifest;
final retained manual rows use `pricing_method=manual_review` and `MANUAL_PRICE_APPLIED`.
- Initial `recorded_review` creates no Finance/rejected version, download, `arr.processing_failed` or monthly event.
One final atomic success creates/activates Finance lineage and emits `arr.daily_version_committed`. Infrastructure
errors retain the frozen manifest as `generation_failed`; deterministic final errors fail the case/run.
## Evidence
- `.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v`: 387 passed, with three intentional skips for a
private acceptance fixture not bundled in the repository. This includes active v4 behavior and direct-MCP v3
no-review compatibility.
- `python3 -m unittest tests.test_arr_opera_daily_ingest -v`: 14 passed, including pure-review grouping, mixed-error
failure, zero-price final replay and package source/archive parity.
- `python3 -m unittest tests.test_arr_ingestion_validation -v`: 7 passed for outer review/manual independent replay.
- `python3 -m unittest tests.test_arr_ingestion_postgres -v`: 16 passed, including source-artifact reload plan, no-Finance
review receipt and deterministic-final-failure audit path.
- `python3 -m unittest tests.test_arr_programmatic -v`: 5 passed, including original-source `0.00` final replay and
deterministic finalization failure closure.
- `python3 -m unittest tests.test_arr_web -v`: covers authenticated/CSRF review routes, strict integer grammar and
privacy-safe response shape. `tests.test_arr_web_job_trace`, `tests.test_arr_web_repository_schema` and migration
static tests also passed during the implementation run.
- `shasum -a 256 -c CHECKSUMS.sha256` passes after rebuilding byte-identical `.zip`/`.skill`; archive filenames are
UTF-8-preserved, including `价格对照.xlsx`.
- Browser QA used a temporary localhost fake service: logged in with test-only credentials, opened the review panel,
rendered candidate/impact fields, saved `0`, finalized to a successful state, and observed no browser console
errors. No production service, private XML, database or object storage was used.
## Live follow-up
- The authorized follow-up reconciled already-live 016 semantics, corrected and applied 017 after an exact up/down
rollback probe, fixed a delivery-placeholder defect exposed by the first real attempt, and proved run 65 reaches the
expected two-key initial-review state with no Finance/event change. See
`2026-08-06-live-daily-price-review-rollout.md`.
- A later repair changed the operator surface to integer prices, applied additive migration 018 for the omitted
`manual_override_json` artifact kind, and corrected the Finance INSERT bind count. Latest run 67 remains frozen at
`200/0` and awaits an explicit employee retry; see `2026-08-06-integer-review-finalize-repair.md`.
- Remaining evidence is Finance-owned finalization: verify run 67's frozen values, retry once, require 153 retained lines
with two `manual_review` rows and one final daily-commit/monthly event, and leave rejected version 28 unchanged.

View File

@@ -0,0 +1,48 @@
# 2026-08-05 Daily PRICE_UNMATCHED diagnosis
## Metadata
- Date: 2026-08-06
- Status: Latest frozen review awaits explicit employee retry after deployed technical repair
- Scope: Daily job `arrjob-f1cd01f5fddd458cbdafada0ec7dd14d`, rejected Finance version 28
- Confidence: Fact for the hash-matched local replay and checked-in reference; production rule-set binary identity remains unproven because the supplied trace omits `rule_set_sha256`
- Source: operator-supplied ARR trace, hash-matched `0805.XML`, deterministic processor 3.0.0 replay, structured failure result, fixed price-reference workbook
- Last verified: 2026-08-06
- Stale trigger: a successful run-67 retry, a price/rule deployment, or a fresh processing job for business date 2026-08-05
## Question
Why did the deployed 2026-08-05 Opera XML daily job fail with `PRICE_UNMATCHED`, and do the reported 151 validation-failed rows represent separate bad source records?
## Evidence
- The server trace reports source size 948,683 bytes, rejected version 28 and outcomes `199 source = 46 excluded + 151 validation_failed + 2 price_unmatched`, with no duplicates or retained output.
- The read-only Desktop XML has the exact logged size and SHA-256. An isolated replay with checked-in processor 3.0.0 exits 2 and creates only `result.json`, `structured-result.json` and `异常清单.xlsx`.
- The two actionable errors are:
- `reservation[2]`: Lian Tai + `LBLT` + Opera amount `0`; zero-night stay.
- `reservation[64]`: Lian Tai + `GRP1` + Opera amount `1150`; one-night stay.
- Every one of the 151 `validation_failed` records carries `BATCH_NOT_VALIDATED`; 150 also carry `PRICE_REFERENCE_MATCHED`, and one carries the approved `ZERO_PRICE_EXCEPTION`. They are atomic batch fallout, not 151 independent field/date errors.
- Read-only artifact-tool inspection of `arr-opera-daily-ingest/references/价格对照.xlsx` shows Lian Tai `GRP1` mappings only at Sheet1 rows 2328 for Opera amounts `900/1200/1300/1400/1800/2400`, and Lian Tai `LBLT` mappings at rows 40/42/43 for `900/1400/2400`. Neither exact target exists.
- Both checked-in price-reference workbooks are byte-identical at SHA-256 `123d1d1ea0e28ce481a63dfdbe4bd22b5e6069c585a8cbde4194376ed18fb0d6` and have not changed since commit `a701de9` (2026-07-29).
- The local replay rule-set SHA is `c41257208324a43e711de13ec9776a5e6486db334757f152531bf8292a2018eb`. The server trace exposes only processor version 3.0.0, so exact production rule-set hash comparison is not available from that trace alone.
## Finding
The deterministic processor correctly failed under the current frozen rules because two exact normalized price keys are absent. Numeric formatting and trailing rate-code spaces are not the cause. The `总价` values cannot be inferred safely from neighboring mappings, and zero nights do not bypass price-key validation under the current approved rule.
## Impact
- The deployed v4 workflow created review cases instead of another rejected Finance version. Latest run 67 has a frozen
GRP1/LBLT manifest of `200/0` and remains retryable `generation_failed` after two repaired infrastructure defects;
it still has no Finance version or run outbox event. Run 66 separately retains `2300/0`, and run 65 remains unset.
- The values apply only to their own task and do not change the fixed reference table. No automatic retry or re-upload
is safe because a successful final replay is the Finance/monthly commit boundary.
- Keep rejected version 28 as immutable failure history. Only an explicitly authorized successful final replay may
activate Finance facts and trigger monthly processing.
## Open Items
- Refresh/login, open latest run 67, confirm its frozen `LBLT=0` and `GRP1=200` values are intended, then invoke its
retry once. Do not select run 66 accidentally because its frozen GRP1 value differs.
- After success, verify 153 retained rows, two `manual_review` rows, one active Finance version and exactly one new
daily-commit/monthly event.

View File

@@ -0,0 +1,51 @@
# Daily Review UI Navigation, Progress And I18n
Date: 2026-08-06
Status: implemented and runtime-active
Scope: authenticated desktop Daily Report UI only
## Reported Problems
- Clicking `待人工处理` opened the task log instead of the manual-price operation panel.
- A retryable frozen case displayed an unwanted frozen-manifest explanation.
- Final confirmation did not change the upload-card status from `等待人工定价` to `日报生成中` while the synchronous
request was running.
- The added manual-price module was not localized with the existing Chinese/English/Thai selector.
## Root Cause And Change
- The history row owned the log-opening click/keyboard behavior, while only the small `复核 x / y` child button owned
`data-open-price-review`. The status chip therefore fell through to the row action. Status plus progress are now one
semantic review button; nested controls are excluded from the row keyboard handler. A successful read focuses and
scrolls the dedicated `daily-price-review-panel`.
- `generation_failed` now hides the instruction node. The removed sentence is absent from HTML, JavaScript and the
language catalog.
- `finalizeDailyPriceReview()` sets the upload progress component to the localized running label before awaiting the
finalize API. Success resolves to complete, an active server-side generation remains generating, and a returned
editable/retryable review resolves back to waiting for manual pricing.
- Static and dynamic review copy, responsive `data-label` values, row states, pagination, validation/toasts, cancel
dialog and review API errors now have explicit Chinese, English and Thai catalog entries. Review prices use the
current locale's integer formatter.
## Verification
- `node --check arr_web/static/i18n.js` and `node --check arr_web/static/app.js` passed.
- Focused Web/static regression passed 31/31 tests.
- Full discovery passed 393 tests in 247.709 seconds, with three intentional skips for private Booking fixtures.
- Authenticated browser verification on the live 8766 page proved:
- the needs-review status is exposed as one button with an explicit open-review accessible name;
- clicking it made `daily-price-review-panel` the active element, scrolled to the panel and left the task-log dialog
closed;
- the retryable instruction node was hidden and the removed sentence was not rendered;
- Chinese, English and Thai rendered localized title, progress, table labels, instructions, row state, save/finalize
controls and status-action names;
- both English and Thai cancellation dialogs rendered localized title, description, audit note and buttons, then
closed through the non-mutating return action.
## Safety Boundary
Browser verification issued only review-detail reads, client-side locale changes and open/close dialog actions. It did
not edit or save a price, submit finalize/retry/cancel, upload XML, create a Finance version or trigger monthly work.
The finalization-progress transition is covered by the source-order regression because clicking it would be a real
business mutation. The Web process was not restarted: authenticated page reload proved the running server reads and
serves the current static files.

View File

@@ -0,0 +1,71 @@
# Integer Review Prices And Finalization Repair
## Metadata
- Date: 2026-08-06
- Status: Deployed and technically verified; latest employee case awaits explicit retry
- Scope: Daily `PRICE_UNMATCHED` review UI/API, PostgreSQL finalization and local Web runtime on port 8766
- Confidence: High; browser/database correlation, exact SQL planning, protected migration probes, a real PostgreSQL
rollback-only vertical slice and the full repository suite agree
- Last verified: 2026-08-06
- Stale trigger: a successful retry of run 67, a new `0805.XML` upload, or a later migration/runtime deployment
## User-visible contract
- Review prices display without trailing `.00`. The PATCH contract accepts a non-negative integer string of at most 16
digits, including explicit `0`; decimal input such as `0.00` is rejected.
- The repository still stores exact `numeric(18,2)` values and the immutable manifest retains canonical two-decimal
text such as `0.00`. Processor arithmetic, result schemas and independent replay therefore remain unchanged.
- `DATABASE_WRITE_FAILED` now has a specific retryable message instead of the unhelpful generic `请求未完成` copy.
## Root causes and corrections
- Runs 66 and 67 reached final validation but rolled back before Finance activation. The Finance-version INSERT declared
21 placeholders for 20 supplied dynamic values. The statement now has exactly 20 placeholders, with a regression
assertion and a live read-only PostgreSQL `EXPLAIN INSERT` plan.
- A full outer-transaction probe then found an earlier live constraint failure: migration 017 referenced a manual
manifest artifact but omitted `manual_override_json` from `ingestion.artifacts`' allowed kinds. Migration 018 is an
additive, target-guarded correction that changes only this check and refuses destructive rollback after an immutable
manual manifest exists.
- Migration 018 up SHA-256 is
`5600d82597304d66ffb4933df4b375406080484dfbc7717c360b48a9e28ccd00`; down SHA-256 is
`514ba908059bd8eadedd9a38da6efae85efb378ccfca0395028eae1fed22b92f`.
## Protected live evidence
- Pre-018 privacy-minimized checkpoint:
`runtime/backups/booking_test_pre_018_20260806T210250+0800/manifest.json`, SHA-256
`fcc24362d43c5982590a6566276e6ac7d1d172da2f2f040e8e54a063ca7e2b16`.
- Up/down outer-transaction probe:
`runtime/backups/booking_test_pre_018_20260806T210250+0800/rollback-probe.json`, SHA-256
`6d7f6b29a2e2a9ac528d18159d5ee1c046e786437c3d096e0fad42e85d779190`. It restored the exact pre-state fingerprint
and left no committed row or schema residue.
- Formal application evidence:
`runtime/backups/booking_test_pre_018_20260806T210250+0800/formal-application.json`, SHA-256
`651dc998d471734ebfc1ebd5ba81f5a5da57942879676aa1747fe5bcb2d24d84`. Pre-existing review cases, business counts
and rejected Finance version 28 stayed unchanged; zero manual-manifest artifacts existed at apply time.
- Post-018 finalization probe:
`runtime/backups/booking_test_pre_018_20260806T210250+0800/finalization-rollback-probe.json`, SHA-256
`857cee393fa232ea4cbd85913f5069948246acdace6add39c019bd91787711dd`. A synthetic review was recorded, integer `0`
read back correctly, its manifest froze `0.00`, finalization created an active two-row version with one
`manual_review` row and one commit event, and the outer rollback left zero probe residue.
## Runtime and remaining business boundary
- Web restarted as PID 26286; anonymous `/healthz` and all six authenticated readiness checks pass. A temporary API
session was revoked after confirming integer read-model output.
- Run 65 remains an unset `0/2` case. Run 66 is separately frozen at GRP1/LBLT `2300/0`. Latest run 67 is revision 2,
frozen at `200/0`, and remains `generation_failed`; none of these runs has a Finance version or run outbox event.
- No finalize, cancel, upload or automatic retry was performed during the repair. After refresh/login, an employee must
open latest run 67, verify `200/0` are intended, and explicitly retry it once. Run 66 must not be selected by mistake.
Successful acceptance must then prove 153 retained rows, two `manual_review` rows, one active Finance version, a
matching manifest SHA and exactly one new daily-commit/monthly event while version 28 remains rejected.
- A final explicit read-only transaction confirms the live artifact constraint includes `manual_override_json`, runs
65/66/67 each still have zero Finance versions and zero run outbox events, run 67 remains revision 2 at `200.00/0.00`,
and version 28 is rejected with no manual lineage. `/healthz` remains `ready` under listener PID 26286.
## Verification
- Full repository discovery: 387 tests passed; three private Booking fixture tests were intentionally skipped.
- Focused Web/PostgreSQL/service/programmatic coverage: 52 passed. Focused migration/PostgreSQL coverage: 21 passed.
- JavaScript/Python syntax, package checksum parity, whitespace checks and both project-document gates pass.

View File

@@ -0,0 +1,52 @@
# Fresh 0805.XML v4 review blocked by live migration mismatch
## Metadata
- Date: 2026-08-06
- Status: Historical root-cause record; resolved by the authorized live rollout
- Scope: local authenticated 8766 Web runtime and controlled `booking_test`
- Confidence: High; UI, PostgreSQL catalog/lifecycle, source identity, isolated processor replay and independent validator agree
- Last verified: 2026-08-06
## Observed behavior
- The authenticated upload UI retained `0805.XML`, displayed `处理失败` / `请求未完成`, and showed zero August history
rows with no browser console error.
- PostgreSQL contains three fresh v4 runs (6163) and attempts (5355), all still `running`. Each has one committed
private OSS source artifact but no delivery row, result identity, business date or finish timestamp.
- All three source artifacts are 948,683 bytes and share SHA-256
`40a40f09dd110cc9814df3da9c747d5b08c9f8357e703fe1ea96ebf8daaeede3` with historical run 58.
- No Finance version and no outbox event references these new runs.
## Root cause
- Live `booking_test` has no `ingestion.daily_review_cases`, `daily_review_items` or `daily_review_events` relations.
Its lifecycle checks also lack `awaiting_review`, `review_required` and delivery result `review_required`, proving 017
is not applied.
- The live monthly publication validator body/comment matches migration 016's OSS-enabled definition, despite the
checked-in applied-migration ledger still marking 016 pending. This is a migration-ledger conflict, not proof that
016 must be rerun.
- The exact local XML under active processor 4.0.0 returns technical success `review_required`, 151 candidate rows and
exactly two grouped keys: `LIAN TAI / LBLT / 0` and `LIAN TAI / GRP1 / 1150`. It creates only result/structured JSON.
The independent validator accepts this review output.
- During live ingestion, the atomic review receipt attempts to insert the missing review case and is wrapped as
`DATABASE_WRITE_FAILED`; the delivery transaction rolls back. Best-effort terminalization then queries the same
missing review table, fails and is swallowed, leaving the registered run/attempt shells `running`. The frontend has
no specific translation for this code and displays generic `请求未完成`.
## Safe correction boundary
Do not convert or reopen runs 6163. After operator authorization, reconcile the live 016 signature with migration
history, back up and probe/apply 017, explicitly terminalize the three infrastructure-failed shells with audit, reload
and verify schema-aware readiness, then perform one fresh upload for the two-key review. No migration, shell cleanup,
re-upload, review edit/finalization, Finance change, object-store write or runtime restart was performed in this
diagnosis.
## Resolution
The operator subsequently authorized the bounded correction. Migration-016 semantics were verified live and not
rerun. A privacy-minimized checkpoint and corrected 017 up/down probe passed, then migration 017 was formally applied.
Runs 6163 were closed as audited `REVIEW_SCHEMA_MIGRATION_MISSING` failures. The first post-migration acceptance
identified and safely closed a separate delivery-placeholder defect; after that one-line SQL binding fix and restart,
run 65 entered an open two-key review case with no Finance/report/outbox effect. See
`2026-08-06-live-daily-price-review-rollout.md` for authoritative rollout and acceptance evidence.

View File

@@ -0,0 +1,66 @@
# Live Daily Price Review Rollout And `0805.XML` Acceptance
## Metadata
- Date: 2026-08-06
- Status: Historical initial-review rollout; superseded for latest runtime state by the integer/finalization repair topic
- Scope: controlled `booking_test`, local authenticated Web on port 8766, exact desktop `0805.XML`
- Acceptance job: `arrjob-dddc27119bea43b19588c78eac087763` (run 65)
- Review case: `dailyreview-878e45ae132b73c131560299b3b2dcb3`
## Rollout evidence
- The privacy-minimized pre-017 checkpoint is
`runtime/backups/booking_test_pre_017_20260806T192026+0800/manifest.json`, SHA-256
`d66cc4341db42420b9382fa78c90214f5ecb3c200062c0ac4bb8d275a7123b17`. It excludes credentials, XML bytes,
guest rows and raw delivery/outbox payloads.
- The first up/down probe failed closed and rolled back because 017 had simplified the migration-010
`artifact_callback`/`direct_mcp` terminal constraint. Corrected up/down preserve that legacy split and add only the
`awaiting_review` branch. The corrected probe restores the exact pre-migration fingerprint and leaves no row/schema
residue; its evidence SHA-256 is `c62e07f60b4f2b481fb6fcb72a6aeaeccc3e73b87f36f87af1478f63027eb971`.
- Live migration-016 function/comment semantics were already exact before the rollout; 016 was not rerun. Corrected
migration 017 SHA-256 `22a0578e8748573d29ff2c15dbc687aaea14d7dc0c34cf8fe1faa2ef56067b5b` was formally applied under an advisory lock.
All new relations, columns, indexes, triggers and lifecycle/pricing values passed post-commit checks while original
business table counts stayed unchanged. Formal evidence SHA-256 is
`9837fce88d5bd18ff58b5575375af9f31fec89252123b7cf44d3a3fb991bd83a`.
- Runs 6163 were closed through the normal repository transition as
`REVIEW_SCHEMA_MIGRATION_MISSING`. Their three private source artifacts remain intact; only three
`arr.processing_failed` audit events were added. No delivery, review case, Finance version or daily-commit event was
created for those shells. Cleanup evidence SHA-256 is
`ad0d4ce01644d6adc5e58fc0c5182240398568ed4b6546be1a3b15a4dbb273a1`.
## Runtime correction and real acceptance
- The first post-migration upload became run 64 and failed safely as `DATABASE_WRITE_FAILED`. Source artifact 173 and
failure event 45 were retained, with no delivery/review/Finance fact. Diagnosis found a 16-value/15-placeholder
mismatch in the newly extended `processing_deliveries` INSERT. The missing placeholder and a regression assertion
were added; 18 focused migration/PostgreSQL tests, compilation and whitespace checks pass, and PostgreSQL plans the
exact 16-parameter INSERT.
- Final repository discovery passes all 384 tests with three intentional skips for private booking fixtures. Package
checksums, Python/JavaScript syntax, diff whitespace and project-document gates also pass.
- Web was restarted as PID 24225. Authenticated health reports database, processing, monthly, download, company report
and company source upload readiness all true.
- One subsequent exact-hash upload reached run 65 and automatically opened the live review panel. PostgreSQL records
`awaiting_review` / `review_required` / `recorded_review`, one open revision-0 case, one
`PRICE_REVIEW_REQUIRED` event and exactly two unset items:
- `LIAN TAI / GRP1 / 1150.00`, impact 1 record / 1 room / 1 room-night;
- `LIAN TAI / LBLT / 0.00`, impact 1 record / 1 room / 0 room-nights.
- The page shows both fixed-price candidate lists, `复核 0 / 2`, and a disabled `确认并生成日报` button. No employee
price was entered and the case was not finalized or cancelled.
- Only source XML, result JSON and structured-result JSON are registered. The run has no daily XLSX, exception XLSX,
manual manifest, Finance version or outbox event. Total `arr.daily_version_committed` remains 20, monthly-run count
remains 14, and historical daily version 28 remains rejected with no review lineage.
- The complete acceptance snapshot is
`runtime/backups/booking_test_pre_017_20260806T192026+0800/live-0805-review-acceptance.json`, SHA-256
`a8994ada1644e039dac7ea888cdba5143bca72cbc6d47c5bf7c4e09d5d6fa315`.
## Remaining boundary
Finance must supply the two approved prices in the open panel. Only after both revision-locked saves should an
authorized employee invoke finalization and verify the expected final 153 retained rows, two `manual_review` rows,
one active Finance version and exactly one new daily-commit/monthly event. Historical version 28 must remain unchanged.
This was the exact run-65 state at rollout time. Subsequent employee interactions created runs 66/67 and exposed two
finalization defects now repaired by migration 018 plus an SQL-bind correction. Use
`2026-08-06-integer-review-finalize-repair.md` for the current retry boundary; do not act on this historical run-65
instruction.

View File

@@ -0,0 +1,26 @@
# Local ARR Web launcher compatibility recovery
## Metadata
- Date: 2026-08-06
- Status: Restored locally; reboot-persistent supervision remains open
- Scope: owner-local `arr2-web-8766` launcher and local Web listener only
- Last verified: 2026-08-06
## Evidence
- No process listened on local ARR ports and `screen -ls` reported no Web session, so the page could not establish a
connection.
- The Keychain-backed launcher configuration check passed, but its command still supplied `--node-binary` and
`--artifact-tool-module`. The current `arr_web.run --help` shows neither option, and its argument parser rejects both
before application startup.
- Removing only those two retired launch arguments and starting one detached `arr2-web-8766` session restored the
service. Local `/healthz` returned HTTP 200; root redirected to the login page, whose browser console had no warnings
or errors. At verification, both `127.0.0.1:8766` and the current LAN address `192.168.3.48:8766` returned root
redirect HTTP 303.
## Boundary
No login, form submission, upload, Finance/object-store/database write, processor run or monthly-worker action was
performed. The detached Screen process is still not reboot-persistent; the existing supervision maintenance item
continues to apply.

View File

@@ -4,6 +4,7 @@
| Date | Document / Module | Issue | Next Action |
|---|---|---|---|
| 2026-08-06 | Daily `PRICE_UNMATCHED` final Finance acceptance / business date 2026-08-05 | Integer UI/API and both finalization defects are repaired and deployed: migration 018 admits the immutable manifest artifact, the Finance INSERT bind count is exact, and a real PostgreSQL rollback slice passes. Latest run 67 is still revision-2 `generation_failed` with frozen GRP1/LBLT `200/0`; it has no Finance version or run outbox. Run 66 separately retains `2300/0`, run 65 remains unset, and rejected version 28 remains immutable | Refresh/login, open latest run 67, verify `200/0` are the intended approved values, then explicitly retry generation once. Do not retry run 66 by mistake. After success, prove 153 retained rows with 2 `manual_review` rows, one active Finance version, matching frozen-manifest SHA and exactly one new final daily-commit/monthly event; do not modify version 28 |
| 2026-07-31 | Booking raw Excel parser/review business acceptance | Parser 2.0, migrations 014/015, atomic draft repository, routes, editor, HTTP transport and current runtime are ready. Supplied-workbook replay, 55 expanded tests, live 5/5 processing and a real-PostgreSQL create/edit/activate/rollback slice pass. No authorized real workbook has replaced the current source | Run one operator-authorized complete-workbook upload/review/activation, then one released five-company job and verify all downloads |
| 2026-07-31 | Booking review audit depth | Migration 015 stores current confirmed/pending/deleted state but no reviewer, reason, explicit review event, old/new values or immutable revision history. Repository logic serializes and gates activation, but declarative DB guards are limited and same-artifact re-extraction deletes earlier unactivated edits | Confirm whether single-operator latest-state editing is sufficient. If review must be auditable, add actor/reason/event or revision tables, optimistic concurrency and DB transition guards before migration acceptance |
| 2026-07-31 | Booking Excel current-source runtime acceptance | Current port 8766 loads the review composition with all readiness flags true; migrations 014/015 are recorded and `booking.current_source_batch` still selects Markdown batch 1. A 14:14 +08 read-only recheck found no reviewing draft; the prior draft is no longer an active task-creation blocker and did not switch the current source. The Markdown five-workbook baseline has been frozen with exact hashes | Run one Markdown-backed five-company Web job after the July period completes if a final-period snapshot is desired, then verify downloads. Keep any later complete-workbook activation operator-authorized, then compare its five overlapping Group Codes with the frozen baseline |
@@ -16,7 +17,7 @@
| 2026-07-30 | `booking_test` current Finance projection | The earlier 417-row snapshot included accepted run `mvp-v1-fixture-20260727` from `synthetic.xml`/`local_fixture`. A 2026-07-31 14:17 read-only company-report recheck found 986 current supported-company facts, so the old 416/417 remediation target is no longer a complete description of current Finance state | Re-audit current daily-version pins and source provenance before any fixture retirement; require explicit authorization for version changes, then verify the clean target and watermark |
| 2026-07-30 | Channel BI refresh lifecycle | Resolved 2026-08-03. Desktop and public H5 now check selected-month metadata every five seconds while visible and reload full analytics only after `updated_at` changes; hidden views pause and transient failures preserve the last good snapshot | Refresh the browser once to load the new static assets, then observe the next authorized publication as a live acceptance check |
| 2026-07-30 | Channel BI KPI label | The card labeled `公司数` renders worksheet-level `channel_count`; LianTai GROUP/FIT are two channels, so six does not mean six companies | Decide whether to relabel it `渠道/子表数` or implement an explicit five-company aggregation |
| 2026-08-04 | Production monthly-worker packaging | Resolved in code: monthly/company builders are Python/openpyxl-only, new report artifacts use private OSS, and `.web-jobs` remains local. Docker/CentOS acceptance and migration 016 application are still operator-side deployment actions | Apply 016, rebuild the clean image, start Web plus the independent worker with existing OSS secrets, then verify OSS-backed downloads after removing the report cache |
| 2026-08-04 | Production monthly-worker packaging | Resolved in code: monthly/company builders are Python/openpyxl-only, new report artifacts use private OSS, and `.web-jobs` remains local. Live `booking_test` function/comment evidence confirms migration-016 semantics are active; its earlier application timestamp is not recorded, and 016 was deliberately not rerun during the 017 rollout. Docker/CentOS acceptance remains operator-side | Rebuild the clean image, start Web plus the independent worker with existing OSS secrets, then verify OSS-backed downloads after removing the report cache |
## Superseded For ARR2.0