Repository navigation
Description
Why this belongs in core, not Plugins
The module that broke — MeshWeaver.Blazor.Views, packagePath: Plugins/DefaultViews — is a Plugins artifact, but the defect is in the loader's decision about what to run, not in the bundle. Every symbol involved is core: ComputeEffectiveModuleEntries (src/MeshWeaver.PluginCatalog/ModuleActivation.cs), the missing provenance line (ModuleLoadReport.cs), the pin path in the evidence (ModuleGenerationPin.cs), and the probe that wrote the .unloadable marker (src/MeshWeaver.Mesh.Contract/ModulePlatformLink.cs). So are the policy and the issues it rests on: #3538, #3552, #3649, #3652, #3661.
A Plugins-side fix would mean republishing a compatible generation — which clears this instance and leaves the next one to fail the same silent way.
Summary
On a portal running an image built from main after #3661, the type-level link probe refused MeshWeaver.Blazor.Views@96af4c59, wrote activation.d/MeshWeaver.Blazor.Views.unloadable naming the exact missing types — and the next boot loaded that generation anyway. The image ships its own current, loadable copy of the same module at /app/MeshWeaver.Blazor.Views.dll, named in Modules:Assemblies, and it was not used.
The user-visible result is that every markdown surface renders the control's ToString():
MarkdownControl { Id = , Style = , Readonly = , Skins = …, ModuleName = MeshWeaver.Layout,
ApiVersion = 1.0.0, Markdown = ### Welcome back, … }
No exception, no [FallbackModule] line, nothing connecting the symptom to the module.
What the policy promises
Doc/Architecture/ModuleAdoptionPolicy (#3652), R1 continuity: "an installation runs the newest module version that loads; if nothing newer ships for its platform, it keeps the one it has." R2: loadability is the link probe and the load itself.
#3649 states the mechanism this rests on:
"When a newer generation is landed but cannot load on the running platform (the link probe refuses it before load, or the load faults), the boot refuses it and the module is absent unless the image ships a baseline copy —
ComputeEffectiveModuleEntrieskeeps the image baseline (#2548) but a Store-only module has none."
Blazor.Views is not a Store-only module. It has a baseline. The documented path exists and did not run.
Measured
Platform: a locally built ci line (the previous day's floor messages on this same install read this deployment runs 3.0.0-ci.0). Image built 2026-09-08T06:31:48Z from a main synced 06:12:10Z; git merge-base --is-ancestor 13d324992 main → 0, so the image contains #3661.
| time (UTC) | event |
|---|---|
| 06:12:10 | core main synced, bringing #3661 |
| 06:31:48 | image built — includes #3661 |
| 06:32:25 | link probe writes activation.d/MeshWeaver.Blazor.Views.unloadable |
| 06:45:58 | pod starts |
| 06:46:00 | that pod loads the refused generation |
The marker, verbatim:
{
"generation": "MeshWeaver.Blazor.Views@96af4c59",
"frameworkMvid": "69da7522172d4e22863a9f4fba2b989f",
"reason": "Module 'MeshWeaver.Blazor.Views' was built against a platform this deployment is NOT
running and CANNOT be loaded here: it references MeshWeaver.Graph.AnchoredComment,
MeshWeaver.Graph.ChangeProjection, MeshWeaver.Graph.ChangeRendering,
MeshWeaver.Graph.CollaborativeRenderer, MeshWeaver.Graph.CommentRendering, which the copy this
process would bind to does not have. Loading it anyway throws TypeLoadException at the first
render that touches it — every render, forever, with nothing connecting it to the install. …",
"measuredAt": "2026-09-08T06:32:25.5819047+00:00"
}What loaded:
[ModuleLoad] MeshWeaver.Blazor.Views ← /tmp/meshweaver-pinned-modules/1-f5c08d69/
MeshWeaver.Blazor.Views@96af4c59/MeshWeaver.Blazor.Views.dll
(source=store, mvid=a87ef06f, written=2026-08-27 14:00:29Z)
What was available and unused — same module, current, in the baseline:
/app/MeshWeaver.Blazor.Views.dll mvid=4409d42e, written=2026-09-08 06:31:47Z
grep -i 'FallbackModule|runs its previous generation|cannot load here' over the full boot log returns nothing. Only one generation of this module existed on the volume, so #3661's step-2 retry had no predecessor to fall back to.
The reporting install is no longer in this state. The offending generation was deleted to restore the portal, and the image has since been rebuilt (2026-09-08T08:15:36Z); Blazor.Views now loads correctly from the baseline. The measurements above are a record of that boot, not a live repro. Reproducing it needs a store generation whose links do not resolve, present as the module's only generation, on a module that also has an appsettings baseline.
Not the same as #3656 / Modules:VersionStrictness
5d09971d6 (2026-09-08) introduces PrebuiltAdoptionPolicy (Exact | Family | Minimum) with a per-assembly ModulePlatformLink check. That is the prebuilt / NodeType-assembly lane. This report is about module activation at boot — the same link-probe machinery, a different consumer — so it is not covered by that change.
Why it is not local-only
Nothing here depends on self-registry or on a local cluster. Any deployment holding a store generation linked against a different MeshWeaver.Graph — the exact situation #3538 describes — gets a silently broken renderer for as long as that generation stays adopted.
What I could not determine
Behaviour only; I have not read the boot path. The plausible reading is that #3661 replaced refuse-and-keep-baseline with retry-the-previous-generation, and the baseline leg was lost — so the fallback chain has one link where R1 needs two: previous generation, then the appsettings baseline. That is inference, not a root cause I have verified.
Workaround, and one that does not work
Deleting the landed generation makes the entry unusable, so the baseline takes the slot and rendering is restored:
kubectl -n <ns> exec deploy/<portal> -- rm -rf "/data/modules/MeshWeaver.Blazor.Views@96af4c59"
kubectl -n <ns> rollout restart deploy/<portal>
Setting "enabled": false in activation.d/MeshWeaver.Blazor.Views.json does not work — that flag governs set-pinning, not boot loading. It survived a restart and changed nothing.
Suggested acceptance
A boot whose link probe refuses the adopted generation, with no previous generation and a loadable Modules:Assemblies baseline present, loads the baseline and logs why.
Activity
rbuergi commentedon Sep 8, 2026
Resolved by #3748, merged into main as
f6b703105acb4fc50992c07be001fa8476335d51.The fix preserves the displaced image baseline through boot composition and tries it after the current and previous landed generations fail. A successful baseline fallback is recorded as
@imageand reported with the refused generation's reason. The regression coverage includes precisely this report's case: a single refused landed generation, no predecessor, and a loadable image copy, plus controls for an unloadable or missing baseline and the previous-generation ordering.Verified the merged implementation and CI run 34260741373: completed/success, with all six test shards executed successfully. Closing the code defect as completed; this audit did not perform a new production rollout. The publication-composition defect remains separately tracked in #3732.