T-ITMN-skills-normalize-sdlc-launcher
Status: closed/done · Impact: medium · Complexity: medium
D-0014 requires that a consumer without Bun can drive every skill. Today
the skill corpus hardcodes bun run …cli/sdlc.ts in six variant shapes
(~132 launcher lines), and the cli/sdlc bash shim itself hardcodes
exec bun run "$here/sdlc.ts". This task makes the launcher the single
invocation seam — it picks the runtime and entry per context — and
rewrites every corpus invocation onto it, so the published artifact’s
Node path needs zero skill edits.
| Location | Role today |
|---|---|
plugin/cli/sdlc | 16-line bash shim: resolves own dir, exec bun run "$here/sdlc.ts" — Bun and .ts hardcoded |
plugin/skills/*/SKILL.md | 115 canonical bun run ${CLAUDE_PLUGIN_ROOT}cli/sdlc.ts lines across 29 skills |
plugin/skills/task-review/SKILL.md | 3 of the 5 stray-slash …}/cli/sdlc.ts variants (others: task-define/SKILL.md, plugin/lib/model/entities/task/file-resolution.md) |
plugin/conventions/commit-messages.md | teaches the hybrid bun run ${CLAUDE_PLUGIN_ROOT}cli/sdlc … shape (3 lines; also in skill-reports.md, principle-review, task-review, task-work SKILL.md) |
plugin/skills/backlog-capture/SKILL.md | one of only 5 lines already in the target shape ${CLAUDE_PLUGIN_ROOT}cli/sdlc <verb> (also task-work ×2, orchestrate ×2) |
plugin/cli/README.md | 4 repo-relative bun run plugin/cli/sdlc.ts examples |
plugin/skills/orchestrate/watch_loop.sh | hardcodes default CLI cli="${SDLC_CLI:-bun run ${CLAUDE_PLUGIN_ROOT}cli/sdlc.ts}" |
plugin/skills/CLAUDE.md | corpus-wide skill-authoring instructions; teaches the bun run …sdlc.ts shape as the example |
.claude/settings.local.json | permission allowlist entries keyed on the bun plugin/cli/sdlc.ts <verb> shape (need re-allowlisting when the shape changes) |
Bare sdlc <verb> prose (op tables, READMEs, ~40+ sites) already matches
the end state and stays.
Proposed
Section titled “Proposed”One invocation shape corpus-wide: ${CLAUDE_PLUGIN_ROOT}cli/sdlc <verb>.
The launcher resolves what runs: if a built sibling sdlc.js exists (the
published artifact — T-XTGT’s layout invariant puts .js in place of
.ts in the same tree), exec node "$here/sdlc.js"; otherwise exec
bun run "$here/sdlc.ts" (the dev repo). Teaching docs teach the
launcher shape. A gate keeps sdlc.ts invocations from re-entering the
corpus.
Approach
Section titled “Approach”- Rewrite
plugin/cli/sdlc: keep the dir-resolution preamble; branch on[ -f "$here/sdlc.js" ]→exec node "$here/sdlc.js" "$@", elseexec bun run "$here/sdlc.ts" "$@". Fail with a clear message naming both runtimes if the required one is absent. - Mechanical corpus rewrite to
${CLAUDE_PLUGIN_ROOT}cli/sdlc <verb>: the 115 canonical lines, 5 stray-slash lines, 1 bare-.tsprose ref, and 7 hybridbun run …cli/sdlclines (drop thebun run); the 4 repo-relative examples inplugin/cli/README.mdbecomeplugin/cli/sdlc <verb>. plugin/skills/orchestrate/watch_loop.sh: default becomescli="${SDLC_CLI:-${CLAUDE_PLUGIN_ROOT}cli/sdlc}"; update its doc-comment.- Update the teaching docs to the launcher shape:
plugin/skills/CLAUDE.md,plugin/conventions/commit-messages.md,plugin/conventions/skill-reports.md,plugin/lib/model/entities/task/spawn-from-post-mortem.md,plugin/lib/model/entities/task/file-resolution.md. - Extend the corpus gate: a check (new
sdlc gateop or a case in the existing skill-prose gate) failing oncli/sdlc.tsorbun run ${CLAUDE_PLUGIN_ROOT}inplugin/**/*.mdorplugin/skills/**/*.sh; wire intosdlc.yamlquality_checks. - Re-run
sdlc plugin install-permissionsand refresh the.claude/settings.local.jsonallowlist entries pinned to the old invocation shape. - Verify:
bun run .claude/scripts/audit_skill_runtime.tspasses; drive the launcher live (${CLAUDE_PLUGIN_ROOT}cli/sdlc --helpand onetask create --dry-run); full quality suite green.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/cli/sdlc | modify | runtime/entry resolution branch (node+sdlc.js when built sibling exists, else bun+sdlc.ts) |
plugin/skills/*/SKILL.md | modify | all bun run …cli/sdlc.ts variants → ${CLAUDE_PLUGIN_ROOT}cli/sdlc <verb> |
plugin/skills/CLAUDE.md | modify | teach the launcher shape in the authoring instructions |
plugin/skills/orchestrate/watch_loop.sh | modify | SDLC_CLI default → launcher path |
plugin/conventions/commit-messages.md | modify | launcher-shape invocation examples |
plugin/conventions/skill-reports.md | modify | launcher-shape invocation example |
plugin/cli/README.md | modify | repo-relative examples → plugin/cli/sdlc <verb> |
plugin/lib/model/entities/task/spawn-from-post-mortem.md | modify | canonical-shape lines → launcher shape |
plugin/lib/model/entities/task/file-resolution.md | modify | stray-slash line → launcher shape |
plugin/lib/services/gate/ops/ | modify | corpus-invocation gate (new op file or extension of the skill-prose gate) |
sdlc.yaml | modify | wire the corpus-invocation gate into quality_checks |
.claude/settings.local.json | modify | refresh allowlist entries pinned to the old invocation shape |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
grep -rE 'cli/sdlc\.ts|bun run \$\{CLAUDE_PLUGIN_ROOT\}' plugin --include='*.md' --include='*.sh'returns zero matches. - AC-2: the launcher execs Bun+
sdlc.tsin the repo (nosdlc.jssibling) and Node+sdlc.jswhen a built sibling is present — both branches covered by a test that stubs the sibling file. - AC-3: the corpus-invocation gate exits non-zero when a
cli/sdlc.tsinvocation is seeded into a SKILL.md, and is listed insdlc.yamlquality_checks. - AC-4:
bun run .claude/scripts/audit_skill_runtime.tspasses against the rewritten corpus. - AC-5:
${CLAUDE_PLUGIN_ROOT}cli/sdlc --helpand one side-effecting verb (… task create --dry-run <slug>) run successfully through the launcher in a live session.
Out of scope
Section titled “Out of scope”- Skill-local
.tsscripts run viabun run ${CLAUDE_PLUGIN_ROOT}skills/…(13 reference sites, e.g.ensure_ready_mutate.ts,dedup_search.ts): they remain Bun-dependent head-side code. D-0014 names them migration debt to be folded into registry ops; that folding is separate work. - Dev-repo tooling that hardcodes Bun (
lefthook.yml,sdlc.yamlquality-check command strings, rootpackage.jsonscripts) — dev-side per D-0014, unchanged. - Producing
sdlc.js(T-XTGT); this task’s Node branch is dormant in the dev repo.
Dependencies
Section titled “Dependencies”- none hard. The Node branch is exercised end to end only once
T-WGV4andT-XTGTland; the branch itself is testable with a stub sibling now.
Discovery context
Section titled “Discovery context”- D-0014 Migration step 2 (launcher-normalization clause).
- Invocation survey 2026-07-02: 115 canonical, 5 stray-slash, 1
bare-
.ts, 7 hybrid, 5 already-target, 4 repo-relative lines, pluswatch_loop.sh’sSDLC_CLIdefault.