Repository navigation
fix(reporting): refuse an adcp-reporting-storage of another storage contract at startup (#1763) - #1789
Conversation
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
Adds a storage-contract version handshake between adcp and adcp-reporting-storage (#1763). The reviewer surfaced no medium-or-higher findings.
What was checked:
- Fail-closed
require_storage_contractruns atReliableReportingService._initialize(service.py:213) before anycreate_schema/check_schemaand atpostgres_storage()construction (storage/init.py:231); both pass the ledger store, consistent with service.store → storage.ledger (service.py:549). - Six shared column lists in
_record_rowsexist and parse correctly for the fingerprint;_STATUS_COLUMNScovered via_STATUS_COLUMNS_BARE. REPORTING_SCHEMA_BEHINDis a pre-existing valid seller-config code withcorrectablerecovery, reused deliberately.- Provenance classification correct by construction; check is local-only (importlib.metadata, no I/O, no timeout needed) and fails once at startup.
- Tests cover classification, both refusal directions, startup-before-schema, construction-time refusal, and real-storage agreement.
- Additive public surface under a
fix:commit — no semver break.
Decision table: no critical/high/medium findings; gated_paths false; high_risk false; no author team gate; no prior decision. Falls through to row 9 → approve.
…ontract at startup adcp 10.0.0b4 added offering_id to the ledger column lists that adcp-reporting-storage reads against its DDL; the published storage release (0.2.0b0) lacked the column, installed cleanly beside 10.0.0b4, and failed every reporting turn with UndefinedColumn, logged only as a configuration failure (#1763). adcp now names the ledger layout it reads with one integer, adcp.reporting.ledger.storage_contract.REPORTING_STORAGE_CONTRACT (2), and checks the installed ledger store against it at service startup (ReliableReportingService._initialize, before any schema work) and when postgres_storage() is built. A store declares its contract with a storage_contract attribute (adcp-reporting-storage 0.2.0b1+, #1787); a store from that distribution without one predates the declaration and is contract 1; other stores are not subject to the check. A mismatch raises ReliableReportingConfigurationError with REPORTING_SCHEMA_BEHIND, naming both installed versions, both contracts, the release to install and the migration to run, so the pair fails once and loudly instead of every turn. tests/test_reporting_storage_contract.py pins a fingerprint of the shared column lists to the contract number, so changing _record_rows without bumping the contract (and the storage DDL with it) fails the build. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fb53ff6 to
ae145e3
Compare
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean pass, no blocking or medium findings.
This is an additive commit introducing adcp.reporting.ledger.storage_contract (REPORTING_STORAGE_CONTRACT=2) plus startup/construction consistency checks in ReliableReportingService._initialize and postgres_storage. Verified:
- Both sites check the ledger store consistently (service.py:549 sets self.store = storage.ledger).
- The check fails closed before any schema work.
- REPORTING_SCHEMA_BEHIND is a valid correctable code.
- Classification by declaration-then-provenance matches the stated contract history.
- The fingerprint hashes six column lists that all exist in _record_rows.
- The
fix()prefix is appropriate for a purely additive guard.
No public-API surface removal/rename, no discriminated-union or forward-compat change, no generated-code hand-edits, no CI-gate disabling, no credential-in-ctx_metadata concerns. high_risk is false; gated_paths is false and review_decision is APPROVED. No no-auto-approve team match. Prior decision was also approve with no findings; current run adds nothing actionable. Falls through to row 9.
4e11f3d
into
claude/issue-1763-storage-migrations
…ontract at startup (#1789) adcp 10.0.0b4 added offering_id to the ledger column lists that adcp-reporting-storage reads against its DDL; the published storage release (0.2.0b0) lacked the column, installed cleanly beside 10.0.0b4, and failed every reporting turn with UndefinedColumn, logged only as a configuration failure (#1763). adcp now names the ledger layout it reads with one integer, adcp.reporting.ledger.storage_contract.REPORTING_STORAGE_CONTRACT (2), and checks the installed ledger store against it at service startup (ReliableReportingService._initialize, before any schema work) and when postgres_storage() is built. A store declares its contract with a storage_contract attribute (adcp-reporting-storage 0.2.0b1+, #1787); a store from that distribution without one predates the declaration and is contract 1; other stores are not subject to the check. A mismatch raises ReliableReportingConfigurationError with REPORTING_SCHEMA_BEHIND, naming both installed versions, both contracts, the release to install and the migration to run, so the pair fails once and loudly instead of every turn. tests/test_reporting_storage_contract.py pins a fingerprint of the shared column lists to the contract number, so changing _record_rows without bumping the contract (and the storage DDL with it) fails the build. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…pin the adcp storage contract (#1763) (#1787) * fix(reporting-storage): upgrade 0.1.0b4/0.2.0b0 ledgers in place and pin the adcp storage contract adcp 10.0.0b4 added offering_id to the ledger column lists in adcp.reporting.ledger._record_rows, which adcp-reporting-storage reads against its own DDL. The only published storage release, 0.2.0b0, does not create the column, declares adcp>=9.0.0,<11, and so installs cleanly beside 10.0.0b4 and fails every reporting turn with UndefinedColumn (#1763). Storage side of the fix: - create_schema() now applies the versioned migrations (0001-0005) that are not yet recorded in adcp_schema_migrations inside its own transaction, under the reporting schema advisory lock, before its optional DDL. A ledger created by 0.1.0b4 or 0.2.0b0 is upgraded in place through either entry point: offering_id is added with '' for every existing account-level generation, obligation and status, the generation key and period index are widened, the managed-delivery foreign keys are re-pointed, and the applied schema versions are recorded. Re-running is a no-op; concurrent boots serialize. No evidence row is rewritten or dropped. - A storage contract number is declared on both sides (adcp_reporting_storage.STORAGE_CONTRACT = 2) and checked when a PostgreSQL ledger store is built: an adcp of another contract is refused at startup with REPORTING_SCHEMA_BEHIND naming both versions and the release to install, instead of failing on the first read. adcp releases without a declaration (10.0.0b4, b5) are classified by their column lists. - The dependency floor moves to adcp>=10.0.0b4,<11, the first release of contract 2, so the incompatible pair no longer resolves. tests/conformance/reporting/test_reporting_storage_published_upgrade_pg.py seeds ledgers with the actual published wheel pairs (adcp 9.3.1 + storage 0.1.0b4, adcp 10.0.0b2 + storage 0.2.0b0), reproduces the b4 failure on 0.2.0b0, and proves both upgrade paths keep every evidence row, read everything back, rebind the stored generations on restart, close the next period, record the version, re-run as a no-op, serialize concurrent boots, and produce a catalog identical to a fresh migration. Closes #1763 (storage side); the adcp-side startup check and the release gate follow in their own PRs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(reporting-storage): bound create_schema to the ledger's own migrations create_schema applies and records the migrations made of the DDL this store owns (0001-0003) and stops there: 0004 (subscription state) and 0005 (inline storage) create reporting_* tables of other stores, and the A/B catalog-identity suites assert that an older binary's installed object set is exactly its own manifest. Those two stay pending for python -m adcp migrate, which postgres_storage requires anyway. Also: the contract module imports ReliableReportingConfigurationError through the public adcp.reporting name, the public API snapshot records STORAGE_CONTRACT and require_adcp_contract, and the release configuration test pins next's storage floor to the first adcp of its storage contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(reporting): refuse an adcp-reporting-storage of another storage contract at startup (#1789) adcp 10.0.0b4 added offering_id to the ledger column lists that adcp-reporting-storage reads against its DDL; the published storage release (0.2.0b0) lacked the column, installed cleanly beside 10.0.0b4, and failed every reporting turn with UndefinedColumn, logged only as a configuration failure (#1763). adcp now names the ledger layout it reads with one integer, adcp.reporting.ledger.storage_contract.REPORTING_STORAGE_CONTRACT (2), and checks the installed ledger store against it at service startup (ReliableReportingService._initialize, before any schema work) and when postgres_storage() is built. A store declares its contract with a storage_contract attribute (adcp-reporting-storage 0.2.0b1+, #1787); a store from that distribution without one predates the declaration and is contract 1; other stores are not subject to the check. A mismatch raises ReliableReportingConfigurationError with REPORTING_SCHEMA_BEHIND, naming both installed versions, both contracts, the release to install and the migration to run, so the pair fails once and loudly instead of every turn. tests/test_reporting_storage_contract.py pins a fingerprint of the shared column lists to the contract number, so changing _record_rows without bumping the contract (and the storage DDL with it) fails the build. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> * test(reporting-storage): follow the shipped migration set and cover a notifications-enabled ledger next added migration 0006 (status checkpoint offerings) after this branch was cut; the published-wheel upgrade test now derives the versions it expects from the shipped set instead of listing them, and seeds a notifications-enabled 0.2.0b0 ledger (outbox and status stores) to prove the in-place upgrade and migrate record every migration, 0006 included, with the evidence unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * style(reporting-storage): wrap the create_schema migration comment Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Why
adcp 10.0.0b4 + adcp-reporting-storage 0.2.0b0 installs cleanly and fails every reporting turn with
UndefinedColumn, logged only asReliable Reporting worker component configuration failed(#1763, item 3(b)).What
adcp.reporting.ledger.storage_contract.REPORTING_STORAGE_CONTRACT = 2names the ledger layout this adcp reads (the_record_rowscolumn lists). Contract history is in the module docstring (1 = SDK 9 and 10.0.0b0–b3; 2 = 10.0.0b4+).Startup check in
ReliableReportingService._initialize()(before any schema work, for bothstore=andstorage=wiring) and inadcp.storage.postgres_storage()at construction. A store declares its contract with astorage_contractattribute (storage 0.2.0b1+); a store fromadcp_reporting_storagewithout one is classified as contract 1 (0.1.0b4 / 0.2.0b0); in-memory and adopter stores are not subject to the check.A mismatch raises
ReliableReportingConfigurationError(code="REPORTING_SCHEMA_BEHIND")(correctable,config_field="adcp-reporting-storage"):The existing cross-SDK code is reused deliberately: the seller configuration vocabulary is pinned to
error-codes.json, and "the storage schema is behind this SDK" is exactly the condition.Fingerprint pin:
tests/test_reporting_storage_contract.pyhashes the shared column lists and pins the hash to the contract number, so a future_record_rowschange without a contract bump (and the matching storage DDL/migration) fails the build.Docs:
MIGRATION_v9_to_v10.md,docs/reliable-reporting-service.md,docs/reporting-subscriptions-errors.md,docs/reporting-subscriptions-seller.md.Tests
Classification (declared / older / newer / undeclared storage release / custom store), refusal messages, service startup refusing before
create_schema()is touched,postgres_storagerefusing at construction, and the realPgReportingLedgerStoreagreeing with adcp. PG migration and core-recovery conformance tests pass locally with the workspace pair.🤖 Generated with Claude Code