Files
msd-core/docs/adr/58-runtime-install-policy-module.md
Jakub Zych a9a7a328e6 refactor: hard-fork GSD -> MSD (Make Software Done)
Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
2026-10-06 01:47:40 +02:00

6.8 KiB
Raw Blame History

Runtime Install Policy Module owns the typed install-plan projection

  • Status: Accepted
  • Date: 2026-06-07
  • Issue: #58
  • Subsumed by: ADR-1239 (MSD as an Embeddable Orchestration Engine) — read it first; see the amendment below
  • Subsumed by: ADR-857 (Capability system) — generalizes this module's install-plan projection; this seam remains live at src/runtime-artifact-install-plan.cts:82

Amendment (2026-07-16): subsumed by ADR-1239 (EoS)

ADR-1239 — MSD as an Embeddable Orchestration Engine (EoS), Accepted — subsumes this ADR as an adapter: the typed InstallPlan projection this module owns becomes one of the surfaces the host negotiates for, rather than the outermost seam at which MSD meets a host.

This ADR is not superseded and its status is unchanged. The InstallPlan seam is live and load-bearing. It is now a component of the EoS frame, not the top-level answer to "how does MSD meet a host?".

Read ADR-1239 first.

Recorded because ADR-1239 declared this subsumption while this file recorded nothing.

Amendment (2026-07-17): also subsumed by ADR-857 (Capability system)

ADR-857 was ratified Proposed → Accepted on 2026-07-17 and generalizes this module's install-plan projection into the unified Capability model (install composes active Features × the chosen Runtime at this ADR's InstallPlan seam).

This ADR remains Accepted and live. ADR-857's header originally read "Supersedes (generalizes)"; on ratification that was corrected to Subsumes, precisely because this seam is not dead — InstallPlan is live at src/runtime-artifact-install-plan.cts:82. This module is now a component of two broader frames: ADR-857 (what composes an install) and ADR-1239/EoS (how a host loads the engine at all).

Context

Runtime install logic is currently spread across one-off helper functions. getGlobalDir(runtime, explicitDir) in bin/install.js switch-dispatches to per-runtime helpers (getOpencodeGlobalDir, getKiloGlobalDir), and getAgentsDir lives separately in src/core.cts. These helpers resolve directories at ~11 call sites and are free to drift from the behavior that install and runtime-query paths actually expect, because nothing owns the composition of an install decision as a single value.

Two adjacent seams already exist:

  • ADR-3660 (Runtime Artifact Layout Module) owns where per-runtime artifacts (commands, agents, skills) are placed.
  • ADR-0009 (Shell Command Projection Module) owns runtime-aware command text rendering (quoting, path style, wrapper prefixes).

But no ADR owns composing those — placements + command text + per-runtime config intentions — into one unified, typed install-plan projection. That missing seam is why directory/config logic re-derives itself ad hoc at each call site.

Decision

Introduce a Runtime Install Policy Module as the seam that, given a runtime and an install context, projects a pure, typed InstallPlan value describing everything that should happen for that runtime. The projection:

  • composes artifact placements by delegating to the Runtime Artifact Layout Module (ADR-3660),
  • composes command text by delegating to the Shell Command Projection Module (ADR-0009),
  • declares config intentions (which config files need which keys/values for that runtime),
  • performs no filesystem IO and no format-specific serialization while resolving the plan.

Concrete execution is owned by runtime-specific adapters (made explicit as a registry in #60). Adapters consume the InstallPlan and perform the effectful work: file mutations, directory creation, and rendering format-specific config (TOML, JSON, Markdown) for their runtime.

This follows the repository's established pure-policy-projects / thin-adapters-execute pattern (ADR-0001, Dispatch Policy Module): the InstallPlan is the narrow waist, resolution stays free of IO, and callers become thin adapters over a stable interface rather than re-deriving directory logic.

What stays OUTSIDE the policy module

To keep the abstraction honest about the filesystem boundary, the following are explicitly not the policy module's responsibility and remain in the runtime adapters:

  • Concrete TOML / JSON / Markdown read-modify-write and serialization.
  • Merge semantics for pre-existing config files (preserving user keys, ordering, formatting).
  • Filesystem effects: directory creation, atomic write/rename, existence/permission checks.
  • Any path resolution that requires touching the disk.

The policy module resolves intent as data; adapters turn that intent into bytes on disk.

Consequences

  • Install logic becomes testable as pure data: assert the projected InstallPlan for a runtime without a filesystem.
  • The scattered directory helpers (getGlobalDir, getOpencodeGlobalDir, getKiloGlobalDir, getAgentsDir) gain a single projection to migrate onto, retiring or narrowing them (tracked in #56).
  • The plan/adapter contract becomes a stability surface that must be held narrow; drift there reintroduces the very divergence this seam removes.
  • Rollout is incremental, not big-bang: this ADR establishes the boundary (#58); the explicit Runtime Adapter Registry lands next (#60); legacy helper retirement follows (#56); downstream cleanup in #57.

Implementation (2026-06-11)

The InstallPlan is realized as the exported resolveInstallPlan(runtime) in runtime-config-adapter-registry (co-located with adapter-selection, not a standalone module). It collects the install-level descriptor axes — installSurface, writesSharedSettings, finishPermissionWriter, hookEvents, extendedHookEvents, and hooksSurface — into one typed InstallPlan value. install() and finishInstall() in bin/install.js consume it directly. The spatial axes (configHome, artifactLayout, commandStyle) remain behind their self-resolving adapter modules (runtime-homes, runtime-artifact-layout, runtime-slash) as the execution adapters — consistent with this ADR's adapters-execute boundary.

References

  • ADR-0001 — Dispatch Policy Module (pure-policy-projects / thin-adapters-execute precedent).
  • ADR-3660 — Runtime Artifact Layout Module (per-runtime artifact placement; delegated to by this projection).
  • ADR-0009 — Shell Command Projection Module (runtime-aware command text; delegated to by this projection).
  • ADR-0008 — Installer Migration Module (adjacent installer seam).
  • CONTEXT.md § Glossary — Domain modules and seams (the architecture seam map / glossary this module is registered in).
  • Installer-refactor chain: #58 (this ADR) → #60 (explicit adapter registry) → #56 (retire legacy directory helpers) → #57.