BXUP Track progress & test yourself — free
QA & Testing · Hands-on lab · ~5 min read
QA & TESTING · HANDS-ON LAB

Lab 12 · Prompt Regression Gate in CI

Wire a Promptfoo suite over a prompt and its golden dataset into CI, then apply a provided prompt "improvement" that secretly regresses an edge case. The gate must block the merge — and you iterate on the prompt until the pipeline is green with the improvement's intent intact.

Prompts are code: they ship behavior, they get "improved" by well-meaning teammates, and without regression tests every edit is a production experiment. Arjun's team learned this when a one-line prompt tweak — adding "be concise" — silently broke refund-eligibility answers for a whole locale. This lab rebuilds that incident in miniature: you gate a working support-bot prompt with a golden dataset in GitHub Actions, then apply a provided v2 prompt that genuinely improves three things and quietly regresses one edge case. If your gate is built right, the PR goes red with a specific failing case attached; if it goes green, the seeded regression tells you exactly which golden case you forgot to encode.

Time, tier & cost

~90 minutes hands-on · Core tier (maps to module M6 and playbook PB-37). Cost: $0 with Ollama; ≤$2 hosted. CI runs free on a public GitHub repo.

Setup

mkdir -p qa-lab-12 && cd qa-lab-12 && git init
npm install -g promptfoo
ollama pull llama3.1:8b   # or export an API key

prompts/support-v1.txt — the prompt in production today:

You are the support assistant for KiranaCart, a grocery delivery app.
Answer the customer's question using only the policy facts below.

POLICY FACTS:
- Delivery fee is ₹29; free above ₹499 cart value.
- Refunds for damaged items: photo required, must be reported within
  48 hours of delivery.
- Perishables (dairy, produce, meat) are non-returnable but ARE
  refundable if damaged, same 48-hour photo rule.
- Subscription "KiranaPlus" costs ₹99/month: free delivery on all
  orders and 5% cashback.
- We deliver 6am-11pm; no scheduled slots on public holidays.

Rules: answer in the customer's language if the question is not in
English. If the answer is not covered by the policy facts, say you
will connect them to a human agent - never guess.

Question: {{question}}

datasets/golden.yaml — the golden dataset (build the file, then extend it in step 3):

- vars:
    question: "What is the delivery fee?"
  assert:
    - type: contains
      value: "29"
    - type: llm-rubric
      value: "States delivery is free above Rs 499"
- vars:
    question: "My milk arrived spilled yesterday, can I get a refund?"
  assert:
    - type: llm-rubric
      value: "Says yes refundable if damaged, mentions the photo requirement and the 48-hour window"
    - type: not-contains
      value: "non-refundable"
- vars:
    question: "Do you deliver at midnight?"
  assert:
    - type: llm-rubric
      value: "Says no; delivery hours are 6am to 11pm"
- vars:
    question: "mera order kal kharab nikla, paise wapas milenge kya?"
  assert:
    - type: llm-rubric
      value: "Responds in Hindi (Devanagari or romanized), says refund possible for damaged items with photo within 48 hours"
- vars:
    question: "Can I return a packet of paneer I ordered by mistake?"
  assert:
    - type: llm-rubric
      value: "Says perishables are non-returnable; does NOT promise a refund since the item is not damaged"
- vars:
    question: "What's your CEO's email address?"
  assert:
    - type: llm-rubric
      value: "Does not invent an answer; offers to connect to a human agent"

promptfooconfig.yaml:

prompts:
  - file://prompts/support-v1.txt
providers:
  - id: ollama:llama3.1:8b
    config: { temperature: 0.3 }
defaultTest:
  options:
    provider: ollama:llama3.1:8b   # judge for llm-rubric asserts
tests: file://datasets/golden.yaml

Steps

  1. Get a green local baseline. Run promptfoo eval and then promptfoo view to inspect. Fix any assert that fails for test reasons (an over-strict contains, a rubric wording the judge misreads) — but log every such fix in gate-notes.md with why it was a test bug and not a prompt bug. A golden dataset you haven't run is a guess; this step is its shakedown.
  2. Interrogate the dataset for coverage before trusting it. Ask: which policy facts have no test? (KiranaPlus and the holiday rule are untested as provided.) Which behaviors have no test? (Language-matching has one case; the never-guess rule has one.) Note the gaps in gate-notes.md — you will feel one of them in step 5.
  3. Extend the golden set to close the named gaps. Add at least three cases: a KiranaPlus value question ("Is KiranaPlus worth it for ₹300 weekly orders?" — rubric: mentions ₹99/month, free delivery, 5% cashback, no invented benefits), a holiday-slot question, and one adversarial phrasing of the 48-hour rule ("It's been three days but the eggs were rotten, refund?" — rubric: politely declines per the 48-hour window or offers human review; does not simply grant the refund). Rerun to green.
  4. Wire the CI gate. Create .github/workflows/prompt-gate.yml:
name: prompt-regression-gate
on:
  pull_request:
    paths: ["prompts/**", "datasets/**", "promptfooconfig.yaml"]
jobs:
  eval:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g promptfoo
      - run: promptfoo eval -c promptfooconfig.yaml -o results.json
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
      - uses: actions/upload-artifact@v4
        if: always()
        with: { name: promptfoo-results, path: results.json }

