Skip to content

feat(account): complete guarded change feed adoption - #2910

Merged
bokelley merged 5 commits into
mainfrom
conductor/account-change-feed-2669
Sep 14, 2026
Merged

bokelley merged 5 commits into
mainfrom
conductor/account-change-feed-2669

Conversation

@bokelley

@bokelley bokelley commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Buyers can already call listAccountChanges(), but need safe boundaries between a webhook wake-up, ordered invalidation ingestion, and an acknowledged durable checkpoint. Complete the adopter-facing remainder of #2669 using the existing generated protocol types, client method, notification enums, and runtime schemas.

  • Add authoritative account.change_recorded parsing with typed errors, bounded JSON input, and logical/retry/account/subscriber/resource/action identity checks. Preserve valid unknown resource/action invalidations.
  • Add incompatible advisory and durable cursor types plus an acknowledged drain. Bootstrap acquires latest before authoritative snapshots, processes intervening pages before exposing a checkpoint, and reboots on structured CURSOR_EXPIRED.
  • Add subscriber-preserving registration construction, expose the existing feed capability declaration, and provide public exports and migration guidance.
  • Prove the packed MCP input schema has a plain object root without top-level unions and still rejects cursor/starting-position conflicts before invoking the handler. Extend the storyboard runner for persistent account-change webhook observations.

Uses merged adcontextprotocol/adcp#7516 for the bounded training-seller shared-creative/capability correction. The recipe pins immutable merge ea5498db0c611ad5b8e95618f97334f9db59e973. Seller implementation stays upstream. Adopter storage, projection/checkpoint transactions, durable feed storage (#2681), and subscription runtime (#2848) remain outside this SDK change.

Validation:

  • 22 focused/packed regressions and both training integration tests pass. The unchanged published #6811 scenario passes every required phase through the merged seller's normal MCP router, with zero skips.
  • Schema checks, package checks, size audit, and clean-room Node/Bun/peer-floor/declaration/browser verification pass. The file-count budget accounts for exactly 21 new artifacts; byte budgets remain unchanged (18.3 MiB packed).
  • All PR CI checks pass and the exact merge head is approved. CodeQL reports no new alerts after the linear-time timestamp fix.
  • Local ci:quick completed its format/type/build/fast checks with one existing local-only test hitting Node's default 4 GB heap ceiling. All 14 tests in that file pass with an 8 GB heap. Remaining slow and workspace suites were run separately and pass.
  • Upstream validation includes 114 focused tests, typechecking, and 9,388 server tests passing (31 skipped). Its transient SI dependency-install failure passed on retry.

No generated adoption changes are duplicated from #2907, which was still open when this PR merged. The published rc.2 and rc.3 account-feed storyboard and schema contracts are equivalent apart from schema version IDs.

Fixes #2669

aao-secretariat[bot]
aao-secretariat Bot previously approved these changes Sep 14, 2026

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — AdCP 3.2 account change feed (drain stream, webhook parser, subscription builder, branded cursor types, storyboard-runner support, migration guide).

No blocking findings. The reviewer verified the acknowledgement/checkpoint state machine, fail-closed drain error handling with bounded reboot, witness-preserving normalization, seller-key sanitization in error paths, JSON hardening, branded cursor type safety, and a correctly-sized minor changeset. No Critical/High/Medium findings were surfaced.

Decision trace:

  • No no-auto-approve team match (HARD RULE not triggered).
  • gated_paths: false → row 2 does not fire despite review_decision REVIEW_REQUIRED.
  • high_risk: false → rows 3 and 5 do not fire.
  • No findings of any severity → rows 1, 4, 8 do not fire.
  • No prior decision → row 6 does not fire.
  • Falls through to row 9 → approve.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean pass, no blocking or medium findings.

Checked:

  • No critical/high/medium findings in this run.
  • Author is not on any no-auto-approve team.
  • gated_paths: false (row 2 not triggered).
  • high_risk: false; no deleted/modified high-risk paths.
  • Prior decision was approve; this is a net-positive iteration adding defensive hardening on the untrusted account.change_recorded webhook path plus a test.

Reviewer notes three defensive changes: removal of a dead item !== null guard in account-change-json.ts (null handled upstream), ReDoS hardening in equalInstant (anchored date regex, O(n) trailing-zero trim, cross-offset instant equality preserved) in account-change-recorded.ts, and an 8192-digit fractional-timestamp test covering identity-preservation and mismatch. Changeset is present and consistent with wire-impact rules.

No escalation triggers fired; falls through to row 9.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — delta pass on a prior clean approve.

The only change since the prior clean approve (head 7bd4d99) is scripts/check-package-size.mjs, where MAX_PACKED_FILE_COUNT is raised 5,995 → 6,016 (+21) to track exactly 21 new packed artifacts (five module sets × 4 + one migration guide). The arithmetic is exact, the bump is tight with no slack, byte budgets are untouched, and a changeset is present.

No critical/high/medium findings this run; no prior blockers to re-evaluate. No author team gate, gated_paths is false, high_risk is false. None of decision rows 1–8 fire → row 9 approve.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean subsequent pass, no blocking or medium findings.

This is a follow-up review on a two-file delta since the prior approve (head 7bd4d99). Changes verified:

  • docs/migration-account-change-feed.md: swaps a mutable PR ref for the pinned immutable SHA ea5498db — correct doc-link hygiene.
  • scripts/check-package-size.mjs: bumps MAX_PACKED_FILE_COUNT 5,995 → 6,016 (+21); arithmetic and comment verified.

No new findings this run, and the prior pass had no blockers to re-evaluate. Changeset present (.changeset/quiet-account-change-drain.md) covering the src/lib/** changes.

Gate checks: gated_paths is false, so row 2 does not apply despite review_decision: REVIEW_REQUIRED. No no-auto-approve team match. high_risk false. No critical/high/medium findings. None of rows 1–8 fire → row 9 approve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDK: support the AdCP 3.2 account change feed

2 participants