feat: sync latest ARR implementation

This commit is contained in:
Wyndham ARR
2026-07-31 15:11:42 +08:00
parent d6f8a747fa
commit bf7939dd1a
185 changed files with 17527 additions and 2260 deletions

View File

@@ -4,8 +4,37 @@ Use this index for searchable, traceable evidence records.
| Date | Topic | Status | Source | Detail |
|---|---|---|---|---|
| 2026-07-31 | Live 2131 company-job Booking Room diagnostic | No Booking-write defect found | [Evidence topic](topics/2026-07-31-live-company-job-booking-room-diagnostic.md) | Published job `8aa1fdef…` contains 598 rows: 224 Booking Room values and 374 expected blanks. Of 986 Finance facts, 372 have no Group Code and two other output rows carry unmatched codes. Total Booking Price is Finance-derived, not a Booking-price calculation. |
| 2026-07-31 | Company-channel early generation and period completeness state | Implemented; focused and project-venv full checks pass | [Evidence topic](topics/2026-07-31-company-report-early-generation.md) | Current-month incomplete C/O periods can be generated from the current committed Finance snapshot using fixed `as_of_date` boundaries; later facts require rerun, and future months remain blocked. UI states are `周期未结束` / `周期已结束`, with an early-generation confirmation note. No report or business data was generated during verification. |
| 2026-07-31 | Company-channel period card copy and result details | Implemented; focused/full checks, month-boundary and deduplication checks pass | [Evidence topic](topics/2026-07-31-company-period-card-copy.md) | Removed stage labels, made the C/O ranges month-aware, collapsed duplicate review problems for display, and replaced the user-facing version number with generation time. Internal period keys, API version metadata, release rules and generation payloads are unchanged. |
| 2026-07-31 | Company-channel card row and generation confirmation | Implemented; focused/full checks and isolated responsive QA pass | [Evidence topic](topics/2026-07-31-company-card-row-and-confirm-dialog.md) | Upload plus three C/O periods share one responsive card row; the month picker is compact and visually unlabeled in the setup card's upper-right; period CTAs read `生成`; native confirmation is replaced by an in-page dialog with cancel/backdrop/Escape dismissal. No report or business data was submitted during QA. |
| 2026-07-31 | Markdown current source and July release recheck | Historical pre-change read-only state; superseded by early-generation behavior | [Evidence topic](topics/2026-07-31-markdown-current-source-release-recheck.md) | Batch 1 remains the current 867-row/348-code Markdown source and no review draft is open. The later live Finance projection has 986 facts, all with C/O 7/217/30; processor 1.2.0 previews valid 5/5 with 598 output rows, 224 filled Booking Rooms and 374 blanks. The original 14:1414:17 check recorded the prior release gate; current behavior is documented in the early-generation topic above. |
| 2026-07-31 | Company-detail title and upload rail | Implemented; focused/full checks and isolated responsive QA pass | [Evidence topic](topics/2026-07-31-company-detail-header-upload.md) | Fixed-company context is shown beside the page title in pale small text; the Excel dropzone/action rail is capped at 760px on desktop and stacks to the panel width on mobile. `报表月份` remains the C/O month selector and `刷新任务` remains read-only, with a tooltip documenting its refresh scope. No report/upload/database mutation occurred. |
| 2026-07-31 | Company-channel action button compacting | Implemented; focused/full checks and isolated responsive QA pass | [Evidence topic](topics/2026-07-31-company-action-button-compact.md) | Period generation CTAs are 104124px wide and centered in each card; `提取并核对` shares the 42px compact button family and centers in the upload action grid. Desktop/mobile geometry has zero center delta, one-line labels, no horizontal overflow or console warnings, and no business action was invoked. |
| 2026-07-31 | Daily KPI card size refinement | Implemented; focused checks pass | [Evidence topic](topics/2026-07-31-daily-kpi-card-height.md) | ARRIVAL DATE, processing duration and NO. OF ROOM retain their original grid-column widths and stretch to the same compact desktop grid-row height as the ARR.XML upload panel. The upload internals are compressed into a horizontal dropzone; mobile behavior and processing are unchanged. |
| 2026-07-31 | Daily overview layout and copy cleanup | Implemented; focused checks pass | [Evidence topic](topics/2026-07-31-daily-overview-layout.md) | The duplicate top `Daily Report` title and `THIS MONTH` eyebrow are gone; the history heading is `Daily Report`, and the upload station plus ARRIVAL DATE, processing duration and NO. OF ROOM cards share one responsive desktop row. Upload/progress/task-log behavior and all APIs are unchanged. |
| 2026-07-31 | Standard monthly page cleanup | Implemented; focused checks pass | [Evidence topic](topics/2026-07-31-standard-monthly-page-cleanup.md) | The desktop monthly surface now keeps only a live-status rail, a six-column download table and 50-row pagination. Obsolete standard-monthly/version-history copy, the monthly version column and the C/O-period footer note are absent; `更新至` is backed by the explicit maximum-ARRIVAL API field. No report or business data changed. |
| 2026-07-31 | Daily upload progress and task-log behavior | Implemented; focused checks pass | [Evidence topic](topics/2026-07-31-daily-upload-progress-and-log-behavior.md) | ARR.XML submission stays on the daily page, the existing task-log dialog remains on-demand, and the upload card shows an accessible stage-based estimate with success/error states. JavaScript syntax plus 15 focused Web/static/trace tests pass. |
| 2026-07-31 | Markdown company-channel baseline freeze | Frozen and independently verified; live publication intentionally excluded | [Evidence topic](topics/2026-07-31-markdown-channel-baseline-freeze.md) | Exact Markdown hash/current batch 1 plus five Finance version pins produced five XLSX files and 314 rows. Independent Markdown comparison found zero Booking Room mismatches; all files pass SHA-256/ZIP checks and all 15 sheets passed visual review. One reviewing Excel draft remained isolated and no business/report write occurred. |
| 2026-07-31 | Booking Excel extraction program | Implemented, migrated and runtime-active; first real source activation pending | [Evidence topic](topics/2026-07-31-booking-extraction-program.md) | Supplied workbook yields 26 Tour Codes/37 items/7 pending; review pages at 50, atomically batch-deletes 1-50 selected items and identifies the records with the draft's uploaded filename instead of a historical-source status. A real-PostgreSQL create/edit/activate slice rolled back cleanly, 346 tests pass, isolated desktop/mobile QA is green, and live July data previews valid 5/5. |
| 2026-07-31 | Booking manual-review schema audit | Superseded implementation snapshot; audit-depth finding remains active | [Evidence topic](topics/2026-07-31-booking-manual-review-schema-audit.md) | The audit correctly found a PATCH/DELETE transport gap, which the later extraction-program implementation repaired. Its live-schema facts and reviewer/reason/revision-history gap remain valid. |
| 2026-07-31 | Booking import dimensions and live database inventory | Booking findings active; Finance counts superseded by later live recheck | [Evidence topic](topics/2026-07-31-booking-import-dimensions-and-live-database.md) | The 867-row/348-code Booking inventory and normalized intake model remain valid. Its 417-row Finance count is a historical snapshot; the later read-only release recheck found 986 current supported-company facts. |
| 2026-07-30 | Daily page visual polish | Active | [Evidence topic](topics/2026-07-30-daily-page-visual-polish.md) | The daily surface now preserves workspace-title hierarchy, exposes task log as a real button and centers a simplified upload station with a compact action. Thirty-three focused tests and four responsive live widths passed without overflow or browser issues. |
| 2026-07-30 | Web login runtime mismatch | Resolved; authenticated runtime active | [Evidence topic](topics/2026-07-30-web-login-runtime-mismatch.md) | Keychain-backed login is active under detached Screen on `*:8766`. The live gateway is reduced to one ARR heading plus `username`/`password` fields while preserving auth behavior; 33 focused tests and two responsive browser widths pass. DHCP moved the current LAN entry back to `.48` on 2026-07-31; credential hardening and reboot-persistent supervision remain follow-ups. |
| 2026-07-30 | Daily upload filename provenance | Implemented/migrated; post-restart write acceptance pending | [Evidence topic](topics/2026-07-30-daily-upload-filename-provenance.md) | Migration 013 separates `processing_runs.uploaded_filename` from canonical artifact `source.xml`; history/trace use only the upload provenance and old rows render unknown. The authenticated runtime is active; one controlled no-PII upload remains to verify the live write path. |
| 2026-07-30 | Task-log relocation and daily-only scope | Historical before-state; superseded by the 2026-07-31 upload interaction update | [Evidence topic](topics/2026-07-30-task-log-relocation-and-scope.md) | The desktop header opens the black task console as a native modal, and history selection still reveals the matching job. The earlier upload-triggered opening is superseded; query inspection still proves both history and trace roots are limited to `pipeline_type = 'opera_daily'`. |
| 2026-07-30 | Company-report path and July month-end readiness | Historical pre-change readiness snapshot; current generation rule superseded | [Evidence topic](topics/2026-07-30-company-report-path-and-month-end-readiness.md) | Missing/unmatched Group Codes leave Booking Room blank and current July projection previews valid 5/5. The topic records the prior July completion boundary and the remaining acceptance work; current-month incomplete periods are now also generatable, with rerun after completion when needed. |
| 2026-07-30 | Channel BI post-update data contamination | Active; repair not yet authorized | [Evidence topic](topics/2026-07-30-channel-bi-post-update-data-contamination.md) | Four hash-matched real files total 416 rooms; live API/monthly V04 show 417 because active Finance version 2 is a one-room local fixture. An open BI view can also stay at pre-07-23 value 308 because analytics does not refresh after upload. |
| 2026-07-30 | Monthly publication live acceptance | Implemented; current data caveat active | [Evidence topic](topics/2026-07-30-monthly-publication-live-acceptance.md) | Publication mechanics remain verified. Later runs reached V04 active with 417 rows and five published events; 416 are real and one is the known fixture. Browser/access logs previously proved automatic list discovery without refresh. |
| 2026-07-30 | First live ARR2 user-run trace | Historical before-state; resolved | [Evidence topic](topics/2026-07-30-first-live-arr2-user-run.md) | The daily commit was real but the then-current monthly action was local-only. This diagnosis led to migration 012 and the now-complete publication/worker/formula repair. |
| 2026-07-30 | ARR2.0 programmatic pipeline | Implemented; local vertical slices pass | [Evidence topic](topics/2026-07-30-arr2-programmatic-pipeline.md) | Independent no-Agent runtime runs the fixed processor, validates/stores artifacts and commits atomically. Success/failure, lifecycle SQL, private OSS behavior, trace, downloads and deployment contracts are covered. |
| 2026-07-30 | SuperAgent OSS fetch Prompt experiment | Fetch/Skill/MCP reached; large payload rejected | [Evidence topic](topics/2026-07-30-superagent-fetch-oss-prompt-experiment.md) | The latest v3 job fetched the exact 629434-byte XML once and generated a valid 135-record result. After repeated reads of the roughly 124 KB JSON, the Agent submitted only 20 records. ARR rejected it as `RESULT_CONTRACT_INVALID`, wrote no Finance version and terminalized the job. XML allowlisting and MCP reachability are proven; exact large-payload transport through model arguments is the current blocker. |
| 2026-07-29 | Full-flow task trace terminal | Verified locally and against live SuperAgent trace contract | [Evidence topic](topics/2026-07-29-task-trace-terminal.md) | New tasks use `include_trace=true`, persist only sanitized run/task/step events, and merge them with authoritative upload/writeback/Finance/outbox facts in one black console. POST-only semantics, non-deduplicating stream keys, background SSE handoff, 38 targeted tests and 275 full-suite tests with 2 skips were verified. On 2026-07-30 the same plain console gained one-click full-text copy with 15 Web tests and live button-state/feedback verification. |
| 2026-07-29 | Repeated XML artifact identity fix | Upload fixed; MCP E2E blocked | [Evidence topic](topics/2026-07-29-repeated-xml-artifact-identity-fix.md) | Migration 011 fixed equal-content cross-run identity and a real `0721.XML` upload reached SuperAgent. The remote run returned success under MCP config 53 but made no submission; its grant expired unconsumed and Finance remained empty. |
| 2026-07-29 | SuperAgent manual XML runs | Historical; production implication superseded by ADR-002 | [Evidence topic](topics/2026-07-29-superagent-manual-xml-run-bypassed-mcp.md) | The exports remain factual processor/chat evidence, but the user confirmed direct chat upload is outside the dedicated Agent contract; production acceptance starts at the controlled ARR entrypoint. |
| 2026-07-29 | Local XML upload runtime re-enabled | Active until local restart/config change | [Evidence topic](topics/2026-07-29-local-xml-upload-runtime-reenabled.md) | The old launchd process omitted `--enable-processing`; its controlled launcher was corrected and restarted, health now reports database/processing ready, and the XML chooser is enabled without submitting a file. |
| 2026-07-29 | Controlled public deployment repository | Active until server E2E | [Evidence topic](topics/2026-07-29-public-deployment-repository.md) | Dockerfile default CMD and Compose explicitly enable XML processing while source CLI/runtime readiness remain fail-closed; 260 tests pass with 2 skips, but the refreshed image and public runtime still require server verification. |
| 2026-07-29 | Channel BI database runtime verification | Superseded after fact changes | [Evidence topic](topics/2026-07-29-channel-bi-database-runtime-verification.md) | The database connection finding remains true, but its one-row fixture projection is historical; use the 2026-07-30 post-update contamination evidence for current values. |
| 2026-07-29 | Controlled public deployment repository | Historical baseline; current server E2E still pending | [Evidence topic](topics/2026-07-29-public-deployment-repository.md) | Dockerfile/Compose processing and fail-closed readiness evidence remains valid. Its Caddy Basic Auth/MCP topology is superseded: current deployment uses Caddy HTTPS plus ARR application login and no active MCP service. |
| 2026-07-29 | Live synthetic XML upload vertical slice | Active blocker | [Evidence topic](topics/2026-07-29-live-synthetic-xml-vertical-slice.md) | Public MCP reachability and one-tool discovery are restored; stale SuperAgent config version 33 must now be refreshed before the next commit test. |
## When To Add Evidence

View File

