Skip to content

Tefx/Invar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,387 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invar Logo

Invar

From AI-generated to AI-engineered code.

Invar brings decades of software engineering best practices to AI-assisted development.
Through automated verification, structured workflows, and proven design patterns,
agents write code that's correct by construction—not by accident.

PyPI version Python 3.11+ License

Dogfooding

What It Looks Like

An AI agent, guided by Invar, writes code with formal contracts and built-in tests:

from invar_runtime import pre, post

@pre(lambda items: len(items) > 0)
@post(lambda result: result >= 0)
def average(items: list[float]) -> float:
    """
    Calculate the average of a non-empty list.

    >>> average([1.0, 2.0, 3.0])
    2.0
    >>> average([10.0])
    10.0
    """
    return sum(items) / len(items)

Invar's Guard automatically verifies the code—the agent sees results and fixes issues without human intervention:

$ invar guard
Invar Guard Report
========================================
No violations found.
----------------------------------------
Files checked: 1 | Errors: 0 | Warnings: 0
Contract coverage: 100% (1/1 functions)

Code Health: 100% ████████████████████ (Excellent)
✓ Doctests passed
✓ CrossHair: no counterexamples found
✓ Hypothesis: property tests passed
----------------------------------------
Guard passed.

🚀 Quick Start

Tool × Language Support

Tool Python Notes
invar guard ✅ Full Static + doctest + CrossHair + Hypothesis
invar sig ✅ Full Signatures + contracts
invar map ✅ Full Symbol map + reference counts
invar refs ✅ Full Cross-file reference finding
invar doc * ✅ Full Language-agnostic docs tools

📦 Two Packages, Different Purposes

┌───────────────────────────────────────────────────────────────────┐
│  Your Project                                                     │
│  ├── pyproject.toml                                               │
│  │   └── dependencies = ["invar-runtime"]  ← Ships with code      │
│  │                                                                │
│  └── Development (never enters production)                        │
│      └── uv run invar guard  ← Guides agents                      │
└───────────────────────────────────────────────────────────────────┘
Package Purpose Install
invar-runtime Runtime contracts. Add to your project development environment. uv add --dev invar-runtime
invar-tools Development tools. Install in project dev dependencies. uv add --dev invar-tools

Why dev dependencies? Invar works best as project-local development infrastructure: pre-commit hooks, MCP, and CLI verification should all run against the same locked environment via uv run.

🎯 Setup

cd your-project

# Initialize managed files
uv add --dev invar-tools invar-runtime
uv run invar init

# Optional: target a different instruction file
uv run invar init --file AGENTS.md

# Optional: preview migration/write plan without changes
uv run invar init --preview

Safe and idempotent — Run invar init anytime. It always merges with existing files, preserving your content.

💡 After claude /init? Just run uv run invar init again to restore Invar configuration.

💬 Example Interaction

You say:

"Add a function that calculates compound interest with validation"

Agent responds (automatically guided by Invar):

Startup status: my-project | main | clean

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Phase: Design (2/4)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

First, I'll define the contract:

@pre(lambda principal, rate, years: principal > 0 and rate >= 0 and years > 0)
@post(lambda result: result >= principal)
def compound_interest(principal: float, rate: float, years: int) -> float:
    ...

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Phase: Validate (4/4)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

$ invar guard --changed
WARN: missing doctest example (compound_interest)

[Agent adds doctest, re-runs guard]

$ invar guard --changed
Guard passed. (1 file, 0 errors)

Final guard status: PASS | 0 errors, 0 warnings

Key insight: The agent verifies and fixes automatically. You review the result, not the process.


Why Invar?

⚠️ The Problem: Unconstrained AI = Unpredictable Quality

Without guardrails, AI-generated code has systematic risks:

  • No specification → Agent guesses intent, misses edge cases
  • No feedback loop → Errors accumulate undetected
  • No workflow → Jumps to implementation, skips validation
  • No separation → I/O mixed with logic, code becomes untestable

Invar addresses each from the ground up.

✅ Solution 1: Contracts as Specification

Contracts (@pre/@post) turn vague intent into verifiable specifications:

# Without contracts: ambiguous
def average(items):
    return sum(items) / len(items)
    # What if empty? Return type?

# With contracts: explicit
@pre(lambda items: len(items) > 0)
@post(lambda result: result >= 0)
def average(items: list[float]) -> float:
    """
    >>> average([1.0, 2.0, 3.0])
    2.0
    """
    return sum(items) / len(items)

Benefits:

  • Agent knows exactly what to implement
  • Edge cases are explicit in the contract
  • Verification is automatic, not manual review

✅ Solution 2: Multi-Layer Verification

Guard provides fast feedback on top of standard type checking. Agent sees errors, fixes immediately:

Layer Tool Speed What It Catches
Type Check mypy ~1s Type errors, missing annotations
Static Guard rules ~0.5s Architecture violations, missing contracts
Doctest pytest ~2s Example correctness
Property Hypothesis ~10s Edge cases via random inputs
Symbolic CrossHair ~30s Mathematical proof of contracts

* Requires separate installation: pip install mypy

┌──────────┐   ┌───────────┐   ┌───────────┐   ┌────────────┐
│ ⚡ Static │ → │ 🧪 Doctest│ → │ 🎲 Property│ → │ 🔬 Symbolic│
│   ~0.5s  │   │   ~2s     │   │   ~10s    │   │   ~30s     │
└──────────┘   └───────────┘   └───────────┘   └────────────┘
Agent writes code
       ↓
   invar guard  ←──────┐
       ↓               │
   Error found?        │
       ↓ Yes           │
   Agent fixes ────────┘
       ↓ No
   Done ✓

✅ Solution 3: Workflow Discipline

Contracts before implementation — the single required rule:

📝 Specify  →  🔨 Build  →  ✓ Validate
     │              │            │
 Contracts        Code        Guard

Guard enforces outcomes, not ceremony. The essential intent: specify with @pre/@post before implementing.

✅ Solution 4: Architecture Constraints

Pattern Enforcement Benefit
Core/Shell Guard blocks I/O imports in Core 100% testable business logic
Result[T, E] Guard warns if Shell returns bare values Explicit error handling

🔮 Future: Quality Guidance

Beyond "correct or not"—Invar will suggest improvements:

SUGGEST: 3 string parameters in 'find_symbol'
  → Consider NewType for semantic clarity

From gatekeeper to mentor.


🏗️ Core Concepts

Core/Shell Architecture

Separate pure logic from I/O for maximum testability:

Zone Location Requirements
Core **/core/** @pre/@post contracts, doctests, no I/O imports
Shell **/shell/** Result[T, E] return types
┌─────────────────────────────────────────────┐
│  🐚 Shell (I/O Layer)                       │
│  load_config, save_result, fetch_data       │
└──────────────────┬──────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│  💎 Core (Pure Logic)                       │
│  parse_config, validate, calculate          │
└──────────────────┬──────────────────────────┘
                   │
                    ▼ Result[T, E]
# Core: Pure, testable, provable
def parse_config(content: str) -> Config:
    return Config.parse(content)

# Shell: Handles I/O, returns Result
def load_config(path: Path) -> Result[Config, str]:
    try:
        return Success(parse_config(path.read_text()))
    except FileNotFoundError:
        return Failure(f"Not found: {path}")

Verification Status

Guard provides clear verification boundaries:

Status Format Purpose
Running invar guard Multi-layer verification
Complete `guard PASS 0 errors`

Historical note: Older versions used extra ceremony like Check-In/Final. Current Invar keeps the rule and drops the ceremony: write contracts first, then let guard enforce outcomes.

Intellectual Heritage

Foundational Theory: Design-by-Contract (Meyer, 1986) · Functional Core/Imperative Shell (Bernhardt) · Property-Based Testing (QuickCheck, 2000) · Symbolic Execution (King, 1976)

Inspired By: Eiffel · Dafny · Idris · Haskell

AI Programming Research: AlphaCodium · Parsel · Reflexion · Clover

Dependencies: deal · returns · CrossHair · Hypothesis


🖥️ Agent Support

Agent Status Setup
Claude Code ✅ Full invar init
Pi ✅ MCP invar init, then add MCP config manually
Cursor ✅ MCP invar init, then add MCP config manually
Other 📝 Manual invar init, then include CLAUDE.md/INVAR.md in your prompt flow

See also: Agent guides in docs/guides/ for editor-specific setup. INVAR.md provides the agent-agnostic protocol.

Claude Code (Full Experience)

All features auto-configured:

  • MCP tools (invar_guard, invar_sig, invar_map)
  • Workflow stays focused on contracts, implementation, and verification
  • Pre-commit hooks

Pi (MCP Support)

Pi can use the same Invar MCP server and protocol docs:

  • Configure MCP as described in the Other Editors flow
  • Keep CLAUDE.md and INVAR.md as the project protocol source
  • Use CLI verification (invar guard) when MCP is unavailable

Cursor (MCP + Rules)

Cursor users get full verification via MCP:

  • MCP tools (invar_guard, invar_sig, invar_map)
  • .cursor/rules/ for workflow guidance
  • Pre-commit hooks

See Cursor Guide for detailed setup.

Other Editors (Manual)

  1. Run invar init
  2. Include generated CLAUDE.md and INVAR.md in your agent's prompt flow
  3. Configure MCP server if supported
  4. Use CLI commands (invar guard) for verification

📂 What Gets Installed

invar init writes this minimal generated surface:

File/Directory Purpose Category
CLAUDE.md Agent guidance with managed Invar block Required
INVAR.md Protocol for AI agents Required
.pre-commit-config.yaml Verification hook config merged by init Required

* mypy hook included in .pre-commit-config.yaml but requires: pip install mypy

Not generated by fresh init: .invar/context.md, .mcp.json, .claude/commands/, skills/hooks scaffolds.

Note: Guard reads config from pyproject.toml ([tool.invar.guard]) or invar.toml ([guard]). .invar/config.toml is deprecated.

Recommended structure:

src/{project}/
├── core/    # Pure logic (@pre/@post, doctests, no I/O)
└── shell/   # I/O operations (Result[T, E] returns)

🔄 Legacy Project Migration

Quick Start: MCP Tools Only

For projects that want Invar's MCP tools without adopting the framework:

uv add --dev invar-tools invar-runtime
uv run invar init

This writes managed files (CLAUDE.md, INVAR.md, .pre-commit-config.yaml) and migrates legacy assets when detected. MCP should then run from the same project environment (for example uv run invar mcp). Your AI agent gets access to:

  • Document tools (invar_doc_toc, invar_doc_read, etc.)
  • Code navigation (invar_sig, invar_map)
  • Basic verification (invar_guard with minimal rules)

Language Support

The onboarding workflow examples include Python pattern guides:

# Error handling: returns library
from returns.result import Result, Success, Failure

def get_user(id: str) -> Result[User, NotFoundError]:
    user = db.find(id)
    if not user:
        return Failure(NotFoundError(f"User {id}"))
    return Success(user)

# Contracts: invar_runtime
from invar_runtime import pre, post

@pre(lambda amount: amount > 0)
@post(lambda result: result >= 0)
def calculate_tax(amount: float) -> float:
    return amount * 0.1

Historical extension-skill workflows (invar skill ..., /invar-onboard) are archived and not part of the current runtime command surface.


⚙️ Configuration

# pyproject.toml

[tool.invar.guard]
# Option 1: Explicit paths
core_paths = ["src/myapp/core"]
shell_paths = ["src/myapp/shell"]

# Option 2: Pattern matching (for existing projects)
core_patterns = ["**/domain/**", "**/models/**"]
shell_patterns = ["**/api/**", "**/cli/**"]

# Option 3: Auto-detection (when no paths/patterns specified)
# - Default paths: src/core, core, src/shell, shell
# - Content analysis: @pre/@post → Core, Result → Shell

# Size limits
max_file_lines = 500
max_function_lines = 50

# Requirements
require_contracts = true
require_doctests = true

# Timeouts (seconds)
timeout_doctest = 60           # Doctest execution timeout
timeout_crosshair = 300        # CrossHair total timeout
timeout_crosshair_per_condition = 30  # Per-function timeout
timeout_hypothesis = 300       # Hypothesis total timeout

# Excluded paths (not checked by guard)
exclude_paths = ["tests", "scripts", ".venv", "node_modules", "dist", "build"]

Pattern Detection

Guard can suggest functional programming patterns to improve code quality:

[tool.invar.guard]
# Minimum confidence for suggestions (low | medium | high)
pattern_min_confidence = "medium"

# Priority levels to include (P0 = core, P1 = extended)
pattern_priorities = ["P0"]

# Patterns to exclude from suggestions
pattern_exclude = []

Available patterns: NewType, Validation, NonEmpty, Literal, ExhaustiveMatch, SmartConstructor, StructuredError

🚪 Escape Hatches

For code that intentionally breaks rules:

# Exclude entire directories
[[tool.invar.guard.rule_exclusions]]
pattern = "**/generated/**"
rules = ["*"]

# Exclude specific rules for specific files
[[tool.invar.guard.rule_exclusions]]
pattern = "**/legacy_api.py"
rules = ["missing_contract", "shell_result"]

