Repository navigation
Description
The interlock that would actually have saved the documents is at EXECUTE time, not load time
From the session that owns the #2813 incident, and it is a better framing than the one I was working
to:
The damage needed TWO ingredients — stale bytes, and something armed to run them. Stale bytes
sitting unloaded harm nobody. Refusing to execute matters more than refusing to load. A type
that renders read-only pages from unverified bytes is a degraded system; a type that WRITES from
them is the incident.
That is the correct ordering. #2813 makes an adopted build's provenance visible and refuses a
provably-stale adoption at load time — but a Crm/Migration-shaped node that fires on type
registration will still fire, against exactly the assembly whose provenance could not be verified.
Load-time refusal is the second line of defence; this is the first.
What exists after #2813
NodeTypeDefinition.BuildProvenance — Compiled | AdoptedVerified | AdoptedUnverified |
AdoptionRefused — readable on the NodeType's own node and mirrored onto the compile-state
satellite at {type}/_Activity/compile-state, so a control-plane watcher can read it through
GetMeshNodeStream(path) without new plumbing. It is deliberately operational (stripped on export,
preserved from the live node on import) so an authored value cannot forge it.
Today every adoption reads AdoptedUnverified, because nothing writes a source fingerprint into
a bundle yet. That is the honest state, and it is also what makes this issue urgent rather than
theoretical: the predicate a control plane needs is already answerable, and its answer right now is
"unverified" for every adopted type on every mesh.
What is missing
A control plane that refuses to act on a type whose build provenance is not
Compiled or AdoptedVerified — specifically anything that would WRITE. The read path can stay
degraded-but-serving; the write path must not run.
Open questions the design has to answer, none of which I want to guess at:
- Where the interlock sits. The activity control plane is the obvious home, but "a type fires on
registration" spans more than one trigger surface, and a check in only some of them is worse than
none — it would make the gap look closed. - Read vs write. Distinguishing "renders a page" from "writes to the mesh" is the whole value of
the interlock and is not currently a property of a type. - The refusal's shape. It must be visible and actionable, not a silent no-op — and it must not
report as a timeout, which is the mistake Force-release is silently discarded: the compile watcher re-adopts the prebuilt bundle instead of compiling, so there is no verb that forces a build of the live source #2818 documents costing an hour. - What an operator does about it. Until Force-release is silently discarded: the compile watcher re-adopts the prebuilt bundle instead of compiling, so there is no verb that forces a build of the live source #2818 lands there is no verb that forces a rebuild while
a bundle resolves, so an interlock could refuse to run something the operator cannot fix.
Sequencing matters.
Why it is filed rather than folded into #2813
Same standard as #2818: the change is not the hard part, the regression is. Proving "an armed node
does NOT fire against an unverified type, and DOES against a verified one" needs a control-plane
trigger driven end to end on a real mesh, plus a type whose provenance is staged both ways. Shipping
an execute-time gate without that test would put a guard on the write path whose failure mode is
silent — and a guard that quietly stops guarding is the failure this repo keeps paying for.
Related: #2813 (provenance, the load-time half), #2818 (the missing recovery verb — an operator who
sees AdoptedUnverified currently cannot force a rebuild while the bundle resolves).
Activity
rbuergi commentedon Aug 30, 2026
Re-measured on current
main: the premise HOLDS, and the blocker is now a one-line reachability factI have been re-running issues' own measurements before acting tonight, because several had drifted. This one has not. #2813/#2821 landed the provenance machinery since this issue was filed, so it would be reasonable to assume the ground had moved. It has not, and here is the exact reason — worth recording so nobody re-derives it.
The three-way comparison exists and CAN produce
AdoptedVerifiedNodeTypeCompilationHelpers(~1018–1050) implements it properly, with the two inconclusive branches carefully distinguished from the refusing one:The
AdoptedUnverifiedbranches even cite #890's lesson explicitly — "a probe must not answer its scariest branch on its own inability to run" — which is the right instinct and the same one this issue's gate will need.But that branch is unreachable in production, and it is one line
PrebuiltAssemblySeederhas two overloads. The 8-arg one takessourceFingerprintand is the one that could make it reachable. Its only caller in the entire tree is the 7-arg overload everything actually uses:And nothing in
MeshWeaver.PluginCatalogorMeshWeaver.Plugin.Packagingwrites a source fingerprint into a bundle at all.So every adoption takes the line-1022 legacy branch, and
AdoptedVerifiedis dead code today — exactly as this issue states. The machinery is built, tested and inert on the branch that matters, which is the same shape asTestTimeouts(built by #2708, zero adopters until today) andInstanceComboVerifier(#2274 — a verifier nothing calls).What that means for the gate this issue asks for
The predicate cannot be "refuse to execute when provenance is not
AdoptedVerified" until a producer writes the fingerprint, because today that refuses everything. That is not a reason to defer the design — it is the acceptance criterion for it:nullat line 257 and thread it from the manifest). Until this exists, nothing downstream can distinguish verified from unverified.AdoptedUnverifiedas its own outcome, never folded into eitherVerifiedorAdoptionRefused. The three-verdict disciplineComboVerificationalready documents, and the sameNotVerifiable ≠ Greentrap The combo gate exists and is never called — nothing verifies an image against the modules an instance actually runs #2274 is about.Suggest ordering this issue explicitly behind step 1, and stating that a gate shipped before it would either refuse every install or be silently disabled — which is how a safety mechanism becomes decoration. The
nullat line 257 is a good place to leave a pointer to this issue, so the next person to touch it knows what it gates.Merge pull request #2982 from Systemorph/feat/execute-time-provenance…