@@ -0,0 +1,41 @@
# Evidence Topic: Channel BI database runtime verification
## Metadata
- Date: 2026-07-29
- Status: Superseded
- Scope: Local Web channel-BI read path from browser through PostgreSQL
- Confidence: Fact
- Source: Live local HTTP responses, browser DOM/console inspection, process/listener inspection and targeted automated tests
- Last verified: 2026-07-29
- Stale trigger: Restart or reconfiguration of the 8765 Web process; database migration or fact changes; changes to `arr_web`, `channel_analytics` or the BI frontend
Superseded for current values by
[`2026-07-30-channel-bi-post-update-data-contamination.md`](2026-07-30-channel-bi-post-update-data-contamination.md)
after real uploads changed the Finance projection. The connection-path finding remains valid.
## Question
Is the current Web “渠道BI” page actually connected to PostgreSQL, rather than only having an unverified code path or mock response?
## Evidence
- Static path: `arr_web/static/app.js` requests `/api/months` and `/api/analytics`; `arr_web/app.py` delegates the analytics route to `PostgresPortalRepository`; `channel_analytics/postgres.py` reads current Finance facts in `REPEATABLE READ READ ONLY` transactions and validates the target database as `booking_test`.
- Live health: `GET http://127.0.0.1:8765/api/health` returned HTTP 200 with `database_ready=true`.
- Live database-backed requests: `/api/months` returned the `2026-07` projection with one channel and one row; `/api/analytics?month=2026-07` returned BI schema 1.2 with QBD, one sold room, three room nights and total price 5400.
- Browser verification: the live “渠道BI” tab rendered the same month, KPIs, QBD ranking and channel-by-room-type matrix; browser console error count was zero.
- Runtime identity: the existing Web process uses the controlled `booking-test-db.env` and is bound to `0.0.0.0:8765`; upload processing is disabled, matching `processing_ready=false`.
- Regression: 24 tests covering channel analytics PostgreSQL behavior, analytics contracts, Web routes and repository schema passed.
## Finding
The current local Web channel-BI path is fully connected and functioning from the rendered page through the live Web API to PostgreSQL. No connection code change is required for this task. `processing_ready=false` is a separate XML-upload runtime state and does not invalidate the BI read path.
## Impact
Future channel-BI work should not begin by replacing or re-wiring the current repository. Reverify the live endpoints after process, configuration, migration or fact changes. Treat the existing all-interface listener as a local test process, not a production security boundary: bind it to loopback for local-only use or place it behind the documented Caddy HTTPS plus ARR application-login boundary.
## Open Items
- Reverify `database_ready=true`, `/api/months` and one known `/api/analytics` month after the formal server deployment.
- Do not expose port 8765 directly; use loopback for local testing or the Caddy boundary for deployment.

View File

@@ -7,7 +7,7 @@
- Scope: Current local Web process on port 8765
- Confidence: Fact
- Source: launchd/process inspection, health response and in-app browser DOM inspection
- Last verified: 2026-07-29
- Last verified: 2026-07-30
- Stale trigger: Restart or reconfiguration of `com.chillishark.opera-arr-report`, route/Keychain changes, or replacement of the 8765 Web process
## Question
@@ -27,6 +27,12 @@ Why was ARR.XML upload disabled in the currently running page, and is it actuall
The upload was disabled because the long-running local launcher omitted the explicit processing feature flag. The corrected process has passed both the runtime readiness gate and the page-level control check.
## 2026-07-30 PROGRAM_INPUT v3 reload
Job `arrjob-1fd0f5e182eb4056af53be7300174981` exposed that the then-running PID 2891 had started at 10:13, before the v3 source files were modified at 11:14. Its 11:34 trace therefore repeated `arr-opera-daily-program-input-2` and ended in `status=running` before any model tool call. The four visible `fetch_oss_file` strings came from the same attachment policy copied through middleware, not four tool calls.
PID 2891 was terminated only after its command and 8765 listener identity were revalidated. The same controlled launcher then started PID 10376 at 11:40. `GET /api/health` returned `database_ready=true`, `processing_ready=true`, `monthly_ready=true`, `download_ready=true`, `company_reports_ready=true` and `agent_writeback_ready=true`. A new job, rather than the old v2 job, is required to prove the loaded v3 contract at the remote Agent boundary.
## Impact
The user can now select ARR.XML on the current local page. A real end-to-end result still requires SuperAgent to call the published MCP tool and an MCP `committed`/`already_committed` receipt; merely selecting or dispatching the file is not a database success signal.

View File

@@ -29,6 +29,12 @@ Does the publishable source snapshot open XML processing through both supported
The repository snapshot now makes direct Docker-image launches and Compose launches consistent: both request XML processing, while the source CLI remains default-closed and the page opens upload only after the database, guarded OSS, SuperAgent and HMAC runtime initialize. The documented Compose topology protects Web traffic with Caddy HTTPS plus Basic Auth, and MCP retains application bearer authentication.
## Authentication Supersession Note — 2026-07-30
The paragraph above remains historical evidence for the 2026-07-29 published snapshot. Current ARR2 deployment no
longer uses Caddy Basic Auth or an active MCP surface: Caddy terminates HTTPS, ARR Web owns login/session/CSRF/logout,
and Compose injects `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD`. Use the current root and `deploy/` runbooks.
## Impact
The server must rebuild/redeploy the image; a platform-level CMD override must retain `--enable-processing`. `processing_ready=true` remains the deployment prerequisite, but business completion still requires SuperAgent tool rediscovery and an MCP `committed`/`already_committed` receipt with matching database facts.

View File

@@ -0,0 +1,40 @@
# Repeated XML Artifact Identity Fix
## Claim
The Web upload failure `PROCESSING_SOURCE_INVALID: processing source object is unavailable` was caused by database content deduplication reusing an older run's artifact row. Migration 011 fixes that failure. A post-fix real upload now reaches SuperAgent, but the wider E2E remains blocked because the successful remote run did not call the direct-result MCP tool.
## Runtime Evidence
- Four repeated submissions created processing runs 17 through 20 between 20:29 and 20:32.
- Each run had a queued attempt and an issued, unconsumed direct-MCP grant, but no remote run identifier and no result submission.
- All four new runs pointed to the same `opera_xml` artifact row and therefore to the committed object key belonging to run 16.
- `OssProcessingMessageBuilder` correctly requires the current `job_id` to be a path segment in the source object key, so it rejected those cross-run references before Open Agent dispatch.
- The browser rendered the safe error text but incorrectly described the task as not created even though the database registration and grant already existed. That display issue remains separate from the identity fix.
## Root Cause
Migration 008 imposed `UNIQUE (artifact_kind, sha256)`, and `PostgresIngestionRepository._ensure_artifact()` looked up artifacts only by those two content fields. Reusing the same XML bytes in a new run therefore reused the old row and old job-scoped OSS key. This contradicted the intended rule that equal content may be processed in independent run/attempt identities.
## Change
- Migration 011 replaces the content uniqueness constraint with a non-unique lookup index. The existing storage-identity unique index remains authoritative.
- `_ensure_artifact()` now finds an artifact by provider, bucket alias, object key and null object version, then verifies all immutable metadata before reuse.
- Three repository regressions cover new-object insertion for equal content, exact storage-identity reuse and metadata conflict rejection.
- Two migration regressions cover the forward shape and the guarded rollback refusal after repeated content exists.
## Verification
- 57 targeted ingestion, direct-ingestion, processing and Web tests passed.
- The complete suite passed 269 tests with 2 expected environment skips.
- An up/down transaction rehearsal passed against `booking_test`.
- Migration 011 was formally applied; artifact row count remained 12 and the expected non-unique lookup index replaced the content uniqueness constraint.
- The controlled 8765 service was restarted and reported all database, processing and writeback health gates as ready.
- The user-authorized `0721.XML` was submitted through the real ARR session/CSRF upload endpoint as job `arrjob-51284ae50150454f9c085b461be7f0d8`. Its new source key contains that job identity, and two independent `opera_xml` artifact rows now exist for the equal content.
- Remote run `3e17e1d3-3b06-4e9b-b008-e231770618b2` started successfully and the Open Agent API reports `success` with no platform error. It resolved the intended Profile and current enterprise MCP configuration version 53.
- After the remote terminal state and an additional propagation window, the grant remained unconsumed, no result submission or Finance version existed, and the grant later expired. Therefore this run proves upload/dispatch but not MCP/Finance completion.
- The authenticated Ops page could be opened, but structured log reads timed out twice. Exact Agent-step diagnosis requires the exported full run log; no further upload should be attempted until that log is reviewed.
## Staleness Trigger
Reverify after changes to artifact identity schema, `PostgresIngestionRepository._ensure_artifact()`, object-key policy, or the active 8765 runtime.

View File

@@ -0,0 +1,51 @@
# Evidence Topic: SuperAgent Manual XML Run Bypassed MCP
## Metadata
- Date: 2026-07-29
- Status: Historical evidence; production requirement superseded by ADR-002
- Scope: Manual SuperAgent XML test versus the accepted Web → OSS → Agent → MCP → PostgreSQL flow
- Confidence: Fact for the exported run; strong inference for the published-Prompt mismatch
- Source: User-exported `XML File Analysis (2).md`
- Last verified: 2026-07-29 19:53 +08:00
- Stale trigger: None; retain as historical evidence and interpret through ADR-002
## Question
Did the exported SuperAgent run exercise the accepted ARR database-ingestion path?
## Requirement Correction
The user confirmed that this Agent is dedicated to a fixed business-system entrypoint whose PROGRAM_INPUT is
already constrained in code. Direct SuperAgent chat uploads are outside the production contract. Therefore these
logs remain factual execution evidence but do not justify input-validation gates in Main Prompt. ADR-002 is the
current authority.
## Evidence
- The 249-line export covers a manual XML conversation from 19:13:23 to 19:14:37.
- The deterministic daily processor succeeded for business date `2026-07-21`; it reported 135 source records and 119 retained output rows.
- Tool-name counts in the complete export were: `read_file=10`, `present_files=1`, `fetch_oss_file=0`, and `arr_submit_processing_result=0`.
- The export contains no `PROGRAM_INPUT`, `submission_grant`, `job_id`/attempt submission identity, MCP receipt, or `enterprise_mcp_transport_unavailable` event.
- The Agent accepted a chat-uploaded XML, processed a local upload path, and presented generated files. The accepted Main Prompt explicitly rejects chat uploads and forbids `present_files` on the direct-ingestion path.
- The Agent's own reasoning says a “main agent prompt file” at a local user path was inaccessible. This is consistent with a Profile containing a file-path reference or an old prompt rather than the full contents of `prompts/arr_opera_daily_main_agent_prompt.md`.
- A second 95-line export from 19:46 shows the compact Prompt recognized that direct `0722.XML` upload lacked PROGRAM_INPUT. It did not call the Skill, `fetch_oss_file`, `arr_submit_processing_result`, or `present_files`.
- That run still called `read_file` once for 20.44 seconds and `ask_clarification` once before stopping, and it invented the field name `fetch_strategy` instead of `attachment_fetch_policy`.
- A third 376-line export from 20:22 processed the chat path `/mnt/user-data/uploads/0721.XML` directly with shell tools and then called `present_files`. It contained zero `fetch_oss_file` calls, zero `arr_submit_processing_result` calls, no submission grant and no committed receipt. The Skill succeeded, but no OSS/MCP/database path ran.
- The third run's reasoning still describes the superseded “validate PROGRAM_INPUT” Prompt and its `present_files` behavior violates the final controlled Prompt. That session therefore does not prove the latest Profile text was saved/published and selected.
- The intermediate 3,095-character defensive Prompt was superseded after the user's boundary clarification. The final source is a 1,321-character controlled orchestrator with no duplicated input gates; a regression test caps it below 1,800 characters and preserves one fetch/Skill/MCP plus receipt boundaries. All 12 targeted tests pass. This source change is not evidence that SuperAgent has published it.
## Finding
The Skill and its deterministic processor worked, but this run did not fetch the source from ARR-controlled OSS, did not receive an attempt-bound submission grant, did not call ARR MCP, and did not write or prove any database facts. It is a processor-only manual test, not an end-to-end ARR test.
The runs do not prove production Profile correctness because they bypassed the supported entrypoint. A valid E2E
must begin with ARR-generated PROGRAM_INPUT and then show `fetch_oss_file`, the Skill, the MCP submission and a
committed database result.
## Impact
- Do not use manual SuperAgent chat uploads as production acceptance tests.
- Paste and publish the final controlled-orchestration Prompt with the MCP tool loaded.
- Test through ARR Web so the backend supplies the validated PROGRAM_INPUT and grant.
- Accept only `committed`/`already_committed` plus matching PostgreSQL facts.

View File

@@ -0,0 +1,109 @@
# Task Trace Terminal Verification
## Scope
Replace the decorative daily-processing status module with a diagnostic view
that exposes the persisted task flow from upload through downstream outbox
state. After the first structured UI pass, the user explicitly required the
plainest possible presentation: one black code console with raw log lines.
## Implemented Evidence Path
- `GET /api/jobs/{job_id}/trace` validates the job identifier and asks the
repository for a privacy-minimized diagnostic projection.
- New tasks are submitted once through
`POST /api/open/agent-sessions/{session_id}/messages/stream?include_trace=true`.
The first `run.started` binds the ARR attempt; the same generator is then
consumed on a background thread until the SuperAgent run terminal event and
SSE `end`.
- Raw SSE and assistant text never reach disk. `arr_processing.agent_trace`
persists only allowlisted run/task/step/end projections in a private,
append-only local JSONL store keyed by the job-id digest.
- `arr_web.job_trace` reconstructs ordered events from the processing run,
attempts, artifact deliveries or direct MCP submissions, Finance daily
versions and downstream outbox events, then merges the sanitized SuperAgent
events by timestamp.
- Equal PostgreSQL transaction timestamps use an explicit business-sequence
tie-breaker so validation/Finance commit precede attempt/job success and
downstream queue events.
- The desktop page renders the response in a single black monospace `<pre>`.
Each event is one line containing timestamp, level, stage, code, title,
message and allowlisted details JSON. The controls remain limited to
historical task selection, four-second active-task polling, manual refresh
and one-click copying of the complete current `<pre>` plaintext.
## Privacy Boundary
The SQL and projection expose only diagnostic columns. `message.delta` and
`message.final` are always dropped. Trace summaries redact credential
assignments, Bearer values, URLs, absolute paths, emails, long numbers and XML
fragments before persistence, and the read path validates the field/details
allowlist again. The response and UI do not contain grants, credentials,
private object paths, source bytes, raw payload/envelope/receipt JSON or guest
PII. Outbox rows are described only as queued/published notifications, not as
proof of monthly-report completion.
The SuperAgent endpoint accepts only POST and does not replay historical runs.
It was also verified to create a different `run_id` when the exact same message
and idempotency key are sent twice. ARR therefore never POSTs a second stream to
backfill logs. An uncertain failure before `run.started` is terminalized as
`PROCESSING_REMOTE_SUBMISSION_AMBIGUOUS` rather than automatically retried.
## Runtime Checks
An isolated read-only Web instance on `127.0.0.1:8766` used the configured
`booking_test` database while the standing 8765 process was left untouched.
- A current queued/running task rendered upload, attempt and current-state
events and continued four-second polling.
- Successful fixture `mvp-v1-fixture-20260727` rendered 11 events in the
required upload → dispatch → result → validation → Finance → job → outbox
order.
- Failed task `arrjob-fdc2c1a0695c41b3b372a52033b2f567` rendered six events
and the terminal line
`FAILURE stage=writeback code=PROCESSING_RESULT_MISSING`.
- Computed UI evidence was `tag=PRE`, black `rgb(8, 11, 16)`, monospace font,
and zero old filter/timeline/evidence elements.
- At a 375×812 viewport, document width stayed 375 pixels; long raw lines
scroll only inside the console. Browser console error/warning count was zero.
- Live contract probes confirmed `405 Allow: POST` for GET/HEAD/OPTIONS, the
outer `trace` SSE event, inner `run.started/task.updated/step.updated/`
`message.delta/message.final/run.completed`, and terminal `end`.
- A live isolated background-handoff probe returned the run handle and then
persisted 45 safe events (28 task updates and 14 step updates) through
completion/end with no capture failure.
- The controlled local LaunchAgent was restarted after implementation. Port
8765 reports database/processing/writeback/monthly readiness, serves
`arr-job-trace-2`, and correctly returns `agent_trace_events=0` for a job that
predates trace capture.
- On 2026-07-30, the live 8765 page showed the copy control disabled before a
trace loaded and enabled for a 228-line terminal containing the final `END`
line. A real click completed the page Clipboard API promise and displayed the
existing success toast; browser console warning/error count remained zero.
The in-app Browser isolates page, automation and macOS clipboard channels, so
no external pasteback-equality claim is made. Source and regression checks
verify that the exact `#process-log.textContent` is passed to the Clipboard
API, with selected-textarea fallback and explicit failure feedback.
## Automated Verification
- `node --check arr_web/static/app.js`: pass.
- 38 post-hardening targeted tests: pass.
- Full repository suite: 275 tests passed with 2 expected artifact-tool skips.
## Result
The status module now reports the real SuperAgent execution path plus the
authoritative persisted ARR processing chain in the user-requested plain
terminal form. It locates failures by stage and stable error code without
treating Agent trace as proof of Finance commit or widening the privacy surface.
Jobs submitted before this capture path was enabled retain only their persisted
ARR facts because historical Agent trace cannot be replayed.
The 2026-07-30 v3 run also established an important limitation: trace capture can
finish normally while the authoritative ARR job remains `running`. Four-second
refresh continued and the trace ended at `AGENT_TRACE_STREAM_ENDED`, but no MCP
callback arrived and the production lifecycle had no terminal reconciler. The UI
was faithfully rendering stale backend state; trace completion alone must not be
promoted to business success, but it must trigger or feed a separate terminal
no-submission reconciliation path.

View File

@@ -0,0 +1,32 @@
# ARR2.0 Programmatic Pipeline Evidence
## Scope
Independent workspace `/Users/chillishark/Desktop/ARR2.0`; source workspace remained read-only.
## Implementation Evidence
- Active Web composition imports PostgreSQL, OSS, local processor and generic ingestion only.
- Compose contains one application service and no MCP port/domain/service or Agent secrets.
- Root requirements do not install Agent integration or MCP packages.
- Old callback route returns 404 and health has no Agent readiness field.
- All ARR-managed OSS objects use private object ACLs.
## Behavioral Evidence
- Real success fixture: 5 source records, 3 retained records, four committed artifacts, active Finance version.
- Real pricing failure fixture: result + structured + exception artifacts, recorded terminal failure, no active version.
- PostgreSQL fake-cursor tests verify running and infrastructure-failure transitions plus failure outbox SQL.
- Daily download test materializes the committed object and rejects a descriptor hash mismatch.
## Verification Snapshot
- Focused Web/download/programmatic/trace/OSS/deployment suite: 25 passed.
- Focused ingestion PostgreSQL/programmatic suite: 13 passed.
- Final full discovery: 282 passed-or-skipped in 125.8 seconds, with 0 failures/errors and 9 explained skips (3 private
booking fixtures intentionally not bundled; 6 optional XLSX renderer/tool checks).
## Remaining External Verification
Docker/Caddy and a real test database/OSS deployment are not available in this workspace. One controlled no-PII server
vertical slice is still required before production rollout.

View File

@@ -0,0 +1,116 @@
# 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.

View File

@@ -0,0 +1,98 @@
# Evidence Topic: Company-report path and July month-end readiness
## Metadata
- Date: 2026-07-30
- Status: Processor and Booking runtime ready; released 5/5 live job pending
- Scope: ARR2 local Web on port 8766, accepted `company_reports` processor, controlled `booking_test` snapshot
- Confidence: Fact
- Source: code/input inspection, live health/job metadata, repeatable-read aggregate queries, pure in-memory report build, real XLSX tests
- Last verified: 2026-07-31 11:34 +08:00
- Stale trigger: port-8766 restart, first Booking draft activation, Finance current-version repair, Booking source/parse changes, or a new July upload
## Question
What path and rules does the current “公司渠道明细” program use, and will the July month-end run publish all five
workbooks from the current database projection?
## Evidence
- The active Web process includes `--enable-company-reports`; loopback health reported `company_reports_ready=true`
at 2026-07-30 15:35 Asia/Bangkok.
- The Web coordinator accepts one month and one cumulative cutoff period, enforces the Bangkok release boundary, and
invokes the deterministic processor for the fixed five-company set. It does not read XML or call an Agent.
- The source query reads `finance.v_active_daily_facts` by `DEPARTURE` from month start through the cutoff in a
repeatable-read, read-only transaction. Booking enrichment comes only from current accepted room items, joined by
normalized complete Group Code.
- Two persisted Web jobs for 2026-07 cutoff 10 and cutoff 20 both completed 5/5 successfully with zero rows for every
company. The current supported-company facts all have C/O in the 21-to-month-end period, so those are valid empty
workbooks rather than dropped rows.
- A read-only July 31 aggregate found 417 supported-company facts: 121 `matched`, 11 `unmatched`, and 285
`missing_group_code`. Missing Group Code counts are LianTai 120, QBD 128, DY-AI-Easy-KB 1, FengRun 33 and HanaTour 3.
- Under the prior strict rule, a pure in-memory preview blocked all five companies because every missing Group Code was
an error. The user then explicitly chose the deterministic fallback: keep each such Finance fact as its own output
row with blank `RES_COMMENT` and blank `Booking Room`, without warning or duplicate highlighting. At that intermediate
step, present-but-unmatched Group Codes still remained strict source-completeness errors.
- The supplied Markdown `/Users/chillishark/ARR项目0727/outputs/res_comment_20260727/RES_COMMENT_TYPE_OF_ROOM.md` matches
the dedicated fixed Booking Markdown importer: SHA-256
`ec2302170c5657d3e465691997e17f7694eb5a7f76feba7e43261632209b8c23`, 867 rows, six source worksheets and 348 distinct
Group Codes. The controlled database already contains an accepted/current batch with the same hash and counts, so
re-importing this exact file is unnecessary and would be idempotent.
- After the fallback change, the same read-only July 31 preview produced:
- LianTai: invalid; 136 candidate rows, including 120 blank Booking Rooms; two present Group Codes
(`LLT260719MA`, `LT260718KC`) still lack accepted Booking matches; two multi-price review warnings remain.
- QBD: valid; 139 rows, including 128 blank Booking Rooms; four multi-price review warnings.
- DY-AI-Easy-KB: valid; one row with blank Booking Room.
- FengRun: valid; 33 rows with blank Booking Room.
- HanaTour: valid; three rows with blank Booking Room.
- At that intermediate step, a source/business error still blocked only the affected company while warning-only reports
could publish, making four companies publishable and leaving only LianTai blocked.
- The user then explicitly broadened the rule: both an absent Group Code and a present Group Code with no resolvable
Booking room leave `Booking Room` blank. Processor 1.2.0 keeps a present code's normal
`(Group Code, ARRIVAL, DEPARTURE)` aggregation and Finance pricing; lookup absence alone creates no error or warning.
- The post-change repeatable-read July 31 preview is valid 5/5 with zero errors:
- LianTai: 138 rows, 122 blank Booking Rooms and two existing non-blocking multi-price warnings.
- QBD: 139 rows, 128 blank Booking Rooms and four existing non-blocking multi-price warnings.
- DY-AI-Easy-KB: one row/one blank; FengRun: 33/33 blank; HanaTour: three/three blank.
- The current Finance projection also contains one `local_fixture`/`synthetic.xml` QBD fact with C/O 2026-07-30. The
company-report source does not filter by storage provider, so the month-end snapshot includes that fact.
- The final scoped company/Booking/report suite ran 32 tests successfully with three expected private-fixture skips;
real artifact-tool coverage included five-company XLSX generation, workbook reopen/value validation, rendering and a
dedicated end-to-end blank-Booking-Room case. Four Web company-task coordinator tests also passed. No database import,
report publication or Finance mutation was performed.
- The historical port-8766 Web process started at 17:13:54 +08, after the company-report core file's 17:10:03 +08
change; it loaded the earlier no-code-only fallback. Processor 1.2.0 was written at 17:34:13 +08 and was not loaded
in that superseded process.
- Processor 1.2.0 passed 37 company/Booking/real-XLSX/Web-coordinator tests with three expected private-fixture skips.
The real builder exported, reopened and value-checked both absent-code and present-but-unmatched blank cells.
- A non-publishing real-data vertical slice loaded the controlled July 31 snapshot in a repeatable-read/read-only
transaction and built all five workbooks in a temporary directory. All five exported, reopened, value-checked and
rendered three previews; row counts were 138, 139, 1, 33 and 3 respectively. The temporary artifacts were removed
automatically and no report/database state changed.
- The authenticated replacement runtime now loads current source and reports `company_reports_ready=true`; activation
preserved three existing job records and deliberately did not submit a new report job.
- A later 2026-07-31 read-only audit first found 014 absent, then observed a concurrent 014/015 deployment at 11:19.
Batch 1 is now the explicit current source and draft tables are empty. Port 8766 restarted at 11:30 with Booking
source-upload readiness and PATCH/DELETE transport active. Historical fixture reads and processor readiness remain
valid. See the later Booking extraction-program evidence.
## Finding
The supplied Markdown is a valid Booking source and its historical accepted rows remain queryable. Processor 1.2.0
makes both absent and unresolved Booking enrichment non-blocking and the current July projection previews valid 5/5.
`LLT260719MA` and `LT260718KC` remain visible business codes with blank Booking Room rather than report errors. The
processor runtime and Booking upload/source gate are active. A controlled real draft activation and one released
populated company-report job are still required before validating the new end-to-end flow. No additional Booking content
is required for the historical lookup baseline.
## Impact
- Do not interpret the two zero-row successful jobs as proof that the month-end data set is publishable.
- Current Web jobs use the activated processor 1.2.0 runtime, but readiness alone is not proof of a published 5/5 run.
- Retire the known fixture from the current projection without deleting immutable history before treating a July
month-end company report as clean business output.
## Open Items
- Accept one controlled Booking review activation, then run one month-end Web job after the 2026-08-01 00:00 Bangkok
release boundary; verify all five downloads and their registered hashes.

View File

@@ -0,0 +1,40 @@
# Daily Page Visual Polish
## Metadata
- Date: 2026-07-30
- Status: Active
- Scope: Desktop daily-page title hierarchy, task-log utility and XML upload composition
- Confidence: Fact
- Source: `arr_web/static/`, focused tests and live local browser verification
- Last verified: 2026-07-30
- Stale trigger: Masthead, daily heading, upload-panel or header-utility styling changes
## User-Facing Outcome
- `ARR Report` is the workspace identity and is visually larger than the daily page title at every verified width.
- `任务日志` remains the same semantic dialog button but now has a compact outlined treatment and the same 13px label
size as the adjacent database status.
- The upload station is centered at desktop, fills the available shell at smaller widths and retains the real
file-input/drop behavior.
- Decorative `01 / UPLOAD` and `本月留痕` labels were removed.
- `开始处理` moved into a compact action footer without changing its disabled, loading or submission behavior.
- No API, upload field, route, task-log scope or business rule changed.
## Verification
- `node --check arr_web/static/app.js`: passed.
- Authenticated Web/auth/daily-visual/task-log tests: 33 passed.
- 1440x900: workspace/daily titles compute to 24px/21px; the upload card is centered at 840px; task-log/status labels
are both 13px; the compact action is 118x40.
- 1024px: the upload card is centered at x=92 with width 840px.
- 768px: the upload card fills the 720px shell.
- 375x812: titles compute to 20px/18px; the upload card fills 347px; the action is 112x42.
- All four widths have zero horizontal overflow. Browser warning/error count is zero.
- Opening the restyled task-log button focuses the native dialog; closing it returns focus to the trigger.
## Design Boundary
This is a preserve-mode operational-dashboard refinement. It keeps the existing dependency-free HTML/CSS, light
neutral theme, single blue accent and rounded component language. No marketing-page imagery, glass effects, new font
dependency or frontend framework was introduced.

View File

@@ -0,0 +1,53 @@
# Daily Upload Filename Provenance
## Status
Implemented, migrated and loaded in the authenticated port-8766 runtime; one no-PII live write-path check remains.
## Problem
The browser submitted the selected XML basename, but `ProgrammaticUploadCoordinator` normalized the source object to
`source.xml` and did not persist the browser value. Daily history and task trace then read
`ingestion.artifacts.original_filename`, exposing the internal canonical artifact name as if it were the uploaded name.
The existing 24 July history rows therefore cannot recover their original client filenames from current database
facts. Guessing from business dates or object names would create false provenance.
## Implemented Boundary
- Added and applied migration `013_daily_upload_filename.sql`.
- Added nullable `ingestion.processing_runs.uploaded_filename` with basename, control-character, length and XML-suffix
checks.
- The active and legacy upload coordinators pass the already validated browser filename through `JobRegistration`.
- `PostgresIngestionRepository` stores and identity-checks that value when registering a run.
- Daily history and task trace now select `run.uploaded_filename`.
- The frontend renders `—` when historical provenance is absent.
- Source objects, processor input and independent validation continue to use canonical `source.xml`.
- The down migration refuses to discard the column after any upload provenance has been recorded.
## Database Evidence
- Migration SHA-256:
`f7ea18d6b844d9bd90fa757a4cf8428d1dd5833c088ae23444ac81fbe204fb0a`.
- Down-migration SHA-256:
`663d287e36bb99533d28918d8ead7a2b03e8bbf7e7d682a527400d11f0ee3fbf`.
- Post-migration state: 25 processing runs, zero non-null uploaded filenames, validated
`processing_runs_uploaded_filename_check`.
- July repository state: 24 history rows, 24 unknown uploaded filenames, zero internal `source.xml` values exposed by
the new query.
- A transaction-only probe used synthetic basename `歌剧院-0723.XML`; both history and trace returned that value while
the source artifact remained `source.xml`. The transaction was rolled back and left no probe data.
## Automated Evidence
- 69 focused migration, programmatic-ingestion, repository, trace, direct-ingestion and Web tests passed.
- JavaScript syntax and changed Python module compilation passed.
- Full discovery passed 312 tests in 99.280 seconds with 10 environment/fixture skips and no failures or errors.
## Runtime Activation Boundary
The former 17:13 process predated both this change and application login. It later exited, and the replacement runtime
now starts with Keychain-backed Web/OSS credentials, reports all five readiness flags true and serves authenticated
`/api/jobs` with the existing total of 24. Activation intentionally submitted no XML, so one controlled no-PII upload
must still verify that a new browser basename appears in live history/trace while the internal artifact remains
`source.xml`.

