Conversation
56a32c1 to
e50d83d
Compare
…3524 stage 2) Stage 1 set up the data model; Stage 2 turns on the per-version issuance path. Today with SUPPORTED_BADGE_VERSIONS=['3.0'] the runtime behavior is byte-for-byte identical to Stage 1 — but the wiring is in place, so flipping the constant to add '3.1' later turns on parallel-version badge issuance with no further code changes. A single comply() call still runs per agent. The returned storyboard statuses are filtered per supported version (using the SDK's existing Storyboard.introduced_in field) before each processAgentBadges() call. Storyboards without introduced_in are "always applied" — every target keeps them — so today a 3.0 target reads the entire catalog. When 3.1-only storyboards arrive they'll declare introduced_in: "3.1" and the 3.0 fan-out skips them. Files: - services/adcp-taxonomy.ts: SUPPORTED_BADGE_VERSIONS constant + isSupportedBadgeVersion type guard - services/storyboards.ts: compareAdcpVersions numeric comparator ('3.10' > '3.2'), getStoryboardsForVersion + getStoryboardIdsForVersion filters - addie/jobs/compliance-heartbeat.ts: iterates supported versions, filters statuses per version, aggregates issued/revoked across versions for a single notification per agent - services/verification-token.ts: adcp_version claim added to VerificationTokenPayload and signed alongside existing claims; validated against ^[1-9][0-9]*\.[0-9]+$ at sign time so poisoned DB rows can't smuggle malformed values into AAO-signed tokens - services/badge-issuance.ts: passes adcpVersion into signVerificationToken 12 new tests cover the comparator (especially the '3.10' > '3.2' case that motivated it), the version filter contract, the new constant + type guard, and JWT round-trip with adcp_version including double-digit minors and malformed-value rejection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Security review (M1, M2, M3): - M2 / fail-closed JWT signing. signVerificationToken used to drop a malformed adcp_version silently and emit a token without it. That converted any poisoned DB row into a downgrade-attack vector — a decentralized verifier reading the token would assume "no adcp_version = pre-Stage-2 legacy, accept as authoritative." Now refuse to sign and log loudly; the heartbeat surfaces it via notifySystemError. Updated 2 tests; added a third covering the full-semver-as-adcp_version programmer-error case. - M1 / verifier-side adcp_version shape check. verifyVerificationToken now applies the same `^[1-9][0-9]*\.[0-9]+$` regex at verify time so a future signer bug, test key, or smuggled claim is rejected symmetrically. - M3 / per-version try/catch in heartbeat fan-out. A failure on 3.1 used to throw out of the loop and skip the notification for an already-completed 3.0 issuance, with the outer catch silently warning. Now each version has its own try/catch, partial-success notifications still fire for completed versions, and per-version failures emit notifySystemError so persistent issues aren't silent. Code review: - adcp_version is now threaded into BadgeIssuanceResult items (issued/revoked/degraded/unchanged) and into the notifyVerificationChange shape. Slack/DM/feed-event text now includes "(AdCP 3.0)" so two simultaneous issuances at different versions don't render as duplicate identical messages. - Shared ADCP_VERSION_RE constant in verification-token.ts; nit caught the duplication across sign-time, verify-time, DB CHECK. - compareAdcpVersions: comment honest about coercion, malformed inputs emit a debug log so a hand-edited storyboard YAML doesn't disappear silently. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e50d83d to
290551a
Compare
This was referenced Apr 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stage 2 of #3524. Stage 1 set up the data model; this PR turns on the per-version issuance path.
Today with `SUPPORTED_BADGE_VERSIONS = ['3.0']`, runtime behavior is byte-for-byte identical to Stage 1 — but the wiring is in place. Flipping the constant to `['3.0', '3.1']` later turns on parallel-version badge issuance with no further code changes.
Architecture
A single `comply()` call still runs per agent (one network round per heartbeat). The flat storyboard-status list it returns is filtered per supported version before each `processAgentBadges()` call. Storyboards opt into a version via the SDK's existing `Storyboard.introduced_in` field — unset means "always applied," so today every storyboard is included for any 3.x target. When 3.1-only storyboards land they'll declare `introduced_in: "3.1"` and the 3.0 fan-out skips them.
What ships
What does NOT change
Test plan
Review focus
🤖 Generated with Claude Code