Repository navigation
test(notes): add LLM-notes eval harness and prompt snapshot tests - #609
Merged
Merged
Conversation
Adds the eval harness for the LLM-notes pipeline: - test/eval/: golden commit-set fixtures run through the real pipeline with provider responses replayed deterministically via the existing on-disk cache format (withContentHashCache) — no API keys in CI. Deterministic assertions (no marker leaks, no duplicated "Updated dependencies" churn, length bounds, past-tense leaning) are the regression net and hold for replayed and live output alike. Opt-in live mode (RELEASEKIT_EVAL=1) runs a real Ollama model; RELEASEKIT_EVAL_RECORD=1 re-seeds fixtures from the human-readable *.recorded.md. - Prompt snapshot tests pin the five default task system prompts (enhance, categorize, enhanceAndCategorize, releaseNotes, summarize) so a wording tweak — which reshapes every generation and busts the response cache — becomes a visible, reviewed diff instead of a silent change. The task prompt builders are exported for this (module-level, not public API). First eval case covers the free-text release-notes path; the structured enhance-and-categorize path (category-distribution assertions) is the next case the harness is built to take. Part of #542. Claude-Session: https://claude.ai/code/session_01K6CmYRXTZQYs4MNo6AETxT Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
Standing release PR: #614 · v0.41.3 queued (4 packages) · open 15m · ✅ ready to merge Release Preview — v0.41.2 → v0.41.3
These changes will be added to the release PR (#614) when merged: Changelogmonorepo 0.41.2 → 0.41.3
After merge — predicted release
Updated automatically by ReleaseKit |
…eck numbered lists - Record/live modes clear the cache before running, so an edited recording or a live re-run regenerates the fixture instead of reusing a read-first cache hit (withContentHashCache returns the entry before ever calling the provider). - checkPastTenseLeaning inspects numbered list items too, so a model that switches to a numbered list can't bypass the tense check vacuously. - biome ignores the recorded cache fixtures (opaque compact blobs written by withContentHashCache); formatting them churned on every re-record. Claude-Session: https://claude.ai/code/session_01K6CmYRXTZQYs4MNo6AETxT Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d of clearing it Two follow-ups from review of #609. The live-mode wrapper passed the real provider's capabilities through. Capabilities are not free-floating metadata: a task reads them to decide whether to send a structured-output schema/toolName, and both are part of the cache key. Ollama advertises structuredOutputs: true while the harness declares false, so a live recording of a structured case would key differently from the replay that has to read it back — a permanent cache miss. The free-text release-notes case only works today because it never sends a schema, so this was latent until enhance-and- categorize lands, which is the case the harness was built to take next. Every mode now reports the same fixed CAPABILITIES, guarded by a test that feeds the wrapper an Ollama-shaped provider and asserts what it reports back. The module-scope rmSync fixed the stale-fixture bug by deleting the whole committed fixture directory at import time. Vitest evaluates the harness once per test file, so a second eval spec under record or live mode would have each fork wiping the shared directory and racing the other's writes; a live run against a stopped Ollama also left the fixture deleted in the working tree. `withContentHashCache` takes a `refresh` option instead — recording modes bypass the read and write over the entry, which is what re-recording means. Pruning entries orphaned by a prompt change is now an explicit `rm -rf` documented in the harness header, rather than an import side effect. Live mode also tees its response back into `*.recorded.md` with the model that produced it, so the human-readable fixture and the cache entry stay the same generation. The cache key pins the model to EVAL_IDENTITY so replay works, which left nothing recording what actually generated a fixture the length and tense bounds are implicitly calibrated against. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FADRmu9uhtCk3StMoqwV27
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the LLM-notes eval harness from #542 — the deterministic quality net for releasekit's clearest differentiator.
The gap this closes
Nothing measured whether LLM notes were good, only structurally valid. Prompt drift was invisible in review diffs (no prompt was snapshot-tested), and a wording tweak silently busted the entire response cache with no signal.
What's here
test/eval/harnessrelease-notes-basic.json) with a fixed date, so cache keys are stable.withContentHashCache's on-disk format, so CI is deterministic with no API keys. A cache miss (i.e. a changed prompt/input → changed key) fails loudly with a re-record hint — the missing fixture is the drift signal.<!-- releasekit- -->marker leaks, no duplicated "Updated dependencies" churn, length bounds, past-tense leaning. These are the regression net and hold for both replayed and live output.RELEASEKIT_EVAL=1runs a real Ollama model (recording as it goes);RELEASEKIT_EVAL_RECORD=1re-seeds fixtures from the human-readable*.recorded.md.Prompt snapshot tests
Pins the five default task system prompts (
enhance,categorize,enhanceAndCategorize,releaseNotes,summarize). A prompt edit now shows up as a reviewed snapshot diff in the same PR instead of a silent change that reshapes every generation. The task prompt builders are exported for this (module-level, not public package API).Scope note
The first eval case covers the free-text release-notes path. The structured
enhance-and-categorizepath (which unlocks the category-distribution assertion) is the next case the harness is deliberately built to take — the record/replay + assertion infrastructure is path-agnostic.Testing
RELEASEKIT_EVAL_RECORD=1seeds the committed cache fixture, and the default run replays it deterministically.typecheck+biome checkclean; coverage only instrumentssrc/**, so the eval helpers don't affect thresholds.Part of #542. Independent of the #544 CLI-envelope work (#608) — touches only
packages/notes.Greptile Summary
This PR implements the LLM-notes eval harness from #542, adding a golden-fixture record/replay infrastructure and prompt snapshot tests to catch quality regressions and prompt drift in the release-notes pipeline.
test/eval/): golden commit sets replay through the real pipeline against committed cache fixtures usingwithContentHashCache's on-disk format, so CI is deterministic with no API keys. A changed prompt busts the cache key and fails loudly. Opt-in live (Ollama) and record-from-markdown modes are available. Therefreshflag added towithContentHashCacheis the enabler for write-through recording.assertions.ts): four pure checks — marker leak detection, duplicate dependency churn, length bounds, and past-tense leaning — serve as the regression net for both replayed and live output.prompts-snapshot.spec.ts): all five default task system prompts are pinned as Vitest snapshots, making any prompt edit an explicit, reviewable CI event rather than a silent cache-busting change.Confidence Score: 5/5
Safe to merge — changes are purely additive test infrastructure and narrow source exports; no production behaviour is altered.
The
refreshflag inwithContentHashCacheis a simple, well-tested write-through option. All source changes are visibility-only exports for testing. The eval harness, assertions, fixtures, and snapshot tests introduce no runtime risk. The two observations are about edge-case gaps in the tense-detection heuristic that don't affect correctness of the current fixture or the replay pipeline.Files Needing Attention: No files require special attention.
Important Files Changed
refreshoption towithContentHashCache— skips the read and always calls the provider, allowing record/replay modes to overwrite existing entries. Well-tested with a new unit case.withContentHashCache. Fixed provider name and capabilities ensure record→replay keys match.refreshtest verifies that the write-through path replaces a stale entry and that a subsequent non-refresh read finds the fresh value.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Test run] --> B{Env flags?} B -->|default| C[strictOfflineProvider] B -->|RELEASEKIT_EVAL_RECORD=1| D[loadRecorded\n*.recorded.md] B -->|RELEASEKIT_EVAL=1| E[OllamaProvider\nlive model] C --> F[withContentHashCache\nrefresh=false] D --> G[cannedProvider\nfixed content] G --> H[withContentHashCache\nrefresh=true] E --> I[asEvalProvider\nre-brand + tee to *.recorded.md] I --> J[withContentHashCache\nrefresh=true] F -->|cache hit| K[return fixture] F -->|cache miss| L[throw with re-record hint] H -->|always writes| M[cache fixture] J -->|always writes| M K --> N[generateReleaseNotes] M --> N N --> O{Quality assertions} O --> P[findMarkerLeaks] O --> Q[findDuplicateDependencyChurn] O --> R[checkLengthBounds] O --> S[checkPastTenseLeaning]Reviews (3): Last reviewed commit: "test(notes): pin eval capabilities and w..." | Re-trigger Greptile