Dotfile convention + reference workspace

.invariants

What must always remain true — in one file, in plain language.

Not a framework. Not SaaS. A .invariants dotfile you commit like .gitignore. Optional starter kit: multi-repo cascade, vecs code index (no markdown in the corpus), GitHub triage via gh, and a conformance agent that pattern-matches severity to BLOCKED, PROCEED TO TRIAGE, PROCEED, or CLOSE. The dotfile is the law; the agent is the clerk.


Projects grow. Intent fades.

When a system spans many repositories, no one holds the full picture. A change in one repo breaks an assumption in another. Tests pass. Builds succeed. Architecture quietly becomes something else. .invariants makes non-negotiable conditions explicit — for humans in PR review and for agents with codebase access.

01
Descriptive, not prescriptive

Claims in plain language — not a rules engine, not ArchUnit, not linter config.

02
Hierarchical by design

Apex constitution plus per-repo files with inherits. One repo is the degenerate case.

03
Precise, not vague

Verdicts come from threatened severity — FROZEN, VERSIONED, ADDITIVE — not from chat mood.

04
Forks are valid — in the report

Incompatible sub-repo claims may be an intentional fork. Say so in the review. Never store fork status, issue numbers, or “currently failing” in the dotfile.


Built on two established shoulders.

.invariants sits at the intersection of ADRs and architectural fitness functions — and fills the gap neither covers alone.

Architecture Decision Records
Michael Nygard, 2011 · adr.github.io

ADRs capture why a decision was made. The why: field in each assertion is an ADR in miniature.

ADRs document the past. They do not tell an agent what to do when a proposal threatens them.

+
Fitness functions
Ford, Parsons, Kua — 2017 · ThoughtWorks

Automated checks on structure and dependencies. severity and the verdict table are fitness-function logic.

They need executable rules. They cannot express “every consumer hashes identity identically” without bespoke code.

.invariants
natural language · semantic evaluation · agent-native

With an LLM evaluator, assertions are executable. Load the cascade, read real source, match severity. The dotfile is the enforcement surface.

fills: semantic invariants static analysis cannot check.


Dotfile everywhere. Starter kit optional.

Ship .invariants in any repo (Level 1). Use the reference workspace when you want the full loop: code index, issue triage, agent reports (Level 3).

Level 1
Dotfile only

cp .invariants.example .invariants in your repo. No vecs, no agent required.

Level 2
Multi-repo cascade

Apex + per-repo files with inherits and cascades_to. Your own agent rule or review checklist.

Level 3
Reference workspace (starter kit)

bash setup.sh — submodules, Qdrant, index, needs_triage issues, conformance agent in Cursor.

01 —
Write the cascade

Apex /.invariants plus {repo}/.invariants. Claims, severity, optional verify hints. Maintainers only — agents never edit dotfiles.

02 —
Index code, not prose

Starter kit rebuilds a code-only Qdrant collection via vecs query. Markdown on disk orients the agent; it is not embedded — avoids “what we wrote” masquerading as “what runs.”

03 —
Triage from GitHub labels

Fetch needs_triage issues into issues/. Agent writes reports/, posts comments, flips in_triage. Ground truth: code in vecs + label state on GitHub.

04 —
Navigate with ·NAV

Long-lived .md files declare edit mode on line 1: <!-- ·NAV:M --> (map paths), S (sync facts). IMPLEMENTATION_MAP.md is meant to be agent-curated after code discovery.

Materializes (vecs)

Source in product repos — .ts, .js, .sol, .cs by default. PASS / FAIL / UNKNOWN from files you read after vecs query.

Navigates (disk)

CONTRACT.md, map, issues, reports. Wildcard when untagged. Constitution stays in .invariants only.

On forks: Incompatible apex vs sub-repo claims may be an intentional fork. State that in the conformance report. Operators decide. Do not add fork metadata, waivers, or PASS/FAIL notes inside .invariants.


A match statement, not a meeting.

The agent pattern-matches threatened severity. Audit PASS/FAIL is per-run output — never written back into the dotfile.

// enforcement logic — proposal threatens a claim: match threatened_assertion.severity {
FROZEN => BLOCKED VERSIONED => PROCEED TO TRIAGE ADDITIVE => PROCEED None => CLOSE }
Mixed issues: split the recommendation (e.g. BLOCKED for the FROZEN part, PROCEED for the additive part).

Starter kit — try it now

Clone. Two copies. One command.

Batteries-included reference workspace: vendor/vecs submodule, Qdrant (Docker by default), optional macOS daemon for power users, gh for triage. Then open the conformance agent in Cursor.

# Node 18+, Docker Desktop (or vecs install:system on macOS) git clone --recurse-submodules https://github.com/boorich/.invariants-starter-kit cd .invariants-starter-kit cp sentinel.config.yml.example sentinel.config.yml cp .invariants.example .invariants bash setup.sh gh auth login # once, if you use GitHub triage

Host this page on GitHub Pages from /docs. Convention only? Copy .invariants.example into any repo — no starter kit required. File format ↓


What a .invariants file looks like

Deliberately simple. Maintainer-edited YAML. No “currently failing”, no issue numbers, no fork flags in the file.

# /.invariants — apex constitution version: "1.0"
scope: "Shared interface — cross-repo guarantees"
authority: "Maintainers only — not agent-writable"

asserts:
  - id: identity_derivation_consistency
    claim: "All consumers derive identity the same way from the same inputs"
    severity: FROZEN
    verify:
      search: "identity derivation keccak256 encodePacked"
    cascades_to:
      - "service-a/.invariants#identity_derivation"
    why: "Cross-consumer mismatch fails silently at runtime."
# service-a/.invariants inherits: "../.invariants"

asserts:
  - id: identity_derivation
    claim: "identity() uses keccak256(encodePacked(['address'], [addr]))"
    severity: FROZEN
    verify:
      search: "identity keccak256 encodePacked"
      in_file: "src/utils.ts"
    why: "Must match all other consumers exactly."

A constitution, not a linter.

Tools describe what was built. .invariants defines what the system is allowed to remain. Navigational markdown may drift and be repaired; the dotfile is governance.

Take the convention into any repo. Use the starter kit when you want the full agent loop on your machine.