Keep spec, tests, and code in sync during AI-assisted development
Project description
Plumb
Plumb is proof of concept software demonstrating how we can manage the "Spec Driven Development Triange". It is very much alpha software, use cautiously.
A tool to keep things true.
plumb keeps your spec, tests, and code in sync during AI-assisted development.
When you work with Claude Code, decisions get made — a caching strategy is chosen, an API contract changes, a behavior is refined. These decisions live in conversation history and staged diffs, but they never make it back to the spec or tests. Over time, the spec drifts from reality, tests cover the wrong behavior, and the codebase becomes its own undocumented source of truth.
Plumb fixes this by intercepting git commit via a pre-commit hook. It analyzes your staged changes and Claude Code conversation, extracts the decisions that were made, and gates the commit on your review. Approved decisions are automatically synced back to the spec and tests. Rejected decisions trigger code modifications to undo them. The result: every committed state has a spec and test suite that could reconstruct the program.
Install
pip install plumb-dev
or
uv add plumb-dev
Quick Start
cd your-project
plumb init
This will:
- Ask for paths to your spec markdown and test directory
- Create a
.plumb/directory for state (commit this to version control) - Install a git pre-commit hook
- Install a Claude Code skill file at
.claude/skills/plumb/SKILL.md - Add a Plumb block to
CLAUDE.md - Create a
.plumbignorefile for excluding irrelevant files from analysis - Parse your spec into requirements
From here, just work normally. Plumb activates when you commit.
How It Works
Committing inside Claude Code
- You run
git commit(or Claude Code does) - The pre-commit hook fires, analyzes the staged diff and conversation log
- It writes pending decisions and exits non-zero, aborting the commit
- Claude Code's skill reads the output and presents each decision:
Question: Should we cache API responses in memory or on disk? Decision made: In-memory cache using a dict. Approve, reject, or edit?
- You respond in chat. The skill calls
plumb approve,plumb reject, orplumb edit - Rejected decisions trigger
plumb modify, which rewrites the staged code - Once all decisions are resolved,
git commitruns again and lands
Committing from the terminal
Same flow, but you drive it with plumb review instead of the skill.
Commands
| Command | What it does |
|---|---|
plumb init |
Initialize Plumb in a git repo |
plumb status |
Show spec files, requirements, pending decisions, coverage |
plumb diff |
Preview what decisions Plumb would extract from staged changes |
plumb review |
Interactively review pending decisions in the terminal |
plumb approve <id> |
Approve a decision and sync it to spec/tests |
plumb approve --all |
Approve all pending decisions at once |
plumb reject <id> --reason "..." |
Reject a decision |
plumb edit <id> "new text" |
Amend a decision's text and approve it |
plumb modify <id> |
Auto-modify staged code to satisfy a rejected decision |
plumb sync |
Sync all unsynced approved/edited decisions to spec and tests |
plumb parse-spec |
Re-parse spec files into requirements |
plumb coverage |
Report code coverage, spec-to-test, and spec-to-code coverage |
Coverage
Plumb tracks three dimensions of coverage:
- Code coverage — pytest line coverage via
pytest --cov - Spec-to-test — which requirements have corresponding tests
- Spec-to-code — which requirements have corresponding implementations
Run plumb coverage to see all three.
.plumbignore
Plumb uses a .plumbignore file with gitignore-style patterns to exclude files from analysis. This keeps noise out of the decision extraction process — lock files, generated code, and other irrelevant diffs won't produce spurious decisions.
Project State
All Plumb state lives in .plumb/ at the repo root:
.plumb/
├── config.json # Spec paths, test paths, settings
├── coverage.json # Cached coverage data
├── decisions.jsonl # Append-only log of all decisions
└── requirements.json # Parsed requirements from the spec
Commit this directory to version control.
Requirements
- Python 3.10+
- A git repository
- An
ANTHROPIC_API_KEYenvironment variable or.envfile (for LLM-powered analysis)
Note: plumb init, plumb status, and plumb review work without an API key. The key is only needed when the hook analyzes diffs or when syncing decisions to spec/tests.
License
MIT
Project details
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 plumb_dev-0.2.2.tar.gz.
File metadata
- Download URL: plumb_dev-0.2.2.tar.gz
- Upload date:
- Size: 237.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5ccbeb109c70b4e3c6ab3e444f1b32cc081c2599dd8fa1387407eb2f02d4c56
|
|
| MD5 |
ac308d43b587d4d9d2ea925abae39461
|
|
| BLAKE2b-256 |
db904c4b6706e8acd522e521da47d89de23f1fad3abf2c35e0a69c811dd99cf4
|
File details
Details for the file plumb_dev-0.2.2-py3-none-any.whl.
File metadata
- Download URL: plumb_dev-0.2.2-py3-none-any.whl
- Upload date:
- Size: 49.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
644893d54d0639794edd96adc9533a60109d5dd57d82c539516010eb19c718de
|
|
| MD5 |
dacc1871d42e86b53d3f0965edcd60a9
|
|
| BLAKE2b-256 |
279a6f0ef62e169ead67980fc5f338fa078122517bc2a19fbe4786cdcd689ef6
|