/bat-story-eval
Compare MCP tool behavior between target and baseline versions using pre-built and custom stories with diff-based triage.
$ npx -y skills add homeassistant-ai/ha-mcp --skill bat-story-eval --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/bat-story-eval
Context preview
The summary Claude sees to decide when to auto-load this skill.
Compare MCP tool behavior between target and baseline versions using pre-built and custom stories with diff-based triage.
SKILL.md
bat-story-eval.SKILL.mdname: bat-story-eval
description: Compare MCP tool behavior between target and baseline versions using pre-built and custom stories with diff-based triage.
disable-model-invocation: true
argument-hint: --baseline v6.6.1 [--agents gemini] [--stories s01,s02]
allowed-tools: Bash, Read, Write, Glob, Grep, Task
BAT Story Evaluation
You are the evaluator. Follow these steps IN ORDER. Do not skip steps.
Parse Arguments
From `$ARGUMENTS`, extract:
- `--baseline`: REQUIRED. Git tag/branch of the released version (e.g., `v6.6.1`).
- `--agents`: Agent list (default: `gemini`). Comma-separated.
- `--stories`: Force specific pre-built story IDs (e.g., `s01,s02`). Overrides triage selection.
- `--all-stories`: Skip triage, run ALL pre-built stories.
- `--keep-container`: Keep HA containers alive after run for manual inspection.
- `--model`: Model for Claude agent (e.g., `haiku`, `sonnet`).
If `$ARGUMENTS` is `--help` or missing `--baseline`, show usage and stop:
/bat-story-eval --baseline v6.6.1
/bat-story-eval --baseline v6.6.1 --agents gemini,claude
/bat-story-eval --baseline v6.6.1 --stories s01,s02
/bat-story-eval --baseline v6.6.1 --all-stories --agents claude --model haiku
Step 0: Triage (Diff Analysis + Custom Story Design)
0a. Compute Diff
cd /home/julien/github/ha-mcp/worktree/uat-stories
git diff <baseline>..HEAD -- src/ha_mcp/ --stat
git diff <baseline>..HEAD -- src/ha_mcp/ --name-only
Classify changed files:
- **Tool modules** (`tools/tools_*.py`): specific tool implementations changed
- **Core code** (`client/`, `server.py`, `errors.py`, `tools/util_helpers.py`): affects all tools
- **Utilities** (`utils/`, `resources/`): may affect all tools
- **No src/ changes**: only tests/docs/config — select 2 smoke-test stories
0b. Select Pre-built Stories
Skip if `--stories` or `--all-stories` was passed.
1. Read the diff from 0a 2. Read all story YAMLs in `tests/uat/stories/catalog/s*.yaml` (title, description, prompt, setup) 3. For each story, reason about whether the diff could affect its outcome:
- What tools/code paths would this story exercise?
- Do any of those overlap with what changed?
4. Rules:
- Story likely exercises changed code -> **selected**
- Core code changed (`client/`, `server.py`, `errors.py`) -> **all stories selected**
- No src/ changes -> 2 representative stories as smoke test
5. Report which stories were selected and why (one sentence per story)
0c. Design Custom Stories (at least 1)
Read the diff carefully. Your job is to catch regressions. For each changed code path NOT covered by selected pre-built stories, ask: "could this break something a user would notice?" If yes, design a custom story to test that hypothesis.
**Guidelines**: Always create at least 1 custom story. Each must test a distinct regression hypothesis — don't create stories that overlap. Stop when you've covered the risky gaps.
Write each as `/tmp/custom_c<NN>.yaml` using the standard story format:
id: c01
title: "Short description of what is being tested"
category: custom
weight: 5
description: >
Rationale: [what changed in the diff and why this scenario tests it]
setup:
- tool: ha_config_set_helper
args:
helper_type: "input_boolean"
name: "Test Entity Name"
prompt: >
[Natural language request a real user would make that exercises the changed code]
teardown: []
verify:
questions:
- "Did the agent achieve the expected outcome?"
- "Did it use the expected tools?"
expected:
tools_should_use:
- ha_search
description: >
[What a correct agent should do]**Design principles:**
- Focus on code paths that changed in the diff
- Plausible user scenarios, not synthetic edge cases
- Setup creates realistic HA state via FastMCP in-memory steps
- Prompts are what a real user would type
- At least 1. Each tests a distinct regression hypothesis. Stop when gaps are covered.
Step 1: Run Baseline Version
For EACH agent, run all stories against the **baseline** version. One container per agent, reused across all stories.
1a. Start container with first story
cd /home/julien/github/ha-mcp/worktree/uat-stories
uv run python tests/uat/stories/run_story.py \
catalog/<first_story>.yaml \
--agents <agent> --keep-container \
--branch <baseline> \
--results-file local/uat-results.jsonl
**CAPTURE from stderr**: HA URL (e.g., `http://localhost:32771`), token, session file path.
1b. Verify, then run remaining pre-built stories
After each story, verify via ha_query.py using the story's `verify.questions`:
uv run python tests/uat/stories/scripts/ha_query.py \
--ha-url http://localhost:PORT --ha-token TOKEN \
--agent <agent> \
"Does an automation with alias 'Sunset Porch Light' exist?"
Record each answer as **confirmed** / **denied** / **unclear**. A non-zero exit from `ha_query.py` means the query itself failed (the output carries an `[exit N]` marker; `[exit 124]` is a timeout) — that is not one of the three outcomes; re-run it, and if it keeps failing record the story as `unverified` (Step 5) rather than scoring it. See `references/evaluation-protocol.md`.
Run remaining pre-built stories on the same container:
uv run python tests/uat/stories/run_story.py \
catalog/<next_story>.yaml \
--agents <agent> --ha-url http://localhost:PORT --ha-token TOKEN \
--branch <baseline> \
--results-file local/uat-results.jsonl
Verify each immediately after running.
1c. Run custom stories on same container
uv run python tests/uat/stories/run_story.py \
/tmp/custom_c01.yaml \
--agents <agent> --ha-url http://localhost:PORT --ha-token TOKEN \
--branch <baseline> \
--results-file local/uat-results.jsonl
Verify each via ha_query.py using the custom story's `verify.questions`.
1d. Stop container
docker stop $(docker ps -q --filter "ancestor=ghcr.io/home-assistant/home-assistant:2026.1.3") 2>/dev/null
Read more
name: bat-story-eval description: Compare MCP tool behavior between target and baseline versions using pre-built and custom stories with diff-based triage. disable-model-invocation: true argument-hint: --baseline v6.6.1 [--agents gemini] [--stories s01,s02] allowed-tools: Bash, Read, Write, Glob, Grep, Task
BAT Story Evaluation
You are the evaluator. Follow these steps IN ORDER. Do not skip steps.
Parse Arguments
From `$ARGUMENTS`, extract:
- `--baseline`: REQUIRED. Git tag/branch of the released version (e.g., `v6.6.1`).
- `--agents`: Agent list (default: `gemini`). Comma-separated.
- `--stories`: Force specific pre-built story IDs (e.g., `s01,s02`). Overrides triage selection.
- `--all-stories`: Skip triage, run ALL pre-built stories.
- `--keep-container`: Keep HA containers alive after run for manual inspection.
- `--model`: Model for Claude agent (e.g., `haiku`, `sonnet`).
If `$ARGUMENTS` is `--help` or missing `--baseline`, show usage and stop:
/bat-story-eval --baseline v6.6.1 /bat-story-eval --baseline v6.6.1 --agents gemini,claude /bat-story-eval --baseline v6.6.1 --stories s01,s02 /bat-story-eval --baseline v6.6.1 --all-stories --agents claude --model haiku
Step 0: Triage (Diff Analysis + Custom Story Design)
0a. Compute Diff
cd /home/julien/github/ha-mcp/worktree/uat-stories git diff <baseline>..HEAD -- src/ha_mcp/ --stat git diff <baseline>..HEAD -- src/ha_mcp/ --name-only
Classify changed files:
- **Tool modules** (`tools/tools_*.py`): specific tool implementations changed
- **Core code** (`client/`, `server.py`, `errors.py`, `tools/util_helpers.py`): affects all tools
- **Utilities** (`utils/`, `resources/`): may affect all tools
- **No src/ changes**: only tests/docs/config — select 2 smoke-test stories
0b. Select Pre-built Stories
Skip if `--stories` or `--all-stories` was passed.
1. Read the diff from 0a 2. Read all story YAMLs in `tests/uat/stories/catalog/s*.yaml` (title, description, prompt, setup) 3. For each story, reason about whether the diff could affect its outcome:
- What tools/code paths would this story exercise?
- Do any of those overlap with what changed?
4. Rules:
- Story likely exercises changed code -> **selected**
- Core code changed (`client/`, `server.py`, `errors.py`) -> **all stories selected**
- No src/ changes -> 2 representative stories as smoke test
5. Report which stories were selected and why (one sentence per story)
0c. Design Custom Stories (at least 1)
Read the diff carefully. Your job is to catch regressions. For each changed code path NOT covered by selected pre-built stories, ask: "could this break something a user would notice?" If yes, design a custom story to test that hypothesis.
**Guidelines**: Always create at least 1 custom story. Each must test a distinct regression hypothesis — don't create stories that overlap. Stop when you've covered the risky gaps.
Write each as `/tmp/custom_c<NN>.yaml` using the standard story format:
id: c01
title: "Short description of what is being tested"
category: custom
weight: 5
description: >
Rationale: [what changed in the diff and why this scenario tests it]
setup:
- tool: ha_config_set_helper
args:
helper_type: "input_boolean"
name: "Test Entity Name"
prompt: >
[Natural language request a real user would make that exercises the changed code]
teardown: []
verify:
questions:
- "Did the agent achieve the expected outcome?"
- "Did it use the expected tools?"
expected:
tools_should_use:
- ha_search
description: >
[What a correct agent should do]**Design principles:**
- Focus on code paths that changed in the diff
- Plausible user scenarios, not synthetic edge cases
- Setup creates realistic HA state via FastMCP in-memory steps
- Prompts are what a real user would type
- At least 1. Each tests a distinct regression hypothesis. Stop when gaps are covered.
Step 1: Run Baseline Version
For EACH agent, run all stories against the **baseline** version. One container per agent, reused across all stories.
1a. Start container with first story
cd /home/julien/github/ha-mcp/worktree/uat-stories uv run python tests/uat/stories/run_story.py \ catalog/<first_story>.yaml \ --agents <agent> --keep-container \ --branch <baseline> \ --results-file local/uat-results.jsonl
**CAPTURE from stderr**: HA URL (e.g., `http://localhost:32771`), token, session file path.
1b. Verify, then run remaining pre-built stories
After each story, verify via ha_query.py using the story's `verify.questions`:
uv run python tests/uat/stories/scripts/ha_query.py \ --ha-url http://localhost:PORT --ha-token TOKEN \ --agent <agent> \ "Does an automation with alias 'Sunset Porch Light' exist?"
Record each answer as **confirmed** / **denied** / **unclear**. A non-zero exit from `ha_query.py` means the query itself failed (the output carries an `[exit N]` marker; `[exit 124]` is a timeout) — that is not one of the three outcomes; re-run it, and if it keeps failing record the story as `unverified` (Step 5) rather than scoring it. See `references/evaluation-protocol.md`.
Run remaining pre-built stories on the same container:
uv run python tests/uat/stories/run_story.py \ catalog/<next_story>.yaml \ --agents <agent> --ha-url http://localhost:PORT --ha-token TOKEN \ --branch <baseline> \ --results-file local/uat-results.jsonl
Verify each immediately after running.
1c. Run custom stories on same container
uv run python tests/uat/stories/run_story.py \ /tmp/custom_c01.yaml \ --agents <agent> --ha-url http://localhost:PORT --ha-token TOKEN \ --branch <baseline> \ --results-file local/uat-results.jsonl
Verify each via ha_query.py using the custom story's `verify.questions`.
1d. Stop container
docker stop $(docker ps -q --filter "ancestor=ghcr.io/home-assistant/home-assistant:2026.1.3") 2>/dev/null
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with Home Assistant. Using natural language, control smart home devices, query states, execute services and manage your automations.
Repo: homeassistant-ai/ha-mcp
Other skills on ha-mcp.
- /bat-adhoc
Run bot acceptance tests to validate MCP tools work correctly from a real AI agent's perspective. Use when testing PRs, detecting regressions, or verifying tool changes end-to-end with Claude/Gemini CLIs.
Open skill - /contrib-pr-review
Review a contribution PR for safety, quality, and readiness. Checks for security concerns, test coverage, size appropriateness, and intent alignment. Use when reviewing external contributions.
Open skill - /contributors-update
Find merged PR authors missing from README and update the contributors list after approval
Open skill - /issue-analysis
Deep analysis of a single GitHub issue with codebase exploration, implementation planning, and architectural assessment. Use when you need to analyze a GitHub issue, assess its complexity, plan implementation approaches, and post a structured analysis comment. Triggers on
Open skill - /issue-to-pr-resolver
Implement a GitHub issue end-to-end — create a worktree branch, implement the feature with tests, create a draft PR, then iteratively resolve all CI failures and review comments until the PR is clean. Use when you need to fully implement a GitHub issue from start to merge-ready.
Open skill - /my-pr-checker
Manage your own GitHub pull requests — check CI status, inline review comments, PR-level comments, resolve review threads, fix issues, and iterate until all checks pass and threads are resolved. Use for managing your own PRs (not external contributions). Triggers on "check my
Open skill

