Task — engineering-spec@1

Add a `memory` verb to `cs`, gated on local availability

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

TASK-IMP-131: Add a memory verb to cs, gated on local availability

Summary

Add memory as a top-level verb in cs's dispatch table that subprocess-dispatches into the locally available cyberos-memory BRAIN-store CLI when present, and fails with a clear, actionable message when it is not — because the npm payload does not currently vendor that CLI's implementation, only its protocol spec.

Problem

docs/plans/PLAN-cli-module-namespacing-2026-07-22/plan.md §5 item 3 asks for a memory verb under cs that reaches modules/memory's BRAIN-store operations, leaving the dispatch mechanism to implementation. cli.mjs already dispatches to two external processes this way — mcp (a Node subprocess, cli.mjs:69) and gates (a bash script inside the TARGET repo's vendored machine, cli.mjs:78) — so a subprocess-dispatch verb is a well-precedented shape, not a new pattern.

What is NOT precedented, and was not established by the plan, is that the actual cyberos-memory Python package is reachable from an installed cs. Reading build.sh:25,159,161-162 shows the payload's memory/ directory is populated with exactly three files — AGENTS.md, memory.schema.json, memory.invariants.yaml — the Layer-1 memory protocol that gets vendored into a consumer repo's own .cyberos/memory/ for humans and agents to follow. None of modules/memory/cyberos/'s actual Python source ships in the npm package. cyberos-memory (modules/memory/pyproject.toml:6) is also confirmed not published to PyPI (plan §2). So on a machine where a user only ran npm install -g @cyberskill/cyberos (or npx cyberos install, soon npx cs install), there is no cyberos-memory for a memory verb to reach — python -m cyberos (modules/memory/cyberos/__main__.py's own documented invocation) will not resolve to anything meaningful unless that machine separately has the internal package pip-installed from a full monorepo checkout.

Proposed Solution

Add a memory entry to cli.mjs's dispatch: on cs memory <args...>, first resolve whether a working cyberos-memory is reachable — try the console-script name cyberos is already taken by the OLD public bin and by the BRAIN CLI itself, so resolution must not rely on bare $PATH lookup of cyberos (that is the exact collision this whole plan exists to remove); instead resolve via python3 -m cyberos --help succeeding in the current environment (matching the module's own documented invocation form), or a CYBEROS_MEMORY_STORE-adjacent override the operator sets explicitly. If that resolution succeeds, spawnSync("python3", ["-m", "cyberos", ...rest], { stdio: "inherit" }). If it fails, print a clear message naming that cyberos-memory is an internal package not bundled with this CLI today, and exit non-zero — never a raw Python traceback or a silent no-op.

Alternatives Considered

Success Metrics

Scope

In scope: the memory dispatch entry in cli.mjs, its python3 -m cyberos resolution check and clear failure message, and the doc updates in help.sh/docs/index.md describing the new verb and its local-availability caveat.

Out of scope / Non-Goals

Dependencies

Depends on TASK-IMP-130 (the cs bin rename must land first; this task adds a verb to the SAME dispatch table cli.mjs exposes under the new name). Blocks TASK-IMP-134's end-to-end regression, which must exercise cs memory <cmd> per the plan's own success criterion (plan §6 item 7).

Relationship to TASK-IMP-076. That task established cli.mjs's spawnSync-based dispatch pattern for mcp and gates; this task reuses the same pattern for a third external-process verb rather than inventing a new dispatch shape.

Sibling-task coordination with TASK-IMP-132. Both this task and TASK-IMP-132 add a new top-level entry to the same cli.mjs dispatch table and depend only on TASK-IMP-130, not on each other — nothing stops them from being implemented in parallel. Whichever lands second MUST rebase its dispatch-table addition against the first rather than assuming a clean apply; this is a merge-conflict/sequencing risk worth naming explicitly rather than leaving implicit, since neither task's spec alone would surface it.

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 — Verdict summary

Five §1 clauses, five ACs, five edge cases including one security-class row and one newly-added identity-confusion row. The most consequential finding in this task is not a rubric-mechanical one — it's the discovery, made by reading build.sh's actual file-copy lines rather than trusting the plan's prose, that the npm payload does not vendor modules/memory's Python implementation at all. That finding reshaped this task's entire scope before the six ISS findings below were even reached.

§2 — Findings (all resolved)

ISS-001 — AC 1 tested only the absence of a failure message, not the presence of correct routing (TRACE-006)

