5.4 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 |
|
|
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:
- 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.mdnow" 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. - 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 "Conditional and Late-Sequence Extraction" section 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 "Skill Design Principles," already articulates the underlying rule: "For load-bearing rules (those that MUST fire reliably), put strong language at the top of the relevant phase in SKILL.md, not just in the reference. References can be skipped; SKILL.md is always loaded." The post-menu routing satisfies the load-bearing definition. Failing to apply this principle was the authoring mistake.
Fix
- Inline a
### Routingblock in SKILL.md Phase 5.4 with one explicit action per menu option. Use platform-explicit invocation language: "Invoke thece-workskill via the platform's skill-invocation primitive (Skillin Claude Code,Skillin 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." - Mirror the same platform-explicit phrasing in
references/plan-handoff.mdso both surfaces converge. The reference still owns the elaborate sub-flows (Proof HITL state machine, Issue Creation tracker detection, post-HITLce-doc-reviewresync, upload-failure fallback) — those are genuinely conditional and multi-step. - 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 theStart /ce-workrouting 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).
Related Patterns
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.