4.2 KiB
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 job60bcda9c8dae49758fb69dc47e0ff9e7 - 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, period11-20, and as-of date2026-07-20. All five companies failed at stagepublishwithCOMPANY_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-20workbooks twice in separate temporary directories. For every company, the two binary SHA-256 values differed while the builder'ssemantic_sha256was identical. AtomicReportPublisher._install_once()rejects an existing archive path when its binary SHA-256 differs from the newly built file, raisingCOMPANY_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
AtomicReportPublishernow 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-tooldependency 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.