feat!: cut 8.0-beta line — flip ADCP_VERSION to 3.1.0-beta.2 (foundation)#1902
Conversation
Foundation for the 8.0-beta line per docs/development/v8.0-beta-plan.md. Enters changesets pre-mode with tag 'beta'; subsequent releases publish as 8.0.0-beta.N under the @beta npm dist-tag until upstream AdCP 3.1 goes GA. Changes: - ADCP_VERSION file: 3.0.12 → 3.1.0-beta.2 - src/lib/version.ts regenerated via sync-version --force - scripts/sync-version.ts: extended buildCompatibleVersions() to accept 3.1.0-beta.x pins; retains 3.0.x GA compat through LAST_3_0_GA_PATCH=12 so an 8.0-beta SDK still talks to a 3.0-pinned seller per spec's open-enum wire compat. - schemas/cache/3.1.0-beta.2 synced as primary (cosign verified) - Regenerated types: tools, schemas, core, manifest (80 codes, 61 tools, 20 specialisms), enums, entity-hydration map - FORWARD_COMPAT_ERROR_CODES emptied — AUTH_MISSING/AUTH_INVALID/ AGENT_SUSPENDED/AGENT_BLOCKED are now in primary manifest-driven ErrorCodeValues. Compile-time disjointness assertion would fail if a code returned to the overlay after manifest absorption. - BuyerRetryPolicy.DEFAULT_CODE_POLICY: added 31 entries for new 3.1 codes (provenance, billing, format-projection, pixel-tracker, scope/permission, configuration, brand, idempotency-in-flight, credential-in-args, retention, etc.) - Mechanical undefined-guards in responses.ts, signals.ts, test-controller-bridge.ts, test-controller.ts What's red (separate follow-up PRs, listed in the changeset): - OutcomeMeasurement export removed (replaced by ?) - Brand object lost categories field (spec migration) - AssetVariant became a union with array - creative-asset shape changed (format_id/manifest) - ControllerScenario gained new enum values; exhaustiveness guard fails - Envelope status REQUIRED on auto-registered get_adcp_capabilities These are all per-feature spec migrations that map to needs:adcp-3.1 queue items; will land as separate PRs against this branch line.
…gence Addresses non-blocking review feedback on #1902: - code-reviewer: 'capability' missing from RetryDecision.escalate.reason union; format-projection + pixel-tracker codes were routed to 'commercial' with a TODO comment. - ad-tech-protocol-expert: divergence comment on the escalate union should enumerate the billing- and provenance-family escalations explicitly so future readers don't "fix" the apparent drift from spec recovery values. Changes: - Add 'capability' to the escalate.reason union with a comment listing the 7 format-projection and pixel-tracker codes that route to it. - Route the 7 codes from 'commercial' to 'capability'. Dashboards can now filter implementation-choice tickets from commercial-policy ones. - Extend the JSDoc on the existing reasons to enumerate billing + provenance escalations and the new SCOPE_INSUFFICIENT / READ_ONLY_SCOPE / etc. routes. - Note that FORMAT_DECLARATION_* codes are spec-advisory (emitted in errors[] on a 200-success), so the retry policy primarily fires when an adopter explicitly routes them; separate follow-up to revisit advisory-vs-error policy dispatch.
Expert review summary (all three: SHIP)code-reviewer — strategy is sound, hand-written surface is small and defensible. Two non-blocking should-fixes both addressed in commit 44b4538:
ad-tech-protocol-expert — wire-compat retention correct, overlay-empty rationale correct, retry-policy defaults mostly correct with two specific notes:
code-reviewer on #1903 — also SHIP, no issues. Dual-name re-export pattern is correct. CI statusStill 17 errors in this PR on its own (15 with #1903 stacked) — the 5 structural breaks listed in the PR body. Each will be its own focused follow-up PR. Foundation is ready for review; will mark "ready" once the remaining structural break PRs close out CI. Follow-ups filed
|
|
Acknowledged. Expert sign-offs recorded, CI state noted (17 errors → structural breaks tracked as follow-up PRs), #1904 filed for the advisory-vs-error dispatch question. Waiting on the structural break PRs to clear before the "ready" flip. Generated by Claude Code |
|
Fold suggestion from issue triage (#1904): before merging, consider adding JSDoc to the seven advisory-code entries in The note to add: these entries fire only when an adopter explicitly routes an advisory code (emitted in If scope expansion would slow review, a follow-up PR against Triaged by Claude Code. Session: https://claude.ai/code/session_0137SDkRk3WqYttGTdKJztLQ Generated by Claude Code |
…CENARIOS AdCP 3.1.0-beta.2 added query_upstream_traffic to ListScenariosSuccess['scenarios'] (spec PR adcp#3816 landed). Promote it from the open-extension literal-string path to a first-class CONTROLLER_SCENARIOS member. Resolves the test-controller.ts:210 exhaustiveness guard from the #1902 foundation sweep. Changes: - CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC added as first-class. - SCENARIO_MAP extended with the queryUpstreamTraffic → QUERY_UPSTREAM_TRAFFIC mapping (auto-advertised via scenariosFromStore, the canonical typed path). - Removed local QUERY_UPSTREAM_TRAFFIC_SCENARIO literal and the `as unknown as ComplyTestControllerResponse` cast at the dispatcher call site — UpstreamTrafficSuccess is now in the generated union. - Exhaustive-scenario test extended with queryUpstreamTraffic store method so the CONTROLLER_SCENARIOS / SCENARIO_MAP coverage invariant holds. 114/114 affected tests pass. Part of the #1902 8.0-beta sweep (2/5 structural breaks closed).
AdCP 3.1.0-beta.2 narrowed the governance_agents[] wire shape from
{url, categories?} to {url} only. Per-agent category signaling moved
out of band; the wire schema no longer carries categories.
Changes:
- src/lib/server/decisioning/account.ts: projectGovernanceAgent emits
{url} only; syncGovernanceRowToWire's inline projection mirrors.
- src/lib/server/responses.ts: stripGovernanceAgentSecrets drops the
categories preservation branch.
- test/lib/sync-governance-credential-strip.test.js: now asserts
categories is stripped (defense-in-depth with the existing
authentication.credentials strip). All 82 governance tests pass.
- src/lib/server/wire-spec-fields.generated.ts: regenerated against
3.1.0-beta.2 schemas (autogenerated).
Adopter migration: the SDK no longer emits categories on
governance_agents[]. Reading the field off the wire returns undefined.
Switch to whatever out-of-band channel the seller now uses for
per-agent category metadata.
Part of the #1902 8.0-beta sweep (3/5 structural breaks closed).
AdCP 3.1.0-beta.2 changed product_card from creative-agent-rendered
({format_id, manifest}) to a self-contained visual card ({image, title,
description, price_label, cta_label}). The card IS the preview now;
no creative-agent round-trip is required.
batchPreviewProducts rewritten to extract product_card.image?.url
directly. The creativeAgentClient + options parameters are retained
for signature compatibility (renamed to _-prefixed unused args)
and marked @deprecated; planned removal in 8.0 final or 9.0.
format_card / batchPreviewFormats are unchanged — only product_card
had this spec migration in 3.1.0-beta.2.
Adopter migration: existing calls keep working (PreviewResult[] still
populated with previewUrl from the new inline image.url). Direct
product.product_card?.image?.url access is the new recommended path.
Part of the #1902 8.0-beta sweep (5/5 structural breaks closed — CI
should now be green on the foundation stack).
…recated (#1903) AdCP 3.1.0-beta.2 renamed the OutcomeMeasurement interface to OutcomeMeasurementDeprecated to signal the surface is on the 4.0 removal track. Update compat.ts and the index.ts re-export to point at the new name, with the original name preserved as a re-export alias so existing adopter imports keep working. Part of the #1902 8.0-beta sweep (1/5 structural breaks).
…CENARIOS (#1905) AdCP 3.1.0-beta.2 added query_upstream_traffic to ListScenariosSuccess['scenarios'] (spec PR adcp#3816 landed). Promote it from the open-extension literal-string path to a first-class CONTROLLER_SCENARIOS member. Resolves the test-controller.ts:210 exhaustiveness guard from the #1902 foundation sweep. Changes: - CONTROLLER_SCENARIOS.QUERY_UPSTREAM_TRAFFIC added as first-class. - SCENARIO_MAP extended with the queryUpstreamTraffic → QUERY_UPSTREAM_TRAFFIC mapping (auto-advertised via scenariosFromStore, the canonical typed path). - Removed local QUERY_UPSTREAM_TRAFFIC_SCENARIO literal and the `as unknown as ComplyTestControllerResponse` cast at the dispatcher call site — UpstreamTrafficSuccess is now in the generated union. - Exhaustive-scenario test extended with queryUpstreamTraffic store method so the CONTROLLER_SCENARIOS / SCENARIO_MAP coverage invariant holds. 114/114 affected tests pass. Part of the #1902 8.0-beta sweep (2/5 structural breaks closed).
…on (#1906) AdCP 3.1.0-beta.2 narrowed the governance_agents[] wire shape from {url, categories?} to {url} only. Per-agent category signaling moved out of band; the wire schema no longer carries categories. Changes: - src/lib/server/decisioning/account.ts: projectGovernanceAgent emits {url} only; syncGovernanceRowToWire's inline projection mirrors. - src/lib/server/responses.ts: stripGovernanceAgentSecrets drops the categories preservation branch. - test/lib/sync-governance-credential-strip.test.js: now asserts categories is stripped (defense-in-depth with the existing authentication.credentials strip). All 82 governance tests pass. - src/lib/server/wire-spec-fields.generated.ts: regenerated against 3.1.0-beta.2 schemas (autogenerated). Adopter migration: the SDK no longer emits categories on governance_agents[]. Reading the field off the wire returns undefined. Switch to whatever out-of-band channel the seller now uses for per-agent category metadata. Part of the #1902 8.0-beta sweep (3/5 structural breaks closed).
… widening) (#1907) AdCP 3.1.0-beta.2 widened each creative_manifest.assets[asset_id] slot from AssetVariant to AssetVariant | AssetVariant[] so carousel cards, responsive_creative headlines, etc. can carry multiple assets per slot. Changes: - getAsset / requireAsset: when the slot is an array, return the first element. Preserves pre-3.1 behavior for single-asset callers. - New getAssetSlot(manifest, assetId, assetType): returns the full array (or single-element array if scalar), filtered by asset_type. Use for carousel / responsive_creative platforms. - 7 new tests pinning array-unwrap behavior on getAsset, the new getAssetSlot helper, and asset_type filtering. Resolves 3 of 3 AssetVariant compile errors in src/lib/server/decisioning/manifest-helpers.ts. Part of the #1902 8.0-beta sweep (4/5 structural breaks closed).
…sweep — CI green!) (#1908) * feat(preview-utils): adopt 3.1.0-beta.2 self-rendering product_card AdCP 3.1.0-beta.2 changed product_card from creative-agent-rendered ({format_id, manifest}) to a self-contained visual card ({image, title, description, price_label, cta_label}). The card IS the preview now; no creative-agent round-trip is required. batchPreviewProducts rewritten to extract product_card.image?.url directly. The creativeAgentClient + options parameters are retained for signature compatibility (renamed to _-prefixed unused args) and marked @deprecated; planned removal in 8.0 final or 9.0. format_card / batchPreviewFormats are unchanged — only product_card had this spec migration in 3.1.0-beta.2. Adopter migration: existing calls keep working (PreviewResult[] still populated with previewUrl from the new inline image.url). Direct product.product_card?.image?.url access is the new recommended path. Part of the #1902 8.0-beta sweep (5/5 structural breaks closed — CI should now be green on the foundation stack). * Merge bokelley/cut-8-0-beta: resolve wire-spec-fields timestamp conflict
| * array — every element must match `assetType`; mixed-type slots are | ||
| * the adopter's responsibility to discriminate per-element. | ||
| * | ||
| * Returns `undefined` if the slot is missing or empty. Returns `[]` if |
There was a problem hiding this comment.
JSDoc says "Returns undefined if the slot is missing or empty" but ![] is false in JS, so an empty array slot flows through to the filter and returns [], not undefined. either fix the doc to "returns undefined if the slot is missing; returns [] if the slot is empty or no elements match assetType", or add || arr.length === 0 to the !slot guard. behavior is probably fine since empty slots would be spec-invalid, just needs the docs to match the code.
| const major = Number(semverMatch[1]); | ||
| const minor = Number(semverMatch[2]); | ||
| const patch = Number(semverMatch[3]); | ||
| const prerelease = semverMatch[4]; |
There was a problem hiding this comment.
heads up: updatePackageJsonVersion lower in this file uses the same adcpVersion.split('.').map(Number) pattern without the prerelease guard, so '0-beta' becomes NaN at index 2. current destructuring only pulls [newMajor, newMinor] so it doesn't blow up today, but worth a comment on that block noting only major/minor are used and the NaN at patch position is intentional — otherwise the next person who adds a newPatch destructure will be surprised.
… T> (#1924) The Zod-from-TS post-processor annotates schemas that hit TypeScript's .d.ts serialization limit (TS7056) so the compiler stops trying to serialize the inferred shape. The previous bare z.ZodType annotation made z.input<typeof X> resolve to `unknown` — breaking AdcpToolMap[K]['params'] narrowing for any annotated request schema. Changes: - TS7056_SCHEMAS entries now carry an optional `tsType`. When present, the annotation uses the 2-type-param Zod v4 form `z.ZodType<T, T>` with `& Record<string, unknown>` widening to reflect runtime passthrough() semantics. z.input<...> resolves correctly; downstream destructures keep field types. - Auto-inject `import type { ... } from './tools.generated'` for the typed annotations. - Pre-emptively annotate 5 schemas that hit TS7056 on 3.1.0-beta.2 (PreviewCreativeRequest, UpdateMediaBuyRequest/Response, BuildCreativeResponse, SyncEventSourcesResponse). Annotation is harmless on the current 3.0.12 pin. - One-line cast at the withOptionalAccount(UpdateMediaBuyRequestSchema) call site so the helper's z.ZodObject<...> constraint is satisfied after annotation widening. Runtime shape unchanged. Why pre-emptive: the 8.0-beta cut (#1902) needs this codegen behavior to compile its dist/. Landing on main decouples it from the 8.0-beta foundation stack. Patch bump — purely codegen tooling; no wire change; no API change.
…al sweep) Foundation rebased onto main. Picks up: - Pre-mode entry (#1933) so foundation publishes under @beta - Codegen TS7056 fix (#1924) - Wholesale-feed-sync rename (#1932, replaces catalog-sync from earlier branch) - 3.1.0-beta.3 schema bundle (was beta.2 on the foundation branch) Resolutions: - .changeset/pre.json: took main's (initialVersions=8.0.0) - src/lib/version.ts: regenerated via sync-version --force - src/lib/types/schemas.generated.ts: regenerated - ADCP_VERSION: bumped 3.1.0-beta.2 → 3.1.0-beta.3 Demoted foundation changeset major → minor so the foundation merge publishes as 8.1.0-beta.0 (stays on 8.x line). The 9.0 major bump can come when v1 deprecation calendar tightens, not from this cut. Beta.3 spec changes folded in (partial): - RETENTION_EXPIRED removed from ErrorCodeValues; STALE_RESPONSE added. Updated DEFAULT_CODE_POLICY accordingly. - Envelope `status` REQUIRED on all responses — added 'failed' to two synthetic-error envelopes in response-unwrapper.ts. - ControllerError / SeedSuccess / wrapped store returns updated to carry envelope `status` (introduced wrapStoreSuccess helper in src/lib/server/test-controller.ts). - Type guards in src/lib/testing/test-controller.ts widened to handle the new union shape with envelope intersection. KNOWN ISSUE: ~45 more compile errors remain across decisioning/ adapter/middleware code from the envelope-status-required change. Each is mechanical (add `status: 'completed'` to a success-shape return) but the sweep is wider than this single merge commit. Pushed for visibility; follow-up PRs will sweep the remainder.
Closes the remaining 45+ compile errors from the beta.2 → beta.3 jump. The spec broadened the envelope-required-status change AND renamed several decision-shape fields to free `status` for `TaskStatus` at the envelope level. Envelope status added to: - src/lib/adapters/content-standards-adapter.ts - src/lib/adapters/governance-adapter.ts - src/lib/adapters/property-list-adapter.ts - src/lib/adapters/si-session-manager.ts - src/lib/core/GovernanceMiddleware.ts - src/lib/server/create-adcp-server.ts - src/lib/server/decisioning/account.ts - src/lib/server/decisioning/list-helpers.ts - src/lib/server/decisioning/runtime/from-platform.ts - src/lib/server/governance.ts - src/lib/server/responses.ts - src/lib/server/test-controller-bridge.ts - src/lib/core/GovernanceTypes.ts Field renames absorbed: - Governance decision `status: 'approved' | 'denied'` → `verdict` - ReportPlanOutcomeResponse `status` → `outcome_state` - Rights response `status: 'acquired'` → `rights_status` - NotificationConfig[] intersection collision resolved at the two account.ts call sites Subagent-executed mechanical sweep. Build:lib clean (0 errors).
…SE policy
Closes the BLOCK items flagged by code-reviewer + ad-tech-protocol-expert
on the post-rebase foundation:
Governance verdict rename (`CheckGovernanceResponse.status` → `verdict`):
- src/lib/testing/scenarios/governance.ts: 19 sites across 5 scenarios
(initial check, over-budget, geo, conditions+recheck, delivery+drift).
Renamed local `const status = data.status` → `const verdict = data.verdict`
and threaded through step.details log strings ("verdict=approved").
Plan-sync status sites (867/872/883) left untouched — different field.
- src/lib/testing/storyboard/context.ts:261: extractor `d?.status` → `d?.verdict`.
- src/lib/testing/stubs/governance-agent-stub.ts:288: stub emits `verdict: 'approved'`.
Outcome rename (`ReportPlanOutcomeResponse.status` → `outcome_state`):
- src/lib/testing/storyboard/context.ts:269: extractor → `d?.outcome_state`.
- src/lib/testing/stubs/governance-agent-stub.ts:315: stub emits `outcome_state: 'accepted'`.
Rights rename (`AcquireRights*.status: 'acquired'` → `rights_status`):
- src/lib/server/responses.ts:616 JSDoc autocomplete hint.
- src/lib/server/decisioning/specialisms/brand-rights.ts:85/91/99 JSDoc.
STALE_RESPONSE retry-policy correction:
- src/lib/utils/buyer-retry-policy.ts: was {action: 'mutate-and-retry',
reason: 'state'} (wrong — implies the payload should be re-fetched).
Now {action: 'escalate', escalateReason: 'terminal'}. Spec semantics:
STALE_RESPONSE is a non-fatal advisory paired with a populated success
payload — consume the payload, surface the advisory, do not retry.
- RetryDecision.escalate.reason JSDoc updated to enumerate STALE_RESPONSE
alongside CREDENTIAL_IN_ARGS in the 'terminal' branch.
Symmetric pattern with the witness-not-translator memory: stub and
consumer must speak the same wire vocabulary or scenarios silently
pass by reading undefined on both sides.
Build:lib clean (0 errors).
Addresses ad-tech-protocol-expert's must-fix on PR #1902. AdCP 3.1.0-beta.2 made envelope `status` REQUIRED. A 3.1-pinned SDK acting as a receiver against a 3.0.12 seller that omits envelope `status` would fail strict validation on every response — COMPATIBLE_ADCP_VERSIONS enumeration alone doesn't relax the validators. New helper: src/lib/utils/envelope-status-compat.ts - `injectLegacyEnvelopeStatus(response)` synthesizes envelope `status` ONLY for responses that declare themselves 3.0.x (adcp_version starting with "3.0", or adcp_major_version: 3 with no adcp_version, or no version fields at all). - `completed` when no top-level errors[] present, `failed` otherwise. - Never overwrites an existing truthy `status`. - 3.1+ responses that omit `status` still fail strict validation — the leniency is back-compat affordance, not permanent loosening. Wired into 5 validation paths: - response-unwrapper.ts:165 — unwrapProtocolResponse main path - response-unwrapper.ts:566 — isAdcpSuccess symmetric path - validation/schema-validator.ts:693 — Ajv validateResponse (applied BEFORE selectResponseVariant so synthesized `completed` doesn't misroute to the async-variant schema) - core/ResponseValidator.ts:327 — runtime ResponseValidator - testing/client.ts:672 + testing/storyboard/validations.ts:476 — storyboard/test-kit direct Zod paths Tests: test/lib/envelope-status-compat.test.js — 17 cases covering all required pins (3.0 → completed, 3.0+errors → failed, 3.1 unchanged, 3.1 with status unchanged, 3.0 with explicit status unchanged, no version → legacy) plus adcp_major_version: 3 legacy, foreign majors (4.0, 2.5), mutation-safety, and null/non-object guard. All pass. Subagent-executed sweep. Build:lib clean.
…r.ts (#1934) Release workflow on #1902 failed for two reasons: 1. Computed version was 9.0.0-beta.0 (next major bump from 8.0.0 in pre-mode). The 5 structural-break changesets from the 8.0-beta sweep (OutcomeMeasurement, ControllerScenario, governance categories, AssetVariant, product_card) were still `major` — only the foundation changeset got demoted before #1902 merged. Demote all 5 so the next release computes 8.1.0-beta.0 (stays on the 8.x line, per intent). 2. prettier --check fail on src/lib/server/test-controller.ts during the release commit's pre-push hook. Pure formatting drift from the wrapStoreSuccess sweep — `npm run format` resolves cleanly. Next release workflow run after this merges should produce a Release PR titled "chore: release package (beta)" cutting 8.1.0-beta.0 under @beta dist-tag.
There was a problem hiding this comment.
LGTM. Follow-ups noted below. The pre-mode foundation pattern is the right shape -- pin flip, type regen, overlay drain, and per-feature breaks folded into the cut as discrete changesets gives reviewers a real surface to read instead of one mega-diff.
Things I checked
- Largest-file rule. Read every non-generated TS file with >50 net lines:
envelope-status-compat.ts(new, 90L),buyer-retry-policy.ts(+89),forward-compat-error-codes.ts(rewritten to empty overlay),preview-utils.ts(rewrite, -112 net),response-unwrapper.ts(+17),responses.ts(+20 net),test-controller.ts(+17 net),manifest-helpers.ts(+31),compat.ts(+11),scripts/sync-version.ts(+28). Generated files skipped. - BuyerRetryPolicy grading vs spec recovery class. All 31 new entries trace correctly.
CREDENTIAL_IN_ARGS: terminal(buyer-retry-policy.ts:322) -- retrying would re-leak.IDEMPOTENCY_EXPIRED: idempotency_check_required(:196) -- natural-key check before fresh mint.IDEMPOTENCY_IN_FLIGHT: retrywith same key (:258) -- safe; seller replay window dedupes.PROVENANCE_VERIFIER_NOT_ACCEPTEDand_CLAIM_CONTRADICTEDroute tocommercial(:288-289); other provenance codes route tomutate-and-retry/validation.STALE_RESPONSE: terminal(:327) with the consume-payload, surface-advisory semantic. Newcapabilityreason on theescalateunion (:76) cleanly separates implementation-choice tickets from commercial. stripGovernanceAgentSecretsdefense-in-depth. Both v5 (responses.ts:768-784-- explicit url-only projection) and v6 (decisioning/account.tsviaprojectGovernanceAgent) project at the wire-emit seam. Closes the as-any smuggling path even for adopters writing raw JS.authentication.credentialsstrip onnotification_configs(responses.ts:111-133) covered by existing tests at four boundaries.SAFE_VERSIONregex. The anchor pattern atscripts/sync-version.ts:67has nomflag, so newlines cannot satisfy the end anchor. Newlines, NUL, quotes, backticks, backslashes, underscore, slash all rejected by the class. Template-injection-into-generated-version.tssealed.MAX_PATCH_ENUMERATION = 500defensive cap (:120) andLAST_3_0_GA_PATCH = 12(:132) at the right shape.FORWARD_COMPAT_ERROR_CODESempty-overlay disjointness.as const satisfieson an empty object compiles.ForwardCompatErrorCoderesolves tonever; the compile-time check aterror-codes.tswould fail closed if a code returned post-absorption.AssetVariantslot widening.getAssetandrequireAssetunwrap single-element arrays (manifest-helpers.ts:48-51, 99-100); newgetAssetSlotreturns the filtered array. Pre-3.1 single-asset callers keep working without code changes.product_cardself-rendering migration scope.batchPreviewProductsrewrite (preview-utils.ts:131-143) only touchesproduct_card;format_cardandbatchPreviewFormatscorrectly left alone -- only the product surface changed in beta.2.OutcomeMeasurementrename. Type alias toOutcomeMeasurementDeprecated(compat.ts:15, 22) plusindex.tsre-export -- wire shape unchanged, adopter imports preserved, deprecation visible at the new identifier.- No transport reimplementation. New code is post-unwrap object manipulation. Generated agent surface at
agents/index.generated.ts:155-178delegates toProtocolClient.callToolplusunwrapProtocolResponse. Official@a2a-js/sdkand@modelcontextprotocol/sdkpaths intact.
Follow-ups (non-blocking -- file as issues)
- L1 MCP error branch missing envelope
status.src/lib/utils/response-unwrapper.ts:315-323synthesizes anadcp_error-shaped envelope with no top-levelstatus, while the sibling synthetic envelope at:371correctly carriesstatus: 'failed' as const. Both are 3.1-strict-validator surfaces. Addstatus: 'failed' as constto the L1 raw-text fallback to match. wrapStoreSuccess'status' in resultbypass.src/lib/server/test-controller.ts:614-617-- thein-operator returns true when an adopter explicitly setsstatus: undefined, short-circuiting injection and producing an envelope with undefinedstatusthat fails wire validation. Match the truthiness check atenvelope-status-compat.ts:74(response.statustruthy, not justin).- Changeset coordination produces
9.0.0-beta.0, not8.x-beta.N. Six changesets in this PR:cut-8-0-beta.mdisminor(intentionally demoted in the merge commit), the other five (asset-variant-array-slots,governance-agents-categories-removed,outcome-measurement-rename,product-card-self-rendering,promote-query-upstream-traffic) aremajor. Under changesets pre-mode the highest bump wins per release, so the nextchangeset versionwill compute9.0.0-beta.0from the current8.0.0initialVersion-- undoing the merge-commit-documented "stays on the 8.x line" intent. Either demote the five follow-ups tominor(intra-beta surface churn, not first-publish-of-major bumps) or update the foundation plan plus thecut-8-0-beta.mdbody to acknowledge the line is actually 9.0-beta. - Envelope shim no-version-fields-to-legacy branch.
src/lib/utils/envelope-status-compat.ts:50-51grantsstatussynthesis to ANY version-less payload. A buggy or compromised 3.1 peer that omits bothadcp_versionandadcp_major_versiongets the leniency and passes strict 3.1 envelope validation. Tighten to strict-reject once 3.1 GA ships (or now -- the SDK's own server stampsadcp_version, and a peer that does not is already drifting from spec). Witness-not-translator points the same direction. - Shim is blind to legacy
task_statusandresponse_status. Per the spec note attools.generated.ts:1151("Agents MUST NOT emit the legacytask_statusorresponse_statusfields alongside this field"), a 3.0.x payload carryingtask_status: 'submitted'with no top-levelstatusgets stampedcompletedby the synthesizer. ExtendisLegacy30xPayloadandinjectLegacyEnvelopeStatusto read the legacy fields before defaulting tocompletedorfailed. - Changeset body cites
3.1.0-beta.2; actual pin is3.1.0-beta.3..changeset/cut-8-0-beta.md:5,7,17will land in CHANGELOG.md verbatim once the Release PR runs. Update before cut. schema-validator.tsv2-vs-v3 leakage.validation/schema-validator.ts:695-711runsinjectLegacyEnvelopeStatusbefore theisV2Bundlegate, so a v2.x seller that omits bothadcp_versionandadcp_major_versionlands in the no-version-fields-to-legacy branch and getsstatus: 'completed'injected. Mostly harmless because v2 schemas areadditionalProperties: true, but cross-version leakage is a code smell. Gate onparseAdcpMajorVersion(version) === 3 || version == null.
Minor nits (non-blocking)
testing/client.ts:676-679does not strip_messagepre-inject. Other call sites do (response-unwrapper.ts:162-166,storyboard/validations.ts:471-482). Low-risk because raw wire data here typically does not carry_message, but the inconsistency is worth a one-line comment or a symmetric strip.- A2A extraction-layer divergence.
core/ResponseValidator.ts:301-309readsartifacts[0].parts[0].data(FIRST artifact, FIRST data part); the canonical unwrapper atresponse-unwrapper.ts:508-520reads LAST of both (conversational-protocol semantics). Pre-existed this PR; the new shim amplifies the divergence -- missingstatusmay be caught on one extractor but not the other. Worth a separate cleanup PR. responses.ts:178capabilitiesResponsespread order. Spreadingdatafirst andstatus: 'completed'last overwrites any adopter-setstatussilently. TodayGetAdCPCapabilitiesResponsedoes not carrystatuson its typed surface so this is theoretical, but spreadingstatusfirst is the safer default since the auto-registered handler atcreate-adcp-server.ts:5048already stamps it upstream -- making the in-builder stamp a backstop, not the source of truth.buyer-retry-policy.ts:77doc-comment listsRETENTION_EXPIREDas aterminalexample, butRETENTION_EXPIREDis not inDEFAULT_CODE_POLICYand theErrorCodemanifest dropped it in beta.3 (STALE_RESPONSEreplaced it). Either re-add the code if the spec still emits it, or drop the reference so a copy-paste into an override does not land on a stale code.enforceMapCapis exported but not enforced. TheTestControllerStoreinterface JSDoc names it in the example but does not require it. An adopter who forgets the cap in their customforceAccountStatusis vulnerable to a sandbox caller looping over fresh IDs. Consider aBoundedSessionMapwrapper class for adopters, or strengthen the per-method JSDoc on the interface.envelope-status-compat.ts:67generic constraint lies on the null edge.<T extends Record<string, unknown>>plus thereturn responseon non-object input meansinjectLegacyEnvelopeStatus(null)returnsnulltyped asT. Runtime fine; type is sloppy. Widen the signature toT | null | undefinedor add a runtime-guard JSDoc note.
Approving on the strength of the V2/V3 ergonomic strategy plus the clean overlay-drain pattern. The forward-compat overlay plus manifest absorption plus compile-time disjointness check is the textbook ratchet -- that part is worth borrowing into adjacent SDKs.
Both spotted by @nastassiafulconis during the 8.0-beta foundation cut and acknowledged at the time; landing now as a small cleanup. - `getAssetSlot` (`src/lib/server/decisioning/manifest-helpers.ts`): JSDoc said "Returns undefined if the slot is missing or empty" but `![]` is falsy, so an empty array slot passes the guard and returns `[]` from the filter. Behavior is correct — returning `[]` for an empty slot is consistent with the filter contract and empty arrays are spec-invalid per `minItems: 1` anyway. Updated the JSDoc. - `updatePackageJsonVersion` (`scripts/sync-version.ts`): `adcpVersion.split('.').map(Number)` yields NaN at index 2+ for prereleases like `'3.1.0-beta.3'`. Today only [major, minor] are destructured so the NaN is intentionally discarded — comment added so the next person to add `newPatch` doesn't get bitten. Empty changeset — docs/comments only, no behavior change.
Draft. Foundation for the 8.0-beta line per the v8.0-beta plan doc. CI will be red on 17 structural compile errors that map to separate follow-up PRs — listed below.
What's in the foundation
What's red (17 structural compile errors, separate PRs)
Each maps to a per-feature spec migration:
Plus from the beta.2 release notes, not yet implemented:
Why merge this draft now
This is the foundation. Every `needs:adcp-3.1` queue item now becomes a per-feature PR against this branch line, exactly as the v8.0-beta plan predicted. Without this foundation merged, every follow-up PR carries the pin-flip + type regen as duplicated work.
Recommended sequencing:
Verification
References
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com