Task — engineering-spec@1

Add a `cuo` verb to `cs` as a redirect stub

doneTASK-IMP-132
module improvement · class product · priority p2 · created 2026-07-22 · shipped null
depends on TASK-IMP-130 · blocks TASK-IMP-134

TASK-IMP-132: Add a cuo verb to cs as a redirect stub

Summary

Add cuo as a top-level verb in cs that recognises the four LLM-orchestrated workflow names (plan, create-tasks, ship-tasks, improve) and prints which slash command to run inside an agent session, rather than attempting to execute anything standalone.

Problem

docs/plans/PLAN-cli-module-namespacing-2026-07-22/plan.md §2 identifies modules/cuo as backing four multi-step, LLM-orchestrated workflows — /plan, /create-tasks, /ship-tasks, /improve — each involving interview, drafting, self-audit, and HITL approval gates. These are not deterministic scripts cli.mjs can spawnSync and get a meaningful result from outside an agent session; running cyberos-cuo's own Python entry point directly (as README.md:47 shows for local dev, cyberos-cuo list-personas) exposes internal persona/workflow machinery, not the guided, gated experience the slash commands provide. Today, cs (post TASK-IMP-130) has no cuo verb at all — a user who types cs cuo plan gets "unknown command," with no signal that the capability exists under a different invocation.

Proposed Solution

Add a cuo entry to cli.mjs's dispatch. cs cuo <name> where <name> is one of plan, create-tasks, ship-tasks, improve prints a short message naming the matching slash command (/plan, /create-tasks, /ship-tasks, /improve respectively) to run inside a Claude Code (or other agent) session, and exits 0 — it is documentation output, not an error. cs cuo with no name, or an unrecognised name, lists all four valid names. No subprocess is spawned; no attempt is made to run the workflow headlessly.

Alternatives Considered

Success Metrics

Scope

In scope: the cuo dispatch entry in cli.mjs recognising exactly plan, create-tasks, ship-tasks, improve; its no-argument/unrecognised-argument listing behaviour; and the help.sh/tools/install/docs/index.md mentions of the new verb.

Out of scope / Non-Goals

Dependencies

Depends on TASK-IMP-130 (adds a verb to the same cli.mjs dispatch table under the renamed bin). Blocks TASK-IMP-134's end-to-end regression.

Relationship to TASK-IMP-076. Same dispatch-table precedent as TASK-IMP-131 (mcp/gates established the spawnSync-based pattern this family of verbs follows); this task, notably, does NOT spawn any subprocess at all — it is the one verb in this batch that is pure static text, which is itself a deliberate scope decision (see Alternatives Considered), not an oversight.

Sibling-task coordination with TASK-IMP-131. Both tasks add a new entry to the same cli.mjs dispatch table and depend only on TASK-IMP-130; see TASK-IMP-131's Dependencies section for the shared merge-conflict/sequencing note — it applies symmetrically to this task and is not repeated in full here to avoid two specs disagreeing if one is edited later.

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 — Verdict summary

Seven §1 clauses (including a split 1.4/1.4a), six ACs, four edge cases including one security-class row. This task's findings cluster around test-design precision and an under-explained asymmetry with its sibling task, rather than a wrong technical premise — the underlying design (a pure-text redirect stub) was sound from the first draft.

§2 — Findings (all resolved)

ISS-001 — AC 1 was redundant with, and weaker than, AC 4

The first draft's AC 1 asserted only that bare cs cuo did not print "unknown command." AC 4 (the listing behaviour) already proves something strictly stronger for the same invocation — that it prints all four valid names and exits 0 — which trivially implies the weaker claim. Citing clause 1.1 to its own separate, weaker AC added a test that could pass without adding evidence beyond what AC 4 already provides. Resolved: retraced clause 1.1 to the cs cuo plan AC instead, which positively proves recognition for a different invocation shape (a valid name, not the bare case), making the two ACs complementary rather than one subsuming the other.

ISS-002 — clause 1.4 conflated two different exit-code semantics

The original single clause covered both "bare cs cuo" and "cs cuo <unrecognised-name>" with the same "exit 0" requirement. cli.mjs's own established convention (exit 2 for "unknown command," cli.mjs:87) treats a genuine usage mistake differently from a bare, information-seeking invocation (exit 0, cli.mjs:59). Treating a mistyped workflow name the same as asking for orientation loses a signal a calling script might want (was this a mistake, or a deliberate ask for help). Material: the spec would have shipped correct-looking behaviour with the wrong exit code. Resolved: split into 1.4 (bare, exit 0) and 1.4a (unrecognised name, exit 2), with matching separate ACs.

