feat(governance): authority_level → reallocation_threshold / human_review_required (#576)#603
Merged
Conversation
…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>
- 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>
6 tasks
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.
8 tasks
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>
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
Closes #576. AdCP removed
budget.authority_levelin favor of two orthogonal fields. There's no rc.4 — this migration lands on the current release line.budget.reallocation_threshold(≥0, inbudget.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 whenpolicy_categoriesincludesfair_housing | fair_lending | fair_employment | pharmaceutical_advertising, or whenpolicy_idsincludeseu_ai_act_annex_iii.Changes
src/lib/governance/— new exports from@adcp/client:buildAnnexIIIPlan(input)— stampshuman_review_required: true.buildHumanOverride({ reason, approver, approvedAt? })— builds the re-sync downgrade artifact; validates reason ≥20 chars and approver email.validateGovernancePlan(plan)— checks invariantsdatamodel-code-generator-style codegen drops fromif/then: budget threshold XOR unlimited, and regulated categories / Annex III policy ids requiringhuman_review_required: true.REGULATED_HUMAN_REVIEW_CATEGORIES,ANNEX_III_POLICY_IDS.authority_levelreference fromsrc/lib/testing/andtest/lib/(~28 occurrences). Mapping:agent_full → reallocation_unlimited: true;agent_limited → keep reallocation_threshold(dropauthority_level);human_required → plan.human_review_required: true(when Annex III) or keepreallocation_threshold(when budget-scoped).skills/build-governance-agent/SKILL.md—check_governancedecision-logic bullets now cover reallocation autonomy, auto-flippinghuman_review_required,data_subject_contestationfindings,human_overrideon re-sync, and the audit-mode-no-downgrade rule.docs/guides/GOVERNANCE-MIGRATION.md— migration guide with the field mapping table and helper usage.Deferred
Storyboards (
compliance/cache/) are synced from upstream perCLAUDE.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 changenode --test test/lib/governance-helpers.test.js— 14/14 new tests passnode --test test/lib/zod-schemas.test.js— 41/41 pass (migrated pharma plan now correctly carrieshuman_review_required: true)npm run test:lib | grep -c authority_level= 0🤖 Generated with Claude Code