Development record
Prompts Are Versioned Like Code
Press Release
abcd treats agent prompts as versioned artefacts. Each prompt carries a version number, a changelog, and a known-good baseline. Editing a prompt requires running its golden-test fixtures and showing a diff in output quality before the change is accepted. Reverts are first-class. Prompt evolution becomes traceable, not a series of unverified edits.
"We had a moment where someone tweaked a prompt and our oracle audits started returning weird verdicts," said Bob, staff engineer. "Golden tests caught it eventually. abcd now makes prompt changes a deliberate act with diff-on-update, so we'd have caught it at edit time."
Why This Matters
abcd ships three prompt-quality layers (B+C+D from the brief): golden-test fixtures per agent, prompt linter, and periodic SOTA-audit oracle prompt. These catch rot but don't prevent it — a prompt edit can sneak in unaudited and break things downstream.
Prompt registry + versioning is the heavier rigour layer (option E from the brief, deliberately deferred). It becomes worthwhile once there are several months of prompt evolution to learn from. Treating prompts as code (with version, diff, review, revert) is the long-term right answer for a 13+ agent system.
What's In Scope
- Per-prompt version field (
## Prompt Version: X.Y) - Per-prompt changelog (
agents/<name>/CHANGELOG.md) - Diff-on-update workflow: prompt edit → run golden tests → show output diff → confirm
- Revert capability via git + version field bookkeeping
- Promotion of
## Last SOTA Audit:footer (added in the baseline) to a structured changelog entry
What's Out of Scope
- Storing prompts in a database rather than
.mdfiles (overengineered) - Cross-prompt dependency tracking (e.g., "this prompt assumes that prompt's output format")
- A/B testing prompts in production (separate concern)
Acceptance Criteria
BDD format, per
itd-1-acceptance-gates. These gates are checked byintent-fidelity-reviewerwhen this intent moves toshipped/.
- Given any agent prompt at
agents/<name>.md, when the prompt linter runs (lint_prompts.py), then the prompt declares bothprompt_version: X.Y.Z(per itd-5) AND a## Changelogsection listing every version with date and one-line summary — missing changelog is aPQlint blocker. - Given an agent prompt is edited, when the user attempts to commit the change without bumping
prompt_version, then the pre-commit hook refuses the commit and points to the version-bump rule. - Given a prompt edit bumps
prompt_version, when the pre-commit hook runs, then it executes the agent's golden-test fixtures AND surfaces a side-by-side diff of pre-edit vs post-edit fixture output for the user to confirm before the commit completes. - Given a prompt change has been merged, when a regression is later discovered, then the user can run
abcd prompts revert <agent> --to <version>and the prompt file is rewritten back to the named version's content (sourced from git history) AND the changelog records the revert. - Given
## Last SOTA Audit:footers exist on existing agents, when the registry migration runs, then every footer is converted to a structured## Changelogentry withaudit:event type AND the original footer date is preserved. - Given itd-15 (self-dogfooded SOTA audit) is shipped, when abcd self-disembarks, then the SOTA audit's findings emit
## Changelogentries (withaudit:event type) on the affected agents — the registry is the canonical surface for both manual and audit-driven updates.
SOTA
Surveyed on 2026-10-23; sources, evidence tiers and the local fit risks are in the models, prompts and measurement SOTA note; the versioning consensus it checks against practice is in the prompting baseline.
- The substance of the first two criteria has shipped in a different
shape. The
agent_contractlint (internal/core/lint/agentcontract.go) requires a semverprompt_version, refuses a prompt changed without a bump at preflight and in CI rather than in a pre-commit hook, and demands a changelog entry per bump in one log kept outsideagents/because a harness loads that tree whole (iss-110), not a## Changelogsection per agent. Criterion 5 has noLast SOTA Auditfooters left to convert. What remains is the golden diff on a bump (3), the revert verb (4) and theaudit:entry shape (6). - Versions are immutable, deployment is a pointer, rollback moves it.
Langfuse gives each version an id, uses labels as movable pointers, returns
404 rather than silently falling back when a label is missing, and defines
rollback as moving the
productionlabel (Langfuse prompt version control docs). - Regression runs on the pull request and posts a diff. promptfoo's GitHub Action evaluates edited prompts against their previous versions on every PR touching them, posts a result link and caches model calls; its page documents no gate (promptfoo GitHub Action docs).
- A prompt asset names its model and parameters (Prompty frontmatter: model id, provider, options), and an optimised prompt is a saved build artefact (DSPy GEPA saves the optimised instructions as JSON).
- "Only formatting" is a behaviour change: spreads of up to 76 accuracy points from formatting alone (Sclar et al., ICLR 2024), so a PATCH bump cannot be exempt from the golden run.
- The same prompt drifts when the model does: 84 % to 51 % on the same questions between two releases of one model (Chen, Zaharia and Zou, 2023), so a version record names the model its golden run used.
- Contrarian: for eighteen in-repo markdown prompts, git plus the lint is the registry; the value left here is the golden diff and a revert that writes its own entry.
Path 2: native, shaped to Langfuse's version-and-pointer model and
promptfoo's PR-time before/after. The version stays in frontmatter; a changelog
entry gains the model id and fixture-set hash of its golden run and an
audit: event type; abcd prompts revert rewrites from git and writes the
entry; the golden diff is a host-run review recorded as a receipt the entry
cites, surfaced before the commit completes only where the host offers a hook
that can wait — a deterministic pre-commit hook cannot run fixtures. The seam:
the entry and receipt are shaped so one promptfoo eval result or Langfuse
version record maps onto them field by field. No dependency. The independent
fit-challenge runs at plan time.
Open Questions
- Where does the prompt version live — frontmatter field, footer, or git tag?
- How aggressive is the "block edit until tests pass" check — pre-commit hook, or manual workflow?
- How does this interact with itd-15 (self-dogfooded SOTA audit)?
- (SOTA 2026-10-23) The first acceptance criterion names
agents/<name>/CHANGELOG.mdandlint_prompts.py; the shipped lint keeps the log outsideagents/(iss-110) and is Go. Does the criterion follow the shipped shape, or is a per-agent log inside the tree still wanted? - (SOTA 2026-10-23) Which golden-run facts the changelog entry records beside the version: model id, fixture-set hash, receipt path — and whether a PATCH bump that ran no golden run is refused or merely marked.
Audit Notes
- 2026-10-02 — Declared mutual pair with itd-15 (the technical facilitator's ruling CY2 of 2026-10-02, verbatim: "SAME, DECLARED PAIRS — each direction names the piece it needs"). This record's builds_on itd-15 needs the self-dogfooded SOTA audit's per-agent findings, which this record's sixth criterion writes into the affected prompts' changelogs as
audit:entries. itd-15's builds_on itd-14 needs the structured per-prompt changelog that replaces the## Last SOTA Audit:footer, which the audit's low-risk self-applied footer updates write into. Both edges stand. The record has no field record-lint's edge_cycle rule reads as a declared pair, so the linter still reports the cycle at warn until it does (iss-2610020836255174). Each piece is read from the two records' own scope, criteria and decisions; the ruling names none. Recorded by lane rd2 of autonomous run A.
Frontmatter
| id | itd-14 |
|---|---|
| type | intent |
| lifecycle | drafts |
| kind | standalone |
| severity | minor |
.abcd/development/intents/drafts/itd-14-prompt-registry-versioning.md
open on GitHub ↗ · commit history ↗
References2
Referenced by4
- iss-2610262038472526itd-14 marks a prompt's version with a `## Prompt Version: X.Y` field; 05-prompt-quality requires semver prompt_version frontmatterrelated
- iss-2610262038484184itd-14 keeps a changelog at agents/< name>/CHANGELOG.md; 01-agents keeps the changelog out of agents/, at .abcd/development/agents/related
- iss-2610262040499773itd-14 versions a prompt with a '## Prompt Version: X.Y' heading; the verification matrix says each prompt carries a semver prompt_version field.related
- itd-15abcd Audits Its Own Prompts on Every Self-Disembarkbuilt on by
mentions11
- dcn-26100200000000122026-10-02 — Ruling CY2 of the technical facilitator (2026-10-02, asked in plain words by abcd-a2 [75597f]; same local-tier file as the H3-0 entry above; recorded by lane rd2), verbatim: "itd-14<->itd-15 and itd-2609081951381895<->itd-2609170822093401: SAME, DECLARED PAIRS — each direction names the piece it needs." Applied as CY1 above: both edges of each pair stand, and each of the four intents carries a dated Audit Notes line naming its pair and the piece each direction needs; edge_cycle still warns on both pairs until the linter can read a declared pair (iss-2610020836255174). This answers the ruling iss-2609300903551032's remedy waited on.
- iss-110agents/CHANGELOG.md and agents/README.md are plain docs (the itd-5 prompt-version log; a readme) but the plugin agent-loader globs agents/*.md, so both are mis-registered as agents (abcd:CHANGELOG, abcd:README appear in the harness agent list). They have no agent frontmatter and are not invokable workers. Fix: either move these docs out of agents/ (e.g. to .abcd/development/ or a docs path) or make the loader skip non-agent files (require agent frontmatter). Surfaced by the derived-changelog plan adversarial review, which had assumed the abcd:CHANGELOG slot was free for a new composer agent.
- iss-2609300903551032Two more builds_on cycles sit in the intent store beside the runner and loop one: the drafts itd-14 and itd-15 each declare builds_on the other, and the planned itd-2609081951381895 (the OpenAI-compatible API oracle adapter) and itd-2609170822093401 (the oracle choice is one repo-wide value) each declare builds_on the other; record-lint's edge_cycle rule reports both
- iss-2610020836255174The intent record has no way to declare a mutual builds_on pair that record-lint's edge_cycle rule honours. The technical facilitator ruled on 2026-10-02 that three two-intent cycles stay as declared mutual pairs, each direction naming the piece it needs (CY1: itd-2609201916056194 and itd-2609201916151817; CY2: itd-14 and itd-15, itd-2609081951381895 and itd-2609170822093401), but the frontmatter offers only builds_on and blocked_by, and edge_cycle reports every strongly connected set whatever the records say, so the three pairs are declared only in each intent's Audit Notes and still read as cycles to the linter, and to anything that orders work by these edges. Until a declared pair is something the linter can read, edge_cycle cannot be promoted from warn to blocker without refusing the ruled pairs.
- itd-1Every Intent Has Acceptance Criteria; Every Audit Verifies Them
- itd-34Intents Promote In Three Flavours — Standalone, Bundle-Member, Discipline
- itd-64abcd Learns From Its Own Runs Which Configurations Work Best, And Tunes Its Configuration Layer Accordingly
- itd-81No Judge Ships Unmeasured, And A Clean Diff Is A Result
- itd-2609150819445595A shipped intent's owed fidelity review is listed, so the debt is paid
- itd-2609211116005482`abcd build next` picks the readiest planned intent itself, and writes down why
- itd-2610161733316518One task done across a model matrix, re-runnably