Anti-drift architecture for normative guarantees + two Tier-1 security fixes#5833
Open
bokelley wants to merge 2 commits into
Open
Anti-drift architecture for normative guarantees + two Tier-1 security fixes#5833bokelley wants to merge 2 commits into
bokelley wants to merge 2 commits into
Conversation
…security findings From the 2026-07 AdCP protocol security audit. The recurring finding was a guarantee stated in prose but not enforced at the layer that should hold it, so this lands the enforcement scaffold and two mechanical fixes with their guards. New anti-drift primitives: - specs/spec-anti-drift.md — RFC: gap analysis + layer-ownership model - static/registry/normative-statements/ — registry of security MUSTs/claims tagged by enforcement layer + status, with a coverage linter - scripts/check-normative-coverage.cjs — linter + dashboard + --min-enforced one-way floor (fails when an enforced statement is lost, not when a gap is added) - scripts/run-doctests.cjs — offline doctest lane; runs `lang doctest` blocks in a scrubbed env, refuses shell blocks (executes PR-authored code in CI) - .github/workflows/normative-guarantees.yml — blocking gate (permissions: read-only) Tier-1 fixes (each flips its registry row to enforced): - example_use -> adcp_use in the request-signing reference verifier — dead code that disabled the key-purpose separation check; guarded by a doctest - add `intent` to the governance-phase enum so a buyer can express the intent-phase check the spec already mandates; guarded by tests/governance-phase-enum.test.cjs + changeset Remaining Tier-1 findings are design decisions tracked in #5826-#5830. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-existing defect (unrelated to the anti-drift work on this branch): MDX reads `<5` as a JSX tag with an invalid name and fails to parse, which trips the local Mintlify broken-links pre-push preflight. The file is not published (absent from docs.json) so CI never caught it. One-character escape; renders identically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Argus is not auto-reviewing this PR because it modifies protected paths that require human review (.changeset/fix-governance-phase-intent.md, .github/workflows/normative-guarantees.yml, static/schemas/source/enums/governance-phase.json, static/schemas/source/governance/check-governance-request.json). A human reviewer should review and merge this PR; Argus will resume on subsequent PRs once these changes land on main.
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
Follow-up to the 2026-07 AdCP protocol security audit. The audit's through-line was that AdCP's strongest guarantees are stated in prose but diluted at the layer that enforces them — a
MUSTwith no schema backing, a reference snippet never executed, a fact hand-copied across docs that drifted. This PR lands the enforcement scaffold plus the two mechanical Tier-1 fixes, each shipped with the check that would have caught it.Anti-drift architecture (
specs/spec-anti-drift.md)Principle: make the enforcement layer the source of truth; push each check to the cheapest deterministic layer; reserve the governing agent for judgment calls.
static/registry/normative-statements/) — every securityMUST/claim tagged by enforcement layer + status, so "prose MUST with no backing" becomes countable.scripts/check-normative-coverage.cjs) — validates the registry, fails when anenforcedentry's linked check is missing, and enforces a--min-enforcedone-way floor (adding agapnever fails; losing an enforced statement does).scripts/run-doctests.cjs) — runs```lang doctestblocks deterministically; the lane the existing live-agent snippet suite couldn't be for pure-logic bugs..github/workflows/normative-guarantees.yml).Tier-1 fixes (each flips its registry row to
enforced)example_use→adcp_usein the request-signing reference verifier (docs/building/by-layer/L1/security.mdx). Dead code that silently disabled the key-purpose separation check. Guarded by a doctest.intentadded to thegovernance-phaseenum so a buyer can express the intent-phase check the spec already mandates (the token MUST carry phase: "intent"). Guarded bytests/governance-phase-enum.test.cjs+ changeset.Reviews addressed
Ran through
code-reviewerandsecurity-reviewer. Applied: scrubbed the doctest child env + dropped the shell runner +permissions: contents: read(executes PR-authored code in CI); wired the enum guard into CI; switched the ratchet to a--min-enforcedcount so cataloguing a gap isn't blocked; sanitized the registrynotesto state the rule/fix rather than ship exploit recipes to a public repo; added aclasscheck and a resolve-under-ROOT guard to the linter; addedNS-GOV-004for the wire-behaviour MUST the enum test doesn't cover.Remaining Tier-1 findings (design decisions, tracked)
Not mechanical — each needs a design call: #5826 (verify_brand_claim key→brand binding), #5827 (report_plan_outcome budget attestation), #5828 (rights_constraint verification), #5829 (content-digest on spend calls), #5830 (TMP privacy claim). Rolls up to #3925.
Incidental
One unrelated commit escapes a pre-existing
<5%inspecs/spec-guardian.md(unpublished; MDX couldn't parse it, tripping the local docs preflight). One-char change, renders identically.Verification
Precommit passed (4380 unit tests, typecheck). Doctest lane, enum guard, and coverage linter all green. Schema rebuild clean.
🤖 Generated with Claude Code