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
Module closure: should a sibling that is ITSELF another package's declared module ride into a second bundle? #3221
Raised from Systemorph/MeshWeaver.Plugins#1271, which closes as a pointer to this one — the decision belongs to the core pack lane (module-owned-platform.sh / ModuleClosureAccounting), not to a satellite.
The gap
BakeHost.ShippedByHostProblem (#3175) refuses a module that is composed with --module AND shipped by the platform host — it checks the host's /app directory and its surface manifest. It does not check whether a second module bundle carries a copy of the same assembly name.
That case is live. Since Plugins#1268 removed MeshWeaver.Markdown.Collaboration.dll from the portal image's app closure, the AI bundle rides a copy of it as a module-owned sibling (ModuleClosureAccounting: a module-owned MeshWeaver.* sibling rides). So the assembly has two module-side producers:
Essentials declares MeshWeaver.Markdown.Collaboration as its module (Essentials/index.json);
AI carries a copy of the same DLL as a riding sibling.
The Chat bundle already has this exact shape with MeshWeaver.AI.dll today, so this is a pre-existing accepted pattern, not a regression from #1268 — nothing is known to be broken by it right now.
Why it is worth deciding rather than leaving implicit
The failure mode #3175 exists to prevent is a dependency-record mismatch at adoption: records naming one build (mvid:…) while the consumer resolves another (ref:…, or a different mvid:), which DECLINES every NodeType binding the name. The guard closes the host-vs-module case. The module-vs-module case is closed only by the accident that both copies happen to be built from the same source in the same wave — a property of the current CD wave, not an invariant anyone asserts.
So the question is not "is it broken today" (it is not) but "what closes it when the wave stops being uniform" — which is exactly the shape of failure #3175 was written for.
The decision
Should a sibling that is itself another package's declared module ride into a second bundle, or should the closure rule exclude declared modules and let the landing resolve them from their owning package?
If the answer is "it should not ride", the guard extension is the same shape as #3175: refuse a bundle that carries an assembly another package declares as its module. That is a guard which can refuse an existing caller (Chat/MeshWeaver.AI today), so by the working agreement it lands with its caller sweep or in grace mode — never on its own.
Surfaced while landing Plugins#1268 (Closes Plugins#1262). No action taken there.
The prediction in this issue came true, and it is now blocking production
Reopening: #3242 closed this by making the verdict count every copy — it did not decide whether
the copy should ride. That detector is now firing on memex, and it is why nothing rolls.
Measured 2026-09-08, Admin/UpdatePolicy on memex (memex.systemorph.com), holding 3.0.0-ci.8053:
Collaboration: 'Collaboration' (Collaboration/Review) binds module MeshWeaver.Markdown.Collaboration,
which the set sealed for framework identity sa927163874eb5a136826a1cf8cd91ffb carries as TWO DIFFERENT
builds (module MeshWeaver.Markdown.Collaboration: source 'plugins' sealed
mvid:0b7040585b9e4c22b1457de2b6e86189 as a sibling riding 'ai.module.nupkg', source 'plugins' sealed
mvid:6fe8369aa39c4ce0b3203c0e304fe5bc as the declared module of 'essentials.module.nupkg') — the
loader keeps whichever it sees first, so the other's NodeTypes are declined at adoption;
the set is inconsistent and nothing rolls
Two different MVIDs, exactly as written here:
"What closes the module-vs-module case today is the accident that both copies happen to be
built from the same source in the same wave — a property of the current CD wave, not an invariant
anyone asserts. The failure #3175 exists to prevent … is exactly what a non-uniform wave would
produce."
The wave stopped being uniform. The detector is behaving correctly; the cause is untouched.
Why this is not just one more held candidate
It is set-level. The other two reasons in the same hold are per-module and clear on their own —
the rc-line floors (core #3554 + Plugins #1447, merged, awaiting a sealed set) and five bundles with
no sealed bake. This one refuses the whole set regardless: fixing the floors and the bakes still
leaves nothing rolls. So it gates every roll of memex, including the roll that would deliver any
other fix.
The decision this issue posed, now forced
Should a sibling that is itself another package's declared module ride into a second bundle, or
should the closure rule exclude declared modules and let the landing resolve them from their owning
package?
The concrete instance: src/MeshWeaver.AI/MeshWeaver.AI.csproj carries
<!-- CollaborationPlugin posts CreateCommentRequest / CreateCommentResponse, which live in the collaboration module. The reference used to resolve against the platform's copy of this assembly; the assembly moved repos but deliberately KEPT ITS NAME … (#2370). -->
<ProjectReferenceInclude="../MeshWeaver.Markdown.Collaboration/MeshWeaver.Markdown.Collaboration.csproj" />
so ModuleClosureAccounting rides a copy into ai.module.nupkg, while Essentials/index.json
declares the same assembly as its module.
Three shapes, in increasing order of blast radius:
Exclude declared modules from the closure (this issue's own suggestion) — the sibling stops
riding and the landing resolves it from Essentials. Fixes the class. Needs the caller sweep this
issue already flags (Chat rides MeshWeaver.AI.dll the same way), so grace mode.
Split the contract.CreateCommentRequest / CreateCommentResponse move to a
platform-shipped contract assembly; MeshWeaver.AI references that and rides nothing — the MeshWeaver.Speech.Contract / MeshWeaver.Observability.Contract precedent. Durable, but it
moves public message types across a repo boundary, so it meets the pair and binary-compat gates.
Make the wave uniform again — republish so both copies share an MVID. Unblocks today and
restores the accident this issue exists to stop relying on.
3 is the stopgap; 1 or 2 is the fix. I have not taken any of them — the choice is a pack-lane
decision and it can refuse existing callers, which by the working agreement lands with a sweep.
Found while diagnosing a separate production symptom (a node opening on the wrong default area,
fixed in #3694); this one is unowned and is the roll blocker.
The question is now answered empirically — by the exact pair this issue names
This issue asks whether a sibling that is itself another package's declared module should ride
into a second bundle, and names the live instance: Essentials declares MeshWeaver.Markdown.Collaboration as its module, while AI carries a copy as a riding sibling.
Production has answered it. Read live from Admin/UpdatePolicy on memex
(lastCheckedAt: 2026-09-09T07:39:42Z), inside heldReason — the same two producers, by name:
Collaboration: 'Collaboration' (Collaboration/Review) binds module
MeshWeaver.Markdown.Collaboration, which the set sealed for framework identity
sf456af88e1d9c07c0d9d75de97b0800c carries as TWO DIFFERENT builds
(source 'plugins' sealed mvid:0ab3db43f03745e49d2145e958b673c0
as a sibling riding 'ai.module.nupkg',
source 'plugins' sealed mvid:a1e559cf1dbc49dab6184554d4aef717
as the declared module of 'essentials.module.nupkg')
— the loader keeps whichever it sees first, so the other's NodeTypes are declined at
adoption; the set is inconsistent and nothing rolls
So the answer to the title question is no, and the reason is not aesthetic:
The loader cannot choose. "keeps whichever it sees first" — the outcome depends on scan
order, so which of the two builds wins is not a property of the publication.
The loser's NodeTypes are declined at adoption, which is what MeshWeaver#3768 measures on the
staff portal: bundle_adoption: 0 of 25 adopted, every entry FrameworkDeclined, and seven node
types unable to type their content — user-visibly, pages render empty.
It blocks the release channel.the set is inconsistent and nothing rolls — the install's
self-update is held on exactly this.
What that means for this issue
The decision this issue was opened to take is settled by evidence rather than by argument: a
sibling that is another package's declared module must not ride. The implementation is
MeshWeaver#3732 ("one producer" — the seal composes the published module bytes rather than a
re-copied sibling), which is staged behind a maintainer sequencing call because it touches a workflow_call lane five satellites publish through.
BakeHost.ShippedByHostProblem (#3175) still only checks the platform host's /app and surface
manifest, so the second-bundle case this issue identifies remains unguarded — that gap is real and is
the durable half. Worth keeping open for the guard, not for the question.
Related, and all one cause: MeshWeaver#3732 (the producer), MeshWeaver#3768 (the consumer symptom),
MeshWeaver.Manufacturing#67 (a gate that cannot register Skill/Agent for the same reason).
A live, daily-reproducing instance of this issue's question — and it names a break shape no gate sees
This issue asks whether a sibling that is itself another package's declared module should ride into a
second bundle. Here is the other half of module closure, failing every night since 2026-09-04 in
MeshWeaver.Manufacturing's scheduled run
(34307719499):
[MeshWeaver.Mesh.IncompatibleModule] Module 'MeshWeaver.AI' did not install against this platform
build and is CONTRIBUTING NOTHING. It requires
'MeshWeaver.ContentCollections.Indexing.ChunkPosition
(MeshWeaver.ContentCollections.Indexing — this deployment carries no such platform assembly)'.
Why that assembly is missing — it LEFT the platform, on purpose
core@main: src/MeshWeaver.ContentCollections/ ← the only one left
(no MeshWeaver.ContentCollections.Indexing, and ChunkPosition appears nowhere)
core history: d136860e1 "The indexing pipeline, the map control and Cornerstone leave the
content surface (#2941)"
c6f0f7f3f "refactor(indexing): the chunking pipeline leaves the content surface"
Plugins@main: src/MeshWeaver.ContentCollections.Indexing.Graph/
src/MeshWeaver.ContentCollections.Indexing.PostgreSql/
src/MeshWeaver.ContentCollections.Indexing.Test/ …
So MeshWeaver.ContentCollections.Indexing is no longer a platform assembly — it is a module-side
assembly in MeshWeaver.Plugins. And MeshWeaver.AI binds a type from it.
The publication seals four bundles (upstream 'plugins' sealed 4 module bundle(s) for identity s1218dc0a…: AI, Essentials, Stripe, Maps). Indexing is not one of them, and the AI bundle evidently
does not carry it either — the loader looks for it as a platform assembly, finds nothing, and
refuses the whole module.
The shape, stated generally
An assembly that leaves the platform for a module breaks every OTHER module that binds it — at
LOAD time, invisibly to every compile gate.
Both halves compile perfectly: core builds without it, the AI module builds against it (it is right
there in the same repo). The pair gate sees a public type leaving src/ and asks for a counterpart —
which #2941 presumably had, since nothing failed to compile. What nothing checks is whether the
assembly it moved into is in the closure of every module that binds it.
That is this issue's question from the other direction: not "may a sibling ride into a second
bundle?" but "must it?" — and the measured answer here is that when it doesn't, the depending
module contributes nothing.
The blast radius, end to end
AI contributing nothing ⇒ its BuiltInAgentProvider / BuiltInSkillProvider never run ⇒ Agent and Skill resolve to nothing ⇒ Store fails on Skill ⇒ Store/Plugin (declared byStore) never
registers ⇒ 36 package installs cascade ⇒ GATE FAILED — 103 NodeType(s) declared, NONE installed. MeshWeaver.Markdown.Collaboration fails the same way in the same run, on MeshWeaver.Blazor.BlazorView\2` — a second assembly that left the platform.
🚨 Two modules, two departed assemblies, one shape — which is what makes this a closure rule
rather than a one-off.
Why it is worth having here rather than only on the satellite
It reproduces daily and deterministically, unlike the portal-side declines.
It shows the failure is not about Agent/Skill being unpublished (they are served by the AI
engine's providers by design, Plugins@7afbd745) — I chased that first and it was wrong.
It shows a green push run proves nothing about the pairing: push pins the image to the wave the
modules came from, so only the release-follow poll exercises it.
Cross-refs: MeshWeaver.Manufacturing#67 (the observable), #3768 (the registry/identity pairing), #3732 (the two-producer half), #3583 (the same asymmetry from the source side).
Reopened — the 2026-09-10 sweep was not a triage decision
This issue was closed not_planned at 2026-09-10T14:31:41Z by a bulk sweep that PATCHed every open issue in all 13 Systemorph repositories — 311 issues in twelve minutes, no comment on any of them — after a session was asked to "close all issues in all repos". Nothing on this thread was read, and no judgement was made about this issue.
All 82 issues the sweep closed in the MeshWeaver fleet were reviewed afterwards. This one is unresolved: Open design decision with live, daily-reproducing instances; the question is answered empirically but not in code.
Sixteen of the 82 were left closed — their fix had actually landed, or the author had withdrawn them; those should have read completed, not not planned.
A live instance, measured this morning — and it is still red after #3933
This issue asks whether a sibling that is itself another package's declared module should ride into a second bundle. MeshWeaver.Crm's gate answered it concretely at 2026-09-11T06:12:18Z (run 34478221819, shard 1/1), and the two lines are adjacent in the log:
composed AI from the sealed publication of 'plugins' for identity s453e5561… (ai.module.nupkg)
composed Essentials from the sealed publication of 'plugins' for identity s453e5561… (essentials.module.nupkg)
sealed 4e241ee5f89308a2 as a sibling riding ai.module.nupkg
sealed 60a97251baddb425 as the declared module of essentials.module.nupkg
##[error]MeshWeaver.Markdown.Collaboration reaches this mesh as more than one build
MeshWeaver.Markdown.Collaboration is the declared module of Essentials AND a sibling riding AI, at two different builds — 4e241ee5f89308a2 vs 60a97251baddb425. The gate's own inventory line counts it "in 15 copies and 3 builds".
Why this matters now: the one-workspace fix does NOT close it
MeshWeaver#3933 (platform, merged 2026-09-10T18:57Z) and the Plugins consolidation that followed both attack divergence between compilations — build the set once, get one MVID. That is necessary and it landed: Plugins main now calls node-repo-module-packonce (was twice), and its Plugin Catalog CI has been green since 04:49Z.
It is not sufficient, because the two copies here are not two compilations of one lane's set — they are one module reached by two different closure paths, one as a declared module and one as a riding sibling. Nothing about compiling in a single workspace makes those the same artifact, so the refusal in compose-sealed-modules.sh still fires.
The cost, measured
This single condition is the sole required-check failure on every satellite PR still open this morning — MeshWeaver.Crm#81/#84/#86, MeshWeaver.Reinsurance#191, MeshWeaver.Manufacturing#78, MeshWeaver.Education#306 (its Disposable-mesh gate fails ontest-repos) — and it was the same on the two MeshWeaver.SocialMedia PRs before they merged. The fleet cannot drain while it stands.
What the decision actually is
The gate is correct and should not be loosened — a set carrying two builds of one assembly means the bake stamps every NodeType against whichever copy loaded first, and every consumer that loaded the other declines them (#3732, Doc/Architecture/ModuleOwnedSiblingsRide). So the choice this issue has been holding open is now load-bearing:
A declared module never rides as a sibling — Essentials owns Markdown.Collaboration; AI takes a dependency on the bundle rather than absorbing a copy. Removes the second build by construction.
A riding sibling is deduplicated against declared modules at seal time — the publication drops the riding copy when another bundle in the same sealed set declares that assembly.
Option 1 is the closure rule this issue proposes; option 2 is a seal-time repair that leaves the closure rule wrong but unblocks sooner. They are not mutually exclusive, and the fleet is red until one of them exists.
Correction to my comment above — its central claim was falsified 47 minutes after I posted it
I wrote that the one-workspace fix "does NOT close it" and that "the fleet cannot drain while it stands." Both are wrong, and the measurement that shows it is the same gate on the same PR, one run later:
06:12:18Z
06:58:18Z
framework identity
s453e556166cdcadeb087d9cf97672ddc
s426f089fcf95b69eb383d55f8af0b8ac
Markdown.Collaboration
4e241ee5f89308a2 riding ai.module.nupkg+60a97251baddb425 declared by essentials.module.nupkg
one copy, declared by essentials.module.nupkg
verdict
##[error] … more than one build
success
MeshWeaver.Crm#86 merged at 07:00Z on the second one.
What I got wrong: I read a red gate as evidence about the closure RULE, when it was evidence about WHICH PUBLICATION that run resolved. The 06:12Z run composed a set sealed under the previous platform identity — sealed before the Plugins consolidation landed. The consolidation could not retroactively fix an already-sealed publication; it only governs sets sealed after it. One fresh core CD later, the same repo, same PR and same gate went green untouched. So #3732/#3933 did close this, and the correct reading of a stale-identity red is "this publication predates the fix", not "the fix is insufficient".
This is the shape AGENTS.md warns about — a snapshot of a transition read as a property. The discriminator I should have checked before drawing a conclusion was the identity string in the gate's own first line, which names exactly which publication it is judging.
The two options I offered are also moot, and their shared premise was wrong: I assumed the ride itself was the defect. #4006 settles that with the measurements — the sibling DOES ride, exclusivity was never available (AIrequires [Store] while Essentials, which declares the assembly, requires [AI, …] — a package cycle), and what makes it safe is one build per name per publication, asserted by the producer at compose time and by the consumer over the sealed set. That is the right answer and it is not either of mine.
The cost figures in my earlier comment were accurate when measured and are now historical: the satellite PRs that failed on this condition are draining — Crm#86 merged, and the SocialMedia pair merged earlier.
This was a design question, not a bug report, and #4006 (merged) settles it with measurements
rather than reasoning. Both files verified on main = ac7617466 by path — Data/Architecture/ModuleOwnedSiblingsRide.md and .github/workflows/node-repo-module-pack.yml —
because the merge went through the queue and the closing keyword did not fire.
The answer
Yes — it rides, IFF the platform host does not already ship it. Being another package's declared
module is not a reason to exclude it.
Two things make that the right answer rather than a shrug:
Exclusivity was never available.AIrequires [Store] while Essentials — which DECLARES
the assembly — requires [AI, …]. That is a package cycle, so "the declaring package alone ships
it" cannot be expressed, and enforcing it would break a supported install.
What makes the ride safe is not exclusivity but ONE BUILD PER NAME PER PUBLICATION — asserted
by the producer at compose time and again by the consumer over the whole sealed set. The hazard
this question was really about was never two bundles, it was two different builds under one
simple name: the loader keeps whichever it sees first, the loser's bytes are never in memory, and
callers compiled against the loser silently run the winner.
Why the question dissolved rather than needing new machinery
Everything that made it hard moved after it was opened:
MeshWeaver.Pluginsmain @ d6fa0338: 79index.json scanned, 37 declare a content.module, 148.csproj under src/. 18 of 37 declared modules transitively
reference another package's declared module; 4 of 37 are actually ridden — Markdown.Collaboration ×14, AI ×13, Maps ×4, Import.
So the practice is common (18/37 could ride) and the reality is narrow (4/37 do) — which is why the
question looked more alarming from the reference graph than it turned out to be in the artefacts.
Also fixed in passing
The one diagnostic line that says which witness answered — a small thing, but the kind that makes
the next investigation of this area start from a true statement instead of a plausible one.
Activity
rbuergi commentedon Sep 8, 2026
The prediction in this issue came true, and it is now blocking production
Reopening: #3242 closed this by making the verdict count every copy — it did not decide whether
the copy should ride. That detector is now firing on
memex, and it is why nothing rolls.Measured 2026-09-08,
Admin/UpdatePolicyonmemex(memex.systemorph.com), holding3.0.0-ci.8053:Two different MVIDs, exactly as written here:
The wave stopped being uniform. The detector is behaving correctly; the cause is untouched.
Why this is not just one more held candidate
It is set-level. The other two reasons in the same hold are per-module and clear on their own —
the
rc-line floors (core #3554 + Plugins #1447, merged, awaiting a sealed set) and five bundles withno sealed bake. This one refuses the whole set regardless: fixing the floors and the bakes still
leaves
nothing rolls. So it gates every roll ofmemex, including the roll that would deliver anyother fix.
The decision this issue posed, now forced
The concrete instance:
src/MeshWeaver.AI/MeshWeaver.AI.csprojcarriesso
ModuleClosureAccountingrides a copy intoai.module.nupkg, whileEssentials/index.jsondeclares the same assembly as its module.
Three shapes, in increasing order of blast radius:
riding and the landing resolves it from Essentials. Fixes the class. Needs the caller sweep this
issue already flags (Chat rides
MeshWeaver.AI.dllthe same way), so grace mode.CreateCommentRequest/CreateCommentResponsemove to aplatform-shipped contract assembly;
MeshWeaver.AIreferences that and rides nothing — theMeshWeaver.Speech.Contract/MeshWeaver.Observability.Contractprecedent. Durable, but itmoves public message types across a repo boundary, so it meets the pair and binary-compat gates.
restores the accident this issue exists to stop relying on.
3 is the stopgap; 1 or 2 is the fix. I have not taken any of them — the choice is a pack-lane
decision and it can refuse existing callers, which by the working agreement lands with a sweep.
Found while diagnosing a separate production symptom (a node opening on the wrong default area,
fixed in #3694); this one is unowned and is the roll blocker.
docs(#3732): name what stops riding, and the one failure it trades ag…
rbuergi commentedon Sep 9, 2026
The question is now answered empirically — by the exact pair this issue names
This issue asks whether a sibling that is itself another package's declared module should ride
into a second bundle, and names the live instance: Essentials declares
MeshWeaver.Markdown.Collaborationas its module, while AI carries a copy as a riding sibling.Production has answered it. Read live from
Admin/UpdatePolicyon memex(
lastCheckedAt: 2026-09-09T07:39:42Z), insideheldReason— the same two producers, by name:So the answer to the title question is no, and the reason is not aesthetic:
order, so which of the two builds wins is not a property of the publication.
staff portal:
bundle_adoption: 0 of 25 adopted, every entryFrameworkDeclined, and seven nodetypes unable to type their content — user-visibly, pages render empty.
the set is inconsistent and nothing rolls— the install'sself-update is held on exactly this.
What that means for this issue
The decision this issue was opened to take is settled by evidence rather than by argument: a
sibling that is another package's declared module must not ride. The implementation is
MeshWeaver#3732 ("one producer" — the seal composes the published module bytes rather than a
re-copied sibling), which is staged behind a maintainer sequencing call because it touches a
workflow_calllane five satellites publish through.BakeHost.ShippedByHostProblem(#3175) still only checks the platform host's/appand surfacemanifest, so the second-bundle case this issue identifies remains unguarded — that gap is real and is
the durable half. Worth keeping open for the guard, not for the question.
Related, and all one cause: MeshWeaver#3732 (the producer), MeshWeaver#3768 (the consumer symptom),
MeshWeaver.Manufacturing#67 (a gate that cannot register
Skill/Agentfor the same reason).rbuergi commentedon Sep 9, 2026
A live, daily-reproducing instance of this issue's question — and it names a break shape no gate sees
This issue asks whether a sibling that is itself another package's declared module should ride into a
second bundle. Here is the other half of module closure, failing every night since 2026-09-04 in
MeshWeaver.Manufacturing's scheduled run
(34307719499):
Why that assembly is missing — it LEFT the platform, on purpose
So
MeshWeaver.ContentCollections.Indexingis no longer a platform assembly — it is a module-sideassembly in MeshWeaver.Plugins. And
MeshWeaver.AIbinds a type from it.The publication seals four bundles (
upstream 'plugins' sealed 4 module bundle(s) for identity s1218dc0a…: AI, Essentials, Stripe, Maps).Indexingis not one of them, and the AI bundle evidentlydoes not carry it either — the loader looks for it as a platform assembly, finds nothing, and
refuses the whole module.
The shape, stated generally
Both halves compile perfectly: core builds without it, the AI module builds against it (it is right
there in the same repo). The pair gate sees a public type leaving
src/and asks for a counterpart —which #2941 presumably had, since nothing failed to compile. What nothing checks is whether the
assembly it moved into is in the closure of every module that binds it.
That is this issue's question from the other direction: not "may a sibling ride into a second
bundle?" but "must it?" — and the measured answer here is that when it doesn't, the depending
module contributes nothing.
The blast radius, end to end
AI contributing nothing ⇒ its
BuiltInAgentProvider/BuiltInSkillProvidernever run ⇒AgentandSkillresolve to nothing ⇒Storefails onSkill⇒Store/Plugin(declared byStore) neverregisters ⇒ 36 package installs cascade ⇒
GATE FAILED — 103 NodeType(s) declared, NONE installed.MeshWeaver.Markdown.Collaborationfails the same way in the same run, onMeshWeaver.Blazor.BlazorView\2` — a second assembly that left the platform.🚨 Two modules, two departed assemblies, one shape — which is what makes this a closure rule
rather than a one-off.
Why it is worth having here rather than only on the satellite
Agent/Skillbeing unpublished (they are served by the AIengine's providers by design,
Plugins@7afbd745) — I chased that first and it was wrong.modules came from, so only the release-follow poll exercises it.
Cross-refs: MeshWeaver.Manufacturing#67 (the observable), #3768 (the registry/identity pairing),
#3732 (the two-producer half), #3583 (the same asymmetry from the source side).
rbuergi commentedon Sep 10, 2026
Reopened — the 2026-09-10 sweep was not a triage decision
This issue was closed
not_plannedat 2026-09-10T14:31:41Z by a bulk sweep that PATCHed every open issue in all 13Systemorphrepositories — 311 issues in twelve minutes, no comment on any of them — after a session was asked to "close all issues in all repos". Nothing on this thread was read, and no judgement was made about this issue.All 82 issues the sweep closed in the MeshWeaver fleet were reviewed afterwards. This one is unresolved: Open design decision with live, daily-reproducing instances; the question is answered empirically but not in code.
Sixteen of the 82 were left closed — their fix had actually landed, or the author had withdrawn them; those should have read
completed, notnot planned.docs(#3221): settle the module-closure question, and fix the witness …
docs(#3221): settle the module-closure question, and fix the witness …
rbuergi commentedon Sep 11, 2026
A live instance, measured this morning — and it is still red after #3933
This issue asks whether a sibling that is itself another package's declared module should ride into a second bundle. MeshWeaver.Crm's gate answered it concretely at 2026-09-11T06:12:18Z (run 34478221819, shard 1/1), and the two lines are adjacent in the log:
MeshWeaver.Markdown.Collaborationis the declared module ofEssentialsAND a sibling ridingAI, at two different builds —4e241ee5f89308a2vs60a97251baddb425. The gate's own inventory line counts it "in 15 copies and 3 builds".Why this matters now: the one-workspace fix does NOT close it
MeshWeaver#3933 (platform, merged 2026-09-10T18:57Z) and the Plugins consolidation that followed both attack divergence between compilations — build the set once, get one MVID. That is necessary and it landed: Plugins
mainnow callsnode-repo-module-packonce (was twice), and its Plugin Catalog CI has been green since 04:49Z.It is not sufficient, because the two copies here are not two compilations of one lane's set — they are one module reached by two different closure paths, one as a declared module and one as a riding sibling. Nothing about compiling in a single workspace makes those the same artifact, so the refusal in
compose-sealed-modules.shstill fires.The cost, measured
This single condition is the sole required-check failure on every satellite PR still open this morning — MeshWeaver.Crm#81/#84/#86, MeshWeaver.Reinsurance#191, MeshWeaver.Manufacturing#78, MeshWeaver.Education#306 (its
Disposable-mesh gatefails ontest-repos) — and it was the same on the two MeshWeaver.SocialMedia PRs before they merged. The fleet cannot drain while it stands.What the decision actually is
The gate is correct and should not be loosened — a set carrying two builds of one assembly means the bake stamps every NodeType against whichever copy loaded first, and every consumer that loaded the other declines them (#3732,
Doc/Architecture/ModuleOwnedSiblingsRide). So the choice this issue has been holding open is now load-bearing:EssentialsownsMarkdown.Collaboration;AItakes a dependency on the bundle rather than absorbing a copy. Removes the second build by construction.Option 1 is the closure rule this issue proposes; option 2 is a seal-time repair that leaves the closure rule wrong but unblocks sooner. They are not mutually exclusive, and the fleet is red until one of them exists.
Refs #3732, #3933.
rbuergi commentedon Sep 11, 2026
Correction to my comment above — its central claim was falsified 47 minutes after I posted it
I wrote that the one-workspace fix "does NOT close it" and that "the fleet cannot drain while it stands." Both are wrong, and the measurement that shows it is the same gate on the same PR, one run later:
s453e556166cdcadeb087d9cf97672ddcs426f089fcf95b69eb383d55f8af0b8acMarkdown.Collaboration4e241ee5f89308a2ridingai.module.nupkg+60a97251baddb425declared byessentials.module.nupkgessentials.module.nupkg##[error] … more than one buildMeshWeaver.Crm#86 merged at 07:00Z on the second one.
What I got wrong: I read a red gate as evidence about the closure RULE, when it was evidence about WHICH PUBLICATION that run resolved. The 06:12Z run composed a set sealed under the previous platform identity — sealed before the Plugins consolidation landed. The consolidation could not retroactively fix an already-sealed publication; it only governs sets sealed after it. One fresh core CD later, the same repo, same PR and same gate went green untouched. So #3732/#3933 did close this, and the correct reading of a stale-identity red is "this publication predates the fix", not "the fix is insufficient".
This is the shape AGENTS.md warns about — a snapshot of a transition read as a property. The discriminator I should have checked before drawing a conclusion was the identity string in the gate's own first line, which names exactly which publication it is judging.
The two options I offered are also moot, and their shared premise was wrong: I assumed the ride itself was the defect. #4006 settles that with the measurements — the sibling DOES ride, exclusivity was never available (
AIrequires [Store]whileEssentials, which declares the assembly,requires [AI, …]— a package cycle), and what makes it safe is one build per name per publication, asserted by the producer at compose time and by the consumer over the sealed set. That is the right answer and it is not either of mine.The cost figures in my earlier comment were accurate when measured and are now historical: the satellite PRs that failed on this condition are draining — Crm#86 merged, and the SocialMedia pair merged earlier.
rbuergi commentedon Sep 11, 2026
Answered. Closing.
This was a design question, not a bug report, and #4006 (merged) settles it with measurements
rather than reasoning. Both files verified on
main=ac7617466by path —Data/Architecture/ModuleOwnedSiblingsRide.mdand.github/workflows/node-repo-module-pack.yml—because the merge went through the queue and the closing keyword did not fire.
The answer
Yes — it rides, IFF the platform host does not already ship it. Being another package's declared
module is not a reason to exclude it.
Two things make that the right answer rather than a shrug:
AIrequires [Store]whileEssentials— which DECLARESthe assembly —
requires [AI, …]. That is a package cycle, so "the declaring package alone shipsit" cannot be expressed, and enforcing it would break a supported install.
by the producer at compose time and again by the consumer over the whole sealed set. The hazard
this question was really about was never two bundles, it was two different builds under one
simple name: the loader keeps whichever it sees first, the loser's bytes are never in memory, and
callers compiled against the loser silently run the winner.
Why the question dissolved rather than needing new machinery
Everything that made it hard moved after it was opened:
min:<version>) ratherthan an MVID pin, so a rider at a different build no longer causes a same-version adoption decline.
"declined at adoption" clause had stopped being true while the loader hazard it was really
protecting against had not.
Measured, with denominators
MeshWeaver.Pluginsmain@d6fa0338: 79index.jsonscanned, 37 declare acontent.module, 148.csprojundersrc/. 18 of 37 declared modules transitivelyreference another package's declared module; 4 of 37 are actually ridden —
Markdown.Collaboration×14,AI×13,Maps×4,Import.So the practice is common (18/37 could ride) and the reality is narrow (4/37 do) — which is why the
question looked more alarming from the reference graph than it turned out to be in the artefacts.
Also fixed in passing
The one diagnostic line that says which witness answered — a small thing, but the kind that makes
the next investigation of this area start from a true statement instead of a plausible one.
🤖 Generated with Claude Code