Skip to content

Adopt TypeScript as the substrate language for the SDLC plugin

Status: open/accepted

  • 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 doctor install-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.

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:

  1. 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.

  2. 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.

  1. 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.

  2. 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.

  3. 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.

  4. An install-check probes runtime deps on demand. sdlc project doctor probes for git, gh, Bun (or Node as fallback), and the claude CLI, and emits a clear MISSING-DEP: <name>; install via <hint> error on failure. Consumers see one actionable message, not a cryptic stack trace mid-flow.

  5. 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.

  6. 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.

  7. 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.

ConcernPython (before)TypeScript (chosen)
Typed contracts across substrate boundariesDuck-typed; type hints are advisoryCompile-time enforcement; refactors land safely
Multi-agent driver abstractionHand-rolled protocol classesNative interface + class shape; structural typing for adapter plug-in
IDE support for substrate workGood (pylance/pyright); but ecosystem fragmented (uv vs poetry vs pip; pyproject variants)Excellent + uniform; bun toolchain is consolidated
Refactor cost as substrate growsReal; rename safety relies on testsCompiler catches more
Author preferenceAcceptablePreferred — material productivity multiplier
Consumer install surfacePython 3.10+ (most environments have it)Bun or Node — explicit install step for some consumers
Existing Python investmentSubstantial (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.

The questions this ADR deferred to M-0002 have all been settled:

QuestionResolution
Bun vs Node as primary runtimeBun 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 livesplugin/lib/ (entity-oriented library) with plugin/cli/ as the CLI adapter (D-0001-project-structure, D-0007-deterministic-op-substrate).
Package distributionRepo package @sksizer/sdlc-plugin; one published npm artifact, @sksizer/sdlc (D-0014-cli-primary-npm-distribution).
Two-language windowNone. The cutover PR deleted the Python substrate; M-0003 deleted the last legacy paths (plugin/scripts/, plugin/validators/).
Install-check shapeError message only — sdlc project doctor reports MISSING-DEP: <name>; install via <hint>; it never installs anything itself.
CI implicationsNo hosted CI pipeline; gates run locally via bun test and lefthook pre-commit hooks.
  • 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.

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).

← Back to Decisions