Skip to content

docs+test(adagents): lock revocation propagation contract through crawler diff path (closes #4506 part B)#4611

Merged
bokelley merged 2 commits into
mainfrom
bokelley/revocation-event-propagation
May 17, 2026
Merged

docs+test(adagents): lock revocation propagation contract through crawler diff path (closes #4506 part B)#4611
bokelley merged 2 commits into
mainfrom
bokelley/revocation-event-propagation

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Closes #4506 part B. The in-memory `AuthorizationIndex` revocation propagation chain works end-to-end through infrastructure built by PR #4538 (writer soft-delete) plus the existing crawler-diff event flow:

  1. Writer's revocation branch (`upsertAdagentsCache` when `revoked_publisher_domains[]` lists the source) soft-deletes catalog rows.
  2. Next crawl pass re-snapshots `(agent, publisher_domain)` pairs via `federatedIndex.getAllAgentDomainPairs()`, which reads from `v_effective_agent_authorizations` (soft-deleted rows excluded by the partial index).
  3. `crawler.produceEventsFromDiff` detects the dropped pair and emits `authorization.revoked`.
  4. `registry-sync` consumes the event and calls `authorizationIndex.removeEntry`.

What's added

  • Snapshot-contract test in `registry-catalog-agent-auth-writer.test.ts`: calls `getAllAgentDomainPairs()` before/after a revocation manifest write; asserts the pair is present before and absent after. The contract step 2 depends on — if it breaks, the crawler diff misses the change and the in-memory index keeps authorizing revoked publishers until the next full re-index.
  • Doc paragraph in `managed-networks.mdx` "Publisher revocation" section: documents the propagation flow, the one-crawl-interval steady-state latency, and a SHOULD for validators without per-publisher crawl to apply revocation eagerly on adopt.

Out of scope

Per-`authorized_agents[]` `last_updated` partial-walk indexing — the spec marks it advisory and writer projection is millisecond-fast today; adding the per-entry index has nontrivial schema + reader-side cost. Punted (named in changeset).

Test plan

  • Typecheck clean
  • Integration test (real postgres) — CI runs the contract assertion

🤖 Generated with Claude Code

bokelley and others added 2 commits May 16, 2026 13:34
…wler diff path

Closes #4506 part B. The in-memory AuthorizationIndex revocation
propagation chain works through existing infrastructure (PR #4538
writer soft-delete + the crawler-diff event flow):

1. Writer's revocation branch soft-deletes catalog rows.
2. Next crawl pass re-snapshots (agent, publisher_domain) pairs via
   federatedIndex.getAllAgentDomainPairs(), which reads from
   v_effective_agent_authorizations (soft-deleted excluded).
3. crawler.produceEventsFromDiff detects the dropped pair and emits
   authorization.revoked.
4. registry-sync consumes the event and calls removeEntry.

Adds:

- Integration test exercising step 2's contract: getAllAgentDomainPairs
  before/after a revocation manifest write, asserting the pair is
  present before and absent after. If this contract breaks, the
  crawler diff misses the change and the in-memory index keeps
  authorizing revoked publishers until the next full re-index.

- Doc paragraph under "Publisher revocation" in managed-networks.mdx
  documenting the propagation flow, the one-crawl-interval steady-
  state latency, and a SHOULD for validators without per-publisher
  crawl to apply revocation eagerly on adopt.

Out of scope (kept as named follow-up): per-authorized_agents[]
last_updated partial-walk indexing. Spec marks it advisory; writer
projection is millisecond-fast today; adding the per-entry index has
nontrivial schema + reader-side cost. Punted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both reviewers (protocol-expert + code-reviewer) converged on the same
shape of finding: the propagation paragraph mixed normative-as-tested
text with implementation-chain prose, leaving the MUST circular and
the eager-adopt SHOULD non-actionable. Rewritten as:

1. Testable conformance MUST. "Validators MUST NOT continue to
   authorize a (agent, publisher_domain) pair more than one crawl
   interval after the validator has successfully refetched an
   adagents.json that lists the publisher in revoked_publisher_domains[]."
   Removes the circular "must update when revocation takes effect"
   framing; bound is observable from outside the validator.

2. Latency bound caveats. References the existing cache-bounded rules
   (24h fallback on 5xx, 7-day absolute cap), notes crawl backlog and
   CDN caching extend practical latency. Tightens "one crawl interval
   at most" to "after a successful refetch."

3. Actionable eager-adopt SHOULD. Concrete instruction: "when adopting
   any adagents.json whose revoked_publisher_domains[] lists any
   publisher domain — regardless of whether the validator currently
   authorizes any agent for it — the validator SHOULD remove every
   (agent, publisher_domain) entry it holds for that publisher from
   the in-memory index before serving the next authorization decision."
   Closes the "previously observed" ambiguity (now: any listed
   publisher, regardless of prior auth state).

4. Composition note. Explicit reference to the append-only durability
   rule above so readers understand the propagation paragraph is in
   addition to, not instead of, the 7-day hold.

5. Reference-impl chain (a-d) moved into a non-normative marker.
   `authorization.revoked` event shape called out as reference-impl
   vocabulary, not a normative wire format.

Plus code-reviewer's test-isolation nit: added a length-delta
assertion to the contract test (snapshot size drops by exactly one),
in case future cross-test contamination breaks the .find() shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley

Copy link
Copy Markdown
Contributor Author

Expert review pass

Two reviews (code-reviewer + ad-tech-protocol-expert). Both converged on the propagation paragraph being load-bearing prose that needed sharpening. Addressed in commit 9fdf627:

  • Testable conformance MUST. "Validators MUST NOT continue to authorize a `(agent, publisher_domain)` pair more than one crawl interval after the validator has successfully refetched an `adagents.json` that lists the publisher in `revoked_publisher_domains[]`." Removes the circular "must update when revocation takes effect" framing.
  • Latency caveats. References cache-bounded staleness from the fetch rules above (24h-on-5xx, 7-day cap). Notes crawl backlog and CDN caching extend practical latency; the requirement is on the validator's own pipeline.
  • Actionable eager-adopt SHOULD. Concrete instruction now: synchronously remove every `(agent, publisher_domain)` entry the validator holds for any publisher in `revoked_publisher_domains[]` before the next authorization decision — regardless of whether the validator currently authorizes any agent for that publisher. Closes the "previously observed" ambiguity.
  • Composition note. Explicitly references the append-only durability rule so readers see the propagation paragraph as in addition to, not instead of, the 7-day hold.
  • Reference-impl chain (a–d) moved into a non-normative marker. The `authorization.revoked` event shape called out as reference-impl vocabulary, not a normative wire format.

Plus code-reviewer's test-isolation nit: added a length-delta assertion (snapshot drops by exactly one) so the contract test still fires if cross-test contamination breaks the `.find()` shape.

@bokelley bokelley merged commit c8a87ed into main May 17, 2026
18 checks passed
@bokelley bokelley deleted the bokelley/revocation-event-propagation branch May 17, 2026 12:44
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.

Server: honor publisher_domains[] compact form + revoked_publisher_domains[] in authorization index

1 participant