View File

@@ -0,0 +1,69 @@
# Evidence Topic: First live ARR2 user-run trace
## Metadata
- Date: 2026-07-30
- Status: Resolved by the monthly-publication implementation completed later on 2026-07-30
- Scope: ARR2 loopback runtime on port 8766, controlled `booking_test`, OSS-backed daily processing, local monthly output
- Confidence: Fact
- Source: persisted Web API trace, read-only PostgreSQL queries, local artifact metadata, source inspection
- Last verified: 2026-07-30
- Stale trigger: Historical evidence; see `2026-07-30-monthly-publication-live-acceptance.md` for current behavior
## Question
Did the user's successful daily run commit to PostgreSQL, and why did the subsequent successful monthly action show no
monthly result or download?
## Evidence
### Daily commit
- Job `arrjob-e6042c9cfe7d4f07aa986c5cffc4548e` is processing run 27 with terminal raw status `accepted`.
- Its one artifact delivery is committed and points to Finance daily version 4.
- Daily version 4 is `active`, version number 1, for business date 2026-07-21.
- Counts reconcile: 135 source rows = 119 retained + 16 excluded-rate-code; duplicate, validation-failed and
price-unmatched counts are zero.
- `finance.current_daily_versions` points 2026-07-21 to daily version 4.
- The trace contains `ARTIFACT_RESULT_COMMITTED`, `FINANCE_VERSION_ACTIVATED` and `JOB_SUCCEEDED` terminal evidence.
### Downstream event
- The same database transaction created `arr.daily_version_committed` for processing run 27.
- At verification time it was `pending`, with zero publish attempts and no published timestamp.
- This is consistent with the documented absence of an automatic monthly outbox consumer.
### Monthly generation and visibility
- The manual monthly request generated `outputs/monthly_reports/2026/07/latest.xlsx` and `latest.result.json` at
2026-07-30 14:28:32 +08:00.
- Result status is `success`; the workbook SHA-256 is
`57160ff45d962ef348d7e9d21eafdc1f89c8b7b00d9d31eba283c2ff6bc40410`.
- The workbook reopens and contains LIANTAI-GROUP 26 rows, LIANTAI-FIT 25, QBD 55, DY-AI-Easy-KB 0 and FENGRUN 14:
120 data rows total. No guest values were recorded in this evidence.
- The workbook contains zero formulas, matching the known formula implementation gap.
- Read-only `information_schema` verification proved `finance.report_versions` is absent from `booking_test`.
- `monthly_reports.repository.PostgresReportRepository.reserve_report()` creates only a deterministic in-memory
generation identity; `activate_report()` revalidates source pins but inserts no report metadata.
- `arr_web.repository.MONTHLY_RUNS_SQL` synthesizes a `source_ready` row with null `report_id`, filename and artifact
hash. The frontend only renders a download link when an artifact hash/report ID is present.
## Finding
The daily success is a real, atomic PostgreSQL commit. The monthly action also produced a real local XLSX, but its
success is not persisted as a monthly report version and cannot be rediscovered or downloaded through the Web API.
The frontend toast therefore overstates the end-to-end outcome: it confirms local file generation, not published report
visibility.
## Impact
- Daily ingestion does not need repair for this run.
- A monthly publication task must define and implement a database-compatible metadata/read model (or an equally durable
controlled artifact index), then align list/download APIs and the success receipt with that persisted state.
- Automatic dispatch remains separate: the accepted outbox event currently has no consumer.
- The required `TOTAL PRICE` formula remains a separate workbook-generation defect.
## Open Items
All three items were resolved later on 2026-07-30 by migration 012, the dedicated worker, persisted list/download
identity and formula validation. This topic remains the before-state diagnosis.

View File

@@ -0,0 +1,89 @@
# Evidence Topic: Monthly publication live acceptance
## Metadata
- Date: 2026-07-30
- Status: Active
- Scope: migration 012, dedicated worker, controlled `booking_test`, local monthly artifacts and ARR2 loopback Web
- Confidence: Fact
- Source: migration rollback probes, automated tests, read-only PostgreSQL queries, workbook reopen inspection and live Web API/download checks
- Last verified: 2026-07-30
- Stale trigger: migration/report schema, formula layout, output storage, worker acknowledgement or Web download behavior changes
## Question
Does a successful daily commit now produce a durable, visible and downloadable monthly report with the correct
ARRIVAL watermark and `TOTAL PRICE` formulas?
## Evidence
### Migration and publication state
- `database/012_monthly_report_publication.sql` was executed inside an uncommitted up/down probe, then in a synthetic
active-publication probe, and both left no objects/data after rollback.
- A privacy-minimized pre-migration checkpoint was created at
`runtime/backups/booking_test_pre_012_20260730T151922+0800/manifest.json`; its SHA-256 is
`230206eb074fd5877132744eb592b3dc6cd6638d12a3dd4d71a8101a9bd2998e`.
- Migration 012 was then applied to `booking_test`. It adds three metadata-only `reporting` tables and permits controlled
`local` monthly artifacts; 008011 were not edited or replayed.
- Current read-only state is 2 Finance daily versions, 120 active facts, one active monthly run, one local
`monthly_xlsx`, one local `result_json`, and two published daily-commit events.
### ARRIVAL derivation and idempotency
- Event 3 referenced daily version 2 with retained `ARRIVAL=2026-07-27`; event 9 referenced daily version 4 with retained
`ARRIVAL=2026-07-21`.
- The worker looked up those facts by `daily_version_id`, ignored payload dates/filenames, and selected the July scope.
- Both events resolved to report ID 1/version 1 with `as_of_date=2026-07-27`, exactly the maximum `ARRIVAL` in the
current 120-row monthly snapshot. The second event reused the published snapshot instead of creating a duplicate.
- Both events became `published` after one attempt and only after the report and its two artifacts were registered.
### Workbook and Web
- The active report contains 120 rows across five worksheets: LIANTAI-GROUP 26, LIANTAI-FIT 25, QBD 55,
DY-AI-Easy-KB 0 and FENGRUN 14.
- Reopening the real workbook found exactly 120 formulas, all in the `TOTAL PRICE` data cells and all equal to the
expected row-relative `=R[row]*C[row]*G[row]`; there were zero missing, mismatched or unauthorized formulas.
- The workbook is 20,741 bytes with SHA-256
`a43cdac3f6d97f6b73f635d0f386cffd0ad8b6416e8f92a7b6afc2f79f1e8635`.
- `/api/monthly-runs?month=2026-07` returns the active database report. `/api/download/monthly?report_id=1` returned
bytes matching the registered size and hash.
- The primary page no longer contains month, cutoff or manual-generate controls and explains the max-ARRIVAL policy.
- The final full discovery passed 291 tests in 108.2 seconds with 7 explained environment/fixture skips and no
failures or errors; this included real monthly XLSX generation/reopen coverage using the configured builder runtime.
### Later live versions and automatic page discovery
- Two later user uploads advanced the live publication history to V03 active and V02/V01 superseded. All four
daily-commit events are published.
- V03 contains 308 rows across six channels. Its persisted `as_of_date` and max current fact `ARRIVAL` are both
2026-07-27.
- An independent reopen of V03 found exactly 308 expected `TOTAL PRICE` formulas and zero bad formulas. The file is
40,635 bytes with SHA-256 `494fb78283f68cb7bffce2501b3531613c494d148a56a7ccc8d2aabe46b1e29c`, matching its registered artifact.
- The monthly page was changed to remove its refresh button and poll only while visible/active. Browser inspection
rendered V03/V02/V01 with download links and `自动更新 · 4 秒`; Web access logs showed repeated
`/api/monthly-runs` reads at the intended cadence without a click or reload.
- A subsequent 07-23 upload committed daily version 7 with 109 retained rows and advanced publication to V04 active
with 417 rows. All five daily-commit events are published; V03/V02/V01 remain superseded history.
### Data-quality caveat discovered after publication
- A later read-only Channel BI reconciliation proved that Finance version 2 in every July snapshot is the database
acceptance run `mvp-v1-fixture-20260727`, sourced from `synthetic.xml` with provider `local_fixture`.
- Publication, lineage, formula and download mechanics remain verified, but V01/V02/V03/V04 are not clean business-data
acceptance artifacts: the fixture adds one row and makes 2026-07-27 the maximum included ARRIVAL.
- See [Channel BI post-update data contamination](2026-07-30-channel-bi-post-update-data-contamination.md) for the
measured impact and repair boundary.
## Finding
The original publication-identity discrepancy is resolved in the controlled local runtime: monthly success now means a persisted report
identity with registered, hash-checked downloadable bytes. The “更新至” label is based on the latest included
`ARRIVAL`, formulas are present and verified, automatic dispatch is performed by a separate durable worker, and the
open monthly page discovers the published version without manual refresh. Current July business values remain subject
to the fixture-contamination caveat above until controlled repair and republication.
## Remaining Boundary
The workstation already runs Web and worker separately. Production Compose packaging must still provide the required
Node/artifact-tool runtime and shared output volume before enabling an equivalent managed worker service.

View File

