v5.13.0
Minor Changes
-
be0d60b: Envelope hygiene: colocate the two error-envelope allowlists into a
single source of truth, and flipwrapEnvelopeto a fail-closed default
for unregistered error codes.Security-review follow-ups from #788 (M3 + M4):
- #800 (M4):
ERROR_ENVELOPE_FIELD_ALLOWLIST(sibling-keys allowlist
used bywrapEnvelope) and the formerCONFLICT_ALLOWED_ENVELOPE_KEYS
(inside-adcp_error allowlist used by the
idempotency.conflict_no_payload_leakinvariant) now live side-by-side
in the newsrc/lib/server/envelope-allowlist.tsmodule. The latter
is renamed toCONFLICT_ADCP_ERROR_ALLOWLISTto make the "keys inside
the adcp_error block" scope obvious. Both are exported from
@adcp/client/serverso callers with custom error envelopes can
inspect / extend the sets. - #799 (M3):
wrapEnvelopenow fails closed on unregistered error
codes. A code with no explicit entry inERROR_ENVELOPE_FIELD_ALLOWLIST
usesDEFAULT_ERROR_ENVELOPE_FIELDS—contextonly — instead of
inheriting success-envelope semantics. Sellers that wantreplayed
oroperation_idon a bespoke error code must register it explicitly.
The fail-closed posture matches the framework's own internal behavior:
create-adcp-server.tserror paths only ever echocontextvia
finalize();injectReplayedis never called on error responses.
Who is affected: consumers calling
wrapEnvelopewith an
adcp_error.codeother thanIDEMPOTENCY_CONFLICT(the only code
registered today) AND relying onreplayedoroperation_idto
round-trip. On upgrade, those fields silently drop — onlycontext
echoes.IDEMPOTENCY_CONFLICTis unchanged.Upgrade path: for bespoke error codes that genuinely need
replayedoroperation_idon the envelope, build the envelope
directly instead of callingwrapEnvelope, or open an issue so the
code can be added toERROR_ENVELOPE_FIELD_ALLOWLIST. The allowlist
is intentionally frozen at the module level — extending it requires a
spec-and-SDK conversation, not a local override.Breaking change (minor —
wrapEnvelopewas just shipped in 5.11.0):
narrow external surface, days-old on npm. - #800 (M4):
-
e5ef1be: Pin to AdCP 3.0.0 GA.
ADCP_VERSIONflips from the rollinglatestalias to the published
3.0.0release. Generated types, Zod schemas, compliance storyboards,
andschemas-data/are now locked to the 3.0.0 registry instead of
tracking whatever the registry serves next.COMPATIBLE_ADCP_VERSIONS
adds'3.0.0'alongside the existingv3alias and the beta.1 /
beta.3 wire-compat entries so mixed-version traffic keeps working.Supply-chain: the 3.0.0 tarball is cosign-verified against
adcontextprotocol/adcp's release workflow OIDC identity, which is a
stricter trust boundary than the checksum-onlylatestalias used
before.Side effects of the pin:
validate_property_deliveryresponse now uses its generated
ValidatePropertyDeliveryResponseSchema(upstream shipped the
registry entry in 3.0.0 GA). The schema requireslist_id,
summary,results, andvalidated_at;compliantis optional.
The previous hand-written stub accepted a bare{compliant}OR a
bare{errors}fallback; the{errors}branch is gone — error
responses now flow through the protocol's async error channel
rather than the response body. Callers readingcompliantstill
work; callers that consumed.errorsfrom the response must switch
to the standardTaskResult.adcpErrorpath.compliance/cache/3.0.0/is populated (cosign-verified) and
replacescompliance/cache/latest/as the storyboard source.
Patch Changes
-
22b44c4: Fix
governance.denial_blocks_mutationto allow expected-denial recovery
paths.The invariant anchored any governance denial (
GOVERNANCE_DENIED,
TERMS_REJECTED,POLICY_VIOLATION, etc.) and then flagged any later
successful mutation in the same run as a silent bypass. That fired on
first-party storyboards whose whole purpose is to test recovery —
media_buy_seller/governance_denied_recovery(buyer shrinks the buy
and retries) andmedia_buy_seller/measurement_terms_rejected(buyer
relaxes terms and retries) — because the retry step succeeded against
the same plan and tripped the anchor.A denial step that the storyboard marks
expect_error: trueis the
author explicitly acknowledging the denial. The subsequent mutation is
a recovery path, not a silent bypass, so the invariant no longer
anchors when the denial step is expected. The silent-bypass signal is
preserved forcheck_governance200s withstatus: deniedand for
adcp_errorresponses the author did not declare expected.When the invariant does fire on a wire-error denial, the failure
message now points the author at theexpect_error: trueescape so
the next author doesn't have to re-derive it from source. The hint is
suppressed oncheck_governance200 denials where the flag has no
effect.Closes #811.