Why CI enforcement matters
- Generation-time hooks cover interactive and agentic Claude Code sessions
- Code can also arrive from manual edits, other tools, or agents without hook coverage
- CI enforcement is the backstop — every commit checked regardless of how it was produced
- Positions governance at three layers: generation time → MR review → CI gate
How it works
mneme checkvalidates a file or diff against the decision corpus in.mneme/project_memory.json- Run against changed files in a merge request to surface any architectural violations
- Exit code 2 = violation found → pipeline job fails → MR blocked
- Exit code 0 = no violations → pipeline job passes
Example pipeline
# .gitlab-ci.yml (governance job)
mneme-governance:
stage: test
image: python:3.11-slim
script:
- pip install mneme
- git diff --name-only $CI_MERGE_REQUEST_DIFF_BASE_SHA...HEAD | xargs -I{} mneme check {}
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Status
- CI enforcement gate is Phase 1 current focus — see the roadmap
- The
mneme checkcommand is available today viapip install mneme - The GitLab CI job above is the reference integration pattern
- Managed GitLab CI template artifact coming in Q3 2026
Layered governance model
Layer 1: Generation-time enforcement via Claude Code hooks
Layer 2: Merge request review (human + AI)
Layer 3: CI gate via Mneme in GitLab CI
What the gate actually checks
The CI step runs mneme check --mode strict against the MR diff and the project's decision corpus. The check returns a structured verdict per touched file, with three possible outcomes:
- PASS — the diff respects the relevant decisions. Exit code 0, pipeline green.
- WARN — the diff introduces something the corpus flags as policy-bordering (an unapproved dependency, a scope-boundary edge case). Exit code 0 in default mode, surfaces a structured note in the job log.
- FAIL — the diff violates a hard architectural decision. Exit code 2 in strict mode, fails the job, blocks the MR until resolved or explicitly overridden.
Override events are themselves tracked: an override is a decision record with a rationale, scope, and expiry, committed as part of the same MR. Weakening a constraint costs the same as introducing one — a structured edit reviewers can see, not a quiet bypass.
FAQ
Should we run this on every merge request or only on the default branch?
What if a merge request needs to override a decision intentionally?
project_memory.json as part of the same MR — with rationale, scope, and expiry. The check picks it up automatically. The override is reviewable in the MR diff like any other change, so weakening a constraint is a visible decision rather than a silent merge.