Repository navigation
[Phase 1] Spec-drift frozen offline eval + tune to zero false positives #1145
Copy link
Copy link
Closed
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Description
Story
As a maintainer who must trust the drift detector before wiring it,
I want a frozen offline eval with dev and held-out splits that tunes the detector on dev only and proves zero false positives on an immutable holdout set,
so that the detector is validated against reward-hacking and stays honest — its baseline can't be quietly moved to make a bad detector look good.
Acceptance Criteria
- A frozen offline eval lives under
evals/spec-drift/with adevand a held-outholdoutsplit (mirroringevals/deep-review/), each case a labeled (diff, acceptance-criteria, expected-verdict) triple conforming to a documented case shape. - The eval harness and its scoring are runnable offline (no live PR required) and documented in
evals/spec-drift/README.md; it drives the Story-2 detector's pure classifier directly. - Overfitting / reward-hacking guard: the detector is tuned ONLY against
evals/spec-drift/dev; theholdoutsplit is never read during tuning. - Artifact-immutability guard: the
holdoutcases and their expected-verdict baseline are committed as frozen fixtures — changing any holdout expectation requires an explicit, reviewed fixture change, not a tuning-loop side effect. - The tuned detector scores 0 false positives on the
holdoutsplit, and this is demonstrated by the offline harness.
Tasks / Subtasks
- Define the eval case shape and build
evals/spec-drift/with dev + holdout splits + README, followingevals/case.schema.jsonconventions. (AC: test issue from agent #1, Go-live improvements for PR review agent #2) - Wire the offline harness to invoke the Story-2 pure classifier and score verdicts. (AC: Go-live improvements for PR review agent #2)
- Tune the detector against the dev split ONLY. (AC: Add @claude delegation, auto-merge, and rebase handling #3)
- Commit the holdout cases + expected-verdict baseline as immutable fixtures and demonstrate 0 false positives on holdout. (AC: Optimize review: small-PR and incremental fast paths #4, feat: add Copilot engine support via REVIEW_ENGINE toggle #5)
Dev Notes
- This story depends on the Story-2 detector and validates it; it changes no production behavior — it adds an eval directory and its harness.
- Follow the split pattern already established by
evals/deep-review/holdout(adevset you tune on and aholdoutset you never train against) and the case-schema conventions inevals/case.schema.json. The frozen holdout baseline is the artifact-immutability guard. - Reward-hacking guard is the whole point: the tuning loop must physically only read
dev. Document in the README that promoting a detector change requires holdout still at 0 false positives, and that editing a holdout expectation is a reviewed fixture change, never automatic. - Keep the eval offline and deterministic where possible (fixture diffs + fixture ACs), so it runs without a live PR and can gate future detector changes.
- Testing standard: the harness itself should be lint-clean; if it includes shell helpers,
shellcheckthem and add bats coverage consistent with the repo's eval tooling (evals/validate-cases.py).
Project Structure Notes
Adds evals/spec-drift/ (dev + holdout + README + harness). Mirrors evals/deep-review/; no existing files change.
References
- evals/deep-review/holdout
- evals/case.schema.json
- evals/validate-cases.py
- evals/README.md
- AGENTS.md
Likely target surface
evals/
Story prepared by the BMAD Scrum Master (Bob) for epic #1142. Status: ready-for-dev.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
dev-leadFor dev-lead agent pickupFor dev-lead agent pickupinitiativeEpic / initiative tracking issueEpic / initiative tracking issue
Activity
don-petry commentedon Jul 14, 2026
Dev-Lead Implementation Plan
Issue: #1145 — [Phase 1] Spec-drift frozen offline eval + tune to zero false positives
Scope
Add a frozen, offline, deterministic eval under
evals/spec-drift/(dev + holdout splits, README, case schema, harness) that drives the Story-2 (scripts/spec-drift.sh) pure classifierclassify_driftdirectly, is tuned only ondev, and proves 0 false positives on an immutableholdoutsplit. No production behavior changes.Design notes
evals/deep-review/(dev/holdout) and followsevals/case.schema.json+evals/validate-cases.pysplit-hygiene conventions.classify_drift(analysis)function. Each case is a labeled (diff, acceptance-criteria, expected-verdict) triple and carries a frozen cheap-tier detectoranalysisfixture — the same "embed the upstream tier's output as a fixture" patternevals/deep-reviewuses (its embeddedTriage result:), so scoring needs no live PR or LLM.DRIFTwhen the expected verdict is notDRIFT(flags a spec-compliant PR). Holdout includes FP-trap cases (prose mentioning "drift" but verdict ALIGNED, empty/timeout → INDETERMINATE) plus genuine DRIFT true-positives.run-eval.sh devnever opensholdout/). Holdout cases + expected verdicts are committed as frozen fixtures, already CI-immutable viaholdout-guard.yml/ CODEOWNERS overevals/**/holdout/.Implementation Checklist
evals/spec-drift/case.schema.json— documented case shape (JSON-Schema 2020-12).evals/spec-drift/dev/cases.jsonl— proposer-visible tuning cases.evals/spec-drift/holdout/cases.jsonl— frozen held-out cases (0-FP baseline).evals/spec-drift/run-eval.sh— offline harness: sourcesscripts/spec-drift.sh, runsclassify_driftper case, scores, fails on any false positive.evals/spec-drift/README.md— case shape, how to run offline, reward-hacking + artifact-immutability guards.tests/test_spec_drift_eval.batsto thebatsjob list in.github/workflows/lint.yml.Tests to Write
tests/test_spec_drift_eval.bats— pure scoring helpers (false-positive detection), end-to-end harness run over a temp fixture dir, and split-isolation (holdout not read when scoring dev).evals/validate-cases.py <cases.jsonl> evals/spec-drift/case.schema.json.evals/validate-cases.py(dev/holdout disjoint ids).Test Command
bats tests/test_spec_drift_eval.bats tests/test_spec_drift.bats tests/test_validate_cases.batsandbash evals/spec-drift/run-eval.sh holdoutdon-petry commentedon Jul 14, 2026
Dev-Lead: Implementation Complete
Plan Execution
evals/spec-drift/case.schema.json— documented case shape (JSON-Schema 2020-12), followingevals/case.schema.jsonconventions.evals/spec-drift/dev/cases.jsonl(7 proposer-visible tuning cases) andevals/spec-drift/holdout/cases.jsonl(8 frozen held-out cases).evals/spec-drift/run-eval.sh— offline harness that sourcesscripts/spec-drift.shand drives its pureclassify_driftdirectly, scoring each case's frozenanalysisfixture against its expected verdict.evals/spec-drift/README.md— case shape, offline run instructions, reward-hacking guard (tune ondevonly), and artifact-immutability guard (frozen holdout).tests/test_spec_drift_eval.batsand wired it into thebatsjob in.github/workflows/lint.yml.scripts/spec-drift.sh— no production behavior changed.Acceptance Criteria
evals/spec-drift/hasdev+holdoutsplits mirroringevals/deep-review/; each case is a labeled (diff, acceptance-criteria, expected-verdict) triple conforming tocase.schema.json. ✔classify_driftdirectly; documented inREADME.md. ✔run-eval.sh devnever opensholdout/); proven by a bats test using a dev-only root. ✔/evals/**/holdout/andholdout-guard.yml(theevals/prefix already covers this path). ✔Test Results
Files Changed
evals/spec-drift/case.schema.json(new): documented case shape.evals/spec-drift/dev/cases.jsonl(new): 7 tuning cases (token variants, timeout, both-tokens, an FP-trap).evals/spec-drift/holdout/cases.jsonl(new): 8 frozen cases (FP-traps that mention "drift"/"no drift"/timeout but are ALIGNED/INDETERMINATE, plus DRIFT true-positives).evals/spec-drift/run-eval.sh(new): offline harness; gate fails if any scored split has a false positive.evals/spec-drift/README.md(new): usage + reward-hacking/immutability guards.tests/test_spec_drift_eval.bats(new): pure-helper, per-split, end-to-end, split-isolation, and schema-conformance coverage..github/workflows/lint.yml: addedtests/test_spec_drift_eval.batsto thebatsjob (per AGENTS.md's repo-specific bats-list extension).Notes
analysisfixture per case makes offline/deterministic scoring possible:classify_driftmaps the cheap-tier analysis text to a verdict, so each case records that text — the same "embed the upstream tier's output as a fixture" patternevals/deep-review/uses (itsTriage result:). The diff + acceptance_criteria ground the scenario; scoring readsanalysis.DRIFTwhen the expected verdict is notDRIFT. Missed drift is a false negative and does not fail the gate — matching this story's explicit "zero false positives" scope.scripts/**, so it does not coverevals/spec-drift/run-eval.sh; the harness isshellcheck-clean (verified locally) and its behavior is guarded bytests/test_spec_drift_eval.batsin thebatsjob.feat: implement issue #1145 — [Phase 1] Spec-drift frozen offline eva…