Skip to content

feat(legacypurchase): Postgres-backed Backend + reverse compact-seller facade (follow-up to #466) #482

Description

@sujanchalla0510

Context

Follow-up to #466 ("feat: implement durable legacy purchase continuation
coordinator for AdCP 3.2"). The core coordinator API, durable-store
interface, and a well-tested in-memory reference backend
(adcp/v3/legacypurchase) are implemented in a companion PR against #466
see that PR for the coordinator itself (Store.RegisterContinuation,
Store.ContinueLegacyPurchase, atomic single-use claim proven under
-race, deterministic replay, fail-closed crash reconciliation via
AmbiguousClaimError, and the products-only-brief-compatibility vectors
from the AdCP 3.2 schema bundle run end to end).

Two pieces of #466's scope were deliberately deferred out of that PR rather
than forced into one oversized change, and are tracked here:

1. Persistent (e.g. Postgres) legacypurchase.Backend implementation

adcp/v3/legacypurchase ships the pluggable Backend interface and
MemoryBackend (in-process) only. MemoryBackend is process-local — a
restart loses in-flight and recently-completed continuation state, which is
unacceptable for a production coordinator meant to prevent double-purchase
across a real deployment (potentially multiple instances behind a load
balancer, matching the same durability motivation as #105 /
adcp/v3/signing/pgreplay's PostgresReplayStore).

Scope for this half:

  • A PostgresBackend implementing legacypurchase.Backend's three-state
    claim/commit/fail FSM atomically (unique constraint on token +
    conditional UPDATE ... WHERE token = $1 AND state = 'offered' for
    ClaimPending, mirroring adcp/v3/idempotency's PgBackend and
    adcp/v3/signing/pgreplay's atomic-insert pattern).
  • A real concurrency proof against a live Postgres instance (e.g.
    testcontainers-go, matching this repo's existing
    registry/redisstore / registry/glidestore /
    adcp/v3/signing/pgreplay integration-test convention): many concurrent
    distinct-key claims against one continuation token, exactly one winner,
    under -race.
  • A migration/schema file and SweepExpiredReplays-equivalent retention
    sweep for terminal and expired-offered records (never sweeping
    StatePending — see the core PR's MemoryBackend for why).

2. Reverse compact-seller → legacy-buyer server-side facade

The spec's "Established buyers against a compact-backed seller" section
(specs/legacy-compact-lifecycle-compatibility.md) and
static/compliance/source/test-vectors/products-only-brief-compatibility/vectors.json's
reverse_compatibility_cases describe the opposite direction from the
coordinator in the core PR: a 3.2 seller that implements refine_proposals
etc. natively but must still preserve its deprecated get_products /
create_media_buy facades for established buyers — using "a private atomic
coordinator" internally rather than a buy-then-sync compensating saga.

This is seller-side adapter work — a materially different scope from the
buyer-side coordinator in #466 / the core PR (different failure surface: the
seller owns both the compact and legacy code paths and must keep them
transactionally consistent, rather than redeeming an opaque token issued by
someone else). It needs its own design pass (at minimum: how a seller
built with this SDK's adcp/v3 package would wire a legacy facade through
its native compact handlers) before implementation.

Acceptance criteria

  • legacypurchase.PostgresBackend (or similarly named) implements
    Backend with atomic claim/commit/fail transitions.
  • Concurrency proof against a live Postgres instance under -race.
  • Reverse-facade design doc or PR covering at least the
    reverse_compatibility_cases vector.
  • Both pieces may ship as separate PRs / separate follow-up issues if
    that turns out cleaner once scoped in detail — this issue exists to
    track that they are real, disclosed, not-yet-done work rather than to
    mandate exactly one combined PR.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedenhancementNew feature or requestsdkSDK public API, framework, or developer experience work

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions