Skip to content

agent: publications executor defers on stale authorization snapshots (3/7) - #3343

Open
bbartman wants to merge 2 commits into
bmb/2781-stack-2-inprocess-authzfrom
bmb/2781-stack-3-publications-staleness
Open

agent: publications executor defers on stale authorization snapshots (3/7)#3343
bbartman wants to merge 2 commits into
bmb/2781-stack-2-inprocess-authzfrom
bmb/2781-stack-3-publications-staleness

Conversation

@bbartman

@bbartman bbartman commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

Split 3 of 4 of #3155 (issue #2781): the publications executor stops failing
publications whose authorization was denied under a stale Snapshot, and
instead defers them until an authoritative Snapshot can decide.

  • Request-relative staleness anchoring: the executor passes
    started_at: Some(row.updated_at) — the instant the publication entered
    queued — so a denial is provisional exactly when the Snapshot predates
    the request. Previously (split 2) the anchor was each spec's own
    last_pub_id, which misclassifies the common shape of an old spec whose
    authorization changed now.
  • Deferral loop: on AuthorizationSnapshotStale the executor records
    awaiting_snapshot_after in the task's persisted PublicationState
    (activating the state carried since the prior deploy-compat change),
    cancels the Snapshot's revoke token to request an early background
    refresh, and returns Action::Sleep(STALE_RETRY_WAKE). Re-polls defer
    cheaply — without loading or building the draft — until the local Snapshot
    postdates the anchor, including on a different agent instance.
  • Test harness: grant helpers split into observed/_unobserved variants
    (modelling the window between a grant landing in Postgres and the next
    Snapshot refresh), refresh_snapshot_authoritative / _stale control the
    Snapshot's taken stamp relative to skew, age_live_spec distinguishes
    spec-relative from request-relative anchors under compressed test time, and
    async_publication becomes queue_publication + a bounded
    re-poll-after-refresh loop mirroring production.

Tests

  • user_publications (new integration tests):
    • test_publication_reschedules_on_stale_data_plane_authz
    • test_publication_defers_polls_until_authoritative_snapshot — persisted
      awaiting_snapshot_after, cross-instance handoff, revoke-token cancel
    • test_old_spec_publication_succeeds_after_late_grant — request-relative
      anchoring guard
    • test_publication_stale_then_authoritative_denial — terminal denial
      once authoritative, with snapshotted flow://unauthorized/ errors
    • test_publication_requests_snapshot_refresh
    • test_publication_uses_one_snapshot_across_phases — grants revoked
      mid-publication don't flip decisions within one operation
  • publications::specs::resolve_tests staleness additions: data-plane-name
    and storage-mapping freshness relative to started, request-relative
    anchoring for old specs, and grant attenuation
    (test_attenuated_data_plane_grant_is_not_visible, new
    attenuated_grants fixture).

Stack

  1. control-plane: Snapshot authorization model and retryable staleness error (1/7) #3341 — Snapshot authorization model + retryable staleness error
  2. control-plane, agent: move catalog authorization in-process against a pinned Snapshot (2/7) #3342 — move authorization in-process against a pinned Snapshot
  3. → this PR — publications executor defers on stale snapshots
  4. agent: discovers executor authorizes data-planes via Snapshot and defers on staleness (4/7) #3344 — discovers executor authorizes via Snapshot and defers on staleness

The publications executor stops failing publications whose authorization was
denied under a stale Snapshot, and instead defers them until an authoritative
Snapshot can decide:

- Staleness is anchored to the queued publication row (started_at =
  row.updated_at) rather than each spec's last_pub_id, correctly handling the
  common shape of an old spec whose authorization changed just before the
  publication was queued.
- On AuthorizationSnapshotStale the executor records awaiting_snapshot_after
  in the task's persisted PublicationState (activating the state carried for
  deploy compatibility), cancels the Snapshot's revoke token to request an
  early background refresh, and reschedules with STALE_RETRY_WAKE. Re-polls
  defer cheaply — without loading or building the draft — until the local
  Snapshot postdates the anchor, including on another agent instance.
- Harness: observed/unobserved grant helpers, authoritative/stale Snapshot
  stamping, age_live_spec, and queue_publication / poll_publication_once for
  tests that drive individual polls.

Also adds the specs.rs staleness-anchoring tests (data-plane and
storage-mapping freshness, request-relative anchoring, grant attenuation via
the new attenuated_grants fixture) which exercise resolve_live_specs behavior
that this deferral makes reachable end to end.

Split 3 of 4 from #3155.
@bbartman
bbartman force-pushed the bmb/2781-stack-3-publications-staleness branch from b6388d1 to e0b475d Compare August 11, 2026 18:23
@bbartman bbartman changed the title agent: publications executor defers on stale authorization snapshots (3/4) agent: publications executor defers on stale authorization snapshots (3/7) Aug 12, 2026
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.

1 participant