feat: add public h5 dashboard and publish traceability

This commit is contained in:
Wyndham ARR
2026-08-03 13:04:41 +08:00
parent 7e7470821b
commit 2107f00e32
29 changed files with 1047 additions and 71 deletions

View File

@@ -4,6 +4,10 @@ Use this index for searchable, traceable evidence records.
| Date | Topic | Status | Source | Detail |
|---|---|---|---|---|
| 2026-08-03 | Public mobile H5 dashboard boundary | Implemented; deployment/restart pending | [Evidence topic](topics/2026-08-03-public-h5-dashboard.md) | Anonymous H5 page/assets and purpose-built `/api/public/h5/*` aggregate routes are allowed. The projection omits `source_monthly_sha256`; desktop, generic/legacy APIs, detailed health, jobs/traces, downloads and writes remain protected. `test_arr_web*` discovery passes 71 tests; no live service or business data was changed. |
| 2026-08-03 | ARR job trace semantics for internet deployment | Implemented; live deployment acceptance pending | [Evidence topic](topics/2026-08-03-arr-job-trace-semantics.md) | Trace projection adds `execution_scope`, `processor_mode` and `remote_dispatch`; `artifact_callback` is identified as ARR fixed-processor runtime execution and legacy `direct_mcp` as remote Agent/MCP delivery. “本地处理” copy is removed, pending downstream explicitly follows Finance commit, and persisted `local-...` delivery identities are retained. Focused trace/Web tests pass; no live task or business write occurred. |
| 2026-08-03 | Booking Excel to company-report traceability | Implemented; authenticated browser acceptance pending | [Evidence topic](topics/2026-08-03-company-source-report-traceability.md) | New report jobs persist a safe source summary captured at submission; current source, job detail and history expose the filename/batch context. Identical active uploads refresh context without re-extraction or automatic generation. Full discovery passes 360 tests with 10 optional skips; no live mutation or restart occurred. |
| 2026-08-03 | 2026-07 company-report retry publication failure | Implemented; live historical rerun pending | [Evidence topic](topics/2026-08-03-company-report-publish-retry.md) | July `11-20` job `60bcda9c…` failed for all five companies at `publish` after an earlier same-period success. Publisher idempotency now validates and reuses a complete matching archive/result pair despite different rebuilt XLSX bytes, while partial or conflicting state fails closed. |
| 2026-08-02 | Cross-month history visibility | Implemented and runtime-active | [Evidence topic](topics/2026-08-02-history-month-visibility.md) | Three independent history-month navigators now default to the latest non-empty month through a read-only count endpoint; company generation/history months are separated. July resolves to 30 daily/4 monthly/4 company jobs, 356 tests pass, 1440/900/390 browser QA is overflow-free, port 8766 is ready and no business write occurred. |
| 2026-07-31 | Channel BI header utilities and compact month selector | Implemented; focused static checks pass | [Evidence topic](topics/2026-07-31-channel-bi-header-utilities.md) | The visible BI panel heading and ready-state 数据库已连接 copy are gone from desktop/H5 while the navigation label, browser metadata and status dot remain. The month selector is compact; task-log/logout use low-emphasis accessible links, and the desktop BI panel now sits 20px closer to the tab bar with a 10px mobile adjustment. No API or business data changed. |
| 2026-07-31 | Channel BI copy and data coverage | Implemented; focused checks pass | [Evidence topic](topics/2026-07-31-channel-bi-copy-and-data-coverage.md) | Desktop and H5 remove the BI-only CHANNEL PERFORMANCE, snapshot watermark and visible BI TOTAL PRICE labels. The company-sales panels show localized 数据范围 from additive min_arrival_date plus existing max_arrival_date values returned by the same repeatable-read current-version snapshot. |

View File

