From 94a71f48bc4331c0c1cc9e708da87f7464c771a2 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sun, 30 Aug 2026 00:00:14 -0400 Subject: [PATCH] chore: make the workflow skills model-invocable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `disable-model-invocation: true` does not merely make a skill prefer `/name` — the model cannot invoke it at all, and its description is absent from the skill listing. So "create a PR for #2163", which is how that work actually starts, could never reach `pr-flow`. The original rationale ("procedures with side effects we'd invoke by name anyway") conflates invoking a skill, which only loads instructions, with performing the procedure it describes. Flipped `pr-flow`, `pre-push-gate`, `board-ops` and `test-servers` to model-invocable. `user-invocable` is an independent axis, so this is purely additive — every `/name` still works. `issue-create`, `issue-triage` and `release` stay deliberate. `board-ops` is in the set because `pr-flow` and `issue-create` delegate their card steps to it: a pointer from a model-invocable skill to a manual-only one is a dead end for the model. Costs weighed: the listing share goes 1,199 → 2,256 of 4,000, and a spurious load costs ~250 characters where a missed one costs a wrong base branch or an unsigned commit. The rules stay resident in AGENTS.md either way — this improves the odds of getting the procedure too, it does not become the guarantee. Eval cases committed for all four, measured at RUNS=3: pre-push-gate, board-ops and test-servers 5/5; pr-flow 4/5, with "take it through to a pull request" at 100% and the Copilot-review case borderline. Three initial cases were replaced rather than tuned green: their answers are resident in AGENTS.md, so the model answering without the skill is correct behaviour, not a miss — the rule this file already records about what makes a valid trigger case. AGENTS.md's maintenance rule now says to default to `false` and explains why, so the advice this change walks back does not get re-applied. Signed-off-by: cliffhall --- .claude/skills/board-ops/SKILL.md | 2 +- .claude/skills/board-ops/evals/evals.json | 22 ++++++++++++ .claude/skills/pr-flow/SKILL.md | 4 +-- .claude/skills/pr-flow/evals/evals.json | 22 ++++++++++++ .claude/skills/pre-push-gate/SKILL.md | 4 +-- .claude/skills/pre-push-gate/evals/evals.json | 22 ++++++++++++ .claude/skills/test-servers/SKILL.md | 2 +- .claude/skills/test-servers/evals/evals.json | 22 ++++++++++++ AGENTS.md | 35 ++++++++++++------- 9 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 .claude/skills/board-ops/evals/evals.json create mode 100644 .claude/skills/pr-flow/evals/evals.json create mode 100644 .claude/skills/pre-push-gate/evals/evals.json create mode 100644 .claude/skills/test-servers/evals/evals.json diff --git a/.claude/skills/board-ops/SKILL.md b/.claude/skills/board-ops/SKILL.md index 47054420..8344172e 100644 --- a/.claude/skills/board-ops/SKILL.md +++ b/.claude/skills/board-ops/SKILL.md @@ -1,7 +1,7 @@ --- name: board-ops description: gh recipes for the Inspector project boards — add a card, move its Status, set or re-score its Priority, delete a card, and recover from a deleted single-select option. Covers board #28 (v2) and board #11 (v1), their node/field/option IDs, and the option-deletion hazard. -disable-model-invocation: true +disable-model-invocation: false --- # Board operations diff --git a/.claude/skills/board-ops/evals/evals.json b/.claude/skills/board-ops/evals/evals.json new file mode 100644 index 00000000..8bd8dcd9 --- /dev/null +++ b/.claude/skills/board-ops/evals/evals.json @@ -0,0 +1,22 @@ +[ + { + "prompt": "Move the board card for issue 2189 to In Review.", + "expect": "board-ops" + }, + { + "prompt": "How do I set the Priority field on a card in the v2 project board?", + "expect": "board-ops" + }, + { + "prompt": "The project board rejected my Status option ID. How do I get the current ones?", + "expect": "board-ops" + }, + { + "prompt": "Explain the difference between TCP and UDP.", + "expect": null + }, + { + "prompt": "What year was the first Moon landing?", + "expect": null + } +] diff --git a/.claude/skills/pr-flow/SKILL.md b/.claude/skills/pr-flow/SKILL.md index 9af16125..87c292d5 100644 --- a/.claude/skills/pr-flow/SKILL.md +++ b/.claude/skills/pr-flow/SKILL.md @@ -1,7 +1,7 @@ --- name: pr-flow -description: Take an issue through to a merged PR — branch naming, the DCO signoff, screenshots, opening the PR against the right base, requesting a Copilot review, responding to review rounds, and closing out (manual close plus Done). -disable-model-invocation: true +description: Take an issue through to a merged PR in this repo, and what to do at each step. Use when asked to open, create or submit a PR; when a DCO or signoff check fails; when requesting a Copilot review or responding to review comments; when naming a branch; when attaching screenshots to a PR; or when closing out after a merge. +disable-model-invocation: false --- # PR flow diff --git a/.claude/skills/pr-flow/evals/evals.json b/.claude/skills/pr-flow/evals/evals.json new file mode 100644 index 00000000..0b83c32e --- /dev/null +++ b/.claude/skills/pr-flow/evals/evals.json @@ -0,0 +1,22 @@ +[ + { + "prompt": "I've finished the fix for issue 2071. Take it through to a pull request.", + "expect": "pr-flow" + }, + { + "prompt": "How do I get Copilot to review a PR in this repo?", + "expect": "pr-flow" + }, + { + "prompt": "The DCO check is red on my branch. How do I fix the commits I already pushed?", + "expect": "pr-flow" + }, + { + "prompt": "What does this regex match? /^[a-z]+$/", + "expect": null + }, + { + "prompt": "Translate 'good morning' into Spanish.", + "expect": null + } +] diff --git a/.claude/skills/pre-push-gate/SKILL.md b/.claude/skills/pre-push-gate/SKILL.md index f17d574a..128e1de5 100644 --- a/.claude/skills/pre-push-gate/SKILL.md +++ b/.claude/skills/pre-push-gate/SKILL.md @@ -1,7 +1,7 @@ --- name: pre-push-gate -description: Run the mandatory pre-push gate and diagnose a failing stage — npm run format then npm run local:gate, what each stage checks, and the known traps (stale tsc cache, symlinked worktree node_modules, concurrent runs, orphaned smoke ports). -disable-model-invocation: true +description: Diagnose a failing stage of this repo's pre-push gate, and run it correctly. Use when npm run local:gate or npm run validate fails; when lint, coverage, a smoke, or Storybook goes red; when a stage behaves differently inside a git worktree; when the whole run times out; or when deciding what to run before pushing. +disable-model-invocation: false --- # The pre-push gate diff --git a/.claude/skills/pre-push-gate/evals/evals.json b/.claude/skills/pre-push-gate/evals/evals.json new file mode 100644 index 00000000..6655b2fe --- /dev/null +++ b/.claude/skills/pre-push-gate/evals/evals.json @@ -0,0 +1,22 @@ +[ + { + "prompt": "npm run local:gate just failed at the coverage stage. What do I do?", + "expect": "pre-push-gate" + }, + { + "prompt": "The Storybook stage of the gate fails in my worktree even though the tests pass. What is wrong?", + "expect": "pre-push-gate" + }, + { + "prompt": "Everything in the gate started timing out at once. What causes that?", + "expect": "pre-push-gate" + }, + { + "prompt": "What is 17 multiplied by 23?", + "expect": null + }, + { + "prompt": "Write a limerick about a cat.", + "expect": null + } +] diff --git a/.claude/skills/test-servers/SKILL.md b/.claude/skills/test-servers/SKILL.md index 8ce55cfb..2e8b91b6 100644 --- a/.claude/skills/test-servers/SKILL.md +++ b/.claude/skills/test-servers/SKILL.md @@ -1,7 +1,7 @@ --- name: test-servers description: Run a composable MCP test server by hand — pick the showcase config that reproduces a given feature or bug, build test-servers, and connect with the right protocol era. Covers the stale-build hazard. -disable-model-invocation: true +disable-model-invocation: false --- # Running a test server diff --git a/.claude/skills/test-servers/evals/evals.json b/.claude/skills/test-servers/evals/evals.json new file mode 100644 index 00000000..25ec237f --- /dev/null +++ b/.claude/skills/test-servers/evals/evals.json @@ -0,0 +1,22 @@ +[ + { + "prompt": "I want to reproduce the OAuth token revocation behaviour by hand. Which test server do I run?", + "expect": "test-servers" + }, + { + "prompt": "How do I start one of the composable MCP test servers with a config file?", + "expect": "test-servers" + }, + { + "prompt": "I renamed a file under test-servers/src and the fixture is still running the old code. Why?", + "expect": "test-servers" + }, + { + "prompt": "Sort this list alphabetically: banana, apple, cherry.", + "expect": null + }, + { + "prompt": "What is the boiling point of water in Fahrenheit?", + "expect": null + } +] diff --git a/AGENTS.md b/AGENTS.md index 48547a7b..0e730f71 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,16 +16,16 @@ users invoke them by name. | Skill | Covers | How it loads | | --- | --- | --- | -| [`local-dev`](.claude/skills/local-dev/SKILL.md) | Install and run each client; the `@inspector/core` alias; and the **reasoning** behind Dependency placement below — what each rule defends against and how to tell you have hit one (the rules themselves stay here) | Model-invoked | -| [`project-structure`](.claude/skills/project-structure/SKILL.md) | Which client owns which surface, what is in `core/`, where a new file belongs | Model-invoked (not user-invocable) | -| [`testing`](.claude/skills/testing/SKILL.md) | Where a test file goes, which command runs it, the tiers, clearing the coverage gate, `renderWithMantine` | Model-invoked | +| [`local-dev`](.claude/skills/local-dev/SKILL.md) | Install and run each client; the `@inspector/core` alias; and the **reasoning** behind Dependency placement below — what each rule defends against and how to tell you have hit one (the rules themselves stay here) | Model-invoked, or `/local-dev` | +| [`project-structure`](.claude/skills/project-structure/SKILL.md) | Which client owns which surface, what is in `core/`, where a new file belongs | Model-invoked only | +| [`testing`](.claude/skills/testing/SKILL.md) | Where a test file goes, which command runs it, the tiers, clearing the coverage gate, `renderWithMantine` | Model-invoked, or `/testing` | | [`issue-create`](.claude/skills/issue-create/SKILL.md) | The five-step create flow: version label, type label, milestone, board card, Status + Priority | `/issue-create` | | [`issue-triage`](.claude/skills/issue-triage/SKILL.md) | The two-pass sweep of unboarded issues, the priority rubric and its score comment, the board audit | `/issue-triage` | -| [`board-ops`](.claude/skills/board-ops/SKILL.md) | `gh project` recipes and the field/option IDs for boards #28 and #11; the option-deletion hazard and its recovery | `/board-ops` | -| [`pr-flow`](.claude/skills/pr-flow/SKILL.md) | Branch naming, DCO signoff, screenshots, opening the PR, requesting a Copilot review, responding, closing out | `/pr-flow` | -| [`pre-push-gate`](.claude/skills/pre-push-gate/SKILL.md) | Running `npm run local:gate` and diagnosing a failing stage | `/pre-push-gate` | +| [`board-ops`](.claude/skills/board-ops/SKILL.md) | `gh project` recipes and the field/option IDs for boards #28 and #11; the option-deletion hazard and its recovery | Model-invoked, or `/board-ops` | +| [`pr-flow`](.claude/skills/pr-flow/SKILL.md) | Branch naming, DCO signoff, screenshots, opening the PR, requesting a Copilot review, responding, closing out | Model-invoked, or `/pr-flow` | +| [`pre-push-gate`](.claude/skills/pre-push-gate/SKILL.md) | Running `npm run local:gate` and diagnosing a failing stage | Model-invoked, or `/pre-push-gate` | | [`release`](.claude/skills/release/SKILL.md) | Cutting a release: bump on `v2/main`, milestone merge, tag `origin/main`, publish | `/release` | -| [`test-servers`](.claude/skills/test-servers/SKILL.md) | Picking and running a showcase test server; the stale-build hazard | `/test-servers` | +| [`test-servers`](.claude/skills/test-servers/SKILL.md) | Picking and running a showcase test server; the stale-build hazard | Model-invoked, or `/test-servers` | Longer-form human documentation lives in [`docs/`](./docs) — see the table in the [README](./README.md#documentation). @@ -164,10 +164,21 @@ that from happening: line** — no BOM, no leading blank line. 2. **Every skill declares its invocation mode explicitly.** `disable-model-invocation` is **required** on every skill (`true` = reachable - only by name, `false` = the model may fire it). Prefer `true` for any procedure - with side effects: it removes the trigger question entirely *and* keeps the - description out of the listing budget. A skill that is background knowledge - rather than an action also sets `user-invocable: false`. + only by name, `false` = the model may fire it). A skill that is background + knowledge rather than an action also sets `user-invocable: false`. + **Default to `false`.** `true` was the original default here, on the argument + that a procedure with side effects would be typed as `/name` anyway — but + *invoking* a skill has no side effects, it loads instructions, and the premise + is false for anything a user asks for in prose. "Create a PR for #2163" is how + that work actually starts, and under `true` the model **cannot** reach the + skill at all: it is absent from the listing and the Skill tool refuses it. The + costs are asymmetric — a spurious load costs ~250 characters, a missed one + costs a wrong base branch or an unsigned commit — and the budget is not tight + (all ten skills would total ~2.8k of 4k). Reserve `true` for a procedure that + is genuinely only ever started deliberately, as `/release` is. + ⚠️ **A `true` skill cannot be reached by another skill either.** If a + model-invocable skill says "see `/board-ops`", that pointer is a dead end for + the model unless `board-ops` is model-invocable too. ⚠️ **`paths` is not a free win.** It looks like the deterministic option, and it does gate loading to matching files — but measured against the `testing` skill's own eval cases, adding it roughly **halved** the rate at which the same @@ -201,7 +212,7 @@ that from happening: overflows, and drops the least-invoked entries **first** — which are exactly the model-invoked skills that must fire on their own. `verify:skills` prints the current cost against the budget recorded in `scripts/lib/skill-manifest.mjs` - (1,199/4,000 characters as of this writing) and fails when it is exceeded. Raise + (2,256/4,000 characters as of this writing) and fails when it is exceeded. Raise the budget deliberately, or tighten a description; each entry is capped at 1,536 characters regardless, so **put the key use case first**.