# 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.