@@ -0,0 +1,205 @@
# SuperAgent fetch_oss_file Prompt Experiment
## Scope
Use the first fresh controlled ARR trace together with historical successful
SuperAgent fetches to separate Agent orchestration behavior, address shape and
platform file-type policy.
## Evidence
- The controlled ARR job reached the intended SuperAgent Agent with its
generated `PROGRAM_INPUT` and invoked `fetch_oss_file` four times.
- The trace shows that the runtime Tool accepted `object_uri` and optional
`filename` arguments. The first OSS shorthand request returned
`public_endpoint_missing`; later HTTPS variants for the XML returned
`extension_not_allowed`. These are Provider results, not Tool Schema
validation failures.
- The Agent also read the Skill and used shell inspection before fetch success,
despite the prior concise Prompt's stop rule. The four calls were four Agent
tool decisions inside one remote run, not four ARR submissions or transport
retries.
- Historical exported conversations contain successful `fetch_oss_file` calls
for `.xlsx` attachments. They returned a local file under the runtime uploads
directory in about 220 seconds. Those inputs included a direct OSS HTTPS URL,
but the exports do not expose the raw Tool Call arguments and do not prove
that `.xml` is allowed in the ARR Agent's Provider configuration.
- No public upstream source or official documentation containing the two
Provider error codes was found; this appears to be a platform-specific Tool.
## Historical Prompt Change (superseded)
The first corrective Main Prompt made fetch the only permitted tool action before a
local source file exists and specifies exactly one call:
```text
object_uri = "oss://" + attachment.oss.bucket + "/" + attachment.oss.object_key
filename = attachment.name
```
It forbids parallel Skill/reference reads, filesystem/environment inspection,
URI substitution, argument repair, shell/curl/SDK fallback and every second
fetch. Any Provider failure, ambiguous result or missing unique local path must
produce one failed Profile object and end the run.
The Prompt is 1,757 characters, remains below the existing 1,800-character
regression limit, and all 12 `tests.test_arr_opera_daily_ingest` tests pass.
## Interpretation at that stage
This was a bounded diagnostic Prompt experiment. It removed retries and
side-path noise but still used the wrong `oss://` address form.
After this Prompt is published, one fresh controlled ARR job has decisive
outcomes:
1. One successful fetch returning one local XML path: continue to verify the
Skill and MCP stages.
2. One `public_endpoint_missing`: confirm whether the Tool expects a public
HTTPS address before assuming it needs a credential-backed Provider.
3. One `extension_not_allowed`: add `.xml`/`application/xml` to the Provider's
allowed source types; do not keep tuning the Prompt.
4. More than one fetch or any pre-fetch shell/Skill read: the published Prompt
was not selected or the model did not obey it; verify Profile version and
runtime trace before any Provider change.
## Published Prompt Follow-up Run
Controlled job `arrjob-737acd9d35d04d7f9bcbbc60e7674236` reached remote run
`53510877-e30a-4f44-9a6c-26980cdc9461` with the intended PROGRAM_INPUT. The
available trace snapshot shows:
- exactly one model-selected tool action: `fetch_oss_file` with `object_uri`
and `filename=source.xml`;
- no `read_file`, shell, Skill processor, `present_files`, or
`arr_submit_processing_result` action before or after that fetch in the
captured events;
- one Provider result with `success=false`,
`error=public_endpoint_missing`, and message
`Public OSS endpoint is not configured`;
- no HTTPS substitution and therefore no new `extension_not_allowed` result.
This validates the Prompt's single-call/no-bypass behavior for the captured
portion and makes the Agent-specific Provider endpoint the first actionable
blocker. The supplied snapshot was refreshed about 20 seconds after the Tool
result and still ended with `status=running`; it contains no `message.final` or
terminal run event, so it does not yet prove final failure-object emission or
ARR terminalization. A later trace snapshot is required for that separate
check.
## Public-read URL correction
The user subsequently confirmed two authoritative deployment facts:
- `fetch_oss_file` reads a publicly accessible OSS URL and does not need a Provider;
- the ARR OSS deployment is `public-read`.
The local implementation therefore replaces the historical `oss://` experiment:
- PROGRAM_INPUT is now `arr-opera-daily-program-input-3` with required `oss.url`;
- ARR generates `https://{bucket}.oss-{region}.aliyuncs.com/{encoded-object-key}` with no query signature;
- the 1,682-character Prompt copies `oss.url` to `object_uri` and `source.xml` to `filename` exactly once;
- only committed source XML has public-read object ACL; staged objects and processing outputs remain private;
- OSS readiness requires the approved public-read bucket.
Forty-four targeted tests and the complete 276-test suite pass with two expected skips. No fresh platform run
has exercised this contract yet. The next controlled trace should no longer return `public_endpoint_missing`.
If it returns the previously observed `extension_not_allowed`, `.xml`/`application/xml` must be enabled in the
platform Tool; more Prompt retries or URI substitutions would not solve that policy failure.
## Mixed-version local run
The next attempted local job, `arrjob-1fd0f5e182eb4056af53be7300174981`, did not exercise this correction.
Its complete available snapshot ended about seven seconds after `run.started`, still in middleware processing,
with no `object_uri`, Tool call, Tool result, Skill load, MCP submission or terminal event. The serialized input
was PROGRAM_INPUT v2 because the local 8765 process had started before the v3 files were modified. After the
validated listener was restarted, all local readiness flags returned true. Acceptance therefore moves to one
new job generated by the restarted process; the mixed-version job must not be used as v3 evidence.
## First true v3 controlled run
Job `arrjob-3c2cd75568554b0ab6d17817d446fed1` provided decisive evidence:
- middleware input contained `arr-opera-daily-program-input-3` and no v2 contract;
- the model selected exactly one `fetch_oss_file` call with a redacted URL and `filename=source.xml`;
- the Tool returned `success=false`, `error=extension_not_allowed` and no artifact;
- the Agent made no second fetch, shell/SDK bypass, Skill call or MCP submission;
- SuperAgent emitted `run.completed` with remote status `success`, meaning the orchestration run ended, not that business ingestion succeeded;
- direct submissions, deliveries, Finance versions and outbox events remained zero.
Because an extension check can precede network download, ARR separately issued an anonymous HEAD without
logging the object URL or reading its body. OSS returned HTTP 200, `Content-Type: application/xml` and
`Content-Length: 629434`, exactly matching the registered source. ARR URL generation, object ACL and public
reachability are therefore verified; the platform XML allowlist is the sole current fetch blocker.
The failure path also exposed two independent defects. The Agent returned only `job_id`, `source_file_id`, a
minimal `processor_result` and `files=[]`, which does not satisfy the Profile output object's required fields
or object-shaped `files`. ARR still reported `status=running` 145 seconds after `run.completed`, because no MCP
submission arrived and remote finalization is not yet projected into a terminal ARR failure. Neither defect
should be addressed by URI retries or renaming the XML.
## Frozen RUNNING diagnosis
A later read-only inspection distinguished a stale ARR state from an active Agent run:
- the page continued polling every four seconds and its `refreshed_at` changed, but two trace samples stayed at 73 events;
- the last persisted Agent event was `AGENT_TRACE_STREAM_ENDED` at 11:42:26 +08, three seconds after remote `run.completed`;
- a read-only remote run lookup reported `success`, while the authoritative ARR run and attempt remained `running` with no finish time;
- the trace JSONL was fully drained and no longer changing; direct submissions, artifact deliveries, Finance versions and outbox events were all zero;
- the Agent-side business result was a safe failure code, `extension_not_allowed`, not an in-progress computation.
The production upload path constructs `ProcessingRunner` but calls only `start()`. No production component invokes
`poll()` or otherwise reconciles a terminal remote run that never calls MCP. Even if polling were wired, the runner's
`delivery_missing` transition is not recognized by `PostgresProcessingState`. The available stale-expiry helper is
also not scheduled and applies to existing submission rows, whereas this run created none. Consequently this task
will remain `running` until separately authorized repair logic or a guarded one-off repair is applied. Diagnosis did
not change state, retry the stream, submit another XML or restart a service.
## Main-flow impact assessment
The stale `running` state is not currently a report-generation gate:
- every upload creates a new independent job id, and the upload control is gated by runtime readiness rather than another job's status;
- Finance facts are written only inside an accepted MCP submission transaction, not by polling or task-log state;
- current monthly and analytics readers consume committed Finance facts and do not wait for all processing jobs to become terminal;
- at this historical ARR1 snapshot, automatic post-commit monthly dispatch was still absent, so no worker was blocked by this stale job. ARR2 later implemented a separate worker under ADR-001.
Terminal reconciliation would still improve operational correctness: accurate failure state, bounded retry semantics,
idempotent failure notification and earlier revocation of an unused writeback grant. It does not make a failed XML run
produce Finance facts or a report. At that snapshot, the immediate business-flow priorities were platform XML
allowlisting, a successful MCP commit and the accepted automatic report-trigger implementation; the later run below
supersedes the allowlist diagnosis while leaving the reconciler as separate control-plane hardening.
## First successful fetch/process and rejected direct submission
Job `arrjob-ae1a40b69c46402fb80e55c980b84886` proves that the platform XML allowlist was changed successfully and moves
the failure boundary beyond OSS ingestion:
- PROGRAM_INPUT v3 caused exactly one `fetch_oss_file` call. The Tool returned `/mnt/user-data/uploads/source.xml`,
`application/xml`, 629434 bytes and SHA-256
`b0019ba8ecdaad878bbde463b0c3129994bf582da16d9caf56d6b96da9a2db2a`, exactly matching ARR registration.
- The Agent loaded the intended Skill and references and invoked `process_daily.py` once. Its successful result reported
135 source rows, 16 excluded rows, 119 output rows and channel counts 26/25/54/14.
- The generated `structured-result.json` passed the Agent's own key check with 135 records, zero errors and a compact
size of 124493 characters.
- Instead of passing that object through once, the Agent repeatedly reopened, printed, compacted and range-read the
file. The captured sequence includes extra shell inspections, a temporary serialization, a full-file read and a
later `start_line=1200,end_line=2500` read.
- The supplied trace snapshot was not a terminal log: it was copied at 12:41:02 +08 with `status=running`, after the
trace connection had failed at 12:40:15 +08. A later read-only ARR trace projection shows the actual terminal state
at 12:41:40 +08.
- Exactly one MCP submission then reached ARR, proving active Tool discovery, network reachability and grant acceptance.
ARR recorded only 20 submitted records, rejected the request as `RESULT_CONTRACT_INVALID`, wrote no Finance version,
terminalized the job as failed and queued one downstream failure event.
The submitted payload therefore was not the complete 135-record object produced by the deterministic Skill. This is
not an OSS, XML allowlist, Skill or MCP reachability problem, and another URI or fetch retry cannot change it. The
current contract makes the model reproduce roughly 124 KB of JSON inside one tool-call argument after receiving that
file through bounded model/tool context. Prompt wording can discourage the observed rereads, but cannot make exact
large-payload transport reliable.
The durable options are either (1) let an installed platform-side submission adapter consume a sandbox file without
serializing it through model output, or (2) make the MCP call a compact attempt-bound completion signal and let ARR use
the deterministic source replay it already performs as the canonical structured payload. A remote MCP server cannot
resolve an Agent-local `/mnt/...` path by itself, so merely replacing `payload` with `payload_path` in the current
remote schema would not work.

View File

@@ -0,0 +1,50 @@
# Task-Log Relocation and Daily-Only Scope
## Metadata
- Date: 2026-07-30
- Status: Historical before-state; superseded by the 2026-07-31 upload interaction update
- Scope: Desktop task-log information architecture, interaction and backend query boundary
- Confidence: Fact
- Source: `arr_web/static/`, `arr_web/repository.py`, focused tests and live local browser verification
- Last verified: 2026-07-30
- Stale trigger: The header utility, trace API/query, processing pipeline type, or task-console selection/polling lifecycle changes
## User-Facing Outcome
- The desktop header position formerly occupied by `手机看板` now contains a `任务日志` button.
- The sole existing black task console moved out of the 日报处理 panel into a native modal dialog; no duplicate console or alternate trace implementation was introduced.
- Before the 2026-07-31 upload interaction update, starting an upload opened the dialog. Mouse or keyboard activation
of a daily-history row still opens the selected job's trace.
- Closing the dialog returns focus to the opener. Trace polling runs only while the dialog is open and the selected job remains active.
- The `/h5` route and assets remain intact, but the desktop header no longer links to them.
## Scope Proof
- `JOBS_SQL`, `JOBS_COUNT_SQL` and `JOB_TRACE_RUN_SQL` each explicitly require
`run.pipeline_type = 'opera_daily'`.
- `GET /api/jobs/{job_id}/trace` resolves exactly one validated job ID.
- `get_job_trace` reads that run's processing attempts, artifact deliveries or legacy direct submissions, linked
Finance daily versions and processing-run outbox events, then sends those allowlisted facts to `build_job_trace`.
- The console therefore represents one daily XML-processing job across upload, processor, validation, database and
linked downstream-notification stages. It is not a global application/server log and does not enumerate other daily
jobs, reporting monthly-run histories or company-report jobs.
## Verification
- `node --check arr_web/static/app.js`: passed.
- Focused unit set: 12 passed across task-log UI contracts, repository SQL/privacy contracts and trace projection.
- At 1440×900, the live modal measured 1040×692, remained centered, had no page horizontal overflow and rendered a
current 13-event terminal. Closing returned focus to `#task-log-trigger`.
- Activating a different daily-history row selected the matching job and loaded `SUCCEEDED`, `13 events` and an `END`
terminal line.
- At 375×812, the modal measured 355×792 with 10-pixel margins. The header trigger remained displayed; document and
dialog horizontal-overflow checks were both false, with long raw lines confined to the terminal scroller.
- Browser warning/error count was zero.
## Known Verification Boundary
The broader `tests.test_arr_web` class currently issues anonymous requests while separate in-progress login work has
already changed the application to return redirects/401 responses. That class reported 13 authentication-boundary
failures/errors before reaching the relocation assertions. Authentication files were left untouched; task-specific
static contracts were isolated in `tests/test_arr_web_task_log_ui.py`.

View File

@@ -0,0 +1,77 @@
# Web Login Runtime Mismatch
## Metadata
- Date: 2026-07-30
- Status: Resolved; authenticated live runtime verified
- Scope: Live ARR Web process on port 8766
- Confidence: Fact
- Source: Listener/process inspection, Keychain/launcher validation, loopback/LAN HTTP probes, source/config inspection, automated tests and isolated browser verification
- Last verified: 2026-07-31
- Stale trigger: Port-8766 process restart, login credential configuration, route/config change or LAN-address change
## Finding
The first page-failure check found a partial runtime mismatch: PID 37865 was still bound to `0.0.0.0:8766` and returned
HTTP 200 for `/`, `/h5`, `/api/health` and `/assets/app.js` on loopback and the then-current LAN address
`192.168.3.48`, while `/login` alone returned 404. The long-lived Python process predated the new login routes but read
newer static files from disk.
The 23:03 recheck found a complete service outage instead. No process listens on 8766, PID 37865 is gone, and `/`,
`/h5`, `/login`, `/healthz`, `/api/health` and `/assets/styles.css` all fail with connection refused on loopback. The
workstation's Wi-Fi address has also changed to `192.168.3.103`, where root and login probes likewise fail. No matching
Web/worker/ngrok process, 8765/8766/8877 listener or launch agent was found. There is no retained process log, so the
evidence does not prove why PID 37865 exited.
The login change was nevertheless relevant to recovery: current source intentionally refuses startup when
`ARR_WEB_USERNAME` or `ARR_WEB_PASSWORD` is absent. After the operator supplied both values, each was placed in a
separate macOS Keychain item and a credential-free local launcher restored the prior controlled database, OSS,
processing, monthly and company-report inputs. Detached Screen session `arr2-web-8766` owns the sole `*:8766` listener.
## Completed Source Implementation
- `arr_web.auth` validates runtime `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD` with constant-time comparison and a bounded
per-client attempt ledger. Missing/invalid configuration fails Web startup closed.
- The authenticated `SessionLedger` issues random server-side sessions and per-session CSRF tokens, expires/revokes
them and retains `HttpOnly`, `SameSite=Strict` plus optional `Secure` cookie behavior.
- Login assets, `POST /api/login` and minimal `/healthz` are the only anonymous routes. Desktop/H5 documents redirect
to an allowlisted login target; all business APIs, detailed health, uploads, traces and downloads reject anonymous access.
- Desktop and H5 clients redirect expired sessions to login and expose CSRF logout. Logout starts hidden when an old
anonymous `/api/session` response lacks `username`; this kept PID 37865's mixed-version UI compatible before activation.
- Caddy Basic Auth was removed; Caddy remains the HTTPS boundary and the Web application owns human authentication.
## Verification
- Python and all three JavaScript files pass syntax checks.
- The final focused auth/Web/company/deployment set passes 33 tests. Full discovery passes 317 tests in 97.311 seconds
with 10 expected environment/fixture skips and no failures/errors.
- Isolated real-browser verification passed generic wrong-credential feedback, password visibility, successful desktop
and `/h5` return, desktop/H5 logout, zero console warnings/errors and no horizontal overflow at 375, 768, 1024 and
1440 CSS pixels.
- Ruby's standard YAML parser loaded the Compose file and deployment contracts confirm login env/readiness/Caddy
shape. Docker is unavailable on this workstation, so no live Compose expansion or image build is claimed.
- Live activation passes anonymous root 303 and business API 401; exact login, hardened cookie/session/CSRF,
authenticated desktop/H5/history reads, all five readiness flags, logout and post-logout rejection. Loopback and
`192.168.3.103` return `/healthz` 200; LAN root redirects to login and LAN `/login` returns 200.
- A 2026-07-31 follow-up found the service still healthy under the same detached Screen process, while DHCP had moved
the workstation address back to `192.168.3.48`. Loopback and `.48` root return 303 to login, `/healthz` and the login
document return 200, and the in-app browser rendered the complete login gateway. The prior `.103` URL is now stale.
- The same-day visual follow-up removed the login page's workflow narrative, numbered steps, duplicate brand placement,
welcome/access introduction and support disclaimers. The live gateway now presents one `ARR Report` heading, the
`username` and `password` fields, password visibility and the existing submit/error states. Thirty-three focused
Web/auth/UI tests pass; browser checks at 390x844 and 1280x720 have no horizontal overflow or console warnings/errors.
- Read-only totals remained 24 daily jobs, 4 monthly versions and 3 company jobs. No XML upload, company-report job,
monthly worker or other business mutation was started during activation.
## Activation Result and Operating Boundary
- Current LAN entry as of 2026-07-31: `http://192.168.3.48:8766/`; DHCP may change this address again.
- `/Users/chillishark/.local/bin/arr2-web-8766` is owner-executable only and contains paths/Keychain labels, not secret
values. Its `--check` mode validates required private inputs without printing them.
- The active Screen session is detached but not a reboot-persistent process manager. A future controlled restart should
validate the launcher, stop the exact 8766 listener and start one `screen -dmS arr2-web-8766` session.
- Credential rotation to a password distinct from the public username is recommended. Update the Keychain item and
restart once; never commit the value or put it in process arguments.
Live login/readiness is complete. A no-PII upload-filename check and one released 5/5 company-report job remain separate
business-mutation acceptance work.

