Repository navigation
ci: core publishes ONE event and finishes — withdraw notify-dependents; the release wave is memex's, subscribers are the Deployment records - #3185
Merged
Conversation
…s; the release wave is memex's, its subscribers the Deployment records Maintainer, 2026-09-03: none of the top-level repos depends on another; memex issues the event that something has a new version, GitHub subscribes, core publishes an event and finishes. The GitHub→GitHub `notify-dependents` fan-out (main-cd, release-packages, the reusable) is deleted; the signed build fact POSTed into Hosting/PlatformBuilds is the one event core emits, and PlatformReleaseNotifyGuard.CoreDispatchesToNoRepository refuses a repository_dispatch sender in any workflow core runs on its own behalf (the two reusable satellite-context lanes are the ledger). The FrameworkBroadcast: Subscribers config seam (BroadcastToConfigured, the chart's four slots) is retired: the subscriber set is data in the mesh — the Hosting/Deployment records' registry-source mounts, derived by the Plugins-side inbox watcher — and the broadcaster's empty-set warning names the records. Docs rewritten to the event design; What's New entry. Pairs-with: Systemorph/MeshWeaver.Plugins#1241 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4xPnGYEbdu8AVvR5jytyj
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
It makes high-impact CI/CD release-chain changes (removing core cross-repo dispatch and shifting subscriber sourcing) that warrant careful human verification of the end-to-end release wave behavior.
Pull request overview
This PR updates MeshWeaver’s release-notification architecture so core CD emits only the signed build-fact POST into memex (Hosting/PlatformBuilds) and never directly dispatches into other repositories, while memex becomes the sole release-wave dispatcher based on Hosting/Deployment records (and the legacy subscriber config seam is removed).
Changes:
- Removes core-side
repository_dispatchfan-out (notify-dependents) and adds a test guard that refuses any reintroduction outside a small allowlist of reusable satellite-context workflows. - Retires
FrameworkBroadcast:Subscribersconfiguration and updates the broadcaster + tests to treat subscribers as mesh data (derived fromHosting/Deploymentregistry sources). - Updates docs, chart templates, and “What’s New” to reflect the new event flow and subscriber source.
File summaries
| File | Description |
|---|---|
| test/MeshWeaver.Documentation.Test/ReleaseDeliveryChainGuard.cs | Updates the “release delivery chain” ledger now that subscribers are no longer a config key. |
| test/MeshWeaver.Documentation.Test/PlatformReleaseNotifyGuard.cs | Adds/updates guard asserting core workflows do not send repository_dispatch, with a small sender allowlist. |
| test/MeshWeaver.Documentation.Test/FrameworkBroadcastEmptySubscribersGuard.cs | Updates expectations for the empty-subscriber warning to point to Hosting/Deployment records instead of config slots. |
| src/MeshWeaver.GitSync/FrameworkReleaseBroadcaster.cs | Removes the “configured subscribers” seam and updates empty-set warning text to the mesh-data subscriber source. |
| src/MeshWeaver.GitSync/FrameworkBroadcastOptions.cs | Updates options documentation to reflect that subscribers are no longer configuration. |
| src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-03-a-platform-release-reaches-plugin-repositories-through-memex.md | Adds a What’s New entry describing the new release event flow. |
| src/MeshWeaver.Documentation/Data/Architecture/RepositoryDependencyDirection.md | Updates architecture guidance to reflect “no dispatcher” in core. |
| src/MeshWeaver.Documentation/Data/Architecture/ReleaseEventBus.md | Updates the fan-out row and troubleshooting joints to the new subscriber source. |
| src/MeshWeaver.Documentation/Data/Architecture/ModuleBuildArchitecture.md | Notes that repository_dispatch release-follow is memex-emitted, with schedule as fallback. |
| src/MeshWeaver.Documentation/Data/Architecture/CrossRepoPairGate.md | Updates narrative on event direction and who emits the release-wave dispatch. |
| src/MeshWeaver.Documentation/Data/Architecture/ContinuousDeliveryContract.md | Rewrites the “release EVENT” section to the new one-emitter design and documents history. |
| src/MeshWeaver.Documentation/Data/Architecture/CiContentBake.md | Updates bake-follow guidance to reflect memex-emitted event and schedule fallback. |
| src/MeshWeaver.Documentation/Data/Architecture/ChartOwnershipAndRunnerPool.md | Updates chart ownership notes for the removed subscriber slots. |
| deploy/helm/templates/memex-portal/config.yaml | Removes rendering of FrameworkBroadcast__Subscribers__0..3 and documents retirement. |
| deploy/aks/values.aks.yaml | Removes the subscriber-slot values from the baseline AKS values file. |
| .github/workflows/release-packages.yml | Removes the package-release notify-dependents dispatch job and documents why. |
| .github/workflows/notify-dependents.yml | Deletes the reusable workflow that performed cross-repo dispatch from core. |
| .github/workflows/main-cd.yml | Removes notify-dependents job and updates “release wave” design docs in-file + preflight messaging. |
| .claude/skills/new-repo/SKILL.md | Updates repo-creation guidance to the memex-emitted event + schedule fallback model. |
Review details
- Files reviewed: 19/19 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+465
to
+469
| no credential that could reach a satellite's CI. `PlatformReleaseNotifyGuard.CoreDispatchesToNoRepository` | ||
| (`test/MeshWeaver.Documentation.Test`) fails the build if a `/dispatches` POST, a dispatch action or | ||
| an `event_type` payload returns to any workflow core runs on its own behalf; the one ledgered sender | ||
| is the reusable `node-repo-publish-bake.yml`, which runs in the CALLING satellite's context and wakes | ||
| that satellite's dependents (`meshweaver-upstream-published`). |
Comment on lines
+135
to
+141
| - **No dispatcher.** Core sends `repository_dispatch` to no repository. `notify-dependents.yml` was | ||
| withdrawn on 2026-09-03 (maintainer: *"core publishes an event and finishes"*): the release wave is | ||
| emitted by memex from the build fact core POSTs into `Hosting/PlatformBuilds`, to the repositories | ||
| the `Hosting/Deployment` records name as registry sources. `PlatformReleaseNotifyGuard.CoreDispatchesToNoRepository` | ||
| refuses a `/dispatches` POST in any workflow core runs on its own behalf; the reusable | ||
| `node-repo-publish-bake.yml` is the one ledgered sender, and it runs in the CALLING satellite's | ||
| context (a satellite waking its own dependents). |
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
…cation instead of dispatching; no ledger of senders Maintainer, 2026-09-03 09:05Z: "end of github pipeline must call memex, which must register release and publish event". node-repo-publish-bake.yml's dispatch-dependents job (read every installed repo's ci.yml, POST to their /dispatches) is replaced by register-publication — one signed POST of the bundle-publication record into the control instance's inbox, RED when unprovisioned or refused, the lane's last job; node-repo-tag-modules.yml's announce step goes the same way. Receivers resolve a memex-emitted meshweaver-upstream-published from client_payload.version; the broadcaster takes an optional payload so the watcher can name source/identity/images. PlatformReleaseNotifyGuard has no ledger — a dispatch sender in any workflow fails; UpstreamBuildGateGuard pins the lane's call. Docs carry the contract (three sentences + diagram) and the registration nodes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4xPnGYEbdu8AVvR5jytyj
rbuergi
marked this pull request as ready for review
September 3, 2026 09:31
rbuergi
added this pull request to the merge queue
Sep 3, 2026
rbuergi
added a commit
that referenced
this pull request
Sep 3, 2026
Copilot review. The reference pointed at /issues/3185; #3185 is the PR that made the release wave memex's. GitHub would redirect, but the URL should say what the thing is. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4xPnGYEbdu8AVvR5jytyj
6 of 26 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Core publishes ONE event and finishes — the release wave is memex's
Maintainer directive (2026-09-03): "None of the top-level repos should have any dependency to anyone else. It must be event based: (1) memex issues an event that something has a new version; (2) GitHub subscribes to this and triggers the build. Core publishes an event and finishes."
What was measured before touching anything
FrameworkReleaseBroadcaster(src/MeshWeaver.GitSync) has existed since 2026-08-23 with no caller in core or in MeshWeaver.Plugins (grep, both trees; the in-mesh call site carried a comment explaining why a since-retired NuGet-floor lane could not compile the reference). Loki,namespace="memex-cloud"(ci.7621), last 48 h: noDispatched,release broadcastorFrameworkReleaseBroadcasterline at all.repository_dispatchthe satellites received came from core's own CI —notify-dependents(Education/Reinsurance/SocialMedia at 07:21Z and 07:28Z today) — or from a sibling'smeshweaver-upstream-published(node-repo-publish-bake.yml'sdispatch-dependents, 23:15–23:53Z). Two independent emitters for one event is exactly the coupling the rule forbids.[PlatformBuilds] results thread failed: … AccessContext must never be null … hub=Hosting/PlatformBuilds, 2026-09-03 04:22Z, 05:21Z, 06:06Z, 06:53Z, 07:39Z, 07:41Z) — so even a wired hop would not have fired. Fixed in the pair PR below.What this PR does (core)
main-cd.ymlnotify-dependentsjob, the reusable.github/workflows/notify-dependents.yml, andrelease-packages.yml's package-release variant are deleted;delivery-verdictno longer lists the leg. TheDEPENDENT_DISPATCH_APP_*secrets stay inpreflightbecauseplugins-modules/plugins-bakestill read the Plugins content tree with them (content-app-id) — the assertion text now says so instead of calling them "the release wave's sender".notify-platform-update— the signed build fact POSTed intoHosting/PlatformBuilds— is the ONE event core emits; the header block above it is rewritten to the event design.PlatformReleaseNotifyGuard.CoreDispatchesToNoRepositoryscans every.github/workflows/*.ymlfor arepository_dispatchSENDER (/dispatchesPOST, a dispatch action,createDispatchEvent, anevent_typepayload; receivers do not count) and refuses any outside a two-entry ledger of reusableworkflow_calllanes —node-repo-publish-bake.yml(meshweaver-upstream-published) andnode-repo-tag-modules.yml(meshweaver-modules-published) — each asserted to run only as a reusable lane, i.e. in the CALLING satellite's context. A ledgered file that stops sending fails too.TheDispatchDetector_FiresOnEachSendingForm_AndIsSilentOnProsepins the detector.FrameworkBroadcastOptions.Subscribers/SubscribersEnvKeyPrefixandFrameworkReleaseBroadcaster.BroadcastToConfiguredare removed; the chart no longer rendersFrameworkBroadcast__Subscribers__0..3andvalues.aks.yamlno longer declares the slots. The subscriber set is DATA IN THE MESH — every repository aHosting/Deploymentrecord on the control instance serves as a registry source (pluginRepos[].isRegistrySource), derived by the Hosting module'sPlatformBuildInboxWatcher(pair PR) and passed toBroadcast(subscribers, version). The broadcaster's empty-set warning on the control instance now names the records, not a config key (FrameworkBroadcastEmptySubscribersGuardupdated);ReleaseDeliveryChainGuarddrops joint 2 (a guard asserting a key nothing reads is a false control) and its documented-source test asserts the records.ContinuousDeliveryContract.md(the release-EVENT section rewritten: one emitter, the event names, the subscriber source, what core does, the measured history),RepositoryDependencyDirection.md,CrossRepoPairGate.md(§ "The dependent reacts to core's events" — the transitional-emitter sentence replaced),CiContentBake.md,ModuleBuildArchitecture.md(release-follow),ReleaseEventBus.md,ChartOwnershipAndRunnerPool.md,.claude/skills/new-repo. What's New (Category: Feature): A platform release reaches plugin repositories through memex.The contract, exactly (maintainer, 2026-09-03 09:05Z: "end of github pipeline must call memex, which must register release and publish event")
notify-platform-update→Hosting/PlatformBuilds/_Inbox). The shared lanenode-repo-publish-bake.ymlnow does too: itsdispatch-dependentsjob (read every installed repo's ci.yml, POST to their/dispatches) is deleted and replaced byregister-publication— the lane's LAST job — which POSTs the HMAC-signed publication record (event: bundle-publication: source, identity, commit, tester image + digest, portal image, version, run) into the same inbox, RED (never skipped, never a warning) whenwebhook-url/webhook-secretare missing or the inbox refuses. Thenode-repo-tag-modules.ymlannounce step (meshweaver-modules-publishedto a hand-listed set) is deleted as well. Retired inputs/secrets (dispatch-dependents,dispatch-app-*,dispatch-token) stay DECLARED and ignored so callers pinned to an earlier lane still validate; each caller passeswebhook-url+webhook-secretwhen it moves its pin (the lane names them RED until then).meshweaver-upstream-publishedwithclient_payload.versionand noimage, both images now resolve from the version tag exactly as for a framework release (memex emits the identity by name); the legacy image-carrying payload still works for a lane pinned before this change.FrameworkReleaseBroadcaster.Broadcast(...)takes an optionalpayload(merged overversion/source), so the watcher can name the publishingsource,identity,image,platform_imageon a bundle-publication event.PlatformReleaseNotifyGuard.CoreDispatchesToNoRepositorynow has NO ledger — a dispatch sender in ANY workflow under.github/workflowsfails;UpstreamBuildGateGuard.TheLaneEndsByRegisteringWithMemex_AndDispatchesToNobodypins the lane's call (signed, RED, unconditional on inputs, no/dispatches, noevent_type).ContinuousDeliveryContract.md,ModuleBuildArchitecture.md(new "The release: the pipeline ends by calling memex"),CrossRepoPairGate.md,RepositoryDependencyDirection.md,Deployment.md(new "How a release reaches the fleet"), and the registration nodes named:Hosting/PlatformBuilds/<version>,Hosting/Publications/<identity>/<source>.The transitional TWO-PRODUCER state, named: with Plugins now receiving
meshweaver-framework-releasedfrom memex, the Plugins repo's own release-follow run bakes and seals its bundles for the released identity — while core CD'splugins-modules/plugins-bake-image/plugins-bakejobs still produce the same publication in-line. Two producers of one publication is the next removal: core's three jobs go once Plugins' event-driven bake is observed sealing for a release — measured by the registry serving Plugins bundles for an identity that the Plugins repo's run (not core CD) produced (the sealed publication'srun/repofields, and theHosting/Publications/<identity>/Pluginsregistration once the watcher writes it). Until then the two producers race to the same content-addressed key; the ledger's claim/wait protocol keeps that from double-building, and nothing consumes the wrong bytes.Named follow-ups (not in this PR set): the Plugins watcher REGISTERING those nodes and handling
event: bundle-publication(register +meshweaver-upstream-published, dependency-scoped through the registry's packagerequiresgraph so a publication cannot wake its own upstream); each node repo passingwebhook-url/webhook-secretat its pin bump; and — once Plugins receives the platform event and publishes its own bundles on it — removing core CD'splugins-bakejob, which then is a second producer of the same publication.Verification
dotnet build test/MeshWeaver.Documentation.Test -c Release -warnaserror→ 0 warnings, 0 errors (compilesMeshWeaver.GitSync).dotnet test MeshWeaver.Documentation.Test -c Release --no-build(whole project — the ratchets): see the CI run; local.trxattached in the log of this branch's session.check-workflow-timeouts.py --root .→ 0 violations;check-workflow-shell.py→ 0 live findings;actionlint main-cd.yml release-packages.yml→ exit 0.namespace="memex-cloud"—09:23:43Z [PlatformBuilds] release broadcast for 3.0.0-rc9.ci.7658: 3 dispatched, 1 NOT notified (Systemorph/education (401: …(and the same for ci.7657 at 09:23:47Z and ci.7655 at 09:23:52Z — the previously undeleted inbox backlog drained by the identity fix). Satellite side, all at 09:23:44–09:23:54Z with NO core CD promote in that window (CD runs were pending/queued/cancelled):repository_dispatch: meshweaver-framework-releasedruns on MeshWeaver.Plugins (33738591658/…98801/…605857 — Plugins is now IN the wave; core's never included it), MeshWeaver.Reinsurance (33738592702/…), MeshWeaver.Crm (33738600156/…); none on SocialMedia (not yet on the record — Systemorph/Memex#173 adds it). The one failure is the staleSystemorph/educationname (fix(graph): eliminate the compile-flake population (single compile driver + complete terminal write + search ordering) #173 fixes the record).[PlatformBuilds] release broadcast for <version>: N subscriber(s) dispatched.line in Loki and arepository_dispatchrun on a satellite whose payload carriessource: memex. Only then does core stop dispatching; otherwise the satellites would stop sealing bundles and every portal's self-update would hold.Deliberately not here
Deployments/memex-cloudrecord (Systemorph/Memex) still carries the fourFrameworkBroadcast__Subscribers__Nextras (now inert) and names Education by its pre-rename URL (Systemorph/education— a POST to a renamed repo answers 301, whichHttpClientdoes not re-POST, so that dispatch reportsNOT notifieduntil the record saysMeshWeaver.Education). Both are Memex-repo edits.Deployments/memex-cloud— the record was incomplete, not SocialMedia out of scope. Systemorph/Memex#173 adds it as a registry source, so it is in memex's wave under the mesh-derived set. Manufacturing stays out (no publish-bake yet, Manufacturing#31/Portal fixes #48). The record IS the subscriber list: adding a repository to the wave = adding it to the record.FrameworkBroadcast__Subscribers__Noverlay keys. Merged BEFORE this PR, the blank-guard on the next memex-cloud deploy would refuse the render (live non-empty → rendered""); merged AFTER, Memex'sconfig-key-coverageis red only until it lands (the chart no longer renders the key). So: this PR first, the Memex PR immediately after.meshweaver-upstream-published(satellite → its dependents, sent by the reusable publish-bake lane in the satellite's context) is a repo-to-repo edge the registry could also emit; out of scope here and ledgered explicitly.[InboxAnchor] anchoring Hosting/PlatformBuilds FAILED(memex-cloud 07:14Z, a 60 sSubscribeRequesttimeout, after which the anchor gives up until restart) is a separate defect.Pairs-with: Systemorph/MeshWeaver.Plugins#1241
🤖 Generated with Claude Code
Claude-Session: https://claude.ai/code/session_01G4xPnGYEbdu8AVvR5jytyj