You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A dropped native payload should REFUSE the pack, not warn — arm it on a measured publish wave (#4126 stage 5) #4367
Split out of #4126, whose other four stages landed in #4366 (a module's native payloads are now
derived by BOTH closure derivations, carried, re-served, and written to disk where ModuleNativeAssets probes).
What is left
ModulePackCommand's deps-closure derivation still emits two warnings rather than refusals,
for the two shapes a bundle genuinely cannot carry:
'<pkg>' declares a native asset at '<path>', which is NOT the layout the module loader probes (exactly runtimes/<rid>/native/<file>) — e.g. runtimes/browser-wasm/nativeassets/<tfm>/<file>;
'<pkg>' declares a RID-specific MANAGED asset the bundle does not carry: <path>
(assetType: "runtime" — the flat closure has one slot per assembly name and no way to choose a
rid at pack time).
#4126's ask was that these become refusals "once the bundle can carry them, so a native dependency
can never again be dropped silently". The bundle now can, for the probed layout — so the
precondition is met for the shapes it carries, and what remains is the decision about the shapes it
still cannot.
🚨 Why this was NOT armed in #4366, and what it needs
A module tripping either warning today still has the host-supplies-it fallback, and arming a
refusal before a full publish wave has been measured clean takes the fleet's publishes down
instead of the drops. That is the #3240 shape exactly: ModulePublish.Validate's
framework-identity refusal was deliberately held until BOTH halves of a stated criterion were
measured (every producing repo past the fix, and a completed publish wave from each stating an
identity), because arming it early would have failed 34 of 34 bundles.
So the criterion here is the same shape, and it is a MEASUREMENT, not a judgement:
for any occurrence that does exist, the module is either moved onto the probed layout
(--with-native) or flattened into the module folder and named with --with <file name> — the
loader's last probe — before the refusal is armed, so the refusal never has a live victim.
Only then flip the two warnings.Add(...) in DepsClosure.Derive to a non-zero exit in ModulePackCommand, with a test per shape and a negative control per shape.
the container lane carries ONE rid's engine, because it resolves against ONE image (the SDK lane,
deriving from a portable publish, carries every rid the package ships);
the module-libraries shelf's native half (DeclaredNativesOf / NativeFileFor) is built and
unexercised — no curated package declares a native yet.
Conserved on Doc/Architecture/Modules → "LANDED, RE-SERVED, and derived by BOTH lanes".
Grepped for the two literals in the issue — which is NOT the layout the module loader probes and declares a RID-specific MANAGED asset the bundle does not carry — across every Module bundle (…) job log in both runs.
🚨 The denominator, which is the whole result
Of the 41 Plugins legs' pack logs, 38 print container path: 0 native payload(s) declared. Not one module in either wave declares a native payload at all.
So the zero has the second of its two causes: nothing exercised the path. It is not evidence that a native-carrying module packs cleanly — it is evidence that the fleet currently has no native-carrying module. Arming the two warnings.Add(...) on this reading would arm a refusal with no positive control anywhere in the system: green by construction, and untested the first time a module ever declares one.
That is the same family as the defect this issue's parent is about, one level up: a clean sweep whose cleanliness comes from an empty population.
Two things this changes about the criterion
1. "A full wave" is not what ran. Plugins selected 41 bundles; the repo carries 72. node-repo-module-pack selects the affected set, so a push-triggered run cannot satisfy "a full wave" by construction — the daily schedule run is the one that can, and that is the run to record here. I did not find one carrying all 72 in the window I looked at.
2. The criterion as written cannot distinguish the two zeros. I'd suggest it needs a third clause, because clauses 1 and 2 are both satisfied today and arming on them would still be wrong:
a positive control per shape — a synthetic package declaring a native asset at runtimes/<rid>/nativeassets/<tfm>/<file>, and one declaring a RID-specific managed asset — each watched producing the refusal, and each watched NOT producing it with the arming reverted.
That is the clause that makes the refusal mean something on the day a real module first trips it, which on this evidence has not happened yet and may be a while.
Happy to build the two controls if that reading is agreed. I have deliberately not flipped the warnings: criterion 2's "so the refusal never has a live victim" is satisfied, but a gate with no victim and no control is not armed, it is merely present.
Correction to my own comment above: I proposed a third clause that is largely already satisfied, and I should have looked before proposing it. Checking the tree rather than reasoning from the issue:
test/MeshWeaver.Graph.Test/DepsClosureTest.cs already carries a positive control for both shapes, and they pass (14/14, dotnet test --filter DepsClosureTest):
shape
control
a native at a non-probed layout
ANativeAtALayoutTheLOADERDoesNotProbe_IsNamed_AndNotCarried, plus the [Theory]OnlyTheEXACTProbedLayoutIsCarried_TheRestAreNamed(string declared) sweeping the variants
a RID-specific MANAGED asset
ARidSpecificMANAGEDAsset_IsStillDropped_AndIsNAMED — and it is a real two-sided control: it asserts the asset does not reach result.Natives (it would if the derivation keyed on runtimeTargets rather than the asset TYPE), that the warning names the exact path, and that the RID-agnostic copy still rides
There is even a control on the message's usefulness: TheGuidanceInADropWarningIsSomethingACallerCanACTUALLYDo.
So my clause 3 was an over-ask. What it should have said, precisely, is the part that is genuinely missing:
when the warnings are flipped to a refusal, extend these existing controls to assert the non-zero exit — each shape watched producing the refusal, and watched NOT producing it with the arming reverted. The DETECTION is already pinned; the REFUSAL cannot be pinned until it exists.
That leaves #4367's real precondition as clause 1 alone — a full node-repo-module-pack wave from each publishing repo with zero occurrences — and my measurement above stands on that: 41 Plugins modules + 1 SocialMedia module, 0 occurrences, but 38 of 41 print 0 native payload(s) declared, so the population that could trip either warning is empty. Arming on that would still give a refusal no live module exercises — which is an argument for arming it (no victim) as much as against (no proof), and that is the judgement the issue reserves.
Activity
rbuergi commentedon Sep 15, 2026
Ran criterion 1's measurement. Zero occurrences — and the zero does not mean what the criterion needs it to mean.
What I measured
MeshWeaver.Pluginsmain)leg verdict — 6/1/6/5/6/5/6/6 ok)MeshWeaver.SocialMediamain)MeshWeaver.Social)Grepped for the two literals in the issue —
which is NOT the layout the module loader probesanddeclares a RID-specific MANAGED asset the bundle does not carry— across everyModule bundle (…)job log in both runs.🚨 The denominator, which is the whole result
Of the 41 Plugins legs' pack logs, 38 print
container path: 0 native payload(s) declared. Not one module in either wave declares a native payload at all.So the zero has the second of its two causes: nothing exercised the path. It is not evidence that a native-carrying module packs cleanly — it is evidence that the fleet currently has no native-carrying module. Arming the two
warnings.Add(...)on this reading would arm a refusal with no positive control anywhere in the system: green by construction, and untested the first time a module ever declares one.That is the same family as the defect this issue's parent is about, one level up: a clean sweep whose cleanliness comes from an empty population.
Two things this changes about the criterion
1. "A full wave" is not what ran. Plugins selected 41 bundles; the repo carries 72.
node-repo-module-packselects the affected set, so a push-triggered run cannot satisfy "a full wave" by construction — the dailyschedulerun is the one that can, and that is the run to record here. I did not find one carrying all 72 in the window I looked at.2. The criterion as written cannot distinguish the two zeros. I'd suggest it needs a third clause, because clauses 1 and 2 are both satisfied today and arming on them would still be wrong:
That is the clause that makes the refusal mean something on the day a real module first trips it, which on this evidence has not happened yet and may be a while.
Happy to build the two controls if that reading is agreed. I have deliberately not flipped the warnings: criterion 2's "so the refusal never has a live victim" is satisfied, but a gate with no victim and no control is not armed, it is merely present.
rbuergi commentedon Sep 15, 2026
Correction to my own comment above: I proposed a third clause that is largely already satisfied, and I should have looked before proposing it. Checking the tree rather than reasoning from the issue:
test/MeshWeaver.Graph.Test/DepsClosureTest.csalready carries a positive control for both shapes, and they pass (14/14,dotnet test --filter DepsClosureTest):ANativeAtALayoutTheLOADERDoesNotProbe_IsNamed_AndNotCarried, plus the[Theory]OnlyTheEXACTProbedLayoutIsCarried_TheRestAreNamed(string declared)sweeping the variantsARidSpecificMANAGEDAsset_IsStillDropped_AndIsNAMED— and it is a real two-sided control: it asserts the asset does not reachresult.Natives(it would if the derivation keyed onruntimeTargetsrather than the asset TYPE), that the warning names the exact path, and that the RID-agnostic copy still ridesThere is even a control on the message's usefulness:
TheGuidanceInADropWarningIsSomethingACallerCanACTUALLYDo.So my clause 3 was an over-ask. What it should have said, precisely, is the part that is genuinely missing:
That leaves #4367's real precondition as clause 1 alone — a full
node-repo-module-packwave from each publishing repo with zero occurrences — and my measurement above stands on that: 41 Plugins modules + 1 SocialMedia module, 0 occurrences, but 38 of 41 print0 native payload(s) declared, so the population that could trip either warning is empty. Arming on that would still give a refusal no live module exercises — which is an argument for arming it (no victim) as much as against (no proof), and that is the judgement the issue reserves.fix(#4367): the refusal's remedy is reachable in the shared lane, and…