Repository navigation
Commit 58e825a
feat(reporting): compute owed reporting-subscription periods and their capabilities (#1696)
* feat(reporting): compute owed reporting-subscription periods and their capabilities
Add adcp.reporting.subscriptions, the shared half of AdCP 3.3 principal-level
reporting subscriptions (media_buy.reporting_subscriptions):
- expected_subscription_periods(): the coverage rule from
core/reporting-subscription.json as a pure function over plain data. It
owes each confirmed buy and resolved offering the complete periods that
start at or after the latest of the version's accepted_at, the buy's
confirmed_at and the account's authorized_at, and that overlap the flight
as of each period's scope_resolved_at. It applies cutovers for versions,
deactivation, resolution changes and account timezone changes, removes
exceptions and gaps over [from, until), and handles revocation and
re-grant with and without backfill_on_regrant. Results are
ExpectedReportingPeriod values, so they feed evaluate_reporting_ledger and
the consumer-status planner unchanged.
- resolve_subscription_offerings(): exact / finest / all selection with ties
to the lowest offering_id in byte order.
- subscription_capabilities() and advertise_reporting_subscriptions():
configuration_tasks, the deprecated configuration_task only when
sync_accounts is listed, the subscriptions block, the feature id and the
principal section.
Civil-time schedules carry the normative local-midnight origin explicitly, so
they do not depend on the ledger's legacy anchor default.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reporting): derive subscription periods exactly as the TypeScript SDK does
Align expected_subscription_periods with adcontextprotocol/adcp-client#3505 so
that both SDKs owe identical periods. The spec leaves each of these choices
open:
- A dedicated period calendar. Years, months and days are local civil
arithmetic with the day clamped from the origin. Hours, minutes and seconds
are exact elapsed time. Calendar months (P1M billing cycles) are supported,
and numeric UTC offsets are refused.
- Cutovers are paired per buy over the union of its packages. A continuing
offering hands off at E of its own outgoing calendar. A newly resolved one
starts at the latest E of everything the previous regime resolved.
- A period_timezone override moves account_timezone and account-resolved
schedules into its zone, while utc and fixed-zone schedules match only
their own zone.
- Versions may carry in-place inactive intervals and their destination
method. The coverage entry's timezone_cutovers are applied.
- A re-grant continues authority only before unauthorized_at plus
status_retention_days, and a re-grant without retention is an error.
The TypeScript SDK's 46-case table is vendored and run as a parity test.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(reporting): adopt the reporting-subscription rules settled in adcp#8585
adcontextprotocol/adcp#8585 settles the ambiguities both SDKs hit.
- Offerings publish standard_dimensions and unmapped_dimensions.
SubscriptionOffering.from_offering reads them from the wire, and sellers use
with_subscription_dimensions() to publish them.
subscription_capabilities() refuses an offering without them.
- Coverage consumes the readback's inactive_intervals.
- timezone_cutovers[] carry previous_timezone and an optional offering_id, and
gaps[] carry an optional offering_id. An entry with an offering_id applies to
that offering only, and previous_timezone supplies the zone before the first
cutover.
- period_timezone follows the normative text: a utc offering matches only UTC
or Etc/UTC, a source_timezone offering only its published zone (never an
account-resolved one), and an account_timezone offering takes the override.
The beta.1 models close these objects, so _reporting_subscription_preview adds
the fields to the generated classes until a 3.3.0-beta.2 bundle carries them.
The vendored TypeScript parity table keeps a scope valid under maxProperties 1
in three cases, matching the fix pending upstream.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reporting): take each offering's prior zone from its own timezone cutover
Follow adcp#8585: the account's timezone is its current zone, as list_accounts
shows it. Before a timezone change, an offering's account-timezone periods use
that change's previous_timezone, considering only the changes that apply to
the offering. An offering with no applicable change uses the current zone
throughout. A timezone change without previous_timezone is now refused, and so
is a wire offering without standard_dimensions.
Previously the first cutover's previous_timezone became the base zone for
every offering, so a cutover scoped to one offering could shift another
offering's boundaries.
Re-vendor the TypeScript parity table at f8b2fddd (54 cases, with the scope
fix now upstream).
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(reporting): serve adcp#8585 subscription fields only to 3.3.0-beta.2 callers
The server validates responses against the release it serves, and the pinned
3.3.0-beta.1 schemas close the objects adcp#8585 extends. The new wire module
gates the new fields on 3.3.0-beta.2 or later, as the TypeScript SDK does:
- standard_dimensions and unmapped_dimensions on offerings;
- inactive_intervals on the readback;
- previous_timezone and offering_id on timezone_cutovers[] and gaps[].
For older callers, per-offering gaps collapse into one account-wide gap
spanning them, and per-offering cutovers into one entry at the latest
effective_at.
The parity test now also validates every subscription in the TypeScript table
against core/reporting-subscription.json.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reporting): address the coverage-rule review and adopt adcp#8588 periods
Fixes from the expert review of the coverage rule:
- An empty flight (stopped at or before it started) overlaps no period, so a
buy canceled before its flight owes nothing.
- Each regime carries its per-offering handoff. E for a continuing or new
offering is the latest of the outgoing calendar's boundary and the outgoing
regime's own handoff, so two cutovers inside one period never owe a period
twice. A timezone change passed at T or at its E now gives the same result.
- The extensible reporting-dimension vocabulary parses on selectors, offerings
and the capability block; an unknown selector value matches nothing.
- Zone names must be IANA zones (posixrules and right/... are refused), and
offerings with a year or month delivery_sla are refused at construction.
- The beta.1 projection keeps distinct timezone changes distinct and drops
prior_versions[].configuration. The feature id is refused without the
principal section. Stale docstrings are corrected.
adcp#8588 (pending for 3.3.0-beta.2):
- Sub-daily periods under a local alignment restart at each local midnight on
wall-clock multiples, so a DST day has 23 or 25 hourly periods. The duration
must divide 24 hours and cannot mix date and time components.
- prior_versions[].configuration is consumed, and
require_prior_configurations makes a missing one an error. A version
accepted while inactive has an empty window.
The parity runner now checks row order, expected_obligations and the error
class.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reporting): accept undated continuation re-grants and re-vendor the parity table
Re-vendor the TypeScript coverage table at a99597a4: 79 cases, adding
- empty flights;
- two cutovers inside one period;
- sub-daily local periods;
- DST gaps and folds at midnight;
- a skipped civil date;
- PT24H versus P1D;
- month-end clamps;
- three-grant chains;
- simultaneous changes;
- unknown dimensions.
All pass under the strict runner.
Match TS on one rule the table pins: a grant whose authorized_at is at or
before the previous loss continues the chain at an unknown instant. This
happens because adcp#8585 keeps authorized_at at the chain start after a
re-grant inside retention. The buyer removes no gap of its own; the seller's
gaps[] decide. Grants are read in input order.
The schema check accepts dimension values outside the beta.1 enum, because
the vocabulary is x-extensible.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(reporting): owe the period containing confirmed_at in full, per adcp#8593
Owner decision for 3.3.0-beta.2:
- The start bound is the first boundary at or after the later of the
version's accepted_at and the account's authorized_at.
- A buy is then owed every period that ends after its confirmed_at, so the
confirmation period is owed in full.
- An empty flight still owes nothing.
The rule lives in the one function that holds it, _first_owed_start.
Re-vendor the TypeScript parity table at f8b48c0c. It has 81 cases,
conformed to the same decision, and adds an ASAP buy confirmed mid-day and a
monthly billing buy confirmed mid-month. All pass under the strict runner.
Refs #1695
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>1 parent 50fe9e8 commit 58e825a
14 files changed
Lines changed: 18343 additions & 0 deletions
File tree
- src/adcp
- reporting
- subscriptions
- types
- tests
- fixtures/reporting_subscriptions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
161 | 161 | | |
162 | 162 | | |
163 | 163 | | |
| 164 | + | |
164 | 165 | | |
165 | 166 | | |
166 | 167 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
0 commit comments