Governed evolution for prompts and skills — learn from runs, propose bounded improvements, stay within contracts.
Project description
Holdfast
Stable outcomes, smarter prompts.
The destination is fixed. The route gets better.
Holdfast separates what your downstream systems depend on (frozen) from how you deliver it (evolvable), then uses evidence from real usage to improve the route while guaranteeing the destination doesn't change.
Two ways to use it
Python pipelines — your pipeline logs evidence, holdfast detects drift, Claude Code reviews and proposes improvements. Install both the Python lib and the Claude skill.
Claude Code only — for evolving CLAUDE.md, coding instructions, skills, and other prompt files. Just install the skill. No Python lib needed.
Install
For Python pipelines (lib + skill):
pip install holdfast
Plus the Claude Code skill below.
For Claude Code only (skill only):
# Personal — available across all projects
mkdir -p ~/.claude/skills/holdfast
cp skills/holdfast/SKILL.md ~/.claude/skills/holdfast/SKILL.md
# Or via plugin
/plugin add kevintelford/holdfast
Quick start — Python pipeline
1. Create a contract
By convention, contracts live under holdfast/contracts/ in your project root:
holdfast/
contracts/
my-pipeline/
├── contract.yaml
├── frozen/
│ └── output_schema.json
├── evolvable/
│ └── prompt.md
├── invariants.yaml
└── detection.yaml # optional — pattern detection rules
contract.yaml:
name: my-pipeline
version: 1
evolution_mode: monitor # monitor | semi-auto | auto
frozen:
output_schema: "frozen/output_schema.json"
evolvable:
prompt: "evolvable/prompt.md"
2. Log evidence from your pipeline
from holdfast import Contract, log_run
contract = Contract.load("holdfast/contracts/my-pipeline/")
prompt = contract.get_evolvable("prompt")
result = your_llm_call(prompt, data)
log_run(contract=contract, output=result, passed=validate(result))
Or use the decorator:
from holdfast import Contract, track
contract = Contract.load("holdfast/contracts/my-pipeline/")
@track(contract)
def classify(item: dict) -> dict:
prompt = contract.get_evolvable("prompt")
# ... your LLM call ...
return result
# Each call logs evidence. Pass/fail determined by invariant validation.
3. Check for patterns
python -m holdfast status holdfast/contracts/my-pipeline/
# Contract: my-pipeline (v1, mode: monitor)
# Evidence: 47 runs (42 passed, 5 failed)
# Alerts: 1 — score variance on 'score' (stddev=0.89)
Or in Python:
from holdfast import Contract, check_contract
contract = Contract.load("holdfast/contracts/my-pipeline/")
alerts = check_contract(contract)
4. Propose and apply an evolution
In Claude Code with the holdfast skill:
"Look at the evidence in holdfast/contracts/my-pipeline/ and propose an evolution."
Or programmatically with your own LLM:
from holdfast import Contract, propose_evolution, apply_evolution
contract = Contract.load("holdfast/contracts/my-pipeline/")
proposal = propose_evolution(contract=contract, llm=my_llm_callable, min_runs=10)
if proposal:
print(proposal.diff)
print(proposal.rationale)
apply_evolution(contract=contract, proposal=proposal)
5. Rollback if needed
from holdfast import Contract, rollback, list_versions
contract = Contract.load("holdfast/contracts/my-pipeline/")
versions = list_versions(contract) # [1, 2, 3]
rollback(contract, to_version=2)
Quick start — Claude Code skill
Install the skill, then in any project with a contract:
"What patterns do you see in my evidence?" "Evolve the prompt based on the last 20 runs." "Check holdfast/contracts/my-pipeline/ for drift."
The skill reads evidence, analyzes patterns, and proposes bounded edits to evolvable surfaces. Frozen surfaces are never touched. You approve before anything changes.
Contracts
A contract separates outcome (frozen) from method (evolvable):
- Frozen surface: output schemas, response formats, scoring scales, coding standards. Protected.
- Evolvable surface: prompts, examples, reasoning instructions. Improves from evidence.
- Invariants (
invariants.yaml): automated checks that must pass before and after changes. - Detection rules (
detection.yaml): pattern detection across runs (variance, drift, failure rate).
Evolution modes
| Mode | Behavior |
|---|---|
monitor |
Detect and alert only. Default. |
semi-auto |
Detect, propose, human approves. |
auto |
Detect, propose, apply if invariants pass. |
Set in contract.yaml as evolution_mode. Graduate when you trust the contract.
Invariant types
| Type | What it checks |
|---|---|
schema |
JSON Schema validation against a frozen schema file |
contains |
Field value is one of the allowed values (scalar) or contains all required values (list) |
custom |
External Python script — passes output as JSON on stdin, checks exit code |
Detection rule types
| Type | What it detects |
|---|---|
variance |
Field values vary too much within a window. Optional group_by to check per-group (e.g. per question). |
drift |
Field average shifted between baseline and recent windows |
failure_rate |
Too many failed runs in a window |
Storage
Everything is flat files in .holdfast/ inside each contract directory:
.holdfast/
├── evidence/ # JSON files, one per run
└── versions/ # snapshots + evolution records
Human-readable, greppable, no database.
Gitignore
Add this to your project's .gitignore:
**/.holdfast/
Evidence and version snapshots are managed state, not source. They can get large with many runs. If you want to track them (e.g., for team review of evidence), remove this line — the files are plain JSON and git handles them fine.
Inspired by
Memento-Skills (Zhou et al., 2026) demonstrated that agents can improve by evolving external artifacts rather than retraining models. Holdfast applies that insight with governance — frozen contracts, invariant validation, audit trails, and graduated trust levels for enterprise pipelines.
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 holdfast-0.1.0.tar.gz.
File metadata
- Download URL: holdfast-0.1.0.tar.gz
- Upload date:
- Size: 42.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5a834fc33409c3713fddf71f241166bb094f197f77a31b27bba22f5ee004fa0
|
|
| MD5 |
7d08ced5498e990a906f7a3152b7cd68
|
|
| BLAKE2b-256 |
c4c418e7e4d8a88243bc8d16eface0a0c0d90701057d4553e3c30875fff9b8c8
|
File details
Details for the file holdfast-0.1.0-py3-none-any.whl.
File metadata
- Download URL: holdfast-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69f9f02a2245ab16030ad651ca50715b9f8697323c35c42af8836b1964316255
|
|
| MD5 |
e26634d4cd11028444886d4d7e868967
|
|
| BLAKE2b-256 |
c974034a7f532ac56c2548f8121bc23295d7a220c1cc25a9b278a20371bca39d
|