feat(decisioning): align AccountStore.resolution literal with JS + Tier 1 docs#330
Merged
feat(decisioning): align AccountStore.resolution literal with JS + Tier 1 docs#330
Conversation
…er 1 docs Round-5 design feedback (salesagent + cross-language parity audit): the Python decisioning module's adopter surface drifts from the JS reference at ``src/lib/server/decisioning/account.ts``. Aligning now, hours post-foundation merge, before any adopter locks in. Pre-release alignment (4.4.0 release PR is held; not on PyPI yet): * ``AccountStore.resolution`` literal: ``'singleton' → 'derived'``, ``'from_auth' → 'implicit'``. ``'explicit'`` unchanged. Class names (``SingletonAccounts``, ``FromAuthAccounts``) kept — they're Python- ergonomic with no JS counterpart to drift from. The literal is what the framework reads at server boot for ``validate_platform`` deployment checks; cross-language parity matters because both SDKs read the same wire-shape concept. NOT a ``feat!:`` because the foundation hasn't shipped to PyPI in 4.4.0 form — the held release PR #328 carries this rename alongside the foundation, so external adopters never see the old literal values. Tier 1 documentation additions (3 of 4 from salesagent's ``adcp-decisioning-design-feedback.md``; the 4th — StateReader UserWarning — was already implemented in round-4 via ``_NotYetWiredStateReader._warn_once``): * ``accounts.py`` module docstring: "Spec-agent vs auth-layer principal" section. Resolves the load-bearing ambiguity adopters hit — what string AuthInfo.principal carries varies by auth shape (agent_url for AdCP v3 signed-request, OAuth subject for bearer, mTLS subject for client-cert). Adopters wiring FromAuthAccounts decide what their auth middleware projects. * ``RequestContext`` docstring: "Identifier disambiguation — when to use which" table. ``account.id`` (data ownership), ``auth_principal`` (caller identity), ``caller_identity`` (framework-managed cache scope key, never read directly), ``tenant_id`` (transport routing). Salesagent flagged this as cognitively heavy. * ``TaskHandoff`` docstring: "What it's for / not for" section. Steers adopters away from human-driven HITL workflows where the background fn would block on a person's clicks. v6.1 may add a ``ctx.handoff_to_human()`` primitive; for v6.0, the recommended pattern for queued approvals is ``input-required`` + adopter- owned webhook emission, not TaskHandoff. Tier 2 items (multi-tenant ``hello_publisher.py``, ``AdagentsAccountStore``, built-in auth adapter wrappers like ``SignedRequestAuth``/``BearerAuth``/``MtlsAuth``) deferred to 4.5.0 with proper RFCs — better design data once salesagent's actual port exists as the reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…le render, doc citations
Code-reviewer + docs-expert findings on the round-5 alignment PR:
- 3 stale literal references missed by the rename pass:
* accounts.py AccountStore.resolve docstring (``'singleton'`` /
``'from_auth'`` references)
* context.py auth_info field docstring
* handler.py _extract_auth_info comment
- RequestContext identifier table:
* Composite cache-key format was incorrect (said ``store.qualname:
account.id``; actual is
``<store_module>.<store_qualname>:<account_id>`` per
dispatch.py:457). The ``__module__`` prefix is load-bearing for
cross-package collision isolation.
* "NEVER read directly in adopter code" claim was overstated —
audit logging and rate-limiting code legitimately reads
``ctx.caller_identity``. Tightened to "treat as opaque; don't
parse, compare, or rewrite" so the contract matches reality.
* Replaced the reST grid table with a definition-list format —
the original had a 1-char width mismatch on the
``caller_identity`` row that would have failed pdoc3's grid-table
parser and fallen back to literal-block rendering. Definition
lists render correctly in pdoc3 + ``help()`` + IDE hover without
width-counting.
- TaskHandoff "what it's NOT for" section:
* Added concrete pointers for the human-driven HITL pattern —
``input-required`` status from
``schemas/cache/enums/task-status.json`` + per-tool
``*_async_response_input_required`` envelopes; webhook
primitives in ``adcp.webhooks`` (payload builders) +
``adcp.webhook_sender`` (HMAC-SHA256 signing + IP-pinned
delivery). Worked example deferred to ``hello_publisher.py`` in
4.5.0.
- accounts.py spec-agent vs auth-principal section:
* Tightened the signed-request claim — the SDK's ``adcp.signing``
primitives verify the signature, but the wiring that writes
``AuthInfo.principal = agent_url`` lives in adopter middleware
today (or the built-in ``SignedRequestAuth`` adapter wrapper that
lands in 4.5.0). Adopters wiring this manually before then should
follow the convention so ``adcp.adagents`` reads the right key.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 1, 2026
Merged
Merged
bokelley
added a commit
that referenced
this pull request
May 1, 2026
…ionLists Protocols (breadth sprint Batch 4 — FINAL) (#335) Final batch of the breadth-sprint per the parity audit. Ports the remaining four specialism Protocols from JS reference. With this PR, every spec specialism slug except ``governance-aware-seller`` has a Protocol class + REQUIRED_METHODS coverage. New Protocols: * ``BrandRightsPlatform`` — covers ``brand-rights``. Identity discovery + licensing. Required (3, sync): ``get_brand_identity``, ``get_rights``, ``acquire_rights``. ``acquire_rights`` returns a 3-arm discriminated success union (acquired / pending / rejected) — rejection-as-data for spec-defined GRANT rejection, AdcpError only for buyer-fixable REQUEST rejection. Async outcomes for the ``pending`` arm flow via ``push_notification_config`` webhook (NOT a polling tool). * ``ContentStandardsPlatform`` — covers ``content-standards``. Brand safety policies, content adjacency rules, per-creative compliance verification. Required (6): list/get/create/update, ``calibrate_content``, ``validate_content_delivery``. Optional (2, analyzer reads): ``get_media_buy_artifacts``, ``get_creative_features``. * ``PropertyListsPlatform`` + ``CollectionListsPlatform`` (specialisms/lists.py) — covers ``property-lists`` and ``collection-lists``. Parallel CRUD shapes (5 methods each, all required) with token-issuance semantics: ``create_*`` returns a per-seller-scoped ``fetch_token``, ``delete_*`` revokes it. Compromise-driven revocation MUST trigger the delete path. Required-method coverage in ``REQUIRED_METHODS_PER_SPECIALISM``: ``brand-rights`` (3), ``content-standards`` (6), ``property-lists`` (5), ``collection-lists`` (5). Public re-exports added at ``adcp.decisioning.__all__``: ``BrandRightsPlatform``, ``ContentStandardsPlatform``, ``PropertyListsPlatform``, ``CollectionListsPlatform``. Test coverage in ``tests/test_decisioning_specialisms.py`` (13 new tests, 43 total in the file): * ``runtime_checkable`` conformance per Protocol. * ``validate_platform`` enforcement per slug — including a security-relevant test that ``property-lists`` REQUIRES ``delete_property_list`` (revocation path) so adopters can't ship list-publishing without revocation primitives. * Contract pins per slug. * **Breadth-sprint completeness pin**: ``test_every_spec_slug_except_governance_aware_seller_is_enforced`` asserts that ``SPEC_SPECIALISM_ENUM - REQUIRED_METHODS.keys()`` yields exactly ``{governance-aware-seller, signed-requests}`` — the two slugs unenforced by design (composition claim and deprecated-moved-to-universal respectively). One existing dispatch test updated: ``test_validate_platform_warns_on_unenforced_spec_specialism`` swapped its canonical "spec-recognized but unenforced" example from ``brand-rights`` (now enforced) to ``governance-aware-seller`` (the only remaining unenforced spec slug — by design). **Breadth sprint COMPLETE.** All 8 missing specialism Protocols from the parity audit are now ported. 9 PRs total accumulating in the held release PR #328: * #316 foundation * #329 codemod ergonomics * #330 parity rename + Tier 1 docs * #331 F12 auto-emit * #332 Signals + Audience (Batch 1) * #333 Creative Builder + AdServer (Batch 2) * #334 Campaign Governance (Batch 3) * #335 Brand + Content + Lists (Batch 4 — this PR) Ready for salesagent validation against editable install before tagging 4.4.0. 2252 tests pass (up from 2239). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Round-5 design feedback applied:
AccountStore.resolutionliteral renamed ('singleton'→'derived','from_auth'→'implicit') to match JS reference atsrc/lib/server/decisioning/account.ts. Class names (SingletonAccounts,FromAuthAccounts) kept; they're Python-ergonomic with no JS counterpart.accounts.pymodule docstring, RequestContext "when to use which identifier" table, TaskHandoff "what it's for / not for" caveat steering adopters away from human-driven HITL._NotYetWiredStateReader._warn_oncefires per method on first call, with deterministic test coverage.Why now
PR #316 (foundation) merged hours ago. The held release-please PR #328 is the only thing accumulating these changes — adopters are on editable installs (salesagent) or none. This is the cheapest moment to align before the literal locks in.
NOT a
feat!:breaking changeThe held #328 hasn't shipped to PyPI. External adopters never see the old literal values; the rename is intra-pre-release alignment. Post-merge release notes will document the literal values for adopters reading
store.resolutionat runtime.Tier 2 deferred
hello_publisher.py(multi-tenant),AdagentsAccountStore,SignedRequestAuth/BearerAuth/MtlsAuthadapter wrappers — punt to 4.5.0 with RFCs. Better design data once salesagent's actual port exists as the reference.Test plan
pytest tests/test_decisioning_*.py tests/test_hello_seller*.py— 155 passedpytest tests/— 2183 passedruff checkcleanmypy src/adcp/decisioning/clean🤖 Generated with Claude Code