Skip to content

feat(adagents): directory inverse-lookup + divergence detector (Parts 2+3 of #749)#752

Draft
bokelley wants to merge 3 commits into
mainfrom
claude/issue-749-part2-part3
Draft

feat(adagents): directory inverse-lookup + divergence detector (Parts 2+3 of #749)#752
bokelley wants to merge 3 commits into
mainfrom
claude/issue-749-part2-part3

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Refs #749

Implements Parts 2 and 3 of #749, following the spec PRs adcp#4828 (directory endpoint) and adcp#4827 (divergence rule). Part 1 (bug fix) shipped as #750.

Part 2 — fetch_agent_authorizations_from_directory

New async wrapper for GET /api/v1/agents/{agent_url}/publishers on the AAO directory (endpoint path confirmed by @bokelley in #749's comments; %-encodes agent_url in the path per RFC 3986).

Returns AgentDirectoryLookup (paged) with one AgentPublisherEntry per publisher. AgentPublisherEntry carries the same discovery_method / manager_domain provenance vocabulary as AdAgentsValidationResult so callers can route directory-sourced and per-domain results through the same code path.

status uses repeated query params (?status=authorized&status=revoked) rather than a comma-joined string.

Part 3 — detect_publisher_properties_divergence

New async verifier that compares the directory's properties_authorized count (inline resolution) against a per-child direct adagents.json fetch (federated resolution). Returns DivergenceReport — an empty list means no count divergence detected.

Per adcp#4827 §Resolution-paths, the federated result is authoritative. sample_size caps sweeps against large managed networks (cafemedia = ~6,800 child fetches without a cap).

Known limitation (documented in type + docstring): The AAO directory currently returns properties_authorized counts, not property-ID lists. Count-equality is a necessary but not sufficient condition for set-equality (same-count property replacement is undetectable). missing_in_inline and missing_in_federated are list[str] | None where None signals count-only mode — callers must NOT treat None as "empty diff computed." A future ?include=properties directory parameter will enable full (publisher_domain, property_id) set-diff.

New public exports

All six new symbols are exported from adcp.__init__:
AgentDirectoryLookup, AgentPublisherEntry, PublisherDivergence, DivergenceReport, fetch_agent_authorizations_from_directory, detect_publisher_properties_divergence

What was tested

  • ruff check src/adcp/adagents.py src/adcp/__init__.py — clean
  • python -m mypy src/adcp/adagents.py — clean (0 errors)
  • pytest tests/test_adagents.py — 131 passed, 0 failed
  • 5 new tests for fetch_agent_authorizations_from_directory: field deserialization, URL encoding, cursor pagination, default-status repeated-key encoding, custom directory URL, caller-client not-closed assertion
  • 4 new tests for detect_publisher_properties_divergence: no-divergence (count match), count divergence (missing_in_* are None in count-only mode), child fetch error, sample_size cap, caller-client not-closed assertion

Pre-PR review

Two review→fix rounds (within the 2-iteration cap):

Round 1:

  • code-reviewer: approved — one remaining blocker (docstring Example called len() on None fields after BLOCKER 1 fix); fixed in round 2
  • ad-tech-protocol-expert: BLOCKER 1 (missing_in_inline/missing_in_federated always [] — fields useless and misleading), BLOCKER 2 (count-equality silently swallows set divergence), NIT (status comma-joining vs repeated keys); all three addressed

Round 2:

  • code-reviewer: approved (1 nit: AgentPublisherEntry.discovery_method typed str not DiscoveryMethod — correct given "adagents_authoritative" is outside the existing Literal; docstring claim of alignment clarified)
  • ad-tech-protocol-expert: blockers resolved; count-only limitation is correctly documented with the None-sentinel design

Nits (not fixed):

  • AgentPublisherEntry.discovery_method: str is a wider type than DiscoveryMethod because the directory uses "adagents_authoritative" which is not in the existing Literal; typed as str intentionally
  • data.get("publishers") or data.get("results") fallback will silently return [] if the directory adds an "items" key — acceptable for now, documented in the response-parsing comment

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See adcp#3121
for context.

Session: https://claude.ai/code/session_01RDYrywLhVbd4crrAHkTnsH


Generated by Claude Code

claude added 3 commits May 20, 2026 14:36
…r (Parts 2+3 of #749)

Part 2 — fetch_agent_authorizations_from_directory:
  New async function wrapping GET /api/v1/agents/{agent_url}/publishers on
  the AAO directory. Returns AgentDirectoryLookup (paged) with one
  AgentPublisherEntry per authorized publisher. Provenance fields
  (discovery_method, manager_domain) align with AdAgentsValidationResult
  so callers can route directory-sourced and per-domain results through
  the same code path. Mutable status default avoided (uses None → ["authorized"]).

Part 3 — detect_publisher_properties_divergence:
  New async verifier that compares the directory's property counts
  (inline resolution) against direct per-child adagents.json fetches
  (federated resolution). Returns a DivergenceReport (list of
  PublisherDivergence dataclasses) — empty means no divergence.
  Per adcp#4827, federated wins on disagreement; this function surfaces
  the signal for operator monitoring. sample_size caps sweeps over large
  managed networks (cafemedia = ~6,800 child fetches without a cap).

Refs #749

https://claude.ai/code/session_01RDYrywLhVbd4crrAHkTnsH
…(Parts 2+3 of #749)

BLOCKER 1 — missing_in_inline/missing_in_federated were always []; changed to
list[str] | None where None signals count-only mode (directory doesn't return
property IDs). Callers can now branch on None vs [] correctly. Docstring updated
to call out the count-only limitation explicitly.

BLOCKER 2 — Count-equality was silently treated as "no divergence" even though
set divergence (same-count property replacement) is undetectable without IDs.
Docstring now documents this false-negative path; fields are None not [] to
prevent callers from treating empty lists as "diff computed, no gaps".

NIT — status query param now uses repeated keys (?status=a&status=b) via
a list-of-tuples param_list, not comma-joined (?status=a%2Cb). Tests updated
to assert the repeated-key form.

Refs #749

https://claude.ai/code/session_01RDYrywLhVbd4crrAHkTnsH
- Docstring Example in detect_publisher_properties_divergence branches on
  missing_in_inline is None (count-only mode) so copy-paste doesn't
  raise TypeError on None.
- Export AgentDirectoryLookup, AgentPublisherEntry, PublisherDivergence,
  DivergenceReport, fetch_agent_authorizations_from_directory, and
  detect_publisher_properties_divergence from adcp.__init__.
- Add isinstance guard on directory response so a non-dict body raises
  AdagentsValidationError instead of bare AttributeError.
- Use row.get() + skip-on-empty for publisher_domain instead of bare
  KeyError-raising access; use 'or' chain to avoid None fallback.
- Assert caller-provided client is not closed in two tests.

Refs #749

https://claude.ai/code/session_01RDYrywLhVbd4crrAHkTnsH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants