Skip to content

T-F9TG-sdlc-dev-runtime-selector

Status: closed/done · Impact: medium · Complexity: medium

One sdlc on PATH that you can point at a live source checkout (to run against a peer or itself) and switch back to the local/published install — a runtime selector, the rustup/nvm model for sdlc itself, and the CLI twin of T-ZEPK-harness-install-dev-link-mode (which selects which plugin the skills load). Implements D-0014 point 12’s deferred SDLC_HOME override and layers an ergonomic toggle on top.

The cli/sdlc launcher (T-ITMN-skills-normalize-sdlc-launcher) resolves runtime/entry purely from its own location — built sdlc.js sibling → Node, else sdlc.ts → Bun. There is no way to run one PATH-installed sdlc against a different checkout, nor to toggle source↔published.

LocationRole today
plugin/cli/sdlcBash launcher: [ -f "$here/sdlc.js" ]node sdlc.js else bun run sdlc.ts; no override hook
plugin/skills/orchestrate/watch_loop.shAlready honors an SDLC_CLI env override — precedent for an env-selectable launcher
plugin/lib/util/runtime.tsmoduleExt/runtimeExec; the runtime primitives

The launcher gains an override layer; a dev noun manages it. Resolution order: SDLC_HOME env → $XDG_CONFIG_HOME/sdlc/home config → local ($here). When the resolved home is a valid checkout other than $here, the launcher execs that checkout’s entry (bun run <home>/plugin/cli/sdlc.ts) directly — never its launcher (no loop). The persistent config redirect applies only when CLAUDE_PLUGIN_ROOT is UNSET (terminal CLI), so a global selector never silently changes which code a peer’s skills run; SDLC_HOME (explicit) always wins.

  1. Launcher (plugin/cli/sdlc) — prepend the override:
    • home="${SDLC_HOME:-}"; if empty AND CLAUDE_PLUGIN_ROOT is unset, home="$(cat "${XDG_CONFIG_HOME:-$HOME/.config}/sdlc/home" 2>/dev/null || true)".
    • If home non-empty, resolves to a real dir with plugin/cli/sdlc.ts, and is not the same checkout as $hereexec bun run "$home/plugin/cli/sdlc.ts" "$@" (with a clear error if bun is absent). Else fall through to today’s local resolution.
  2. New dev service (plugin/lib/services/dev/ops/), runtime-agnostic (node:fs/os/path, process.env), mirroring the harness service:
    • sdlc dev use <path> — validate <path> is an sdlc checkout (has plugin/cli/sdlc.ts); write its absolute path to <config>/sdlc/home; report what sdlc now runs.
    • sdlc dev off — remove <config>/sdlc/home; report that sdlc reverts to the local/published launcher on PATH.
    • sdlc dev status — report the resolved source in order: SDLC_HOME env (if set) / config value / local (<built|source> at <path>); note whether a sdlc shim is on PATH and whether the config redirect is suppressed (CLAUDE_PLUGIN_ROOT set).
    • sdlc dev link [--bin-dir <dir>] — symlink the running launcher into <dir> (default ${XDG_BIN_HOME:-$HOME/.local/bin}) as sdlc; idempotent; warn if <dir> is not on PATH.
  3. <config> = ${XDG_CONFIG_HOME:-$HOME/.config} — resolved identically in bash and TS. Provide a shared configHome() helper in the dev service.
  4. Tests: stub HOME/XDG_CONFIG_HOME to a temp dir. use writes the config + validates the path; off clears it; status reports each resolution branch; link creates the symlink idempotently. A bash-level test of the launcher’s override branch (stub a fake home checkout, assert it execs that entry) is a bonus.
  5. README: the dev-mode workflow (dev link once → dev use <checkout> → run against a peer → dev off to return to the PATH install), and the CLAUDE_PLUGIN_ROOT-suppression note.
LocationKindChange
plugin/cli/sdlcmodifySDLC_HOME/config override branch (suppressed when CLAUDE_PLUGIN_ROOT set)
plugin/lib/services/dev/ops/use.tsnewdev use <path> — write config
plugin/lib/services/dev/ops/off.tsnewdev off — clear config
plugin/lib/services/dev/ops/status.tsnewdev status — report resolution
plugin/lib/services/dev/ops/link.tsnewdev link — install the PATH symlink
plugin/lib/services/dev/ops/_common.tsnewconfigHome(), homeConfigPath(), checkout validation
plugin/lib/services/dev/tests/newop tests (stubbed HOME/XDG)
plugin/cli/tests/goldens/ + sdlc.test.tsmodifyre-snapshot for the new dev noun
README.mdmodifythe dev-mode selector workflow
  • AC-1: sdlc dev use <checkout> writes the abs path to $XDG_CONFIG_HOME/sdlc/home (validated as an sdlc checkout); a subsequent sdlc <verb> (invoked via the launcher with CLAUDE_PLUGIN_ROOT unset) execs that checkout’s sdlc.ts — proven by a launcher test that stubs a fake home and asserts the chosen entry.
  • AC-2: sdlc dev off removes the config; sdlc reverts to the local resolution ($here).
  • AC-3: SDLC_HOME=<path> sdlc … overrides the config (env wins), and applies even when CLAUDE_PLUGIN_ROOT is set; the persistent config is suppressed when CLAUDE_PLUGIN_ROOT is set.
  • AC-4: sdlc dev status reports the active source and its origin (env / config / local) and whether a shim is on PATH.
  • AC-5: sdlc dev link creates <bin-dir>/sdlc → the running launcher, idempotently, and warns when <bin-dir> is not on PATH.
  • AC-6: sdlc --help lists the dev noun; goldens re-snapshotted; bunx tsc --noEmit, bun test plugin/lib/services/dev plugin/cli/tests, and gate runtime-agnostic all clean.
  • A separately-recorded published target (dev use --published auto-detecting a brew/npm install): today off = the launcher on PATH, which is published once a published version is installed. Recording an explicit published target is a nicety for when a release channel (T-75UD) or compiled binary exists.
  • Multi-path deployment (brew/compiled binary) — the follow-up decision.
  • The derived-plugin transform pipeline (D-0014 point 4).

← Back to Tasks