Catch contract drift across documentation, configuration, specifications, and tests.
Project description
RepoInvariant
Alpha: catch contract drift across repository artifacts before merge.
RepoInvariant is a deterministic CLI and GitHub Action that checks whether the contracts spread across your repository still agree. It focuses on two expensive, repeatable failure modes:
- environment variables drifting between
.env.example, Docker Compose, Kubernetes, GitHub Actions, and Spring configuration; - requirement IDs disappearing between Markdown, OpenAPI
x-feature-id, and tests.
It reports exact evidence instead of guessing intent. No API key, LLM, or source upload is required.
Status
RepoInvariant v0.1.0 is the first public alpha. The configuration and finding codes may change
before v1.0.0. Pin an exact commit SHA when using the GitHub Action.
Quick start
Install the CLI from PyPI:
uv tool install repoinvariant
cd /path/to/your/repository
repoinvariant init
repoinvariant check .
During development, use:
uv sync --frozen --extra dev
uv run repoinvariant check examples/ticket-service
uv run pytest
A finding looks like this:
compose.yml:12:7: error ENV001: DATABASE_URL is used but missing from the environment contract
docs/requirements.md:18:1: error TRACE001: REQ-HOLD-CREATE is missing from the specification
FAIL: 6 files, 2 errors, 0 warnings
Configuration
Run repoinvariant init to create .repoinvariant.yml:
version: 1
env:
contracts: [.env.example]
compose: [compose*.yml, docker-compose*.yml]
kubernetes: [k8s/**/*.yml]
workflows: [.github/workflows/*.yml]
spring:
- src/main/resources/application*.yml
- src/main/resources/application*.properties
ignore: [CI, HOME, PATH, GITHUB_*, RUNNER_*]
features:
requirements: [docs/**/*.md]
specifications: [openapi*.yml, docs/openapi*.yml]
tests: [tests/**/*, src/test/**/*]
id_pattern: '\bREQ-[A-Z0-9][A-Z0-9-]*\b'
openapi_extension: x-feature-id
requirements_mode: definitions
ignore: []
rules:
ENV001: error
ENV002: warning
ENV003: warning
TRACE001: error
TRACE002: error
TRACE003: error
TRACE004: warning
requirements_mode: definitions counts IDs only when they look like canonical Markdown
definitions (headings, definition lists, and the first column of tables). Set it to mentions for
legacy repositories where any prose reference is authoritative.
The built-in REQ-* pattern is printed verbatim because it is a constrained public identifier
format. Matches from a custom id_pattern are reported as deterministic custom-id-N labels;
source locations remain exact, but arbitrary matched repository text never reaches logs or report
artifacts.
Each finding code can be set to error, warning, or off. This makes staged adoption explicit:
start a noisy rule as a warning, fix the baseline, then promote it to an error. Quote "off" when
editing YAML to avoid YAML 1.1 boolean parsing surprises.
Then run one of the stable report formats:
repoinvariant check . --format text
repoinvariant check . --format json --output repoinvariant-report.json
repoinvariant check . --format markdown --output repoinvariant-report.md
repoinvariant check . --format sarif --output repoinvariant-report.sarif
Exit code 0 means no blocking drift, 1 means a configured contract failed, and 2 means
the command or configuration was invalid. Add --fail-on warning for a stricter merge gate.
GitHub Action
The repository must be checked out before RepoInvariant runs. For supply-chain safety, pin an exact commit SHA:
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: xixvivji/RepoInvariant@<commit-sha>
with:
path: .
format: sarif
output: repoinvariant-report.sarif
no-features: "true" # optional during staged adoption
The action installs only the source bundled with the pinned action revision. It does not transmit repository contents.
The action also accepts no-env and no-features boolean inputs. Prefer rule-level severity
configuration when only one check needs a temporary downgrade.
Finding codes
| Code | Meaning | Default severity |
|---|---|---|
ENV001 |
A consumer uses an environment variable absent from the contract | error |
ENV002 |
A contract variable has no discovered consumer | warning |
ENV003 |
Explicit defaults disagree across artifacts | warning |
TRACE001 |
A requirement ID is absent from the specification | error |
TRACE002 |
A specification ID has no requirement | error |
TRACE003 |
A specification ID has no test reference | error |
TRACE004 |
A requirement appears to be defined more than once | warning |
Design boundaries
RepoInvariant deliberately does not:
- decide whether two differently worded requirements mean the same thing;
- modify repository files automatically;
- compare live infrastructure or databases;
- print secret values found in configuration;
- claim full OpenAPI, Compose, Kubernetes, or Spring validation.
Use their native validators alongside RepoInvariant. RepoInvariant owns the gap between artifacts.
Configured files and report destinations must stay inside the repository. RepoInvariant rejects configuration/output symlinks, limits configuration files to 256 KiB and scanned files to 2 MiB, and fails closed on malformed configured YAML. Custom requirement patterns run with a timeout and one shared matching-time budget. File reads and atomic report writes use no-follow directory descriptors so a concurrent symlink swap cannot redirect them outside the repository.
Roadmap
- Environment contract checks
- Requirement → OpenAPI → test traceability
- Text, JSON, Markdown, and SARIF reports
- Composite GitHub Action
- Version-baseline contracts across Gradle, Docker, CI, and documentation
- Reusable parser plugin API
- PyPI trusted publishing and provenance-attested release automation
- Real-world compatibility fixtures from external projects
See CONTRIBUTING.md to help shape future releases.
License
Apache License 2.0. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file repoinvariant-0.1.0.tar.gz.
File metadata
- Download URL: repoinvariant-0.1.0.tar.gz
- Upload date:
- Size: 100.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57a13c25b97b7face650fdb5ecbcc9c3ad5b85ec8f1a7fa6a39e4f02243d8cda
|
|
| MD5 |
6fc3f6d37fe248f10c83735e3cbaa35a
|
|
| BLAKE2b-256 |
016863c76ba9579338ea800e22a4c228e17bc0a8023b88ea819eebb3c5fad52e
|
Provenance
The following attestation bundles were made for repoinvariant-0.1.0.tar.gz:
Publisher:
release.yml on xixvivji/RepoInvariant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoinvariant-0.1.0.tar.gz -
Subject digest:
57a13c25b97b7face650fdb5ecbcc9c3ad5b85ec8f1a7fa6a39e4f02243d8cda - Sigstore transparency entry: 2279685221
- Sigstore integration time:
-
Permalink:
xixvivji/RepoInvariant@7b6e624dbf45076738e6eaa830340d9ebda8e039 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xixvivji
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7b6e624dbf45076738e6eaa830340d9ebda8e039 -
Trigger Event:
push
-
Statement type:
File details
Details for the file repoinvariant-0.1.0-py3-none-any.whl.
File metadata
- Download URL: repoinvariant-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90b27f6554f5f0afde3bcc2c9a93ef0ff85a183a7bd76c0b1659beff68e3c214
|
|
| MD5 |
f566dd6f4753ef24175203fadceb7d27
|
|
| BLAKE2b-256 |
392a6b769613ab1a6978c01d58b9c3b261f7d3e45f4b669120b739dd6da0ced5
|
Provenance
The following attestation bundles were made for repoinvariant-0.1.0-py3-none-any.whl:
Publisher:
release.yml on xixvivji/RepoInvariant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
repoinvariant-0.1.0-py3-none-any.whl -
Subject digest:
90b27f6554f5f0afde3bcc2c9a93ef0ff85a183a7bd76c0b1659beff68e3c214 - Sigstore transparency entry: 2279685241
- Sigstore integration time:
-
Permalink:
xixvivji/RepoInvariant@7b6e624dbf45076738e6eaa830340d9ebda8e039 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/xixvivji
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7b6e624dbf45076738e6eaa830340d9ebda8e039 -
Trigger Event:
push
-
Statement type: