From 3a8ce634b656c550bf0c4df671461662e3818d5d Mon Sep 17 00:00:00 2001 From: goosewobbler <432005+goosewobbler@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:13:53 +0100 Subject: [PATCH] docs(adr): record ecosystem-enablement and required-LLM-model decisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0004 captures the "detection enables, config opts out" ecosystem-enablement model (#554); ADR-0005 captures shipping no LLM model defaults and requiring an explicit llm.model (#541). Both are decisions someone will otherwise re-litigate — "why does cargo publish by default?" and "let's add model defaults for convenience" — so the durable why belongs in an ADR. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_011i1KvoyxATU3bz5V5Sf1Xu --- ...ement-detection-enables-config-opts-out.md | 31 +++++++++++++++++++ ...quire-an-explicit-llm-model-no-defaults.md | 30 ++++++++++++++++++ docs/adr/README.md | 2 ++ 3 files changed, 63 insertions(+) create mode 100644 docs/adr/0004-ecosystem-enablement-detection-enables-config-opts-out.md create mode 100644 docs/adr/0005-require-an-explicit-llm-model-no-defaults.md diff --git a/docs/adr/0004-ecosystem-enablement-detection-enables-config-opts-out.md b/docs/adr/0004-ecosystem-enablement-detection-enables-config-opts-out.md new file mode 100644 index 00000000..bcb03ca6 --- /dev/null +++ b/docs/adr/0004-ecosystem-enablement-detection-enables-config-opts-out.md @@ -0,0 +1,31 @@ +# 4. Ecosystem enablement: detection enables, config opts out + +Date: 2026-07-19 + +## Status + +Accepted. Implementation tracked in [#554](https://github.com/goosewobbler/releasekit/issues/554). + +## Context + +ReleaseKit's pipeline is registry-agnostic and spans multiple ecosystems (npm, crates.io, pub.dev today; more planned). Enablement had grown asymmetric: npm published by default while crates.io and pub.dev were opt-in (`publish.*.enabled: false`), and npm alone had no version-handling toggle at all — the other two did. + +That asymmetry is a footgun that worsens with every ecosystem added. A contributor wiring a new adapter has to decide, per ecosystem, whether it versions and whether it publishes by default, and a user reading the config finds the same key means different things in different ecosystems. A detected-but-off ecosystem also fails silently — the packages are simply never published, with no error. + +The question this settles: when ReleaseKit detects an ecosystem, what does it do with it by default? + +## Decision + +**No ecosystem is special — detection enables, config opts out.** Every ecosystem ReleaseKit detects is versioned, and (where publishing is configured and authenticated) published, by default. Explicit config is only ever an *opt-out*. + +- `publish..enabled` defaults `true` for every ecosystem (was `false` for crates.io/pub.dev). +- `version..enabled` exists for every ecosystem (npm gained `version.npm.enabled`) and defaults `true`. +- The two opt-outs are kept deliberately separate because they express different real needs: `version..enabled: false` means "don't touch this ecosystem's manifests at all" (e.g. a vendored crate, or Rust versioning handled by release-plz); `publish..enabled: false` means "version and tag this ecosystem, but publish it elsewhere". + +Default-on is safe because the pipeline already fails safe *structurally*, independent of the toggle: an enabled registry that discovers zero targets no-ops, and publishing without credentials fails or skips at the auth check. The toggle is an opt-out, not a safety mechanism. + +## Consequences + +- One symmetric mental model that scales: a newly-added ecosystem adapter is on-by-default with no per-ecosystem special-casing, and "detected" and "released" no longer diverge silently. +- Flipping the crates.io/pub.dev publish defaults from `false` to `true` is a behaviour change — a pre-existing config relying on the old opt-in would start publishing. Acceptable here (pre-1.0, single maintainer, configs updated alongside the change); a `!`/`BREAKING CHANGE` release note carries it. +- Two independent per-ecosystem opt-outs are more config surface than one, but collapsing them would conflate "don't manage this ecosystem" with "manage but don't publish" — two outcomes users genuinely need to select between. diff --git a/docs/adr/0005-require-an-explicit-llm-model-no-defaults.md b/docs/adr/0005-require-an-explicit-llm-model-no-defaults.md new file mode 100644 index 00000000..f25f07b1 --- /dev/null +++ b/docs/adr/0005-require-an-explicit-llm-model-no-defaults.md @@ -0,0 +1,30 @@ +# 5. Require an explicit LLM model; ship no defaults + +Date: 2026-07-19 + +## Status + +Accepted. Implementation tracked in [#541](https://github.com/goosewobbler/releasekit/issues/541). + +## Context + +LLM-enhanced release notes need a model id per provider. The obvious convenience is to ship a curated default per provider so a user can set `provider` and omit `model`. + +Model ids rot. Verifying the defaults while implementing this showed the openai default we were about to ship — `gpt-4o-mini` — was already deprecated and removed from OpenAI's API. A shipped default is therefore two liabilities at once: a per-release maintenance treadmill for ReleaseKit (every default must be re-checked and bumped as providers churn), and a silent failure mode for consumers (anyone relying on the default breaks the day the provider removes that model). + +Crucially, there is no zero-config path to protect: LLM enhancement is already an explicit opt-in that requires an API key (or a running Ollama with a model already pulled). Naming the model is a marginal addition to a setup the user has already committed to. + +## Decision + +**ReleaseKit ships no model defaults. `llm.model` is required and validated at config load.** + +- `llm.model` is a required, non-empty string (Zod `z.string().min(1)`) — a missing or empty model fails at config validation, not silently inside the notes soft-fail catch. +- `provider` is a closed Zod enum (`openai | openai-compatible | anthropic | ollama`); `openai-compatible` additionally requires `baseURL`. +- Configs assembled in-process (the CLI's `--llm-*` flags, which bypass `loadConfig`'s Zod pass) are run through the same schema before use, so a bad provider or missing model fails loud (non-zero exit) rather than degrading to non-LLM notes. + +## Consequences + +- Zero model-id maintenance for ReleaseKit: no default to track, re-verify, or get wrong. The consumer owns the fast-moving model choice for their account and budget — the party best placed to keep it current. +- Fail-loud at config validation is consistent with the `provider`/`baseURL` validation this decision also introduces; misconfiguration surfaces at load, not three stages deep. +- The "zero-config local-first Ollama" story loses its literal zero-config-ness — a user must name their local model. Acceptable: an Ollama user already has to run the server and pull a model, so they know its name. +- Docs use a `` placeholder in examples rather than a real id, so the examples themselves can't rot into recommending a dead model — the same rot this ADR refuses to ship in code. diff --git a/docs/adr/README.md b/docs/adr/README.md index c83386da..064a1e39 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -9,3 +9,5 @@ Roadmap sequencing lives in [ROADMAP.md](../../ROADMAP.md); implementation detai | [0001](./0001-language-remains-typescript.md) | ReleaseKit remains a TypeScript codebase | Accepted | | [0002](./0002-no-mcp-server-cli-is-the-agent-surface.md) | The agent-facing surface is the CLI, not an MCP server | Accepted | | [0003](./0003-optional-change-file-input.md) | Change files are supported as an optional input, in changesets format | Accepted | +| [0004](./0004-ecosystem-enablement-detection-enables-config-opts-out.md) | Ecosystem enablement: detection enables, config opts out | Accepted | +| [0005](./0005-require-an-explicit-llm-model-no-defaults.md) | Require an explicit LLM model; ship no defaults | Accepted |