View File

@@ -0,0 +1,84 @@
# Evidence Topic: Booking Excel extraction program
## Metadata
- Date: 2026-07-31
- Status: Implemented, migrated and runtime-active; first real source activation pending
- Scope: Raw Tour Code/`โรงแรม` XLSX parsing, editable/batch-deletable review drafts, HTTP transport and current-source activation
- Confidence: Fact
- Source: supplied workbook, deterministic parser output, source/migration inspection, unit/integration tests, browser QA, live database transaction probe and authenticated runtime probe
- Last verified: 2026-07-31 12:43 +08:00
- Stale trigger: parser/review API change, migration change or first real draft activation
## Question
Does ARR2.0 now contain the requested program that extracts Tour Code plus one or more room-type/quantity rows, keeps
uncertain items for human correction/deletion and activates only a fully reviewed workbook?
## Evidence
- The parser reads only worksheets containing Tour Code plus exact `โรงแรม`, strips Tour Code whitespace,
uses physically-last-row replacement/cancellation semantics and does not infer cancellation from yellow fill.
- Parenthesized `【label】 quantity` items are split in source order. Missing outside quantity defaults to one. Numeric
variants normalize to `U-TWN`/`U-DBL`; `高级房TWN`/`高级房DBL` normalize to `TWN`/`DBL`. Unknown or unbracketed room
names preserve raw text and parsed/default quantity as pending.
- A direct parse of the supplied `ai样板.xlsx` found one source sheet, 26 distinct Tour Codes, 37 room items and 7
pending items. Confirmed quantity is 198; all extracted quantities including pending are 208.
- Supplied examples replay exactly: `LLT260715AC` becomes separate `TWN / 12` and `DBL / 9` items;
`LT260715LD` becomes confirmed `U-TWN / 17` plus pending raw `U-เตียงเสริม13 / 2`.
- Migration 015 stores confirmed/pending/deleted item state outside canonical Booking facts. Repository activation rejects
pending or empty drafts, groups confirmed items back into source rows, creates immutable accepted facts and switches
`booking.current_source_batch` in the same transaction.
- The desktop review table exposes Tour Code, raw label, editable room type/quantity, status, save and delete. Pending
items are visually separated and excluded until saved; discard and whole-draft activation are explicit operations.
- Review pages are fixed at 50 records. Individual and all-visible checkboxes feed a strict 1-50-unique-ID batch route;
the PostgreSQL repository soft-deletes the entire set under one advisory-locked transaction and rolls back if any
selected item is missing, already deleted, foreign to the draft or no longer reviewing. The old item route remains.
- Single and batch delete use one native in-page modal with count-aware copy, focus containment/restoration, backdrop,
button and explicit Escape cancellation. Isolated browser QA selected one, two and all 50 visible rows, proved no
browser-native prompt appeared and did not invoke the confirm action or any deletion endpoint.
- The upload helper, review kicker/description/guidance and source counts/time were removed as requested. The later
filename-context follow-up removed the separate accepted/historical-source status and summary completely. The review
heading is `Booking记录提取`, with `summary.filename` directly below it as the provenance for the displayed records.
- `arr_web.server` now forwards POST/PATCH/DELETE through the same bounded request-body gate. Real loopback socket tests
cover PATCH and DELETE forwarding, security headers, missing length and oversized requests.
- Migrations 014/015 were applied after a transaction-only migration probe. Current batch 1 retained 867 room items,
348 Group Codes and quantity 867; the review tables remained empty after application.
- A real-PostgreSQL transaction-only vertical slice created a two-item draft with one pending item, edited that item to
`EXTRA BED`, activated one immutable source row with quantity three, then rolled back the outer transaction. Batch 1
remained current and zero synthetic drafts/artifacts remained.
- Port 8766 restarted at 12:43:37 +08 with the final review-filename source. Authenticated health reports database, processing,
monthly, download, company-report and company-source-upload readiness true; the draft endpoint is empty and logout succeeds.
Anonymous PATCH/DELETE reach the application and return JSON `401 AUTH_REQUIRED`, proving the old HTTP 501 gap is gone.
- JavaScript syntax, 40 focused Web/review/router tests and repository commit/rollback tests passed. Final post-change
discovery ran 346 tests in 100.681 seconds: all passed, with 10 environment-dependent optional renderer/private-fixture skips.
- The filename-context follow-up passed JavaScript/HTML checks, 36 focused Web/company/review/router tests and a clean
346-test discovery in 98.882 seconds with the same 10 expected skips. Isolated desktop and 390-pixel QA used a long
filename, found no horizontal overflow or console errors and submitted no upload, deletion or activation.
- Authenticated isolated browser QA covered desktop and 390-pixel upload/review/generation layouts, pending-item
editing, 1/2/50-row selection, single/batch dialog cancellation, zero horizontal overflow and zero console errors.
- An expanded Booking/company/real-XLSX suite passed 55/55. The live read-only July 31 snapshot supplied 417 Finance
facts, 27 current Booking items and 27 matched Group Code versions; the processor returned valid reports for all five
companies with row counts 138/139/1/33/3 and zero errors. Missing/unmatched Booking Room cells remained blank.
- Company generation reads `booking.v_current_room_items` and `booking.v_group_room_item_summary` on every repeatable-
read snapshot, so a reviewed Booking activation is visible to the next report without re-importing Opera/Finance.
- The Web release gate intentionally keeps July `21-month-end` closed until 2026-08-01 00:00 Asia/Bangkok. Earlier July
periods are open but currently contain zero rows, so they do not prove populated Booking Room enrichment.
## Finding
The requested extraction capability is implemented, migrated and active in the authenticated company-report page.
Uncertain labels preserve their quantity and remain explicit, editable, removable draft records; they cannot silently
enter accepted room counts. The HTTP and database transaction gaps found during the earlier audit are repaired. The
company-channel processor is already ready for this workflow; the remaining boundary is activation of an actual
operator workbook, not another enrichment-algorithm change.
## Open Items
- Perform one explicitly authorized real workbook upload -> review/edit/delete -> activation before replacing the current
business Booking source. Activation is complete-source replacement, so use a checkpointed complete workbook and a
restoration plan on the shared test database.
- Run a released populated company-report period after activation. For current July month-end, wait until the Bangkok
release boundary or use an isolated coordinator/clock fixture.
- Decide separately whether latest-state single-operator review is sufficient or reviewer/reason/revision history is
required.

View File

@@ -0,0 +1,84 @@
# Evidence Topic: Booking import dimensions and live database inventory
## Metadata
- Date: 2026-07-31
- Status: Database findings active; intake implementation superseded by later manual-review audit
- Scope: Controlled `booking_test`, checked-in Booking Excel/current-source path, current Finance and company-report projections
- Confidence: Fact
- Source: repeatable-read/read-only PostgreSQL queries, schema/view/importer/Web composition inspection, focused tests
- Last verified: 2026-07-31 11:34 +08:00
- Stale trigger: Booking source activation, Finance current-version change, runtime restart, or Booking join-contract change
## Question
What data is currently in the ARR database, and which dimensions must an operator provide when Booking data is manually
imported for Group Code enrichment?
## Evidence
- Read-only snapshots between 10:44 and 10:49 +08:00 confirmed `booking_test` contains:
- Booking: one accepted fixture batch, 867 source rows/current parses/room items, and 348 Group Code summaries.
- Finance: five current daily versions, 475 immutable records, and 417 active retained facts; 58 records are
historical/non-current. The active 417 still include the known one-row `local_fixture` fact.
- Ingestion: 25 runs, 24 attempts, five deliveries, 42 artifacts and ten outbox events.
- Reporting: four monthly runs, 14 daily-version lineage rows and 22 channel-manifest rows.
- `condon`: 11 room-type reference rows and no owner/entitlement/usage rows; it is not the Booking enrichment source.
- The only Booking batch is accepted `expected_fixture`/`md/1.0` from `RES_COMMENT_TYPE_OF_ROOM.md`: six worksheets,
867 rows, 348 distinct normalized Group Codes, four room types and total quantity 867.
- Booking currently groups by normalized `group_code_key + room_type` and sums quantity. It then emits one room summary
per Group Code. Worksheet, source row, artifact and parse version remain provenance/version fields, not report join
dimensions.
- Of 348 Group Codes, 122 have multiple source rows, none has multiple room types, and 23 appear in both
`DY-AI-Easy-KB` and `LIANTAI-FIT`. Those 23 are globally merged by the current view and none is used by the current
Finance projection.
- Finance has 417 active retained rows: 121 rows/27 distinct Group Codes are matched, 285 rows have no Group Code, and
11 rows/two Group Codes are unmatched. The 29 nonblank Group Codes show no reuse across stay segment, company,
channel, business date or block in the current snapshot.
- The company-report projection contains nonblank Booking enrichment for all 121 matched rows and blank room/quantity
values for all 285 missing-code plus 11 unmatched rows. This proves the accepted blank fallback in the database
projection without publishing a report.
- During this audit the worktree parser changed from a normalized three-column workbook to processor 2.0.0. The latest
parser expects `Tour Code` (Group Code aliases accepted) plus `โรงแรม`/Hotel text, keeps the physically last row per
Tour Code, removes last-row cancellations, and derives one or more room-type/quantity items from the hotel text.
Known TWN/DBL families are confirmed and unknown labels are marked for review.
- At 10:59 the earlier direct PostgreSQL importer and old Web fixtures were not aligned with parser 2.0. This was a
point-in-time observation superseded by the later draft repository/editor implementation described in the dedicated
manual-review audit.
- The intended Excel path treats each workbook as a complete replacement source: it creates an immutable batch and
switches the singleton `booking.current_source_batch`; equal-content uploads are SHA-256 idempotent/reactivatable.
- A later 11:19 read-only snapshot found migrations 014/015 structurally live. `current_source_batch` selects historical
batch 1 and both review draft tables are empty. The audit did not perform those concurrent migrations.
- The port-8766 process started at 2026-07-30 23:19, before the new Booking composition, and has no hot reload. The current
HTTP adapter also lacks PATCH/DELETE even though the review editor requires them, so the end-to-end review path remains
unavailable despite the new database tables.
- The latest focused parser/migration/review/Web suite runs 27/27, but it uses fake repositories/router calls and does not
exercise real PostgreSQL or the HTTP method adapter. No Booking import, draft activation or report publication was
performed by this audit.
## Finding
For the current data, `Group Code + room type + quantity` is a sufficient normalized Booking allocation model. It is the
database/report dimension, not a currently stable statement about manual workbook headers. The necessary invariant is
that a normalized Group Code globally identifies one room allocation across worksheets, companies and stay segments.
If the same Group Code can represent different allocations by stay dates or channel, the Booking schema, current views
and company-report join must also gain those dimensions.
Parser 2.0 targets `Tour Code + Hotel` extraction. The separate review draft design is now present in source and live
database; the later 11:30 runtime loads it with PATCH/DELETE and source-upload readiness. The first real draft activation
remains controlled acceptance work. Existing blank/missing lookup behavior remains correct.
## Impact
- Keep `Group Code + room type + quantity` as the normalized result dimensions, while separately choosing and freezing
whether operators upload a normalized template or the real raw `Tour Code + Hotel` export.
- Treat workbook/sheet/row/hash as audit provenance, not as hidden business keys.
- Accept the parser/draft repository through a real PostgreSQL activation before claiming manual Excel import business-
write acceptance; HTTP transport/runtime readiness are now complete.
## Open Items
- Decide whether the 23 cross-worksheet Group Codes are intentionally additive or should be isolated by channel/date.
- Add real-PostgreSQL repository/rollback coverage; PATCH/DELETE handlers and server tests are complete.
- Record the concurrent 014/015 migration provenance, then restart and import one controlled workbook under the accepted
format, test review/idempotence/replacement, and run one released job with explicit write authorization.

View File

