diff --git a/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md b/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md index 690056dc..1b4fb451 100644 --- a/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md +++ b/03-DESIGN/01-to-be/48-a-module-says-how-it-is-healthy.md @@ -1,7 +1,7 @@ --- layer: to-be status: in-progress -code: [mesh-host, mesh-controller, mesh-lab] +code: [mesh-host, mesh-controller, mesh-lab, mesh-catalog] updated: 2026-10-07 decisions: - 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md @@ -269,6 +269,75 @@ Still to do for Phase A's "done when": every machine's report carrying a state f resource, and a module stopped on purpose raised and cleared, are read on the live mesh once both are rolled out — the node-engine first, then the controller. +## As built — Phases B to E + +Each phase is its own set of pull requests, none merged yet. What the build chose where this design left it +open: + +**Phase B — the field.** + +- **Its words.** On a long-running resource, `health` carries `kind`, the endpoint by its `listens` name, for + http a `path`, the `status` expected (none: any answer under 400), a text the answer must hold and the + scheme, for exec a `command` run by the container's shell, for tool a `tool`, the timing as durations + (`interval`, `timeout`, `grace`) and `looks`, and `needs`. A `port`, `address`, `host`, `url` or `ip` is + refused by name, as is every other key; so is `health` on a step, on anything scheduled and on a service + not stated running. +- **Sent only to an engine that reads it.** The engine's statement now says contract 2; the controller + composes the field — the endpoint as the port the machine published it on, the defaults written out — only + for a machine whose newest statement says so, and strips it for an older engine, which parses strictly + and would refuse the whole declaration. So the engine rolls out first, then the controller, and the + catalogue declares only once that controller runs: until then the merge gate refuses a declaration, which + is the protection working. +- **The engine's looks.** http and tcp from the machine to the published port; a redirect is an answer and + is not followed; a certificate is not judged. A unit's readiness is read from the show liveness already + makes; an exec or adopted check from the inspect liveness already makes, read out of the container's whole + state — naming the check's own key fails the runtime's whole read for a container that carries none. The + runtime's retries are the declared looks and its start period the grace; only these two kinds are part of + what a container is, so declaring an http or tcp check recreates nothing, and adopting an image's check + recreates the container once. A tool is asked on the module's tool at its own machine's instance, the one + subject the engine is granted for it; it answers healthy or not, with why. +- **The state.** Starting until the check passed (a pass inside the grace counts); healthy once it has; + unhealthy once its looks after the grace fail the declared number of times in a row, said with what the + check found. What the check found stays in the condition's evidence: its summary names the check, never a + path or an address. +- **The budget.** Ninety looks a minute on one machine — the default interval on the busiest machine the + research measured; above it the engine spaces its own looks out and says so. +- **The count.** `module check` warns of every long-running resource without `health`, prints the count, and + refuses one from 2026-11-18, six weeks after Phase A went live. +- **The proof.** The replay of issue 145 — a web server whose application accepts every request and never + answers — is said unhealthy by its http check in two looks while a tcp check connects, and the controller + raises the module on the second statement. Proved: it fails on the trunk before Phase B and passes on it. + +**Phase C — the provider hold.** The provider a consumer's `needs` names is its recorded binding to data +([ADR 0232](../../02-DECISIONS/0232-a-binding-to-a-consumers-data-moves-only-by-a-person.md)), and +otherwise the machine its credential for the provision comes from and the module there that offers it. +Only a check's own finding is held — down or restarting is the consumer's. A provider is unhealthy on the +record when its condition is open or its machine's newest statement says so, so the order the machines' +statements arrive in does not matter. The gate has a word between a pass and a fault: *waiting*, which never +puts a build back at the bound. Proved by the test of rule 5 (one provider, three consumers on two +machines: one condition, urgent, listing all three; their gates wait; after recovery only the one still +failing is raised), which fails with four conditions when the hold is taken out. + +**Phase D — the bed.** In mesh-lab's replays, which every merge check of a module of the graph already runs +from the lab's trunk with the runtime and the change's catalogue — so nothing else had to change for the +catalogue's check to start it. It proves the resources whose declared check or image the change touches +(every declared one when it cannot tell), each alone: its literal environment, the files its module writes +in full, an empty place for every other mount, no secret and no provider. A program that stays up while its +check does not see it fails the change; one that does not stay up alone is said and left to the first +machine's gate; a mesh-built image and a tool or unit check are said. The replay of the studio — a server +bound to the address its hostname names, and an image check asking the loopback — fails the bed, and passes +once the hostname is set to every address. + +**Phase E — the migration.** The catalogue keeps the count in a file of its own, and its merge check fails +a change that raises it and one that lowers it without writing the new number. It starts at 93 long-running +resources. The first batch takes it to 70: the image checks of the seven modules that ship them, adopted where +they read healthy on the live mesh — except the mail antivirus, whose six-minute start is past the bound, +and the mail cache, whose image ships none — and tcp or http on endpoints four services already declare. + +Still to do for these phases' "done when": each read on the live mesh once rolled out in order — the +node-engine, then the controller, then the catalogue's declarations; the count's first live reading; and +the bed's first run on a catalogue change at the build seat. + ## What is not decided here - Whether a healer restarts what stays unhealthy (its own record, under ADR 0231).