Files
2026-07-13 12:20:01 +08:00

6.2 KiB

title, date, category, module, problem_type, component, severity, applies_when, tags, related_issue
title date category module problem_type component severity applies_when tags related_issue
Always-on routing for interactive menus belongs inline in SKILL.md, not in references 2026-04-28 skill-design compound-engineering architecture_pattern ce-plan medium
Authoring a skill that ends in an `AskUserQuestion`-style menu where the user picks the next action
Deciding whether per-option routing belongs in SKILL.md or in a reference file
Reviewing a skill where the agent renders a menu and stops at the user's selection without acting
skill-design
menu-routing
skill-md-vs-references
ce-plan
extraction-rule
load-bearing-rules
https://github.com/EveryInc/compound-engineering-plugin/issues/714

Problem

ce-plan Phase 5.4 presented a four-option post-generation menu (Start /ce-work, Create Issue, Open in Proof, Done for now). The action that should fire when the user picked an option lived only in references/plan-handoff.md. The skill body said "Routing each selection ... lives in references/plan-handoff.md — follow it for every branch" plus a "Load references/plan-handoff.md now" instruction in 5.3.8.

In practice, agents rendered the menu, captured the user's selection, and stopped without firing the routed action. The user picked "Start /ce-work (Recommended)" and watched the agent acknowledge the choice in prose ("User picked Start /ce-work. Handing off — invoke /ce-work next") instead of programmatically invoking ce-work.

Root Cause

Two failure modes compounded:

  1. The agent didn't load the reference. SKILL.md content caches at session start; references load on demand. An agent that renders past the "Load references/plan-handoff.md now" instruction on the way to the menu has no per-option routing in its loaded context. The menu becomes a textual handoff with no associated action.
  2. Even an agent that loaded the reference saw ambiguous language. The reference said **Start /ce-work** -> Call /ce-work with the plan path. That doesn't name the platform's skill-invocation primitive. "Call /ce-work" can be read as "tell the user to type /ce-work in chat" rather than "fire the Skill tool now."

The plugin's own AGENTS.md "Extract Conditional and Late-Sequence Blocks" rule (under "Writing Skill Instructions") guides extraction: extract content that is conditional or late-sequence and represents ~20%+ of the skill. The bare per-option routing was late-sequence (only fires after Phase 5) but not conditional — option 1 always means "invoke ce-work," option 4 always means "end the turn." The always-on subset should not have been extracted.

The same AGENTS.md, in "Inline the Trigger, Not the Content," already articulates the underlying rule: a load-bearing instruction (one that MUST fire reliably) belongs inline at the top of its phase, because references load on demand and an agent that skipped one would stop or guess. The post-menu routing satisfies the load-bearing definition. Failing to apply this principle was the authoring mistake.

Fix

  1. Inline a ### Routing block in SKILL.md Phase 5.4 with one explicit action per menu option. Use platform-explicit invocation language: "Invoke the ce-work skill via the platform's skill-invocation primitive (Skill in Claude Code, Skill in Codex, the equivalent on Gemini/Pi), passing the plan path as the skill argument. Do not merely tell the user to type /ce-work — fire the invocation now so the plan executes in this session."
  2. Mirror the same platform-explicit phrasing in references/plan-handoff.md so both surfaces converge. The reference still owns the elaborate sub-flows (Proof HITL state machine, Issue Creation tracker detection, post-HITL ce-doc-review resync, upload-failure fallback) — those are genuinely conditional and multi-step.
  3. Add a regression test (tests/skills/ce-plan-handoff-routing.test.ts) that fails if any of the four inline routing lines disappear, and specifically asserts that the Start /ce-work routing names the skill-invocation primitive and the plan path.

Authoring Checklist for Future Skills

Before extracting a block to a reference file, ask:

  • Is the block always executed when this phase is reached? If yes, lean toward inlining. References are for branches the agent enters only sometimes.
  • Does the block carry routing for an interactive menu the skill renders? If yes, the bare per-option action belongs inline. The elaborate sub-flow for each option (multi-status state machines, retry logic, downstream skill dispatch) can stay in a reference.
  • Could an agent that skips the reference still complete the skill correctly? If no — if the agent without the reference would stop or guess — the missing content is load-bearing and belongs inline.
  • Is the language platform-explicit? When a routing line says "Call /ce-work," ask whether an agent could read it as "tell the user" rather than "fire the tool." Name the platform primitive (Skill tool, skill-invocation primitive) and the argument shape (plan path, file path).
  • Does the inline block command the agent to load and act, or does it summarize what the reference contains? Inlining is two-sided. The firing imperative and the load instruction belong inline (the rest of this checklist). But a paraphrase of the reference's substance backfires the opposite way: it drifts from the reference (nothing tests the two copies against each other), and it suppresses the load — an agent that already has a workable inline summary judges it "has enough" and never opens the file, so the reference's templates and examples never reach it. Inline the trigger; keep the substance in the one reference that owns it. Test: if the inline text is complete enough to act on alone, the agent will, and the reference's nuance never lands. (See AGENTS.md → "Inline the Trigger, Not the Content.")
  • docs/solutions/skill-design/git-workflow-skills-need-explicit-state-machines.md — same family: skills that render decision points need their state transitions to be deterministic in the loaded context, not one reference-load away.
  • docs/solutions/skill-design/confidence-anchored-scoring.md — load-bearing scoring rubrics also belong inline in SKILL.md so they fire reliably across sessions.