@@ -0,0 +1,43 @@
# ARR Job Trace Semantics for Internet Deployment
## Question
Whether the ARR task trace is understandable when ARR is deployed on a server reachable through the internet, rather
than being interpreted as if processing occurred on the operator's computer.
## Findings
- The current `artifact_callback` path is owned by ARR's fixed processor. It does not create a remote Agent/MCP run,
so `remote_run_id=null` is expected and is not a failure.
- Persisted delivery keys such as `local-arrjob-...-a1` are delivery/idempotency identities. They are retained for
compatibility and are not a claim about the user's machine or the server's geographic location.
- A successful job with `current_stage=downstream` and `DOWNSTREAM_EVENT_QUEUED` has already committed the Finance
version. `publish_status=pending` means the durable outbox event is waiting for its downstream consumer.
## Change
The trace job summary now adds:
| Field | `artifact_callback` | legacy `direct_mcp` |
|---|---|---|
| `execution_scope` | `arr_runtime` | `remote_agent` |
| `processor_mode` | `fixed_processor` | `legacy_direct_mcp` |
| `remote_dispatch` | `none` | `mcp` |
User-visible messages now say `ARR 固定处理器输出` and explicitly state `Finance 数据库提交已完成` before the
outbox wait. Existing `delivery_mode`, `remote_run_id`, trace version, persisted delivery IDs and privacy allowlist
remain compatible.
## Verification
- `python3 -m unittest tests.test_arr_web_job_trace tests.test_arr_web_task_log_ui tests.test_arr_web` — 27 passed.
- `python3 -m py_compile arr_web/job_trace.py` — passed.
- `node --check arr_web/static/app.js` — passed.
- `git diff --check` — passed.
- No live task was rerun, no service was restarted and no Finance/report/Booking business data was changed.
## Follow-up
After deployment, inspect one successful `artifact_callback` trace and confirm the additive header fields appear in the
deployed CLI/API/UI. This is a display/observability acceptance step; it is separate from rerunning the historical
2026-07 company-report `11-20` publication.

View File

@@ -0,0 +1,46 @@
# Evidence Topic: 2026-07 company-report retry publication failure
## Metadata
- Date: 2026-08-03
- Status: Implemented; live historical rerun pending
- Scope: 2026-07 company-channel report, C/O `11-20`, local Web job `60bcda9c8dae49758fb69dc47e0ff9e7`
- Confidence: Fact for recorded job/build observations; inference for the exact publisher guard
- Source: job JSON, prior successful job JSON, read-only controlled PostgreSQL snapshot, source-code inspection and temporary-directory double-build probe
- Last verified: 2026-08-03
- Stale trigger: changes to the company XLSX builder, artifact identity, reservation-version derivation or publisher idempotency
## Question
Why did the Web show `正式表格发布失败` for 2026-07?
## Evidence
- The failed job is for report month `2026-07`, period `11-20`, and as-of date `2026-07-20`. All five companies failed at stage `publish` with `COMPANY_REPORT_PUBLISH_FAILED`; there are no record IDs and no source/Booking/business validation errors.
- The immediately preceding job for the same month and period succeeded for all five companies and created archive versions. The failure is therefore a retry of an already published semantic snapshot, not a first-build data failure.
- `company_reports.repository.PostgresReportRepository.reserve_report()` derives the version directory from a deterministic semantic identity containing the company, period, as-of date, processor/rule/schema versions, daily-version pins and Booking-version pins. The retry therefore targets the same archive version path.
- A read-only double-build probe loaded the current controlled July snapshot and built each of the five valid `11-20` workbooks twice in separate temporary directories. For every company, the two binary SHA-256 values differed while the builder's `semantic_sha256` was identical.
- `AtomicReportPublisher._install_once()` rejects an existing archive path when its binary SHA-256 differs from the newly built file, raising `COMPANY_REPORT_PUBLISH_FAILED`. The result JSON also embeds the binary SHA-256, so allowing only the archive copy would still leave the same retry-identity conflict at result validation.
- No database write, report rerun or output repair was performed during this diagnosis.
## Implemented Fix
- `AtomicReportPublisher` now checks for an existing archive/result pair before creating legacy/current side effects. A
retry is reusable only when both files are regular files, the result JSON matches the deterministic report identity and
current semantic SHA, and the archive's actual SHA-256 matches the result JSON's stored artifact SHA.
- A matching retry returns the existing archive/result metadata and activates the repository without copying the new
XLSX bytes. An existing current file is left untouched; a missing current file is repaired from the existing archive.
- Partial, corrupt, hash-inconsistent or semantically conflicting publication state fails closed with
`PUBLISH_FAILED`. First publication and activation-failure rollback paths remain covered.
- Regression coverage passed for same-semantic/different-binary retry, semantic mismatch, partial publication and
corrupted archive. The real ArtifactTool integration regression is present but was skipped locally because the
optional `@oai/artifact-tool` dependency is unavailable.
- No live report rerun, output repair or business-data mutation was performed after the code change.
## Conclusion
The 2026-07 `11-20` failure was caused by a non-byte-deterministic XLSX build colliding with the publisher's deterministic archive/version identity on retry. The workbook contents were semantically the same, but changing XLSX bytes made the immutable archive and result identity appear conflicting. The existing successful 11-20 artifacts remain valid; the failed retry did not replace them. The publisher now handles this case idempotently by reusing that stable identity.
## Fix direction
The publisher-side idempotency fix is implemented and is the active remedy: reuse/validate the existing artifact by semantic identity and preserve one stable binary artifact identity. Making the XLSX builder byte-deterministic remains an optional future optimization, not a prerequisite for retry success. The historical job still needs a deliberate live rerun to verify the deployed path.

