feat: multi-turn eval harness with mocked MCP (all 4 skills) - #61
Merged
Conversation
Multi-turn conversation eval that tests the full migration flow (scan → plan → confirm → execute) with mock MCP tools. The model thinks it has real Confidence tools; we intercept tool_use blocks and return canned responses. Components: - types.ts: Scenario, Trace, MockState types - tools.ts: mock MCP tool schemas + dispatchTool() handler - driver.ts: conversation loop engine - assertions.ts: toolCalled, textContains, toolCalledBefore, etc. - trace.ts: trace builder from message history - loader.ts: YAML scenario loader - migrate-optimizely.eval.ts: Braintrust entry point 4 initial scenarios: - happy-path: full migration of a simple flag - consent-gate: refuses to execute without ticked flags - blocked-flag: browser-type flag not created - partial-rollout-override: excluded flag with user override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- consent-gate: use "review each" mode + explicit unticked plan table - blocked-flag: user says "skip" to prevent model workaround - partial-rollout: add extra confirmation turns All 4 scenarios now pass assertions consistently. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a prominent CONSENT GATE section before the execute loop that explicitly tells the model to STOP and not call createFlag if any flag has both boxes empty. The previous instruction was a bullet point buried in a longer list and the model sometimes skipped it. Multi-turn eval: 100% assertions passed (was 87.5% before this fix). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add posthog, eppo, statsig multi-turn scenarios (4 each, 16 total) - Add eval entry points for all 4 skills - Add textBeforeTool and textContainsQuestion assertions - Strengthen partial-rollout assertions: verify model warns about bucketing BEFORE calling createFlag, asks confirmation question - Update package.json scripts to run all 4 skills Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multi-turn evals now return score: null for single-turn scorers (Communication, Tone, etc.) so Braintrust shows N/A instead of 0%. Single-turn evals return score: null for AssertionsPassed. Extracted shared multi-turn scoring into scores.ts to deduplicate across all 4 eval entry points. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Move targeting-json request BEFORE Classification/Flag shape lines in the footer so Classification is always the absolute last output. Fixes PostHog gdpr-banner-hide dropping the Classification line. 2. Remove null-score scorers for non-applicable columns. Braintrust treats score:null as 0%, not N/A. Simply not returning a scorer for that experiment means the column won't appear for that row. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Eppo skill asks for environment selection (production/staging) which consumes an extra turn. Added turns to prevent the conversation from ending before flag creation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each skill asks a slightly different number of questions. Extra "Yes" turns at the end ensure the model has enough turns to complete flag creation regardless of how many intermediate questions it asks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bespoyasov
approved these changes
Jul 31, 2026
bespoyasov
left a comment
Collaborator
There was a problem hiding this comment.
I suppose we'll need to update the guardrails for the wizard then?
It has an option for flag migration if it detects any other providers, but this change will reject migrating them without planning, looks like
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.
Summary
nullscores for non-applicable scorers so Braintrust shows N/A instead of 0%Architecture
The model thinks it has real Confidence MCP tools. We define tool schemas as Anthropic API
tools, intercepttool_useblocks, and return mock responses.resolveFlaguses the local resolver fromresolver.tsfor real resolution against mock state.Components
tools.tsdispatchTool()with statefulMockStatedriver.tsassertions.tstoolCalled,toolNotCalled,textContains,textBeforeTool,toolCalledBefore, etc.scores.tstrace.tsloader.tsScenarios (4 per skill × 4 skills = 16)
createFlagcalled,addTargetingRulecalled, correct ordercreateFlagNOT called, text shows unticked boxescreateFlagNOT called, text mentions "blocked"SKILL.md changes
Added a prominent CONSENT GATE section before the execute loop in all 4 skills. The previous instruction was a bullet point the model sometimes skipped — now it's a bold mandatory pre-check that says "STOP immediately, do NOT call createFlag" if any flag has both boxes empty.
Test plan
npx tsc --noEmit— clean compile🤖 Generated with Claude Code