@@ -0,0 +1,90 @@
# Evidence Topic: Booking manual-review schema audit
## Metadata
- Date: 2026-07-31
- Status: Superseded implementation snapshot; audit-grade history gap remains active
- Scope: Live `booking_test`, Booking parser 2.0, migrations 008/014/015 and Web review composition
- Confidence: Fact
- Source: repeatable-read live metadata, DDL/contract/source inspection, focused tests, process timestamps and HTTP probes
- Last verified: 2026-07-31 11:21 +08:00
- Stale trigger: port-8766 restart, HTTP method-adapter change, first real draft/activation, or review-policy decision
## Question
Can the current Booking database model and running program support parser 2.0's unrecognized-room items and a human
confirmation loop?
## Evidence
- Migration 008 already supplied a coarse row gate: `parse_versions.parse_status` permits `needs_review`, while
`current_row_parses` and downstream views permit only accepted parses in accepted batches. The existing 867 parses are
all accepted; all 867 canonical room items have non-null room codes.
- At 11:12 +08:00 the live database still lacked migrations 014/015. At 11:19 +08:00 a new read-only snapshot found
`booking.current_source_batch`, `booking.extraction_drafts` and `booking.extraction_draft_items`. This audit did not
perform those concurrent migrations.
- The live singleton now points to accepted source batch 1. Both draft tables are empty, so no real upload/review/activate
transaction has yet been evidenced by their data.
- The live draft schema supports a basic latest-state operator loop: draft `reviewing/activated/superseded`; item
`confirmed/pending/deleted`; source sheet/row/item, Group Code, raw hotel/room text, canonical room type, quantity,
automatic flag, source fragment and timestamps. A pending item must have a null canonical room type; a confirmed item
must have one. All new constraints are validated.
- The schema is not an approval ledger. It has no assignee/reviewer/actor, reason/note, explicit decision timestamp,
before/after values, immutable item revision or append-only review event. No trigger makes closed drafts immutable or
enforces zero pending items at activation; these rules currently depend on application transactions.
- The new PostgreSQL repository uses serializable transactions plus a shared advisory lock, edits only reviewing drafts,
rejects pending/empty activation, writes confirmed items to accepted canonical facts, switches the current source and
closes the draft atomically. Re-extracting the same non-activated artifact deletes its earlier draft/items, so prior
manual edits are not durable history.
- The browser and business router model upload, list, edit, delete, discard and activate. However the browser sends
`PATCH` for confirmation and `DELETE` for item/draft removal, while `arr_web/server.py` implements only `do_GET` and
`do_POST`. Read-only loopback probes returned HTTP 501 for both `PATCH` and `DELETE`. The application-route unit tests
call the router directly and therefore do not catch this transport gap.
- The active port-8766 process started at 2026-07-30 23:19. The new Python composition files were modified at
2026-07-31 11:07-11:13, and the server has no hot-reload mechanism, so the running process has not loaded the new
repository/routes even though the database structure is now present.
- Twenty-seven focused parser/migration/review-contract/fake-repository/Web-route tests pass and a fresh source import
succeeds. No test executes draft CRUD/activation against a real PostgreSQL transaction, and no server-adapter test
covers `PATCH`/`DELETE`.
## Follow-up Resolution
Later on 2026-07-31, `arr_web/server.py` gained bounded PATCH/DELETE dispatch and real loopback socket coverage. The
supplied workbook then passed parser replay, the live review schema passed a read-only repository probe and the complete
source implementation was recorded in
[Booking Excel extraction program](2026-07-31-booking-extraction-program.md). The original audit remains evidence of the
before-state and of the still-open actor/reason/revision-history decision; its claim that PATCH/DELETE are currently
unimplemented is no longer authoritative.
## Original Point-In-Time Finding
The problem is not relational normalization itself. Canonical `booking.room_items` should remain accepted facts; pending
extraction belongs in a separate draft/workflow layer. The now-live 015 tables implement that separation and are enough
to represent a simple single-operator correction state.
At the 11:21 snapshot, the complete workflow was not runnable: the active service predated the new composition and the
HTTP adapter could not transport manual-confirm/delete calls. The later extraction-program work repaired and loaded
those implementation gaps. The audit-depth finding remains: if “人工确认” must be attributable and auditable, the schema
stores only the latest edited state, not who decided what, why, and how it changed.
## Original Point-In-Time Capability Classification
| Capability | Live database | Current program/runtime |
|---|---|---|
| Keep pending extraction out of canonical reports | Yes, through separate draft tables and accepted current-source gate | Repository models it; real PostgreSQL flow untested |
| Item pending/confirmed/deleted | Yes | UI/router/repository model it |
| Edit room type and quantity | Schema supports latest state | At 11:21, blocked because `PATCH` returned 501; later resolved |
| Delete item / discard draft | Schema supports status changes | At 11:21, blocked because `DELETE` returned 501; later resolved |
| Zero-pending atomic activation | Not declaratively enforced | Repository enforces it; real DB transaction untested |
| Reviewer/reason/history | No | No |
| Currently loaded on port 8766 | N/A | No; process predates new Python source |
## Original Open Items And Resolution
- PATCH/DELETE handling, server-level tests, controlled restart and Booking-source readiness are resolved in the later
extraction-program evidence.
- Add real-PostgreSQL draft CRUD, atomic activation and rollback tests before the first business workbook import.
- If audit-grade review is required, add actor/reviewer, action/reason, event time, old/new values or immutable item
revisions, optimistic concurrency and database transition guards.
- Run one controlled raw workbook through upload -> pending review -> confirmation/deletion -> activation -> company
report. No such business mutation was performed by this audit.

View File

@@ -0,0 +1,32 @@
# Evidence Topic: Company-channel action button compacting
## Metadata
- Date: 2026-07-31
- Status: Implemented
- Scope: Company-channel upload action and three period generation CTAs
- Confidence: Fact
- Source: `arr_web/static/styles.css`, `tests/test_arr_web.py`, 28 focused tests, 348-test discovery, isolated browser geometry/screenshots and port-8766 health probe
- Last verified: 2026-07-31 13:37:49 +08:00
- Stale trigger: company-generation markup, responsive breakpoint or action-label change
## Change
- `.company-period-cta` now uses a compact `clamp(104px, 25%, 124px)` width, 42px height, centered alignment and
one-line 10px label treatment. Existing hover, disabled and active feedback remain in place.
- `.company-upload-button` uses the same width/height family and is centered inside the desktop 172px action column;
the mobile single-column override keeps the button centered instead of stretching it full width.
- No API, upload, extraction, report-generation or database code changed.
## Verification
- Static CSS assertions, JavaScript syntax and HTML parsing passed.
- Focused `tests.test_arr_web` plus `tests.test_arr_web_company_reports`: 28/28 passed.
- Full `.venv/bin/python -m unittest discover -s tests`: 348 passed, 10 expected optional skips, 98.283 seconds.
- Isolated authenticated fake Web browser QA at 1280×720 and 390×844 found all three CTA center deltas at `0px`,
matching 104×42px upload/action geometry, one-line labels, and `document.scrollWidth` equal to the viewport width.
Browser console error/warning logs were empty; no upload, extraction, deletion, activation or report-generation
action was invoked. Screenshots showed the compact blue generation actions and subdued disabled upload action
without overlap.
- Port 8766 was restarted after the change under detached Screen session `arr2-web-8766`; PID 71849 serves the
updated static assets and `GET /healthz` returns `ready`.

View File

@@ -0,0 +1,35 @@
# Company-channel card row and in-page generation confirmation
## Metadata
- Date: 2026-07-31
- Scope: `arr_web/static/index.html`, `arr_web/static/styles.css`, `arr_web/static/app.js`, `tests/test_arr_web.py`
- Change type: UI composition and interaction affordance only
- Business-data mutation: none
## Behavior facts
- The large `生成报表` surface now contains one desktop row ordered `上传 Excel 报表`, `第一期`, `第二期`,
`第三期`. The layout falls back to two columns at the tablet breakpoint and one column on narrow mobile screens.
- The `报表月份` input remains an accessible month control but its visible label is visually hidden. It sits in the
setup header's upper-right corner, continues to drive C/O release/history filtering, and is still submitted as
`report_month` after confirmation.
- Period card CTAs now visibly read `生成`; the period range, C/O range and release state remain visible in each card.
- Clicking a released period opens a styled in-page `<dialog>` with the selected month and period. The dialog uses a
restrained light card, backdrop blur, explicit `取消`/`确认生成` actions and focus on `取消` when opened.
- Cancel, Escape and backdrop dismissal close the dialog without calling the generation API. Explicit confirmation keeps
the existing POST body (`report_month`, `period`) and then uses the existing job save/poll/render path. Errors remain
inline in the dialog for retry or cancellation. No `window.confirm` remains in the generation path.
## Verification
- `node --check arr_web/static/app.js`: passed.
- HTML parser check: passed.
- Focused suite: `28 tests`, `OK` (`tests.test_arr_web`, `tests.test_arr_web_company_reports`).
- Full discovery: `348 tests`, `OK (skipped=10)`.
- Isolated authenticated fake-server browser QA did not upload, extract, delete, activate or submit a report. At
1280x720 all four cards share one row; at 390x844 the layout is single-column and `scrollWidth` equals the viewport.
The generation dialog opened with `确认生成`, showed `2026年07月01-10`, and cancel/Escape both closed it without
mutation. Browser console warnings/errors: none.
- The isolated QA server on port 8876 was stopped after verification. Shared port 8766 was restarted at 14:26:56 +08;
its `/healthz` endpoint returns `ready`.

View File

@@ -0,0 +1,28 @@
# Company-detail title and upload rail
## Metadata
- Date: 2026-07-31
- Scope: `arr_web/static/index.html`, `arr_web/static/styles.css`, `tests/test_arr_web.py`
- Change type: UI layout and explanatory affordance only
- Business-data mutation: none
## Behavior facts
- The fixed generation scope is rendered beside `公司渠道明细` as `固定生成LianTai、QBD、DY-AI-Easy-KB、FengRun、HanaTour` in a muted, small helper style.
- The duplicate `固定生成公司` block under `生成报表` is removed; only the `报表月份` input remains in that generator control row.
- `报表月份` is intentionally retained. The client uses it to calculate each C/O period and release state, query company history for the selected month, and submit `report_month` with a generation request.
- `刷新任务` is intentionally retained. It rechecks health and reloads the current Excel source, review draft and company-generation history, then shows a toast; it does not start a report. Its `title` documents this read-only scope.
- The month input has a non-intrusive `title` explaining that it selects the C/O report month and filters the generation records below.
- The Excel upload rail is `min(100%, 760px)` on desktop with the 104px `提取并核对` action column; at the mobile breakpoint it becomes full-width and one column.
## Verification
- JavaScript syntax and HTML parser checks passed.
- Focused suite: `28 tests`, `OK` (`tests.test_arr_web`, `tests.test_arr_web_company_reports`).
- Full discovery: `348 tests`, `OK (skipped=10)`.
- Isolated authenticated fake-server browser QA did not upload, extract, delete, activate or generate anything.
- At 1280x720: source rail `760px`, dropzone `644px`, upload action `104px`, document scroll width `1280px`.
- At 390x844: source rail/dropzone `324px`, single-column action, document scroll width `390px`; fixed-company helper wraps safely under the title.
- Browser console warnings/errors: none.
- Isolated QA server on port 8876 was stopped after verification; shared port 8766 was not changed during QA.

View File

@@ -0,0 +1,32 @@
# Company-channel period card copy
## Metadata
- Date: 2026-07-31
- Scope: `arr_web/static/index.html`, `arr_web/static/styles.css`, `arr_web/static/app.js`, `tests/test_arr_web.py`
- Change type: display copy, card hierarchy and result-detail presentation
- Business-data mutation: none
## Behavior facts
- The three generation cards no longer show the generic `第一期`, `第二期`, `第三期` labels.
- The card headline values now read `C/O:01-10`, `C/O:11-20` and a month-aware final range: `C/O:21-30` for 30-day months or `C/O:21-31` for 31-day months.
- These are display labels only. The internal period keys remain `01-10`, `11-20` and `21-month-end`; release-date
calculation, button enablement, confirmation context, POST payload and generation polling are unchanged.
- Period-completeness chips (`周期未结束` / `周期已结束`) and the computed Bangkok completion time remain below each
C/O headline. The old duplicate C/O detail line was removed so each card has one clear range label.
- A targeted execution check confirms June resolves to `C/O:21-30` and July resolves to `C/O:21-31`.
- Result problems are deduplicated by kind/code/period for display; the internal warning and error arrays are not mutated.
- The result table now shows `生成时间` from `job.finished_at`, falling back to `job.created_at`; `version_no` remains in
the API payload but is no longer exposed as a user-facing revision label.
## Verification
- `node --check arr_web/static/app.js`: passed.
- HTML parser check: passed.
- Focused suite: `28 tests`, `OK` (`tests.test_arr_web`, `tests.test_arr_web_company_reports`).
- Full discovery: `348 tests`, `OK (skipped=10)`.
- Targeted execution check: repeated same-code/same-period warnings collapse to one display item.
- Isolated authenticated fake-server browser QA at 1280x720 and 390x844 confirmed all three labels, no stage-name
remnants, the requested C/O values and zero horizontal overflow. Page console warnings/errors: none. No upload,
extraction, deletion, activation or report generation was submitted.

View File

@@ -0,0 +1,31 @@
# Company-channel early generation and period completeness state
## Metadata
- Date: 2026-07-31
- Scope: `arr_web/company_jobs.py`, `arr_web/static/app.js`, `arr_web/static/index.html`, `arr_web/static/styles.css`, `tests/test_arr_web_company_reports.py`, `tests/test_arr_web.py`
- Change type: generation eligibility, period-state copy and confirmation guidance
- Business-data mutation: none
## Behavior facts
- A current-month C/O period can be submitted before its calendar completion boundary. The backend still derives the
same fixed `as_of_date`: the 10th for `01-10`, the 20th for `11-20` and natural month-end for `21-month-end`.
- The report processor continues to read the current committed Finance snapshot through that cutoff. A later source
commit is not retroactively inserted into an existing workbook; the user can generate the same period again.
- Historical months remain rerunnable. Future report months are rejected by the backend and disabled in the frontend to
avoid creating an empty future-month workbook.
- The right-side period state now says `周期未结束` or `周期已结束`. The state describes calendar completeness in the
Bangkok timezone and no longer controls whether a valid current/historical period can be generated.
- An incomplete-period confirmation explicitly says the workbook uses current stored data and may need a later rerun.
- `period_release_at` remains as a compatibility alias for callers/tests; the implementation-facing name is
`period_complete_at`.
## Verification
- `node --check arr_web/static/app.js`: passed.
- Python compilation for the changed backend/tests: passed.
- HTML parser check: passed.
- Focused suite: `28 tests`, `OK` (`tests.test_arr_web`, `tests.test_arr_web_company_reports`).
- Project virtual-environment full discovery: `348 tests`, `OK (skipped=10)`.
- No real report generation, upload, database write or business-data mutation was performed.

View File

@@ -0,0 +1,32 @@
# Daily KPI Card Size Refinement
## Metadata
- Date: 2026-07-31
- Status: Implemented
- Scope: Daily ARRIVAL DATE, processing duration and NO. OF ROOM cards
- Confidence: Fact
- Source: `arr_web/static/styles.css`, `tests/test_arr_web_daily_visual_ui.py`
- Last verified: 2026-07-31
## User-Facing Outcome
- The three daily KPI cards retain their original grid-column widths.
- Desktop cards now stretch to the same compact grid-row height as the upload panel, approximately one-third of the
prior screenshot height.
- KPI content remains vertically centered within each equal-height card.
- The upload card's dropzone is compressed into a horizontal icon-and-copy layout, with smaller heading, progress and
footer spacing so the whole row stays compact.
- At narrow widths, the original larger upload/dropzone treatment and stacked content remain in place.
## Verification
- `node --check arr_web/static/app.js`: passed.
- `python3 -m unittest tests.test_arr_web_daily_visual_ui tests.test_arr_web_task_log_ui tests.test_arr_web_job_trace tests.test_arr_web_repository_schema tests.test_arr_web.PortalApplicationTests.test_fixed_static_routes_and_required_content tests.test_arr_web.PortalApplicationTests.test_all_public_assets_exclude_removed_copy`: 20 passed.
- No upload request, progress state, task-log behavior, API route or processing coordinator changed.
## Design Boundary
This is a preserve-mode refinement using the existing light neutral surfaces, blue accent, 16px card radius and native
CSS. The selected dials remain DESIGN_VARIANCE: 3, MOTION_INTENSITY: 3 and VISUAL_DENSITY: 5; the change addresses the
visual proportion shown in the supplied reference without adding new dependencies, motion or a new component system.

View File

