# Evidence Topic: Channel BI post-update data contamination ## Metadata - Date: 2026-07-30 - Status: Active - Scope: ARR2 local Web port 8766, controlled `booking_test`, Channel BI and July monthly publication - Confidence: Fact - Source: source/hash inspection, isolated deterministic replay, live privacy-minimized APIs, repeatable-read read-only PostgreSQL reconciliation and focused tests - Last verified: 2026-07-30 - Stale trigger: any change to July current Finance pins, retirement of fixture version 2, monthly republication, or BI grouping/label behavior ## Question What data does Channel BI consume, and why did its July values look wrong after the latest uploads? ## Evidence ### Implemented source and formulas - The browser calls `/api/analytics?month=YYYY-MM`; `PostgresPortalRepository` delegates to `PostgresAnalyticsRepository` against `booking_test`. - Month/date pins come from `finance.current_daily_versions`, channel membership/order/count expectations from `finance.daily_channel_metrics`, and aggregates/detail from current retained `finance.v_active_daily_facts`. - Sold rooms are `sum(no_of_rooms)`, room nights are `sum(no_of_rooms * nights)`, and revenue is `sum(total_price)` without multiplying the stored total again. Monthly XLSX files are not a BI source. ### Live reconciliation The active ARR2 Web API on port 8766 and direct PostgreSQL reads return the same current projection. Legacy port 8765 is no longer listening. | Business date | Daily version | Provenance | Retained rows | Revenue | Current role | |---|---:|---|---:|---:|---| | 2026-07-20 | 5 | `0720.XML`, hash-matched real upload | 100 | 370,500 | valid real daily slice | | 2026-07-21 | 4 | real ARR upload | 119 | 452,000 | V01 operational source | | 2026-07-22 | 6 | `0722.XML`, hash-matched real upload | 88 | 269,700 | valid real daily slice | | 2026-07-23 | 7 | `0723-RES_DETAIL-修正版.xml`, hash-matched real upload | 109 | 359,150 | valid real daily slice | | 2026-07-27 | 2 | `mvp-v1-fixture-20260727` / `synthetic.xml` / `local_fixture` | 1 | 5,400 | test contamination | - The 07-23 upload committed at 16:12:27, after the earlier 15:44 diagnostic snapshot. This explains why that snapshot contained only 307 real rows; it did not prove the four-day business total. - Excluding fixture version 2 now yields 416 rows/rooms, 853 room-nights, 1,451,350 revenue, nine room types, six worksheet channels and latest real ARRIVAL 2026-07-23. - The live BI includes the fixture and therefore returns 417 rows/rooms, 856 room-nights, 1,456,750 revenue, ten room types and `更新至 2026-07-27`. Its extra type is explicitly `SYNTHETIC ROOM TYPE`. - July monthly V04 is active with 417 rows; V01–V04 all inherit fixture version 2 and its false operational 7.27 watermark. ### Source identity and date attribution - Immutable source SHA-256 maps the accepted sources exactly to local originals: `0720.XML` is version 5 and `0722.XML` is version 6; `0721.XML` and `0723-RES_DETAIL-修正版.xml` map to versions 4 and 7 respectively. - The processor derives business date from agreeing XML `GROUPBY1_SORT_COL`/`GROUPBY1_COL` values and requires each retained reservation ARRIVAL to match it; filename and upload order are not date authorities. - An isolated replay independently reproduced `0720.XML -> 2026-07-20 -> 100 retained` and `0722.XML -> 2026-07-22 -> 88 retained`. The user was correct about the 416 total but had those two day counts verbally reversed. ### Frontend freshness - Channel BI loads at page boot, month change, or first BI-tab entry only. A successful XML upload refreshes daily jobs but not analytics, and later BI-tab entries skip reload once `state.analytics` exists. - Therefore an already-open page can continue showing the pre-07-23 value 308 even though the API has advanced to 417. Unlike the monthly list, Channel BI has no polling or visibility-aware reload. ### Exact V01 reconciliation - V01 report ID 1 is persisted as superseded, `as_of_date=2026-07-27`, 120 rows and five channels. - Its legitimate OSS source lineage is daily version 4: 119 retained rows, all with `ARRIVAL=2026-07-21`. - Its only later lineage is daily version 2: one retained row with `ARRIVAL=2026-07-27`, accepted under run `mvp-v1-fixture-20260727` from `synthetic.xml` using provider `local_fixture`. - Reopening the exact registered V01 workbook independently found the same 119/1 ARRIVAL distribution. The file is 20,741 bytes and its SHA-256 `a43cdac3f6d97f6b73f635d0f386cffd0ad8b6416e8f92a7b6afc2f79f1e8635` matches the artifact registry. - Therefore the user's operational statement “V01 is updated through 2026-07-21” is correct. The stored 7.27 label is the consequence of contaminated report content, not a filename parser or a metadata-only rendering defect. ### Channel/company semantics - Six channel keys are valid for these real facts: LianTai intentionally splits into `LIANTAI-GROUP` and `LIANTAI-FIT`, alongside QBD, DY-AI-Easy-KB, FENGRUN and HanaTour. - The BI card renders worksheet-level `channel_count` but labels it `公司数`; six channels can represent five top-level companies, so the label is misleading even after data cleanup. ### Regression and mutation boundary - The corrected focused run passed 24 analytics-contract, PostgreSQL-provider and Web tests. - All database investigations used `REPEATABLE READ READ ONLY`; no Finance pointer, version, artifact or report was changed during diagnosis. ## Finding Channel BI is correctly reading and aggregating its configured current PostgreSQL facts. One proven cause of the reported mismatch is a data-hygiene incident: a database acceptance fixture remained pinned as a current Finance day after real uploads were enabled. It explains the one-room difference between the correct real total 416 and live API total 417, as well as every false 7.27 watermark. A second frontend defect can leave an open BI view showing 308 after the 07-23 commit. The user's 07-20/07-22 verbal attribution was reversed, but this does not change the 416 total. A smaller UX issue independently mislabels worksheet-channel count as company count. ## Impact - Business-facing July BI and monthly V04 are not clean acceptance outputs until fixture version 2 is removed from the current projection and a clean July monthly version is published. - Historical V01 cannot be truthfully corrected by changing only its `as_of_date` or filename because the registered workbook itself contains the fixture row. Preserve that immutable artifact as contaminated audit history and publish a clean operational replacement after the current projection is corrected. - Remediation should preserve immutable audit history; retire/unpin the fixture instead of deleting its version rows. - Production/readiness checks should reject any current Finance source whose provider is `local_fixture` (or other explicitly non-operational provenance). ## Open Items - Obtain authorization for the controlled Finance-pointer repair and clean monthly republication. - Make Channel BI refresh after successful upload and/or on visible BI-tab re-entry so it cannot retain an old snapshot. - Decide whether to relabel the BI card as `渠道/子表数` or add a distinct five-company aggregation.