Skip to content

[adr-conformance] Add the mechanical ADR guardrail foundation #129

Description

@chrisdpurcell

Summary

Introduce the first deterministic ADR enforcement layer in Project Standards 5.17.0 without turning the ADR authoring package into a semantic policy engine.

The proposed release boundary is:

  1. package-declared provider input capture;
  2. one generic read-only repository check command;
  3. a new opt-in adr-conformance@1.0 package;
  4. mechanical rule-scope containment;
  5. three static checker kinds; and
  6. one independently tested dogfood decision.

This is the foundation only. Applicability resolution, plan binding, behavioral evidence, waivers, authorization, rule lifecycle, and dual-baseline policy transitions remain later work.

Fresh repository context

Project Standards 5.16.0 is published. The ADR-related 5.17.0 train is already defined:

ADR 1.4 deliberately remains guidance-first. Its provider checks only the three required MADR headings and explicitly does not infer semantic scope from prose. The corpus assessment in docs/reviews/adr-conformance/2026-08-05-1941-adr-1-4-conformance-assessment.md demonstrates the resulting enforcement gap.

The control plane also still owns provider input selection in src/project_standards/control_plane/provider_inputs.py. That module states that its retirement path is payload-declared input shapes. A new adr-conformance package must not add another package-ID branch there.

Problem

Agents can read a correctly bounded ADR and still:

  • apply its rule to the wrong repository population;
  • target paths the ADR explicitly excluded;
  • miss an exact dependency, import, or path obligation;
  • treat an out-of-scope case as an exception;
  • produce a mechanical rule that governs more than the ADR; or
  • report conformance without evaluating every active static rule.

The platform can answer these questions mechanically, but it currently lacks a package-declared way to capture provider inputs and a generic local/CI command equivalent to MCP's aggregate read-provider behavior.

Proposed architecture

1. Keep ADR authoring and enforcement separate

Create a new consumer package rather than expanding adr@1.5 into an enforcement engine:

[payload]
standard = "adr-conformance"
version = "1.0"
availability = "consumer"

[capabilities]
provides = ["adr.conformance"]
consumes_platform = ["project-standards.reconcile"]

[relations]
extends = ["adr"]

The extends edge requires its own project ADR and immutable decision evidence. Enabling adr must not enable conformance automatically.

2. Preserve the authority split

Surface Authority
Decision, rationale, boundary, amendment, supersession ADR prose and frontmatter
Deterministic obligation projection Consumer-owned conformance contract
Rule grammar and checker semantics Immutable adr-conformance payload
Capture, containment, snapshots, limits, dispatch Unified control plane
Protected pass/fail CI and repository policy
Early feedback Local CLI and MCP

The contract is a projection of an accepted decision, never a second or broader decision authority.

3. Use one consumer-owned contract in v1

Initial path:

.standards/extensions/adr-conformance/contracts.toml

It is selected through a closed package option and declared as a referenced extension. The central lock records its path and digest without claiming or rewriting it.

Recommended shape:

schema_version = "1.0"

[capture]
include = [
  "pyproject.toml",
  "src/project_standards/mcp_server/**/*.py",
  "src/project_standards/mcp_services/**/*.py",
]
exclude = []

[[decisions]]
adr_id = "adr-0025-project-standards-mcp-service-and-sdk-boundary"
document = "docs/adr/adr-0025-project-standards-mcp-service-and-sdk-boundary.md"
governed_paths = [
  "pyproject.toml",
  "src/project_standards/mcp_server/**",
  "src/project_standards/mcp_services/**",
]
excluded_paths = [
  "src/project_standards/control_plane/**",
  "standards/**",
]

[[decisions.rules]]
id = "ADR-0025-R1"
kind = "toml-array-member"
target = "pyproject.toml"
pointer = "/project/dependencies"
value = "mcp==2.0.0"

The explicit capture section gives the control plane a fixed, generic location from which to derive the immutable snapshot before provider execution. The provider must verify that capture is no broader than the union of the decision boundaries and that every rule target is captured.

