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