Adopt TypeScript as the substrate language for the SDLC plugin
Status: open/accepted
Summary
Section titled “Summary”- The SDLC plugin’s substrate (CLI, validators, dispatch scripts, orchestration tooling) is TypeScript, converted from Python via M-0002.
- Markdown skills (SKILL.md prose Claude reads) stay markdown — this ADR is about the executable substrate, not the authoring surface.
- Author preference + multi-driver ergonomics (typed agent abstractions across Claude Code, Codex, future agents) drive the language choice.
- Bun is the runtime; the
sdlc project doctorinstall-check probes runtime deps and tells consumers what is missing. - This ADR fixes the direction; the conversion mechanics were scoped to M-0002 (closed/done).
- Language choice is decoupled from dispatch/workflow library. DarkFactory was not adopted; the op substrate was built in-house per D-0007-deterministic-op-substrate.
- Scope bounded by D-VSLI-distributed-work-runner-architecture: the engine is Rust; this ADR governs the op/entity substrate and the flowline authoring surface, which the engine invokes as child-process steps over the step protocol. Shared contract types are excluded — those are defined in Rust and generated to TypeScript.
Context
Section titled “Context”At decision time (2026-05-30) the SDLC plugin’s substrate was Python: plugin/scripts/,
plugin/cli/, plugin/validators/, plugin/lib/ were all Python. Skills under plugin/skills/*/
were markdown prose the LLM reads, plus per-skill Python helpers (e.g. parse_touchpoints.py,
ensure_ready_mutate.py). The plugin ran under any Python 3.10+ environment via uv run --script
shebangs.
Two pressures motivated the language re-evaluation:
-
Author preference. TypeScript is the preferred language for substrate work going forward. Quality of typed APIs, refactoring tooling, IDE support, ecosystem maturity around CLI + concurrency primitives all favor it.
-
Multi-driver ergonomics. P-0008-harness-agnostic-substrate commits the substrate to harness-agnosticism — i.e., the dispatch layer should support multiple agent runners (Claude Code today; Codex, Cursor, future agents tomorrow). A typed driver abstraction (interface + implementations) is materially easier to maintain in TypeScript than in Python’s duck-typed style. Multi-driver pluralism is the load-bearing test for substrate agnosticism; TypeScript makes it cheaper.
Recent execution-friction evidence (the 2026-05-30 dispatch wave’s verdict-contract escapes, the lease-protocol owner-ID mismatch, the validator-path bug) showed that the substrate’s safety properties — schema validation, sentinel parsing, sub-agent invocation contracts — need strong typing and good refactoring tools to evolve without regressions. Python serves; TypeScript serves better for the substrate’s growth direction.
This ADR fixes the language choice. The conversion sequence, scope, and migration mechanics were scoped to M-0002, since delivered.
Decision
Section titled “Decision”-
TypeScript is the substrate language for the SDLC plugin. New substrate code is authored in TypeScript. The Python substrate was ported and deleted via M-0002.
-
Markdown skills stay markdown. SKILL.md files Claude reads at dispatch time remain prose; the per-skill helper scripts those skills shell out to are TypeScript.
-
Bun is the recommended runtime. Bun’s fast startup, built-in TS transpilation, and bundled test runner reduce the install surface. Node compatibility means consumers without Bun can use Node + a transpilation step if needed; the install-check surfaces the recommended path.
-
An install-check probes runtime deps on demand.
sdlc project doctorprobes forgit,gh, Bun (or Node as fallback), and theclaudeCLI, and emits a clearMISSING-DEP: <name>; install via <hint>error on failure. Consumers see one actionable message, not a cryptic stack trace mid-flow. -
The conversion landed as one cutover. M-0002 ported the substrate on a single long-lived branch under a local parity gate and deleted the Python substrate in the cutover commit. No Python substrate remains.
-
Language choice is independent of dispatch/workflow library. DarkFactory (
sksizer/DarkFactory/ts) was not adopted; the deterministic op substrate was written from scratch per D-0007-deterministic-op-substrate. -
Multi-driver expansion is a separate later milestone. This ADR establishes the language choice; the driver-abstraction work (Codex, Cursor, etc.) is downstream and scoped separately. The point of mentioning it here is that TypeScript supports that future work better than Python does.
| Concern | Python (before) | TypeScript (chosen) |
|---|---|---|
| Typed contracts across substrate boundaries | Duck-typed; type hints are advisory | Compile-time enforcement; refactors land safely |
| Multi-agent driver abstraction | Hand-rolled protocol classes | Native interface + class shape; structural typing for adapter plug-in |
| IDE support for substrate work | Good (pylance/pyright); but ecosystem fragmented (uv vs poetry vs pip; pyproject variants) | Excellent + uniform; bun toolchain is consolidated |
| Refactor cost as substrate grows | Real; rename safety relies on tests | Compiler catches more |
| Author preference | Acceptable | Preferred — material productivity multiplier |
| Consumer install surface | Python 3.10+ (most environments have it) | Bun or Node — explicit install step for some consumers |
| Existing Python investment | Substantial (CLI, validators, scripts) | Discarded at the M-0002 cutover |
The trade-off table makes the consumer-install downside concrete: Python “just works” in most consumer environments; Bun/Node requires an explicit install. Decision item 4 (install-check) directly addresses this — the plugin tells consumers what they need, doesn’t fail silently.
How the open questions resolved
Section titled “How the open questions resolved”The questions this ADR deferred to M-0002 have all been settled:
| Question | Resolution |
|---|---|
| Bun vs Node as primary runtime | Bun is primary; Node is the documented fallback. The published npm artifact ships built JS so plain Node can run it (D-0014-cli-primary-npm-distribution). |
| Where the TS substrate lives | plugin/lib/ (entity-oriented library) with plugin/cli/ as the CLI adapter (D-0001-project-structure, D-0007-deterministic-op-substrate). |
| Package distribution | Repo package @sksizer/sdlc-plugin; one published npm artifact, @sksizer/sdlc (D-0014-cli-primary-npm-distribution). |
| Two-language window | None. The cutover PR deleted the Python substrate; M-0003 deleted the last legacy paths (plugin/scripts/, plugin/validators/). |
| Install-check shape | Error message only — sdlc project doctor reports MISSING-DEP: <name>; install via <hint>; it never installs anything itself. |
| CI implications | No hosted CI pipeline; gates run locally via bun test and lefthook pre-commit hooks. |
Out of scope
Section titled “Out of scope”- DarkFactory adoption. This ADR commits to TS, not to a specific library; the dispatch substrate was built in-house (D-0007-deterministic-op-substrate).
- Workflow DSL conversion. Replacing SKILL.md prose with typed workflow objects is a downstream milestone. This ADR keeps skills as markdown.
- Multi-driver implementation. Adding agent drivers beyond Claude Code is downstream. This ADR notes that TS makes the abstraction cheaper; the actual drivers are future work.
Rollout
Section titled “Rollout”The conversion shipped via M-0002: a single long-lived branch, ported under a local parity
gate, with the Python substrate deleted in the cutover PR. M-0003 then re-cut the ported
scripts into the entity-oriented op substrate of D-0007-deterministic-op-substrate behind the
unified sdlc CLI, deleting plugin/scripts/ and plugin/validators/. The toolchain lives at
the repo root (package.json, tsconfig.json, bun.lock); tests run under bun test.
- Surfaced 2026-05-30 in the same session that produced
D-0005-repair-on-prepare-and-aggressive-entity-upgrade and T-0015 (sort
--ready). The execution-friction evidence motivated re-evaluating substrate choices; the language decision (this ADR) is the cleanest first move. - Quote from the design session: “Typescript would be the target. if we adopt the ts version we’d have to restructure the project, and we’d have some sort of install test/check for the plugin (which is a downside to TS, but its the language i would prefer). We would add multiple agent drivers besides claude code so it’d be easier to be agnostic actually.”
- Related to P-0008-harness-agnostic-substrate: the multi-driver intent is the substantive P0008 commitment. This ADR’s language choice serves that downstream commitment.
- Related to P-0004-leverage-best-tool: the substrate language is “the best tool for substrate work” rather than the cheapest one to start with.
- DarkFactory adoption was deliberately decoupled so the language commitment wasn’t blocked on the library evaluation; no adoption ADR emerged — the dispatch substrate was built in-house (D-0007-deterministic-op-substrate).