Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion docs/conventions/invocation-mode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ setup skills are class (ii) by contract; the 141
| `dometrain:sync` | (iii) maintainer-only | KEEP `true` |
| `education:teach` | (i) deliberate mode-entry, persistent coaching state | KEEP `true` |
| `firecrawl:update` | (iii) maintainer-only | KEEP `true` |
| `planning:questionnaire` | none — no side effects, not setup, not maintainer-only | **FLIP → `false`** (filed as [#2969](https://github.com/melodic-software/claude-code-plugins/issues/2969)) |
| `planning:questionnaire` | none — no side effects, not setup, not maintainer-only | **FLIPPED → `false`** ([#2969](https://github.com/melodic-software/claude-code-plugins/issues/2969); the re-check for a latent rationale found none — see below) |
| `playbooks:update` | (iii) maintainer-only | KEEP `true` |
| `repo-fleet-hygiene:apply` | (i) mutating fleet apply incl. branch deletion | KEEP `true` |

Expand All @@ -131,6 +131,29 @@ the enforcement criterion shipped alongside them as `skill-quality:check` **chec
edited in-lane. Fleet after that normalization (2026-08-19): 220 top-level skills = 161 `false` /
0 missing key / 59 `true`.

**The one flip, and the latent rationale it was re-checked against (2026-08-19, #2969).** The grade
found no exception class for `planning:questionnaire`, so the flip was gated on first looking for a
reason the grade could not see. The candidate was a trigger collision with `planning:interview` —
both plausibly firing on "I need to ask…"-shaped requests. There is none: the two are separated by
*who holds the knowledge*, and each description already routes to the other on that axis
(`questionnaire` says to run `/planning:interview` when the user can answer themselves;
`interview`'s phrases — "ask me questions first", "what do you need to know" — are about
interrogating the user, while `questionnaire`'s phrases name the third-party holder who is asked in
the user's place). Two costs of the `true` surfaced instead, both now paid: its trigger phrases were
deliberately left unoptimized because a
hidden skill's description is never matched against user text (planning CHANGELOG 0.30.1), and its
own description advertises a hand-off from an interview branch that the invocation-reach invariant
made unreachable while it stayed hidden. Fleet after the flip: 162 `false` / 58 `true` = 48 `*:setup`
plus 10 non-setup.

**One of those 10 is not graded above, and this is where that is recorded.**
`session-flow:show-options` landed 2026-08-18, a day after the grade, so the table's population
predates it and the ADR 0005 bound leaves it unswept rather than silently covered. Its `true` is
therefore un-attributed to any exception class as of this writing — check 24 emits its
hand-verify note for exactly this case. Grading it is filed as
[#3024](https://github.com/melodic-software/claude-code-plugins/issues/3024); the other nine
carry the verdicts in the table.

## Cross-references

- PLUGIN-PHILOSOPHY: setup contract (class ii source), Instruction economy (listing-cost
Expand Down
2 changes: 1 addition & 1 deletion plugins/planning/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "planning",
"version": "0.30.7",
"version": "0.31.0",
"userConfig": {
"use_ask_user_question": {
"type": "boolean",
Expand Down
17 changes: 17 additions & 0 deletions plugins/planning/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
All notable changes to the `planning` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.31.0]

### Changed

- **`questionnaire` is model-invoked (#2969).** Course lane 8's fleet grade found it was the one
`disable-model-invocation: true` skill matching none of the rubric's three exception classes: it
has no side effects beyond writing a Markdown document, is not a setup skill, and is not
maintainer-only. The flip was gated on re-checking for a latent rationale the grade could not see
— a trigger collision with `interview` — and there is none: the two are separated by who holds
the knowledge, and each description already routes to the other on that axis. Rubric:
`docs/conventions/invocation-mode/README.md`.
- **`questionnaire` gains real trigger phrases.** 0.30.1 deliberately left them unoptimized because
a hidden skill's description is never matched against user text. Now that it is model-invoked,
the description carries phrases a user would actually type ("I don't know, that's the client's
call", "send this to someone else to answer", "write up questions for our security team") beside
the ones it already had. Every phrase already present is preserved verbatim.

## [0.30.7]

### Added
Expand Down
6 changes: 3 additions & 3 deletions plugins/planning/skills/questionnaire/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
description: "Turn a decision the user cannot answer — because another person holds the knowledge — into a Markdown questionnaire handed off async. Interviews the user only about the send (who it goes to, what they need back), never about the subject the recipient holds, then writes a discovery questionnaire aimed at the gap. Use when: 'make a questionnaire for X', 'I need to ask my DBA/the client/legal about this', 'turn this into a doc someone else fills in', or when an interview branch defers to a person-arbiter; skip when the user can answer themselves (run /planning:interview) or when the answer is agent-lookupable."
description: "Turn a decision the user cannot answer — because another person holds the knowledge — into a Markdown questionnaire handed off async. Interviews the user only about the send (who it goes to, what they need back), never about the subject the recipient holds, then writes a discovery questionnaire aimed at the gap. Use when: 'make a questionnaire for X', 'I need to ask my DBA/the client/legal about this', 'turn this into a doc someone else fills in', 'I don't know, that's the client's call', 'send this to someone else to answer', 'write up questions for our security team', or when an interview branch defers to a person-arbiter; skip when the user can answer themselves (run /planning:interview) or when the answer is agent-lookupable."
Comment thread
kyle-sexton marked this conversation as resolved.
argument-hint: "[topic]"
user-invocable: true
disable-model-invocation: true
disable-model-invocation: false
metadata:
workflow-stage: contract
summary: Turn a decision someone else must answer into an async questionnaire
Expand All @@ -22,7 +22,7 @@ This is the third routing bucket beside `/planning:interview`'s facts-vs-decisio

**Interview the send, not the subject.** Interview the user only about the *send*, which they can always answer: who it goes to, and what they need back. Never quiz the user about the subject the recipient holds — that knowledge gap is exactly why the questionnaire exists. The questions in the document target the **gap** between what the recipient knows and what the user needs.

**Route away when no one else holds the answer.** If it emerges that the user can answer the decision themselves (no third-party knowledge holder), do not produce a questionnaire for nobody — recommend `/planning:interview` and stop. Never invent a recipient to justify the artifact.
**Route away when no one else holds the answer.** If it emerges that the user can answer the decision themselves (no third-party knowledge holder), do not produce a questionnaire for nobody — invoke `/planning:interview` via the Skill tool and stop this skill. Never invent a recipient to justify the artifact. The explicit hand-off matters now that this skill is model-invoked: the model can land here from a natural-language request, and bare `/name` prose would read as advice to the human and strand the decision unresolved.

## The loop

Expand Down
15 changes: 13 additions & 2 deletions plugins/planning/skills/questionnaire/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
"id": 3,
"name": "routes-self-answerable-to-interview",
"prompt": "/planning:questionnaire cache eviction policy — actually I think I know the answer, I just haven't decided.",
"expected_output": "The skill recognizes the decision is the user's own to make (no third-party knowledge holder) and routes to /planning:interview instead of producing a questionnaire for nobody.",
"expected_output": "The skill recognizes the decision is the user's own to make (no third-party knowledge holder) and hands off to /planning:interview — invoked via the Skill tool, not named as bare advice — instead of producing a questionnaire for nobody.",
"files": [],
"expectations": [
"Output does not produce a questionnaire when no other person holds the knowledge",
"Output recommends /planning:interview (or an equivalent decision conversation with the user) instead",
"The hand-off to /planning:interview is an explicit Skill-tool invocation rather than bare /name prose left for the user to act on",
"The skill never invents a recipient to justify the artifact"
]
},
Expand All @@ -49,6 +49,17 @@
"Output does not hardcode a gh/GitHub call to track the questionnaire lifecycle",
"Output does not attempt to send or transmit the questionnaire itself; delivery stays with the user"
]
},
{
"id": 5,
"name": "model-routes-third-party-decision-here",
"prompt": "I can't finish this plan until I know the data-retention window, and that's legal's call, not mine.",
"expected_output": "With no slash command typed, the request routes to this skill rather than to /planning:interview, because a third party holds the knowledge — the axis the two skills split on. It then interviews the send only (who legal is to the user, what they need back) and never quizzes the user about retention rules, which is the gap the questionnaire exists to close.",
"expectations": [
"The request routes here rather than to /planning:interview, on the grounds that another person holds the knowledge",
"Questions to the user cover the send (recipient, what is needed back), never the retention subject itself",
"The deliverable is a questionnaire document written to the topic's memory slice, with its path reported"
]
}
]
}