ADR 0021 — Filesystem layout, config resolution, and local installation¶
Status: Accepted (phases 1–3 shipped; the optional MCP user service is deferred)
Context¶
mklang installs cleanly from PyPI, but the CLI still assumes a repo checkout:
--config defaults to the relative path config/runtime.example.yaml, console
sessions live in the hardcoded ~/.mklang/console/sessions, and machines are
resolved only from the bundled stdlib, entry-point plugins, and an explicit
--dir. There is no user- or system-level home for config and machines, and no
scaffolding command — a pip install mklang outside the repo has nothing to run
against until the user hand-builds a config.
Best practices §13 defines the filesystem taxonomy for machines (host paths,
workspace .mk, data tools); this ADR is the host-side counterpart: where the
host itself keeps config, machines, and state.
Decision¶
- XDG layout, three roots (module
mklang/paths.py):
| Root | Path | Holds |
|---|---|---|
| config | $XDG_CONFIG_HOME/mklang (default ~/.config/mklang) |
runtime.yaml, .env |
| data | $XDG_DATA_HOME/mklang (default ~/.local/share/mklang) |
machines/ |
| state | $XDG_STATE_HOME/mklang (default ~/.local/state/mklang) |
console sessions, checkpoints |
System level: /etc/mklang/runtime.yaml and /usr/share/mklang/machines/.
Env overrides MKLANG_CONFIG_DIR / MKLANG_DATA_DIR for tests and sandboxes.
-
Config resolution order when
--configis omitted (first hit wins): explicit flag →$MKLANG_CONFIG→./config/runtime.yaml(project) → user config root →/etc/mklang/runtime.yaml→ bundled example (read-only fallback, preserving today's behavior inside the repo). -
Machine search path, later wins, extending
base_registry's existing layering: stdlib ← entry-point plugins ← system machines ← user machines ← project--dir.mklang machineslabels the new sourcessystem/useralongsidestdlib/plugin/local. -
mklang initscaffolds a project (config/runtime.yaml,machines/,.envtemplate);mklang init --userseeds the user config root from the bundled example, never overwriting existing files. -
Legacy migration.
~/.mklang/console/sessionskeeps working: the state root is preferred for new sessions, the legacy path is read if present. -
Packaging artifacts (
packaging/): a pipx-basedscripts/install.sh(pipx install 'mklang[console,mcp]'+mklang init --user, with--uninstall) and an AURPKGBUILDinstalling the system-level config and machines. The optional systemd user unit for the MCP server is deferred (see Rollout).
Consequences¶
pip install mklang && mklang consoleworks from any directory oncemklang init --userhas run — no repo checkout required.- Precedence is boring and predictable: project beats user beats system beats bundled, mirroring the machine-registry layering authors already know.
- The bundled example config stops being the implicit production default; inside the repo nothing changes.
- New surface to test:
paths.pyresolution under monkeypatched env, init idempotency, and registry labeling of the new sources.
Rollout¶
Phases 1–2 shipped: paths.py, config resolution, state migration,
mklang init, machine search path, and discovery source labels. Phase 3
shipped: scripts/install.sh (pipx) and the AUR recipe in packaging/arch/.
The optional systemd user unit is deferred: the MCP server is stdio-only —
clients spawn it per session, so a persistent service has nothing to listen
on; it becomes meaningful once the server grows a network transport
(e.g. streamable HTTP).