@@ -0,0 +1,35 @@
# Daily Overview Layout and Copy Cleanup
## Metadata
- Date: 2026-07-31
- Status: Implemented
- Scope: Desktop daily report overview layout and visible labels
- Confidence: Fact
- Source: `arr_web/static/index.html`, `arr_web/static/styles.css`, `arr_web/static/app.js`, focused tests
- Last verified: 2026-07-31
## User-Facing Outcome
- The duplicate top-level `Daily Report` heading is removed.
- `THIS MONTH` is removed from the daily-history panel.
- The daily-history heading is now `Daily Report`.
- The ARR.XML upload station, ARRIVAL DATE card, processing-duration card and NO. OF ROOM card are direct children of
one responsive overview grid. They display in one row on desktop and stack on narrow screens.
- The existing inline upload progress bar, upload request, task-log dialog and history interactions are unchanged.
## Verification
- `node --check arr_web/static/app.js`: passed.
- `python3 -m unittest tests.test_arr_web_daily_visual_ui tests.test_arr_web_task_log_ui tests.test_arr_web_job_trace tests.test_arr_web_repository_schema tests.test_arr_web.PortalApplicationTests.test_fixed_static_routes_and_required_content tests.test_arr_web.PortalApplicationTests.test_all_public_assets_exclude_removed_copy`: 20 passed.
- Frontend static-resource scan confirms the removed daily title class, `THIS MONTH` and the old daily-history wording are
absent from the public HTML/CSS/JavaScript surface.
- A full `tests` discovery was attempted but not used as acceptance because six Agent integration test modules failed
collection in the local environment when the optional `httpx` dependency was unavailable.
- No API route, upload contract, processing coordinator or task-trace query changed.
## Design Boundary
This is a preserve-mode operational UI refinement using the existing dependency-free HTML/CSS system. The chosen design
dials were `DESIGN_VARIANCE: 3`, `MOTION_INTENSITY: 3` and `VISUAL_DENSITY: 5`: one calm four-column desktop composition,
the existing light neutral surfaces and blue accent, and no new animation or dependency.

View File

@@ -0,0 +1,36 @@
# Daily Upload Progress and Task-Log Behavior
## Metadata
- Date: 2026-07-31
- Status: Implemented
- Scope: Desktop daily ARR.XML upload interaction and inline progress feedback
- Confidence: Fact
- Source: `arr_web/static/index.html`, `arr_web/static/app.js`, `arr_web/static/styles.css`, focused tests
- Last verified: 2026-07-31
## User-Facing Outcome
- Clicking `开始处理` keeps the operator on the daily page; it no longer opens the task-log dialog automatically.
- The header `任务日志` button and a selected daily-history row still open the same trace dialog on demand.
- The upload card exposes a compact accessible progressbar with approximate stages for upload, fixed processing,
independent validation and database commit.
- The estimate advances while the synchronous upload request is in flight, caps before terminal completion, then
resolves to 100% on success or an inline red error state on failure.
- The file chooser and dropzone are temporarily disabled while the request is in flight so the visible progress stays
attached to the submitted file.
## Verification
- `node --check arr_web/static/app.js`: passed.
- `python3 -m unittest -v tests.test_arr_web_task_log_ui`: 5 passed.
- `python3 -m unittest -v tests.test_arr_web_job_trace tests.test_arr_web_repository_schema`: 8 passed.
- `python3 -m unittest -v tests.test_arr_web.PortalApplicationTests.test_fixed_static_routes_and_required_content tests.test_arr_web.PortalApplicationTests.test_all_public_assets_exclude_removed_copy`: 2 passed.
- No API route, upload contract, processing coordinator or task-trace query changed.
## Design Boundary
This is a preserve-mode operational UI refinement. It keeps the existing dependency-free HTML/CSS, light neutral theme,
single blue accent and rounded upload-card language. Progress values are intentionally approximate because `POST
/api/jobs` returns after the ARR-owned processing flow reaches a terminal receipt; no fake server progress contract was
introduced.

View File

@@ -0,0 +1,34 @@
# Evidence Topic: Live 2131 company-job Booking Room diagnostic
- Date: 2026-07-31 14:5215:00 Asia/Shanghai
- Status: Resolved diagnostic; no code or data mutation
- Scope: Persisted company job `8aa1fdef3b644f3a9cc0f26924445a29` and its five published XLSX artifacts
- Confidence: Fact
- Stale trigger: New Booking source activation, Finance-version change, report regeneration, or a processor/rule change
## Facts
- The job succeeded for `2026-07`, period `21-month-end` / label `21-31`, with five successful company results.
- Artifact-tool inspection of the published workbooks found 598 populated `21-31` rows: 224 have a nonblank
`Booking Room` and 374 are blank. The per-company totals are LianTai 144/167, QBD 31/128,
DY-AI-Easy-KB 39/28, FengRun 5/33 and HanaTour 5/18 (filled/blank).
- Current Finance facts classify as 614 with a consistent nonblank Group Code and 372 with neither Group Code field
populated. The latter remain their own blank-`Booking Room` output rows.
- Of the rows with a Group Code, only `LLT260719MA` and `LT260718KC` have no current Booking source match. They account
for the other two blank output rows; no other nonblank Group Code yielded an empty `Booking Room`.
- The LianTai workbook's `21-31` sheet row 26 is a representative direct match:
`RES_COMMENT=LT260718KB` and `Booking Room=【DBL】12`. The accepted Markdown contains twelve `LT260718KB` / `DBL`
source rows, so the output is the expected Group-Code aggregation.
- `Total Booking Price` is independent of the Booking mapping. It displays the Finance fact's room-category/price
composition (for example `RM3` and static prices), while Booking only contributes room type and booked quantity. The
current Markdown source has no unit-price field from which a Booking-derived total could be calculated.
## Finding
There is no observed failure to write `Booking Room` in the generated job. Blank cells match the established rule for
missing or unresolved Group Codes; the apparent all-blank impression can result from looking at the leading no-code
rows in a workbook. In the LianTai sheet, the first matching Booking value occurs at Excel row 26.
If the intended business rule is instead to derive a price from the Markdown Booking room type, that is a separate
product change: the Markdown schema needs a price source and an explicit room-type-to-price policy. It is not supported
by the current Group Code / room type / quantity contract.

View File

@@ -0,0 +1,72 @@
# Evidence Topic: Markdown company-channel baseline freeze
## Metadata
- Date: 2026-07-31
- Status: Frozen and independently verified; live publication intentionally excluded
- Scope: Accepted Markdown Booking batch 1, July 31 Finance snapshot, processor 1.2.0 and five company XLSX outputs
- Confidence: Fact
- Source: exact source copy/hash, repeatable-read/read-only PostgreSQL snapshots, independent Markdown aggregation,
artifact-tool export/reopen/value validation, rendered-sheet review and SHA-256 inventory
- Last verified: 2026-07-31 12:26 +08:00
- Stale trigger: Booking current-source activation, Finance current-version change, company processor/rule change or
mutation of a frozen package file
## Question
What exact channel-processing result existed under the Markdown Booking fixture before the first real Booking Excel
activation, and can it be preserved as an independent comparison baseline?
## Evidence
- The source is
`/Users/chillishark/ARR项目0727/outputs/res_comment_20260727/RES_COMMENT_TYPE_OF_ROOM.md`, SHA-256
`ec2302170c5657d3e465691997e17f7694eb5a7f76feba7e43261632209b8c23`, 39,209 bytes.
- Independent parsing produced 867 source rows, six worksheets, 348 normalized Group Codes and total room quantity 867.
- A read-only database guard found accepted/current source batch 1, `expected_fixture`/`md/1.0`, with the same filename,
byte size, hash and counts. The guard was repeated after workbook generation; the current source and current room-item
counts were unchanged.
- The July 31 snapshot contains 417 supported-company Finance facts pinned to five current daily versions:
2026-07-20/version 5, 2026-07-21/version 4, 2026-07-22/version 6, 2026-07-23/version 7 and
2026-07-27/version 2. Twenty-seven requested Group Codes resolve to current Booking parse versions.
- Processor 1.2.0/rule SHA-256
`12ab16062e9b4e5e3785b16cbb5724f3cf0a1d2364b8c4cb12c361a2dfd24c17` produced valid reports for all five
companies:
| Company | Output rows | Booking Room filled | Booking Room blank | Warnings | Errors | Independent mismatches |
|---|---:|---:|---:|---:|---:|---:|
| LianTai | 138 | 16 | 122 | 2 | 0 | 0 |
| QBD | 139 | 11 | 128 | 4 | 0 | 0 |
| DY-AI-Easy-KB | 1 | 0 | 1 | 0 | 0 | 0 |
| FengRun | 33 | 0 | 33 | 0 | 0 | 0 |
| HanaTour | 3 | 0 | 3 | 0 | 0 | 0 |
- Every output row was compared with an expectation built directly from the copied Markdown by normalized
`Group Code + room type`, summing quantity. All 314 rows match: 27 filled Booking Room values and 287 correct blanks.
- The checked-in artifact-tool builder exported and reopened each XLSX, verified exact worksheet names, headers and
cell values, found zero formulas/formula errors, and rendered all three periods. All 15 previews were visually
inspected; populated and empty periods are legible with no clipping or structural defect.
- `SHA256SUMS` verifies every retained source, mapping, payload, validation, workbook, summary and preview file. All five
XLSX files also pass ZIP integrity checks.
- The package is saved at
`outputs/019fb225-1b2c-7211-a4c1-2b12b482f6c1/markdown-channel-baseline-20260731-md-ec230217/`.
- The operation invoked neither the company publisher nor any database mutation. One reviewing Booking Excel draft
existed before and after the freeze, remained outside accepted facts and was not edited or activated.
## Finding
The Markdown-driven channel-processing result is now a durable, independently checkable baseline rather than a
temporary preview. It proves current processor correctness for the exercised data and preserves the exact pre-Excel
comparison point. It does not claim Web job persistence, report-version registration or post-activation integration.
## Impact
- The current Excel draft can be reviewed and deliberately activated later without losing the prior expected result.
- Post-activation comparison should use the frozen source/hash, manifest, row-level comparison and workbook hashes rather
than re-querying a changed current source.
- A released five-company Web job remains necessary to accept task creation, publication metadata and downloads.
## Open Items
- Finish or delete all pending items in the current Excel draft, activate the complete replacement deliberately, then
generate one released five-company job and compare its five overlapping Group Codes against the Excel review result.

View File

@@ -0,0 +1,41 @@
# Evidence Topic: Markdown current source and July release recheck
- Date: 2026-07-31 14:1414:17 Asia/Shanghai
- Scope: Current Booking pointer, open-review state, July company-report period distribution and read-only processor preview
- Mutation: None
## Evidence
- A direct read-only repository check found `booking.current_source_batch` still selecting batch 1,
`expected_fixture`, with 867 source rows, six worksheets, 348 distinct Group Codes and total room quantity 867.
- The current-review query returned no reviewing Booking Excel draft. The previously observed draft is therefore no
longer a live company-task blocker, and no Booking source switch occurred.
- The July 31 company-report snapshot now contains 986 supported-company Finance facts. Their C/O dates range from
2026-07-21 through 2026-07-30: zero facts are in `01-10`, zero are in `11-20` and all 986 are in `21-month-end`.
- A read-only in-memory processor 1.2.0 preview remains valid for all five companies:
| Company | Output rows | Booking Room filled | Booking Room blank | Errors |
|---|---:|---:|---:|---:|
| LianTai | 311 | 144 | 167 | 0 |
| QBD | 159 | 31 | 128 | 0 |
| DY-AI-Easy-KB | 67 | 39 | 28 | 0 |
| FengRun | 38 | 5 | 33 | 0 |
| HanaTour | 23 | 5 | 18 | 0 |
| Total | 598 | 224 | 374 | 0 |
- The Web release rule opens July `21-month-end` at 2026-08-01 00:00 Asia/Bangkok, which is
2026-08-01 01:00 Asia/Shanghai. At the time of the check the period was not released.
## Finding
Markdown remains the current Booking lookup source and the deterministic processor can already be tested directly.
Because all currently relevant Finance facts fall in `21-month-end`, the earlier released July periods can only produce
empty results. A formal Web-created populated job must wait for the release boundary.
The frozen 314-row Markdown package remains a valid, immutable result for its pinned five-version Finance snapshot. It
must not be mistaken for the later live 986-fact projection; the current read-only preview has 598 output rows.
## Open Item
After 2026-08-01 01:00 Asia/Shanghai, create one July `21-month-end` five-company Web job while Markdown batch 1 remains
current, then verify persistence, five downloads and output counts against the job's own pinned snapshot.

View File

@@ -0,0 +1,35 @@
# Evidence Topic: Standard monthly page cleanup
## Metadata
- Date: 2026-07-31
- Status: Implemented; focused checks pass
- Scope: desktop monthly tab DOM/CSS/JS and monthly-run Web response contract
- Confidence: Fact
- Source: static inspection, focused Web/schema tests and JavaScript/Python syntax checks
- Last verified: 2026-07-31
- Stale trigger: monthly table columns, monthly-run response shape, publication watermark rule or desktop navigation changes
## Request
Does the standard monthly page remove the redundant version-history surface while keeping the correct ARRIVAL watermark?
## Changes
- The monthly tab is labeled `月报`; the page heading, `VERSION HISTORY`, `标准月报版本记录` title and C/O-period footer sentence are removed.
- The top of the remaining list is identified by the concise `月报处理` heading alongside the live-update state.
- The visible monthly table now has six columns: `更新至`, `状态`, `数据行`, `公司数`, `生成时间` and `月报`. The separate company-report `版本` column is unchanged.
- The repository exposes `max_arrival_date` as an explicit alias of `reporting.monthly_runs.as_of_date`; `as_of_date` remains in the response for compatibility. Publication rules already define that stored date as the greatest included `ARRIVAL`.
- The frontend renders `max_arrival_date` with an `as_of_date` fallback for older fixtures, preserving 50-row pagination, ordering and four-second visible-tab polling.
- The remaining panel uses a compact live-status toolbar, a wider six-column table and responsive table overflow rather than leaving the removed title block as empty space.
## Verification
- `node --check arr_web/static/app.js` passed.
- Python compilation passed for the changed repository/app/tests modules.
- `tests.test_arr_web`, `tests.test_arr_web_repository_schema`, `tests.test_arr_web_task_log_ui` and `tests.test_arr_web_daily_visual_ui` passed: 32 tests.
- The live authenticated browser tab was not available for this check; a fresh unauthenticated local tab correctly redirected to `/login`, so no credentials were entered and no business action was attempted.
## Finding
The UI now displays the monthly watermark from the same persisted maximum-ARRIVAL source used by monthly publication, without exposing version-history language or the monthly version number as a visible column. No report, Finance or database business data was mutated.