Skip to content

ADR-0002: Zero Runtime Dependencies

  • Status: Accepted
  • Date: 2026-04-14
  • Supersedes: None
  • Amendment: ADR-0006 permits yaml@^2.8 as a narrowly-scoped runtime dependency (2026-04-15).

Context and Problem Statement

package.json's dependencies block is the only way nubos-pilot can ship transitive complexity to end users through npx. Every runtime dependency is a three-headed cost: a supply-chain surface, a version-compatibility constraint, and an install-failure mode (Windows path quirks, corporate proxies, air-gapped networks, peer-dep conflicts, abandoned maintainers). The question: should nubos-pilot ever declare runtime dependencies?

Scope

This ADR is about package.json.dependencies specifically — the subset of package.json that ships to end users via npm install / npx:

  • "Zero runtime deps" means: package.json.dependencies === {} (empty object, not absent) — no library is pulled down at install time on an end-user machine.
  • devDependencies are explicitly permitted. Test runners, optional hook bundlers, and similar authoring-time tooling live there. They are never shipped to end users.
  • Environment assumptions are not dependencies. git, node >=22, and the host agent CLI are assumed to exist on the user's machine — they are prerequisites, not things nubos-pilot ships.

Decision Drivers

  • Sufficiency of Node builtins — the full markdown-workflow surface (frontmatter parsing, readline prompts, file locking, ANSI output, child-process spawn) is reachable through fs, path, os, child_process, readline, crypto, and util alone.
  • npx install reliability — zero deps ≈ zero failure modes on Windows, corporate networks, and air-gapped environments.
  • Patchability (Core Value) — users copy .cjs files verbatim into .claude/nubos-pilot/ and sometimes patch them locally; there is no node_modules/ tree to keep in sync.
  • Security — zero runtime deps ≈ zero supply-chain surface.

Considered Options

  • Zero runtime dependencies — Node builtins + hand-rolled helpers. (CHOSEN)
  • Rich dependency tree — adopt a broad runtime surface (coding-agent SDK, playwright, sharp, chokidar, @modelcontextprotocol/sdk, chalk/picocolors).
  • Native Rust N-API engine — publish per-platform prebuilt binaries as optionalDependencies.
  • Accept a single narrow dependency pragmatically — e.g. yaml for frontmatter parsing.

Decision Outcome

Chosen: "Zero runtime dependencies", because it is the only option that reinforces the Core-Value patchability story and minimizes install-failure modes on the weakest user environments simultaneously. The devDependencies escape hatch covers authoring-time needs without leaking into end-user installs.

Escape hatch for future exceptions: if a concrete future feature genuinely requires a runtime dep that builtins cannot satisfy, the exception is introduced by a new ADR that either supersedes ADR-0002 wholesale or amends it narrowly with a name-scoped exemption. The escape is deliberately bureaucratic so that "just add a dep" never becomes the reflex answer. (See ADR-0006 for the first exercise of this hatch.)

Consequences

  • Goodnpm install is effectively a no-op for end users.
  • Good — supply-chain audits are trivial.
  • Good — users can copy-patch .cjs files without module-resolution confusion.
  • Good — the install-payload tree contains only .cjs files and Markdown.
  • Bad — we reimplement small utilities (YAML frontmatter via hand-rolled parser, readline prompts, raw ANSI escapes). Accepted cost.
  • NeutraldevDependencies are permitted and do not ship to users.
  • NeutraloptionalDependencies for native prebuilt binaries is also rejected by this ADR — no accidental backdoor.

Pros and Cons of the Options

Zero runtime dependencies — chosen

  • Good — Node builtins cover the entire markdown-workflow surface.
  • Good — preserves the Core Value "markdown-only, multi-runtime, ohne eigenes Daemon".
  • Bad — every small utility must be hand-rolled. Accepted.

Rich dependency tree — rejected

  • Good — chalk/picocolors produce nicer output; @clack/prompts produces nicer Q&A flows.
  • Bad — playwright, sharp, sql.js, chokidar, image addons target features we do not implement.
  • Bad — @modelcontextprotocol/sdk as a runtime dep contradicts REQUIREMENTS.md §"Out of Scope".
  • Bad — @anthropic-ai/claude-agent-sdk implies we spawn agents — that's the daemon pattern ADR-0001 forbids.
  • Bad — every transitive node_module is an install-failure risk on the environments that most need nubos-pilot to "just work".

Native Rust N-API engine — rejected

  • Good — native binaries offer raw-speed grep/ast-grep/syntax-highlighting.
  • Bad — requires per-platform prebuilt binaries with the associated CI/release plumbing.
  • Bad — we have no TUI, no image pipeline, no watcher.
  • Bad — Claude Code already exposes Grep, Read, Bash as first-class tools.
  • Bad — introducing a binary-ship story violates patchability.

Accept a single narrow dependency pragmatically — rejected (for now)

  • Good — one dep would make frontmatter parsing robust against multiline sequences and anchors.
  • Bad — "just one dep" is a slippery slope.
  • Bad — the escape-hatch route exists for exactly this situation.

(Update: see ADR-0006yaml@^2.8 was eventually accepted under the escape-hatch route.)

More Information

  • Related ADR: ADR-0005 — the install-payload tree contains only .cjs + Markdown.
  • CLAUDE.md: §"Technology Stack" → "Installation"; §"External runtime dependencies"; §"Alternatives Considered".
  • REQUIREMENTS.md: §"Out of Scope" → "Nubos-MCP als First-Class-Dependency".

This ADR does not describe CI enforcement. CI-gate enforcement of the zero-deps rule is deferred to a later deploy/CI phase per ROADMAP.md; current enforcement consists of human PR review and this ADR as the authoritative reference.