Skip to content

fix(reporting-storage): period retirement resolves the period's open issue occurrences (#3648 follow-up) - #3662

Merged
bokelley merged 2 commits into
mainfrom
fix/3648-retired-issue-occurrences
Nov 9, 2026
Merged

bokelley merged 2 commits into
mainfrom
fix/3648-retired-issue-occurrences

Conversation

@bokelley

@bokelley bokelley commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #3654 (#3648): period retirement now resolves the retired period's open issue occurrences.

retireExpiredPeriods deleted a period's obligation and consumer statements, but it left the period's CONSUMER_STATUS_MISMATCH occurrences open in the issue-event outbox, and after that nothing could reach them:

  • The escalation sweep skips an occurrence that has already escalated.
  • Prune collects only resolved occurrences.

So an occurrence that had opened and escalated, and whose events were delivered, stayed open forever. The issue observer never got a resolved, and the rows were never pruned. The reviewer of #3654 reproduced this through the real retireExpiredPeriods: after retirement, with delivery backdated past 7 days, the sweep recorded 0 events and prune deleted 0 rows.

Fix

  • Inside the retirement transaction (phase 3, under the account lock), right after deleting the obligation, retireExpiredPeriods calls the existing settleIssueObligationOn. With the obligation gone nothing conflicts any more, so every open occurrence of that obligation gets resolved, recorded in the same transaction.
  • Outbox rows are never deleted at retirement, so undelivered events (an earlier opened, the new resolved) still go out in order.
  • The resolved event carries issue: null, and it has no principal because the obligation is gone, as documented in feat(reporting): issueObserver — notify the seller when a reporting issue opens, escalates or resolves (#3648) #3654.
  • The whole-occurrence prune then collects the occurrence once it's delivered: it is resolved, and its statement and obligation are gone.
  • ReportingIssueEventStoreV1 now states the contract for custom stores: a store that deletes an obligation resolves the obligation's open occurrences in the same transaction. The guide explains retirement.
  • Memory store: no change needed. memoryStorage() has no retirement path and never deletes an obligation.

Python

Python has the same gap. Its period retirement (_delete_records) never touches reporting_issue_lifecycle, and its settle retires an occurrence only through a current statement, which retirement deletes. Combined with adcontextprotocol/adcp-client-python#1885, a retired period's occurrence re-escalates every retention period, forever. I filed it as adcontextprotocol/adcp-client-python#1886 and cross-linked it from #1885.

Tests

  • packages/reporting-storage/test/reporting-issue-events-pg.test.js, new period retirement case (25/25), driven through the real store.retireExpiredPeriods:
    • An escalated occurrence with delivered opened/escalated gets exactly one undelivered resolved in the retirement.
    • The dispatcher delivers it with issue: null and no principal, and prune then deletes the whole occurrence (3 rows).
    • Control: retirement deletes no outbox rows. A second occurrence's undelivered opened survives retirement and is delivered before its resolved.
    • Fails without the fix: removing the settle call fails exactly this test.
  • Local PostgreSQL 16, all passing:
    • Issue-events, production, ledger, ingest and migration suites: 59/59.
    • Run alone (serial group): reliable-reporting-core-maintenance-pg 12/12, reporting-row-retention-pg 9/9, reporting-host-reads-pg 17/17, reporting-bigquery-sink-pg 80/80.
    • typecheck, typecheck:reporting-storage, lint:boundaries and format:check are clean.

Changeset

@adcp/reporting-storage patch and @adcp/sdk patch (the store-contract TSDoc). No migration.

🤖 Generated with Claude Code

bokelley and others added 2 commits November 9, 2026 01:27
…issue occurrences (#3648)

retireExpiredPeriods deleted the obligation and statements but left already-
escalated mismatch occurrences open in the issue-event outbox forever: never
resolved for the observer and never prunable. Resolve them in the retirement
transaction with settleIssueObligationOn; pending events stay deliverable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hrough retireExpiredPeriods (#3648)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@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 bugfix with no blocking or medium findings.

Checked:

  • retireExpiredPeriods now calls settleIssueObligationOn inside the phase-3 transaction (postgres.ts:2293), deliberately placed after DELETE FROM adcp_reporting_obligations so the obligation is gone, conflicts is empty, and every OPEN occurrence resolves. The OPEN filter prevents double-resolve.
  • The outbox table (adcp_reporting_issue_events) has no FK references and is never deleted at retirement; the only obligation-delete site is this one.
  • Test (reporting-issue-events-pg.test.js, +92) drives the real retirement path and includes a control proving undelivered outbox rows survive — covers the new branch.
  • Changeset present and correctly typed patch for the two experimental packages (bugfix, not breaking per REPORTING-STABILITY policy); SDK change is TSDoc-only, so changeset-vs-wire-impact is satisfied.

Decision table: no critical/high findings (row 1 n/a); gated_paths false (row 2 n/a); high_risk false, no deletions/modifications triggers (rows 3-5 n/a); no prior decision (row 6 n/a); no no-auto-approve team match (row 7 n/a); zero medium findings (row 8 n/a). Falls through to row 9 → approve. Note: review_decision is REVIEW_REQUIRED but gated_paths is false, so no gate applies.

@bokelley
bokelley merged commit 28694b0 into main Nov 9, 2026
68 of 70 checks passed
@bokelley
bokelley deleted the fix/3648-retired-issue-occurrences branch November 9, 2026 02:05
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.

1 participant