Skip to content

A dropped native payload should REFUSE the pack, not warn — arm it on a measured publish wave (#4126 stage 5) #4367

Closed

Description

@rbuergi

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:

  1. a full node-repo-module-pack wave from every repo that publishes modules
    (MeshWeaver.Plugins, MeshWeaver.SocialMedia) completes with zero occurrences of either
    warning in its pack logs — recorded with the run ids, as Arm the registry's own 400: ModulePublish.Validate still accepts a manifest that states no framework identity #3240 recorded them; and
  2. 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.

Also recorded on #4366, and not part of this

  • 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".

🤖 Filed from Claude Code.

Activity

  1. rbuergi commented on Sep 15, 2026

    @rbuergi
    ContributorAuthor

    Ran criterion 1's measurement. Zero occurrences — and the zero does not mean what the criterion needs it to mean.

    What I measured

    repo run packed occurrences of either warning
    MeshWeaver.Plugins 34939850754 (2026-09-15T07:04Z, main) 41 of 41 selected, across 8 legs (leg verdict — 6/1/6/5/6/5/6/6 ok) 0
    MeshWeaver.SocialMedia 34941532528 (2026-09-15T07:24Z, main) 1 (MeshWeaver.Social) 0

    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:

    1. 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.

  2. rbuergi commented on Sep 15, 2026

    @rbuergi
    ContributorAuthor

    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:

    1. 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.

  3. added a commit that references this issue on Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions