feat(experiments): add draft launchdarkly-experiment-hypothesis-builder skill - #95
feat(experiments): add draft launchdarkly-experiment-hypothesis-builder skill#95mabramowitz-pixel wants to merge 15 commits into
Conversation
…er skill Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Skill eval results
Only suites whose source actually changed since their last recorded score were re-run. Soft-failing while we stabilise the baseline. |
|
From the eval on #96 — here's a prompt you can paste into Claude (run from the repo root) to fix the three findings, then re-run the eval to confirm it goes green. The three findings map to failing assertions in the suite ( via LD Research 🤖 |
… alignment in hypothesis-builder Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hard, dominant, and strip write walkthrough Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ag/update-feature-flag) at top of hypothesis-builder Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ilder Five test cases picked by the three-lens method (golden path, weak-input coaching, search-term stemming, metric<->outcome alignment, non-real input safety). Adds shared list-metrics / list-metric-events tool defs + mocks (reused by future experiment and metrics suites), manifest entry, and npm scripts. Stacked on the skill PR (#95) so the diff is eval-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o obtain IDs; leave null Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h) blocking — forbid A/A hypothesis + silent metric substitution Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Eval outcome + known residual (A/A gating)The eval suite (#96) is green against these skill fixes. Summary of the three behaviors the evals check:
Known residual — A/A gating (model adherence). For the input "A/A test to validate bucketing," the agent (Sonnet 4.6) recognizes it's an A/A yet still drafts an "A/A validation hypothesis" rather than stopping to confirm intent. Step 0 was strengthened twice to forbid exactly this (an A/A has identical variants ⇒ no hypothesis; reply must be a single clarifying question), and the agent still builds the package. Behavior is also noisy run-to-run. This is a model-adherence limitation on one input, not a wording gap — the suite clears the 75% gate because the other four cases are clean and nothing writes. Filing as a known limitation rather than over-tuning the prose further. |
Advisory / handoff-only run via read-only mcp_tool_allowlist; trajectory + llm-rubric assertions across 5 cases (golden path, weak input, metric resolution, metric/outcome mismatch, A/A safety). Adds shared list-metrics and list-metric-events tool defs + mocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ique model Replace the 9-step workflow with a focused scaffold engine that detects components (change/measurement/rationale), fills holes from user input, and returns fixed critique messages. Slims the handoff payload and moves flag/metric resolution, config, and sample sizing to experiment-setup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep both the new investigate skill from main and the updated hypothesis-builder entry with v0.2.0 description. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures skills.json matches what generate_catalog.py --check expects (unicode escapes for non-ASCII characters). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cchung-at-LD
left a comment
There was a problem hiding this comment.
I've made changes to the skill file according to the designs.
| force_skill_invocation: true # force-load the skill so the eval tests it, not base Claude | ||
| # Advisory / handoff-only: expose read tools only so it cannot write. | ||
| mcp_tool_allowlist: | ||
| - list-flags |
There was a problem hiding this comment.
Don't think it needs access to any mcp tools right now
Align to finalized flow designs: rename Generate to "Fix and grade", remove Apply control (Fix and grade commits ghost suggestions), update header/helper/footer copy, make stale-until-graded loop explicit, and document panel-never-saves boundary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ement rule, fix toggle rescoring
…de (EXPT-4644) (#134) fix(experiments): reject nonsense hypotheses; add JSON mode [EXPT-4644]
…ot junk (#139) * fix junk classification for ai assisted hypothesis builder * fix non sequitur routing
…#141) * fix(experiments): unblock hypothesis-builder validate and eval checks Two independent breakages on this branch turned 4 checks red. Neither came from #139 — it only touched SKILL.md, which is what diff-changed-skills.js uses to decide the suite must re-run, so it surfaced both. validate: SKILL.md declared name: launchdarkly-experiment-hypothesis-assistant while the directory is ...-builder, and validate_skills.py requires the two to match. Landed in 7e486ab (v0.3.0). Everything else in the repo already said -builder (_manifest.js, the evals/<suite> dir, the provider's skill_slug, the eval:hypothesis-builder script), so the frontmatter was the lone outlier. Renamed it, plus the handoffFrom value where the skill names itself — the only other occurrence in the repo, with no consumer matching the literal. This also realigns the eval's forced invocation: the provider invokes /<skill_slug> = /launchdarkly-experiment-hypothesis-builder, which did not match the name the skill declared. Evaluate suite: all 5 tests errored in 0s with "Template render error [Line 190, Column 90] expected variable end" — none reached an API call. The suite registered SKILL.md as promptfoo's prompt, promptfoo renders prompts through nunjucks, and SKILL.md documents its own hole syntax ({{measurement:...}}, {{component:hint}}) in the output-contract examples. Nunjucks reads `measurement` as a variable, hits the `:`, and throws. Landed in 849ee1e (#134), whose CI never ran these checks. Replaced the prompt with a suite-local function prompt that returns SKILL.md wrapped in {% raw %}. A function prompt does not skip nunjucks — renderPrompt assigns the return value to basePrompt and still calls renderString on it — and promptfoo's own autoWrapRawIfPartialNunjucks only fires on unclosed tags, so closed-but-invalid {{measurement:...}} sails through. Wrapping supplies the {% raw %} that helper would have added; renderString then returns the file byte-for-byte, so results.json still shows the exact skill text and SKILL.md on disk keeps its hole syntax byte-identical to the o11y contract. Kept suite-local rather than in evals/shared/, which is a GLOBAL_TRIGGERS entry and would force all 7 suites to re-run. Verified: validate_skills.py passes 46/46; nunjucks reproduces the exact Line 190 Column 90 failure on the bare file and renders the wrapped prompt byte-identically; npm test 70/70; one live suite test passes in 1m2s (was 5 errors in 0s). Aggregate scores and Evaluate gate need no separate fix — both were cascades of the errored suite. * update comments * fix evals to match new skill
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0e81d12. Configure here.
| 1. **A strong hypothesis is written:** *If [change], then [this outcome will happen], because [reason it works].* | ||
| 2. **A hypothesis must follow that structure.** The scaffold exists to enforce it. | ||
| 3. **A measurement is a described outcome, not a named metric.** "More clicks," "faster time," "less drop-off" all count. Never require a formal metric name; never invent one. Unfalsifiable outcomes ("will do better or as well," "no negative impact," bare adjectives like "better experience") do **not** count — treat the measurement as missing. A goal that names a specific outcome ("raise click-through," "increase checkout completion") is a measurement with holes — scaffold it, not junk. A vague direction with no specific outcome ("grow the business," "optimize the funnel") is not a measurement — leave it a hole — but it is still a scaffold, not junk. | ||
| 4. **Exactly one measurement — the primary — goes in the hypothesis.** ~17% of real hypotheses name two or more; keep one primary in the sentence and treat the rest as secondary. |
There was a problem hiding this comment.
Contradictory multi-measurement rules
High Severity
Foundational rule 4 tells the agent to keep one primary measurement in the sentence, while the Multiple measurements section and the "What NOT to do" list tell it to put every measurement in the then hole joined with "or". The headless schema also says holes are only for missing slots, yet this path treats measurement as present. The eval suite already leaves holes unasserted because of this conflict, so multi-measurement outputs stay undefined.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 0e81d12. Configure here.
| "alsoWatching": ["secondary measurements, if any"], | ||
| "rationale": "the mechanism, or null", | ||
| "components": { "change": true, "measurement": false, "rationale": false }, | ||
| "measurementCount": "0 | 1 | multiple", |
There was a problem hiding this comment.
Ambiguous handoff count value
Medium Severity
In the handoff JSON example, measurementCount is shown as the literal string "0 | 1 | multiple", with no separate prose listing allowed values. Models often copy example payloads verbatim, so the handoff can emit that pipe-separated string instead of one of 0, 1, or multiple, breaking downstream display-state composition.
Reviewed by Cursor Bugbot for commit 0e81d12. Configure here.


Summary
Draft skill for AI-assisted experiment hypothesis building, now in its third iteration (v0.3.0).
v0.3.0 changes — Structural alignment to finalized designs and detection-model improvements:
is_junk/is_aa/measurement_count/rewrite_worthy), with routing as a separate UI-presentation stepthenhole joined with "or" and an amber Measurement warning (replaces the up-arrow primary-switch pills)routefield withcomponents+measurementCount+ non-exclusiveflagslaunchdarkly-experiment-setupfor buildPrevious work
(Same-repo replacement for #94, which was opened from a fork and couldn't run Skill Evals.)
Test plan
python3 scripts/validate_skills.pypassespython3 scripts/generate_catalog.pyregeneratesskills.jsonwith updated entryevals/launchdarkly-experiment-hypothesis-builder/🤖 Generated with Claude Code
Note
Overview
Adds a draft advisory skill (
launchdarkly-experiment-hypothesis-builder, v0.3.0) that coaches rough experiment ideas into an If/then/because hypothesis: component detection, routing (scaffold/rewrite/junk/aa), scaffold holes, fixed critique copy, and a headless JSON contract for the experiment builder. It is read-only toward LaunchDarkly (no writes) and hands off tolaunchdarkly-experiment-setup.Catalog & docs: README Experiments table,
skills.json, and areferences/diagnostic-tree.mdflaw-to-config reference.Eval harness: New promptfoo suite with
prompt.js(nunjucks{% raw %}aroundSKILL.md), binary contract asserts (route, components, holes, measurements, security no-echo), npm scripts, manifest entry, and metric list mocks pluslist-metrics/list-metric-eventstool defs.Provider:
claude-skill-agent-sdkgains optionalmcp_tool_allowlistso advisory evals can expose only read-only MCP tools; the hypothesis-builder eval disables MCP tools entirely and forces skill invocation.Reviewed by Cursor Bugbot for commit 0e81d12. Bugbot is set up for automated code reviews on this repo. Configure here.