diff --git a/CONTEXT.md b/CONTEXT.md index 13357f78c..0a15b14ff 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -118,6 +118,9 @@ Module owning which skills and agents are written to runtime config directories ### Runtime Artifact Layout Module Module owning the per-runtime mapping from artifact kind to filesystem placement. ADR-3660 defines the typed `kinds` per runtime (`commands`, `agents`, `skills`) with destination subpath, prefix, and stage adapter (with per-runtime converters in `bin/install.js`: `convertClaudeCommandToClaudeSkill`, `…CodexSkill`, `…CopilotSkill`, `…AntigravitySkill`). Phase 1 applies this seam to the Runtime Surface Module (`surface.cjs:applySurface`). Phase 2 is planned to migrate install/uninstall in `bin/install.js` so all lifecycle sites iterate one shared layout table instead of re-encoding runtime layout logic. This design is intended to remove the #3659 class of omissions. Migrations remain under the Installer Migration Module (ADR-0008). See ADR-3660. +### Runtime Install Policy Module +Projects a pure, typed install plan for a given runtime by composing artifact placements (Runtime Artifact Layout Module), command text (Shell Command Projection Module), and per-runtime config intentions — with no filesystem IO or format-specific serialization. Runtime-specific adapters consume the plan and execute concrete file mutations and config rendering. See ADR-58. + ### Knowledge Graph Module Module owning the graphify integration: config gate (`isGraphifyEnabled`), disabled response (`disabledResponse`), subprocess helper (`execGraphify`, typed `GRAPHIFY_REASON` enum), presence detection (`checkGraphifyInstalled`), version checking (`checkGraphifyVersion`), query surface (`graphifyQuery` — BFS seed-expand + budget trim), status surface (`graphifyStatus` — node/edge counts, mtime staleness, commit-staleness tri-state via `built_at_commit`/`commits_behind`/`commit_stale`), diff surface (`graphifyDiff` — added/removed/changed nodes+edges), build pre-flight (`graphifyBuild`), snapshot management (`writeSnapshot`). Reads `.planning/config.json:graphify.enabled` as config gate; writes to `.planning/graphs/`. Auto-update hook (`hooks/gsd-graphify-update.sh`) triggers a detached background rebuild after HEAD-advancing git operations on the default branch when `graphify.auto_update=true`. Status file `.planning/graphs/.last-build-status.json` carries `{ ts, status, exit_code, duration_ms, head_at_build, graphify_version }`. Graph IR uses `nodes[]`, `edges[]` (or `links[]` for graphify ≥0.7 compat), `hyperedges[]`, `built_at_commit`. `commit_stale` is tri-state: `false` (known fresh), `true` (stale), `null` (unknown — no git or pre-v0.7 graph). Source: `gsd-core/bin/lib/graphify.cjs`. Skill: `commands/gsd/graphify.md`. diff --git a/docs/adr/58-runtime-install-policy-module.md b/docs/adr/58-runtime-install-policy-module.md new file mode 100644 index 000000000..5cbe35794 --- /dev/null +++ b/docs/adr/58-runtime-install-policy-module.md @@ -0,0 +1,56 @@ +# Runtime Install Policy Module owns the typed install-plan projection + +- **Status:** Accepted +- **Date:** 2026-06-07 +- **Issue:** #58 + +## 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. + +## 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. diff --git a/docs/adr/README.md b/docs/adr/README.md index f59e54ce6..612e4891f 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -54,6 +54,7 @@ See **[CONTRIBUTING.md — "Proposing an ADR or PRD"](../../CONTRIBUTING.md#prop | [456-test-rigor-architecture.md](456-test-rigor-architecture.md) | Test-rigor architecture — deterministic scheduling, antagonistic tier, typed-surface mandate, delete-bad-tests policy | Accepted | | [457-generated-cjs-single-source.md](457-generated-cjs-single-source.md) | Collapse hand-written CJS to generated single-source | Proposed | | [660-release-from-next-head.md](660-release-from-next-head.md) | Release from the head of next; immutable release tags; @next dist-tag as the RC surface | Proposed | +| [58-runtime-install-policy-module.md](58-runtime-install-policy-module.md) | Runtime Install Policy Module owns the typed install-plan projection | Accepted | ## Seam map