feat: add public h5 dashboard and publish traceability
This commit is contained in:
@@ -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. |
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user