Skip to content

T-NV49-op-path-substrate

Status: closed/done · Impact: high · Complexity: large

Every port in the op-substrate sweep lands on machinery that does not exist yet: 3-segment command paths, group-aware help, hidden leaves, long-running services, and non-JSON output. Land that substrate first so the thirteen port tasks are mechanical. This is D-0007 §Migration step 1 (taxonomy + registry contract first) applied to the adversarially-reviewed surface in D-H7FS-op-substrate-surface / docs/plans/op-substrate-full-sweep.md.

LocationRole today
plugin/lib/registry.ts#OpDescriptor{noun, verb} strings; keyOf = "noun verb"; KEBAB-validated per segment
plugin/lib/registry.ts#opModuleFileswalk opens only <root>/<name>/ops/*.ts; service peers and group subdirs are invisible
plugin/cli/registry_adapter.tsflat noun→verb commander build; JSON-only output via emitJson; zero verb-level hidden handling
plugin/cli/sdlc.ts#wantsAdvancedhonors --advanced only before the first positional
plugin/cli/sdlc.ts#composeNounsnever imports @lib/model/ops — only entities validate registers, via a task update import side effect
.claude/skills/project-check/check_op_modules.tsasserts noun/verb descriptor keys

The registry accepts path: string[] (2–3 kebab segments, hard cap 3) with a {noun, verb} back-compat normalizer; keyOf = path.join(' '); the CLI generator builds a recursive trie with per-leaf and per-group hidden plus --advanced threading; a defineService descriptor (lifecycle start/stop?/list?) registers via a parallel validator, dispatches outside invokeOp’s output-parse pipeline, and is discovered by a second walk over services/<svc>/<name>-service.ts; a bounded ops/<group>/*.ts descent supports depth-3 ops; the output contract of D-H7FS-op-substrate-surface §4 is implemented — one --output text|json|jsonl parameter (--json alias), op-declared defaults (render hook → text, else json; never TTY-sniffed), streaming as a descriptor property, the cli.render(output, io) hook as the text projection with exit shaping; a SERVICE_ERROR exit tier exists; the model/ops barrel is imported at composeNouns so all four entities verbs register; check_op_modules asserts path shape and projected-name uniqueness.

  1. Golden-snapshot sdlc --help, sdlc --help --advanced, and a representative dispatch set before any change.
  2. Descriptor: path[] + normalizer + derived noun/verb/groups accessors; keyOf join; getOp/invokeOp array overloads beside the 2-string overloads.
  3. Recursive trie generator, per-leaf/per-group hidden, --advanced threading in registry_adapter.ts/sdlc.ts.
  4. Output contract per D-H7FS §4: --output text|json|jsonl parameter, op-declared defaults, streaming descriptor property, cli.render hook + exit shaping (jsonl accepted-and-specified; implemented with the first machine-streaming consumer).
  5. defineService + parallel validator + lifecycle dispatcher + SERVICE_ERROR tier.
  6. Walk: ops/<group>/ descent + service-peer traversal, with fixtures proving both before any real consumer exists.
  7. composeNouns barrel import; update check_op_modules.
  8. Assert post-change help snapshots byte-identical for the existing 2-level surface; ship as its own revertible PR.
LocationKindChange
plugin/lib/registry.tsmodifypath[], keyOf, overloads, defineService, SERVICE_ERROR, walk descent + service-peer traversal
plugin/cli/registry_adapter.tsmodifyrecursive trie, hidden rendering, render hook, lifecycle dispatch
plugin/cli/sdlc.tsmodify—advanced threading; composeNouns imports @lib/model/ops
.claude/skills/project-check/check_op_modules.tsmodifypath-shape + projected-name-uniqueness assertions
plugin/lib/tests/registry.test.tsmodifypath[], service, group-walk coverage
plugin/lib/tests/fixtures/registry/modifydepth-3 and service-peer fixtures
plugin/cli/tests/modifyhelp snapshots, trie, hidden, render-hook tests
  • AC-1: sdlc --help and sdlc --help --advanced byte-match the pre-change snapshots — the existing 2-level surface is unaffected.
  • AC-2: a fixture op at services/<svc>/ops/<group>/leaf.ts declaring path: ['svc','group','leaf'] is discovered, renders under sdlc svc group --help, and dispatches.
  • AC-3: a fixture services/<svc>/fixture-service.ts registers via defineService; its lifecycle leaves render and dispatch without routing through invokeOp output parsing.
  • AC-4: a hidden leaf under a visible noun is absent from sdlc <noun> --help, present with --advanced, and still dispatches when addressed directly.
  • AC-5: an op with a cli.render hook defaults to its text contract on stdout and shapes its exit code; --output json (and the --json alias) emits validated JSON; an op without a hook defaults to JSON.
  • AC-6: sdlc entities audit|validate|migrate|check-identifiers all resolve from a clean process; check_op_modules passes, including projected-name uniqueness.
  • AC-7: bunx tsc --noEmit + bun test green; the PR reverts cleanly in isolation.
  • Porting any real capability (the port tasks consume this substrate).
  • MCP/HTTP adapter implementations — only the projection contract in D-H7FS-op-substrate-surface binds them.
  • Making model/ops a discovery-walk root (T-U72C owns that question; the barrel import here is independent).
  • none — this is the first execution task; every port depends on it.
  • docs/plans/op-substrate-full-sweep.md (Rev 2) §1b machinery contract; adversarial refutation findings: JSON-only adapter vs line-oriented consumers, invisible service peers, entities ops unregistered. Executes D-0007-deterministic-op-substrate §Migration step 1.

← Back to Tasks