Task — engineering-spec@1

"Canonical slash-commands — /cyberos-run, /cyberos-memory, /cyberos-skill-list, /cyberos-route markdown definitions in modules/plugin/commands/"

draftTASK-PLUGIN-003
module plugin · class product · priority p0 · created 2026-05-19 · shipped null
depends on TASK-PLUGIN-001, TASK-PLUGIN-002 · blocks none

§1 — Description (BCP-14 normative)

The PLUGIN module MUST ship 4 canonical slash-commands at modules/plugin/commands/<name>.md. Each command is a markdown file with YAML frontmatter binding the command to one or more MCP tools exposed by TASK-PLUGIN-002.

  1. MUST ship exactly 4 commands in v1 per DEC-2421:
  1. MUST use the frontmatter schema documented at modules/plugin/commands/SCHEMA.md (clause 6 below for the schema itself).
  1. MUST mirror the underlying MCP tool's input_schema exactly per DEC-2423. A command argument list is derived from the tool's properties object; required-argument list is derived from the tool's required array. Authors MUST NOT inline alternative schemas.
  1. MUST declare 4 example trigger phrases per command per TASK-SKILL-111 + DEC-2424. Phrases live in the frontmatter triggers: array. Hosts that route by description match (Claude Code's skill router) use these to disambiguate.
  1. MUST include a description: field of 60-480 characters per TASK-SKILL-111. This is the host-rendered command summary. The 60-char floor forces meaningful copy; the 480-char ceiling forces conciseness.
  1. MUST carry frontmatter conforming to this YAML shape: ```yaml --- name: <slash-command-name> # without leading /, kebab-case description: <60-480 char summary> binds_to:
  1. MUST validate via modules/plugin/tests/test_commands_*.py that:
  1. MUST include a body section per command that explains: when to invoke, what scopes are required, what side-effects occur, and a worked example. Body is rendered in the host's command-detail view (Claude Code's ? button on a command).
  1. MUST NOT declare a tool binding to a non-existent tool (validated by test_commands_bind_to_valid_tools).
  1. MUST NOT add new commands without a successor task (task-PLUGIN-003a, etc.) per DEC-2421.
  1. MUST NOT rename a command between v1.x.y releases — rename = breaking change, requires major bump.

§2 — Why this design

Why markdown + frontmatter (clause 2)? Markdown commands are the standard format Claude Code expects (per Anthropic Agent Skills spec) and Cowork rendering uses. YAML frontmatter gives structured metadata (bindings, triggers) while keeping the body rich for human readers.

Why exactly 4 commands in v1 (DEC-2421)? Each command is a learnable UI primitive — too many and users can't remember what's available. The 4 cover orchestration (run + route), memory (memory read/append), and discovery (skill-list). Future commands land via successor tasks after usage data.

Why mirror tool input_schema (DEC-2423, clause 3)? Two sources of truth for the same shape drift. The manifest schema (TASK-PLUGIN-001) is canonical. Commands MUST derive from it.

Why 4 trigger phrases (DEC-2424, clause 4)? TASK-SKILL-111 calibrated 4 as the minimum count that gives the description-match router enough fingerprint to disambiguate. Two phrases is too noisy; eight is overkill for a slash command that the user already typed.

Why 60-480 char description (clause 5)? Same as TASK-SKILL-111 description-enrichment range. Below 60 = unhelpful blurb; above 480 = wall-of-text in cramped UI panels.

Why destructive flag in frontmatter (clause 6)? Hosts surface destructive-command warnings differently. The flag is the host's hook to render that UI. Without it, every command looks the same and users invoke destructive ops without warning.

Why body section with worked example (clause 8)? Slash commands are user-facing. The body teaches the user how to use the command — required scopes, expected behaviour, sample output. Without the body, hosts surface a stub that says "no description provided" and adoption stalls.

Why no command renames within v1.x.y (clause 11)? Renames break user muscle memory and any scripts that invoke commands by name. Rename = major version bump.


§3 — API contract

commands/SCHEMA.md (excerpt)

# CyberOS slash-command frontmatter — v1 contract
name: string                          # required, kebab-case, no leading /
description: string                   # required, 60-480 chars
binds_to:                             # required, ≥1 entry
  - tool: string                      # required, SEP-986 pattern
    when: string                      # optional; "always" if omitted
arguments:                            # optional; subset of bound tool's input_schema
  - name: string
    description: string
    required: boolean
    type: enum[string,number,boolean,object,array]
triggers:                             # required, exactly 4 entries
  - string
destructive: boolean                  # required; true if any bound tool has destructive: true

commands/cyberos-run.md (excerpt)

---
name: cyberos-run
description: Execute a CyberOS workflow chain for a chosen persona. Used when the user wants to run a structured multi-step process (e.g. architect a system, prepare an investor update) and benefit from CUO's persona-aware orchestration with memory audit emission.
binds_to:
  - tool: cyberos.cuo.execute_workflow
    when: always
arguments:
  - name: persona
    description: Persona slug (e.g. chief-technology-officer)
    required: true
    type: string
  - name: workflow
    description: Workflow slug (e.g. architect-new-system)
    required: true
    type: string
  - name: inputs
    description: Workflow-specific input parameters as JSON
    required: false
    type: object
triggers:
  - "/cyberos-run chief-technology-officer architect-new-system"
  - "Run the architect-new-system workflow for the CTO"
  - "Execute CUO workflow"
  - "Kick off the ADR-quick-capture flow"
destructive: false
---

## When to use

Use this command when you have a CUO-defined workflow ready to execute end-to-end. Workflows are
multi-step chains that run through the CyberOS supervisor and emit memory audit rows for every step.

## Required scopes

- `cyberos:cuo:execute`
- `cyberos:memory:write` (for audit emission)

## Side effects

- Spawns one Task per invocation (long-running, async)
- Emits 1× `plugin.invoked` audit row at task start
- Emits N× `cuo.step_completed` audit rows during execution
- Emits 1× `cuo.workflow_completed` audit row at finish

## Example

```text
You: /cyberos-run chief-technology-officer adr-quick-capture Plugin: Started task t-abc123. Polling status... Plugin: Step 1/3 complete — issue_authored Plugin: Step 2/3 complete — adr_drafted Plugin: Step 3/3 complete — adr_published Plugin: Workflow completed in 4.2s. ADR-2402 published.

### Frontmatter validators (Python pseudocode)

```python
def validate_command_file(path: Path) -> List[ValidationError]:
    fm, body = parse_frontmatter(path.read_text())
    errors = []
    if not 60 <= len(fm["description"]) <= 480:
        errors.append(("description", "length out of [60,480]"))
    if len(fm["triggers"]) != 4:
        errors.append(("triggers", f"expected 4, got {len(fm['triggers'])}"))
    for bind in fm["binds_to"]:
        if bind["tool"] not in REGISTERED_TOOL_NAMES:
            errors.append(("binds_to", f"tool '{bind['tool']}' not in TASK-PLUGIN-002 registry"))
    return errors

§4 — Acceptance criteria

  1. Exactly 4 command files existls modules/plugin/commands/cyberos-*.md | wc -l → 4.
  2. Every command has parseable YAML frontmatter — test parses each file.
  3. Every command's description is 60-480 chars — test asserts length.
  4. Every command has exactly 4 triggers — test asserts len(triggers) == 4.
  5. Every binds_to.tool exists in TASK-PLUGIN-002 registry — test loads tool list, checks each bind.
  6. Every argument name + type is a subset of bound tool's input_schema — test loads manifest, compares.
  7. /cyberos-run binds to cyberos.cuo.execute_workflow — explicit fixture check.
  8. /cyberos-memory binds to cyberos.memory.read_audit AND cyberos.memory.append_audit — explicit fixture check (2 bindings with when disambiguators).
  9. /cyberos-skill-list binds to cyberos.skill.list_catalog — explicit fixture check.
  10. /cyberos-route binds to cyberos.cuo.route — explicit fixture check.
  11. /cyberos-memory append variant has destructive: true — test asserts.
  12. Other 3 commands have destructive: false — test asserts.
  13. Frontmatter validator rejects missing description — fixture file fails validation.
  14. Frontmatter validator rejects 3 triggers — fixture file fails validation.
  15. Frontmatter validator rejects unknown tool binding — fixture with tool: foo.bar.baz fails.
  16. Body section has worked example — test grep-asserts ## Example appears in every command body.
  17. Body section lists required scopes — test grep-asserts ## Required scopes appears in every command body.

§5 — Verification

# modules/plugin/tests/test_commands_have_frontmatter.py
from pathlib import Path
import yaml

COMMANDS_DIR = Path(__file__).parent.parent / "commands"

def test_exactly_four_commands():
    files = sorted(COMMANDS_DIR.glob("cyberos-*.md"))
    assert len(files) == 4, f"expected 4, got {len(files)}: {[f.name for f in files]}"

def test_each_has_yaml_frontmatter():
    for f in COMMANDS_DIR.glob("cyberos-*.md"):
        raw = f.read_text()
        assert raw.startswith("---\n"), f"{f.name} missing frontmatter"
        fm_end = raw.find("\n---\n", 4)
        fm = yaml.safe_load(raw[4:fm_end])
        assert "name" in fm and "description" in fm and "binds_to" in fm
# modules/plugin/tests/test_commands_bind_to_valid_tools.py
REGISTERED = {
    "cyberos.cuo.list_personas", "cyberos.cuo.list_workflows",
    "cyberos.cuo.route", "cyberos.cuo.execute_workflow",
    "cyberos.memory.read_audit", "cyberos.memory.append_audit",
    "cyberos.skill.list_catalog", "cyberos.skill.invoke_skill",
}

def test_all_bindings_exist():
    for f in COMMANDS_DIR.glob("cyberos-*.md"):
        fm = load_frontmatter(f)
        for bind in fm["binds_to"]:
            assert bind["tool"] in REGISTERED, \
                f"{f.name}: tool '{bind['tool']}' not in TASK-PLUGIN-002 registry"
# modules/plugin/tests/test_commands_description_length.py
def test_description_length_in_range():
    for f in COMMANDS_DIR.glob("cyberos-*.md"):
        fm = load_frontmatter(f)
        n = len(fm["description"])
        assert 60 <= n <= 480, f"{f.name}: description length {n} not in [60,480]"
# modules/plugin/tests/test_commands_trigger_count.py
def test_each_has_four_triggers():
    for f in COMMANDS_DIR.glob("cyberos-*.md"):
        fm = load_frontmatter(f)
        assert len(fm["triggers"]) == 4, \
            f"{f.name}: expected 4 triggers, got {len(fm['triggers'])}"
        for t in fm["triggers"]:
            assert isinstance(t, str) and len(t) > 0

§6 — Implementation skeleton

Markdown commands are content, not code. The skeleton is: write each of 4 files following the SCHEMA.md frontmatter contract; write 4 validator tests; wire commands into the manifest's commands[] array.


§7 — Dependencies


§8 — Example payloads

(See §3 for full /cyberos-run example.)

Manifest commands array entry:

{
  "name": "/cyberos-run",
  "file": "commands/cyberos-run.md",
  "description": "Execute a CyberOS workflow chain for a chosen persona."
}

§9 — Open questions

All resolved.


§10 — Failure modes inventory

FailureDetectionOutcomeRecovery
Command file missing frontmatterparser raisesvalidator test failsAuthor adds --- fences
Frontmatter YAML invalidyaml.safe_load raisesvalidator test failsAuthor fixes YAML
Description too shortlength checkvalidator test failsAuthor expands to ≥60 chars
Description too longlength checkvalidator test failsAuthor trims to ≤480 chars
Triggers count != 4length checkvalidator test failsAuthor adds/removes triggers
Tool binding doesn't existset membershipvalidator test failsAuthor fixes tool name OR ships task-PLUGIN-002a to add the tool
Argument type mismatchtype check vs input_schemavalidator test failsAuthor fixes argument type
Destructive flag missing on append-memory commandexplicit fixture assertionvalidator test failsAuthor adds destructive: true
New command added without taskmanifest commands[] growsvalidator test fails (5 instead of 4)Author rolls back OR ships task-PLUGIN-003a
Command rename within v1git diff on filenamemanual review caught at PRRevert or bump to v2
Host renders empty command bodymanual smoke testUX bug surfaces in installAuthor adds body
Body missing worked examplegrep test for ## Examplevalidator test failsAuthor adds Example section
Two commands with same namefilesystem uniquenessinstall failsinherent — filenames unique
Trigger phrase duplicate across commandsnot detected (acceptable)host router may misrouteAuthors choose distinct phrasings

§11 — Implementation notes


End of TASK-PLUGIN-003 spec.