287 lines
14 KiB
Markdown
287 lines
14 KiB
Markdown
---
|
|
status: canonical
|
|
updated: 2026-10-11
|
|
derives: the constitution page
|
|
decisions:
|
|
- "ADR 0020"
|
|
---
|
|
|
|
# How we build
|
|
|
|
The rules that hold across the mesh. Short, and each one earned.
|
|
|
|
**This document is the source of the mesh constitution.** The governed page the mesh injects
|
|
into design sessions is *derived* from it, section for section, and carries the rules without
|
|
the reasoning. Never edit that page directly — an edit there survives until the next sync and
|
|
then vanishes, taking its reasoning with it. The sync is playbook
|
|
[`process/05-constitution-sync.md`](process/05-constitution-sync.md), and it is how the claim
|
|
"HQ is the source" is checked.
|
|
|
|
Section numbers are stable. The orchestrator and the review fragments cite them.
|
|
|
|
---
|
|
|
|
## 1. Purpose
|
|
|
|
These are the principles and guardrails every piece of work in the mesh is checked against —
|
|
design sessions, analysis gates, reviews, and agents acting on their own. A rule stated here is
|
|
non-negotiable unless amended per §6.
|
|
|
|
They exist because each was violated first. Where a rule reads as arbitrary, that is a sign the
|
|
incident behind it is not written down, and the fix is to write it down, not to relax the rule.
|
|
|
|
---
|
|
|
|
## 2. Non-negotiables
|
|
|
|
| Rule | What it means |
|
|
|---|---|
|
|
| **Never write to a production database directly** | No insert, update, delete or schema statement executed against production by hand. Schema changes go through numbered migrations; data changes go through application code or the module's own capabilities. Raw statements skip every side effect the proper path has — events, audit, cache invalidation, fan-out. |
|
|
| **Every schema change is a migration** | Numbered, in the module's own language, compiled with it. Both a baseline for a fresh installation *and* an incremental migration for installations that already exist. If code references a column, the migration creating it must exist. ADR 0013 |
|
|
| **Never bypass the pipeline** | No manual database edit, no manual restart as a workaround. Fix the cause and deploy. A workaround that works is a workaround that is never removed, and the next person cannot tell the node from its declaration. |
|
|
| **Never create a symlink** | A hand-made link caused production data loss through container volume resolution, and the judgement needed to make a safe exception is exactly the judgement unavailable at the moment it matters. **The mesh creates none at all** (ADR 0012, which supersedes ADR 0012). The links the installer still reconciles are a migration, not a permission. |
|
|
| **Never push directly to the main branch** | Branch, push, review, merge. Every merge is a human checkpoint, without exception — **including in this repository**. A documentation repository is not a lower tier of care; a decision record lands the same way a service does. |
|
|
| **One change per pull request, and never merge unapproved work** | Unrelated improvements bundled together cannot be reviewed or reverted separately. And the checkpoint is **a person deciding, not a person clicking** — work may be merged by whoever wrote it once a human has explicitly approved *that merge*, and never on a standing permission, an instruction to do the work, silence, or the author's own judgement that it is ready. ADR 0023 |
|
|
| **Never open a pull request unprompted** | A permissions list saying it is allowed is not a request. |
|
|
| **A failed step fails the job** | A sequence that continues past a failure does the next thing in the wrong place. Gate each step on the last. ADR 0010, and §5. |
|
|
|
|
### A failed step must stop the steps after it — how it was earned
|
|
|
|
A worktree creation failed because the branch name collided with an existing namespace. The
|
|
change into that worktree failed too. The copy, the staging and the commit that followed all
|
|
ran in the shared checkout and committed to a local main. The error was printed and scrolled
|
|
past.
|
|
|
|
This is the same shape as the faults the mesh's whole refactor exists to remove: a step
|
|
reported failure, nothing stopped, and the damage happened somewhere nobody was looking.
|
|
|
|
---
|
|
|
|
## 3. Module and infrastructure rules
|
|
|
|
### Manifests
|
|
|
|
- **Never bump a version by hand.** The builder owns versioning. A version change in a diff is
|
|
a defect; revert it.
|
|
- **Features are detected, not declared.** The installer discovers what a module carries from
|
|
what its directory contains. A declared list and the directory it describes drift, and the
|
|
directory is the one that is true.
|
|
- **Every runtime variable is declared.** A variable the module reads and the manifest does not
|
|
declare is invisible to the mesh: it will not be generated, injected, or audited.
|
|
- **Provisioned credentials arrive through declared requirements**, never hardcoded in code,
|
|
compose files or scripts. ADR 0009
|
|
- **Never install a package by hand.** A package is declared in the manifest and arrives the
|
|
way every other package does. A hand-installed package is invisible to the mesh: it is not
|
|
declared, not reproduced on the next node, and not present after a rebuild — and the node
|
|
works until it doesn't.
|
|
|
|
### Placement
|
|
|
|
- **Core modules belong to the monorepo** — the runtime, delivery, provisioning, configuration,
|
|
knowledge, and the shared infrastructure the mesh provisions against.
|
|
- **Every standalone application gets its own repository**, with a manifest at its root,
|
|
registered as a build source. Creating an application directory in the monorepo is a
|
|
convention violation and reviewers reject it.
|
|
ADR 0015
|
|
|
|
### Migrations
|
|
|
|
- Numbered, idempotent, and safe to re-run. Guard every statement.
|
|
- The initial migration is **frozen** once it has run anywhere. Change is a new number.
|
|
- Numbers are unique. A duplicate prefix is a defect, not a style question.
|
|
|
|
### Managed files
|
|
|
|
**A file edited on a node is a bug with a delay on it.** Everything under the mesh's managed
|
|
surface is regenerated from the mesh database; a local edit survives one synchronisation and is
|
|
then silently overwritten, bringing back whatever it fixed. Use the mesh operation that owns
|
|
the value. If unsure whether a file is managed, ask the tooling — the answer is not visible
|
|
from the file. ADR 0011
|
|
|
|
---
|
|
|
|
## 4. Naming and boundaries
|
|
|
|
### Name a context after its aggregate, not after a metaphor
|
|
|
|
A context named `agents` owns **Agent**. A *brain* — memory, thoughts, cognition — is something
|
|
an agent **has**: a concept inside the aggregate, not a module.
|
|
|
|
The cost of getting this wrong is visible today. An anatomy name points at the node runtime, so
|
|
the most evocative word in the system names infrastructure; another describes itself as "mesh
|
|
messaging" in its manifest while the anatomy documentation calls it the interactive runtime —
|
|
and it runs on no node at all.
|
|
|
|
Anatomy makes attractive names and poor boundaries. Name the thing the domain calls it.
|
|
|
|
### Things that change together share an authority, not a package
|
|
|
|
When several modules always change together under one intent, name the **context** that decides
|
|
for them. Do not merge them into one module: they are delivered to different nodes, and a module
|
|
that must be assigned where half of it is unwanted is not a boundary either.
|
|
|
|
Coherence is a context. Delivery is a module. Relationships are edges, not folders.
|
|
ADR 0009
|
|
|
|
### Contexts integrate through the record, never through a shared schema
|
|
|
|
Publish to the stream; do not join across a boundary. Today several domains share one
|
|
forty-five-table schema, which is why work belonging to one context keeps having to be
|
|
implemented in another.
|
|
|
|
---
|
|
|
|
## 5. Evidence and verification
|
|
|
|
### Ubiquitous language is checked, not assumed
|
|
|
|
**If a document states a rule about the mesh, it says how the rule is verified.** This
|
|
repository has a documented requirement that every capability-exposing module declare the core
|
|
runtime as a dependency. Zero modules do.
|
|
|
|
An unenforced rule is indistinguishable from a wrong one, and costs more, because people
|
|
believe it.
|
|
|
|
### Behavioural criteria require runtime evidence
|
|
|
|
A criterion of the form *"the script runs"*, *"the endpoint answers"*, or *"the migration
|
|
applied"* is satisfied only when the change has actually been exercised: a real run, a real
|
|
request, a pipeline log, a live query showing the expected result.
|
|
|
|
**Marking a runtime criterion verified from a diff is itself a violation.** A reviewer who
|
|
finds one names the evidence required and returns the work.
|
|
|
|
The reason is the mesh's most consistent failure shape: a green result proves transport, not
|
|
effect. Absence reads as success unless something looked.
|
|
|
|
### A test defends a decision
|
|
|
|
The rule above applies to prose. It applies to **decisions** too: a decision record states
|
|
something that must be true, and a test asserts it. A decision with no test is one that will
|
|
quietly stop being true, and nobody will learn that from a document.
|
|
|
|
- Structure and logic — what is accepted, what is refused, how a value is derived — is tested
|
|
**first**, because the behaviour is knowable before the code.
|
|
- Behaviour against a real system is tested **alongside**, because it is discovered rather than
|
|
known.
|
|
- **Mocking the boundary is forbidden.** A test that fakes the system under integration asserts
|
|
that the fake behaves as expected.
|
|
- **The gate is blocking.** Green is the definition of done; a change that has not run its tests
|
|
is not finished, whatever the diff looks like.
|
|
|
|
Not test-driven development as a blanket rule — a test written first against undiscovered
|
|
behaviour asserts a guess. The obligation is that every decision has a defender.
|
|
|
|
### A report is read from the system, never from what asked for it
|
|
|
|
The same rule as the two above, pointed at reporting rather than at verification. **Anything
|
|
that describes the state of the mesh — a status view, an inventory, a diagram, a health
|
|
check — is assembled from the running system.** Assembling it from the intended state produces
|
|
a report that always agrees with itself and can never disagree with reality, which is not a
|
|
report.
|
|
|
|
Where the system does not natively hold a fact the report needs, **the thing that applied the
|
|
fact records it** — and:
|
|
|
|
> **A record of behaviour is written after the behaviour works, never when the resource is
|
|
> created.**
|
|
|
|
Written up front it restates the request in a new place and inherits none of the authority of
|
|
having happened. A failed run leaves its wreckage standing, and a report of that wreckage must
|
|
not describe what the wreckage was supposed to be.
|
|
|
|
This is the production form of the mesh's most expensive fault: a firewall key declared in five
|
|
manifests and read by no code
|
|
([04-ISSUES/003](../04-ISSUES/003-firewall-scope-is-read-by-no-code/00-report.md)). The
|
|
declaration was never wrong. Nothing ever asked the system.
|
|
|
|
### Search the record before forming a hypothesis
|
|
|
|
The first action on any error message, failing service or unexpected behaviour is to search the
|
|
record for the literal error text (`records.records_search`) — before a hypothesis, not after one fails.
|
|
The search matches the words as written, so search with the error's own words.
|
|
|
|
This fires hardest on *familiar* ground, where a confident trail feels like progress. Two
|
|
entries were each rediscovered from scratch over several hours in a single session because the
|
|
search was skipped. Both were already written down.
|
|
|
|
---
|
|
|
|
## 6. Amendment
|
|
|
|
This document is governed. It does not change by commit message or unilateral decision.
|
|
|
|
1. **Propose** — a change stating what rule is changing, why the current wording is
|
|
inadequate, and what reviewed it.
|
|
2. **Review** — sign-off by reviewers who are not the proposer.
|
|
3. **Record** — the change is a decision and gets a decision record in the knowledge base (playbook [02](process/02-graduation.md)), because a
|
|
rule the mesh enforces is architecturally significant.
|
|
4. **Sync** — playbook [`process/05-constitution-sync.md`](process/05-constitution-sync.md)
|
|
publishes the derived page. An unsynced rule is a rule the mesh does not enforce, whatever
|
|
this document says.
|
|
|
|
No drive-by edits. Every change traces to a recorded decision.
|
|
|
|
**Review means a person who is not the proposer.** The enforced page carried a stricter bar —
|
|
a design meeting with at least two node operators — which has never been met and cannot be, as
|
|
there is one operator. A rule that cannot be satisfied is not a high standard; it is a rule
|
|
everything silently violates. Recorded here as resolved in favour of what is achievable, and
|
|
what has in fact been practised (ADR 0022).
|
|
|
|
---
|
|
|
|
## 7. Overrides
|
|
|
|
A team or product may define additional constraints that **narrow or tighten** these rules.
|
|
They may never relax them.
|
|
|
|
An override says which rule it tightens, or which gap it fills, and follows the same amendment
|
|
process. Absence of an override means these rules apply unmodified.
|
|
|
|
---
|
|
|
|
## 8. Code quality
|
|
|
|
*Absorbed 2026-08-26 from the enforced page, which carried these rules while this document did
|
|
not — ADR 0022.*
|
|
|
|
**These rules are recorded because they are enforced, not because this repository earned them.**
|
|
Every other rule here states the incident or measurement behind it. These state nothing,
|
|
because nothing is written down. That is a gap, not a style choice, and it is marked rather
|
|
than dressed up: a rule whose reasoning nobody recorded is one nobody can argue with correctly,
|
|
which is the condition §2 exists to avoid.
|
|
|
|
### Structure
|
|
|
|
- **One reason to change** per module, class or function.
|
|
- **Extend by composition**, not by editing what already works.
|
|
- **Depend on abstractions**, and inject the concrete thing rather than reaching for it.
|
|
- **Small, focused interfaces** over one large one.
|
|
- A substitute for a type must not break the behaviour its users rely on.
|
|
|
|
### Layering
|
|
|
|
Data access, business logic and the interface layer are separate.
|
|
|
|
- No queries in route handlers, tool definitions or daemon loops — those belong in repositories.
|
|
- No orchestration or validation in repositories — that belongs in services.
|
|
- Shared logic belongs in the SDK. Duplicating it into a surface is how two answers to one
|
|
question start to exist.
|
|
|
|
### Types
|
|
|
|
*Scope: the mesh's services and surfaces. Tier 0 is a statically linked binary that must depend
|
|
on nothing installed first, and is written in Go —
|
|
ADR 0005.*
|
|
|
|
- TypeScript throughout; no new untyped JavaScript.
|
|
- Strict, with no implicit `any` and no unchecked index access.
|
|
- Public functions state their return type. Prefer `unknown` with a guard over `any`. Errors
|
|
are typed, never thrown as strings.
|
|
|
|
### Restraint
|
|
|
|
- **Do not over-abstract.** Three similar lines beat a premature abstraction.
|
|
- **Build what is needed now.** A hypothetical future is not a requirement.
|
|
- Names reveal intent, functions fit on a screen, and boundaries fail fast.
|