From ff3aa61eca3b039bbeaf15f4f702c71c96cbc07e Mon Sep 17 00:00:00 2001 From: mhoennig Date: Tue, 1 Sep 2026 14:15:15 +0200 Subject: [PATCH] ADR 0009: one Werkator instance serves a set of repositories Records the tenet revision and its shape: instance registry and instance keys in ~/.werkator.yml (one instance per OS user), optional repo defaults in an explicit defaults: block merged below every repo's own layers, repo config unchanged in each repository, registry-wins precedence, basename repo names. Rejected: a federation dashboard over single-repo instances (keeps N services), and the status quo. Konzept/AGENTS architecture wording changes only when the implementation lands; the decision list carries 0009 now. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 1 + .../0009-2026-09-01.multi-repo-instance.md | 60 +++++++++++++++++++ docs/plan/22-multi-repo.md | 7 +-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 docs/adrs/0009-2026-09-01.multi-repo-instance.md diff --git a/AGENTS.md b/AGENTS.md index ffbbbe1..e341e4e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,6 +81,7 @@ All major decisions are in `docs/adrs/`. Run `adr-status` (after `source .envrc` - **Runtime bundle distribution**: `./gradlew runtimeBundle` builds a jlink-trimmed JRE + jar tarball for hosts without a Java runtime; GraalVM native image and a containerized runtime were rejected (ADR 0006) - **Build definitions**: a top-level `builds` section of named builds with `trigger` blocks replaces the branch-owned `autoBuild` schedules (ADR 0007) - **bwrap build runtime**: on hosts without root and without Docker (Hostsharing Managed Webspaces), builds run in a `bwrap` user-namespace sandbox over a prepared rootfs — filesystem isolation only, network and uid shared with the host; proot/fakechroot and unisolated native builds were rejected (ADR 0008) +- **Multi-repo instance**: one instance serves a registry of self-contained repositories (instance config in `~/.werkator.yml`, repo config in each repo); revises the one-instance-per-repository tenet, implementation planned as `docs/plan/22-multi-repo.md` (ADR 0009) ## Skills diff --git a/docs/adrs/0009-2026-09-01.multi-repo-instance.md b/docs/adrs/0009-2026-09-01.multi-repo-instance.md new file mode 100644 index 0000000..95592e6 --- /dev/null +++ b/docs/adrs/0009-2026-09-01.multi-repo-instance.md @@ -0,0 +1,60 @@ +# One Werkator Instance Serves a Set of Repositories + +**Status:** +- proposed: 2026-09-01 +- accepted: 2026-09-01 +- rejected: - +- superseded: - + +**Decision [accepted]:** The founding tenet "one instance per repository" is revised to "one instance per repository *set*": a Werkator instance aggregates self-contained repositories listed in an instance registry, sharing one service, one port, one UI, one watcher schedule, and one global executor cap. +Implementation is planned as `docs/plan/22-multi-repo.md`; this ADR records the decision and its shape, not the code. + +## Context and Problem Statement + +"One instance per repository" (docs/Werkator-Konzept.md) does not scale even to two repositories on a Hostsharing Managed Webspace: building Werkbaum next to Werkator on mih34 would need a second service, a second assigned port, a second tunnel or domain, a second UI and metrics page. +Every repository multiplies operations while the instance-level resources could be shared. + +### Technical Background + +Everything repository-specific already lives inside the repository or is keyed by it: machine config with secrets, build results, auto-build slots, worktrees, and buildenvs under `.git/werkator/`; the artifact store under a per-repo key. +An instance can therefore aggregate repositories without absorbing their state — adding or removing a repository is a registry entry, never a data migration, and single-repo mode stays the degenerate case (a registry of one, implicitly the current working directory). + +## Considered Options + +* One instance per repository set (registry + aggregation) — chosen +* A federation dashboard proxying several single-repo instances +* Status quo: one instance, one repository + +### One Instance per Repository Set + +Good: + +- One service, port, UI, tunnel/domain, metrics page for any number of repositories. +- Repositories stay self-contained; per-repo secrets, history, and pinning semantics are untouched. +- Global concurrency control across all repositories. + +Bad: + +- The repo dimension must be threaded through executor pools, watcher cycle, routes, and UI — the largest refactor since the rewrite (mitigated by a behavior-preserving `RepoContext` session first). +- Instance-level and repo-level configuration must be split cleanly (see below). + +### Federation Dashboard over Single-Repo Instances + +Rejected: less invasive, but it keeps N services, N ports, and N tunnels — it solves only the UI aggregation, not the operations burden that motivated the change. + +### Status Quo + +Rejected: on a webspace, ports and domains are the scarce, manually assigned resource; per-repository services do not scale there. + +## Decision Outcome + +One instance per repository set, with the configuration split decided 2026-09-01: + +- **Instance config** lives in `~/.werkator.yml` in the home directory of the user running the instance — one instance per OS user, matching the platform model. It carries `server.*` (port, domain/public base URL, nginx), the repository registry, the control token, the global `executor.maxConcurrent`, and the watcher schedule. The file name stays `.werkator.yml` in all three locations; the location carries the meaning. +- **Repo defaults** may live in the home file, but only in an explicit `defaults:` block, merged *below* every repository's own layers (home defaults → committed project config → repo machine config → branch layer); pinning semantics are unchanged. Accepted cost: secrets may then live in two places. +- **Repo config** stays in each repository: the committed `.werkator.yml` and the machine config in its `.git/werkator/`. +- Instance keys found in a repo's machine config are ignored with a warning naming both files once a home config exists — never merged silently. +- When a home config with a registry exists, `werkator server` serves the registry regardless of the current directory; without one it serves the current directory exactly as before. +- Repository names (routes, UI) default to the directory basename, are overridable per registry entry, and duplicates abort the start loudly. + +Consequences: `docs/Werkator-Konzept.md` and AGENTS.md change their wording when the implementation lands (plan step 22 sessions B–D); until then this ADR documents the target and the existing behavior remains accurate. diff --git a/docs/plan/22-multi-repo.md b/docs/plan/22-multi-repo.md index 1ac230f..585c651 100644 --- a/docs/plan/22-multi-repo.md +++ b/docs/plan/22-multi-repo.md @@ -38,14 +38,13 @@ The pinning model is untouched: pinned keys still come from each repo's machine ### A — Decision and schema (ADR 0009) -- Write ADR 0009: revise the one-instance-per-repository tenet to one-instance-per-*set*; record the aggregator idea and the key ownership split above. - Considered alternative to record: a federation dashboard proxying several single-repo instances — less invasive, but it keeps N services/ports and solves only the UI, not the operations burden. +- ~~Write ADR 0009~~ — done 2026-09-01: `docs/adrs/0009-2026-09-01.multi-repo-instance.md` revises the one-instance-per-repository tenet to one-instance-per-*set* and records the aggregator idea, the key ownership split, the four 2026-09-01 decisions, and the rejected federation-dashboard alternative. - Define the instance config: `~/.werkator.yml` (decided 2026-09-01) — the repository registry plus the instance-level keys above; one instance per OS user, which matches the platform model (pac users on a webspace, service users elsewhere). `werkator server` without a home config serves the current directory exactly as today. - Decide the transition for instance keys that today sit in a repo's machine config (mih34's carries `server.*`): once a home config exists, repo-level instance keys are ignored with a warning naming both files — never merged silently. - Repo identity for display and routes (decided 2026-09-01): a short unique name per registry entry, defaulting to the repository's directory basename, overridable in the entry; duplicate resulting names abort the start loudly. Used as the route segment (`/repos//…`) and UI grouping key. - Precedence (decided 2026-09-01): when a home config with a registry exists, `werkator server` serves the registry regardless of the current directory — one user, one instance, deterministic; without a home config it serves the current directory exactly as today. -- Update `docs/Werkator-Konzept.md` and AGENTS.md wording ("one instance per repository set"). +- Update `docs/Werkator-Konzept.md` and the AGENTS.md architecture wording ("one instance per repository set") when the implementation lands (sessions B–D) — until then the existing behavior description remains accurate; the AGENTS.md decision list carries ADR 0009 already. ### B — RepoContext refactor, behavior unchanged @@ -80,7 +79,7 @@ The pinning model is untouched: pinned keys still come from each repo's machine ## Acceptance Criteria -- Session A: ADR 0009 merged; registry and key-ownership documented in `docs/configuration.md`. +- Session A: ADR 0009 written (done 2026-09-01); the registry and key ownership land in `docs/configuration.md` together with the implementing sessions, since that reference describes implemented configuration only. - Session B: full suite green with `RepoContext` threaded through; no route or behavior change observable. - Session C: an instance with two registered repos builds pushes in both, with per-repo error isolation proven by a test (one broken origin, the other keeps building). - Session D: both repos browsable in one UI; single-repo installations keep their existing URLs.