🔧 Tool Reference

CLI Commands

Command Purpose
invar guard Full verification (static + doctest + property + symbolic)
invar guard --changed Only git-modified files
invar guard --static Static analysis only (~0.5s)
invar guard --coverage Collect branch coverage from tests
invar init Initialize or migrate managed files
invar init <path> Initialize or migrate managed files
invar init --file AGENTS.md Write managed block to a non-default target file
invar init --preview Show migration/create plan without writing
invar sig <file> Show signatures and contracts
invar map Symbol map with reference counts
invar refs <file>::<symbol> Find all references to a symbol
invar doc toc <file> View document structure (headings)
invar doc read <file> <section> Read specific section by slug/fuzzy/index
invar doc find <pattern> <files> Search sections by title pattern
invar doc replace <file> <section> Replace section content
invar doc insert <file> <anchor> Insert content relative to section
invar doc delete <file> <section> Delete section
invar rules List all rules with severity
invar mcp Start MCP server for Claude Code
invar dev Developer commands for Invar project development
invar version Show version info

MCP Tools

Tool Purpose
invar_guard Smart multi-layer verification
invar_sig Extract signatures and contracts
invar_map Symbol map with reference counts
invar_doc_toc Extract document structure (TOC)
invar_doc_read Read specific section
invar_doc_read_many Read multiple sections (batch)
invar_doc_find Search sections by title pattern
invar_doc_replace Replace section content
invar_doc_insert Insert content relative to section
invar_doc_delete Delete section

📚 Learn More

Created by invar init:

  • CLAUDE.md — Managed Invar block merged with project guidance
  • INVAR.md — Protocol v5.0
  • .pre-commit-config.yaml — Guard hook configuration

Documentation:


📄 License

Component License Notes
invar-runtime Apache-2.0 Use freely in any project
invar-tools GPL-3.0 Improvements must be shared
Documentation CC-BY-4.0 Share with attribution

See NOTICE for third-party licenses.

About

From AI-generated to AI-engineered code. Guides AI agents to write verifiable code through contracts and structured workflows.

Topics

Resources

License

Apache-2.0, GPL-3.0 licenses found

Licenses found

Apache-2.0
LICENSE
GPL-3.0
LICENSE-GPL

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages