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
[ModuleLoad] cannot name the commit a bundle was built from, nor the assembly a NodeType's content type resolved to — a stale adopted build and a stale registry shelf are indistinguishable from the consumer #4158
[ModuleLoad] prints, per module: the path, source=, mvid= and written=. Two questions that decided a night of production diagnosis are NOT on that line, and nothing else answers them from the consumer:
Which commit the loaded bundle was built from. On 2026-09-10 (MeshWeaver.Plugins#1585) the loaded MeshWeaver.AI@a7971ab5… had the newest generation, the newest written=, and types that predated two merged PRs. mvid and written said "newest"; only the served JSON schema said "old". The reading "the registry serves stale bytes" was written down, acted on (three RefreshModules, two restarts), and later falsified by /health (bundle_adoption: … AI: FrameworkDeclined — the bundle was never adopted; the previously adopted build kept serving under the same-MAJOR rule of fix(compile): a NodeType never errors because of delivery — last build keeps serving, keyed on module-version compatibility (#3583) #3844).
Record the built-from commit (and the framework identity it was compiled against) in the bundle manifest at pack time, and print both on [ModuleLoad] beside mvid/written — so "the generation is newest" and "its types are current" stop being read as one fact.
Make the per-NodeType diagnostics name the assembly (path + mvid) the content type resolved to — the thing /schema/<Type> is actually serialising from — so a stale adopted build and a stale registry shelf can be told apart from the consumer.
Both are additive diagnostics; neither changes what an installation runs. Measured context and the 05:52Z falsification: MeshWeaver.Plugins#1585 (closed on the portal outcome). Adjacent: #3583 (adoption vs sweep count), #3732 (two builds of one assembly name), #3844 (stale-but-serving rule).
2026-09-13 check — precisely stated; half the data exists one level up
On main (99a3600de): ModuleLoadReport.cs:120 prints source=, mvid=, written= and nothing else per module — the built-from commit is not on the line. It DOES exist per publication: SealedPublicationIndex.SourceCommit reads source-commit.txt (src/MeshWeaver.Hosting/SealedPublicationIndex.cs:14), so the first ask is a plumbing move (publication → bundle manifest → load line), not new data. The second ask (which assembly a NodeType's content type resolved to) has no source today. Both additive; left open, no work in this pass.
Ask 1 is built (#4195, draft, green) — ask 2 still has no source
Ask 1.[ModuleLoad] now carries built-from=<commit> and framework=<identity> beside mvid=/written=. framework= needed no new data — ModuleActivationEntry.FrameworkMvid has been
recorded since #3154 and was simply never printed. built-from= is plumbed end to end, additively,
public surface growing by init properties only: module-pack --source-commit → the bundle manifest's sourceCommit → BundleReader.Manifest.SourceCommit → ModulePublish.Accepted / PluginBundleClient.LandFromBundle → ModuleLandingService → ModuleActivationEntry.SourceCommit
(and PreviousSourceCommit, so a fallback generation names its own build rather than the head's).
Absence prints (unrecorded) and is never derived from the version, the generation, the MVID or the
path — proved able to fail, not just asserted: with the fix in place, injecting entry.SourceCommit ?? entry.Version turns the negative control red with Expected: "(unrecorded)" / Actual: "1.4.0".
Two hops still read (unrecorded), both deliberate and both now written down in Doc/Architecture/Modules → "The file is not the source" (committed with the change, not left in
this thread):
The CI producer hop.node-repo-module-pack.yml does not pass --source-commit, for two
measured reasons. A satellite pins the lane at @main while the pack tool is published from
the resolved sealedplatform-ref, which lags main — an unconditional new flag would exit-2
(unrecognised argument) every satellite's pack jobs for that whole window. And github.sha is
the wrong value anyway: the lane reuses content-addressed module builds from earlier runs
(module-build-ledger.py), so the run's commit is not the commit that built those bytes. Stamping
it would put a newer commit on older bytes — this issue's own defect with one more field to be
misled by. The lane must take the commit from the build's provenance.
The registry's re-pack. A consumer landing a producer-packed bundle records the commit, and a
registry's own [ModuleLoad] names it (its shelf entry is written by ShelveModule). But when a
registry re-serves a shelved module it composes a fresh manifest, and that composer is in the
MeshWeaver.Plugins route rather than core src/ — one additive line there, enabled by feat: [ModuleLoad] names the commit a module was built from, and what it was built against (#4158 ask 1) #4195.
Ask 2 — which assembly a NodeType's content type actually resolved to — has no source today, as
the 2026-09-13 check said, and #4195 does not invent one. Leaving this issue open on it; #4195 uses Refs, not Fixes.
Ask 2 is DONE — the source exists, and it is a property of the BYTES. Merged in #4236.
The brief on this was: find whether a correct source exists — the bundle's sealed provenance, SealedPublicationIndex.SourceCommit, the assembly's MVID, anything that is a property of the bytes
rather than of the run. One does, and it was already in hand and being discarded.
Where it was
ContentDegradationRegistry.IsResolvable resolves the content Type and throws it away with out _ (ContentDegradationRegistry.cs:113-114).
DataExtensions.GenerateJsonSchema holds the very Type it is about to serialise from
(DataExtensions.cs:3840) and passes on only CollectionName.
From a resolved Type, three coordinates are properties of the loaded bytes and cannot be stale:
field
source
mvid
Assembly.ManifestModule.ModuleVersionId — stamped INTO the bytes by the compiler
assembly
Assembly.Location — the file this process actually loaded
collectible
Assembly.IsCollectible — a runtime compile has no file and a collectible context; a shipped module has both the other way round
That last pair is the #3732 discriminator: it is what separates two builds of ONE assembly name in
one process, which is the state in which a stale ADOPTED build and a stale registry SHELF read
identically from a consumer — the night this issue was filed about.
What shipped
get_diagnostics carries a contentType block on every branch (Ok, Error, Compiling,
Unavailable, Foreign, Unknown, and the degraded-JsonElement path, which needs it most), read from
the live IMeshContentTypeRegistry at the moment of the call. Nothing gates on it — a reading, like
ask 1's built-from=.
Watched failing on unmodified main before the fix, both cases, with the payload quoted in #4236.
Three rules, each held by a case that fails without it:
Absence is PRINTED, never inferred.unresolved (asked, nothing registered) and not-asked
(no registry in this process) are two different sentences; the unresolved one names the
consequence — its views render empty.
Every key is written, always. Measured on the first attempt: the hub's options stamp a
polymorphic $type on a NESTED object (putting <>f__AnonymousType156[…]into an envelope people read) and their ignore condition DROPS null and default members —typeName, assembly, mvidand afalsecollectibleall vanished. The block is now an explicitJsonObject`.
It is read from the TYPE, never from the path. The control loads the test assembly's own
bytes into a collectible AssemblyLoadContext, so the same full name is present twice; the two
readings must differ.
The two residues from ask 1 are unchanged, and both are still (unrecorded)
Neither is fixed here and neither is new — restated so this issue's closure does not imply they are:
The CI producer hop.node-repo-module-pack.yml still does not pass --source-commit,
because github.sha is the wrong value: the lane reuses content-addressed module builds from
earlier runs, so the run's commit is not the commit that built those bytes. The lane must take it
from the BUILD's provenance. (The equivalent one level up — reading a publication's real source
from the bake's own receipt instead of head_sha — is now an option on the canonical resolver, ci: the publication's OWN source is a SECOND option on the canonical resolver (#4171) #4238.)
Activity
rbuergi commentedon Sep 13, 2026
2026-09-13 check — precisely stated; half the data exists one level up
On
main(99a3600de):ModuleLoadReport.cs:120printssource=,mvid=,written=and nothing else per module — the built-from commit is not on the line. It DOES exist per publication:SealedPublicationIndex.SourceCommitreadssource-commit.txt(src/MeshWeaver.Hosting/SealedPublicationIndex.cs:14), so the first ask is a plumbing move (publication → bundle manifest → load line), not new data. The second ask (which assembly a NodeType's content type resolved to) has no source today. Both additive; left open, no work in this pass.rbuergi commentedon Sep 13, 2026
Ask 1 is built (#4195, draft, green) — ask 2 still has no source
Ask 1.
[ModuleLoad]now carriesbuilt-from=<commit>andframework=<identity>besidemvid=/written=.framework=needed no new data —ModuleActivationEntry.FrameworkMvidhas beenrecorded since #3154 and was simply never printed.
built-from=is plumbed end to end, additively,public surface growing by init properties only:
module-pack --source-commit→ the bundle manifest'ssourceCommit→BundleReader.Manifest.SourceCommit→ModulePublish.Accepted/PluginBundleClient.LandFromBundle→ModuleLandingService→ModuleActivationEntry.SourceCommit(and
PreviousSourceCommit, so a fallback generation names its own build rather than the head's).Absence prints
(unrecorded)and is never derived from the version, the generation, the MVID or thepath — proved able to fail, not just asserted: with the fix in place, injecting
entry.SourceCommit ?? entry.Versionturns the negative control red withExpected: "(unrecorded)" / Actual: "1.4.0".Two hops still read
(unrecorded), both deliberate and both now written down inDoc/Architecture/Modules→ "The file is not the source" (committed with the change, not left inthis thread):
node-repo-module-pack.ymldoes not pass--source-commit, for twomeasured reasons. A satellite pins the lane at
@mainwhile the pack tool is published fromthe resolved sealed
platform-ref, which lagsmain— an unconditional new flag would exit-2(
unrecognised argument) every satellite's pack jobs for that whole window. Andgithub.shaisthe wrong value anyway: the lane reuses content-addressed module builds from earlier runs
(
module-build-ledger.py), so the run's commit is not the commit that built those bytes. Stampingit would put a newer commit on older bytes — this issue's own defect with one more field to be
misled by. The lane must take the commit from the build's provenance.
registry's own
[ModuleLoad]names it (its shelf entry is written byShelveModule). But when aregistry re-serves a shelved module it composes a fresh manifest, and that composer is in the
MeshWeaver.Plugins route rather than core
src/— one additive line there, enabled by feat: [ModuleLoad] names the commit a module was built from, and what it was built against (#4158 ask 1) #4195.Ask 2 — which assembly a NodeType's content type actually resolved to — has no source today, as
the 2026-09-13 check said, and #4195 does not invent one. Leaving this issue open on it; #4195 uses
Refs, notFixes.🤖 Generated with Claude Code
ci: the publication's OWN source is a SECOND option on the canonical …
rbuergi commentedon Sep 13, 2026
Ask 2 is DONE — the source exists, and it is a property of the BYTES. Merged in #4236.
The brief on this was: find whether a correct source exists — the bundle's sealed provenance,
SealedPublicationIndex.SourceCommit, the assembly's MVID, anything that is a property of the bytesrather than of the run. One does, and it was already in hand and being discarded.
Where it was
ContentDegradationRegistry.IsResolvableresolves the contentTypeand throws it away without _(ContentDegradationRegistry.cs:113-114).DataExtensions.GenerateJsonSchemaholds the veryTypeit is about to serialise from(
DataExtensions.cs:3840) and passes on onlyCollectionName.From a resolved
Type, three coordinates are properties of the loaded bytes and cannot be stale:mvidAssembly.ManifestModule.ModuleVersionId— stamped INTO the bytes by the compilerassemblyAssembly.Location— the file this process actually loadedcollectibleAssembly.IsCollectible— a runtime compile has no file and a collectible context; a shipped module has both the other way roundThat last pair is the #3732 discriminator: it is what separates two builds of ONE assembly name in
one process, which is the state in which a stale ADOPTED build and a stale registry SHELF read
identically from a consumer — the night this issue was filed about.
What shipped
get_diagnosticscarries acontentTypeblock on every branch (Ok, Error, Compiling,Unavailable, Foreign, Unknown, and the degraded-
JsonElementpath, which needs it most), read fromthe live
IMeshContentTypeRegistryat the moment of the call. Nothing gates on it — a reading, likeask 1's
built-from=.Watched failing on unmodified
mainbefore the fix, both cases, with the payload quoted in #4236.Three rules, each held by a case that fails without it:
unresolved(asked, nothing registered) andnot-asked(no registry in this process) are two different sentences; the
unresolvedone names theconsequence — its views render empty.
polymorphic
$typeon a NESTED object (putting<>f__AnonymousType156[…]into an envelope people read) and their ignore condition DROPS null and default members —typeName,assembly,mvidand afalsecollectibleall vanished. The block is now an explicitJsonObject`.bytes into a collectible
AssemblyLoadContext, so the same full name is present twice; the tworeadings must differ.
The two residues from ask 1 are unchanged, and both are still
(unrecorded)Neither is fixed here and neither is new — restated so this issue's closure does not imply they are:
node-repo-module-pack.ymlstill does not pass--source-commit,because
github.shais the wrong value: the lane reuses content-addressed module builds fromearlier runs, so the run's commit is not the commit that built those bytes. The lane must take it
from the BUILD's provenance. (The equivalent one level up — reading a publication's real source
from the bake's own receipt instead of
head_sha— is now an option on the canonical resolver,ci: the publication's OWN source is a SECOND option on the canonical resolver (#4171) #4238.)
one additive line there, enabled by feat: [ModuleLoad] names the commit a module was built from, and what it was built against (#4158 ask 1) #4195.
Both are written up in
Doc/Architecture/Modules→ "The file is not the source"; ask 2's half isbeside it as "…and the RECORD is not the resolution".
Closing: ask 1 shipped in #4195, ask 2 in #4236.
🤖 Generated with Claude Code