prompt-lock
Git-native prompt regression testing with judge calibration.
Guards at the gaps in your LLM CI/CD pipeline. Fails the build when a prompt change causes a regression — and verifies that your LLM judge actually agrees with humans before trusting it as a gate.
pip install prompt-lock
The problem
You changed a prompt. Did your model outputs get worse?
You probably don't know. 82% of teams have no automated detection for prompt quality regressions. The few that do often use LLM-as-a-judge — but their judge is miscalibrated: it disagrees with human evaluators on 20–40% of examples and they've never measured it.
The solution
prompt-lock does three things no other tool does together in a single pip install:
- Detects changed prompts via git diff — only evaluates what changed, keeping costs low
- Verifies judge calibration — runs your LLM judge against human-labeled examples, measures agreement rate and Spearman correlation, and blocks the CI pipeline if the judge can't be trusted
- Regression gate — fails the build if eval scores drop more than a configurable threshold from baseline
Quick start
pip install prompt-lock
cd your-llm-project
prompt-lock init
init creates .prompt-lock.yml, prompts/, tests/test_cases.jsonl, and tests/human_labels.jsonl.
Fill in your test cases:
{"input": "Summarize this article: ...", "output": "The article discusses ...", "expected_output": "A summary of the article."}
Run:
prompt-lock check
Configuration
# .prompt-lock.yml
version: "1"
model: gpt-4o-mini
# Judge calibration — the key differentiator
judge:
enabled: true
human_labels_file: tests/human_labels.jsonl
model: gpt-4o-mini
criteria: "Rate the quality of this response from 0.0 to 1.0."
min_agreement: 0.80 # 80% of examples must agree (within ±0.15)
min_spearman: 0.70 # Spearman correlation with human scores
prompts:
- path: "prompts/*.txt"
name: "My Prompts"
test_cases_file: tests/test_cases.jsonl
evals:
- type: llm_judge
criteria: "Is the response helpful, accurate, and well-structured?"
threshold: 0.70
- type: semantic_similarity
threshold: 0.80
gate:
mode: regression # hard | regression | soft
regression_threshold: 0.05 # fail if score drops >5% from baseline
Eval types
| Type | What it checks | Requires |
|---|---|---|
llm_judge |
LLM scores output against criteria (0.0–1.0) | criteria |
semantic_similarity |
Cosine similarity to expected output (offline, all-MiniLM-L6-v2) | expected_output in test cases |
exact_match |
Exact string match | expected_output in test cases |
regex |
Output matches a regex pattern | pattern |
custom |
Your own Python function fn(input, output) -> float |
custom_fn |
Works with any LLM provider via LiteLLM:
gpt-4o-mini,gpt-4oclaude-haiku-4-5-20251001,claude-sonnet-4-6mistral/mistral-small- Any local model via Ollama:
ollama/llama3
Gate modes
regression (default) — fail if score drops more than regression_threshold from recent baseline. Good for ongoing development.
hard — fail if score is below hard_threshold. Good for critical prompts with known minimum quality.
soft — never fail, warn only. Good for new prompts without established baselines.
Judge calibration
The unique feature. Before running evals, prompt-lock checks whether your LLM judge actually agrees with human evaluators:
prompt-lock calibrate
┌─────────────────────────────────────────────────────────────┐
│ Calibration Summary │
│ │
│ PASSED │
│ │
│ Agreement rate 87.5% (min: 80%) │
│ Spearman r 0.831 (min: 0.70) │
│ Bias +0.042 (positive = judge inflates scores) │
│ Examples 16 │
└─────────────────────────────────────────────────────────────┘
If calibration fails, prompt-lock check exits with code 2 and blocks deployment. Your CI pipeline doesn't trust an uncalibrated judge.
Create tests/human_labels.jsonl:
{"input": "What is 2+2?", "output": "The answer is 4.", "human_score": 1.0}
{"input": "What is 2+2?", "output": "It's roughly 5.", "human_score": 0.0}
{"input": "Explain Python.", "output": "Python is a high-level language.", "human_score": 0.9}
Minimum 5 examples. More is better.
GitHub Actions
# .github/workflows/prompt-lock.yml
name: Prompt Regression Tests
on: [push, pull_request]
jobs:
prompt-lock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # needed for git diff detection
- uses: buildworld-ai/prompt-lock@v1
with:
config: .prompt-lock.yml
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
Or with other providers:
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CLI reference
prompt-lock init # initialize config and example files
prompt-lock check # run regression checks (git-diff aware)
prompt-lock check --all-prompts # eval all prompts, not just changed ones
prompt-lock check --no-calibrate # skip calibration check
prompt-lock check -v # verbose: show per-test-case results
prompt-lock calibrate # run calibration and show detailed results
prompt-lock traces show # show recent eval runs from trace ledger
prompt-lock traces show -n 50 # show last 50 runs
prompt-lock traces diff abc123 def456 # compare scores between two commits
Trace ledger
Every eval run is recorded in a local SQLite database (.prompt-lock/traces.db) with the git commit SHA. This is how regression detection works — it compares current scores to recent passing baselines.
prompt-lock traces show
┌───────────────────────┬─────────┬─────────────────┬───────────┬───────┬──────┐
│ Timestamp │ Commit │ Prompt │ Type │ Score │ Pass │
├───────────────────────┼─────────┼─────────────────┼───────────┼───────┼──────┤
│ 2026-03-27T14:32:01 │ a1b2c3d │ prompts/sum.txt │ llm_judge │ 0.841 │ ✓ │
│ 2026-03-27T14:32:00 │ a1b2c3d │ prompts/sum.txt │ semantic │ 0.923 │ ✓ │
│ 2026-03-26T09:15:44 │ e4f5g6h │ prompts/sum.txt │ llm_judge │ 0.710 │ ✓ │
└───────────────────────┴─────────┴─────────────────┴───────────┴───────┴──────┘
Why not Promptfoo / LangSmith / DeepEval?
| Capability | prompt-lock | Promptfoo | LangSmith | DeepEval |
|---|---|---|---|---|
| Git-diff aware (only eval changed prompts) | ✓ | ✗ | ✗ | ✗ |
| Judge calibration against human labels | ✓ | ✗ | partial | ✗ |
| Block CI if judge is miscalibrated | ✓ | ✗ | ✗ | ✗ |
| Regression gate (baseline comparison) | ✓ | ✓ | ✓ | ✓ |
| Commit-linked trace ledger | ✓ | ✗ | ✓ | ✗ |
| Framework-agnostic (LiteLLM) | ✓ | ✓ | ✗ | ✓ |
| Offline semantic similarity | ✓ | ✗ | ✗ | ✓ |
| Zero hosted infrastructure | ✓ | ✓ | ✗ | partial |
pip install in 30 seconds |
✓ | ✗ | ✗ | ✓ |
Promptfoo was acquired by OpenAI in March 2026 — its roadmap is now OpenAI-aligned. prompt-lock is MIT licensed and provider-agnostic.
Contributing
Issues and PRs welcome. See CONTRIBUTING.md.
License
MIT. Built by BuildWorld.
Guards at the gaps. Nehemiah 4:13.
Metadata
Release files for inject-lock 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| inject_lock-0.1.0.tar.gz | 20.3 kB | Without provenance | Details for inject_lock-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| inject_lock-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for inject_lock-0.1.0-py3-none-any.whl |
Total release size: 41.9 kB
Release files / inject_lock-0.1.0.tar.gz
| Download URL | inject_lock-0.1.0.tar.gz |
|---|---|
| Size | 20.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
794a025b1744905adb559aec138c53ba7125bd6330393428ebf69917393b5cf1
|
|
BLAKE2b-256 checksum How to use checksums |
93c06f0be73cd0e29860e07d47edfbd425e4b1d92af7b0e096742c28f2cef360
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / inject_lock-0.1.0-py3-none-any.whl
| Download URL | inject_lock-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
30949bbf44a65f5685a34b9d332ac46a5dea380dd05f3659e335567b902add4c
|
|
BLAKE2b-256 checksum How to use checksums |
ca37a079cb17989c8ece4ea887c1338a4d33110b595cce2aba79dcf5bbf942d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for inject-lock 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.