Skip to content

feat(governance): authority_level → reallocation_threshold / human_review_required (#576)#603

Merged
bokelley merged 3 commits into
mainfrom
bokelley/issue-576
Apr 19, 2026
Merged

feat(governance): authority_level → reallocation_threshold / human_review_required (#576)#603
bokelley merged 3 commits into
mainfrom
bokelley/issue-576

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Closes #576. AdCP removed budget.authority_level in favor of two orthogonal fields. There's no rc.4 — this migration lands on the current release line.

  • budget.reallocation_threshold (≥0, in budget.currency) / budget.reallocation_unlimited: true — mutually exclusive; budget reallocation autonomy.
  • plan.human_review_required — mandatory human review for decisions affecting data subjects under GDPR Art 22 / EU AI Act Annex III. Auto-flipped by governance agents when policy_categories includes fair_housing | fair_lending | fair_employment | pharmaceutical_advertising, or when policy_ids includes eu_ai_act_annex_iii.

Changes

  • src/lib/governance/ — new exports from @adcp/client:
    • buildAnnexIIIPlan(input) — stamps human_review_required: true.
    • buildHumanOverride({ reason, approver, approvedAt? }) — builds the re-sync downgrade artifact; validates reason ≥20 chars and approver email.
    • validateGovernancePlan(plan) — checks invariants datamodel-code-generator-style codegen drops from if/then: budget threshold XOR unlimited, and regulated categories / Annex III policy ids requiring human_review_required: true.
    • Constants: REGULATED_HUMAN_REVIEW_CATEGORIES, ANNEX_III_POLICY_IDS.
  • Migrate fixtures and scenarios — removed every authority_level reference from src/lib/testing/ and test/lib/ (~28 occurrences). Mapping: agent_full → reallocation_unlimited: true; agent_limited → keep reallocation_threshold (drop authority_level); human_required → plan.human_review_required: true (when Annex III) or keep reallocation_threshold (when budget-scoped).
  • skills/build-governance-agent/SKILL.mdcheck_governance decision-logic bullets now cover reallocation autonomy, auto-flipping human_review_required, data_subject_contestation findings, human_override on re-sync, and the audit-mode-no-downgrade rule.
  • docs/guides/GOVERNANCE-MIGRATION.md — migration guide with the field mapping table and helper usage.
  • Changeset — minor.

Deferred

Storyboards (compliance/cache/) are synced from upstream per CLAUDE.md, so new YAML scenarios for Annex III rejection / contestation-missing / audit-no-downgrade / downgrade-without-override belong in the adcp repo. The client-side helpers, validator, and docs here are what SDK consumers need.

Test plan

  • npm run build (tsc clean)
  • npm run test:lib — 3307 pass, 5 skipped/pre-existing training-agent integration failures (bid_price schema) unrelated to this change
  • node --test test/lib/governance-helpers.test.js — 14/14 new tests pass
  • node --test test/lib/zod-schemas.test.js — 41/41 pass (migrated pharma plan now correctly carries human_review_required: true)
  • npm run test:lib | grep -c authority_level = 0

🤖 Generated with Claude Code

…human_review_required (#576)

AdCP removed `budget.authority_level` in favor of orthogonal fields:
- `budget.reallocation_threshold` / `budget.reallocation_unlimited` for
  budget reallocation autonomy (mutually exclusive).
- `plan.human_review_required` for GDPR Art 22 / EU AI Act Annex III
  human-in-the-loop review, flipped automatically for regulated verticals.

Migrate every test fixture and doc reference off `authority_level`, add
`@adcp/client` helpers for building Annex III plans and human_override
artifacts, and add a client-side validator for invariants that `if/then`
codegen drops from the generated types.

- `src/lib/governance/`: `buildAnnexIIIPlan`, `buildHumanOverride`,
  `validateGovernancePlan`, `REGULATED_HUMAN_REVIEW_CATEGORIES`,
  `ANNEX_III_POLICY_IDS`.
- Migrate `src/lib/testing/` and `test/lib/` fixtures (~28 occurrences).
- `skills/build-governance-agent/SKILL.md`: update check_governance
  decision-logic bullets to cover reallocation autonomy, auto-flipping
  human_review_required, data_subject_contestation findings, human_override
  on re-sync, and the audit-mode-no-downgrade rule.
- `docs/guides/GOVERNANCE-MIGRATION.md`: migration guide.
- `test/lib/governance-helpers.test.js`: 14 new tests.

Fixes #576.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/lib/governance/index.ts Fixed
bokelley and others added 2 commits April 19, 2026 05:54
- Rename buildAnnexIIIPlan → buildHumanReviewPlan. The helper stamps
  human_review_required: true but does not infer the reason; GDPR Art 22
  covers cases beyond Annex III, so tie the name to the action, not the
  specific regime. Update exports, docs, and tests.
- Drop `?: never` on ReallocationAutonomy. The PlanBudget index signature
  reopens the type so the discriminator doesn't enforce mutual exclusion
  at compile time. Document that validateGovernancePlan is the source of
  truth.
- Tighten EMAIL_PATTERN to reject consecutive dots and single-char TLDs.
- Reject control characters in human_override reason and approver
  (audit-log safety).
- Validate approvedAt: reject Invalid Date instances and unparseable ISO
  strings instead of passing them through.
- Tighten validateGovernancePlan JSDoc: call out that governance agents
  resolve synonyms and custom policies server-side and remain
  authoritative; missing budget is structural validation (Zod's job).
- Rename describe/it blocks in governance-e2e.test.js from "Human-review
  denial" / "human_required authority" to reallocation-threshold
  language, matching the migrated semantics.
- Add tests for control chars, invalid dates, synonym non-inference,
  explicit human_review_required: false, single-char TLDs, and the
  migration guide's example (doc-as-test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 6034b50 into main Apr 19, 2026
12 checks passed
@bokelley bokelley deleted the bokelley/issue-576 branch April 19, 2026 10:04
bokelley added a commit that referenced this pull request Apr 19, 2026
Two upstream landings touched skill guidance:

- #603 split the old plan.authority_level enum into
  plan.human_review_required (GDPR Art 22 / EU AI Act, covers data-subject
  decisions) and budget.reallocation_threshold / reallocation_unlimited (budget
  autonomy). Plan shape updated; handler code updated; spend-authority specialism
  row updated.
- #600 shipped the RFC 9421 signed-requests grader
  with three phases (capability_discovery, positive_vectors, negative_vectors).
  Skill no longer claims "runner not yet implemented"; signed-requests section
  now documents the grading surface (WWW-Authenticate error codes), test-kit
  prerequisites, and the revoked-keyid / per-keyid cap requirements.

No changeset — docs only.
bokelley added a commit that referenced this pull request Apr 20, 2026
…ilder testing (#624)

* docs(skills): add specialism coverage to every build-*-agent skill

Every SKILL.md now has a "Specialisms this skill covers" table mapping
specialism IDs to concrete deltas, plus per-specialism sections where
the compliance storyboard requires behavior the baseline did not cover.
Root CLAUDE.md gets an inverse specialism → skill index.

Derived from a fresh-builder test against all 21 specialisms; median
build confidence was 3/5 on the prior skills, with six specialisms at
≤2/5. Upstream issues filed as adcontextprotocol/adcp#2284-#2288.

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

* docs: align skills to AdCP 3.0 GA

* fix(skills): correct shapes round-2 builder tests found wrong

* fix: align skills with upstream main (escalated drop, signing API)

Upstream merges today invalidated two pieces of guidance:

- #592 dropped the 'escalated' check_governance
  status. The spec enum is now approved | denied | conditions. Human review
  is signalled via a denied decision with a critical-severity finding, and
  the buyer resolves review off-protocol before re-calling.
- #593 landed the real signing API surface
  (verifyRequestSignature, createExpressVerifier, VerifierCapability). My
  earlier skill guidance imported a fictional verifyRfc9421Signature helper.

Also fixes the approved-with-conditions response shape — was status:'approved'
plus a conditions[] array, should be status:'conditions' (the enum itself has
a conditions value).

No changeset — docs only.

* fix(skills): align governance + signed-requests to main

Two upstream landings touched skill guidance:

- #603 split the old plan.authority_level enum into
  plan.human_review_required (GDPR Art 22 / EU AI Act, covers data-subject
  decisions) and budget.reallocation_threshold / reallocation_unlimited (budget
  autonomy). Plan shape updated; handler code updated; spend-authority specialism
  row updated.
- #600 shipped the RFC 9421 signed-requests grader
  with three phases (capability_discovery, positive_vectors, negative_vectors).
  Skill no longer claims "runner not yet implemented"; signed-requests section
  now documents the grading surface (WWW-Authenticate error codes), test-kit
  prerequisites, and the revoked-keyid / per-keyid cap requirements.

No changeset — docs only.

* fix(skills): round-3 bug-fix pass — shapes, phase field, signed-requests wiring

Round-3 fresh-builder tests surfaced 4 residual bugs (6 specialisms tested, median 5/4):

- governance-delivery-monitor summary-table row said 'delivery_evidence' but
  code and storyboard use 'delivery_metrics'. Fixed.
- Skill claimed 'phase' is not on the wire — storyboard sends phase:'delivery'
  and llms.txt lists it as optional. Now: phase is optional-but-authoritative;
  handler routes on phase || delivery_metrics presence.
- validate_content_delivery 'artifact.assets' was shown as an object; actual
  shape is an array of {type, url, width, height, duration_ms?}. Also added
  'artifact.description' which the storyboard uses for calibration matching.
- signed-requests: showed JWKS with a comment 'mark revoked before grading'
  but never showed the revocationStore.insert call, had no per-keyid cap
  example for vector 020, and no mount-order warning for express body-parsers
  (covers_content_digest silently breaks if JSON parser runs first). All
  three now have copy-pasteable snippets.

Also fixed CLAUDE.md stale 'delete_audience' reference in the specialism
index — deletion rides on sync_audiences, no separate tool.

No changeset — docs only.

* feat(skills): unify idempotency pattern and add OAuth guidance for sellers

Two cross-cutting fixes on the seller skill's Protocol-Wide Requirements section:

1. Idempotency — replace the manual ctx.store.get('idempotency',...)/put
   pattern (which reinvents the wheel and misses payload-hash conflict
   detection) with the real createIdempotencyStore + createAdcpServer({
   idempotency }) wiring. Matches what build-si-agent already uses. Framework
   handles replay detection, IDEMPOTENCY_KEY_CONFLICT, and ttl validation.

2. OAuth — section was missing entirely. Adds a focused subsection covering
   the one thing the seller actually has to do right (WWW-Authenticate Bearer
   challenge + RFC 9728 / RFC 8414 metadata documents). Clients handle
   discovery and NeedsAuthorizationError; sellers just need to advertise
   correctly. Points at npx @adcp/client diagnose-auth for validation.

No changeset — docs only.

* feat(skills): composition guide for OAuth + signing + idempotency

Round-4 cross-cutting test scored 2/5 on composition despite 4-5/5 on each
concern in isolation. The builder surfaced four specific gaps at the
boundaries between OAuth, signing, idempotency, and async submitted flows:

1. Middleware mount order — OAuth → signature verify → JSON parse → MCP
   transport → framework idempotency. Raw body must reach the verifier;
   bearer validation runs first so we don't canonicalize unauthenticated
   requests.

2. Principal threading — resolveSessionKey MUST come from an authenticated
   identity (OAuth subject + verified signing keyid), not self-declared
   buyer metadata. Two buyers on one OAuth tenant with different signing
   keys must not share a replay namespace.

3. 401 disambiguation — OAuth fails first. Bearer challenge goes out alone
   when the bearer is missing/expired; Signature challenge only when the
   request is authenticated but signed wrong. RFC 7235 allows multiple
   challenges; the signed-requests grader doesn't exercise the combined
   case.

4. Idempotency × submitted — ground-truthed against src/lib/server/
   idempotency/store.ts: the framework caches successful mutations
   including submitted envelopes, injects replayed:true on replay, and
   uses a 120s in-flight TTL matching the AdCP working-response timeout.
   Second IO is not created on replay; setup.url stays stable.

All ground-truthed against src/ before writing; the example middleware
chain is not speculative.

No changeset — docs only.

* docs(skills): name idempotency error codes, JWT claim mapping, cross-links

Three R5 follow-ups, all ground-truthed against src/:

1. Name the three idempotency error codes the framework emits, with a
   table keyed on what triggers each and what the buyer should do.
   In-flight parallels return SERVICE_UNAVAILABLE with retry_after:1
   (verified in src/lib/server/create-adcp-server.ts). Handler authors
   no longer need to grep framework source to write replay assertions.

2. OAuth claim-to-principal paragraph. Covers aud/iss/scope validation,
   using sub as the principal, multi-tenant composition (tenant:sub to
   avoid sub collisions across tenants), and the OAuth/signing keyid
   reconciliation rule. resolveSessionKey example updated to compose
   tenant, subject, and keyid.

3. Cross-links: the OAuth section and the signed-requests specialism
   section both point down to the composition subsection. Added an
   explicit anchor (composing-oauth-signing-and-idempotency) so the
   links are stable across heading edits.

No changeset — docs only.

* docs(skills): align to upstream serve() composition API

* fix(skills): correct exports, preTransport wiring, and sales-guaranteed task shape

* fix(skills): brand-rights schema accuracy

* chore: regen schemas + add changeset for PR #624

* fix(skills): address expert review blockers on PR #624

* fix(skills): verifyApiKey/verifyBearer/anyOf import path

* docs(skills): expert-review follow-ups (pwr sections, shape fixes, nits)

* docs: add 5.1->5.2 migration guide; trim seller skill length

* docs: link migration guide from CLAUDE.md

* docs: migration guide covers 4.30.1 -> 5.2.0

* docs: migration guide + skills cover 5.2 webhook surface

* fix(skills): webhook JWK example, measurement_windows shape, rights-terms exclusivity

* docs(skills): distinguish submitted vs pending_creatives + inventory-list persistence

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

v3.0.0-rc.4: migrate to reallocation_threshold + human_review_required (breaking)

2 participants