Issue 011 — an action is a gate, which the first fix got wrong

The first fix continued past every failure, and the next lab run failed
at the bootstrap: the store did not answer in three minutes and then said
"the database system is shutting down". Carrying on past the readiness
gate had started the broker and the control plane against a machine that
was not ready, and on a small machine that is how a database still
initialising has its memory taken away.

An action is the only shape whose purpose is to make something true
BEFORE the next thing needs it, which is why it is the only one with a
verify. So a failed action stops what follows and nothing else does —
which fixes both this and the hostage problem the issue was opened for.
This commit is contained in:
2026-08-30 20:07:41 +02:00
parent 4a601ceb02
commit 1b49e684e1
@@ -59,7 +59,27 @@ so attempting it produces *more* information than skipping it.
applied. That is a different thing — *this machine could not do it* against *this was never a
declaration* — and they are fixed in different places.
### And one shape still stops what follows, which the first fix got wrong
**An action does.** The first version of this fix continued past everything, and the next lab run
failed at the bootstrap: the store did not answer in three minutes and then said *the database
system is shutting down*. Carrying on past the readiness gate had started the broker and the
control plane against a machine that was not ready, and on a small machine that is how a database
still initialising has its memory taken away.
**An action is the only shape whose purpose is to make something true *before* the next thing needs
it** — which is why it is the only one with a `verify`. The bootstrap is a row of them: the store
answers, then its databases exist, then their schemas, then the broker. Everything else is
independent state: a package that will not install has nothing to do with a file on the other side
of the declaration.
So the rule is: **a failed action stops what follows; nothing else does.** Both faults are fixed by
it, and the report says which happened — *these things failed* and *these things failed and the
rest was never tried* are different machines.
## How this is checked
`internal/apply`: an apply with a resource that cannot succeed still applies the ones after it,
reports every failure, and says how many. Confirmed to fail if the loop stops at the first.
`internal/apply`, three tests: an apply with a resource that cannot succeed still applies the ones
after it; every failure is counted, not just the first; and a failed **action** stops what follows
and says so. Each confirmed to fail when its behaviour is removed — including the last, which fails
if actions stop being treated as gates *and* if everything is treated as one.