ISS-003 — AC 5's test mechanism does not match this repo's actual test conventions

The original AC 5 called for "a process-spy wrapper around child_process.spawnSync" — a JS-level module-mocking approach with no precedent anywhere in tools/install/tests/, which is exclusively bash scripts asserting on stdout/exit codes against stub binaries on $PATH (see TASK-IMP-131's own tests). An implementer following this AC literally would need to invent a mocking mechanism the rest of the suite doesn't use. Resolved: revised to the tripwire-binary style already established — stand-in python3/bash scripts that write a marker file if invoked, asserted absent afterward.

ISS-004 — AC 6's documentation check was satisfiable by an unrelated match

The original check ("contains a word from {redirect, prints, run inside}") tested for the word's presence anywhere in the whole file, which could pass on a coincidental, unrelated use of one of those common words elsewhere in help.sh or tools/install/docs/index.md (e.g. "prints this text" describing an unrelated flag) without the cuo verb itself being described as a redirect at all. Resolved: tightened to require the descriptive word on the same or adjacent line as the actual cuo mention.

ISS-005 — tools/install/docs/index.md:27's "same eight commands" count was never flagged as needing an update

This task (and its sibling TASK-IMP-131) each add a new verb to the same dispatch table tools/install/docs/index.md:27 describes as having exactly eight commands. Neither task's first draft had any clause or AC checking that this hardcoded count gets updated — a real, concrete way for the docs to go silently stale the moment either verb ships. Resolved: added to clause 1.6 and AC 6, with an explicit note that whichever of TASK-IMP-131/132 lands second owns the numeral update.

ISS-006 — the asymmetry with TASK-IMP-131's local-availability detection was unexplained

Clause 1.5 forbids cuo from probing whether cyberos-cuo is installed locally, while TASK-IMP-131's memory verb does exactly that (detects and reports local availability) for its own underlying tool. Left unexplained, a reader comparing the two sibling specs could reasonably conclude one of them made an arbitrary or inconsistent design call. It is not arbitrary — the plan itself constrains cuo more tightly than memory ("do not implement standalone execution," with no mechanism left open, versus memory's "mechanism ... is an implementation decision"). Resolved: added the explanation directly to clause 1.5.

ISS-007 — FM-101: title exceeded the 72-character limit by one (caught by the machine floor)

task-lint.mjs, run after the six findings above were resolved, flagged the title at 73 code points — one over the cap. Resolved: shortened to "Add a cuo verb to cs as a redirect stub" (title metadata only).

§3 — TRACE-006 semantic sufficiency (per clause)

ClauseVerb demandCited test assertsVerdict
1.1 MUST recognise as known commandpositive routing occurs for a valid nameAC 1: cs cuo plan reaches its redirect output, which only happens if cuo was recognisedsufficient after retracing (was redundant/weak - ISS-001)
1.2 MUST redirect to /planpositive text + exit codeAC 1: both assertedsufficient
1.3 MUST redirect the other three namespositive text + exit code per nameAC 2: all three assertedsufficient
1.4 bare invocation MUST list + exit 0positive listing + specific codeAC 3: both assertedsufficient
1.4a unrecognised name MUST list + exit 2positive listing + a DIFFERENT specific code than 1.4AC 4: both asserted, distinct from AC 3sufficient after the split (was conflated - ISS-002)
1.5 MUST NOT spawn/probeabsence of a side effect across all four valid invocationsAC 5 (revised): tripwire markers absent after all four runsufficient after revision (mechanism was unrealistic - ISS-003)
1.6 MUST document as redirect + correct countpositive proximate text + a computed-not-hardcoded numberAC 6 (revised): both asserted with proximity + dynamic countsufficient after revision (was loose - ISS-004, ISS-005)

§4 — Resolution

Six findings, all material, all resolved in the audited revision. Score = 10/10.

Status transition draft -> ready_to_implement is authorised by this verdict per STATUS-REFERENCE.md §1.1. The two human-acceptance gates in /ship-tasks remain unchanged and are recorded human verdicts.


End of TASK-IMP-132 audit.