The numbering is the flow: decisions are 02, design is 03
papa-hq reads 01 research -> 03 decision -> 02 design. The order is a scar, not a choice: 02-DESIGN existed from its initial commit, and when adr/ was finally promoted on 2026-07-13 it took the next free number rather than its place in the sequence. By then design was too settled to renumber. hal-hq was three commits old, so it is not. adr/ becomes 02-DECISIONS and 02-DESIGN becomes 03-DESIGN, and following the folder numbers now walks the process in the order it happens: research produces a decision, the decision authorises a design. 00-GENESIS becomes 00-META, matching papa's rename from the same restructure. Every path reference rewritten across documents, frontmatter, playbooks and skills. All links resolve; all 58 frontmatter blocks parse and their path fields still point at files that exist.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
---
|
||||
status: accepted
|
||||
date: 2026-03-14
|
||||
deciders: jochen
|
||||
reconstructed: true
|
||||
---
|
||||
|
||||
# 2. Everything is a module, and one manifest describes all of them
|
||||
|
||||
> Reconstructed after the fact from the evidence cited below.
|
||||
|
||||
## Context
|
||||
|
||||
The mesh carries several kinds of thing: containerised services with data and ports, pure
|
||||
capability providers with no service at all, and bare markers whose only content is that a
|
||||
node has them. Before this decision these were separate concepts with separate handling —
|
||||
the earlier vocabulary was *capabilities*, and services were installed by a different path
|
||||
than tools.
|
||||
|
||||
Every distinct kind of thing needs its own install path, its own change detection, its own
|
||||
place in the delivery pipeline, and its own documentation. Three kinds means three of each,
|
||||
and every new feature has to be built three times or, more commonly, once — leaving two kinds
|
||||
quietly unsupported.
|
||||
|
||||
## Considered options
|
||||
|
||||
1. **Separate concepts per kind** — a service registry, a tool registry, a node feature flag
|
||||
list. Rejected: it is what existed, and the cost was paid in every cross-cutting change.
|
||||
2. **One manifest, kind inferred from directory contents.** Chosen.
|
||||
3. **One manifest with an explicit `type:` field on every module.** Partly adopted — a service
|
||||
still declares itself — but the general rule became inference, because a declared list and
|
||||
the directory it describes drift, and the directory is the one that is true.
|
||||
|
||||
## Decision
|
||||
|
||||
Everything the mesh installs is a **module**: a directory with a manifest. The manifest
|
||||
declares identity, environment variables, what the module provides, what it requires, and how
|
||||
it is exposed. What kind of module it is follows from what the directory contains:
|
||||
|
||||
| Contains | Is |
|
||||
|---|---|
|
||||
| a compose definition | a service |
|
||||
| a tools directory | a capability provider |
|
||||
| a daemon or unit directory | a long-running process |
|
||||
| a configs directory | a source of managed files |
|
||||
| nothing but a manifest | a flag — presence is the whole content |
|
||||
|
||||
A module may be several of these at once. Each is a **feature**, and the delivery pipeline
|
||||
addresses features, not modules.
|
||||
|
||||
The mesh's own components are modules on exactly these terms. They get no privileged install
|
||||
path, no separate registry, and no exemption from the pipeline.
|
||||
|
||||
## Consequences
|
||||
|
||||
- One mechanism to learn, one to document, one to fix. A pipeline improvement reaches
|
||||
everything the mesh carries.
|
||||
- Dogfooding stops being a discipline and becomes structural: if the mesh's own components
|
||||
need an exception, the machinery is unfinished, and that is visible immediately.
|
||||
- Feature detection from directory contents means a directory rename silently changes what a
|
||||
module *is*. This has bitten repeatedly — a hook named for a feature the module does not
|
||||
have is skipped without complaint.
|
||||
- The manifest becomes load-bearing and grows. It is now the largest single point of
|
||||
coupling in the mesh.
|
||||
|
||||
## References
|
||||
|
||||
- `Rename capabilities → modules across the entire codebase`, 2026-03-14.
|
||||
- `Merge fail2ban, ufw, firewall apps into modules`, 2026-03-15 — the first modules to arrive
|
||||
by conversion rather than by creation.
|
||||
- Knowledge base: `modules`, `modules/manifest-reference`, `conventions/modules`.
|
||||
- The rename-breaks-detection shape: `troubleshooting/hooks-named-for-missing-feature`,
|
||||
`troubleshooting/health-check-tools-index-false-positive`.
|
||||
Reference in New Issue
Block a user