feat: add public h5 dashboard and publish traceability
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# ADR-005: Public Read-Only H5 Dashboard
|
||||
|
||||
## Status
|
||||
|
||||
Accepted — 2026-08-03
|
||||
|
||||
## Context
|
||||
|
||||
ARR Web login protects Finance operations, but the mobile H5 dashboard is a read-only aggregate view intended for
|
||||
broader viewing. Making only the HTML page public would leave its data requests behind the login gate. Making generic
|
||||
desktop APIs public would widen access to jobs, traces, operational health, filenames and other operator data.
|
||||
|
||||
## Decision
|
||||
|
||||
1. Publish the H5 page/assets anonymously.
|
||||
2. Add purpose-built anonymous `GET /api/public/h5/months` and `GET /api/public/h5/analytics` routes.
|
||||
3. Return only aggregate dashboard data and month/date coverage from those routes; omit source hashes, filenames,
|
||||
traces, detailed health, source coordinates, guest/detail rows and operational metadata.
|
||||
4. Keep desktop pages, generic and legacy APIs, jobs/traces, downloads, uploads, Booking/company/report operations and
|
||||
mutations behind the existing server-side session/CSRF boundary.
|
||||
5. Keep `/healthz` as the only public readiness signal; public JSON responses remain `no-store`.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Anyone with the H5 URL can view the aggregate channel/room dashboard and all month options returned by the public H5
|
||||
month index.
|
||||
- Finance/operator workflows remain authenticated and the public H5 surface has no write capability.
|
||||
- Deployment must treat the aggregate metrics, channel names and financial totals as intentionally public data and should
|
||||
apply normal HTTPS, access logging and rate/abuse controls at the edge.
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
| ID | Decision | Status | Date | Applies To | Detail |
|
||||
|---|---|---|---|---|---|
|
||||
| ADR-005 | 手机 H5 看板通过专用只读聚合接口公开访问;桌面及运营接口继续认证 | Accepted | 2026-08-03 | Web access boundary, H5, Channel BI | [ADR-005](ADR-005-public-read-only-h5-dashboard.md) |
|
||||
| ADR-004 | ARR2.0 owns deterministic processing, artifact validation and terminal ingestion; no Agent/MCP in production XML flow | Accepted | 2026-07-30 | Upload, processing runtime, OSS ACL, validation, deployment | [ADR-004](ADR-004-arr-owned-programmatic-processing.md) |
|
||||
| ADR-001 | 月报由独立 worker 自动触发;“更新至”取最新纳入的 `ARRIVAL`;发布后页面自动新增;`TOTAL PRICE` 为公式字段 | Implemented | 2026-07-29 | 月报触发、持久化、页面同步、XLSX 输出 | [ADR-001](ADR-001-automatic-monthly-trigger-and-total-price-formula.md) |
|
||||
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
## Web Access Boundary
|
||||
|
||||
1. An anonymous browser requesting `/` or `/h5` is redirected to `/login` with an allowlisted return target; anonymous API/download requests receive JSON `401 AUTH_REQUIRED`.
|
||||
1. An anonymous browser requesting `/` is redirected to `/login` with an allowlisted return target. `/h5` and its H5
|
||||
assets are public; anonymous access is limited to `/api/public/h5/months`, `/api/public/h5/analytics` and the
|
||||
no-detail `/healthz` signal. Other anonymous API/download requests receive JSON `401 AUTH_REQUIRED`.
|
||||
2. `POST /api/login` accepts strict same-origin JSON, verifies runtime-owned credentials under a bounded per-client attempt ledger and issues a random server-side session plus CSRF token in an `HttpOnly`, `SameSite=Strict` cookie.
|
||||
3. Every portal page, detailed health response, read API, upload/mutation, task trace and artifact download validates the authenticated session. Mutations additionally validate the session CSRF token.
|
||||
3. Every desktop portal page, detailed health response, generic read API, upload/mutation, task trace and artifact
|
||||
download validates the authenticated session. Public H5 endpoints return only sanitized aggregate dashboard data;
|
||||
mutations additionally validate the session CSRF token.
|
||||
4. `POST /api/logout` validates CSRF, revokes the server-side session and expires the cookie. `/healthz` remains a no-detail anonymous readiness signal for container orchestration.
|
||||
5. `GET /api/history-months` is an authenticated read-only discovery path. It combines daily processing/monthly publication counts from PostgreSQL with durable company-job counts; each desktop history then reads its selected month through the existing paged list endpoint.
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
| `arr_storage/aliyun_oss_v2.py` | Encrypted/unversioned OSS adapter | Writes all objects private |
|
||||
| `arr_web/downloads.py` | OSS daily + controlled local report download routing | Rechecks metadata, size and SHA-256 |
|
||||
| `arr_web/job_trace.py` | Programmatic persisted-fact trace | No external trace store |
|
||||
| `arr_web/app.py`, `arr_web/repository.py`, `arr_web/company_jobs.py` | Authenticated portal routes, paged history reads and read-only history-month discovery | Default-deny login gate protects pages/APIs/downloads; daily/monthly counts and rows share a repeatable-read snapshot; `/api/history-months` merges daily/monthly database counts with company job-state counts; company totals/slices share one lock |
|
||||
| `arr_web/app.py`, `arr_web/repository.py`, `arr_web/company_jobs.py` | Authenticated portal routes plus public H5 aggregate routes, paged history reads and read-only history-month discovery | Default-deny login gate protects the desktop/API/download surface; purpose-built `/api/public/h5/*` exposes only sanitized aggregate metrics; daily/monthly counts and rows share a repeatable-read snapshot; `/api/history-months` merges daily/monthly database counts with company job-state counts; company totals/slices share one lock |
|
||||
| `arr_web/server.py` | Standard-library HTTP transport | Dispatches GET/POST/PATCH/DELETE with one bounded body reader; real socket tests cover review update/delete and missing/oversized lengths |
|
||||
| `arr_web/static/login.html`, `login.css`, `login.js` | Responsive ARR login gateway | Labeled form, password visibility, generic inline failures, safe desktop/H5 return target and reduced-motion support |
|
||||
| `arr_web/static/app.js`, `arr_web/static/h5.js` | Authenticated client state, rendering and polling | Desktop daily/monthly/company histories own independent viewing-month state and default to the latest non-empty month; company generation month remains separate. Desktop also includes 50-row Booking draft review/edit, the draft's validated uploaded filename below the review title, individual/all-visible selection, count-aware in-page delete confirmation and activation; company generation keeps the fixed five-company context beside the page title, a four-card upload/period setup row, cumulative CO display labels, short centered period actions and an in-page generation confirmation dialog; session-expiry redirect and CSRF logout remain shared; monthly versions auto-refresh the selected viewing month every four seconds |
|
||||
| `arr_web/static/app.js`, `arr_web/static/h5.js` | Authenticated desktop and anonymous-capable H5 client state, rendering and polling | Desktop daily/monthly/company histories own independent viewing-month state and default to the latest non-empty month; company generation month remains separate. H5 reads only public aggregate endpoints and may retain optional logout for an authenticated session. Desktop also includes 50-row Booking draft review/edit, the draft's validated uploaded filename below the review title, individual/all-visible selection, count-aware in-page delete confirmation and activation; company generation keeps the fixed five-company context beside the page title, a four-card upload/period setup row, cumulative CO display labels, short centered period actions and an in-page generation confirmation dialog; session-expiry redirect and CSRF logout remain shared; monthly versions auto-refresh the selected viewing month every four seconds |
|
||||
| `monthly_reports/worker.py` | Dedicated outbox consumer | Lease/reclaim, retry/dead-letter, success acknowledgement after activation |
|
||||
| `monthly_reports/repository.py` | Monthly snapshot and publication repository | Derives scope from `ARRIVAL`; persists metadata/lineage/artifact identities |
|
||||
| `monthly_reports/xlsx/build_workbook.mjs` | Monthly XLSX builder and reopen validator | Exact row-relative `TOTAL PRICE` formulas only in column S |
|
||||
@@ -26,7 +26,7 @@
|
||||
| `database/012_monthly_report_publication.sql` | Additive metadata-only publication schema | Applied after immutable 008–011 baseline |
|
||||
| `database/014_booking_current_source_batch.sql` | Booking full-workbook current-source pointer and view scoping | Formally applied on 2026-07-31; batch 1 remains selected |
|
||||
| `database/015_booking_excel_review_drafts.sql` | Item-level Booking extraction draft state | Formally applied and empty; basic latest-state review only, with no actor/reason/revision history and no DB-enforced zero-pending activation |
|
||||
| `compose.yaml`, `deploy/` | Web-login + Caddy-HTTPS template and worker deployment boundary | Requires Web credentials; only `/healthz` is anonymously probed; no MCP port/domain/service |
|
||||
| `compose.yaml`, `deploy/` | Web-login + Caddy-HTTPS template and worker deployment boundary | Requires Web credentials for the desktop/operational surface; public H5 aggregate routes and `/healthz` remain anonymously reachable; no MCP port/domain/service |
|
||||
| `tests/test_arr_programmatic.py` | Real success/failure vertical slices | Primary ARR2.0 acceptance proof |
|
||||
|
||||
Historical ARR1 compatibility modules and migrations remain for audit/tests but are not imported, installed or deployed
|
||||
|
||||
@@ -31,7 +31,9 @@ from committed `ARRIVAL` facts, publishes a validated workbook, and records meta
|
||||
## Important Boundaries
|
||||
|
||||
- The uploaded source is re-materialized from committed object storage before processing.
|
||||
- Login documents/assets, `POST /api/login` and minimal `/healthz` readiness are the only anonymous Web routes. Portal documents redirect to login; business APIs, detailed health, uploads, traces and downloads return `401` without an authenticated server-side session.
|
||||
- Login documents/assets, the H5 read-only page/assets, purpose-built public H5 aggregate endpoints, `POST /api/login`
|
||||
and minimal `/healthz` readiness are anonymous Web routes. The desktop portal redirects to login; generic business
|
||||
APIs, detailed health, uploads, traces and downloads return `401` without an authenticated server-side session.
|
||||
- Operator credentials come only from runtime `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD`; the application fails closed when either is absent. Authenticated mutations also require the per-session CSRF token.
|
||||
- Processor exit code and JSON status must agree; output paths are confined to an isolated temporary directory.
|
||||
- Validation completes before the database transaction; any transaction failure leaves no partial active version.
|
||||
|
||||
@@ -10,7 +10,38 @@ download identity.
|
||||
The company-channel detail generator now separates generation permission from calendar completeness: a current-month
|
||||
or historical C/O period can be generated from the current committed Finance snapshot, while future report months stay
|
||||
blocked. A not-yet-ended period keeps its fixed C/O cutoff and must be rerun after later source facts arrive if the final
|
||||
workbook needs those facts.
|
||||
workbook needs those facts. Duplicate publication of the same semantic snapshot is idempotent: the first validated
|
||||
archive/result pair remains authoritative even if a retry rebuilds different XLSX bytes.
|
||||
|
||||
## Completed On 2026-08-03
|
||||
|
||||
- Made the mobile H5 dashboard publicly readable without weakening the desktop/operator boundary. Anonymous users can
|
||||
load `/h5`, its H5 assets and purpose-built `/api/public/h5/months` plus `/api/public/h5/analytics` aggregate routes;
|
||||
the public projection omits source hashes and operational metadata. Generic analytics, legacy H5 APIs, desktop pages,
|
||||
detailed health, jobs/traces, downloads and all write paths remain authenticated. H5 keeps optional authenticated
|
||||
logout, uses no-detail `/healthz` for its connection indicator, and 71 `test_arr_web*` tests plus syntax checks pass.
|
||||
No live service restart or public deployment was performed.
|
||||
- Clarified ARR trace semantics for internet-facing deployment. `artifact_callback` traces now expose additive
|
||||
`execution_scope=arr_runtime`, `processor_mode=fixed_processor` and `remote_dispatch=none`; legacy `direct_mcp`
|
||||
traces identify remote Agent/MCP delivery. User-visible messages no longer call ARR output “本地处理”, and the
|
||||
queued downstream message explicitly says Finance database commit is complete while the outbox waits for its
|
||||
consumer. Persisted `local-...` delivery keys and existing trace fields remain unchanged. Trace/Web tests (27) plus
|
||||
Python/JavaScript syntax and diff checks pass; no live task or business data was mutated.
|
||||
- Fixed company-report duplicate publication. A retry now validates the existing archive/result pair against the
|
||||
deterministic reservation and builder `semantic_sha256`, verifies the archive hash against the result JSON, and
|
||||
reuses the first successful artifact metadata instead of comparing the new XLSX binary SHA. Partial, corrupt or
|
||||
semantically conflicting pairs fail closed; an existing `current` file is not overwritten, while a missing one can be
|
||||
repaired from the authoritative archive. First-publication rollback behavior and monthly publication code are
|
||||
unchanged. Publisher tests pass 6/6, company-report tests pass 32 with five optional ArtifactTool skips, Web
|
||||
company/trace tests pass 13/13 and Python compilation is clean. No live report rerun or business-data mutation was
|
||||
performed; the historical 2026-07 `11-20` retry remains to be rerun as a separate acceptance action.
|
||||
- Added Booking Excel to company-report traceability. New report jobs persist a safe source summary captured server-side
|
||||
at submission (batch id, filename, activation time and counts); the company page now shows the active source, job
|
||||
detail shows the submission-time source, and history adds a source column. Re-uploading the same active workbook is
|
||||
treated as an informational refresh that keeps the existing source and report history; it does not re-extract or
|
||||
auto-generate a report. Historical jobs without source metadata remain readable and show that the source was not
|
||||
recorded. Full discovery passes 360 tests with 10 optional skips; no live upload, activation, report generation or
|
||||
service restart was performed because the available browser session was unauthenticated.
|
||||
|
||||
## Completed On 2026-08-02
|
||||
|
||||
@@ -318,6 +349,10 @@ workbook needs those facts.
|
||||
- Booking review persistence passed a real-PostgreSQL transaction-only vertical slice: draft creation exposed two items
|
||||
with one pending, edit confirmed the pending item, activation produced one source row with quantity three, and the outer
|
||||
rollback restored batch 1 with no synthetic draft, source or artifact residue.
|
||||
- A 2026-08-03 diagnosis reproduced the 2026-07 `11-20` company-report retry failure: all five companies failed at
|
||||
`publish` after an earlier same-period success because repeated XLSX builds had identical semantic SHA-256 values but
|
||||
different binary SHA-256 values. Publisher idempotency now reuses a complete matching archive/result pair and fails
|
||||
closed on partial or conflicting state; the historical retry still needs a deliberate live rerun.
|
||||
- Isolated authenticated browser checks covered the upload-first desktop flow, editable review table, fixed-company text
|
||||
and responsive 375/390-pixel layouts with no horizontal overflow or console errors.
|
||||
|
||||
@@ -351,4 +386,4 @@ workbook needs those facts.
|
||||
|
||||
## Last Updated
|
||||
|
||||
2026-07-31
|
||||
2026-08-03
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
| Date | Task | Outcome | Docs Updated |
|
||||
|---|---|---|---|
|
||||
| 2026-08-03 | Make the mobile H5 dashboard publicly readable | Added public H5 shell/assets and purpose-built sanitized aggregate month/analytics endpoints; desktop, generic APIs, legacy H5 APIs, detailed health, jobs/traces, downloads and writes remain protected. Anonymous H5 loading plus authenticated compatibility is covered by 71 Web tests and syntax checks; no live restart or deployment occurred | Current state/history, architecture, business rules, ADR-005, H5 public-access evidence/index, scoped planning record |
|
||||
| 2026-08-03 | Clarify ARR trace semantics for internet deployment | Added additive execution-scope, processor-mode and remote-dispatch fields; replaced misleading “本地处理” copy and clarified that a pending downstream event follows a completed Finance commit. Existing delivery IDs/trace fields remain compatible; 27 trace/Web tests plus Python/JavaScript syntax and diff checks pass, with no live mutation | Current state/history, ARR trace-semantics evidence/index, scoped planning record |
|
||||
| 2026-08-03 | Add Booking Excel to company-report traceability | New company-report jobs store the server-captured active source summary; the page shows the current source, job detail/history show the source Excel, and identical active uploads become an informational refresh instead of a generic extraction error. Old jobs remain compatible without source metadata. Focused Web/company tests and the 360-test suite pass with 10 optional skips; no live mutation or restart was performed | Current state, source-to-report traceability evidence/index, scoped planning record |
|
||||
| 2026-08-03 | Fix duplicate company-report publication | Made same-reservation/same-semantic retries reuse the first validated archive/result identity even when a rebuilt XLSX has different binary bytes. Partial, corrupt or semantic-conflict states fail closed; an existing current file is preserved and a missing one can be repaired from the archive. Publisher tests pass 6/6, company-report tests pass 32 with five optional ArtifactTool skips, Web company/trace tests pass 13/13 and compilation is clean. No live rerun or business-data mutation was performed | Current state, company-report retry evidence/index, scoped plan |
|
||||
| 2026-08-03 | Diagnose 2026-07 company-report `正式表格发布失败` | Found the historical `11-20` retry failed for all five companies at publication after an earlier same-period success. A read-only double-build probe showed identical semantic workbook identities but different binary XLSX SHA-256 values; the immutable archive/result identity rejects that retry. No code, report, database or output state was changed | Current state, company-report retry evidence/index |
|
||||
| 2026-08-02 | Restore prior-month history across Daily Report, monthly processing and company-channel detail | Added a read-only merged history-month count endpoint and three independent month navigators with latest-non-empty defaults, month-aware empty/pagination states and Chinese/English/Thai responsive copy. Company generation/history months are separate and monthly polling follows only the viewing month. A live read confirms July 30/4/4 records, 356 tests pass, isolated 1440/900/390 QA is overflow-free, and the restarted 8766 runtime is ready without business writes | Current state/history, history-visibility evidence/index, stale item, scoped planning record |
|
||||
| 2026-08-02 | Diagnose missing prior-month history and design its display | Proved the data was retained: July has 30 daily jobs, 4 monthly publications and 4 company jobs, while the August-default frontend view is empty. Daily/monthly loaders hard-code the Bangkok current month; company history hides its month filtering inside generation setup. Proposed visible per-history month navigation, recent-record empty states and separation of company generation/history months; no product code, runtime or business data changed | History-visibility evidence/index, stale item, scoped planning record |
|
||||
| 2026-07-31 | Tighten Channel BI top spacing | Raised the desktop BI panel by 20px toward the four-tab bar and applied a lighter 10px mobile adjustment. Other panels, tab behavior and BI data are unchanged; JavaScript syntax and 39 focused Web tests pass | Current state/history, Channel BI utility evidence |
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
## Durable Rules
|
||||
|
||||
- Human access to desktop/H5 pages, detailed health, business APIs, uploads, traces and downloads requires an authenticated ARR Web session. Only login assets, `POST /api/login` and no-detail `/healthz` readiness are anonymous.
|
||||
- Human access to the desktop Finance workspace, detailed health, generic business APIs, uploads, traces and downloads
|
||||
requires an authenticated ARR Web session. The H5 mobile dashboard is an intentional anonymous read-only exception:
|
||||
only its page/assets, `/api/public/h5/months`, `/api/public/h5/analytics` and no-detail `/healthz` are public; the H5
|
||||
projection contains aggregate channel/room metrics and omits source hashes and operational metadata.
|
||||
- The single-operator login identity is injected as `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD`; real credentials are never stored in source or returned/logged. Missing credentials fail Web startup closed.
|
||||
- Login failures use a generic message and bounded attempt blocking. Logout revokes the server-side session; authenticated mutations continue to require CSRF.
|
||||
- The user uploads XML; the user does not submit a report year, report month, cutoff date, or separate monthly-generation request in the primary workflow.
|
||||
|
||||
@@ -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