Skip to content

Latest commit

 

History

History
1054 lines (806 loc) · 32.9 KB

File metadata and controls

1054 lines (806 loc) · 32.9 KB

Invar: Technical Design

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.


Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                         INVAR SYSTEM                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │   Protocol   │  │  Perception  │  │    Guard     │          │
│  │  (INVAR.md)  │  │  (Map, Sig)  │  │  (Enforce)   │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
│         │                 │                 │                   │
│         └─────────────────┴─────────────────┘                   │
│                           │                                     │
│  ┌────────────────────────┴────────────────────────┐           │
│  │              External Integrations               │           │
│  │  deal │ returns │ pydantic │ hypothesis │ pytest │           │
│  └──────────────────────────────────────────────────┘           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Component 1: Protocol (INVAR.md)

Purpose

The Protocol is a document that defines how agents should work. It provides significant value with zero dependencies.

The Six Laws

┌────────────────────────────────────────────────────────────────┐
│ 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)     │
└────────────────────────────────────────────────────────────────┘

Contracts Before Code (DX-91)

The ONE mandatory workflow rule: Write @pre/@post contracts 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:

  1. Write @pre with ALL parameters (including defaults)
  2. Write @post receiving only result
  3. Add doctests: normal case, zero/boundary case, edge case
  4. Run invar guard — fix violations before proceeding

Component 2: Perception

Purpose

Provide agents with compressed, high-signal context for large codebases.

2.1 Map Generator

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"]
  }
}

2.2 Signature Extractor

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_total

Output:

# 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."""
    ...

Component 3: Guard

Purpose

Enforce architecture rules. Prompts can be ignored; Guard cannot.

3.1 Rules Engine

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

3.2 Honest Limitations

Guard CAN detect:

  • Static import and from ... import statements
  • 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"]

3.3 Violation Report

$ 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.

3.4 CI Integration

# .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 errors

Tip: Install invar-tools as a dev dependency with uv add --dev invar-tools invar-runtime, then run uv run invar guard.


Component 4: Testing Strategy

Philosophy

Tests serve multiple purposes:

  1. Documentation - Show how to use the code
  2. Verification - Prove correctness
  3. Regression - Catch future breakage
  4. Specification - Define expected behavior

Different test types serve different purposes.

4.1 Doctest (Recommended for All)

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

4.2 Property Tests

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 <= amount

Pros:

  • 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

4.3 Recommended Strategy

[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

Component 5: Core/Shell Gray Areas

The Problem

Real-world code doesn't always fit cleanly into Core vs Shell.

Practical Guidelines

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 call

Current 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)))

Decision Framework

┌─────────────────────────────────────────────────────────────┐
│              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     │
└─────────────────────────────────────────────────────────────┘

CLI Design

# 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 version

MCP Server (DX-16)

Invar provides an MCP (Model Context Protocol) server for AI agent integration.

Tools

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

Configuration

invar init creates .mcp.json at project root with auto-detected execution method:

{
  "mcpServers": {
    "invar": {
      "command": "uvx",
      "args": ["invar-tools", "mcp"]
    }
  }
}

MCP method priority:

  1. uvx (recommended - isolated environment, always latest)
  2. command (if invar is in PATH)
  3. python (fallback to current interpreter)

Agent Instructions

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

File Structure for Invar-enabled Projects

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

Phase 2 Design: Adoption Improvements

Configuration Sources

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()  # defaults

Pattern-based Classification

Problem: 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)

Flexible invar init

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 file

Implementation Phases

Phase 1: Protocol + Guard (MVP) ✅ Complete

Deliverables:

  • INVAR.md template
  • invar guard command
  • Basic pyproject.toml configuration
  • invar init command

Value: Architecture enforcement, contract checking

Phase 2: Adoption ✅ Complete

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

Phase 3: Guard Enhancement ✅ Complete

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-pure mode
  • New core/purity.py module

Value: Catch common pureness violations; better line count accuracy

Phase 4: Perception ✅ Complete

Deliverables:

  • invar map command (with AST-based reference analysis)
  • invar sig command
  • JSON output for agent consumption
  • core/references.py, core/formatter.py, shell/commands/perception.py

Value: Context compression for large projects

Phase 5: Guard Refinement ✅ Complete

Deliverables:

  • Shell Result validation (warn when Shell functions don't return Result)
  • Unified rule signatures
  • RuleConfig as Pydantic model

Value: Self-consistency, cleaner codebase

Phase 6: Verification Completeness ✅ Complete

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_lines config option

Phase 7: Agent-Native Foundation ✅ Complete

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

Phase 8: Agent Efficiency ✅ Complete

Goal: Optimize for Agent iteration speed.

Deliverables:

  • --changed mode (git-modified files only)
  • --agent mode (JSON output with fix instructions)
  • @pre param mismatch detection

New files: shell/git.py

Phase 9: Release ✅ Complete

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)

Phase 10: Agent-Native Advanced (Long-term)

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

Phase 11: Language-Agnostic Protocol (LX-05) ✅ Complete

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

Phase 12: TypeScript Tooling (LX-06) ✅ Complete

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

Phase 13: Extension Skills (LX-07) ✅ Complete

Goal: Optional specialized skills for quality assurance.

Deliverables:

  • invar skill CLI command
  • Skill registry and templates
  • /security and /acceptance extension skills
  • Skill isolation options (--quick, --standard, --deep)

Dependencies

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

Bootstrap Strategy

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.

Lessons Learned from Bootstrap

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.

Future Considerations

Guard Improvements:

  1. Separate code/docstring line counts: Report Function 'foo' has 55 lines (35 code, 20 docstring)
  2. Per-zone limits: Different max_function_lines for Core (50) vs Shell (80)
  3. invar guard --explain: Show why each file was classified as Core/Shell
  4. 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


Distribution

Package Structure (v1.0+)

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)

Distribution Channels

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

Installation Options

# 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

invar init Behavior

$ 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)

Version Strategy

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 Roles

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