Clause 1.1 demands memory be recognised as a known command. The original AC 1 asserted only that "unknown command" text was absent. A no-op implementation that silently swallowed the memory verb without dispatching anywhere would also produce no "unknown command" text, and would pass. Resolved: AC 1 now also asserts that, given a working stub, the stub's own output actually appears — proving the command is routed to the resolution/dispatch logic, not merely that one specific failure string is missing.

ISS-002 — AC 2's non-$PATH-lookup requirement had no concretely distinguishing test

Clause 1.2 forbids resolving availability via a bare $PATH lookup of cyberos. The original AC 2 described this requirement but did not specify a test setup capable of telling the two mechanisms apart — "not used to decide availability" is not itself observable. Resolved: AC 2 now sets up two differently-labelled fake binaries (a $PATH cyberos that would print WRONG-PATH-DISPATCH, a python3 -m cyberos stub that would print CORRECT-DISPATCH) so a test can prove which mechanism actually fired.

ISS-003 — Success Metrics' primary lacked a baseline statement

Unlike TASK-IMP-129 and TASK-IMP-130 (both of which state "Baseline today: ..."), the first draft's primary metric stated only the target state. Resolved: added "Baseline today: memory is not in cli.mjs's SCRIPTS table at all... falls through to the 'unknown command' branch."

ISS-004 — the resolution check does not verify the resolved module's identity

python3 -m cyberos --help succeeding is treated as sufficient evidence that cyberos-memory specifically is present. An unrelated Python package also importable as cyberos on the same machine (low probability, but not impossible) would be indistinguishable to this resolution check and could be silently mis-dispatched to. This was not named anywhere in the first draft. Resolved: added as an explicit, accepted-limitation edge case rather than left as a silent gap — proportionate hardening (a signature check against the real CLI's own --help text) is named as a deferred future option, not built into this task.

ISS-005 — no coordination note between this task and its dispatch-table sibling, TASK-IMP-132

Both this task and TASK-IMP-132 add a new entry to the same cli.mjs dispatch table, and both depend only on TASK-IMP-130 — nothing in either task as first drafted stopped them from being implemented in parallel with no awareness of each other, risking a merge conflict or an inconsistent dispatch-table shape. Resolved: added a Dependencies note naming the risk and requiring whichever lands second to rebase against the first.

ISS-006 — clause 1.4's failure exit code was left as "non-zero" instead of matching the file's own established convention

cli.mjs already uses exit code 2 for two other "recognised but unusable" cases (gates missing at cli.mjs:76, unknown command at cli.mjs:87). The first draft's clause 1.4 and AC 4 said only "non-zero," which would technically pass an implementation using an inconsistent code (e.g. exit 1) elsewhere in the same file. Resolved: tightened both the clause and its AC to require exit code 2 specifically.

ISS-007 — FM-101: title exceeded the 72-character limit (caught by the machine floor, not the manual pass)

Running task-lint.mjs against the spec — after the six findings above were already resolved — flagged FM-101: the frontmatter title was 85 code points, over the rubric's 72-character cap. The manual audit pass above did not catch this (title length is exactly the kind of mechanical check a linter exists to catch reliably where manual review is inconsistent). Resolved: shortened to "Add a memory verb to cs, gated on local availability" (title metadata only — no clause, AC, or normative content changed). Recorded here rather than silently folded into the machine-floor summary line, since a real defect the manual pass missed is worth surfacing on its own.

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

ClauseVerb demandCited test assertsVerdict
1.1 MUST recognise as known commandpositive routing occurs, not just one failure string absentAC 1 (revised): both halves - absence of unknown-command text AND presence of stub output when availablesufficient after revision (was insufficient - ISS-001)
1.2 MUST resolve via python3, MUST NOT via bare $PATH cyberosa test that can tell the two mechanisms apartAC 2 (revised): two distinctly-labelled fakes prove which one firedsufficient after revision (was unfalsifiable as originally worded - ISS-002)
1.3 MUST spawn + forward exit codeargs and exit code both observedAC 3: echoed args + exit code 3 both assertedsufficient
1.4 MUST print message + exit code 2 + not invoke pythonspecific code, specific message content, and the environment already precludes invocationAC 4 (revised): code exactly 2, message substrings, no python3 present at allsufficient after revision
1.5 MUST document + state gatingpositive mention plus a caveat marker in both filesAC 5: asserts bothsufficient

§4 — Resolution

Six findings, all material, all resolved in the audited revision. Machine floor clean by manual pass. 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, not superseded by this audit.


End of TASK-IMP-131 audit.