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
Arm the registry's own 400: ModulePublish.Validate still accepts a manifest that states no framework identity #3240
ModulePublish.Validate still accepts a manifest that states no framework identity, so the registry can still shelve a null and advertise it on /api/plugins/bundles/index.json. #3237 (part 3 of Plugins#931) closed the blind spot at the producer — the packer refuses to write such a bundle, the pack step refuses to pack without naming the anchor, and the publish step refuses to POST one — but deliberately did not arm the registry's own 400.
The precondition #3211 states: "every producer CI in the fleet must be emitting the field before the refusal is armed, or the refusal takes the fleet's publishes down."
Measured on 2026-09-03, before #3237: none of them was. MeshWeaver.Plugins run 33773265959 packed all 34 bundles built-against MVID (unrecorded), on both the sdk and the container path.
The registry rolls from core main automatically; every producer pins node-repo-module-pack.yml@<sha> and bumps it deliberately, per repo, in its own PR. Arming the 400 on #3237's merge would therefore have redded every satellite main-push publish for as long as its pin lagged — the nulls would have survived and the publishes would not.
Module publish for {Plugin}: '{Module}' version {Version} states NO framework identity … Its producer packs on a lane older than MeshWeaver#3211; bump that repo's node-repo-module-pack.yml pin. This becomes a 400.
A quiet window across at least one full publish wave of each producer is the signal.
Then
ModulePublish.Validate returns a named refusal (the bundle states no framework identity …) → the route's existing 400 path, with a test beside ModulePublishShelfTest.
The warning above is replaced by the refusal (it exists only as the runway).
ModuleBuildArchitecture → "A bundle states what it was built against" → "Still to arm" is updated, and the Roadmap entry closed.
Arming criterion 1 re-measured 2026-09-04: 1 of 2 producers has moved its pin
Re-ran this issue's own check rather than reading it. #3237's merge commit is da2bb12d346e14f27f39c4a3482139e42eec5528 (committed 2026-09-03 17:49:52Z — note the merge-queue
skew: mergedAt reads 19:25Z, so an ordering-by-timestamp comparison gets this wrong).
So criterion 1 reduces to one pin bump in MeshWeaver.SocialMedia. Criterion 2 (a quiet window in
the registry's publish log across one full wave of each producer) still has to follow that bump —
SocialMedia cannot have gone quiet on a lane that does not yet emit the field.
ModulePublish.Validate is unchanged on main at fec69fc66 — no framework-identity refusal, and
the Accepted.FrameworkMvid doc comment still carries the runway note ("Arming the same refusal
HERE … is the last step, and it waits on the fleet"). Leaving open.
Measured 2026-09-04 against core origin/mainfec69fc66 and both producers' committed ci.yml.
Arming criterion 1 is now SATISFIED — measured 2026-09-04. Criterion 2 is the only thing left.
This issue names two conditions, and blocks on both. Re-measured today:
1. Every producer's node-repo-module-pack.yml pin is past #3237 ✅
#3237 merged as core da2bb12d346e14f27f39c4a3482139e42eec5528. Both producers, resolved from their live ci.yml and tested by ancestry rather than by date:
When I first measured this morning, SocialMedia was the single blocker — it pinned 04a2401c6 (2026-09-01), behind #3237. That is now fixed by Systemorph/MeshWeaver.SocialMedia#135 (merged). MeshWeaver.Education and MeshWeaver.Reinsurance do not call the lane, as this issue states.
Three findings from that bump worth carrying here, because they change what "the pin" means:
It was three refs, not one.uses: …@, platform-ref: and MW_PLATFORM_REF had silently drifted to three different commits (04a2401c6 / 94d18f9cb / 89f886275) under a comment asserting they move together. Moving uses: alone would have been fatal: the new lane runs module-build-key.py, module-build-ledger.py and workspace-build-verify.py out of the platform checkout at platform-ref, and none of the three exists at 94d18f9cb.
No new required inputs, so no caller startup_failure: the required set is still modules + platform-ref; the new inputs default, and ledger-token is required: false. Job count went 14 → 16 (not zero), and all four required contexts read literally SUCCESS.
🚨 SocialMedia's bundles were NOT (unrecorded) before. The old lane's default probe happened to find an anchor here (it produced dbe64fa6…), unlike the 34 Plugins bundles this issue measured. So the gain is that the anchor is now named (--graph-dll) rather than guessed — the publish now states packed … built against framework cef92e9759e940ea8a1aa73173b12227, and the lane carries a hard postcondition that reds an empty frameworkMvid. Do not read "criterion 1 met" as "SocialMedia was previously emitting nulls".
2. A quiet window on the registry's warning — NOT yet demonstrated ⏳
This is now the only thing gating the 400. The runway warning #3237 added is:
Module publish for {Plugin}: '{Module}' version {Version} states NO framework identity … Its producer packs on a lane older than MeshWeaver#3211; bump that repo's node-repo-module-pack.yml pin. This becomes a 400.
SocialMedia has published once on the new lane (registry answered 200, "published": true, MeshWeaver.Social@1.1.7). Plugins has not published a full wave on its bumped pin that I can attest to — and this issue's criterion is "a quiet window across at least one full publish wave of each producer", which is deliberately stronger than "the pins are right".
🚨 That distinction matters more than usual right now: there has been no green Plugins: bake + seal in the last 60 core-CD runs (Systemorph/MeshWeaver.Plugins#1262 — MeshWeaver.Markdown.Collaboration doubly produced), so Plugins' publish path has not been exercised end to end. Arming the 400 against a producer whose wave has not run since the bump would be arming against an untested lane. Wait for #1262 to land and one clean Plugins wave to publish, then read the registry log for the warning.
Then
Unchanged from the issue body: ModulePublish.Validate returns the named refusal into the route's existing 400, with a test beside ModulePublishShelfTest; the warning is deleted (it exists only as the runway); ModuleBuildArchitecture → "A bundle states what it was built against" → "Still to arm" is updated and the Roadmap entry closed.
Leaving open — criterion 2 is a measurement over time, not a code change.
Re-measured 2026-09-04, after Plugins#1268 merged — still DO NOT arm. Criterion 2 is not met, and its blocker has moved.
Re-ran both criteria against the live state rather than reading the 06:22Z comment. One of them had
already drifted since that measurement.
Criterion 1 — every producer's node-repo-module-pack.yml pin is past #3237 ✅ (re-measured)
🚨 Plugins' pin has MOVED since this issue last measured it. The 06:22Z comment recorded b88be8a05; it now reads c41a34fda — the #1333 pin move landed in between. Reading the remembered
value would have measured a pin that no longer exists.
Criterion 2 — a quiet window across one full publish wave of each producer ⛔ NOT met
This issue's criterion is "a quiet window across at least one full publish wave of each producer". Neither producer has published a wave today. Both are down, for two different reasons, and
neither is Plugins#1262/#1268 any more.
Core CD's Plugins wave — blocked on a NEW defect, not the old one.#1268 did what it promised: Module bundle (MeshWeaver.Markdown.Collaboration) now packs green on every run. But the pack
step then refuses two other modules, deterministically, on all four of today's runs
(33867513503, 33873443795, 33873909869, 33874892203):
failure Module bundle (MeshWeaver.Maps)
failure Module bundle (MeshWeaver.Payments.Stripe)
failure All selected bundles built
skipped Plugins: bake + seal the publication for this identity
##[error]no identity anchor for MeshWeaver.Maps — expected MeshWeaver.Compiler.dll at
'…/src/MeshWeaver.Maps/bin/Release/net10.0/publish/MeshWeaver.Compiler.dll' …
Plugins' own lane — red on its own gates.Plugin Catalog CI on main has failed on all seven
runs today, latest 33874680424 on the #1268 merge commit 2f7a95d6 itself. Its Module bundles / Module bundles (floor) jobs are skipped, and test-repos then ends:
##[error]external modules were requested but none were assembled — the artifact pattern matched
nothing and no registry bundle was fetched.
So there are no fresh bundles from either producer, and the registry log therefore has nothing to be
quiet about. A quiet window over zero publishes is the vacuous pass this issue was written to
avoid — arming on it would arm against a lane that has not run.
🚨 And the identity being emitted today is not yet trustworthy either
While measuring the above I found the source arm's anchor is wrong in a second, SILENT way — directly
relevant here, because it is the very field the 400 would police. In run 33874892203, one platform
and one commit produced two different framework identities:
MeshWeaver.Plugin.AI.1.3.18 … built against framework be27d0fb9ad54ae6a862bfa7aeb97c9b
MeshWeaver.Plugin.Essentials.1.0.24 … built against framework d756b82e09804a11b0ea44d26233af6c
The anchor was read from each module's own output, where it had been rebuilt under that module's -p:Version (reproduced locally: 914d015c… → 9b9fa234…, changing nothing but the version). So a
bundle can pass a stated-ness check while stating an identity no consumer can ever match — exactly
the outcome #3211 exists to prevent, one layer down. A 400 that only asks "is the field non-blank"
would have shelved all of these. Worth folding into this issue's acceptance: the quiet window
should be read on bundles whose identity is also consistent across the wave, not merely present.
Fixed in #3306; recorded as Doc/Architecture/ModuleIdentityAnchor.
#3293 merged at 14:02Z, mid-review of my comment above, and fixed the RED half of the pack
blocker (no identity anchor on Maps / Payments.Stripe). Attribution corrected: that half is #3293, not #3306. #3306 is now only the remaining half.
Criterion 2 is still not met, and for the reasons already given: no producer has published a
wave. Core CD's first post-#3293 run (33881823753) has not yet reached the pack step, and Plugins main is still red on its own gates.
🚨 The sharpening this issue should adopt before arming
My comment above suggested the quiet window be read on identities that are consistent across the
wave, not merely present. #3293 turns that from a nice-to-have into a hard precondition, because
it makes the lane emit one identity per module while being green:
$ dotnet build src/MeshWeaver.Compiler/MeshWeaver.Compiler.csproj -c Release -warnaserror \
-p:MeshWeaverRoot=<root> [-p:Version=<v>]
-p:Version=1.3.18 (AI) → 34337c31960d47f5b5251d32ef923fc6
-p:Version=1.0.24 (Essentials) → 7c1c4f70de084da78659e6bda495e1c5
no override, run A → 71cc81badb364c5d8558ac5e7db6a44e
no override, run B → 71cc81badb364c5d8558ac5e7db6a44e ← identical
$VERSION is the MODULE's package version; MSBuild writes it into the assembly version attributes,
which are part of the metadata the MVID is computed over. So on today's main, a full wave would
publish four bundles stating four different framework identities for one platform — every one of them non-blank, and therefore every one of them accepted by a 400 that only asks whether the field is
stated.
That is the exact failure this issue exists to prevent, arriving through the door the fix opened.
A registry refusal keyed on stated-ness alone would have shelved all four and reported success.
Consequence for the arming criterion
Add a third condition, and read criterion 2 through it:
The wave's bundles agree. Every bundle from one publish wave states the SAME framework
identity — that is what makes the value a platform build rather than a per-module accident. A
quiet window over a wave whose bundles disagree is a vacuous pass.
#3306 makes that true at the producer (drops -p:Version from the anchor build, guards it, and
refuses a module-local anchor in ModulePackCommand for every caller). Until it lands, do not arm —
and when arming, consider whether the 400 should compare against the wave rather than only assert
non-blankness, since a per-module identity is exactly as useless to a consumer as an absent one and
strictly harder to notice.
Still open. Do not arm.
Measured 2026-09-04 against fc4adc666 and local builds of MeshWeaver.Compiler.csproj.
Armed by #3340, merged — closing by hand, since the merge queue does not fire Closes links here.
Verified on origin/main after the merge: ModulePublish.Validate carries the named refusal, and
the runway warning at the publish endpoint is gone (it existed only as the measurement the arming
step reads).
Both halves of this issue's own criterion were measured before arming:
half
measurement
every publishing repo's node-repo-module-pack.yml pin past #3237 (da2bb12d3, 09-03 17:49Z)
MeshWeaver.Plugins c41a34fda (09-04 05:57Z), MeshWeaver.SocialMedia fec69fc66 (09-03 21:27Z) — checked with merge-base --is-ancestor in BOTH directions. Education and Reinsurance do not call the lane.
a full publish wave from each, stating an identity
Plugins run 33941672487 (09-05 03:31Z) built against: g7d644de95…; SocialMedia run 33941835795 (09-05 03:27Z) built against: cef92e9759…. Both publish: true, both green, so the producer-side refusal never fired.
🚨 Worth keeping, because it nearly went the other way: MeshWeaver.Plugins' recent main runs are red, and "no successful run" would have read as "no evidence, do not arm". The failures are in test-repos and a ratchet — the module-pack lane succeeded and published. An absent wave is not
a quiet window, and a red workflow is not an absent wave.
The refusal is deliberately shape-agnostic: g<sha>, a 32-hex MVID and s<hash> are all
identities a lane legitimately states, and only ABSENCE is refused — AStatedIdentityOfAnyShape_IsAccepted pins that from the other side so a future narrowing cannot
pass by refusing everything. Mutation-proved: removing the refusal fails exactly the refusal test
and leaves the other three green.
ModuleBuildArchitecture's "Still to arm" section now records the arming and the evidence, and the
Roadmap entry is closed.
Activity
docs: name the follow-up that arms the registry's own 400 (#3240)
rbuergi commentedon Sep 4, 2026
Arming criterion 1 re-measured 2026-09-04: 1 of 2 producers has moved its pin
Re-ran this issue's own check rather than reading it.
#3237's merge commit isda2bb12d346e14f27f39c4a3482139e42eec5528(committed 2026-09-03 17:49:52Z — note the merge-queueskew:
mergedAtreads 19:25Z, so an ordering-by-timestamp comparison gets this wrong).Resolved by ancestry, not by tag or date order:
modules-floor+modules-rest)b88be8a05(2026-09-03 17:57Z, 3 commits after the merge)04a2401c6(2026-09-01 04:35Z, "untangle the needs cycle")So criterion 1 reduces to one pin bump in MeshWeaver.SocialMedia. Criterion 2 (a quiet window in
the registry's publish log across one full wave of each producer) still has to follow that bump —
SocialMedia cannot have gone quiet on a lane that does not yet emit the field.
ModulePublish.Validateis unchanged onmainatfec69fc66— no framework-identity refusal, andthe
Accepted.FrameworkMviddoc comment still carries the runway note ("Arming the same refusalHERE … is the last step, and it waits on the fleet"). Leaving open.
Measured 2026-09-04 against core
origin/mainfec69fc66and both producers' committedci.yml.rbuergi commentedon Sep 4, 2026
Arming criterion 1 is now SATISFIED — measured 2026-09-04. Criterion 2 is the only thing left.
This issue names two conditions, and blocks on both. Re-measured today:
1. Every producer's
node-repo-module-pack.ymlpin is past #3237 ✅#3237 merged as core
da2bb12d346e14f27f39c4a3482139e42eec5528. Both producers, resolved from their liveci.ymland tested by ancestry rather than by date:When I first measured this morning, SocialMedia was the single blocker — it pinned
04a2401c6(2026-09-01), behind #3237. That is now fixed by Systemorph/MeshWeaver.SocialMedia#135 (merged). MeshWeaver.Education and MeshWeaver.Reinsurance do not call the lane, as this issue states.Three findings from that bump worth carrying here, because they change what "the pin" means:
uses: …@,platform-ref:andMW_PLATFORM_REFhad silently drifted to three different commits (04a2401c6/94d18f9cb/89f886275) under a comment asserting they move together. Movinguses:alone would have been fatal: the new lane runsmodule-build-key.py,module-build-ledger.pyandworkspace-build-verify.pyout of the platform checkout atplatform-ref, and none of the three exists at94d18f9cb.startup_failure: the required set is stillmodules+platform-ref; the new inputs default, andledger-tokenisrequired: false. Job count went 14 → 16 (not zero), and all four required contexts read literallySUCCESS.(unrecorded)before. The old lane's default probe happened to find an anchor here (it produceddbe64fa6…), unlike the 34 Plugins bundles this issue measured. So the gain is that the anchor is now named (--graph-dll) rather than guessed — the publish now statespacked … built against framework cef92e9759e940ea8a1aa73173b12227, and the lane carries a hard postcondition that reds an emptyframeworkMvid. Do not read "criterion 1 met" as "SocialMedia was previously emitting nulls".2. A quiet window on the registry's warning — NOT yet demonstrated ⏳
This is now the only thing gating the 400. The runway warning #3237 added is:
SocialMedia has published once on the new lane (
registry answered 200,"published": true,MeshWeaver.Social@1.1.7). Plugins has not published a full wave on its bumped pin that I can attest to — and this issue's criterion is "a quiet window across at least one full publish wave of each producer", which is deliberately stronger than "the pins are right".🚨 That distinction matters more than usual right now: there has been no green
Plugins: bake + sealin the last 60 core-CD runs (Systemorph/MeshWeaver.Plugins#1262 —MeshWeaver.Markdown.Collaborationdoubly produced), so Plugins' publish path has not been exercised end to end. Arming the 400 against a producer whose wave has not run since the bump would be arming against an untested lane. Wait for #1262 to land and one clean Plugins wave to publish, then read the registry log for the warning.Then
Unchanged from the issue body:
ModulePublish.Validatereturns the named refusal into the route's existing400, with a test besideModulePublishShelfTest; the warning is deleted (it exists only as the runway);ModuleBuildArchitecture→ "A bundle states what it was built against" → "Still to arm" is updated and the Roadmap entry closed.Leaving open — criterion 2 is a measurement over time, not a code change.
rbuergi commentedon Sep 4, 2026
Re-measured 2026-09-04, after Plugins#1268 merged — still DO NOT arm. Criterion 2 is not met, and its blocker has moved.
Re-ran both criteria against the live state rather than reading the 06:22Z comment. One of them had
already drifted since that measurement.
Criterion 1 — every producer's
node-repo-module-pack.ymlpin is past #3237 ✅ (re-measured)🚨 Plugins' pin has MOVED since this issue last measured it. The 06:22Z comment recorded
b88be8a05; it now readsc41a34fda— the #1333 pin move landed in between. Reading the rememberedvalue would have measured a pin that no longer exists.
Resolved by ancestry against #3237's merge commit
da2bb12d346e14f27f39c4a3482139e42eec5528,not by date or tag order:
Criterion 1 holds — on the CURRENT pins.
Criterion 2 — a quiet window across one full publish wave of each producer ⛔ NOT met
This issue's criterion is "a quiet window across at least one full publish wave of each producer".
Neither producer has published a wave today. Both are down, for two different reasons, and
neither is Plugins#1262/#1268 any more.
Core CD's Plugins wave — blocked on a NEW defect, not the old one. #1268 did what it promised:
Module bundle (MeshWeaver.Markdown.Collaboration)now packs green on every run. But the packstep then refuses two other modules, deterministically, on all four of today's runs
(
33867513503,33873443795,33873909869,33874892203):Plugins' own lane — red on its own gates.
Plugin Catalog CIonmainhas failed on all sevenruns today, latest
33874680424on the #1268 merge commit2f7a95d6itself. ItsModule bundles/Module bundles (floor)jobs are skipped, andtest-reposthen ends:So there are no fresh bundles from either producer, and the registry log therefore has nothing to be
quiet about. A quiet window over zero publishes is the vacuous pass this issue was written to
avoid — arming on it would arm against a lane that has not run.
🚨 And the identity being emitted today is not yet trustworthy either
While measuring the above I found the source arm's anchor is wrong in a second, SILENT way — directly
relevant here, because it is the very field the 400 would police. In run
33874892203, one platformand one commit produced two different framework identities:
The anchor was read from each module's own output, where it had been rebuilt under that module's
-p:Version(reproduced locally:914d015c…→9b9fa234…, changing nothing but the version). So abundle can pass a stated-ness check while stating an identity no consumer can ever match — exactly
the outcome #3211 exists to prevent, one layer down. A 400 that only asks "is the field non-blank"
would have shelved all of these. Worth folding into this issue's acceptance: the quiet window
should be read on bundles whose identity is also consistent across the wave, not merely present.
Fixed in #3306; recorded as
Doc/Architecture/ModuleIdentityAnchor.Where that leaves the arming criterion
Unchanged in substance, sharper in sequence:
on fix(#3176): the identity anchor must not carry the module's version — #3293's cure leaves one identity per module #3306 (core CD's pack) and on Plugins
maingoing green on its own gates.Leaving open. Do not arm.
Measured 2026-09-04 against both producers' live
ci.yml, core runs 33867513503 / 33873443795 / 33873909869 / 33874892203, and Plugins run 33874680424.rbuergi commentedon Sep 4, 2026
Correction + a sharpening of the arming criterion
#3293 merged at 14:02Z, mid-review of my comment above, and fixed the RED half of the pack
blocker (
no identity anchoron Maps / Payments.Stripe). Attribution corrected: that half is#3293, not #3306. #3306 is now only the remaining half.
Criterion 2 is still not met, and for the reasons already given: no producer has published a
wave. Core CD's first post-#3293 run (
33881823753) has not yet reached the pack step, and Pluginsmainis still red on its own gates.🚨 The sharpening this issue should adopt before arming
My comment above suggested the quiet window be read on identities that are consistent across the
wave, not merely present. #3293 turns that from a nice-to-have into a hard precondition, because
it makes the lane emit one identity per module while being green:
$VERSIONis the MODULE's package version; MSBuild writes it into the assembly version attributes,which are part of the metadata the MVID is computed over. So on today's
main, a full wave wouldpublish four bundles stating four different framework identities for one platform — every one of them
non-blank, and therefore every one of them accepted by a 400 that only asks whether the field is
stated.
That is the exact failure this issue exists to prevent, arriving through the door the fix opened.
A registry refusal keyed on stated-ness alone would have shelved all four and reported success.
Consequence for the arming criterion
Add a third condition, and read criterion 2 through it:
identity — that is what makes the value a platform build rather than a per-module accident. A
quiet window over a wave whose bundles disagree is a vacuous pass.
#3306 makes that true at the producer (drops
-p:Versionfrom the anchor build, guards it, andrefuses a module-local anchor in
ModulePackCommandfor every caller). Until it lands, do not arm —and when arming, consider whether the 400 should compare against the wave rather than only assert
non-blankness, since a per-module identity is exactly as useless to a consumer as an absent one and
strictly harder to notice.
Still open. Do not arm.
Measured 2026-09-04 against
fc4adc666and local builds ofMeshWeaver.Compiler.csproj.rbuergi commentedon Sep 5, 2026
Armed by #3340, merged — closing by hand, since the merge queue does not fire
Closeslinks here.Verified on
origin/mainafter the merge:ModulePublish.Validatecarries the named refusal, andthe runway warning at the publish endpoint is gone (it existed only as the measurement the arming
step reads).
Both halves of this issue's own criterion were measured before arming:
node-repo-module-pack.ymlpin past #3237 (da2bb12d3, 09-03 17:49Z)c41a34fda(09-04 05:57Z), MeshWeaver.SocialMediafec69fc66(09-03 21:27Z) — checked withmerge-base --is-ancestorin BOTH directions. Education and Reinsurance do not call the lane.built against: g7d644de95…; SocialMedia run 33941835795 (09-05 03:27Z)built against: cef92e9759…. Bothpublish: true, both green, so the producer-side refusal never fired.🚨 Worth keeping, because it nearly went the other way: MeshWeaver.Plugins' recent
mainruns arered, and "no successful run" would have read as "no evidence, do not arm". The failures are in
test-reposand a ratchet — the module-pack lane succeeded and published. An absent wave is nota quiet window, and a red workflow is not an absent wave.
The refusal is deliberately shape-agnostic:
g<sha>, a 32-hex MVID ands<hash>are allidentities a lane legitimately states, and only ABSENCE is refused —
AStatedIdentityOfAnyShape_IsAcceptedpins that from the other side so a future narrowing cannotpass by refusing everything. Mutation-proved: removing the refusal fails exactly the refusal test
and leaves the other three green.
ModuleBuildArchitecture's "Still to arm" section now records the arming and the evidence, and theRoadmap entry is closed.