The problem this solves

Most teams have ADRs. They sit in docs/adr/, written in good faith, referenced in onboarding, and then quietly ignored by every AI coding agent that touches the codebase. The agent has no access to them, no enforcement against them, and no way to know when code it generates violates a decision the team already made.

mneme adr import closes that gap without requiring you to rewrite your ADRs or adopt a new format. Add a single section to flag machine-readable constraints, run the import, and those decisions enter the same enforcement pipeline as any other Mneme rule — applied at whichever workflow boundary your integration supports, from pre-mutation prevention to the CI gate.

Step 1: add a Constraints section

ADR files need YAML frontmatter and an optional ## Constraints section. The rest of the body is free-form markdown and is preserved as rationale.

---
id: ADR-005
title: Brand vs Package Namespace Enforcement
status: accepted
priority: foundational
date: "2026-05-04"
scope: code
---

The package namespace is `mneme`. The brand name is "Mneme HQ".
These must never be conflated in code-bearing surfaces.

## Constraints

- FORBID_LITERAL: pip install mneme  # correct: pipx install "mneme-hq>=0.5.0"

The canonical, mechanically enforced directive is FORBID_LITERAL. Its value is matched as an exact, case-sensitive token sequence — no stemming, no synonyms, no regex. When a checked artifact contains the literal, the verdict is a deterministic FAIL.

Typed rules with path applicability

Where a rule applies is a separate question from what it matches. The structured form attaches explicit path selectors to the same typed rule:

## Constraints

- FORBID_LITERAL:
    value: install legacy-client
    include_paths:
      - "**/*.md"
    exclude_paths:
      - "docs/history/**"

include_paths/exclude_paths determine applicability only — they never loosen the exact-match semantics. Every check reports how each scoped rule resolved against the real target path, so you can see which rules applied, which were skipped, and why. Rules without selectors stay corpus-wide.

YAML frontmatter is required. Nygard-style **Status:** Accepted headers are not parsed. Adding a 6-line frontmatter block per file is a one-time, scriptable conversion.

Legacy directives

Three older directive kinds are still accepted for compatibility. They compile into decision constraints that feed retrieval guidance and audit visibility — but they are not typed rules and do not by themselves produce deterministic verdicts:

Directive (legacy) What it compiles to Deterministically enforced?
FORBID_DEPENDENCY: X Stored as "no X" in decision constraints (retrieval-gated heuristic) No — guidance only
FORBID_PATH: glob Stored verbatim for visibility and audit No
REQUIRE_PATH: glob Stored verbatim for visibility and audit No
FORBID_LITERAL: value A typed rule with exact semantics Yes — corpus-wide FAIL

New policies should author FORBID_LITERAL first. Unknown directive kinds raise a parse error rather than being silently dropped — a typo should not defeat governance.

Step 2: preview the import

The default mode is dry-run. Nothing is written.

mneme adr import docs/adr --memory .mneme/project_memory.json

Output shows the active decision set and each parsed constraint:

ADR import preview
============================================================

Active set (4 ADRs):
  [ADR-001] status=active
      (no ## Constraints directives)
  [ADR-005] status=active
      rule: FORBID_LITERAL "pip install mneme"  # correct: pipx install "mneme-hq>=0.5.0"
      constraint: FORBID_PATH site/use-cases/**/Mneme HQ*

ADRs with status proposed, deprecated, or superseded are excluded from the active set. Explicit supersession via the supersedes frontmatter field is handled silently. Active-active contradictions (two accepted ADRs sharing the same scope, priority, and date) surface as diagnostics and block the apply step until resolved.

Step 3: apply the import

mneme adr import docs/adr --memory .mneme/project_memory.json --apply

The import writes atomically: the updated memory is serialized to a sibling temp file, then swapped in with os.replace(). A failed write leaves the original file intact. No backup files are created — the original is always recoverable from git history.

On success, the command reports what was written:

Wrote 4 decisions to .mneme/project_memory.json

Step 4: verify enforcement

Once imported, typed rules enter the MemoryStore → DecisionRetriever → check_prompt pipeline unchanged — and unlike legacy constraints, they are enforced corpus-wide: the query does not need to retrieve the decision for the rule to fire.

mneme check \
  --memory .mneme/project_memory.json \
  --input agent-output.sh \
  --query "local development setup"

If agent-output.sh contains pip install mneme (use pipx install "mneme-hq>=0.5.0" instead):

FAIL  [ADR-005] FORBID_LITERAL "pip install mneme" -- trigger: pip install mneme  # correct: pipx install "mneme-hq>=0.5.0"
      Brand vs Package Namespace Enforcement

Result: FAIL

A path-scoped rule additionally reports its applicability outcome against the target path (PATH PASS / SKIP / UNKNOWN lines), so scoped policies stay explainable:

PATH SKIP   [ADR-009] docs/history/** excluded by selector
Result: PASS

The same input without the forbidden literal passes cleanly with Result: PASS.

Conflict handling

Same-id collision. If an incoming ADR id already exists in the target memory file's decisions[] array, the import refuses. Pass --update-existing to overwrite the existing entry in place.

Active-active contradiction. If two accepted ADRs share the same scope, priority, and date, the compiler cannot pick a winner deterministically. The import surfaces this as a diagnostic and refuses --apply unless --approve-conflicts is also passed. The fix is to edit the contradicting ADRs: mark one superseded, give one a higher priority, or give one a newer date.

Dogfooding

Mneme HQ enforces its own ADRs this way. The docs/adr/ directory in the public repo contains the team's architectural decisions, and ADR-005 (namespace enforcement) is imported into the project's live memory with its FORBID_LITERAL typed rule active. When an AI-assisted task generates content using the wrong package namespace, Mneme catches it deterministically.

The runnable demo is at examples/demo-adr-import.py in the repo. It imports the ADR corpus, applies it to a fresh memory file, and shows the WARN and PASS verdicts end-to-end.