Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26fbade21a |
+2
-8
@@ -387,14 +387,8 @@ planner, the walk). **Downstream of** Module, Core, Health and repair and Data.
|
||||
Not a *core component* (ADR 0236's controller, node-engine, tool runner and bus, each with a health probe):
|
||||
the core modules add mesh-delivery and the resolver.
|
||||
- **delivery time** — from a delivery's merge to every node of its deploy plan that is heard from running its
|
||||
modules' builds and reporting them healthy. Its **delivery budget** is what it is held to: three minutes for
|
||||
a leaf module (ADR 0304), ten for a core module (ADR 0282). Not the walk's time, which ends at the send. A
|
||||
measure inside the fix budget, not the operator's target (ADR 0313).
|
||||
- **fix time** — from an issue's first move to `fixing` to the moment the last of its fixes is delivered: the
|
||||
operator's measure of a fix, the agent's work, the review and the checks included, the delivery time its last
|
||||
part. Its **fix budget** is what it is held to: five minutes for a leaf module, ten for a core module, counted
|
||||
from the move to `fixing`; an issue whose fixes move no module has none (ADR 0313). Not the delivery time,
|
||||
which starts at the merge.
|
||||
modules' builds and reporting them healthy. Its **delivery budget** is what it is held to: five minutes for
|
||||
a leaf module, ten for a core module (ADR 0282). Not the walk's time, which ends at the send.
|
||||
- **release** — a person's word that a held delivery goes on; the verb `mesh-delivery.release`. Used in
|
||||
this sense only.
|
||||
- **rollback** — putting a node back on the build it ran before, by something other than the build being
|
||||
|
||||
@@ -3,9 +3,8 @@ layer: to-be
|
||||
status: in-progress
|
||||
code:
|
||||
- mesh-controller
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- "ADR 0315"
|
||||
- "ADR 0001"
|
||||
- "ADR 0005"
|
||||
- "ADR 0006"
|
||||
@@ -65,20 +64,11 @@ applications** — first-party, shipped with everything else, and running on the
|
||||
anything else does. A task does not need to know a node exists, and *being ours does not make
|
||||
something infrastructure*. `ai` is folded into `config`: a provider licence is an ordinary grant.
|
||||
|
||||
**`record` is not an eighth entry: it is the bus** (2026-10-12, ADR 0315). Contexts integrate through
|
||||
it (ADR 0008), and where it lives was left open by research 006, for fear that putting it in the foundation
|
||||
would recreate the circularity the tier design removed. ADR 0315 writes down what later records built: the
|
||||
record is the bus's, the EVENTS stream, where each module says the events it declares (ADR 0041), and the
|
||||
key-value buckets each module declares for its current state, which another module names in `reads` to read
|
||||
(ADR 0201). Asking another context is a call to its verbs over the bus, request and reply, and is not the
|
||||
record; a secret, and whatever a context keeps out of its buckets, is asked for. The controller's own records
|
||||
are read from its record mirrors (ADR 0289) or asked of its verbs, never from its store. The record lives in
|
||||
the foundation's bus, not in the store, which stays each context's private database reached only with that
|
||||
context's own credential, so no store holds a record shared between contexts and the circularity does not
|
||||
return. No module owns the record whole: each event and each bucket belongs to the module that declares it,
|
||||
which alone writes it. *How it is checked:* where each rule was decided — the runtime's refusal of a bucket
|
||||
not issued or a write to one only read (ADR 0201), one credential per context's store (ADR 0008), and the
|
||||
mirrors' events counted live and compared with the store by probe D17 (ADR 0289).
|
||||
**`record` is an open question rather than an eighth entry.** Contexts integrate through it
|
||||
(ADR 0008), which makes it load-bearing,
|
||||
and *where it lives* is unresolved, tracked by issue 414 since
|
||||
research 006 closed — putting it in the foundation risks recreating the circularity the tier design just
|
||||
removed. Listing it here would settle by naming what has not been settled by arguing.
|
||||
|
||||
**One of the seven is built.** `inventory` owns a database of that name and holds the node records;
|
||||
the rest do not exist. What it takes to run any of them — the language, and what must already be
|
||||
@@ -235,8 +225,8 @@ every public name.
|
||||
|
||||
- ~~**The contexts themselves.**~~ **Decided** — seven, by
|
||||
ADR 0006.
|
||||
What remained open, **where the record lives**, is decided by ADR 0315: on the bus, in the EVENTS
|
||||
stream and the buckets each module declares, never in a store (above).
|
||||
What remains open is narrower and named there: **where the record lives**, which research 006
|
||||
leaves unresolved because the foundation is the one place it must not go.
|
||||
- **How far it may be split.** One deployable today. Splitting a context out costs the single
|
||||
interface a surface depends on
|
||||
(research 011).
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-controller internal/catalogue]
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-08
|
||||
decisions:
|
||||
- "ADR 0312"
|
||||
- "ADR 0319"
|
||||
- "ADR 0309"
|
||||
- "ADR 0262"
|
||||
- "ADR 0225"
|
||||
- "ADR 0112"
|
||||
@@ -154,24 +151,6 @@ ADR 0107).
|
||||
never created, owned or removed by the mesh. The module requires read or read-write access. Where
|
||||
the data is, is an operator value on the assignment.
|
||||
|
||||
*A read-write access writes through the path's own group (2026-10-12, ADR 0312, extending ADR 0051; the
|
||||
build is tracked by issue 537):* when a module declares a `read-write` access, the node-engine reads the group
|
||||
that owns the path as it applies, and makes the identity the module's code runs as a member of it: a
|
||||
container's process gains it as a supplementary group, and an account that runs the module's tools is put in it
|
||||
as ADR 0252 puts an account in a group. The module then writes the path as the path's group may, and as nothing
|
||||
else. The path itself is never changed: no owner, group or mode is set on it or under it, and a read-write
|
||||
access whose path's group cannot write is stated unhealthy, saying that the operator decides. A `read` access
|
||||
grants no group. A path owned by group 0, or by a group that would let the identity act as root, is refused by
|
||||
name. A group the node-engine granted is taken back when no declared read-write access asks for it any more,
|
||||
never one the identity held before (ADR 0252 §3). A container is recreated with the group, and a service of
|
||||
the machine's own service manager that runs the module's tools as an account is restarted once the account
|
||||
holds it; a unit of the account's own service manager, or a login session, waits for a new login as ADR 0252
|
||||
§5 and ADR 0254 say. The tool runner's account so gains the groups of every read-write access of the modules it
|
||||
serves: the boundary is the account's, not the module's. *How it is checked:* the node-engine's tests ADR 0312
|
||||
lists (the group given and the path's owner, group and mode unchanged; a read access granting nothing; an
|
||||
unwritable group unhealthy and group 0 refused; the group taken back; the container recreated and the system
|
||||
unit restarted holding the group).
|
||||
|
||||
**A port** is what ADR 0038 already decided: the
|
||||
module says which port its software uses, and the node-engine answers with where the machine put it.
|
||||
|
||||
@@ -233,26 +212,6 @@ gains a setting by shipping its current value as the default, and `settings` lis
|
||||
source: the default, the mesh, or the node. The first module to use it is the notifier, whose font size
|
||||
and width are preferences. *How it is checked:* the table in ADR 0262.
|
||||
|
||||
*A value several modules share is set once (2026-10-12, ADR 0309, changing ADR 0164 §7 and §8 and ADR 0262
|
||||
§3, §7, §8 and §10, and ADR 0319, changing ADR 0164 §4 and §9 and ADR 0309 §2, §3, §5 and §6; the build is
|
||||
tracked by issue 415):* a module keeps every setting it takes, and a preference may also name a mesh-wide
|
||||
**shared name**, such as `desktop.font-size`. Unset in every layer of its own module, the preference
|
||||
reads the shared value, and only when that is unset too its default; read from the bottom, last
|
||||
winning: the default, the shared value, the module's mesh-wide layer, the node's. So a person sets
|
||||
the interface font size once and every module that names it follows, while any one module or node
|
||||
can still be set apart in its own layer. A shared value is a fourth place of change beside the three
|
||||
of ADR 0164 (a new default no longer reaches an assignment whose shared value is set), and the plan
|
||||
of a change to it names every assignment, of every module that reads it,
|
||||
whose effective value moves. It is set and cleared through the `settings` verb without a module, and
|
||||
both `settings` listings name the shared name as a value's source beside the default, the mesh and the
|
||||
node. **A shared value reaches no trusted setting** (ADR 0319): a trusted setting may not declare a
|
||||
shared name, and registration refuses one that does, so a shared value is never trusted and needs no
|
||||
warrant; a trusted setting is set in its own module's layer, on the operator's warrant (ADR 0277) or at
|
||||
the terminal, as before. A trusted setting that a stored manifest still declares with a shared name
|
||||
never reads the shared value, and both listings say why beside it. Whether a shared value is a
|
||||
number, a colour or a string waits for the typed-settings work (issues 346 and 410). *How it is
|
||||
checked:* the tables in ADR 0309 and ADR 0319.
|
||||
|
||||
*The operator's value travels only where it is asked for (2026-09-30,
|
||||
[issue 173](../../04-ISSUES/173-a-modules-settings-reach-every-fact-it-contributes/00-report.md)):*
|
||||
a setting overrides a key a contribution or a served fact declares and adds none; a file keeps taking
|
||||
|
||||
@@ -12,9 +12,8 @@ code:
|
||||
- mesh-tools src/main.ts
|
||||
- mesh-catalog modules/mesh-catalog
|
||||
- mesh-tools node-tools/internal/runtime (a module's state, ADR 0201)
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-07
|
||||
decisions:
|
||||
- "ADR 0307"
|
||||
- "ADR 0240"
|
||||
- "ADR 0233"
|
||||
- "ADR 0201"
|
||||
@@ -261,17 +260,6 @@ them on the bundle's channel — refusing, with the reason, a name it was not is
|
||||
bucket it only reads. A watch hands the current values first, then every change: a bundle that starts
|
||||
late, or starts again, has the whole of the state before it has any of the news.
|
||||
|
||||
*A key's kept values are read too (2026-10-12, ADR 0307, extending ADR 0201).* A bucket keeps as many past
|
||||
values per key as its owner declared, and the runtime serves them: `history`, given a state and a key, answers
|
||||
every value the bucket still keeps for that key, oldest first, each with its revision, whether it was a put or
|
||||
a delete, and when it was written; an empty answer for a key it holds nothing for. It is a read like `get`:
|
||||
served to the owner and to every reader, refused with the reason to a module the bucket was not issued to, and
|
||||
it writes nothing. The Go SDK's `State(name)` carries it as `History(key)`, beside `Get`. Its first user is the
|
||||
issue tracker's switch to its log (ADR 0300 §6), which carries every value its bucket still keeps; that work is
|
||||
tracked by issue 502. *How it is checked:* the runtime's tests (several values and a delete answered oldest
|
||||
first, with revision, operation and time; an empty answer for an unknown key; a reader served, a module the
|
||||
bucket was not issued to refused, nothing written) and the SDK's tests of `History`.
|
||||
|
||||
The owner says how many past values a key keeps and how long a value lives, as a seat says how long
|
||||
its backlog survives (§3); the mesh caps a value's size and a bucket's. **A bucket outlives its
|
||||
module's assignment** — what a module stored is data
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-host, mesh-controller, mesh-catalog]
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-09
|
||||
decisions:
|
||||
- "ADR 0310"
|
||||
- "ADR 0268"
|
||||
- "ADR 0252"
|
||||
- "ADR 0203"
|
||||
@@ -89,7 +88,6 @@ WP4 the service manager's module, finished (mesh-catalog) independ
|
||||
WP5 assign and prove (operator-gated) needs WP1–WP3 merged and rolled
|
||||
WP6 a module puts the account in a group (mesh-host, mesh-controller, mesh-catalog) issue 247
|
||||
WP7 the control-node withholds execute (mesh-controller, mesh-catalog) ADR 0268
|
||||
WP8 a node's files and accounts are read (mesh-sdk, mesh-catalog, mesh-controller) ADR 0310, issue 418
|
||||
```
|
||||
|
||||
WP1, WP2 and WP4 are independent, and are built in parallel on one feature branch per repository
|
||||
@@ -336,33 +334,3 @@ out, the control-node's value is set to `withhold` and zsh is pushed to it. Then
|
||||
2. the control-node's `zsh_config` answers `execute` as not served, and says why;
|
||||
3. `mesh_search` for `node-login-shell execute` names the other three nodes and not the control-node;
|
||||
4. a call to the control-node's `node-login-shell.execute` is refused, and the other nodes still answer.
|
||||
|
||||
## WP8 — A node's files and accounts are read through `node-reads`
|
||||
|
||||
*`mesh-sdk`, `mesh-catalog`, `mesh-controller`. ADR 0310, extending ADR 0268; tracked by issue 418.*
|
||||
|
||||
ADR 0268 §5 has narrow read tools take the common reads over from `execute`, so that an approval is asked for an
|
||||
act and not for a read, and ADR 0286 §44 sets the approval mark on `execute` only once those reads have a tool on
|
||||
every node. Two had none: reading or searching a file, and looking up an account or a group.
|
||||
|
||||
- **A node seat, `node-reads`, on every node**, the control-node included, held by the tool runner; the
|
||||
controller holds the seat's definition beside the other node seats.
|
||||
- **Three reads:** a file read bounded in size, a search by fixed string bounded in matches and in depth, and a
|
||||
lookup of an account or a group. Each verb says what it replaces (ADR 0245): `cat`, `grep`, `getent passwd`,
|
||||
`getent group` and `id`. The bounds are set by the build and stated in each verb's description.
|
||||
- **It reads as an account of its own**, which the mesh creates on every node, in no group that holds secrets,
|
||||
with no login shell and no home of the operator's, so the node's file modes bound what it sees. `/proc` and
|
||||
`/sys` are read as far as that account may.
|
||||
- **Secret places are refused by name** before any read: the vault's directories, every module's own-secret
|
||||
directory and the known credential files, whatever the file modes would allow.
|
||||
- **One content rule.** The rule the operator channel applies to what it sends moves into `mesh-sdk`, and both
|
||||
the operator channel and every `node-reads` answer apply it.
|
||||
|
||||
Once the seat answers on every node, the approval mark on `node-login-shell.execute` is set as ADR 0286 §44 says,
|
||||
under the same issue.
|
||||
|
||||
**Proof.** The rows of ADR 0310's *how it is checked*: the controller's seat listing names a holder of
|
||||
`node-reads` on every node; the tool runner's tests of the bounds, the account and the refusal by name; one
|
||||
`mesh-sdk` test of the content rule and one in each of its users; `mesh_search` for `cat`, `grep` and
|
||||
`getent passwd` finding the verbs. Live, the kernel's RAID status file is read through the seat on the home
|
||||
server, and the agent's credentials file on the laptop is refused.
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-catalog, mesh-controller, mesh-host]
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- "ADR 0303"
|
||||
- "ADR 0215"
|
||||
- "ADR 0173"
|
||||
- "ADR 0040"
|
||||
@@ -121,20 +120,6 @@ not write into its include directory. Each contribution is a dependency on the s
|
||||
so assigning one of them without a window manager is refused. The first versions, which still write
|
||||
the include files themselves, move to contributions once the controller derives the dependency.
|
||||
|
||||
**A package may be declared by several modules** (2026-10-11, ADR 0303, changing ADR 0210 §1, §10 and §11
|
||||
for packages). Two modules on one node that each need a program may each declare its package, and it is
|
||||
present while any of them declares it. Declaring a package asserts only that it is present: the node-engine
|
||||
installs one the machine lacks, never upgrades it, and never removes it because a declaration went away, so a
|
||||
second declaration changes nothing on the machine and taking one module off leaves the package to the others.
|
||||
A module that needs a program another seat holder installs may declare that package itself, or still depend on
|
||||
that seat. One module declaring a package present while another declares it absent (ADR 0180) on one node is
|
||||
refused at assignment and at composition, naming both. A tool's configuration files and the contributions to
|
||||
its seat keep their one owner, as above: only the package itself may be declared by several modules. *How it
|
||||
is checked:* the controller's tests (two declarations of one package are assigned; present against absent is
|
||||
refused, naming both modules) and the node-engine's tests ADR 0303 requires (a package declared twice is
|
||||
installed once, one no module declares any more is not removed, an installed one is reported unchanged and not
|
||||
upgraded).
|
||||
|
||||
**Added 2026-10-04.** `triggerhappy` holds `node-hotkeys` on both workstations (ADR 0212). The
|
||||
laptop model's vendor keys become its contribution. The window-manager fragments of the launcher, the
|
||||
clipboard, the wallpaper, the bar and the laptop model become `config` contributions to
|
||||
|
||||
@@ -4,7 +4,7 @@ status: in-progress
|
||||
code: [mesh-controller, mesh-host, mesh-tools, mesh-catalog, mesh-sdk, mesh-lab]
|
||||
updated: 2026-10-12
|
||||
decisions:
|
||||
- "ADR 0314"
|
||||
- "ADR 0318"
|
||||
- "ADR 0293"
|
||||
- "ADR 0289"
|
||||
- "ADR 0285"
|
||||
@@ -376,7 +376,23 @@ section; to-be 46 §13 builds what follows from it.
|
||||
(warning); on `condition-cleared`, by editing the first message where the channel can. A silenced
|
||||
condition sends nothing. Urgent goes to both channels; warning to the desktop notifier when the
|
||||
operator's session is there, otherwise to Telegram.
|
||||
*Amended 2026-10-12, ADR 0318:* **history is never news.** On start, every ten minutes, and at once
|
||||
when an event older than its last reading arrives, the holder reads the open conditions from the
|
||||
controller and takes them as its state; an event is acted on only when it is newer than that reading
|
||||
(five seconds of clock slack) and its own `at` is no more than ten minutes old — otherwise it is
|
||||
recorded as state and never said. A condition open in the holder's state but absent from the
|
||||
controller's open set ended while the holder was away, and is closed quietly: edited where a channel
|
||||
can, never sent to wake a desktop. A clearing of something never said says nothing, and a message
|
||||
still waiting is dropped the moment its clearing is read. Every urgent condition a reading finds open
|
||||
and unsaid is said once, together, naming all of them. A grace minute follows every start or
|
||||
reassignment: nothing is sent in it except that one message.
|
||||
- **Rate:** at most twenty messages an hour; the excess is folded into one message naming them all.
|
||||
*Amended 2026-10-12, ADR 0318:* a channel holds what it has to say for thirty seconds from the first
|
||||
item and sends it together — alone as itself, several as one digest, one line each; an urgent item
|
||||
breaks a thirty-second quiet at once. The desktop keeps a gentler rhythm than Telegram: a warning
|
||||
reaches it at most once every fifteen minutes, never an urgent one. Reaching the cap is said once, in
|
||||
its own message, and what it holds back waits for the next digest the hour allows, rather than one
|
||||
message per excess item.
|
||||
- **What may leave the mesh:** roles and words. A message carrying an address, a path or anything
|
||||
shaped like a secret is refused by the holder and raises `channel-refused` instead.
|
||||
*Amended:* the mesh's own machine names may appear too; a concrete detail travels as a reference only
|
||||
@@ -532,22 +548,6 @@ the controller's replay R301 and its tests of the rule).
|
||||
> its cause, so the exception is read from the verbs. Nothing decided changes: ADR 0230's acts stay
|
||||
> exempt, and pushes by hand — what roll-out by default (ADR 0236) exists to end — still count.
|
||||
|
||||
**A condition no healer can fix becomes an issue** (2026-10-12, ADR 0314, extending ADR 0285; the build is
|
||||
tracked by issue 409). A condition no healer can fix is one whose kind no healer answers, one a healer passes
|
||||
over because it does not apply, or one handed to the operator after a healer's budget was spent. When such a
|
||||
condition is raised, passed over or handed on, and no issue links its key (ADR 0285), the issue tracker opens
|
||||
one itself, from the controller's condition events, with the condition's summary, its evidence and its key, and
|
||||
links the key at once; the controller sets no link. Every message about the condition then names the issue
|
||||
through its tracking line. Agent sessions take such an issue as any other: a claiming move that names the
|
||||
worker, a branch, review and the required checks; no standing agent acts on a condition by itself. What an
|
||||
agent does about it is written in the issue's notes, and every act it makes by hand on the mesh is also written
|
||||
to the hand-act log, which S15 reads. ADR 0275 §9's week stands: the tracker opens no issue for a condition
|
||||
before 2026-10-17 20:17 UTC, and until then such a condition is linked by whoever opens an issue for it. *How
|
||||
it is checked:* the tracker's tests feeding condition events (one issue opened and linked for each of the three
|
||||
cases, none for a condition a healer acts on or one already linked, none before the week ends); live, the
|
||||
messenger's preview of such a condition says "Tracked as issue N", and the issue's notes and the hand-act log
|
||||
hold what the agent did.
|
||||
|
||||
## 8. Staged core upgrades and rollback (rule 8)
|
||||
|
||||
**Health, per core component**, as probes in the registry (§4):
|
||||
@@ -898,3 +898,4 @@ in [to-be 47](47-delivery-from-commit-to-delivered.md), and their progress is tr
|
||||
- Routing by presence, quiet hours, answering back through a channel, and an external dead-man
|
||||
service — decided by ADR 0234,
|
||||
designed in [to-be 46](46-the-conversation-with-the-operator.md).
|
||||
- A condition that needs judgement handed to an agent as work — tracked by issue 409, since research 017 closed.
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-controller, mesh-tools, mesh-sdk, mesh-catalog]
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- "ADR 0308"
|
||||
- "ADR 0286"
|
||||
- "ADR 0277"
|
||||
- "ADR 0259"
|
||||
@@ -290,40 +289,6 @@ Presence facts are events on the bus. The router keeps them as **current state o
|
||||
machine and per intake kind, overwritten — never as a history, and never in a message's words. A
|
||||
consumer that wants a timeline of the operator's day is a decision of its own.
|
||||
|
||||
### While a delivery walks
|
||||
|
||||
*2026-10-12, ADR 0308; the build is tracked by issue 532.*
|
||||
|
||||
A delivery restarts what it moves, and those restarts raise conditions that clear once it is done; a router
|
||||
that restarted used to say again what it had been told to keep quiet (issue 531). So the router holds what a
|
||||
walk touches and tells the walk's result once.
|
||||
|
||||
- **A walk names what it touches.** The controller's walk events say, when a walk starts and again when it
|
||||
ends with its outcome (delivered, failed or stopped), the modules it moves and the machines it sends them to.
|
||||
What it touches is those modules on those machines, and every module a moved tool runner or node-engine
|
||||
carries there.
|
||||
- **The router holds what an open walk touches.** A message whose subject is a touched module or machine is
|
||||
held instead of sent, kept with when it was held, and `history` says it was held. A message several open
|
||||
walks touch is held until the last of them ends or its bound passes.
|
||||
- **Some messages are never held:** an ask (§6), a secret given or replaced, a message about anything no open
|
||||
walk touches, and a walk's own failure.
|
||||
- **A walk's end is told once:** one message with its outcome, the modules and machines it reached and how
|
||||
long it took, then each held message whose condition is still open, one line each. One whose condition
|
||||
cleared during the walk is only counted. A walk delivered with nothing held and nothing open is one line, or
|
||||
nothing on a channel set to say only what needs the operator.
|
||||
- **A hold has a bound:** twice the largest delivery budget in force among the walk's deliveries
|
||||
([to-be 47](47-delivery-from-commit-to-delivered.md)): 20 minutes for a core module, 10 for leaf modules at
|
||||
a five-minute budget and 6 at a three-minute one; 15 minutes, not doubled, for a walk none of whose
|
||||
deliveries has a budget. Past it, every held message still open is sent with a line saying the walk still
|
||||
runs and since when, and the cleared ones are counted. A walk that hangs never silences the mesh.
|
||||
- **A restart says nothing it has said:** a router that restarts says again only what is open and was never
|
||||
answered, never a silenced message or one withheld for its content.
|
||||
|
||||
*How it is checked:* the controller's tests of the walk events, and the router's tests of holding, never
|
||||
holding, the end message, the bound and the restart, as ADR 0308 lists them; live, the next delivery of the
|
||||
tool runner gives the operator one message at its end, and the channel's `history` shows what was held and
|
||||
released.
|
||||
|
||||
### The content rule
|
||||
|
||||
The router's, applied before anything reaches any holder; §9 says what it allows. A `private` holder
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-controller, mesh-catalog modules/mesh-delivery]
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-11
|
||||
decisions:
|
||||
- "ADR 0313"
|
||||
- "ADR 0306"
|
||||
- "ADR 0299"
|
||||
- "ADR 0298"
|
||||
- "ADR 0282"
|
||||
@@ -104,11 +102,7 @@ delivery merged with no walk answering it within ten minutes is held too, with t
|
||||
forge says that later merge commit contains it (`gitea_contains`, asked once per pair and kept). Merge
|
||||
order is only the first filter, and a forge that cannot answer means not yet covered. `mesh/delivery`
|
||||
says `covered: a later merge that contains it was delivered` as a success. A module that runs on one
|
||||
machine only is covered too: its walk records the empty rest as sent once its first machine passes
|
||||
(ADR 0270, as corrected 2026-10-12);
|
||||
- *a stop whose walk ended without building its modules* (issue 474) stays `stopped`, a final state, and
|
||||
is read as covered once a later merge covers it by the same test: `mesh/delivery` then says `covered after
|
||||
its stop` with the covering merge, once, as a success. No state or transition is added (issue 480);
|
||||
machine only gets no "rest" step and is never covered;
|
||||
- the covered row is tried before `go`, from `held` and from `ready` past the ten minutes.
|
||||
|
||||
**History is retired, not held** (ADR 0250).
|
||||
@@ -223,14 +217,6 @@ shown and held to no budget. Measured on 51 walks on 2026-10-09 and 10, a leaf w
|
||||
(7 of 20 within five), a core walk of one tier 5.6 (worst 8.8), and a controller walk dragging two catalogue tiers
|
||||
14.2 (issue 338).
|
||||
|
||||
*A wider measure, around the delivery budget (2026-10-12, ADR 0313, extending ADR 0282).* The delivery budget
|
||||
above runs from the merge; the operator's own target is wider, the **fix time**, which runs from an issue's
|
||||
first move to `fixing` to the moment its last fix is delivered. Each issue's class carries a **fix budget** too
|
||||
(five minutes leaf, ten core, the delivery budget's own figures), and the tracker splits a fix time into four
|
||||
phases — implementing, review, checks, and merge to running (the delivery time above) — so a fix over its
|
||||
budget is said loud, naming its longest phase, and clears at the next fix of its class delivered within budget.
|
||||
The delivery budget stays as written: a measure inside the fix budget, not replaced by it.
|
||||
|
||||
**What changes in the walk.**
|
||||
|
||||
- **A lone merge is cut at once**: no batch assembling, and no member of its delivery group still unmerged.
|
||||
@@ -246,32 +232,6 @@ The delivery budget stays as written: a measure inside the fix budget, not repla
|
||||
- **Every phase is kept**: the window, the wait, the build, the send, each reading, the rest's first healthy
|
||||
report; the plan shows an expected landing time from the last seven days' medians.
|
||||
|
||||
*Walks beside each other (2026-10-12, ADR 0306, extending ADR 0304, which narrowed the bullets above to what
|
||||
a change shares).* Building the walks at once found what those bullets left open, and ADR 0306 decides it:
|
||||
|
||||
- **A walk's builds are its own, never another's backlog.** A send composes a module whose build another open
|
||||
walk owns at the build its node was last sent, and neither carries nor judges it: that build is moved and
|
||||
judged only by its own walk, so a failed walk puts back only its own builds. What waits on a node with no open
|
||||
walk to move it is still backlog, carried and judged by the next gated send there (this changes ADR 0236 §17
|
||||
for builds an open walk owns).
|
||||
- **A delivery group still assembles.** A merge whose delivery group has a member not yet merged waits for the
|
||||
merge window whether or not a batch is assembling, so a group's members walk in one batch, in their order.
|
||||
- **Related means everything the walks would move.** A module in common is any module a walk plans to build or
|
||||
send, the dependents of what its merges moved included; a change whose modules are not known yet is related to
|
||||
every change. Two related walks are neither cut beside each other nor judged beside each other on one node.
|
||||
- **A starved judgement is said.** A leaf judgement set aside by core builds three times in a row raises a
|
||||
warning naming the walk and the core walks that set it aside.
|
||||
- **A node's own fault fails every judgement on it.** A node found unwell as a whole (a failed apply, an
|
||||
unhealthy network or engine) fails every judgement open on it, leaf and core, each walk putting back only its
|
||||
own builds.
|
||||
- **A rollback beside another walk's judgement** behaves as it does today; whether it should wait is tracked by
|
||||
issue 528.
|
||||
|
||||
*How it is checked:* the controller's tests ADR 0306 lists, one per clause: an unsent build of walk A left out
|
||||
of walk B's send and not taken up as backlog while A is open; a group's second member joining its first's batch;
|
||||
overlapping dependents keeping two walks apart; the warning on the third set-aside; a failed apply failing every
|
||||
open judgement on its node.
|
||||
|
||||
**Who builds what.**
|
||||
|
||||
| Part | Owner | What it adds |
|
||||
@@ -347,7 +307,7 @@ In this order, each by its owner:
|
||||
|
||||
| Verb | Answers or does |
|
||||
|---|---|
|
||||
| `deliveries` | every delivery not final, the final ones of the last day, and every final one `stalled` still lists, one line each; filtered by state, repository or group |
|
||||
| `deliveries` | every delivery not final, and the final ones of the last day, one line each; filtered by state, repository or group |
|
||||
| `show` | one delivery or group whole: plan, transitions, machine steps, order, the walk |
|
||||
| `groups` | every group with its members in order and its derived state |
|
||||
| `what-if` | the delivery plan a diffset would have, asked of the controller's planner, kept nowhere |
|
||||
|
||||
@@ -6,9 +6,8 @@ code:
|
||||
- mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its unbuilt code removed)
|
||||
- mesh-catalog modules/gitea (package retention)
|
||||
- mesh-catalog modules/docker (image pruning)
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-08
|
||||
decisions:
|
||||
- "ADR 0311"
|
||||
- "ADR 0251"
|
||||
- "ADR 0189"
|
||||
- "ADR 0257"
|
||||
@@ -111,8 +110,7 @@ ADR 0257:
|
||||
failed one, and the controller keeps a row per copy in `artifact_mirrored`. The copies the existing
|
||||
build records already named were recorded by the migration that made the table.
|
||||
- **Kept by the kept builds that stood on it** (`stood-on`), and by nothing else: a definition never
|
||||
keeps a copy. Otherwise a copy is eligible, like any artifact the mesh made. *(A copy of a pinned
|
||||
third-party image is the exception, below: ADR 0311 §3.)*
|
||||
keeps a copy. Otherwise a copy is eligible, like any artifact the mesh made.
|
||||
- **Held whole.** A copy counts as held only when its index and every platform the index names are
|
||||
held; otherwise it is copied again. A copy a build copies again is no longer recorded as let go of.
|
||||
- **An index goes with its platforms only in a confirmed collect.** Before its first delete, it reads
|
||||
@@ -124,35 +122,6 @@ ADR 0257:
|
||||
`unrecorded_mirrors`, with what it alone marks. The controller's `mirrors` verb records the ones a
|
||||
person names, and the sweep then decides about them.
|
||||
|
||||
### Pinned third-party images
|
||||
|
||||
*2026-10-12, ADR 0311, extending ADR 0257; the build is tracked by issue 421.* A third-party image a module
|
||||
pins was held only upstream, and an image withdrawn there was found only when a node that lacked it tried to
|
||||
deploy (issue 113). So the mesh holds a copy of what its modules pin, and checks both sources:
|
||||
|
||||
- **Every pinned third-party image is copied** into the artifact store when it is first used, into the
|
||||
repository of its upstream image (`upstream/<host>/<path>`, as above).
|
||||
- **Its record names the pin.** The copy's row names, as its reason, the module whose definition pinned the
|
||||
digest (changing ADR 0257 §6, which named only a build or a person's reason).
|
||||
- **Kept while a module pins it.** A copy is kept, with the reason `pinned`, while any module's current
|
||||
definition pins its digest; neither the sweep after a build nor the nightly clean-up lets it go. For these
|
||||
copies a definition does keep a copy, and one is eligible only when no kept build stood on it and no current
|
||||
definition pins it (changing ADR 0257 §8, §9 and §20). `artifact_store_references` shows them with the reason
|
||||
`pinned`.
|
||||
- **A node pulls from the mesh first**, from the artifact store's copy, and from the upstream only second.
|
||||
- **A scheduled check pulls every pinned image as an empty node would**, once from the copy and once from the
|
||||
upstream. An upstream that no longer serves while the copy does raises a warning condition naming the module
|
||||
and the image; neither serving raises an urgent one; each clears once the image is served again. An upstream
|
||||
that serves while the copy does not is not decided yet (issue 421); a node then still pulls from the upstream.
|
||||
- **A module may declare its upstream**, the object store's module first, with its maintained fork, and a view
|
||||
shows each declared upstream's last release and open advisories, so ADR 0288's triggers of no release for six
|
||||
months and of an advisory unpatched for 30 days can be read there.
|
||||
|
||||
*How it is checked:* the table in ADR 0311 — the controller's copy and its row, the collection test of keeping
|
||||
by pin, the node-engine's pull from the store with the upstream unreachable, the check's two conditions, the
|
||||
manifest test of a declared upstream, the view against a recorded answer, and live, the object store's pinned
|
||||
image held in the store.
|
||||
|
||||
### The controller's verbs for the store
|
||||
|
||||
- **`artifacts`** reads only. It answers every reference the mesh recorded making, with its state and,
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
layer: to-be
|
||||
status: designed
|
||||
code: []
|
||||
updated: 2026-10-12
|
||||
updated: 2026-10-11
|
||||
decisions:
|
||||
- "ADR 0290"
|
||||
- "ADR 0321"
|
||||
---
|
||||
|
||||
# 55 — The knowledge base
|
||||
@@ -126,17 +125,8 @@ approval. On `approve`:
|
||||
|
||||
On acceptance the record and its clauses move to `accepted` and `in force`, every clause its `changes` and
|
||||
`supersedes` name moves to `changed` or `superseded`, and `accepted` and `changed` events are emitted. Then the 4
|
||||
rules that judge the whole record set run (an accepted decision cited by nothing, and the like). A failure does not
|
||||
undo the acceptance. It is a fault of the records, which their authors repair, not of the module: it is told to the
|
||||
operator once, as a warning naming the rule and the records, and cleared once the rule holds again. The module's
|
||||
health names it and stays healthy, because health says only whether the module works, and a finding never holds a
|
||||
walk at the module's first-node gate (the operator's decision of 2026-10-12 on issue 504, as issue 508 decided for a proposal never
|
||||
reviewed).
|
||||
|
||||
The rule `cited` is also judged ahead, in the review, before the judgement: a decision that names no home and that no
|
||||
accepted record links is not accepted. Its review, by the knowledge base itself, asks changes naming the rule (issue
|
||||
504). It is not refused at `propose`, because no record can link a number only reserved: a record that cites a new
|
||||
decision is proposed after it and accepted, and the new decision is proposed again.
|
||||
rules that judge the whole record set run (an accepted decision cited by nothing, and the like); a failure raises a
|
||||
condition naming the rule and the records, and does not undo the acceptance.
|
||||
|
||||
An accepted record is changed only by a new record, or by a progressive insight: a new version marked as one, which
|
||||
is reviewed like a proposal.
|
||||
@@ -158,14 +148,6 @@ per clause whether the record says exactly that; an accepted clause becomes `con
|
||||
same way. When every record of a topic has been through both, the operator is told once per topic, with the counts
|
||||
confirmed and still drafted. `return` with a record's number and a reason sends it back to be drafted again.
|
||||
|
||||
**A drafted clause may be changed, and a linked clause keeps its number** (ADR 0321, issue 494). A later record's
|
||||
`changes` may name a drafted clause, confirmed or not, as it may one in force or changed; only a superseded or proposed
|
||||
clause is refused. On that record's acceptance the drafted clause is marked `changed`, naming the clause that changed
|
||||
it, keeps its number and stays `drafted`: it is still never served as in force, and `get` and `ask` say both. Because
|
||||
a redraft numbers a record's clauses anew, `return` refuses a record any of whose clauses another record changed or
|
||||
links by number, in a version served or proposed, naming each such link; a record that changes the drafted clause is
|
||||
the way to correct it. A record no other record links by clause is returned as before.
|
||||
|
||||
## 8. The import
|
||||
|
||||
`import` takes a commit of this repository (and `dry-run`) and writes every decision record and research effort
|
||||
@@ -202,12 +184,10 @@ repository keeps no copy of the records, generated or otherwise (issue 435, answ
|
||||
|---|---|
|
||||
| A proposal that breaks a per-record rule is refused, naming every reason | the module's table test, one case per rule of § 4 |
|
||||
| A record is accepted only after the knowledge base's own judgement approves it, and, when it needs the operator, only on the warrant | the module's tests: a proposal whose judgement answers changes, or cannot be had, stays proposed; a `needs operator` record unaccepted without the warrant, accepted with it |
|
||||
| A decision is not accepted cited by nothing | the module's test: a decision with no home and no record linking it gets changes from the review, with no judgement asked; with a home, or once a record citing it is accepted, it is accepted |
|
||||
| A whole-set rule that fails is loud, and leaves the module healthy | the module's test: a correction that leaves a decision cited by nothing tells the operator once, as a warning naming the rule and the record, not again after a restart, and clears it once a record cites it; the module's health stays healthy throughout |
|
||||
| A whole-set rule that fails is loud | the module's test: an acceptance that leaves a decision cited by nothing raises the condition |
|
||||
| Every version is kept and comparable | the module's test: three versions, `history` lists them, the difference of the first and third is exact |
|
||||
| The import changes nothing on a second run, and loses nothing | the module's test over a fixture repository; and the comparison of § 8 on issue 426 |
|
||||
| A drafted clause is never served as in force | the module's test: `holds` over a record with only drafted clauses answers none, and `ask` marks them |
|
||||
| A drafted clause may be changed, and a linked clause is never renumbered | the module's tests: a record changing a drafted clause is accepted, and the clause is then `changed`, keeps its number, stays drafted and is not served; `return` of a record whose clause another record changed or links by number is refused naming the link, and writes nothing |
|
||||
| An answer stays within its bound | the module's test over the full import of `main` at a recorded commit: every `ask`, `get` and `holds` of a fixed question list |
|
||||
| A record written in this repository meanwhile writes its clauses | `records.py`, check `clauses`, on every hq pull request, until the records left this repository (issue 426); since then `records.py`, check `no-records`, refuses any record written here |
|
||||
| Live | the acceptance sentence, asked from a session outside this repository |
|
||||
|
||||
Reference in New Issue
Block a user