4. Add package-declared provider input capture

Add a generic capture declaration to a Standard Bundle Authoring successor and to executable provider declarations.

Candidate form:

[[input_captures]]
id = "conformance-repository"
kind = "referenced-extension-globs"
extension = "contracts"
include_pointer = "/capture/include"
exclude_pointer = "/capture/exclude"
include_extension_content = true
max_files = 5000
max_total_bytes = 52428800

[[providers]]
id = "verify-conformance"
operation = "verify"
kind = "python"
phase = "verify"
effect = "findings"
input_capture = "conformance-repository"

The exact names require specification, but the contract must provide deterministic expansion, root containment, symlink safety, regular-file checks, size limits, extension bytes and digest, and fail-closed findings.

Compatibility requirement:

  • New declarations take precedence.
  • Existing released payloads continue through the concentrated compatibility seam.
  • No new package-ID branch is permitted.
  • Full retirement of provider_inputs.py is deferred until supported families migrate through successors.

5. Add a generic repository check command

Recommended working surface:

project-standards check [--repo <dir>] [--standard <id>] [--operation <operation>] [--json]

The authoritative default runs all applicable enabled validate, verify, and lint providers. drift-check remains explicit.

The command must:

  • use the same selected-package resolver, capture authority, and dispatcher as reconciliation and MCP;
  • aggregate findings deterministically;
  • support human and versioned JSON output;
  • be read-only;
  • provide distinct clean/findings/invocation exit categories; and
  • allow standard narrowing only for diagnostics, not the protected all-package CI invocation.

This is aggregate checking, not arbitrary provider dispatch, and remains compatible with ADR 0026's MCP surface.

6. Initial closed rule registry

Limit v1 to:

  • toml-value
  • toml-array-member
  • python-import-boundary

No rule may name shell commands, executables, Python symbols, plugins, policy expressions, or network resources.

7. Make scope containment the first guardrail

Before rule evaluation, prove:

  1. the ADR document exists and its id/doc_type match;
  2. the decision is active;
  3. each rule ID is unique and ADR-scoped;
  4. each rule target is contained by governed_paths;
  5. no target intersects excluded_paths;
  6. each target is included in the captured corpus;
  7. capture is no broader than required decision scope; and
  8. malformed, unsupported, or indeterminate contracts fail closed.

The provider does not infer scope from prose. Human review confirms the projection matches the ADR; the mechanical layer prevents the projection from silently governing more than it declares.

Invariants

  • ADR prose remains authoritative.
  • A contract is a deterministic projection only.
  • Every rule has a stable ADR-scoped ID.
  • Rule kinds are closed and declarative.
  • Mechanical scope cannot exceed declared decision scope.
  • Providers consume immutable snapshots only.
  • Shared dispatch stays package-agnostic.
  • Every active static rule runs in every authoritative check.
  • CI is authoritative; hooks and MCP are feedback.
  • Indeterminate blocking rules fail closed.
  • Existing packages remain independently adoptable.
  • Released predecessors remain byte-immutable and selectable.

Interaction with #127 and #128

Recommended sequencing:

  1. Land [adr] Standard defines only supersession, so partial amendment has no sanctioned form #127 as additive adr@1.5.
  2. Decide the generic create-only scaffold refresh mechanism needed by [adr] Bring the active ADR corpus into 1.4 conformance — full remediation backlog for the next release #128 item 1.
  3. Remediate the active ADR corpus using the 1.5 amendment form.
  4. Accept the project ADR authorizing the conformance authority split and generic capture seam.
  5. Implement package-declared capture and the aggregate check command.
  6. Implement adr-conformance@1.0.
  7. Dogfood against remediated ADR 0025.
  8. Qualify the combined 5.17.0 release.

The scaffold refresh decision remains separate. Conformance must not grant overwrite authority over create-only consumer files.

Dogfood

Use ADR 0025 because it already freezes deterministic obligations and has independent conventional tests:

  • exact mcp==2.0.0 dependency pin;
  • only mcp_server may import mcp;
  • mcp_services imports neither mcp nor mcp_server.

Required negative controls:

  • alter the dependency pin;
  • import mcp from a forbidden service path;
  • keep an allowed adapter import green;
  • broaden a rule into an excluded path;
  • remove or mismatch the ADR document;
  • corrupt a target file.

Existing handwritten tests remain in 5.17.0 as independent evidence.

Acceptance criteria

Package and graph

  • adr-conformance@1.0 is a complete immutable consumer payload.
  • Its extends = ["adr"] relationship has accepted decision evidence.
  • Enabling adr alone changes nothing.
  • Catalog 5 advertises one default for the new family without removing predecessors.
  • Package, graph, schema, projection, and distribution checks pass.

Provider input authority

  • The provider receives repository bytes through one declared capture.
  • No new package ID appears in shared dispatch or the legacy compatibility table.
  • Expansion is deterministic, contained, and bounded.
  • Unsafe paths, stale extension digests, missing inputs, and limits fail closed.
  • Existing packages retain equivalent behavior.

Contract and rules

  • Closed schema; unknown fields and rule kinds fail.
  • ADR identity and status are verified.
  • Duplicate decision/rule IDs fail.
  • Every rule target is governed, not excluded, and captured.
  • The three checker kinds have exact documented semantics.
  • Parsing failures are findings, not skips.
  • Findings have stable codes and identities.

Integration

  • Generic CLI and MCP validate_repo produce equivalent findings.
  • Check mode is provably non-mutating.
  • Protected CI runs the all-package command.
  • Dogfood negative fixtures fail both conventional tests and conformance rules.
  • Existing ADR 1.5 heading/frontmatter validation remains independent.

Release

  • The combined train remains MINOR.
  • adr@1.5, adr-conformance@1.0, and any internal authoring successor are advertised without altering released bytes.
  • Source, direct wheel, sdist-derived wheel, and installed payloads are byte-identical.
  • Reconcile converges with an empty second plan.

Explicit 5.17.0 exclusions

  • prose-to-rule extraction;
  • LLM pass/fail review;
  • general policy languages or arbitrary commands;
  • diff-aware applicability;
  • plan/task binding;
  • behavioral evidence adapters;
  • waivers or authorization providers;
  • rule lifecycle and semantic compatibility;
  • base-versus-candidate policy transition evaluation;
  • cross-language adapters beyond the dogfood needs;
  • incremental caching or analytics;
  • agent-authored policy approval;
  • automatic ADR repair.

Known v1 limitation

The first release protects ordinary implementation changes against an accepted candidate contract. It does not prove that a branch modifying the ADR, contract, package, and lock is semantically non-weakening.

For 5.17.0, those remain explicitly reviewed policy surfaces and the dogfood retains independent tests. A later release should add base/candidate policy evaluation, semantic rule digests, transition classification, and authorization-backed changes. This limitation must be documented rather than hidden.

Decisions required before specification

  1. Separate adr-conformance package versus expanding adr.
  2. extends = ["adr"] and the owning project ADR.
  3. One fixed consumer-owned TOML contract for v1.
  4. The initial referenced-extension-globs capture kind.
  5. The three checker kinds.
  6. ADR 0025 dogfood and retention of independent tests.
  7. Final spelling of the aggregate CLI command.
  8. Managed CI workflow versus command/composition only.
  9. Acceptance of the v1 same-change policy limitation.
  10. Confirmation that scaffold refresh stays a separate control-plane decision.
  11. Deferred full retirement of provider_inputs.py.
  12. Confirmation that this scope remains manageable beside [adr] Standard defines only supersession, so partial amendment has no sanctioned form #127 and [adr] Bring the active ADR corpus into 1.4 conformance — full remediation backlog for the next release #128.

Suggested implementation decomposition

  1. Architecture ADR.
  2. Provider input specification.
  3. Control-plane capture implementation.
  4. Generic repository check command.
  5. adr-conformance@1.0 package.
  6. ADR 0025 dogfood and protected gate.
  7. Combined 5.17.0 release qualification.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions