Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
168 commits
Select commit Hold shift + click to select a range
3dcbf64
feat(reporting): buyer-computable coverage for reporting subscriptions
bokelley Oct 29, 2026
0f98ba1
fix(reporting): defer the subscriptions feature id until adcp.princip…
bokelley Oct 29, 2026
f8b2fdd
feat(reporting): conform subscription coverage to adcp#8585
bokelley Oct 29, 2026
a99597a
fix(reporting): settle coverage edge cases and adopt adcp#8588 calendars
bokelley Oct 29, 2026
f8b48c0
feat(reporting): the confirmation period is owed (adcp#8593 start rule)
bokelley Oct 29, 2026
b6d741d
feat(reporting): serve reporting subscriptions from the principal run…
bokelley Oct 29, 2026
08291bf
fix(reporting): fail closed on partial rosters and project 3.3 fields…
bokelley Oct 29, 2026
ed79ca3
fix(reporting): scan path_template placeholders in one linear pass
bokelley Oct 29, 2026
c3316b0
test(reporting): drop an unused constant from the subscription engine…
bokelley Oct 29, 2026
8843fab
feat(reporting): conform the subscription runtime to adcp#8585
bokelley Oct 29, 2026
4323ee4
fix(reporting): immutable subscription history, sourced change instan…
bokelley Oct 29, 2026
36e2e4c
test(reporting): the engine owes the confirmation period (adcp#8593)
bokelley Oct 29, 2026
f103466
fix(reporting): isolate a subscription whose readback exceeds 1000 ac…
bokelley Oct 29, 2026
9d917bc
feat(reporting): buyer runtime and durable storage for reporting subs…
bokelley Oct 29, 2026
2d6b4cd
test(reporting): cover multi-version readback ordering in version rec…
bokelley Oct 29, 2026
60c748a
feat(reporting): read inactive_intervals and standard_dimensions on t…
bokelley Oct 29, 2026
b35d3ff
fix(reporting): fail closed on an incomplete engine-store principal walk
bokelley Oct 29, 2026
800adfc
fix(reporting): buyer subscription runtime review fixes
bokelley Oct 29, 2026
a74b552
feat(reporting): hold subscription obligations in the reporting ledger
bokelley Oct 29, 2026
209ded8
feat(reporting): plan and execute subscription obligations in the ser…
bokelley Oct 29, 2026
5258eb5
test(reporting): keep the option-off transcript independent of the ru…
bokelley Oct 30, 2026
ff2feb6
feat(reporting): managed delivery of subscription obligations
bokelley Oct 30, 2026
b1d1c04
Merge origin/main into bokelley/reporting-subscription-ledger
bokelley Oct 30, 2026
2de8ee7
Merge bokelley/reporting-subscription-ledger (origin/main) into bokel…
bokelley Oct 30, 2026
7ecf3cd
Merge bokelley/reporting-subscription-ledger-service (origin/main) in…
bokelley Oct 30, 2026
f8fcd02
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
2ad906d
chore(reporting): packed-size budget for the subscription coverage mo…
bokelley Oct 30, 2026
0af8b6f
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
e37f6ab
chore(reporting): packed-size budget for the subscription runtime aft…
bokelley Oct 30, 2026
803023e
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
73120d5
fix(reporting): bound inactive intervals when omission deactivates a …
bokelley Oct 30, 2026
3005c07
Merge bokelley/reporting-subscription-runtime into bokelley/reporting…
bokelley Oct 30, 2026
3c3fb57
Merge bokelley/reporting-subscription-consumer into bokelley/reportin…
bokelley Oct 30, 2026
f65231b
Merge bokelley/reporting-subscription-ledger into bokelley/reporting-…
bokelley Oct 30, 2026
bb3c98c
Merge bokelley/reporting-subscription-ledger-service into bokelley/re…
bokelley Oct 30, 2026
331d732
feat(reporting): owner-scoped receipts and statuses, coverage that fo…
bokelley Oct 30, 2026
4084b81
feat(reporting): reporting-subscriptions storyboard and reference seller
bokelley Oct 30, 2026
a1c4fbb
feat(reporting): deactivated subscriptions read inactive in coverage …
bokelley Oct 30, 2026
6f5ae60
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
a214e0e
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
2d9b28c
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
936b3a3
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
32cfbeb
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
fbcbf99
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
c5bc42f
fix(reporting): drop the post-loss receipt window; lock replace_accou…
bokelley Oct 30, 2026
0b83d9e
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
bcbe0a3
test(reporting): no post-loss receipt check in the PostgreSQL restart…
bokelley Oct 30, 2026
9c1ed22
docs(reporting): the engine has no post-loss receipt window
bokelley Oct 30, 2026
7dbc1bf
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
6efb751
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
7f1aba2
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
ae56440
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
830ad35
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
f6da30b
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
18be800
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
35c63ce
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
1860db3
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
ce21aeb
chore(reporting): count the subscription wire-types module in the fil…
bokelley Oct 30, 2026
c55592b
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
0c3de4b
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
5c57aa2
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
9a59380
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
e65ab09
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
5428368
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
221ba7d
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
ffe7351
chore(reporting): count the example storyboard and reference seller i…
bokelley Oct 30, 2026
d942a1f
feat(reporting): move reporting-subscription building blocks to repor…
bokelley Oct 30, 2026
ed8ed37
feat(reporting): wind down with windDownGrant instead of revoking
bokelley Oct 30, 2026
adbbad4
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
edff7a7
feat(reporting): typed reporting-subscription errors with code, recov…
bokelley Oct 30, 2026
c9b3318
fix(reporting): align ReportingSubscriptionError with the canonical e…
bokelley Oct 30, 2026
3428a00
test(reporting): run the canonical reporting-subscription coverage ve…
bokelley Oct 30, 2026
1e7a5c3
feat(reporting): freeze reporting-subscription names before beta.3
bokelley Oct 30, 2026
88977b2
chore(reporting): raise the packed-byte budget for the reporting-subs…
bokelley Oct 30, 2026
870361f
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
012dea1
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
ba96a6f
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
68e1563
test(reporting): validate the parity table against the pinned AdCP bu…
bokelley Oct 30, 2026
698c218
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
be83582
test(reporting): serve the pinned AdCP release (3.3.0-beta.2) in subs…
bokelley Oct 30, 2026
0037ffb
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
e56c2a8
chore(reporting): packed-size budget for the buyer runtime after main…
bokelley Oct 30, 2026
dbd4993
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
fb7f953
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
ad1281a
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
2635bd1
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
79d5113
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
fb2b279
test(reporting): the option-off golden transcript serves main's 3.3.0…
bokelley Oct 30, 2026
6e11b9c
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
f520d3d
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
4ad844c
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
395f113
Merge remote-tracking branch 'origin/bokelley/reporting-subscription-…
bokelley Oct 30, 2026
2330106
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
9ae1aaf
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
864a706
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
026caca
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
11620c0
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
91712f9
style(reporting): format the coverage parity test
bokelley Oct 30, 2026
df782e5
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
27bf1e3
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
e7a74e9
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
e27f6ed
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
4fbfdac
style(reporting): format the parity-table schema check
bokelley Oct 30, 2026
315b82f
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
a2f27cc
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
64b7a10
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
373687f
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
d6b83c9
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
817e77e
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
e0eb8c8
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
0ca664b
Merge branch 'bokelley/reporting-subscription-conformance' into bokel…
bokelley Oct 30, 2026
1d6cbbc
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
5ab5d7d
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
b775e36
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
c15d594
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
545db54
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
7ea6162
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
44420f2
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
c073df6
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
58d5f9e
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
f4b3020
chore(reporting): raise the packed-byte budget at the bottom of the D…
bokelley Oct 30, 2026
4d1e45a
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
b927035
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
df6845d
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
147c5fa
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
62473e9
Merge remote-tracking branch 'origin/main' into bokelley/reporting-su…
bokelley Oct 30, 2026
4b7e38f
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
6c32dfd
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
a4484d6
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
e77024d
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
72fdbdd
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
bf14d28
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
62be3da
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
f44c32c
Merge branch 'bokelley/reporting-subscription-conformance' into bokel…
bokelley Oct 30, 2026
53e76ef
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
35e1168
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
b1e7a12
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
d5a8dbf
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
3f5bf10
fix(reporting): refuse a sub-daily billing_cycle calendar
bokelley Oct 30, 2026
0dfb8dd
Merge branch 'bokelley/reporting-subscription-coverage' into bokelley…
bokelley Oct 30, 2026
64770b7
fix(reporting): only an explicit false clears a pending replace_accou…
bokelley Oct 30, 2026
a12175f
fix(reporting): refresh a changed pending action and report the lates…
bokelley Oct 30, 2026
33fc09d
Merge branch 'bokelley/reporting-subscription-runtime' into bokelley/…
bokelley Oct 30, 2026
b0ae1a0
Merge branch 'bokelley/reporting-subscription-consumer' into bokelley…
bokelley Oct 30, 2026
a582ff0
Merge branch 'bokelley/reporting-subscription-ledger' into bokelley/r…
bokelley Oct 30, 2026
3146133
Merge branch 'bokelley/reporting-subscription-ledger-service' into bo…
bokelley Oct 30, 2026
ce3bef7
Merge branch 'bokelley/reporting-subscription-delivery' into bokelley…
bokelley Oct 30, 2026
f52370d
Merge branch 'bokelley/reporting-subscription-receipts' into bokelley…
bokelley Oct 30, 2026
5155db8
Merge branch 'bokelley/reporting-subscription-conformance' into bokel…
bokelley Oct 30, 2026
9c3d12f
Merge branch 'bokelley/reporting-subscription-dx-advanced' into bokel…
bokelley Oct 30, 2026
0f76b46
Merge branch 'bokelley/reporting-subscription-dx-revocation' into bok…
bokelley Oct 30, 2026
2b97971
Merge branch 'bokelley/reporting-subscription-dx-errors' into bokelle…
bokelley Oct 30, 2026
2ce2799
Merge branch 'bokelley/reporting-subscription-dx-vectors' into bokell…
bokelley Oct 30, 2026
8997e1d
feat(reporting): write per-offering exclusions across calendars
bokelley Oct 30, 2026
18a1c0e
feat(reporting): managed reporting-subscription block on the Reliable…
bokelley Oct 30, 2026
ebb6cc7
feat(reporting): the grant journal is the one source of authorized_at
bokelley Oct 30, 2026
989e83b
feat(reporting): buyer write facade and the raw syncPrincipal guard
bokelley Oct 30, 2026
8c941d6
fix(reporting): keep offering selection out of the focused client's e…
bokelley Oct 31, 2026
137353b
fix(reporting): honour cancellation in derived reads and keep only id…
bokelley Oct 31, 2026
37aa01e
Merge branch 'bokelley/reporting-subscription-dx-managed' into bokell…
bokelley Oct 31, 2026
d9e8b93
fix(reporting): report a failed authorized_at projection and test the…
bokelley Oct 31, 2026
d86316a
Merge branch 'bokelley/reporting-subscription-dx-grants' into bokelle…
bokelley Oct 31, 2026
ec1394c
test(reporting): the facade refuses a readback the seller altered
bokelley Oct 31, 2026
f8368e7
feat(reporting): one storage decision with versioned migrations
bokelley Oct 31, 2026
31949f4
test(reporting): use a valid consumer owner token in the storage faca…
bokelley Oct 31, 2026
af3c5e1
style(reporting): format the storage facade suite
bokelley Oct 31, 2026
f69c0f8
fix(reporting): the buyer consumer refuses process-local storage in p…
bokelley Oct 31, 2026
055f4df
fix(reporting): the buyer consumer refuses storage not declared durab…
bokelley Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/reporting-subscription-consumer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@adcp/sdk': minor
---

Buyer runtime and durable storage for AdCP 3.3 principal-level reporting subscriptions (experimental `media_buy.reporting_subscriptions`, #3504).

- `syncReportingSubscriptionsV1(client, subscriptions, options)` writes the complete `reporting_subscriptions` set through the guarded `sync_principal` lifecycle.
- It refuses, before writing, to deactivate an active subscription by omission unless `allowDeactivation: true`. Sending `active: false` is always allowed.
- It checks that every submitted subscription reads back at its version and activation state, and that every submitted configuration field is stored unchanged.
- `reportingSubscriptionVersionRecordsV1({ state, configurations? })` rebuilds the coverage function's version records from the readback (`accepted_at`, `prior_versions`, `deactivated_at`). Each version's configuration comes from the buyer's own records, or else from the readback: the current `configuration`, and `prior_versions[].configuration` from sellers on 3.3.0-beta.2 (adcp#8588). Pass the readback's `inactive_intervals` to `expectedSubscriptionPeriods` unchanged.
- `subscriptionExpectedReportingPeriodsV1(obligations, template)` turns owed obligations into the consumer runtime's `ExpectedReportingPeriod`s. The template must supply `coverage`; no default could match an obligation that reports packages or partial coverage.
- The reliable reporting consumer runtime accepts a per-account `subscriptionExpectations` hook. It reads the summary view's `subscription_coverage[]` before each reconciliation and reconciles the returned periods alongside configuration-derived ones.
- The hook's `now` is the summary's `ledger_as_of` minus `subscriptionExpectationGraceMs`, never the wall clock.
- Use it only against a seller that materializes subscription obligations in its ledger.
- `handleAuthenticatedPrincipalChanged` calls `onPrincipalChanged` (for example to re-read `get_principal` and `accounts_unauthorized` and replace the roster) and then reconciles every account of the consumer scope.
- It drops a redelivery with an already seen `idempotency_key`.
- Notifications that arrive during a run share one trailing run.
- `createReportingSubscriptionEngineStateStoreV1({ store })` persists the engine's sharded state on any `AdcpStateStore`: one CAS document per principal and one per (principal, account), listed per tenant. With `PostgresStateStore` it is durable and needs no migration beyond `ADCP_STATE_MIGRATION`. Subscription version history stays in the principal row and is never pruned, so `prior_versions` remain answerable for retained obligations.
13 changes: 13 additions & 0 deletions .changeset/reporting-subscription-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@adcp/sdk': minor
---

Add the buyer-computable coverage rule for AdCP 3.3 principal-level reporting subscriptions (experimental `media_buy.reporting_subscriptions`, #3504). `expectedSubscriptionPeriods(input)`, exported from `@adcp/sdk/reporting/consumer` and `@adcp/sdk/reporting/ledger`, is a pure function of the buyer's own records: subscription versions with their `accepted_at`, visible confirmed buys and their earlier flight values, the account's grants and timezone, the seller's published offerings (matching reads each offering's `standard_dimensions` and `unmapped_dimensions`), the readback's `inactive_intervals`, and the published `subscription_coverage` exceptions, including per-offering `gaps[]` and `timezone_cutovers[]` with `previous_timezone`. It returns every owed (buy, offering, period), and `groupExpectedSubscriptionPeriodsV1` groups them into the obligations that must carry them. It implements the start rule of adcp#8593 (the first boundary at or after the later of `accepted_at` and `authorized_at`, and every period ending after the buy's `confirmed_at`, so the period in which a buy is confirmed is owed in full), flight overlap as of each period's resolution, unclipped periods, the single cutover rule for new versions, deactivation, resolution changes and account timezone changes, revocation and re-grant with and without `backfill_on_regrant`, and exceptions that only remove periods.

Also exported: `createReportingPeriodCalendarV1`, the normative `period_generation` calendar for `utc`, `account_timezone`, `source_timezone` and `billing_cycle` schedules; `resolveReportingSubscriptionOfferingsV1` for `exact`, `finest` and `all` selector resolution with byte-order ties; `reportingSubscriptionScopeIncludesV1`; and `reportingConfigurationCapabilitiesV1`, which builds `configuration_tasks`, the deprecated `configuration_task` and the `subscriptions` block consistently.

`installConfiguration` now throws a typed `ReportingConfigurationImmutableError` (`code: 'REPORTING_CONFIGURATION_IMMUTABLE'`, with `accountId`, `deliveryConfigId` and `deliveryConfigVersion`) when an existing generation is reinstalled with different content. The message is unchanged; use `isReportingConfigurationImmutableError` instead of matching it.

The rules follow adcontextprotocol/adcp#8585. The fields it added after 3.3.0-beta.1 (`standard_dimensions`, `unmapped_dimensions`, `inactive_intervals`, `previous_timezone`, and per-offering `offering_id` on gaps and cutovers) are hand-modeled until 3.3.0-beta.2 ships. The cross-SDK parity table is `test/fixtures/reporting-subscriptions/coverage-cases.json`.

Sub-daily periods under a local alignment restart at each local midnight on wall-clock multiples (adcontextprotocol/adcp#8588). A gap yields no boundary and a fold one per offset, and the duration must divide 24 hours; mixed date and time components are refused. Calendars gain `next()`. An empty flight overlaps nothing, and a cutover carries the outgoing stretch's own start, so two cutovers in one period never owe a period twice. A billing anchor inside a DST fold is interval zero itself. A wire `authorized_at` equal to the chain start continues the chain. The walk has an iteration budget. The SDK's schema validation accepts dimension values outside the published `enums/reporting-dimension.json` vocabulary, which is extensible.
18 changes: 18 additions & 0 deletions .changeset/reporting-subscription-delivery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@adcp/sdk': minor
'@adcp/reporting-storage': minor
---

Managed delivery for AdCP 3.3 subscription obligations (experimental `media_buy.reporting_subscriptions`, #3504).

- `reportingSubscriptions.bindManagedGeneration` binds a managed subscription generation to its destination before the generation's first obligation is written. The planner calls it through `planSubscriptionObligations({ bindGeneration })`. A rejection writes nothing, and the next cycle tries again. A subscription that resolves a managed offering without a binding composition fails to plan.
- Every row of a subscription revision carries `account_id`. It is stamped before the revision is bound, and a row naming another account is refused. Account revisions are unchanged.
- `ReportingLedgerSubscriberV1.principal` scopes status subscribers: a subscription obligation's transitions reach only its owning principal.
- The engine gains `isAuthorized(principal, accountId)`, and `frozenOwedSets` versions carry the frozen `destination_method`. `withReportingSubscriptionCoverageV1` is exported for compositions.
- `@adcp/reporting-storage`:
- `createPostgresReliableReportingProductionService` delivers managed subscription obligations. Pass the principal runtime as `reportingSubscriptions.principal`; the engine must have `isAuthorized`.
- `createReportingSubscriptionManagedBindingV1` resolves the owner's current destination generation, authorizes it per account (`ensureDestinationAuthorization`), and installs a binding that freezes the version's `path_template`.
- The managed worker passes the expanded `delivery_path` to adapters. It re-checks the owner's account authority before delivering and before settling, and releases a lost claim with `holdMaterialization` without failing it. Reads are scoped with `resolveSubscriptionPrincipal`.
- `REPORTING_MANAGED_DELIVERY_MIGRATION` drops the three-column `adcp_reporting_managed_bindings` key, found by its columns, so bindings are keyed by their Core configuration. It is additive and idempotent.
- `reportingObjectWriteName` takes an optional delivery path that sits inside the generation prefix, and `isReportingObjectWriteName` recognizes it. The GCS adapter writes there, and status reads scope to the owner.
- Notification activity for a subscription obligation is fanned out only to its owning principal.
10 changes: 10 additions & 0 deletions .changeset/reporting-subscription-dx-advanced.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@adcp/sdk': minor
'@adcp/reporting-storage': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): the low-level building blocks move to a new `@adcp/sdk/reporting/advanced` subpath, tagged `@experimental`.

- `@adcp/sdk/reporting/advanced` exports the coverage engine (`createReportingSubscriptionEngineV1`) and its stores (`createInMemoryReportingSubscriptionEngineStoreV1`, `createReportingSubscriptionEngineStateStoreV1`), the coverage function (`expectedSubscriptionPeriods`, `groupExpectedSubscriptionPeriodsV1`, `reportingSubscriptionCoverageGrantedV1`), the period calendar, offering selection, the capability builder (`reportingConfigurationCapabilitiesV1`), the consumer bridge (`reportingSubscriptionVersionRecordsV1`, `subscriptionExpectedReportingPeriodsV1`), and the service's subscription hooks (`ReliableReportingSubscriptionsOptionsV1` and its input types, `withReportingSubscriptionCoverageV1`).
- `@adcp/sdk/reporting/service`, `/consumer` and `/ledger` no longer re-export them. Change the import path; the symbols are unchanged. The `reportingSubscriptions` option on `createReliableReportingService` is unchanged.
- `@adcp/reporting-storage` imports `withReportingSubscriptionCoverageV1` and `ReliableReportingSubscriptionBindingInputV1` from the new subpath, so it needs an `@adcp/sdk` that has it.
14 changes: 14 additions & 0 deletions .changeset/reporting-subscription-dx-buyer-facade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@adcp/sdk': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): the buyer write facade and the raw `syncPrincipal` guard.

- **`client.reporting`** (`ReportingClient`, on `AgentClient` and `SingleAgentClient`) offers `subscribe`, `unsubscribe`, `replaceSubscriptions`, `listSubscriptions` and `preview`.
- Writes are patch-shaped over the replace-shaped wire: other entries are kept byte for byte.
- `subscription_version` is optional. It is kept for in-place changes and bumped on a semantic change.
- `registerDestination` upserts a destination in the same write.
- **Pre-dispatch refusals.** A `ReportingSubscriptionError` with `dispatched: false` is thrown for: an implicit scope, an unregistered destination, a missing `{account_id}`, `SUBSCRIPTION_ID_CLASH`, `SUBSCRIPTION_OPERATOR_SCOPE_REQUIRED` and `SUBSCRIPTION_SELECTOR_UNMATCHED` (with `nearest`).
- **Retries and readback.** On `CONFLICT` the facade re-reads, re-merges and retries with a fresh key (3 attempts), then throws `SUBSCRIPTION_CONFLICT_EXHAUSTED`. A transport failure retries the same key and body. The readback is verified, and a mismatch throws `SUBSCRIPTION_READBACK_MISMATCH`. Names follow the Python SDK's `ReportingClient`; the preview result type is `SubscriptionPreviewResult`.
- **Raw `syncPrincipal` guard.** A raw `client.syncPrincipal` that submits `reporting_subscriptions` or `reporting_destinations` reads the principal once. It refuses, before sending, a write that would deactivate a subscription (`SUBSCRIPTION_WOULD_DEACTIVATE`) or revoke a destination (`DESTINATION_WOULD_REVOKE`) unless you pass `allowDeactivation` / `allowRevocation`. Dry runs, the lifecycle helpers and storyboard runs are not guarded.
- `guardSyncPrincipal` is exported, and `ReportingSubscriptionError` accepts a seller's wire code or a local `INVALID_REQUEST`.
10 changes: 10 additions & 0 deletions .changeset/reporting-subscription-dx-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@adcp/sdk': minor
---

Typed reporting-subscription errors (experimental `media_buy.reporting_subscriptions`, AdCP 3.3), using the shared error vocabulary (`error-codes.json` in the spec repository).

- `ReportingSubscriptionError` (package root) carries `code`, `recovery` and the vector context fields (`wouldDeactivate`, `wouldRevoke`, `deliveryConfigId`, `accountId`, `nearest`, `readback`). The five refusal codes the SDK raises before sending always carry `dispatched: false`. Its codes are `SUBSCRIPTION_WOULD_DEACTIVATE`, `DESTINATION_WOULD_REVOKE`, `SUBSCRIPTION_ID_CLASH`, `SUBSCRIPTION_OPERATOR_SCOPE_REQUIRED`, `SUBSCRIPTION_SELECTOR_UNMATCHED`, `SUBSCRIPTION_CONFLICT_EXHAUSTED` and `SUBSCRIPTION_READBACK_MISMATCH`. `syncReportingSubscriptionsV1` throws it for its deactivation guard (with `wouldDeactivate`), for conflict exhaustion and for a readback mismatch (with `readback`), where it used to throw `PrincipalLifecycleError`.
- `PrincipalLifecycleError` is now an `ADCPError`, with `code` (`PRINCIPAL_CONFLICT_EXHAUSTED`, `PRINCIPAL_TRANSPORT_FAILED`, `PRINCIPAL_SETUP_TIMEOUT`, ...), `recovery`, and `dispatched: false` when no `sync_principal` write was sent. `applied` is renamed `appliedResponse` so it can't be read as a boolean next to `dispatched`. `applied` stays as a deprecated alias, and the constructor still accepts it.
- `CoverageInputError` (`COVERAGE_INPUT_INCOMPLETE`, `COVERAGE_INPUT_INVALID`, `COVERAGE_BUDGET_EXCEEDED`, with a `detail` such as `missing_account_timezone` or `invalid_period_duration`) replaces the `TypeError` and `RangeError` the coverage function and the period calendar threw.
- The engine throws `ReportingSubscriptionEngineError` (`SUBSCRIPTION_STATE_CONFLICT`, `SUBSCRIPTION_STATE_CORRUPT`, `SUBSCRIPTION_ROSTER_INCOMPLETE`, `SUBSCRIPTION_HOST_FACT_INVALID`) at run time, and `PlatformConfigError` for configuration mistakes. `PlatformConfigError` gains `code` (`PLATFORM_CONFIG_INVALID` by default; `REPORTING_STORAGE_NOT_DURABLE` and `SUBSCRIPTIONS_REQUIRE_STANDARD_DIMENSIONS` where they apply), `configField` and `recovery: 'correctable'`.
7 changes: 7 additions & 0 deletions .changeset/reporting-subscription-dx-exclusions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@adcp/sdk': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): the engine writes per-offering exclusions.

When the offerings a condition covers have different period boundaries, the engine writes one `excluded[]` entry per offering, with `offering_id` and that offering's own cutover instants. This covers `capacity`, `excluded_by_buyer`, `action_required` and `overridden` across calendars, and a buy's `offering_unsupported` after a resolution change. Before, it wrote one account-wide entry cut at the latest end. One calendar, or several with the same schedule, still gets a single entry. A per-offering entry closes at its own offering's cutover. Callers served a release whose `excluded[]` has no `offering_id` get the entries folded back into one entry cut at the latest instant, as before.
10 changes: 10 additions & 0 deletions .changeset/reporting-subscription-dx-grants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@adcp/sdk': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): the grant journal is the one source of `authorized_at`.

- **Served value.** With the managed `subscriptions` block, the service serves the journal's `authorization.authorized_at` on `list_accounts` and `sync_accounts` to callers holding subscriptions. The derived roster reads the same value, so buyers read what coverage uses.
- **Journal rules.** A host value is adopted the first time a chain is observed, and an omitted one is stamped. A value that moves an open chain earlier is refused and logged as `authorized_at_regressed`. A re-grant inside `status_retention_days` continues the chain; a later one starts a new chain.
- **`service.grants.changed({ principal, accountId, at, authorized, allowedTasks?, timezone?, operator? })`** pushes a grant change for an exact instant.
- **Engine.** `engine.observeGrants(principal, observations)` exposes the journal (`@adcp/sdk/reporting/advanced`). `authorizationChanged` applies the chain rule to pushed re-grants. `resolveRevocationInstant` is optional: without it, a revocation is dated when it was observed.
19 changes: 19 additions & 0 deletions .changeset/reporting-subscription-dx-managed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@adcp/sdk': minor
'@adcp/reporting-storage': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): the managed seller path.

- `createReliableReportingService({ subscriptions: SubscriptionOptions })` enables the feature without assembling the engine by hand. The service:
- builds the coverage engine;
- installs the `reporting_subscriptions` section on the principal runtime (`subscriptions.principals`, created without `reportingSubscriptions`);
- fills in `capabilities.principal` in `install()`;
- reconciles a principal after each `sync_principal` write that submits subscriptions (`reconcileOnWrite`, on by default);
- sweeps every subscribed principal at the start of each `runCycle`.
- **Derived inputs.** The roster comes from the platform's `accounts.list` and the buys from `sales.getMediaBuys`, read under the principal's identity. `accounts`, `buys`, `buyOfferings`, `accountDeliveryConfigs`, `endAccountConfigs`, `isPeriodDelivered` and `resolvePrincipal` override them. `configure` is required.
- **Failures.** A roster that can't be read is a `PlatformConfigError` coded `SUBSCRIPTION_ROSTER_UNAVAILABLE`. Reconcile and planning failures are returned in `runCycle`'s `subscriptionPlanningFailures` and passed to `planningErrorHandler`; they are never thrown as an `AggregateError`.
- **Options.** The names follow the Python SDK's `SubscriptionOptions`. The advanced block with your own `engine` still works, as before.
- **Principal runtime.** The runtime gains `installReportingSubscriptions` (internal) and `PrincipalReportingSubscriptionSupport.onApplied`. Its `capabilities` reflects a section installed later.
- **Service.** `service.subscriptionWiring` exposes the resolved wiring to compositions.
- **`@adcp/reporting-storage`.** The production service accepts the managed block.
15 changes: 15 additions & 0 deletions .changeset/reporting-subscription-dx-naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@adcp/sdk': minor
'@adcp/reporting-storage': minor
---

Reporting subscriptions (experimental `media_buy.reporting_subscriptions`, AdCP 3.3): names frozen before beta.3. Every old name stays as a deprecated alias for one beta.

- `PrincipalRef { tenantId, principalId }` (package root, `@adcp/sdk/server`, `/reporting/advanced`, `/reporting/ledger`) is the one principal identity type. `PrincipalStoreScope` and `ReportingLedgerSubscriptionPrincipalV1` are deprecated aliases.
- "Owed" is the one word for what a seller must deliver (`@adcp/sdk/reporting/advanced`):
- `expectedSubscriptionPeriods` → `owedSubscriptionPeriodsV1`, which also gains the `V1` suffix the module's other low-level symbols carry;
- `groupExpectedSubscriptionPeriodsV1` → `groupOwedSubscriptionPeriodsV1`;
- `ExpectedSubscriptionPeriodV1`, `ExpectedSubscriptionObligationV1` and `ExpectedSubscriptionPeriodsInputV1` → `OwedSubscriptionPeriodV1`, `OwedSubscriptionObligationV1` and `OwedSubscriptionPeriodsInputV1`;
- `subscriptionExpectedReportingPeriodsV1` → `toExpectedReportingPeriodsV1`, which keeps "expected" because it feeds the consumer's `ExpectedReportingPeriod`.
- The principal runtime's `recordReportingSubscriptionCoverage` → `recordSubscriptionReadback`, because it writes readback lists, not `subscription_coverage`. The engine's `listPrincipals` → `listSubscriptionPrincipals`, which matches the service hook it feeds.
- `createReliableReportingService({ subscriptions })` and `createPostgresReliableReportingProductionService({ subscriptions })` replace `reportingSubscriptions`, which stays as a deprecated alias. Passing both is a `PlatformConfigError`.
Loading
Loading