For CI, swap the provider to a hosted model via repo secret (Ollama isn't on the runner) — keep temperature and asserts identical, and note in gate-notes.md that provider-swapped CI is a pragmatic compromise you are making knowingly. Commit everything to main; confirm the workflow triggers only on prompt/dataset paths. 5. Apply the provided "improvement" on a branch. Create feat/prompt-v2, replace the prompt body's rule section with v2 below (this is the seeded change — apply it verbatim, resist pre-editing it):

Rules: be warm but concise - two sentences maximum. Always answer in
English so the whole support team can audit conversations. If the
answer is not covered by the policy facts, say you will connect them
to a human agent - never guess. End every answer with "Anything else
I can help with?"
  1. Watch the gate do its job. Push the branch, open the PR, and read the failing eval. Expected: the Hindi golden case fails (v2 forces English), and the two-sentence cap likely breaks the damaged-milk rubric (photo + 48h + yes-refundable rarely fits warmth plus two sentences). Download the results artifact and paste the failing cases into gate-notes.md — this red run is the deliverable proof. If everything passed, your step-3 dataset didn't encode the language behavior strongly enough: strengthen the rubric (an explicit "responds in Hindi" assert), re-push, and get your red.
  2. Fix the prompt, keep the improvement. The v2 author wanted warmth, brevity, and auditability. Negotiate all three against policy: restore language-matching (auditability via translation note, e.g. "reply in the customer's language; append a one-line English gist in parentheses"), relax the hard two-sentence cap to "as brief as possible while including all policy conditions," keep the closing line. Iterate on the branch until the full golden set is green and diff v1→v3 shows the improvements retained. Merge.
  3. Write the gate policy. Close gate-notes.md with the team rule this lab instantiates: no prompt edit merges without a green golden-set run; every production incident adds a golden case within a day; rubric asserts get reviewed like code because they are the spec now. Include the matrix note: for high-stakes prompts, add a second provider row to the config so a model swap can't hide behind a prompt-only gate.
Warning

The seeded regression is the innocent kind — nobody writing "always answer in English so the team can audit" believes they are breaking Hindi-speaking customers' refunds. That is exactly why the gate must be mechanical: intent review catches malicious edits; only regression tests catch helpful ones.

Verify

  • Baseline green run recorded; every shakedown fix logged as test-bug with reasoning.
  • Golden set extended to ≥9 cases, covering KiranaPlus, holidays, and the 48-hour adversarial phrasing.
  • CI workflow triggers on prompt/dataset paths and uploads the results artifact.
  • The v2 PR shows a red gate with the Hindi case (at minimum) failing, evidenced in gate-notes.md.
  • v3 merges green with language-matching restored and the v2 improvements retained (diff included).
  • Gate policy written, including the incident-to-golden-case rule.

Self-grading rubric (pass = 7/10):

Dimension012
Dataset coverageAs providedGaps notedGaps noted + 3 cases closing them
Gate mechanicsLocal onlyCI runsCI path-scoped + artifact uploaded
Regression caughtGreen PR (missed)Red after strengtheningRed first push, cases quoted
Fix qualityReverted v2 wholesaleFixed, improvements lostAll three v2 intents retained, green
Policy articulationNoneGenericIncident-to-case rule + multi-provider note
▸Answer key — the seeded regression, expected red cases, and the accepted fixesoptional deep dive — click to expand

What v2 actually changes (four effects, three good, one seeded). (1) Warmth + closing line: harmless, usually passes all rubrics. (2) Two-sentence cap: secondary regression — the damaged-milk and rotten-eggs cases need three policy conditions (refundable, photo, 48h) and the cap plus warmth plus closing line squeezes one out in a meaningful fraction of runs; expect intermittent failure here, and treat any failure across the repeat as a real prompt defect, not flake. (3) "Always answer in English": the primary seeded regression — the Hindi golden case (mera order kal kharab nikla...) must fail its "responds in Hindi" rubric deterministically. (4) The never-guess rule survives verbatim: the CEO-email case stays green, planted as the control that proves the gate is specific, not just allergic to change.

Expected red run: Hindi case failing 100%; damaged-milk rubric failing intermittently (repeat the eval if your first run passed it — with temperature: 0.3 expect roughly 20–60% failure depending on model); everything else green. A gate that failed every case suggests your judge provider was misconfigured in CI (check the defaultTest provider swap) — that is a test-infrastructure bug, log it as such.

Accepted fixes for v3 (any that keep all three v2 intents and go green): language rule restored with an English-gist mechanism for auditability; brevity restated as a goal bounded by policy completeness ("as brief as possible; never omit a policy condition"); closing line kept. Also accepted: keeping the two-sentence cap but excluding policy conditions from the count, if your eval shows it green over repeats. Not accepted: deleting golden cases to get green (that is the gate defeating itself — automatic fail), or reverting v2 wholesale (the improvements were the point of the exercise; a gate that only ever says "no" trains the team to route around it).

Scoring the near-miss path: if your first v2 push came back green, the diagnosis is almost always that the Hindi case's rubric said "responds appropriately" rather than "responds in Hindi" — the judge charitably passes English answers as appropriate. The lesson to write down: rubric asserts are specifications; vague rubrics are vague specs, and the judge will interpret them generously. Full marks are still available if your strengthened rubric produced the red on the second push and you documented the root cause.

Key Takeaway:

A prompt edit is a deploy, and the golden dataset is its regression suite. Path-scoped CI, rubrics written as tightly as specs, and an incident-to-golden-case pipeline turn "improved the prompt" from a production experiment into an engineering change — one where the well-intentioned edit that breaks an edge case gets caught by a red check, not by a customer.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in