Make AI-generated code trustworthy through mathematical verification
Project description
spec-test
Make AI-generated code trustworthy through specification-driven development.
Stop reviewing code. Start verifying specs.
The Problem
AI writes code 100x faster than humans can review it. Traditional code review is now the bottleneck:
Human writes code --> Human reviews code --> Ship
(slow) (slow)
AI writes code --> Human reviews code --> Ship
(fast) (slow)
^
BOTTLENECK
You cannot keep up. And the code AI generates is often correct, but how do you know?
The Solution
Shift review from CODE to SPECS.
Specs are 10x smaller than implementations. If your specs are correct and verification passes, the code is correct by definition.
+------------------+
| YOU REVIEW |
| SPECS |
| (10 minutes) |
+--------+---------+
|
v
+----------------+ +-------------------+ +-----------------+
| AI Generates | --> | spec-test | --> | Verified Code |
| Code | | VERIFIES | | Ready to Ship |
+----------------+ +-------------------+ +-----------------+
Quick Start
Installation
pip install spec-test
# With Z3 formal proofs (optional)
pip install spec-test[z3]
1. Initialize Project
spec-test init
Creates:
design/
issues/ # Why - intentions, decisions
specs/ # What - formal requirements
prompts/ # How - AI instructions
2. Write an Issue First
Document why before what:
# design/issues/001-user-auth.md
## Summary
Add user authentication to the API.
## Motivation
Users need secure access to their data.
## Decision
Use JWT tokens with 24h expiry.
3. Define Specs
# design/specs/auth.md
## User Authentication
### Related Issue
- [001-user-auth](../issues/001-user-auth.md)
### Requirements
- **AUTH-001**: User can login with valid email and password
- **AUTH-002**: Invalid credentials return 401 error
- **AUTH-003**: JWT token expires after 24 hours
- **AUTH-010** [integration]: User session persists to database
4. Implement with Tests
from spec_test import spec
@spec("AUTH-001", "User can login with valid email and password")
def test_login_success():
result = login("user@example.com", "password123")
assert result.success
assert result.token is not None
5. Add Runtime Contracts
from spec_test import contract
@contract(
spec="AUTH-001",
requires=[
lambda email, password: "@" in email,
lambda email, password: len(password) >= 8,
],
ensures=[
lambda result: result.token is not None,
],
)
def login(email: str, password: str) -> LoginResult:
user = db.get_user(email)
if verify_password(password, user.password_hash):
return LoginResult(success=True, token=generate_jwt(user))
return LoginResult(success=False, token=None)
6. Verify
$ spec-test verify
╭─────────────────────────────────────────╮
│ Specification Verification Report │
╰─────────────────────────────────────────╯
✓ AUTH-001: User can login with valid email and password
✓ AUTH-002: Invalid credentials return 401 error
✓ AUTH-003: JWT token expires after 24 hours
! AUTH-010: No test
Summary: 3 passing, 0 failing, 1 missing
Architecture: Functional Core, Imperative Shell
All code should follow this pattern with Dependency Injection:
┌─────────────────────────────────────────────────────────────┐
│ Imperative Shell │
│ (thin layer, injected dependencies) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Functional Core │ │
│ │ (pure functions, all business logic) │ │
│ │ │ │
│ │ • Deterministic (same input → same output) │ │
│ │ • No side effects │ │
│ │ • Easy to test (unit tests) │ │
│ │ • Can be formally proven (Z3) │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ • Database, Email, API calls via DI │
│ • Testable with mocks │
│ • Integration tests verify real I/O │
└─────────────────────────────────────────────────────────────┘
Example
# FUNCTIONAL CORE: Pure function, no dependencies
def calculate_total(items: list[Item]) -> Decimal:
return sum(item.price * item.quantity for item in items)
# IMPERATIVE SHELL: Thin layer with injected dependencies
class OrderService:
def __init__(self, db: Database, email: EmailSender): # DI
self.db = db
self.email = email
def place_order(self, items: list[Item]) -> Order:
total = calculate_total(items) # Call pure function
order = Order(items=items, total=total)
self.db.save(order) # Injected dependency
self.email.send_confirmation() # Injected dependency
return order
Testing Strategy
┌─────────────┬────────────────┬─────────────────────┬───────────┐
│ Test Type │ What │ How │ Speed │
├─────────────┼────────────────┼─────────────────────┼───────────┤
│ Unit │ Pure functions │ Direct call │ Fast (ms) │
├─────────────┼────────────────┼─────────────────────┼───────────┤
│ Mock │ Service wiring │ Inject mocks via DI │ Fast (ms) │
├─────────────┼────────────────┼─────────────────────┼───────────┤
│ Integration │ Real I/O works │ Real DB/API │ Slow (s) │
└─────────────┴────────────────┴─────────────────────┴───────────┘
Aim for ~90% unit/mock tests, ~10% integration tests.
Verification Types
Mark specs with verification type in brackets:
| Type | Meaning |
|---|---|
| (none) | Automated unit test |
[integration] |
Integration test (real I/O) |
[manual] |
Manual human verification |
[contract] |
Runtime contract verification |
[provable] |
Z3 formal proof |
- **AUTH-001**: User can login (unit test)
- **AUTH-010** [integration]: Session persists to database
- **AUTH-020** [manual]: UI follows brand guidelines
- **AUTH-030** [provable]: Token expiry is always positive
Runtime Contracts
Basic Contract
from spec_test import contract
@contract(
spec="CART-001",
requires=[lambda items: len(items) > 0],
ensures=[lambda result: result >= 0],
)
def calculate_total(items: list[Item]) -> Decimal:
return sum(item.price * item.quantity for item in items)
With Invariants
@contract(
spec="CART-002",
requires=[lambda items, discount: discount >= 0],
ensures=[lambda result: result >= 0],
invariants=[lambda items, discount: discount <= 100],
)
def apply_discount(items: list[Item], discount: int) -> Decimal:
total = calculate_total(items)
return total * (1 - discount / 100)
Comparing Pre/Post State with old()
from spec_test import contract, Old
@contract(
spec="LIST-001",
capture_old=True,
ensures=[lambda result, old: len(result) == len(old.items) + 1],
)
def append_item(items: list, item) -> list:
return items + [item]
Z3 Formal Proofs
For mathematical verification of pure functions:
pip install spec-test[z3]
from spec_test import provable, contract
@provable(spec="MATH-001")
@contract(
requires=[lambda x, y: x >= 0, lambda x, y: y >= 0],
ensures=[lambda result: result >= 0],
)
def add_positive(x: int, y: int) -> int:
return x + y
$ spec-test verify --proofs
╭─────────────────────────────────────╮
│ Proof Verification Summary │
╰─────────────────────────────────────╯
Proven: 1 | Refuted: 0 | Unknown: 0
For AI Agents
spec-test is designed for AI-assisted development.
Workflow
Write Issue --> Define Specs --> Implement Code --> Verify --> Review
| | | | |
(why) (what) (how) (check) (approve)
Add to CLAUDE.md
## Specification-Driven Development
This project uses spec-test. Every behavior must be backed by a passing test.
## Architecture
- Pure functions for business logic (Functional Core)
- Dependency Injection for I/O (Imperative Shell)
- Integration tests only for verifying real I/O
## Rules
1. Write an issue before writing specs
2. Every spec ID must have a corresponding @spec test
3. Run `spec-test verify` before committing
4. Prefer pure functions; push side effects to edges
AI Agent Skills
Initialize with skills for Claude:
spec-test init
Installs skills to .claude/skills/:
spec-workflow.md- Complete development workflowspec-issue.md- Writing issuesspec-feature.md- Defining specificationsspec-implement.md- Implementation with architecture principlesspec-verify.md- Running verification
Commands
spec-test verify # Verify all specs have passing tests
spec-test verify --proofs # Include Z3 formal proof verification
spec-test verify --coverage # Include code coverage analysis
spec-test list-specs # List all specifications
spec-test list-specs -i # Show related issues for each spec
spec-test check AUTH-001 # Verify single spec
spec-test init # Initialize spec-test in a project
spec-test context # Output CLAUDE.md for LLM context
Roadmap
Note: spec-test is in active development.
- Spec-to-test linking with
@specdecorator - Runtime contracts with
@contract(requires/ensures/invariants) -
old()support for comparing pre/post state - Z3 formal proof verification with
@provable - Issues-first workflow with
design/structure - AI agent skills
- Hypothesis property testing integration
- Spec coverage reporting
Contributing
git clone https://github.com/Varuas37/spec-test.git
cd spec-test
uv sync
# Run tests
uv run pytest
# Run verification
uv run spec-test verify
License
MIT
Stop reviewing code. Start verifying specs.
pip install spec-test
Project details
Release history Release notifications | RSS feed
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 spec_test-0.3.1.tar.gz.
File metadata
- Download URL: spec_test-0.3.1.tar.gz
- Upload date:
- Size: 112.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc14c79b0c88d0b55aaad1a74266ab93835479fa705925d6bf24a9f2f5df4d14
|
|
| MD5 |
a4e72ea34e2459fc9ddc424b8917dd7b
|
|
| BLAKE2b-256 |
b4f1cdd69a31c38a52c239ccf7ecff9b3f0e79e50c828d9b0557e45ec5837c23
|
Provenance
The following attestation bundles were made for spec_test-0.3.1.tar.gz:
Publisher:
publish.yml on Varuas37/spec-test
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spec_test-0.3.1.tar.gz -
Subject digest:
fc14c79b0c88d0b55aaad1a74266ab93835479fa705925d6bf24a9f2f5df4d14 - Sigstore transparency entry: 885288458
- Sigstore integration time:
-
Permalink:
Varuas37/spec-test@f757186c364c4980d3d56890cbf746628dd15ad7 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Varuas37
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f757186c364c4980d3d56890cbf746628dd15ad7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file spec_test-0.3.1-py3-none-any.whl.
File metadata
- Download URL: spec_test-0.3.1-py3-none-any.whl
- Upload date:
- Size: 47.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6e66720093fae871dfc0c4046771e8583da9eb170e840d58b75368b0de7032e
|
|
| MD5 |
0007392bab2e8bdcc5d552b3cbcb11e7
|
|
| BLAKE2b-256 |
3fe675c9a44ecbc4f87e33b00fa799895c5b14fd9148abe17819590f38ab7cbf
|
Provenance
The following attestation bundles were made for spec_test-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on Varuas37/spec-test
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spec_test-0.3.1-py3-none-any.whl -
Subject digest:
b6e66720093fae871dfc0c4046771e8583da9eb170e840d58b75368b0de7032e - Sigstore transparency entry: 885288519
- Sigstore integration time:
-
Permalink:
Varuas37/spec-test@f757186c364c4980d3d56890cbf746628dd15ad7 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/Varuas37
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f757186c364c4980d3d56890cbf746628dd15ad7 -
Trigger Event:
release
-
Statement type: