Skip to content

fix(reporting): classify pending status and owed history as transient, unresolved references as REFERENCE_NOT_FOUND - #1631

Merged
bokelley merged 1 commit into
mainfrom
claude/issue-1618-reporting-recovery
Oct 26, 2026
Merged

bokelley merged 1 commit into
mainfrom
claude/issue-1618-reporting-recovery

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Fixes #1618

What changed

installed_reporting_call used to project three LedgerConflictError codes as terminal. They now map as follows (src/adcp/reporting/_errors.py):

Ledger code Before After
STATUS_PROJECTION_UNAVAILABLE terminal STATUS_PROJECTION_UNAVAILABLE / transient
HISTORY_UNAVAILABLE terminal HISTORY_UNAVAILABLE / transient
LOOKUP_UNAVAILABLE terminal REFERENCE_NOT_FOUND / correctable, generic message Reporting reference not found

Why each was chosen

  • Pending status lifecycle. The raise sites are "status lifecycle is pending", "did not converge", "projection is unavailable" and "invalid waiver chain". All of them are seller-side state. The buyer's request is valid, and the same retry succeeds once the seller captures or repairs the snapshot. The SDK knows no retry_after for these, so none is set.
  • HISTORY_UNAVAILABLE. Every raise site a buyer can reach is retained evidence the seller still owes inside the retention window. That evidence is either not yet materialized ("materialization dependencies are unavailable", "receipt revision is unavailable") or awaiting operator repair ("revision history requires repair", "reconciliation history is inconsistent"). This is the same situation as fix(reporting): answer unreadable exact revision reads with SERVICE_UNAVAILABLE #1471, where rows that fail their binding answer SERVICE_UNAVAILABLE / transient. Terminal would tell a buyer to stop sending a correct request. The permanent "history older than retention" meaning of this code is the get_reporting_status issue code, which carries its own action_required severity. It is never raised as an error.
  • LOOKUP_UNAVAILABLE. These are buyer-supplied reporting references that don't resolve for the caller. AdCP 3.2's REFERENCE_NOT_FOUND says: "Typed parameters that lack a dedicated standard code MUST also use REFERENCE_NOT_FOUND rather than minting a custom *_NOT_FOUND code". Its uniform-response rule requires a generic message, so the projection now rewrites the message too. This follows fix(reporting): answer unreadable exact revision reads with SERVICE_UNAVAILABLE #1471, which made revision lookups REFERENCE_NOT_FOUND, and the existing REVISION_NOT_FOUND mapping.

Other codes in the table

I went through every LedgerConflictError code that can reach installed_reporting_call. Cursor and input codes are already correctable. The other unlisted codes are producer-side writes, adopter composition faults (REPORTING_CONFIGURATION_UNADMITTED, UNAUTHORIZED) or integrity failures, and terminal remains correct for them. I left out one code because its meaning is mixed: REPORTING_RECORD_UNAVAILABLE is used both for unresolved receipt targets and for stale boundaries, and it only reaches buyers through per-item receipt results. Those per-item results (sync_reporting_status / sync_reporting_receipts failed entries) carry no recovery today. That gap is separate from this table.

Store-level callers still see the original LedgerConflictError codes. Only the buyer projection changes.

Tests

  • tests/test_reporting_installed_errors.py: the issue's reproduction on both the decisioning and handler paths, plus updated extension-code expectations.
  • make lint, make typecheck-all and make test pass locally except one pre-existing failure, tests/integration/test_a2a_context_id.py::test_two_calls_share_server_assigned_context_id. It fails only inside the full serial suite (Unknown skill: create_media_buy), and it fails the same way on clean origin/main.

🤖 Generated with Claude Code

…, unresolved references as REFERENCE_NOT_FOUND

installed_reporting_call projected STATUS_PROJECTION_UNAVAILABLE,
HISTORY_UNAVAILABLE and LOOKUP_UNAVAILABLE as terminal. A pending status
lifecycle and retained history the seller still owes resolve on the seller
side, so they are transient. A reporting reference that does not resolve for
the caller answers the standard, nondisclosing REFERENCE_NOT_FOUND
(correctable) with a generic message.

Fixes #1618

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bokelley
bokelley force-pushed the claude/issue-1618-reporting-recovery branch from c0d65ed to f868413 Compare October 26, 2026 01:58

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean error-code reclassification fix in the private adcp.reporting._errors module.

Checked:

  • Reclassifies three LedgerConflictError codes: STATUS_PROJECTION_UNAVAILABLE / HISTORY_UNAVAILABLE (terminal→transient) and LOOKUP_UNAVAILABLE→REFERENCE_NOT_FOUND/correctable with a nondisclosing generic message.
  • Verified against the pinned 3.2.0-beta.9 manifest: REFERENCE_NOT_FOUND is correctable, so the mapping is spec-correct and follows the existing REVISION_NOT_FOUND precedent.
  • Uniform-nondisclosure MUST honored — test confirms obl_secret is scrubbed.
  • Private module, no public-surface change; fix: prefix is correct. No semver, forward-compat, codegen, import-layering, or wire-translation rules in play.
  • Both decisioning and handler paths are tested.

Decision path: no critical/high/medium findings; gated_paths false; high_risk false; no no-auto-approve team match; no prior decision. review_decision is REVIEW_REQUIRED but gated_paths is false, so row 2 does not fire. Falls through to row 9 → approve.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve.

Reclassifies three reporting ledger codes in installed_reporting_call: STATUS_PROJECTION_UNAVAILABLE and HISTORY_UNAVAILABLE move terminal→transient (routed through _REPORTING_TRANSIENT), and LOOKUP_UNAVAILABLE maps to the nondisclosing REFERENCE_NOT_FOUND/correctable with a generic message that drops the buyer-supplied reference (verified no obl_secret leak). Raise-site audit confirms all buyer-reachable sites are seller-owed, retry-succeeds state. No public signature change, no enum/recovery-value break, no DR-0032/0033 pathway applies. Tests cover both decisioning paths × all three codes with redaction asserts.

No critical/high/medium findings. gated_paths is false and high_risk is false, so rows 2–5 do not apply. No no-auto-approve team match (row 7 n/a). Prior decision was approve (row 6 n/a). Fewer than three medium findings (row 8 n/a). Falls through to row 9 → approve. Note: review_decision is REVIEW_REQUIRED, but since gated_paths is false, row 2's gate does not fire.

@bokelley
bokelley merged commit a2ec81c into main Oct 26, 2026
47 checks passed
@bokelley
bokelley deleted the claude/issue-1618-reporting-recovery branch October 26, 2026 03:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Installed reporting tools mark STATUS_PROJECTION_UNAVAILABLE, LOOKUP_UNAVAILABLE and HISTORY_UNAVAILABLE as terminal

1 participant