Issue 066 resolved: a file and its reader are guarded by a gate, proven by the coupled-pair spike; design 20 says so

This commit is contained in:
2026-09-21 22:04:14 +02:00
parent e993233004
commit c1a10dc3f8
3 changed files with 33 additions and 4 deletions
+21 -1
View File
@@ -5,8 +5,9 @@ code:
- mesh-catalog modules/showcase - mesh-catalog modules/showcase
- mesh-controller internal/builder - mesh-controller internal/builder
- mesh-sdk src - mesh-sdk src
updated: 2026-09-15 updated: 2026-09-21
decisions: decisions:
- 02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md - 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
- 02-DECISIONS/0040-what-a-module-is.md - 02-DECISIONS/0040-what-a-module-is.md
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md - 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
@@ -178,3 +179,22 @@ knows. Putting the Plex client anywhere else is the shared-library disease with
the builder records that as the build edge — but *a mesh that can rebuild a commit and get a the builder records that as the build edge — but *a mesh that can rebuild a commit and get a
different library* is a real change from how everything else here works, and it should be a different library* is a real change from how everything else here works, and it should be a
decision rather than a consequence. decision rather than a consequence.
## A file and the thing that reads it are guarded by a gate
*Written 2026-09-21, from resolving [04-ISSUES/066](../../04-ISSUES/066-a-partly-applied-declaration-leaves-a-mixed-state-with-no-rollback/00-report.md).*
The apply is not a transaction: every resource is attempted, every failure reported, and only a
failed gate stops what follows it ([ADR 0053](../../02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md)).
So a push can half-happen, and the pairing that matters is a configuration file beside the
service that reads it. A module keeps that pair correct by declaring them in this order: the file;
a `run-once` container that validates it; the service, with `restart-on` naming the file. A file
the validator refuses reaches the disk and nothing else — the gate fails, the service after it is
left as it was, and the machine reports the push failed. The service never serves what the
validator refused. That is the grouping, made from what the manifest already has; no primitive
withholds a file from the disk, and a service that reads its file live rather than at start is the
one shape this does not protect, and should not be written.
*How it is checked:* the lab's coupled-pair spike declares exactly this pair, pushes a refused
file, and asserts the file on disk is the new one, the service serves the old one, and the machine
reports the push failed.
@@ -1,9 +1,9 @@
--- ---
status: located status: resolved
opened: 2026-09-20 opened: 2026-09-20
located-in: [mesh-host internal/apply, mesh-controller internal/inventory (node_report)] located-in: [mesh-host internal/apply, mesh-controller internal/inventory (node_report)]
fixed-by: fixed-by: nothing new — a run-once validator before a service that restarts on the file is the grouping (ADR 0053); the lab's coupled-pair spike proves the service never serves what the validator refused; the visible half was issue 065
amended-design: amended-design: 03-DESIGN/01-to-be/20-writing-a-module.md
--- ---
# 066 — A partly applied declaration leaves a mixed state, with no rollback # 066 — A partly applied declaration leaves a mixed state, with no rollback
@@ -18,3 +18,12 @@
the grouping question alone; it closes when a coupled pair that must not be half-applied is the grouping question alone; it closes when a coupled pair that must not be half-applied is
found in a module, and the declaration gains a way to say so — or when enough modules have run found in a module, and the declaration gains a way to say so — or when enough modules have run
that the absence is evidence. The visibility half is done. that the absence is evidence. The visibility half is done.
*2026-09-21, later.* The pairing was made on purpose, in a lab spike: a config file, a run-once
validator, a service that reads the file once at start and restarts on it. The second push changed
the file and made the validator refuse it. Observed: the file on disk was the new one, the service
served the old one, and the machine reported the push failed. The half-state exists — and it is
harmless, because the gate held: the refused configuration never reached the service. The grouping
the report asked for is this order of three resources, made from what a manifest already has. The
one shape it does not protect is a service that reads its file live, which a module should not
write. Resolved; the pattern is in design 20 with the spike as its check.