View File

@@ -0,0 +1,41 @@
# Booking Excel to company-report traceability
## Scope
The Booking Excel upload page could tell the operator that an identical workbook was already active, but the company-report
history and job detail did not show which source was associated with a generated result. This change adds source identity
without exposing workbook rows, guest data or full artifact hashes.
## Diagnosis
- `booking_ingestion/excel_review_postgres.py` deduplicates an identical activated artifact and raises
`BOOKING_EXCEL_SOURCE_ALREADY_ACTIVATED` with `这份 Excel 已经启用,无需重复提取`.
- `/api/company-reports/source` already returned a privacy-safe source summary, but the frontend used it only as a
readiness boolean.
- `POST /api/company-reports/jobs` accepted only month and period, and persisted no source metadata in the durable job
state. The frontend API helper also discarded error codes, preventing an informational duplicate-upload branch.
## Implemented behavior
- The server captures the current active source when a report job is submitted; the browser cannot choose or spoof the
source identity.
- New job records expose only source batch id/type, safe filename, activation time, disposition and summary counts.
- The company page shows the current active workbook and summary counts.
- Job detail and history show the source Excel captured at submission; old job files without this field render
`历史任务未记录来源`/an equivalent localized label.
- Re-uploading the same active workbook clears the selected file, refreshes the current-source and report-history views,
and shows an informational message. It does not create a second extraction or automatically generate a report.
- Report generation remains an explicit month/period action. Upload controls stay blocked while a report job is active, so
the submission-time source remains the trace shown for that job.
## Verification
- `./.venv/bin/python -m unittest tests.test_arr_web_company_reports tests.test_arr_web`: 28 tests passed.
- `./.venv/bin/python -m unittest discover -s tests -p 'test_arr_web*.py'`: 69 tests passed.
- `./.venv/bin/python -m unittest discover -s tests`: 360 tests passed, 10 optional skips.
- `node --check arr_web/static/app.js` and Python compilation passed.
- `git diff --check` passed.
The available in-app browser session landed on the login page without an authenticated session. No login, upload,
activation, report generation, service restart or business-data mutation was attempted; authenticated browser acceptance
remains the deployment follow-up.

View File

@@ -0,0 +1,30 @@
# Public Mobile H5 Dashboard
## Request
Make the mobile dashboard publicly viewable while preserving the authenticated Finance/operator surface.
## Public contract
- Anonymous `GET /h5`, `/h5.html`, `/assets/h5.css` and `/assets/h5.js` are allowed. `i18n.js` was already public as a
login asset.
- Anonymous `GET /api/public/h5/months` returns only month key, update time and maximum ARRIVAL watermark.
- Anonymous `GET /api/public/h5/analytics?month=YYYY-MM` returns aggregate channel/room metrics and date coverage.
The public projection omits `source_monthly_sha256`, filenames and operational metadata; JSON responses are
`Cache-Control: no-store`.
- H5 uses anonymous `/healthz` for its connection indicator and does not require a CSRF token for read-only data. When a
logged-in operator opens H5, the existing session is still detected and the optional logout control remains available.
## Protected contract retained
Anonymous desktop `/`, `/api/health`, generic `/api/months`, `/api/analytics`, legacy `/api/h5/months`, legacy
`/api/monthly/{month}/analytics`, jobs, traces, downloads, uploads, Booking/company/report routes and mutations remain
protected by the existing session/CSRF boundary.
## Verification
- `python3 -m unittest tests.test_arr_web_auth tests.test_arr_web` — 26 passed.
- `python3 -m unittest discover -s tests -p 'test_arr_web*.py'` — 71 passed.
- `node --check arr_web/static/h5.js`, `node --check arr_web/static/app.js` and `python3 -m py_compile arr_web/app.py` — passed.
- `git diff --check` — passed.
- No live service restart, public deployment, database write or business/report mutation was performed.