Prerequisite: Read vision.md for philosophy.
Design Principle: Agent-Native Execution, Human-Directed Purpose. See vision.md.
Mechanism Guides: See reference/ for detailed technical documentation.
┌─────────────────────────────────────────────────────────────────┐
│ INVAR SYSTEM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Protocol │ │ Perception │ │ Guard │ │
│ │ (INVAR.md) │ │ (Map, Sig) │ │ (Enforce) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └─────────────────┴─────────────────┘ │
│ │ │
│ ┌────────────────────────┴────────────────────────┐ │
│ │ External Integrations │ │
│ │ deal │ returns │ pydantic │ hypothesis │ pytest │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
The Protocol is a document that defines how agents should work. It provides significant value with zero dependencies.
┌────────────────────────────────────────────────────────────────┐
│ LAW 1: SEPARATION │
│ Pure logic (Core) and I/O (Shell) must be physically separate │
├────────────────────────────────────────────────────────────────┤
│ LAW 2: CONTRACT COMPLETE │
│ Define COMPLETE, RECOVERABLE boundaries before implementation │
│ Complete = uniquely determines implementation (Clover) │
│ Recoverable = guides fixes when violations occur (Pel) │
├────────────────────────────────────────────────────────────────┤
│ LAW 3: CONTEXT ECONOMY │
│ Read map → signatures → implementation (only if needed) │
├────────────────────────────────────────────────────────────────┤
│ LAW 4: DECOMPOSE FIRST │
│ Break complex tasks into sub-functions before implementing │
│ Implement leaves first, then compose (Parsel: +75%) │
├────────────────────────────────────────────────────────────────┤
│ LAW 5: VERIFY REFLECTIVELY │
│ If fail: Reflect (why?) → Fix → Verify again │
│ Don't just fix—understand first (Reflexion: +11%) │
├────────────────────────────────────────────────────────────────┤
│ LAW 6: INTEGRATE FULLY │
│ Verify all feature paths connect correctly │
│ Local correctness ≠ global correctness (DX-07 post-mortem) │
└────────────────────────────────────────────────────────────────┘
The ONE mandatory workflow rule: Write
@pre/@postcontracts BEFORE implementation.
This replaces the USBV workflow (DX-32), which has been removed per DX-91 simplification. Guard enforces this rule — uncontracted Core functions are rejected.
Why contracts first:
- Contract defines the boundary (what the function promises)
- Implementation fills the interior (how it delivers)
- Doctests verify at the boundary (examples of the promise)
- Guard checks the structure (no I/O in Core, Result in Shell)
Before implementing any Core function:
- Write
@prewith ALL parameters (including defaults) - Write
@postreceiving onlyresult - Add doctests: normal case, zero/boundary case, edge case
- Run
invar guard— fix violations before proceeding
Provide agents with compressed, high-signal context for large codebases.
Input: Project root directory Output: Symbol map with reference counts and contracts
Algorithm:
Phase 1: Discovery
├── Walk directory tree
├── Filter: *.py, exclude __pycache__, tests, .venv, etc.
└── Build file list
Phase 2: Extraction (per file)
├── Parse AST (skip files with syntax errors, log warning)
├── Extract symbols: functions, classes, methods
├── Extract metadata: signature, docstring, decorators
└── Detect contracts (@pre, @post from deal)
Phase 3: Reference Analysis (AST-based)
├── For each file, walk AST to find:
│ ├── Name nodes (variable references)
│ ├── Call nodes (function calls)
│ └── Attribute nodes (method calls)
├── Match references to known symbols
├── Exclude self-references (same file, definition line)
└── Build reference map: symbol → count
Phase 4: Ranking & Formatting
├── Sort symbols by reference count (descending)
├── Apply display rules:
│ ├── refs > 10: Full signature + docstring + contract
│ ├── refs 3-10: Signature + contract summary
│ └── refs < 3: Name only (collapsible)
└── Generate output (human or JSON)
Note on Reference Counting:
- Uses AST analysis, not string matching (avoids false positives from comments/strings)
- Cannot detect dynamic calls (
getattr,__import__) - Cross-module references require import resolution
Output Format (Human):
📁 src/core/pricing.py (156 lines, 8 functions)
🔥 calculate_total(items: list[Item], tax_rate: Decimal) -> Decimal [refs: 47]
│ "Calculate order total with tax."
│ @pre: len(items) > 0, 0 <= tax_rate <= 1
│ @post: result >= 0
│
├─ _apply_discount(price, rate) -> Decimal [refs: 12]
└─ _round_currency(amount) -> Decimal [refs: 8]
ƒ validate_line_item(item: RawItem) -> Result[LineItem, Error] [refs: 23]
"Validate a single line item."
Output Format (JSON for agents):
{
"project": "/path/to/project",
"generated": "2024-12-18T10:00:00Z",
"files": [
{
"path": "src/core/pricing.py",
"lines": 156,
"symbols": [
{
"name": "calculate_total",
"kind": "function",
"line": 42,
"signature": "(items: list[Item], tax_rate: Decimal) -> Decimal",
"docstring": "Calculate order total with tax.",
"contracts": {
"pre": ["len(items) > 0", "0 <= tax_rate <= 1"],
"post": ["result >= 0"]
},
"refs": 47
}
]
}
],
"summary": {
"total_files": 24,
"total_lines": 3420,
"total_symbols": 156,
"hottest": ["calculate_total", "validate_order", "process_payment"]
}
}Purpose: Extract just signatures + contracts for a specific file/symbol.
Use case: Agent needs to understand a dependency without reading full implementation.
invar sig src/core/pricing.py
invar sig src/core/pricing.py::calculate_totalOutput:
# src/core/pricing.py - Signatures only
@pre(lambda items: len(items) > 0)
@pre(lambda tax_rate: 0 <= tax_rate <= 1)
@post(lambda result: result >= 0)
def calculate_total(items: list[Item], tax_rate: Decimal) -> Decimal:
"""Calculate order total with tax."""
...
def validate_line_item(item: RawItem) -> Result[LineItem, ValidationError]:
"""Validate a single line item."""
...Enforce architecture rules. Prompts can be ignored; Guard cannot.
Rule Categories:
| Category | Rules | Severity |
|---|---|---|
| Architecture | Core cannot import I/O modules | ERROR |
| Architecture | Shell functions should return Result | WARNING |
| Contracts | Public Core functions need @pre or @post | WARNING |
| Contracts | Contracts need doctest examples | WARNING |
| Size | File > 500 lines | ERROR |
| Size | Function > 50 lines | WARNING |
| Style | No **kwargs in Core | WARNING |
Guard CAN detect:
- Static
importandfrom ... importstatements - Decorator presence (
@pre,@post) - File and function line counts
- Missing type annotations
Guard CANNOT detect:
- Dynamic imports:
__import__('os') - Method calls on allowed imports:
Path('/tmp').write_text() - Semantic quality of contracts:
@pre(lambda x: True)passes - Runtime behavior
Configuration (pyproject.toml):
[tool.invar.guard]
# Directory classification
core_paths = ["src/core", "src/domain"]
shell_paths = ["src/shell", "src/api", "src/cli"]
# Size limits
max_file_lines = 500
max_function_lines = 50
# Required for Core
require_contracts = true
require_doctests = true
# I/O modules forbidden in Core (static import check only)
forbidden_imports = [
"os", "sys", "socket", "requests", "urllib",
"subprocess", "shutil", "io", "pathlib"
]
# Paths to exclude from checking
exclude_paths = ["tests", "scripts", "migrations"]$ invar guard
Invar Guard Report
==================
❌ ERROR: src/core/pricing.py
Line 15: Imports 'os' (forbidden in Core)
❌ ERROR: src/core/validation.py
File has 342 lines (max: 300)
⚠️ WARNING: src/core/utils.py
Line 45: Function 'parse_date' missing @pre or @post
⚠️ WARNING: src/core/models.py
Line 78: Function 'validate' has no doctest examples
Summary: 2 errors, 2 warnings
Exit code: 1 (errors found)
Note: Guard performs static analysis only. Dynamic imports and
runtime behavior are not checked.
# .github/workflows/invar.yml
name: Invar Guard
on: [push, pull_request]
jobs:
guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install invar-tools
- run: invar guard --strict
# --strict: treat warnings as errorsTip: Install invar-tools as a dev dependency with
uv add --dev invar-tools invar-runtime, then runuv run invar guard.
Tests serve multiple purposes:
- Documentation - Show how to use the code
- Verification - Prove correctness
- Regression - Catch future breakage
- Specification - Define expected behavior
Different test types serve different purposes.
Purpose: Documentation + simple verification Location: In docstrings
def calculate_discount(amount: Decimal, rate: Decimal) -> Decimal:
"""
Apply discount rate to amount.
Examples:
>>> from decimal import Decimal
>>> calculate_discount(Decimal("100"), Decimal("0.1"))
Decimal('90.00')
>>> calculate_discount(Decimal("100"), Decimal("0"))
Decimal('100.00')
>>> calculate_discount(Decimal("0"), Decimal("0.5"))
Decimal('0.00')
"""Conventions:
- Include: normal case, zero case, boundary case
- Keep examples simple and focused
- Run with:
pytest --doctest-modules
Purpose: Edge case discovery, specification Location: Choose based on project preference
Option A: Inline (Agent-friendly)
# In source file, after function definition
if __debug__:
from hypothesis import given, strategies as st
@given(
amount=st.decimals(min_value=0, max_value=1e6, allow_nan=False),
rate=st.decimals(min_value=0, max_value=1, allow_nan=False)
)
def test_discount_properties(amount, rate):
result = calculate_discount(amount, rate)
assert result >= 0
assert result <= amountPros:
- Test and code in same file (no context switch)
- Modification triggers test update awareness
- Removed in production with
python -O
Cons:
- Requires pytest configuration
- Files become longer
- Less common in Python ecosystem
Required configuration for inline tests:
[tool.pytest.ini_options]
python_files = ["*.py"]
python_functions = ["test_*"]
testpaths = ["src"]Option B: Separate Directory (Convention-friendly)
tests/
├── core/
│ ├── test_pricing.py
│ └── test_validation.py
└── conftest.py
Pros:
- Follows pytest conventions
- Source files stay short
- Standard tooling works out of box
Cons:
- Context switch between files
- Risk of forgetting to update tests
- Agent needs to access multiple files
[tool.invar]
# Choose test style: "inline" or "separate"
test_style = "inline" # Default: agent-friendly| Test Type | Location | Purpose |
|---|---|---|
| Doctest | Docstring | Documentation, simple cases |
| Property (inline) | Source file | Core function specification |
| Property (separate) | tests/ | Complex multi-module tests |
| Integration | tests/integration/ | Cross-module, I/O tests |
Real-world code doesn't always fit cleanly into Core vs Shell.
Logging:
# Core: Use structured return values, not logging
def validate(data: Input) -> Result[Output, list[ValidationError]]:
errors = []
if not data.name:
errors.append(ValidationError("name", "required"))
# Return errors instead of logging them
# Shell: Log at the boundary
result = validate(data)
if result.is_failure():
logger.warning(f"Validation failed: {result.failure()}")Configuration:
# Core: Accept config as parameter
def calculate_tax(amount: Decimal, tax_rates: TaxConfig) -> Decimal:
...
# Shell: Load and inject config
config = load_config("tax_rates.toml") # I/O here
result = calculate_tax(amount, config) # Pure callCurrent Time:
# Core: Accept time as parameter
def is_expired(expiry: datetime, now: datetime) -> bool:
return now > expiry
# Shell: Inject current time
expired = is_expired(token.expiry, datetime.now())Random Values:
# Core: Accept random value as parameter
def select_winner(participants: list[str], random_index: int) -> str:
return participants[random_index % len(participants)]
# Shell: Generate and inject
import secrets
winner = select_winner(participants, secrets.randbelow(len(participants)))┌─────────────────────────────────────────────────────────────┐
│ Is this operation deterministic? │
│ │
│ YES (same input → same output) │ NO (external state) │
│ ↓ │ ↓ │
│ CORE │ SHELL │
│ │ │
│ Examples: │ Examples: │
│ - Math operations │ - File I/O │
│ - Data transformation │ - Network calls │
│ - Validation logic │ - Database queries │
│ - Business rules │ - Current time │
│ │ - Random generation │
└─────────────────────────────────────────────────────────────┘
# Perception
invar map [path] # Generate map (human format)
invar map [path] --json # Generate map (JSON format)
invar map [path] --top 10 # Show top 10 most-referenced symbols
invar sig <file> # Extract signatures
invar sig <file>::<symbol> # Extract specific symbol
# Guard
invar guard [path] # Run architecture checks
invar guard --strict # Treat warnings as errors
invar guard --json # Output as JSON for tooling
# Utilities
invar init # Initialize INVAR.md and config
invar version # Show versionInvar provides an MCP (Model Context Protocol) server for AI agent integration.
| Tool | Purpose | Replaces |
|---|---|---|
invar_guard |
Smart Guard verification (static + doctests) | pytest, crosshair |
invar_sig |
Show function signatures with @pre/@post | Reading entire files |
invar_map |
Symbol map with reference counts | grep for definitions |
invar init creates .mcp.json at project root with auto-detected execution method:
{
"mcpServers": {
"invar": {
"command": "uvx",
"args": ["invar-tools", "mcp"]
}
}
}MCP method priority:
uvx(recommended - isolated environment, always latest)command(ifinvaris in PATH)python(fallback to current interpreter)
The MCP server provides instructions to enforce tool usage:
- Check-In requirement (display guard status + top entry points in first message)
- Tool substitution rules (use MCP tools instead of Bash commands)
- Task completion definition
project/
├── pyproject.toml # Invar + tool configuration
├── INVAR.md # Protocol document (for agents)
│
├── src/
│ ├── core/ # CORE - Pure logic
│ │ ├── __init__.py
│ │ ├── models.py # Pydantic models
│ │ ├── pricing.py # Business logic + inline tests
│ │ └── validation.py # Validation rules
│ │
│ └── shell/ # SHELL - I/O adapters
│ ├── __init__.py
│ ├── api.py # HTTP handlers
│ ├── database.py # DB operations
│ └── files.py # File operations
│
└── tests/
├── integration/ # Cross-module tests
└── e2e/ # End-to-end tests
Problem: Requiring pyproject.toml excludes scripts, notebooks, and legacy projects.
Solution: Support multiple configuration sources with priority:
Priority (highest to lowest):
1. pyproject.toml [tool.invar.guard] # Standard Python projects
2. invar.toml [guard] # Standalone config
3. Built-in defaults # Fallback
invar.toml Format:
# invar.toml - standalone configuration
[guard]
core_paths = ["src/core"]
shell_paths = ["src/shell"]
max_file_lines = 500
max_function_lines = 50
require_contracts = true
require_doctests = true
forbidden_imports = ["os", "sys", "socket", "requests", "urllib", "subprocess", "shutil", "io", "pathlib"]
exclude_paths = ["tests", ".venv"]
# Pattern-based classification (optional)
core_patterns = []
shell_patterns = []Config Loading Algorithm:
def load_config(project_root: Path) -> RuleConfig:
# Try sources in priority order
if (pyproject := project_root / "pyproject.toml").exists():
config = parse_pyproject(pyproject)
if config:
return config
if (invar_toml := project_root / "invar.toml").exists():
return parse_invar_toml(invar_toml)
if (deprecated := project_root / ".invar/config.toml").exists():
warn_deprecated(deprecated)
return RuleConfig() # defaultsProblem: Requiring src/core and src/shell directories forces project restructuring.
Solution: Support glob patterns for flexible classification.
Configuration:
[tool.invar.guard]
# Option 1: Path-based (default, for new projects)
core_paths = ["src/core"]
shell_paths = ["src/shell"]
# Option 2: Pattern-based (for existing projects)
core_patterns = [
"**/domain/**",
"**/models/**",
"**/services/internal/**",
"**/core/**"
]
shell_patterns = [
"**/api/**",
"**/views/**",
"**/cli/**",
"**/handlers/**",
"**/services/external/**"
]
# Exclude from all checking
exclude_patterns = ["**/legacy/**", "**/generated/**"]Classification Priority:
1. Explicit exclude_patterns → Skip file entirely
2. core_patterns match → Classify as Core
3. shell_patterns match → Classify as Shell
4. core_paths contains file → Classify as Core
5. shell_paths contains file → Classify as Shell
6. Neither → Uncategorized (no Core/Shell rules applied)
Pattern Matching Rules:
- Uses glob syntax (fnmatch)
**matches any directory depth- Patterns are relative to project root
- First match wins (patterns checked before paths)
Updated Behavior:
$ invar init
# Step 1: Detect config location
pyproject.toml exists?
├── Yes → Add [tool.invar.guard] to pyproject.toml
└── No → Create invar.toml
# Step 2: Create protocol files
├── Create INVAR.md (always)
├── Create CLAUDE.md (always)
└── Create .invar/context.md (always)
# Step 3: Create directories (optional)
Create src/core and src/shell? [Y/n]
├── Yes → Create directories with __init__.py
└── No → Skip (user will use patterns)CLI Options:
invar init # Interactive mode with menus
invar init --preview # Non-interactive preview for migration/create plan
invar init --file AGENTS.md # Write managed block to non-default target fileDeliverables:
- INVAR.md template
invar guardcommand- Basic pyproject.toml configuration
invar initcommand
Value: Architecture enforcement, contract checking
Goal: Lower barriers for existing projects to adopt Invar.
Deliverables:
- Multiple configuration sources (pyproject.toml, invar.toml)
- Pattern-based Core/Shell classification
- Flexible
invar init(works without pyproject.toml)
Value: Zero-refactor adoption for existing projects
Goal: Enhance verification for better self-dogfooding during Invar development.
Deliverables:
- Function-internal import detection (not just top-level)
- Impure function call detection (datetime.now, random.*, open, print)
- Code line count excluding docstrings/comments
-
invar guard --strict-puremode - New
core/purity.pymodule
Value: Catch common pureness violations; better line count accuracy
Deliverables:
-
invar mapcommand (with AST-based reference analysis) -
invar sigcommand - JSON output for agent consumption
- core/references.py, core/formatter.py, shell/commands/perception.py
Value: Context compression for large projects
Deliverables:
- Shell Result validation (warn when Shell functions don't return Result)
- Unified rule signatures
- RuleConfig as Pydantic model
Value: Self-consistency, cleaner codebase
Goal: Fix critical gaps in what Guard can verify.
Deliverables:
- Class method extraction in parser.py
- Contract/size/doctest rules applied to methods
- Purity checks (internal imports, impure calls) for methods
-
exclude_doctest_linesconfig option
Goal: Detect Agent-specific failure modes.
Deliverables:
- Empty contract detection (
@pre(lambda: True)) - Redundant type detection (isinstance-only when typed)
- Concrete fix suggestions with lambda skeletons
New files: core/contracts.py, core/suggestions.py
Goal: Optimize for Agent iteration speed.
Deliverables:
-
--changedmode (git-modified files only) -
--agentmode (JSON output with fix instructions) - @pre param mismatch detection
New files: shell/git.py
Goal: Enable adoption by other projects.
Deliverables:
- PyPI release (
pip install invar-tools/pip install invar-runtime) - Documentation (README, VISION, consolidated docs)
- CI templates (GitHub Actions)
Goal: Full Agent-native architecture.
Rule Engine:
- Rules YAML化 - Machine-readable with priorities
- Rule conflict resolution
- Phase-aware contract validation (historical: USBV precheck, superseded by DX-91 guard enforcement)
Config & Profiles:
- Config profiles ("strict", "standard", "relaxed" presets)
- Configurable impure list (user-defined)
Guard Enhancements:
invar guard --explain(show classification reasons)- Per-zone size limits (Core: 50, Shell: 80)
- Transitive impurity detection
Goal: Support multiple programming languages.
Deliverables:
- Language-agnostic INVAR.md templates
- TypeScript contract examples (Zod schemas)
- Language detection in templates
- Template variables for language-specific content
Goal: Verification for TypeScript projects.
Deliverables:
- TypeScript guard support (
guard_ts.py) - Zod schema verification
- JSDoc doctest extraction and execution
- Hypothesis/property testing for TypeScript
Goal: Optional specialized skills for quality assurance.
Deliverables:
-
invar skillCLI command - Skill registry and templates
-
/securityand/acceptanceextension skills - Skill isolation options (--quick, --standard, --deep)
invar-runtime (lightweight):
deal >= 4.0 # Contracts engine
returns >= 0.20 # Result type
invar-tools (includes invar-runtime):
typer >= 0.9 # CLI framework
rich >= 13.0 # Pretty output
pydantic >= 2.0 # Validation
hypothesis >= 6.0 # Property testing
crosshair-tool # Symbolic verification
mcp # Model Context Protocol
Development:
pytest >= 7.0
mypy >= 1.0
ruff >= 0.1
Invar itself follows Invar principles:
src/invar/
├── core/ # Pure logic (AST parsing, rule checking)
│ ├── parser.py # Parse Python files, extract symbols
│ ├── rules.py # Rule definitions and checking
│ └── models.py # Pydantic models for symbols, violations
│
└── shell/ # I/O (file system, CLI)
├── cli.py # Typer commands
├── fs.py # File system operations
└── config.py # Configuration loading
Invar v1.0 is written with human oversight. Once stable, Invar helps maintain itself.
When Invar was used to check itself, several issues were discovered:
| Issue | Root Cause | Fix |
|---|---|---|
returns API misuse |
No usage examples in docs | Added Common Pitfalls section |
deal @pre signature mismatch |
Examples only showed single-param functions | Added multi-param example |
| .venv scanned (1800 files) | Default excludes too narrow | Expanded default exclude list |
| Class methods flagged as functions | ast.walk() traversed all nodes |
Changed to tree.body only |
| CLI functions too long | Typer declarations are verbose | Extracted helper functions |
Key Insight: Documentation examples should cover common edge cases, not just happy paths.
Guard Improvements:
- Separate code/docstring line counts: Report
Function 'foo' has 55 lines (35 code, 20 docstring) - Per-zone limits: Different
max_function_linesfor Core (50) vs Shell (80) invar guard --explain: Show why each file was classified as Core/Shell- Static contract validation: Check @pre lambda signature matches function
Init Improvements: 5. Smart framework detection: Detect Django/Flask and suggest appropriate patterns 6. Config validation: Warn when core_patterns and shell_patterns overlap 7. Path existence check: Warn when configured paths don't exist
IDE Integration: 8. Real-time feedback: Show violations while coding 9. Quick fixes: Auto-extract helper functions when limit exceeded
Invar uses a two-package architecture:
| Package | Size | Purpose |
|---|---|---|
invar-runtime |
~3MB | Runtime contracts (@pre, @post, must_use, invariant) |
invar-tools |
~100MB | Development tools (guard, map, sig, MCP server) |
invar-runtime (PyPI)
└── src/invar_runtime/
├── contracts.py # @pre/@post, Contract class
├── decorators.py # @must_use, @strategy, @skip_property_test
├── invariant.py # Loop invariants
└── resource.py # @must_close
invar-tools (PyPI)
├── src/invar/
│ ├── core/ # Parser, rules, models
│ ├── shell/ # CLI, file system
│ ├── mcp/ # MCP server
│ └── templates/ # Files copied on init
└── (depends on invar-runtime)
| Channel | Content | Target Users |
|---|---|---|
| PyPI invar-tools | CLI tool + templates | Developers using AI |
| PyPI invar-runtime | Runtime contracts | Projects using contracts |
| uvx | No-install execution | Quick usage |
| GitHub | Source + dev docs | Contributors |
# Recommended: install as project dev dependency
uv add --dev invar-tools invar-runtime
# Then use via uv run
uv run invar guard
uv run invar init$ uv run invar init
✓ Added [tool.invar.guard] to pyproject.toml
✓ Created INVAR.md (Invar Protocol)
✓ Created CLAUDE.md (customize for your project)
✓ Created .pre-commit-config.yaml (verification hook)Protocol and tool versions are separate:
INVAR.md v5.0 # Protocol version (MAJOR.MINOR)
invar-tools 1.0.2 # Tool version (semver)
invar-runtime 1.0.2 # Runtime version (semver)
| File | In Package | Copied by Init | Purpose |
|---|---|---|---|
| INVAR.md | ✅ | ✅ | Protocol reference |
| CLAUDE.md.template | ✅ | ✅ (as CLAUDE.md) | Project guide |
| context.md.template | ✅ | ✅ (as .invar/context.md) | Context management |
| proposal.md.template | ✅ | ✅ (as .invar/proposals/TEMPLATE.md) | Protocol change proposals |
| AGENTS.md | ❌ | ❌ | Optional, in docs/ |
| DESIGN.md | ❌ | ❌ | Dev docs only |
| VISION.md | ❌ | ❌ | Dev docs only |