Files
wyndham-ARR/.project-docs/50-evidence/topics/2026-07-30-channel-bi-post-update-data-contamination.md
2026-07-31 15:11:42 +08:00

117 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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; V01V04 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.