Repository navigation
feat(reporting): conform the subscription engine to the AdCP 3.3.0-beta.3 rules - #3560
Conversation
…tion surface Replace the 3.3.0-beta.2 pin with the signed 3.3.0-beta.3 bundle (spec commit a6106149, sha256 ba61919c..., cosign-verified). The SDK now advertises and negotiates only 3.3-beta.3; beta.2 peers fall back to a shared stable release. - Regenerate types, Zod schemas, manifest constants, wire-spec fields and docs. - Delete the hand-modelled reporting wire types (wire.ts) and every TODO(3.3.0-beta.2): the generated types carry standard_dimensions, inactive_intervals, prior_versions[].configuration, previous_timezone, per-offering offering_id and status inactive with deactivated_at. - authorization.regranted_at joins the account authorization wire keys; toWireAccount and windDownGrant keep it. - Serve the readback totals (prior_versions_count, accounts_with_exceptions_count, accounts_unauthorized_count) and cap prior_versions at 16 inline (adcp#8619), gated by the served bundle like the other settled fields. The gate follows local $refs and treats an absent served release as the pin, as Python does. - Re-pin the canonical coverage vectors to v3.3.0-beta.3; the nine cases the engine does not satisfy yet are named expected failures. - Codegen: fold composed members of a referenced proposal arm so the submitted arm keeps status submitted and task_id, and restore envelope-inherited Zod constraints through core $refs. - Commercial-terms semantics: beta.3 carries the beta.2 contracts. - creative.staged_update_changed joins ACCOUNT_NOTIFICATION_TYPES. - Tests follow the beta.3 contracts (status on every 3.3 response, account request money terms, seeded canonical-format products) and read ADCP_VERSION. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tures for beta.3 A terminal task webhook's result is the task's full response, so on 3.1+ it carries the task status like a synchronous one; a media-buy lifecycle value in status moves to media_buy_status as on the synchronous path. 3.2 list_products/decline_proposals and 3.0 results are left as returned. Give the multi-tenant example's governance responses their now-required status, and make the async discovery and webhook fixtures schema-complete. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ta.3 rules The coverage function, engine, principal runtime, ledger and managed delivery follow beta.3; all 120 canonical coverage vectors pass and the expected failures are removed. - adcp#8617: cancellation_effective_at ends the flight; a billing cutover owes the [E, first boundary) stub, carried as stub: true to the wire obligation. - adcp#8618: flight_history replaces the non-wire prior_flights; regranted_at replaces the host fact's granted_at, and the grant journal serves it on list_accounts and sync_accounts; the managed path reads each package's offering snapshot and cancellation_effective_at from get_media_buys. - adcp#8619: sticky per-package resolution (resolveReportingSubscriptionPackageV1); admission ordered by authorized_at then account_id; several pacing/analytics feeds per purpose with package-level overridden exceptions; billing must be match exact with dimensions; destination_overrides validated, read back and routed by the managed binder; in-place membership edits recorded as excluded_by_buyer intervals; get_principal reporting_subscription_page; partial grants refused with SCOPE_INSUFFICIENT; managed offerings must key rows by account_id. - adcp#8620: accounts.ensure outcomes carry resolution, effective_at and not_carried_over. - adcp#8622: the day start is always a sub-daily boundary; reactivation resumes no earlier than the end of the last period owed before deactivation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
The reviewer verified commit eeeb370 (~1530 insertions across 39 files, diff reconstructed from git) and found no blocking or Medium findings that survived verification.
Checks performed:
- coverage.ts billing stub (L344-392) traced against the main walk — no double-count; adjacent-billing-regime cutover edge (L1099
cutoverEnd) resumes atceil(H)adjacent to, not overlapping, the prior stub. - engine.ts admission rewrite (L1143-1165) and
overriddenPackagescoherent;observeGrantsregranted_at chain-continuation (L1858+) uses host value within(revocation, now]. - Changeset correctness:
minorfor both packages, correct policy for@experimentalreporting surfaces even with documented breaking removals (prior_flights, granted_at→regranted_at). Witness-faithful; official transport clients untouched. - 120 canonical vectors plus new unit tests cover the new branches.
Decision-table walk: no critical/high findings (row 1 n/a); gated_paths is false (row 2 n/a); high_risk is true but the only reason is bin/adcp.js (modified) with no medium finding on it (rows 3/5 n/a, no deletions); no medium data-loss/schema/infra findings (row 4 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 since gated_paths is false, no hard gate applies.
…porting-subscription-beta3-engine
The merge-base changed after approval.
…it merges (#3565) #3559/#3560 (AdCP 3.3.0-beta.3) and #3564 (creative package-fit decisions) landed in parallel. #3564's test pinned adcpVersion 3.3.0-beta.2, which the SDK no longer bundles, and its published guide put the package one file over #3559's exact file-count budget. Read ADCP_VERSION in the test, count the guide, and raise the packed cap to cover the combined size plus the release PR's CHANGELOG growth. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…adroom (#3568) * fix(packaging): stop shipping duplicate compliance and declaration bytes Trim @adcp/sdk from 26,862,556 to 24,993,401 packed bytes (8,529 to 8,176 files) without changing runtime behavior: - Exclude compliance/cache/*/domains/ from the package. Every bundle's domains/ is a byte-identical copy of protocols/, the only directory the storyboard loader reads (-286,556 bytes, -353 files). - Emit one-line ESM facades for the type-only generated declarations (types/core.generated, types/tools.generated, registry/types.generated), as types/schemas.generated already did, instead of copying 5.7 MB of declarations (-1,174,602 bytes). - Store the self-contained MCP tools/list request projections as compact JSON. They are only ever JSON.parse'd (-407,997 bytes). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci(packaging): size budgets with headroom and a per-PR growth check The packed-byte and file-count budgets were pinned to the measurement, so two PRs that each added a file or a few KB passed alone and broke main together (#3510 + #3541, #3564 + #3559/#3560). Replace the pins with: - Ceilings with headroom in scripts/package-size-budget.json: @adcp/sdk 26,000,000 packed bytes / 135,000,000 unpacked / 8,450 files against a measured 24,993,401 / 129,047,083 / 8,176 (about 4%), and @adcp/reporting-storage 490,000 / 2,250,000 / 150. A warning fires when less than two PRs' allowance of headroom is left. - A per-PR growth check. main caches each commit's measurement; a pull request compares itself with the record for its exact merge base and may add 100 KB / 25 files to the SDK (15 KB / 8 files to the storage package). More needs the same PR to raise the ceiling, so the growth is reviewed in the diff. A missing record skips only this comparison. - The focused-client eager graph count becomes a ceiling (230, measured 216) instead of an exact pin; the named heavy-module exclusions stay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
T1(b) of the reporting-subscriptions DX plan (#3504): the coverage function, engine, principal runtime, ledger and managed delivery conform to the AdCP 3.3.0-beta.3 rules. All 120 canonical coverage vectors pass, and the 9 expected failures from #3559 are removed.
This PR stacks on #3559 (
bokelley/reporting-subscription-beta3, the signed beta.3 adoption), which stacks on #3557 and the rest of the #3504 stack. It targetsmainso CI runs, so the diff againstmainincludes everything below it.Review only
eeeb370d(on top of4e881b08).Each beta.3 rule and where it landed
cancellation_effective_atends the flightcoverage.tsflightTimeline; managedtoBuyreads it fromget_media_buys[E, first boundary),stub: truecoverage.tsmain walk viaincomingHandoff; rows andgroupOwedSubscriptionPeriodsV1; ledgerReportingSubscriptionLedgerObligationInputV1/ReportingLedgerObligationV1stub;handler.tswireObligation;service/index.tsplan mappingflight_historyreplaces the non-wireprior_flightscoverage.ts(ReportingSubscriptionFlightHistoryEntryV1); managedtoBuyregranted_atas the wire source for re-grantsgranted_atrenamed toregranted_at); grant journalobserveGrantsreturns{authorized_at, regranted_at?}and adopts a host value inside(revocation, now]; managedprojectAccountsserves it onlist_accounts/sync_accounts; managed roster passes it to factstoBuyusespackages[].reporting_delivery_offering_idsfromget_media_buys(falls back to all registered offerings when absent)available_from/withdrawn_at)ReportingSubscriptionOfferingV1 = ReportingDeliveryOffering)selection.tsresolveReportingSubscriptionPackageV1, shared by coverage (packageResolutionAt) and the engine (resolvePackage)authorized_at, thenaccount_id, stickyexclude_account_idsare not candidatessubscription_idowesassertNoSameLevelOverlap(billing only); engineoverriddenPackages(package-leveloverriddenwithpackage_ids/overridden_by); coverage honours package-level exceptionsmatch: exact+dimensionsvalidateReportingSubscription(schema-enforced too)destination_overridesdestination_override_refs(gated);reportingSubscriptionDestinationForAccount; binder inputdestination;@adcp/reporting-storagebinder binds the override'sdestination_refand templatemembershipEditson the version; enginemembershipOf, givingexcluded_by_buyerfrom the edit's E, closing at the re-add's E, and a closed "joined" entry; removed accounts stay listed while open; coverage reads membership fromexcluded_by_buyerentries when published; buyer facade auto-versioningget_principalreporting_subscription_page(cursor,max_results≤ 100,total_count); full lists kept and served 1,000 inline with totals;onReadbackOverflowdeprecated (never called)SCOPE_INSUFFICIENTassertReportingTaskGrantedV1; SDK servicesync_reporting_status, storage servicesync_reporting_receiptsaccount_idinprimary_keysmatched/created,effective_at,not_carried_overaccounts.ensureAccountReadycarriesresolution,effective_atandnot_carried_over(money terms onaccount); seller responses are validated by the beta.3 schema (fixtures in #3559)calendar.tssub-daily local calendar: the first instant of each local date is a boundarycoverage.tsbuildRegimesresumes+incomingHandoffbilling_cycleNon-wire inputs removed
ReportingSubscriptionBuyV1.prior_flightsandReportingSubscriptionPriorFlightV1are replaced by the wireflight_history.ReportingSubscriptionAccountFactV1.granted_atbecomes the wireregranted_at.Canonical vectors
a6106149a2b5c929eec0caa0042adbae4ccf3a9c(tagv3.3.0-beta.3);vectors.jsonsha256 is56cc381bc336b31cb0abc589d73d0f1a8ecd52f5c42532fc47fb9112c3b594a4.stubon rows and obligations.Deviations and spec ambiguities
error-codes.jsonhas no code for theaccount_idprimary-key rule, so the refusal uses the genericPLATFORM_CONFIG_INVALIDuntil the spec adds one.SCOPE_INSUFFICIENTscope. It relies on the host's resolver returning the account with itsauthorization. A resolver that refuses the task itself still answers as unknown.destination_overridesitems are empty objects (ananyOf/not/allOfcodegen gap), so the principal module uses a local structural type. Follow-up: fix codegen.grant-cases.jsonrenamesgranted_attoregranted_at, and the Python copy needs the same rename.buyRevisions. These internal snapshots are kept for package add/remove and for a host that changes a snapshot it shouldn't.Checks
typecheck,typecheck:reporting-storage,lint:boundaries, and Prettier on the changed files all pass.test:node:fast: 24,410 tests, 0 failures.test:node:slowand the@adcp/reporting-storagesuite also pass (177 passed, 1 skipped). PostgreSQL cases run in CI.check:package-size: 26,838,560 bytes packed, 8,528 files, within the budgets set in feat: adopt signed AdCP 3.3.0-beta.3 and serve the reporting-subscription surface #3559.@adcp/reporting-storagepacks to 447,061 bytes.@adcp/sdkand@adcp/reporting-storage.account_idprimary keySCOPE_INSUFFICIENT🤖 Generated with Claude Code