==> read the decisions
ADR-0014: A user repo's layout is the contract, and its flake is one call
Status: Accepted (2026-10-14). Amends ADR-0008: the shared repo, its pin and its build recipe stand; what a user repo declares, and how, changes. Implemented 2026-10-15.
Context
Section titled “Context”ADR-0008 made every user repo a flake over the shared library, so the toolchain is structural rather
than a convention. In practice each user flake grew to about forty-five lines of the same wiring
(projects = { … entry; migrations; lang; }, ui, service, release), copied per repo, with the
project list restating paths the directory tree already implies. project.json existed and nothing
read it.
Decision
Section titled “Decision”-
A user flake is one call:
outputs = { self, shared }: shared.lib.mkRepo { src = self; user = "<name>"; };mkRepoemitsui,service,releaseandchecks(cs-check: tsc + eslint). -
The layout is the contract, discovered by convention and validated when the release is built:
repo.json { "fallback_lang": "en" }backend/index.ts defineUserService(...)projects/<slug>/project.json { "title": "..." } (required)frontend/<slug>.tsx the custom element cs-project-<slug> (ADR-0004)db/migrations/NNN_*.sql applied to schema p_<slug> (ADR-0010)lang/<lang>.json overlays on the fallback language -
Breaking the contract fails the build with a message that names the rule: a missing
project.json, an entry whose name does not match its slug, a migration that does not matchNNN_*.sql, a locale key the fallback lacks, an unknown file in a slot the contract defines. -
The manifest is still a build product (ADR-0008): rendered from the discovered layout, read by provisioning. One declaration, now the tree itself.
-
Each repo is a Bun project (ADR-0011):
package.jsonandbun.lock, built withimportBunLock.
Consequences
Section titled “Consequences”- A new user repo is the layout plus a five-line flake; the wiring cannot drift between repos.
- What a project is, is visible in the tree, and checked.
- Changing the contract is a shared-library change that every repo meets on its next push (the hub builds user releases against shared main).
Alternatives rejected
Section titled “Alternatives rejected”- Keep the explicit
projectsattribute set. It restates the tree, and the restatement was the part that drifted. - A JSON manifest at the repo root listing projects. The same restatement in another format.