Skip to content

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

Closed

Description

@rbuergi

Three satellites have NO pin-consistency gate — a wrong digest merges green

Measured 2026-09-06 while staging the coordinated platform-pin move for the 3.0.0 wave.

repo check-platform-pins.py "Both platform pins name ONE promoted build" pin sites a move must pass through
MeshWeaver.Plugins ✅ ✅ 14
MeshWeaver.Reinsurance ✅ ✅ 12
MeshWeaver.SocialMedia ❌ absent ❌ absent 4
MeshWeaver.Manufacturing ❌ absent ❌ absent 1
MeshWeaver.Crm ❌ absent ❌ absent 1 (a step output, not env:)

0 matches in the bottom three — this is not a gate that skips, it is a gate that does not exist.
🚨 The distinction matters because the fleet's usual failure is a skipped gate wearing a passing
tick; here there is nothing to skip. A pin move that names one build in one place and a different
build in another merges green in those three repos, and the first symptom is whatever the wrong
reference set does at bake or compile time.

Why this is a blocker-list item rather than a nice-to-have

The pins are moving right now as one coordinated set for the release wave, and they move again at
the tag. In Plugins and Reinsurance a half-moved set is caught mechanically. In SocialMedia,
Manufacturing and Crm it is caught by whoever notices.

