Skip to content

feat: multi-turn eval harness with mocked MCP (all 4 skills) - #61

Merged
vahidlazio merged 8 commits into
mainfrom
feat/multi-turn-eval
Jul 31, 2026
Merged

feat: multi-turn eval harness with mocked MCP (all 4 skills)#61
vahidlazio merged 8 commits into
mainfrom
feat/multi-turn-eval

Conversation

@vahidlazio

@vahidlazio vahidlazio commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Multi-turn conversation eval testing the full migration flow (scan → plan → confirm → execute) with mock MCP tools
  • Covers all 4 skills: Optimizely, PostHog, Eppo, Statsig (16 scenarios total)
  • Strengthens consent gate in all 4 SKILL.md files to prevent executing without ticked flags
  • Returns null scores 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, intercept tool_use blocks, and return mock responses. resolveFlag uses the local resolver from resolver.ts for real resolution against mock state.

Components

File Purpose
tools.ts Mock MCP tool schemas + dispatchTool() with stateful MockState
driver.ts Conversation loop: send message → intercept tool_use → mock respond → next turn
assertions.ts toolCalled, toolNotCalled, textContains, textBeforeTool, toolCalledBefore, etc.
scores.ts Shared scoring with null scores for non-applicable single-turn scorers
trace.ts Trace builder from Anthropic message history
loader.ts YAML scenario loader

Scenarios (4 per skill × 4 skills = 16)

Scenario Tests Key assertions
happy-path Full flow: scan → plan → execute createFlag called, addTargetingRule called, correct order
consent-gate Execute without ticking flags → refuses createFlag NOT called, text shows unticked boxes
blocked-flag Unsupported operator → BLOCKED, not created createFlag NOT called, text mentions "blocked"
partial-rollout-override Excluded flag with user override Warns about bucketing BEFORE createFlag, asks confirmation question

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
  • Optimizely: 100% AssertionsPassed
  • PostHog: 100% AssertionsPassed
  • Eppo: 100% AssertionsPassed
  • Statsig: 100% AssertionsPassed
  • Null scores for N/A columns in Braintrust

🤖 Generated with Claude Code

vahidlazio and others added 5 commits July 30, 2026 11:47
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>
@vahidlazio vahidlazio changed the title feat: multi-turn eval harness with mocked MCP feat: multi-turn eval harness with mocked MCP (all 4 skills) Jul 30, 2026
vahidlazio and others added 3 commits July 30, 2026 13:59
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 bespoyasov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@vahidlazio
vahidlazio merged commit b4f0a33 into main Jul 31, 2026
1 check passed
@vahidlazio
vahidlazio deleted the feat/multi-turn-eval branch July 31, 2026 12:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants