Most prompts in a repo were pasted out of a chat window and never versioned. Here is what a lockfile's discipline gives them — and three prompts in full.
The problem: a prompt with no provenance
Here is a scene you have probably watched. Someone scrolls up a chat window, copies a paragraph that told a model to do the thing, pastes it into a terminal, and it works. Nobody writes down what the prompt was; it survives as muscle memory and a scrollback buffer.
Months later somebody pastes it again. The model changed. The library changed. The author has moved teams. The output is still plausible, so nobody reports that it is wrong — it quietly returns something else, and the difference is absorbed into the code.
That is what makes prompts different from other artifacts in a repo. A broken script throws. A stale dependency changes behaviour and says nothing about what changed. A prompt with no owner, no version, no date and no test rots silently. The fix is not a more clever prompt but a more constrained one: a name, a version, a stored input, a pass condition.
A prompt that produces part of your work is a dependency in the same sense as a library: it has an upstream (the model, which changes without notice and without a changelog you control), callers and a contract. Without a version you cannot tell whether a bad result came from a changed model, a changed prompt, or your own code. They rot quietly; the cure is bookkeeping, not cleverness.
What "treat it like code" means concretely
Four fields. That is the whole system.
| Field | What it prevents | How you check it |
|---|---|---|
| Owner | The prompt outliving everyone who knows why | A name in the file; nobody to answer means it is dead |
| Version | Silent behaviour change on edit | A number that changes on every edit, with that edit's date |
| Test case | A prompt that still runs but no longer does the job | One stored input and a pass condition you can read |
| Last verified | A prompt that was fine against a model that no longer exists | The date you last ran the stored input |
One file per prompt, in a prompts/ directory: the body is the prompt text, the test file next to it holds one input and the condition the output must satisfy.
name: triage-failing-test
version: 1
owner: <team or person>
last_verified: <YYYY-MM-DD>
models: [<model ids you run>]
test_case: prompts/tests/triage-failing-test.md
not_covered: "reads only the CI output you paste; runs nothing"
The loop is boring, which is the point. Paste the stored input, check the output against the pass condition. If it fails, decide: the prompt drifted, so fix it and bump the version, or the model drifted, so adapt it to the model you run — bump the version either way.
The "not covered" line is not a disclaimer but a routing rule: it tells the next reader which file to open.
Three prompts, written out in full
A note before you copy these: the three prompts below were written with AI assistance. They are original — none is reproduced from our paid packs — and each ships with a test case, because the argument above only works if the prompts obey it.
1. Triage a failing CI test
For: getting from a red build to one next experiment, without letting the model jump to a fix it cannot justify.
Fill in: the exact failing command, its complete output (not a screenshot), what changed recently, the runtime versions.
You are triaging one failing CI job. You cannot run code.
INPUT
- Command: <exact command that failed>
- Output: <complete stdout and stderr, unedited>
- Recent change: <what changed, or "none">
- Runtime: <language, version>
OUTPUT — these four sections, in this order, nothing else:
1. HYPOTHESIS — one sentence: the most likely cause.
2. EVIDENCE — up to three bullets. Each quotes a line of the output above and says what it rules
in or out. No bullet may cite a line that is not in it.
3. NEXT PROBE — one non-destructive command, runnable from the repository root (no deletes, no
force pushes, no database writes, no cache clearing), that separates your hypothesis from the
next most likely cause. Then: which result means which.
4. CONFIDENCE — high, medium or low, then the single fact that would change it.
RULES
- If the output is truncated, write "INSUFFICIENT OUTPUT" and ask for the rest.
- Do not propose a fix; a fix comes after the probe.
- Do not invent file paths, environment variables, stack frames or versions.
A bad output: a hypothesis with no quoted evidence, a "fix" that rewrites the function under test, a probe that clears a cache, or "high" confidence with nothing behind it.
Test case: a failing test whose cause is a changed default value. The probe must separate that from other causes; if it is "add a print statement", the prompt was not obeyed.
2. Audit an API change for breaking callers
For: deciding whether a change to a published interface is safe, and who has to be told before it ships.
Fill in: language and runtime version, the change (a diff, or old and new signature), who consumes the interface, whether it shipped.
You are auditing one change to a published API for breaking changes.
INPUT
- Runtime: <language and version>
- The change: <diff, or old -> new>
- Consumers: <teams, packages, or "unknown">
- Shipped: yes / no
OUTPUT — a table with these columns:
| Change | Breaking | Affected callers | Migration |
- "Change": what changed, as name and old -> new.
- "Breaking": yes, no, or unknown. Use "unknown" if the input shows too little.
- "Affected callers": name only callers you can prove from the input; otherwise write "not
determinable" — do not guess.
- "Migration": the smallest change that keeps a caller working: the call site before -> after.
THEN
- ASSUMPTIONS: every assumption you made.
- QUESTIONS FOR THE OWNER: questions whose answers would change the table, or "none".
- FIRST CHANGE: the change to make first to reduce breakage, and why.
RULES
- Judge the change as specified, not as it might be later.
- If the calling code is not in the input, say so in the table instead of assuming call sites.
A bad output: every row marked "breaking: no", callers invented from the shape of the API, a migration that quietly changes the contract, or a "Migration" cell that is really a rewrite.
Test case: a change that keeps the type but alters a parameter's default value. The answer must be "breaking: yes" or "unknown" — "no" means the prompt was not obeyed.
3. Turn a paragraph into a testable contract
For: converting a fuzzy feature description into something a reviewer can check line by line.
Fill in: the description in one paragraph, the shape it must fit (a type, a schema, an API payload), the non-negotiable constraints.
You are converting a feature description into a testable contract.
INPUT
- Description: <one paragraph>
- It must fit: <type, schema or payload shape>
- Not negotiable: <constraints, or "none">
OUTPUT
1. AMBIGUOUS WORDS
Every word two engineers could read differently ("fast", "recent", "valid", "default"). For
each: the two readings and the question that resolves it. If none, write "none".
2. CONTRACT
One code block: the type or schema, every field required unless the description gives a
default. No extra properties unless it allows them.
3. ACCEPTANCE CRITERIA
Five numbered criteria, each checkable by a test rather than by opinion, each naming an
observable: a value, a status code, an error, a log line. None may say "should" or "properly".
4. THE TWO CASES MOST LIKELY MISSED
Two edge cases, each an input and its expected result: one a boundary (empty, zero, a maximum,
the limit), one a conflict between two rules.
RULES
- If the description does not say, do not decide it silently: record the gap in section 1, take
the stricter reading in section 2, list it under assumptions.
- Do not add features or fields the description does not ask for.
A bad output: a schema where every field is optional, criteria that say the endpoint "responds quickly", or edge cases that restate the happy path.
Test case: a description containing the word "fast". If the ambiguous-words section comes back empty and the contract has no measurable bound, the prompt was not obeyed.
What we sell, and what we do not claim
We sell prompt packs, built the same way as the three above: every prompt ships with a stated input, a stated output shape, and a note on what it does not cover. Nothing in a pack is a magic phrase, and no pack claims your output will match a sample.
AI Prompts for Developers — $29. Twenty copy-paste templates for code review, schemas and API design.
https://aukawave8.gumroad.com/l/developer-productivity-pack
Notion Code Snippets Template — $15. A cheatsheet, a library, and a place for snippets you will reuse.
https://aukawave8.gumroad.com/l/code-snippets
Notion Research Wiki Template — $18. Notes, sources and citations, so a claim keeps its source attached.
https://aukawave8.gumroad.com/l/research-wiki
AI Prompts for Freelancers — $29. Proposals and client work, for technical freelancers.
https://aukawave8.gumroad.com/l/freelancer-pro
What we do not claim: a prompt cannot know your system, cannot make the judgement call about your data, and cannot be verified by whoever did not write the test case. Every pack states its coverage, so you can tell when a prompt is out of its depth.
If you take one thing away
A prompt is a dependency, and an unversioned one that fails silently is the worst kind there is. Give it four fields — owner, version, test case, date — and the failure stops being silent. The date is the one everyone skips: a prompt verified four months ago against a changed model is unverified, whatever its filename.
Take one prompt you already use. Write down its owner, its version, and one input you can paste into it. Run it. If you cannot say what a passing answer looks like, you do not have a prompt yet — you have a habit.
Note on authorship: the three prompts in this post were written with AI assistance. They are original, and none is taken from our paid packs. We have not attached sample outputs, because a prompt's output is model-dependent — each ships with the pass condition you should check against your model.
Top comments (0)