117 lines
7.1 KiB
Markdown
117 lines
7.1 KiB
Markdown
# 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.
|