Issues 173 and 174 resolved; the installation check refuses at registration (ADR 0155)

A setting overrides a key a contribution or served fact declares and adds none; a provider that must
tell its consumers an operator's value declares it as ${setting:…} (173). The mesh's own files for a
module are a placed directory, `place: "mesh"`, and forty-eight definitions name no host path for
them (174). Registration refuses a definition naming an installation, the day the list emptied
rather than a release later (0155, progressive insight; 134). Designs 27 and 18 carry the rules.
This commit is contained in:
2026-09-30 22:36:20 +02:00
parent e84c822e89
commit 36454d7e4a
7 changed files with 72 additions and 12 deletions
@@ -67,6 +67,8 @@ registration**, because the list it prints is the list that shrinks, and a regis
manifest whose only remedy is a merge elsewhere would refuse the mesh's own catalogue on the day the manifest whose only remedy is a merge elsewhere would refuse the mesh's own catalogue on the day the
check landed. It moves to registration when the list has been empty for a release. check landed. It moves to registration when the list has been empty for a release.
> **Progressive insight — 2026-09-30.** The list was empty the day the check landed — every remaining name declared with its reason — and the operator asked for registration to refuse at once rather than after a release. It does, since mesh-controller PR 175: `module add` and a build's result are refused in the check's words, naming the way out, and the build stays recorded. The decision stands; only the day moved.
**A name a definition means is declared with its reason.** `names-on-purpose` on a resource maps each **A name a definition means is declared with its reason.** `names-on-purpose` on a resource maps each
such name to why: *the federation's public key server, the world's*; *built outside the mesh, from the such name to why: *the federation's public key server, the world's*; *built outside the mesh, from the
application's own repository, until that repository is a build source on the git seat*. A name the map application's own repository, until that repository is a build source on the git seat*. A name the map
@@ -113,6 +115,7 @@ no base for a seat a context names refuses the build by the seat's name rather t
| An image from an installation's registry needs a reason | a test without and with the word | | An image from an installation's registry needs a reason | a test without and with the word |
| A module named after a domain is reported | a test | | A module named after a domain is reported | a test |
| No definition in the catalogue names an installation | `TestNoCatalogueManifestNamesAnInstallation` over the checkout, and `module check modules/` | | No definition in the catalogue names an installation | `TestNoCatalogueManifestNamesAnInstallation` over the checkout, and `module check modules/` |
| A definition naming an installation is refused at registration, and one declaring its names passes | `TestRegistrationRefusesADefinitionNamingAnInstallation` (2026-09-30) |
| `${setting:key}` fills from the layers, node over mesh; refused by name when unset; not stray when set | three tests | | `${setting:key}` fills from the layers, node over mesh; refused by name when unset; not stray when set | three tests |
| A context on a seat is cloned from the base the mesh sent; a seat with no base is refused by name | the builder's test | | A context on a seat is cloned from the base the mesh sent; a seat with no base is refused by name | the builder's test |
+1 -1
View File
@@ -234,7 +234,7 @@ counts as a copy and what as a base.
| resource | is | a module may | | resource | is | a module may |
|---|---|---| |---|---|---|
| `directory` | a directory with a mode and an owner | ✅ | | `directory` | a directory with a mode and an owner, **placed by the mesh** under the node's root: `place: "."` is the assignment's own root, `place: "mesh"` the mesh's directory for the module, a pathless one sits beneath the root by its id; a stated path is the placement for data that must stay where it is, and may itself sit beneath a placed one (`${dir:<id>}/…`). Everything else names it as `${dir:<id>}` ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md), [174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)) | ✅ |
| `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) | ✅ | | `file` | literal content, with `${bound:…}`, `${secret:…}`, `${dir:…}`, `${port:…}`, `${machine:…}` and `${setting:…}` filled in — the last an operator's value from the assignment's settings, refused by name when unset ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)) | ✅ |
| `user` | a login | ✅ | | `user` | a login | ✅ |
| `access` | a pre-existing path it may use and must not own | ✅ | | `access` | a pre-existing path it may use and must not own | ✅ |
@@ -184,9 +184,21 @@ placeholders have, not yet the one requirement form below; they are phase 1's fi
*Phase 3, in part (2026-09-30):* every definition's **own** data directory is placed; the conversion *Phase 3, in part (2026-09-30):* every definition's **own** data directory is placed; the conversion
moved no data, proven by resolving both catalogues with the controller's rule and comparing moved no data, proven by resolving both catalogues with the controller's rule and comparing
([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md)). ([issue 119](../../04-ISSUES/119-a-module-definition-decides-where-its-files-live/00-report.md)).
What the mesh writes *for* a module is still placed by the definition What the mesh writes *for* a module was still placed by the definition
([issue 174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)), ([issue 174](../../04-ISSUES/174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md)),
which is the gap this design answered on 2026-09-26 and has not built. the gap this design answered on 2026-09-26; built later the same day: a directory saying
`place: "mesh"` is `<root>/mesh/<module>`, a directory beneath a placed one states its path as
`${dir:<id>}/<rest>` and moves with it, and the same proof — both catalogues resolved and compared —
shows forty-eight definitions naming the paths they named before.
*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
any key. A provider that must tell its consumers an operator's value — a mail server's domain, an
identity provider's issuer — declares it in what it serves as `${setting:<key>}`, and it is refused by
name when nothing sets it. That is the contract half of this design's operator provider in the shape
the placeholder allows: the definition says which values reach which requirement, and nothing else
does. *How it is checked:* the unit tests named in issue 173, and the plan comparison that closed it.
## How a definition reads what was resolved ## How a definition reads what was resolved
@@ -396,7 +408,9 @@ writes (`/var/lib/mesh/<module>`: sealed credentials, composed bindings) need a
module-visible reservation. They do not: a module *requires* a `host-path` and receives a module-visible reservation. They do not: a module *requires* a `host-path` and receives a
location; what the mesh writes for the module is the mesh's plumbing, placed where the mesh location; what the mesh writes for the module is the mesh's plumbing, placed where the mesh
chooses and mounted in — never part of the module's contract. One reservation per chooses and mounted in — never part of the module's contract. One reservation per
requirement, `<root>/<module>/<name>`. requirement, `<root>/<module>/<name>`. *(Built 2026-09-30: the mesh's directory for a module is
`<root>/mesh/<module>`, named in the definition as a placed directory and nowhere as a path —
issue 174.)*
**Resolution happens in the controller, at declaration composition.** The node receives **Resolution happens in the controller, at declaration composition.** The node receives
concrete paths exactly as today — the wire format and the host's apply do not change for concrete paths exactly as today — the wire format and the host's apply do not change for
@@ -153,7 +153,7 @@ not exist.
**What remains is not this record's.** The 232 host paths still in the catalogue are where the mesh **What remains is not this record's.** The 232 host paths still in the catalogue are where the mesh
writes what it makes for a module, under `/var/lib/mesh/<module>`; design 27 says the mesh places writes what it makes for a module, under `/var/lib/mesh/<module>`; design 27 says the mesh places
those itself, and it does not yet. That is [issue 174](../174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md). those itself, and it does not yet. That is [issue 174](../174-the-meshs-own-files-for-a-module-are-placed-by-the-definition/00-report.md). *Placed since later the same day: `place: "mesh"` — issue 174 is resolved.*
The defects this record listed under *where that has already gone wrong* are unchanged by this and The defects this record listed under *where that has already gone wrong* are unchanged by this and
stay in 174's scope where they concern the mesh's files; the operator's shared data stays an access stay in 174's scope where they concern the mesh's files; the operator's shared data stays an access
([ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md)). ([ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md)).
@@ -77,4 +77,4 @@ composes for its route**, read through the route's binding, and an operator's ow
another mesh's forge stays a URL, which the check reports and `names-on-purpose` would declare. The another mesh's forge stays a URL, which the check reports and `names-on-purpose` would declare. The
seven values that remain are declared with their reason — four applications built outside the mesh — seven values that remain are declared with their reason — four applications built outside the mesh —
and are the list that shrinks ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)). and are the list that shrinks ([ADR 0155](../../02-DECISIONS/0155-a-definition-names-no-installation-and-how-that-is-checked.md)).
Registration does not refuse yet; it will when the list has been empty for a release. Registration does not refuse yet; it will when the list has been empty for a release. *2026-09-30, later the same day:* it refuses — the list was empty the day the check landed, and the operator asked for it (mesh-controller PR 175); `module add` and a build's result are refused in the check's words, with the way out, and the build stays recorded.
@@ -1,9 +1,9 @@
--- ---
status: located status: resolved
opened: 2026-09-30 opened: 2026-09-30
located-in: [mesh-controller internal/catalogue/settings.go (settle), mesh-controller internal/catalogue/declaration.go (composed, ownNames)] located-in: [mesh-controller internal/catalogue/settings.go (settle), mesh-controller internal/catalogue/declaration.go (composed, ownNames)]
fixed-by: fixed-by: mesh-controller PR 175 (a setting overrides a declared key and adds none; `${setting:…}` in a served or contributed value); mesh-catalog PR 196 (mail declares its domain, the identity provider its issuer)
amended-design: amended-design: 03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md
--- ---
# 173 — A module's settings reach every fact it contributes, not only the file that asked # 173 — A module's settings reach every fact it contributes, not only the file that asked
@@ -45,3 +45,28 @@ asked for it. Until that form exists, this is the cost of the placeholder being
- What does a consumer do with a served key it did not expect? Today: nothing, silently. A served - What does a consumer do with a served key it did not expect? Today: nothing, silently. A served
map is not checked against what the provision's contract says it carries, because there is no such map is not checked against what the provision's contract says it carries, because there is no such
contract yet. contract yet.
## Resolved, 2026-09-30
**A setting overrides a key a contribution or a served fact declares, and adds none.** A file keeps
taking any key, because a configuration file is where an operator adds things; a contribution and a
served fact are a contract the other side reads, and a setting made for one of the module's files is
no part of it. A key that lands nowhere — no mergeable file, no `${setting:…}` asking for it, no
contribution or served fact declaring it — is named as stray when the node is planned, rather than
dropped.
The first open question is answered the smaller way, and it turned out to be the right one: the two
keys consumers actually read through the leak — the mail provider's `domain`, the identity provider's
`issuer` — are now **declared** by the provider in what it serves, as the operator's value
(`${setting:domain}`, `${setting:issuer}`), filled from the same setting that used to leak and refused
by name when nothing sets it. So the contract says what travels, which is the shape design 27 draws,
without a second key for overrides. The second question stands: a consumer still checks nothing
against a contract, because there is none yet; what it is told is now only what the provider
declared.
*How it was checked:* the plans of all four nodes, under the running controller and the one with the
rule, compared resource by resource — every key that disappears from a contribution is a leaked file
setting or one of the mesh's own words (`expose`, `endpoints`), and nothing a provider reads goes
away; the two served keys were declared before the controller rolled. Unit tests: a setting a route
never declared does not reach the proxy; a served value nothing sets is refused by name; the setting
a served fact asks for is not stray.
@@ -1,9 +1,9 @@
--- ---
status: located status: resolved
opened: 2026-09-30 opened: 2026-09-30
located-in: [mesh-controller internal/catalogue/dir_into.go, mesh-controller internal/catalogue/declaration.go, mesh-catalog modules] located-in: [mesh-controller internal/catalogue/dir_into.go, mesh-controller internal/catalogue/declaration.go, mesh-catalog modules]
fixed-by: fixed-by: mesh-controller PR 175 (`place: "mesh"`, a directory beneath a placed one, the proof test resolving both sides); mesh-catalog PR 197 (48 definitions converted)
amended-design: amended-design: [03-DESIGN/01-to-be/27-a-module-requires-the-mesh-resolves.md, 03-DESIGN/01-to-be/18-building-a-module.md]
--- ---
# 174 — The mesh's own files for a module are placed by the definition, not by the mesh # 174 — The mesh's own files for a module are placed by the definition, not by the mesh
@@ -46,3 +46,21 @@ test that proved 119 can prove again with the rule extended.
- A binding file today lands wherever `binds` says; a module's code reads it from an environment - A binding file today lands wherever `binds` says; a module's code reads it from an environment
variable naming the container path. If the host side is the mesh's, is the container side still the variable naming the container path. If the host side is the mesh's, is the container side still the
definition's to choose? It should be: it is the software's contract. definition's to choose? It should be: it is the software's contract.
## Resolved, 2026-09-30
The word is the one issue 119 introduced, with a second place: a directory saying `place: "mesh"` is
the mesh's directory for the module, `<root>/mesh/<module>`, beside the assignment's own root and
under the same node setting. The mesh's files keep their map shape and the container side of every
mount stays the definition's — only the host side changed, to `${dir:mesh-state}/…`. A directory that
sat beneath the mesh's (a forge's runtime state, a manager's output) states its path as
`${dir:mesh-state}/<rest>` and moves with it; one that sits elsewhere by adoption (the registry's data)
keeps its literal path as the exception it is.
Forty-eight catalogue definitions and the controller's own manifest were converted mechanically. The
proof is the same test that let issue 119 close, now resolving *both* checkouts before comparing,
because the earlier manifest already placed its own directories: resolved on the default root, every
converted definition names exactly the paths it named before. Nothing moved.
Both open questions are answered by keeping what exists: the map stays, the host side is placed; the
container side is the software's contract and stays where the definition says.