{"id":"522ffe6c-4865-474d-ab64-0a0cdee9ba97","shortId":"Xmvc8z","kind":"skill","title":"Prompt Version Control Workflow","tagline":"Sets up a prompt versioning system with naming conventions, diff tracking, A/B evaluation gates before promotion, and rollback triggers.","description":"# Prompt Version Control Workflow\n\n## What this skill does\n\nThis skill designs a prompt versioning workflow so you can track changes, test before promoting, and roll back safely. Most teams store prompts as hardcoded strings inside application code — this means prompt changes go unreviewed, regressions are invisible, and rollbacks require a full code deploy. This skill fixes that.\n\n## How to use\n\n### Claude Code / Cline\n\nCopy this file to `.agents/skills/prompt-version-control/SKILL.md` in your project root.\n\nThen ask:\n- *\"Use the Prompt Version Control skill to set up versioning for our prompts.\"*\n- *\"How should we manage prompt versions across staging and production?\"*\n\nProvide:\n- Number of prompts in your system\n- Current storage method (hardcoded, env vars, database, files)\n- Whether you need A/B testing or just safe deploys\n- Tech stack\n\n### Cursor / Codex\n\nDescribe your current prompt management alongside these instructions.\n\n## The Prompt / Instructions for the Agent\n\n### Step 1 — Choose a storage approach\n\n| Approach | Best for | Tradeoffs |\n|---|---|---|\n| **Git files** (prompts/*.md) | Small teams, prompts change rarely | Simple, diffable, but requires deploy to change |\n| **Database table** | Prompts change frequently, need hot-swap | Live updates, but needs admin UI |\n| **Dedicated tools** (Braintrust, LangSmith, Langfuse) | Eval-heavy teams | Best tooling, external dependency |\n| **Environment variables** | Single prompt, simple apps | Easy but no history, no diffs |\n\n**Recommended default: Git files** — prompts are code, they should be reviewed like code.\n\n### Step 2 — File-based prompt versioning\n\n```\nprompts/\n  support-bot/\n    v1.md          ← old version (keep for rollback reference)\n    v2.md          ← current production version\n    v3.md          ← candidate being tested\n    current.txt    ← contains \"v2\" — points to active version\n  document-summarizer/\n    v1.md\n    current.txt\n```\n\nEach prompt file has a frontmatter header:\n```markdown\n---\nversion: 2\ncreated: 2026-01-15\nauthor: tates\ndescription: Improved tone, added JSON output format\nchanges_from_previous:\n  - Added JSON output requirement\n  - Shortened system role description\n  - Added explicit refusal instructions\n---\n\nYou are a customer support agent for Acme Corp...\n```\n\nLoad the active prompt at runtime:\n```python\nimport os, pathlib\n\ndef load_prompt(name: str) -> str:\n    prompts_dir = pathlib.Path(\"prompts\") / name\n    current = (prompts_dir / \"current.txt\").read_text().strip()\n    return (prompts_dir / f\"{current}.md\").read_text().split(\"---\\n\", 2)[-1].strip()\n```\n\n### Step 3 — Evaluation gate before promotion\n\nNever promote a new prompt version without running evals first. Add this as a CI check:\n\n```python\n# scripts/eval-prompt-candidate.py\nimport sys\nfrom eval_harness import run_eval_suite, load_dataset\nfrom prompt_loader import load_prompt_version\n\nPASS_THRESHOLD = 0.85  # 85% pass rate required\n\ndef main(prompt_name: str, candidate_version: str):\n    dataset = load_dataset(f\"evals/{prompt_name}/test_cases.json\")\n\n    current = load_prompt_version(prompt_name, \"current\")\n    candidate = load_prompt_version(prompt_name, candidate_version)\n\n    current_results = run_eval_suite(current, dataset)\n    candidate_results = run_eval_suite(candidate, dataset)\n\n    print(f\"Current version pass rate:   {current_results['pass_rate']:.1%}\")\n    print(f\"Candidate version pass rate: {candidate_results['pass_rate']:.1%}\")\n\n    # Block promotion if candidate is worse\n    if candidate_results[\"pass_rate\"] < PASS_THRESHOLD:\n        print(f\"FAIL: Candidate below {PASS_THRESHOLD:.0%} threshold\")\n        sys.exit(1)\n\n    if candidate_results[\"pass_rate\"] < current_results[\"pass_rate\"] - 0.05:\n        print(\"FAIL: Candidate is more than 5% worse than current\")\n        sys.exit(1)\n\n    print(\"PASS: Candidate meets quality threshold\")\n\nif __name__ == \"__main__\":\n    main(sys.argv[1], sys.argv[2])\n```\n\n### Step 4 — GitHub Actions workflow\n\n```yaml\n# .github/workflows/prompt-eval.yml\nname: Prompt Evaluation\n\non:\n  pull_request:\n    paths:\n      - 'prompts/**'\n\njobs:\n  eval:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Detect changed prompts\n        id: changes\n        run: |\n          git diff --name-only origin/main HEAD -- prompts/ | \\\n          grep -oP 'prompts/\\K[^/]+' | sort -u > changed_prompts.txt\n          cat changed_prompts.txt\n\n      - name: Run evals for changed prompts\n        run: |\n          while read prompt_name; do\n            echo \"Evaluating: $prompt_name\"\n            # Find the candidate version (newest non-current .md)\n            CANDIDATE=$(ls prompts/$prompt_name/*.md | sort -V | tail -1 | xargs basename .md)\n            python scripts/eval-prompt-candidate.py \"$prompt_name\" \"$CANDIDATE\"\n          done < changed_prompts.txt\n        env:\n          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}\n```\n\n### Step 5 — Promotion and rollback\n\n**Promote a candidate to production:**\n```bash\n# scripts/promote-prompt.sh\nPROMPT_NAME=$1\nNEW_VERSION=$2\n\necho \"$NEW_VERSION\" > prompts/$PROMPT_NAME/current.txt\ngit add prompts/$PROMPT_NAME/current.txt\ngit commit -m \"chore: promote $PROMPT_NAME to $NEW_VERSION\"\ngit push\n```\n\n**Rollback (instant, no code deploy needed):**\n```bash\n# Roll back to previous version\necho \"v1\" > prompts/support-bot/current.txt\ngit commit -am \"revert: roll back support-bot to v1 — tone regression\"\ngit push\n```\n\nIf prompts are loaded dynamically from the filesystem (not baked into a build), a rollback takes effect on the next request — no redeploy needed.\n\n### Step 6 — Database-backed hot-swap (advanced)\n\nFor teams that need to change prompts without any deploy:\n\n```sql\nCREATE TABLE prompt_versions (\n  id SERIAL PRIMARY KEY,\n  name TEXT NOT NULL,\n  version INTEGER NOT NULL,\n  content TEXT NOT NULL,\n  is_active BOOLEAN DEFAULT false,\n  eval_pass_rate NUMERIC,\n  created_at TIMESTAMPTZ DEFAULT NOW(),\n  promoted_by TEXT,\n  UNIQUE(name, version)\n);\n\n-- Get active prompt\nSELECT content FROM prompt_versions\nWHERE name = 'support-bot' AND is_active = true;\n\n-- Promote new version (atomic swap)\nBEGIN;\nUPDATE prompt_versions SET is_active = false WHERE name = 'support-bot';\nUPDATE prompt_versions SET is_active = true WHERE name = 'support-bot' AND version = 3;\nCOMMIT;\n```\n\nCache active prompts in memory with a 5-minute TTL — re-fetch on cache miss. This gives you live prompt updates with one database query overhead.\n\n### Naming conventions\n\n| Convention | Example |\n|---|---|\n| Semantic versioning for major changes | `v1`, `v2`, `v3` |\n| Date-based for frequent iteration | `2026-01-15`, `2026-02-03` |\n| Feature-branch style | `v2-json-output`, `v3-shorter-tone` |\n\nAlways include in commit messages: what changed, why it changed, and eval result. Example:\n```\nchore: promote support-bot to v3\n\nChanges: added JSON output format, removed verbose greeting\nReason: downstream parser requires structured output\nEval: 91% pass rate (up from 87% on v2)\n```","tags":["prompt","version","control","openagentskills","notysoty","agent-skills","claude","claude-code","claude-skills","cline","cursor","llm"],"capabilities":["skill","source-notysoty","skill-prompt-version-control","topic-agent-skills","topic-claude","topic-claude-code","topic-claude-skills","topic-cline","topic-cursor","topic-llm","topic-llm-skills","topic-skills"],"categories":["openagentskills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/Notysoty/openagentskills/prompt-version-control","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add Notysoty/openagentskills","source_repo":"https://github.com/Notysoty/openagentskills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (7,057 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:13:23.690Z","embedding":null,"createdAt":"2026-05-18T13:20:45.263Z","updatedAt":"2026-05-18T19:13:23.690Z","lastSeenAt":"2026-05-18T19:13:23.690Z","tsv":"'-01':292,905 '-02':908 '-03':909 '-1':366,629 '-15':293,906 '/test_cases.json':432 '0':504 '0.05':517 '0.85':412 '1':164,472,483,507,529,541,667 '2':243,289,365,543,670 '2026':291,904,907 '3':369,857 '4':545 '5':524,654,866 '6':749 '85':413 '87':963 '91':958 'a/b':16,139 'acm':325 'across':117 'action':547 'actions/checkout':569 'activ':273,329,789,809,823,836,848,860 'ad':299,306,314,944 'add':384,678 'admin':202 'advanc':756 'agent':162,323 'agents/skills/prompt-version-control/skill.md':91 'alongsid':154 'alway':922 'anthrop':647 'api':642,645,648,651 'app':222 'applic':59 'approach':168,169 'ask':97 'atom':828 'author':294 'back':49,702,714,752 'bake':733 'base':246,900 'basenam':631 'bash':663,700 'begin':830 'best':170,213 'block':484 'boolean':790 'bot':252,717,820,842,854,940 'braintrust':206 'branch':912 'build':736 'cach':859,873 'candid':265,422,440,446,455,460,475,479,487,491,500,509,520,532,613,620,637,660 'cat':593 'chang':43,64,180,188,192,303,573,576,599,762,894,928,931,943 'changed_prompts.txt':592,594,639 'check':389 'choos':165 'chore':685,936 'ci':388 'claud':84 'cline':86 'code':60,75,85,235,241,697 'codex':148 'commit':683,710,858,925 'contain':269 'content':784,812 'control':3,26,102 'convent':13,887,888 'copi':87 'corp':326 'creat':290,768,797 'current':128,151,261,348,359,433,439,448,453,464,468,513,527,618 'current.txt':268,279,351 'cursor':147 'custom':321 'databas':134,189,751,883 'database-back':750 'dataset':402,425,427,454,461 'date':899 'date-bas':898 'dedic':204 'def':337,417 'default':230,791,800 'depend':216 'deploy':76,144,186,698,766 'describ':149 'descript':296,313 'design':34 'detect':572 'diff':14,228,579 'diffabl':183 'dir':344,350,357 'document':276 'document-summar':275 'done':638 'downstream':952 'dynam':728 'easi':223 'echo':607,671,706 'effect':740 'env':132,640 'environ':217 'eval':210,382,395,399,429,451,458,560,597,793,933,957 'eval-heavi':209 'evalu':17,370,553,608 'exampl':889,935 'explicit':315 'extern':215 'f':358,428,463,474,498 'fail':499,519 'fals':792,837 'featur':911 'feature-branch':910 'fetch':871 'file':89,135,174,232,245,282 'file-bas':244 'filesystem':731 'find':611 'first':383 'fix':79 'format':302,947 'frequent':193,902 'frontmatt':285 'full':74 'gate':18,371 'get':808 'git':173,231,578,677,682,692,709,722 'github':546 'github/workflows/prompt-eval.yml':550 'give':876 'go':65 'greet':950 'grep':586 'har':396 'hardcod':56,131 'head':584 'header':286 'heavi':211 'histori':226 'hot':196,754 'hot-swap':195,753 'id':575,772 'import':334,392,397,406 'improv':297 'includ':923 'insid':58 'instant':695 'instruct':156,159,317 'integ':781 'invis':69 'iter':903 'job':559 'json':300,307,916,945 'k':589 'keep':256 'key':643,646,649,652,775 'langfus':208 'langsmith':207 'latest':566 'like':240 'live':198,878 'load':327,338,401,407,426,434,441,727 'loader':405 'ls':621 'm':684 'main':418,538,539 'major':893 'manag':114,153 'markdown':287 'md':176,360,619,625,632 'mean':62 'meet':533 'memori':863 'messag':926 'method':130 'minut':867 'miss':874 'n':364 'name':12,340,347,420,431,438,445,537,551,571,581,595,605,610,624,636,666,688,776,806,817,839,851,886 'name-on':580 'name/current.txt':676,681 'need':138,194,201,699,747,760 'never':374 'new':377,668,672,690,826 'newest':615 'next':743 'non':617 'non-curr':616 'null':779,783,787 'number':122 'numer':796 'old':254 'one':882 'op':587 'openai':641 'origin/main':583 'os':335 'output':301,308,917,946,956 'overhead':885 'parser':953 'pass':410,414,466,470,477,481,493,495,502,511,515,531,794,959 'path':557 'pathlib':336 'pathlib.path':345 'point':271 'previous':305,704 'primari':774 'print':462,473,497,518,530 'product':120,262,662 'project':94 'promot':20,46,373,375,485,655,658,686,802,825,937 'prompt':1,8,24,36,54,63,100,110,115,124,152,158,175,179,191,220,233,247,249,281,330,339,343,346,349,356,378,404,408,419,430,435,437,442,444,552,558,574,585,588,600,604,609,622,623,635,665,674,675,679,680,687,725,763,770,810,814,832,844,861,879 'prompts/support-bot/current.txt':708 'provid':121 'pull':555 'push':693,723 'python':333,390,633 'qualiti':534 'queri':884 'rare':181 'rate':415,467,471,478,482,494,512,516,795,960 're':870 're-fetch':869 'read':352,361,603 'reason':951 'recommend':229 'redeploy':746 'refer':259 'refus':316 'regress':67,721 'remov':948 'request':556,744 'requir':72,185,309,416,954 'result':449,456,469,480,492,510,514,934 'return':355 'revert':712 'review':239 'role':312 'roll':48,701,713 'rollback':22,71,258,657,694,738 'root':95 'run':381,398,450,457,562,577,596,601 'runs-on':561 'runtim':332 'safe':50,143 'scripts/eval-prompt-candidate.py':391,634 'scripts/promote-prompt.sh':664 'secrets.anthropic':650 'secrets.openai':644 'select':811 'semant':890 'serial':773 'set':5,105,834,846 'shorten':310 'shorter':920 'simpl':182,221 'singl':219 'skill':30,33,78,103 'skill-prompt-version-control' 'small':177 'sort':590,626 'source-notysoty' 'split':363 'sql':767 'stack':146 'stage':118 'step':163,242,368,544,567,653,748 'storag':129,167 'store':53 'str':341,342,421,424 'string':57 'strip':354,367 'structur':955 'style':913 'suit':400,452,459 'summar':277 'support':251,322,716,819,841,853,939 'support-bot':250,715,818,840,852,938 'swap':197,755,829 'sys':393 'sys.argv':540,542 'sys.exit':506,528 'system':10,127,311 'tabl':190,769 'tail':628 'take':739 'tate':295 'team':52,178,212,758 'tech':145 'test':44,140,267 'text':353,362,777,785,804 'threshold':411,496,503,505,535 'timestamptz':799 'tone':298,720,921 'tool':205,214 'topic-agent-skills' 'topic-claude' 'topic-claude-code' 'topic-claude-skills' 'topic-cline' 'topic-cursor' 'topic-llm' 'topic-llm-skills' 'topic-skills' 'track':15,42 'tradeoff':172 'trigger':23 'true':824,849 'ttl':868 'u':591 'ubuntu':565 'ubuntu-latest':564 'ui':203 'uniqu':805 'unreview':66 'updat':199,831,843,880 'use':83,98,568 'v':627 'v1':707,719,895 'v1.md':253,278 'v2':270,896,915,965 'v2-json-output':914 'v2.md':260 'v3':897,919,942 'v3-shorter-tone':918 'v3.md':264 'v4':570 'var':133 'variabl':218 'verbos':949 'version':2,9,25,37,101,107,116,248,255,263,274,288,379,409,423,436,443,447,465,476,614,669,673,691,705,771,780,807,815,827,833,845,856,891 'whether':136 'without':380,764 'workflow':4,27,38,548 'wors':489,525 'xarg':630 'yaml':549","prices":[{"id":"9d6be752-8eef-4bc9-ac82-d3b0538a0ea4","listingId":"522ffe6c-4865-474d-ab64-0a0cdee9ba97","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"Notysoty","category":"openagentskills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:20:45.263Z"}],"sources":[{"listingId":"522ffe6c-4865-474d-ab64-0a0cdee9ba97","source":"github","sourceId":"Notysoty/openagentskills/prompt-version-control","sourceUrl":"https://github.com/Notysoty/openagentskills/tree/main/skills/prompt-version-control","isPrimary":false,"firstSeenAt":"2026-05-18T13:20:45.263Z","lastSeenAt":"2026-05-18T19:13:23.690Z"}],"details":{"listingId":"522ffe6c-4865-474d-ab64-0a0cdee9ba97","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"Notysoty","slug":"prompt-version-control","github":{"repo":"Notysoty/openagentskills","stars":8,"topics":["agent-skills","claude","claude-code","claude-skills","cline","cursor","llm","llm-skills","skills"],"license":"mit","html_url":"https://github.com/Notysoty/openagentskills","pushed_at":"2026-03-28T06:50:19Z","description":"A  community-driven library of reusable AI agent skills for Claude Code, Cursor, Codex, Cline, and more.","skill_md_sha":"7db2aa23469bfdcefae30900c32b1aeb5b954909","skill_md_path":"skills/prompt-version-control/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/Notysoty/openagentskills/tree/main/skills/prompt-version-control"},"layout":"multi","source":"github","category":"openagentskills","frontmatter":{"name":"Prompt Version Control Workflow","description":"Sets up a prompt versioning system with naming conventions, diff tracking, A/B evaluation gates before promotion, and rollback triggers."},"skills_sh_url":"https://skills.sh/Notysoty/openagentskills/prompt-version-control"},"updatedAt":"2026-05-18T19:13:23.690Z"}}