The precedent is on record and it was expensive: a withdrawal that took a line it did not add
dropped a 4th pin, took core CD down for an hour, and every check was green
(#3344). That is
exactly the shape these three repos cannot detect.

What the gate would have to check

Not merely "is the digest well-formed". The two properties that matter:

  1. Every pin in the repo names ONE promoted build — the set name, the source ref, the tester
    digest and the portal digest must be mutually consistent, including sites outside the env: block.
  2. The digests actually resolve in ACR at the tag the set names (--check-tags). 🚨 This one is
    load-bearing given Retention: the locks hold nightly — apply the decided 30-day window to the paused purge-old-images, read production back, and inventory bundles/active builds as consumers #3438: ACR retention has already deleted a pinned tester digest and wedged an
    entire repo's CI including its own nightly.

🚨 A placeholder must fail RED, not silently pass. check-platform-pins.py matches
sha256:[0-9a-f]+, so a non-hex placeholder digest reads as "no literal pin present" and the check
passes having verified nothing. Any gate added here must be falsified against that case specifically
— it is the vacuity trap this fleet keeps rediscovering (most recently in #3441, where a release
gate read its denominator from the store it was judging).

Site counts are not uniform, which is the other half of the risk

A single sed is wrong everywhere. From the same survey:

  • Plugins: 14 sites — set 1, source sha 7 (four platform-ref: values, of which two are
    literals
    at L2994/L3153 and two are derived ${{ needs.platform-ref.outputs.sha }}), tester
    digest 3, portal digest 3. A digest pasted into env: alone leaves four stale pins.
  • Reinsurance: 12 sites — set 1, source sha 9, tester 1, portal 1.
  • 🚨 clients/react/src/i18n/catalog-source.json holds the same core sha and is NOT a platform
    pin.
    A repo-wide replace silently moves the i18n mirror's comparison ref, whose drift guard
    compares VALUES against a pinned core commit. Excluded deliberately.
  • Education pins under entirely different names — MW_PORTAL_DIGEST, MW_MIGRATION_DIGEST,
    MW_TEST_DIGEST — so any fleet-wide gate keyed on MW_IMAGE_DIGEST/MW_PLATFORM_SET would report
    Education clean while checking none of its pins. Another absence that reads as a pass.

Provenance

Surfaced while staging the five coordinated draft PRs for the wave (Plugins#1415, Reinsurance#170,
SocialMedia#138, Manufacturing#56, Crm#56). Filed at the release session's request against #3355's
blocker list; whether it blocks the tag is theirs to decide once the site count is visible.

Activity

  1. rbuergi commented on Sep 6, 2026

    @rbuergi
    ContributorAuthor

    The generalised form — and a second instance, reproduced independently within the hour

    The Education line in the description ("a fleet-wide gate keyed on MW_IMAGE_DIGEST/MW_PLATFORM_SET would report it compliant") is not a footnote to this issue's thesis. It is a second instance of it, and the general form is worth stating outright because it is what any gate added here must be falsified against:

    🚨 A count of occurrences cannot distinguish "the thing exists and omits X" from "the thing does not exist". Both read 0. Whenever a sweep's negative has two possible causes, the sweep must measure the denominator too.

    Instance 1 — the pin sweep (this issue). grep -c MW_IMAGE_DIGEST over the fleet returns 0 for MeshWeaver.Education. That 0 does not mean "Education has no unpinned platform digest to worry about"; it means Education pins under MW_PORTAL_DIGEST / MW_MIGRATION_DIGEST / MW_TEST_DIGEST and the sweep checked none of its three pins. A fleet pin-consistency gate keyed on the common names would report Education compliant while verifying nothing — a gate that does not exist, wearing a passing tick, which is precisely this issue's thesis.

    Instance 2 — measured 2026-09-06, in the act of relaying instance 1. A sweep for the publish-bake webhook defect counted webhook-url: occurrences per repo and read every 0 as "the caller omits it":

                        reusable-caller   webhook-url:
    Education                 0                0     ← no caller AT ALL
    SocialMedia               1                0     ← caller, unwired
    Manufacturing             1                0     ← caller, unwired
    Crm                       1                0     ← caller, unwired
    Plugins                   1                0     ← caller, unwired
    Reinsurance               1                1     ← already correct
    

    Education's 0 meant "there is no caller to omit it" — it runs a local publish-bake with no Register the publication with memex step at all. Without the reusable-caller column (the denominator), Education is indistinguishable from the four genuinely-broken repos. The corrected count is four, not five.

    That the same trap was walked into while relaying a warning about the same trap is the argument for encoding it in a gate rather than in guidance.

    Why this belongs on this issue specifically

    This issue is about gates that do not exist reading as gates that pass. The denominator failure is the mechanism by which that happens in a sweep: the numerator alone is a measurement whose negative is ambiguous, so a clean-looking 0 is produced by both the compliant case and the never-examined case.

    It is also the same family as this fleet's standing rule that config audits go by LENGTH, never keys[] — a key present with an empty value reads identically to an absent one. Same defect, different surface.

    Concretely, for whatever gate lands here: it must emit a per-repo denominator — "N pin sites found, of which M verified" — and fail RED on N = 0 rather than reporting a pass. A repo whose pins the gate cannot locate (because they are named differently, as Education's are) has not been checked, and "not checked" must not be spelled the same way as "clean". This is the same acceptance criterion the description already sets for the non-hex placeholder digest, applied one level up: there, a pin the regex cannot match reads as "no pin"; here, a repo the sweep cannot key on reads as "no findings".

  2. rbuergi commented on Sep 6, 2026

    @rbuergi
    ContributorAuthor

    Requirement 2 of this issue is now covered fleet-wide by #3462 — requirement 1 is not

    #3462 (the #3438 guard half) lands .github/scripts/check-pinned-digests.py + a daily
    pinned-digests.yml. Mapping it onto this issue's "What the gate would have to check":

    • ❌ 1. "Every pin names ONE promoted build." Not covered, and deliberately out of scope there.
      Set-consistency is a property of one repository's own workflow and stays this issue's ask.
    • ✅ 2. "The digests actually resolve in ACR." Covered, for every repository the
      meshweaver-cloud App reaches — which is org-wide (repository_selection=all), so SocialMedia,
      Manufacturing and Crm are swept whether or not they ever gain a local gate.
    • ✅ The placeholder-vacuity trap this issue names. sha256:PLACEHOLDER is a failure there,
      not an absence, and so is a truncated digest that sha256:[0-9a-f]+ would happily accept. Both
      are asserted in a self-test that runs on every core PR.

    One correction to the survey, measured against the live registry 2026-09-06

    This issue's table is right that the three repos have no local gate. It is worth adding that
    they do all pin, and that a fleet check keyed on variable NAMES would have reported them clean:
    Manufacturing writes MW_IMAGE_DIGEST, SocialMedia and Crm write theirs into a $GITHUB_OUTPUT
    step output, and Education uses three names of its own. #3462 extracts by shape for that reason.

    Live sweep, denominator printed — 8 scanned · 6 pin · 2 do not · 15 pin declarations over 9
    distinct digests
    · 12 resolve · 3 GONE · 0 indeterminate · 0 malformed.

    🚨 The 15-vs-9 gap is directly relevant to the "site counts are not uniform" section here. The
    fleet pins 9 manifests from 15 places, so a pin move edits 15 sites, and the three GONE are
    Education's — #3438 itself, still live on main as of this comment:

    GONE  memex-migration@sha256:d81df6cc79ad…   ci.yml:MW_MIGRATION_DIGEST
    GONE  memex-portal-ai@sha256:dab2a7b3a7e1…   ci.yml:MW_PORTAL_DIGEST
    GONE  mw-plugin-test@sha256:df19f10afc1f…    ci.yml:MW_TEST_DIGEST
    

    Education's bump has to move all three, not just the tester.

    Not closing anything here — requirement 1 is untouched. Background:
    Doc/Architecture/PinnedImageRetention.

  3. added 5 commits that reference this issue on Sep 6, 2026
  4. rbuergi commented on Sep 6, 2026

    @rbuergi
    ContributorAuthor

    Closed by hand — all four parts are merged and each merge commit verified an ancestor of its repo's main

    The merge queue does not fire Closes links, so this is manual and the ancestry was measured, not
    assumed.

    repo PR merge commit ancestor of origin/main
    Systemorph/MeshWeaver (platform half) #3486 cac18fd9ce40c312a50966bdb4c4b93d1136b54c ✅
    Systemorph/MeshWeaver.SocialMedia #141 14c917e490091e9fe4c36523bb4de9da98aaca4d ✅
    Systemorph/MeshWeaver.Crm #58 d9e2768cba6d415f28d3281ce10b21ec91ebcac8 ✅
    Systemorph/MeshWeaver.Manufacturing #58 f0a2b031a005191070d619294bc85e5e974ad6ba ✅

    RED-then-GREEN in every repo — a gate nobody has watched fail is not a gate

    Each satellite branch carried a deliberate half-move of ONE of its two platform-image-digest:
    literals — the publish-bake copy, which never runs on a pull request, so nothing but the new
    pin-set job could see it. This is the #3344 shape reproduced.

    repo RED run (the gate firing) invariants named
    SocialMedia https://github.com/Systemorph/MeshWeaver.SocialMedia/actions/runs/34065822083/job/101574270633 I1 + I2
    Manufacturing https://github.com/Systemorph/MeshWeaver.Manufacturing/actions/runs/34065951622/job/101574607639 I1 + I2
    Crm https://github.com/Systemorph/MeshWeaver.Crm/actions/runs/34066028662/job/101574814744 I1 + I2
    ##[error]<repo> — `platform-image-digest` is written 2 times in ci.yml with 2 DIFFERENT values. A pin move edited some and not others.
    ##[error]<repo> — the image `memex-portal-ai` is pinned to 2 DIFFERENT digests in the same repository. Half of a set was moved.
    ##[error]Process completed with exit code 1.
    

    Then restored, and Platform pins name one build is green on each repo's main.

    The denominator, printed — and why "3" is the number, not "4 / 3 / 4"

    repo workflow files digest pin sites classified unclassified roles lane calls literal platform-ref: source refs
    SocialMedia 5 3 3 0 2 8 1 1
    Manufacturing 2 3 3 0 2 4 0 0
    Crm 4 3 3 0 2 6 1 0

    🚨 The issue's "4 / 3 / 4 literal pin sites" and the extractor's "3 / 3 / 3" are counting different
    things, and neither is wrong.
    The extractor's denominator counts literal digest pins; the
    issue's figure adds the literal core-commit refs a pin move must also pass. Under that second
    accounting SocialMedia is arguably 5, not 4 — it carries MW_PLATFORM_REF and a literal
    platform-ref: on its node-repo-module-pack call. The number CI now prints on every PR is the one
    to reason with, because it is the one the verdict is computed from.

    No unclassified pin in any of the three (I5 has nothing to report), and no pattern was widened to
    make it so.
    All three jobs run with --require-pins, so a ZERO denominator is RED (I6) — verified
    separately against a tree with workflows and no pin: exit 1 with "not one literal platform pin was
    found… zero means the extractor stopped matching, not that pinning stopped"
    , versus exit 0 and
    "a measured zero, not an unchecked one" without the flag.

    Two premises the adoption corrected

    1. The three repos are not variations of one shape. Two write the tester pin into a
      $GITHUB_OUTPUT and one into an env: key; one declares MW_PLATFORM_REF and two do not; one
      passes no literal platform-ref: at all. So I3 and I8 have nothing to compare in Manufacturing
      today
      — and the printed denominator says so instead of the verdict quietly reading "consistent".

    2. The gate could not be fetched at each repo's existing platform pin, because that value is
      older than the script. Hence MW_PIN_GATE_REF, a script-DEFINITION pin in the same category as
      a uses: …@<sha> lane ref — and named so that I8 cannot see it: a guard ref matching
      platform.?ref would be read as a platform source ref and demanded a lane pinned at it, which
      is exactly the coupling this gate refuses to assert (Plugins#1268).

    🚨 A finding outside the issue's scope: Manufacturing had never run the platform's OTHER guards

    check-workflow-timeouts.py and check-pr-secret-preflight.py live inside
    node-repo-validate.yml. MeshWeaver.Manufacturing hand-rolls validate and does not call that lane,
    so neither had ever executed against it — while both siblings measured 0 violations the same day:

    ::error file=.github/workflows/ci.yml::job 'preflight'   has no timeout-minutes …
    ::error file=.github/workflows/ci.yml::job 'validate'    has no timeout-minutes …
    ::error file=.github/workflows/ci.yml::job 'tag-modules' has no timeout-minutes …
    check-workflow-timeouts: 6 job(s) checked, 4 reusable-call job(s) exempt, 3 violation(s), cap=45 min
    

    GitHub's default is 360 minutes against a fleet cap of 45. Capped in MeshWeaver.Manufacturing#58.
    The transferable rule — a repository that keeps a hand-rolled copy of a shared lane silently opts
    out of every guard that lane grows later, and nothing anywhere reports it
    — is the same shape as
    the pin gate that did not exist, and is written up in #3502.

    Branch protection updated in the same change set

    Platform pins name one build is now a required context on all three repos' main (each measured
    first: all three are classic protection, strict: false, their only ruleset being the Copilot
    review — no ruleset carries required checks here). No context was RENAMED: a job was added, no lane
    was adopted. The five open PRs that predate the job were each told, on the PR, that they need a
    main sync.

    Work products

    • Platform-level → docs(#3454): what the satellite pin-set lane looks like, and how the three repos differ #3502 (Doc/Architecture/PinSetConsistency): the satellite lane's shape, why
      MW_PIN_GATE_REF must be invisible to I8, the per-repo difference table, and the hand-rolled-lane
      rule. Held as a draft on purpose — core main is inside a release wave and a docs merge
      enqueues another CD run; take it out of draft when the release session says the wave has settled.
    • Repo-specific → a Platform pins section in each satellite's own AGENTS.md, carrying that
      repo's pin table, its denominator, and the grep-the-old-value move procedure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions