Repository navigation
MeshWeaver.Plugins runs NO secret preflight and neither it nor Manufacturing calls node-repo-validate — a hand-rolled lane opts out of every guard the lane grows later #3504
Description
Measured: two repos do not call node-repo-validate, and one of them runs NO secret preflight at all
Found while applying the rule that #3454's work produced, rather than by looking for it:
The guards live INSIDE the shared lane. A hand-rolled copy therefore opts out of every guard that
lane grows LATER — silently, retroactively, and invisibly from the repo that made the copy.
check-workflow-timeouts.py and check-pr-secret-preflight.py are both invoked inside
node-repo-validate.yml. Neither existed when the older copies were made, so nobody chose to skip
them — the copy chose, months of commits later. A repo that forked a lane before a guard was written
looks identical to one that passes it.
The adoption matrix, measured 2026-09-07 against each repo's main
Read from each repo's actual workflow files, not from AGENTS.md — which named Education a caller
while Education called none (that is the precedent, #258).
| repo | validate | compile-check | gate | module-pack | publish-bake | tag-modules | platform-ref-bump |
|---|---|---|---|---|---|---|---|
| MeshWeaver.SocialMedia | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| MeshWeaver.Reinsurance | ✅ | ✅ | ✅ | — | ✅ | ✅ | — |
| MeshWeaver.Crm | ✅ | ✅ | ✅ | — | ✅ | ✅ | — |
| MeshWeaver.Education | ✅ | — | — | — | ✅ | ✅ | — |
| MeshWeaver.Manufacturing | ❌ | ✅ | ✅ | — | ✅ | — | — |
| MeshWeaver.Plugins | ❌ | — | ✅ | ✅ | ✅ | — | ✅ |
A missing lane is not automatically wrong — a repo that packs no modules has no use for
module-pack. validate is different, because it is where the fleet-wide guards run.
What each of the two actually has
| repo | check-workflow-timeouts.py |
check-pr-secret-preflight.py |
|---|---|---|
| MeshWeaver.Manufacturing | ✅ inline in ci.yml (+ auto-arm.yml) |
✅ inline in ci.yml |
| MeshWeaver.Plugins | auto-arm.yml |
🚨 not invoked in any workflow |
Manufacturing is covered today and fragile tomorrow — its coverage came from #3454's PR adding
the two calls by hand, which is exactly the copy that will not carry guard number three.
🚨 Plugins has NO secret-preflight coverage, and it is the worst repo for that to be true of. It
is the largest satellite, carries the most lanes and the most secrets, and the guard exists for the
failure mode Plugins is most exposed to: a secret consumed by a PR-reachable job that no preflight
asserts surfaces late, wearing a message that names no secret. AGENTS.md cites Reinsurance#128 —
compose-sealed-modules.sh: --registry-url needs --registry-key was an absent MW_REGISTRY_KEY
with preflight GREEN. Nothing today would stop the same thing in Plugins.
Its check-workflow-timeouts.py coverage is also thinner than it looks: auto-arm.yml is a
different workflow with a different trigger from the one validate would run on.
What to do, and the two traps that make it not a one-liner
Adopt node-repo-validate.yml in both repos.
- 🚨 Adopting a shared lane RENAMES that repo's required status-check contexts to
<caller job> / <name>. That must land in the SAME change as the branch-protection or ruleset
update, or the repo ends up requiring a context nothing posts — and an absent required context
counts as SATISFIED, so the failure is a silently unguarded repo, not a stuck PR. - 🚨 Branch protection is per-repo and lives in one of two places. Measured: core and Memex use
rulesets (the classic endpoint 404s, which read as "unprotected" is simply wrong);
MeshWeaver.Plugins uses classic protection with 5 contexts; the three satellites Three satellites verify nothing about the digest they pin — SocialMedia, Manufacturing and Crm have no pin-consistency gate at all, so a half-moved set merges green #3454 touched
are classic withstrict: false. Check both endpoints, per repo, at the time of the change. - A new required input on the lane would be a silent startup failure for existing callers, so
the adoption must not need one.
Plugins first — it is the one with an actual hole rather than a fragile patch.
Why this is filed rather than fixed
Plugins is mid-release: its platform pin is moving (Plugins#1429), its staleness bound is being
managed, and renaming its required contexts while a tag is being cut off a sealed set is the wrong
hour for it. This is deliberately queued behind the 3.0.0 wave.
Related: #3454 (where the rule came from), #258 / the Education adoption, Reinsurance#128
(the incident the secret preflight exists for), and Doc/Architecture/ModuleBuildArchitecture
("never hand-roll a repo's build; a deviating repo is behind, not different").
Activity
rbuergi commentedon Sep 7, 2026
STAYING OPEN — Manufacturing adopted, Plugins has not, and the contexts prove it
Verified 2026-09-07T12:25Z. This issue names both repos, so it closes only when both adopt the
lane. One has.
Manufacturing — done, contexts renamed in the same change
Systemorph/MeshWeaver.Manufacturing#63—merged = true,merged_at = 2026-09-07T09:05:20Z.repos/Systemorph/MeshWeaver.Manufacturing/rulesets→ one ruleset,20625978"Copilot review for default branch", active — no required-status-checks rule.
repos/Systemorph/MeshWeaver.Manufacturing/branches/main/protection→ classic,strict: false,6 contexts, and the two that matter now carry the caller-job prefix:
validate / Validate node reposandcompile-check / Compile every NodeType (vs core).So Manufacturing did not end up requiring a context nothing posts. ✅
MeshWeaver.Plugins — NOT adopted
Systemorph/MeshWeaver.Plugins#1453("fix(ci): call the shared validate lane instead of copyingit") —
state = open,merged = false,mergeable_state = dirty. It needs a merge from mainbefore it can even run CI.
rulesets→ one ruleset,19008635"Copilot review for default branch", active — norequired-status-checks rule.
branches/main/protection→ classic,strict: false, 5 contexts, all un-prefixed:Validate node repos,Compile every NodeType (vs core),Build + test the portal hosts,Module bundles / All selected bundles built,Module bundles (floor) / All selected bundles built.The un-prefixed names are the direct evidence that the adoption has not landed. Plugins — the repo
this issue calls "the one with an actual hole rather than a fragile patch", with no
check-pr-secret-preflight.pyinvocation in any workflow — is still unguarded.🚨 One correction to #3575's own table, which lists Plugins as "0 (adopted, #1453)": that row
anticipates a merge that has not happened. #3575 is the durable write-up and merged correctly
(
ce09ad62a); the adoption it describes is half-real.Also spun out of this work, already tracked
#3576 — Crm, SocialMedia and Education call
node-repo-validateat a pin that predates thesecret guard (4 unasserted PR secrets each,
MW_REGISTRY_KEYamong them). That is the "calling thelane is necessary and not sufficient" half and is not this issue.
This issue closes when Plugins#1453 merges and Plugins' required contexts are renamed to
<caller job> / <name>in the same change.Measured 2026-09-07T12:25Z over REST:
pulls/63,pulls/1453, and for each repo BOTHrulesetsandbranches/main/protection— neither classic endpoint 404'd, so rulesets were checkedfor a required-status-checks rule and carry none.
rbuergi commentedon Sep 7, 2026
Plugins#1453 is now UP TO DATE and its own gate is GREEN — blocked only on the platform pin
Measured 2026-09-07T15:05Z. Two of the three things this issue waits on are done; the third is not mine.
1. The conflict is resolved and
mainis infix/3504-plugins-adopts-node-repo-validatewasdirty(one conflict:Hosting/manifest.lock,both sides having bumped
moduleVersion/versionoff their own trees). Resolved the sanctionedway —
scripts/gen-manifests.py --resolve, which regenerates over the merged tree, rather thanby picking a side (
scripts/merge-manifest-lock.py's header explains why a merge driver cannot:it runs before git has written the other paths). Result
1.13.1= main's1.13.0(index.jsonmoved to
"1.13") plus the patch the branch's ownPlatformBuilds.mdedit earns.The resolution was diffed against both parents, because a careless one here has silently
reverted merged fixes in this fleet before:
origin/main..HEADci.yml,check-shared-lanes.py, the allow file, the two skills,PlatformBuilds.md, the lock, the What's New note)3ed781a7..HEADci.yml's 63 changed lines are identical toef3ffcb7..origin/main'sHead is now
03cec7ae.2. 🚨 The adoption WORKS —
validate / Validate node reposis green on the first run that had itRun
34135660815:Repo policy gateshas exactly one failing step, and it is not this diff:Reproduced locally against the same inputs:
MW_PLATFORM_SET 3.0.0-ci.7938/MW_PLATFORM_REF aa4075832are 227 commits and 18.0 h behind coremain(5453be493) — bounds 120 and 24 h.The pin move is Plugins#1463, whose own CI failed at 11:12Z on
Portal hosts (shard 3) → Test the platform projects that moved here. That PR is owned elsewhere and untouched here.So the required contexts cannot go green until the pin lands. Everything else on the branch is
green locally too:
check-shared-lanes.py(self-test 11/11, 7 lane calls, 0 violations),check-platform-pins.py,check-pr-secret-preflight.pyat the pinned core (5 reachable,2 asserted, 0 violations — the three exemptions hold),
check-workflow-timeouts.py(18 jobs, 7 reusable-call exemptions, 0 violations),
gen-manifests.py --check,validate-repos.py(260 nodes / 61 repos),check-modules-published.py,check-skip-sets.py,check-test-projects.py,check-build-invocations.py,check-test-references.py,check-main-runs-not-cancelled.py.3. The context rename is staged, deliberately NOT applied yet
Measured again from both endpoints (a 404 on the classic one would have meant "look in
rulesets", never "unprotected" — neither 404'd):
rulesets→19008635Copilot review for default branch, active, no required-status-checks rule.branches/main/protection→ classic,strict: false,enforce_admins: true, 5 contexts, all un-prefixed.The target set is
validate / Validate node repos+Repo policy gates+Compile every NodeType (vs core)+Build + test the portal hosts+ the twoModule bundles… / All selected bundles built.🚨 Applying it before the merge would make the deadlock worse, not better. The bare
Validate node reposis never published again once #1453 lands, and the two new contexts are notpublished by any branch that predates it — so flipping protection now would leave every open
Plugins PR unable to satisfy the required set, including Plugins#1463, which is the one PR that
unblocks #1453. The rename therefore goes on immediately after the merge, in the same operation
window, as this issue asks.
What is left
One thing: Plugins#1463 lands → merge
maininto #1453 again → green → merge → rename thecontexts → close this. Nothing else about the adoption is outstanding.
Read over REST only.
pulls/1453,pulls/1463,actions/runs/34135660815(/jobs),actions/runs?head_sha=c5fd9d004…, and both protection endpoints for MeshWeaver.Plugins.rbuergi commentedon Sep 8, 2026
Backlog audit, 2026-09-08: leaving this open for a concrete remaining step.
Systemorph/MeshWeaver.Plugins#1453 has now merged (2026-09-08T11:45:46Z,
10768c9f015ffc314f7f73fc7c4feaf0d1d1b0ad), so the earlier discussion's merge blocker is stale. However, the required-context rename has NOT happened.Fresh REST reading of Plugins branches/main/protection still requires the old
Validate node reposcontext, plusCompile every NodeType (vs core),Build + test the portal hosts, and the two module-bundle aggregate contexts. It requires neithervalidate / Validate node reposnorRepo policy gates. Also checked rulesets: the sole active ruleset 19008635 has deletion, non_fast_forward and copilot_code_review rules, with no required-status-checks rule supplying the missing contexts.Therefore the issue's explicit closure condition (adoption AND matching branch protection) is still unmet. The remaining action is to replace the stale validation context with the shared lane's emitted context and require Repo policy gates, preserving the other required checks. No protection settings were changed by this audit.
rbuergi commentedon Sep 9, 2026
Completed the remaining protection update for #3504. Plugins main now explicitly requires
validate / Validate node reposandRepo policy gates, both bound to GitHub Actions app 15368. Read the saved settings back successfully;strict: falseand all five existing required checks are preserved.Correction to the previous audit note: current ci.yml has a
validate-name-shimwhich republishes the shared lane result asValidate node reposand fails unless that lane succeeds. The old context therefore was not an unenforced check. I retained it alongside the new names, avoiding a transition with weakened protection. Four currently open PR heads already emit both new names; #1529 has no such reported checks and must update/run the current workflow before merging.Both adoption PRs are merged (Manufacturing#63 and Plugins#1453). Manufacturing protection also still requires its shared validation and repository-policy checks. With the explicit Plugins protection requirements now saved, closing as completed. No workflow or application source was changed for this repair.