Skip to content

fix(mesh): the router names a spokesman — no one publishes from the main hub, and gone-subscriber stream-end drops go quiet - #2928

Merged
rbuergi merged 5 commits into
mainfrom
fix/router-spokesman
Sep 1, 2026
Merged

rbuergi merged 5 commits into
mainfrom
fix/router-spokesman

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Kills two measured log floods at the root ('no one must ever publish from main hub'):

  1. Recycle announcements no longer carry the router's identity: MeshBuilder registers a RouterCarrier spokesman (the nodeops execution hub — the seam all node CRUD already uses); Workspace resolves it when its parent is router-typed. RecycleStrandsLiveSubscriberTest (the production strand repro) passes in 587ms — a naive climb-past-the-router instead hung it a full minute, which is why the spokesman exists.
  2. StreamEndedEvent to an already-departed subscriber drops at Debug — a typed no-op ('nothing more comes' to someone who already left); every other StreamMessage drop stays WARN because those are real signal.

🤖 Generated with Claude Code

…okesman, and a terminal notice to a departed subscriber is Debug

'No one must ever publish from main hub' + the log flood ('we get tons of this … get rid of
all of this'), maintainer, 2026-09-01. Two roots, measured on the compile+render gate and the
live portal:

1. Workspace's recycle announcement posted StreamEndedEvent through Configuration.ParentHub —
   for a per-node/activity hub that IS the mesh router, so every recycled activity produced a
   ROUTER_TRAFFIC violation ('StreamEndedEvent has the mesh hub as sender'). The router now
   registers a RouterCarrier spokesman (MeshBuilder → the nodeops execution hub, the same seam
   every node-CRUD path already hops onto); Workspace resolves it when the parent is
   router-typed, falls back to the router's own non-router parent, and with neither present
   keeps the documented change-feed-latch recovery. RecycleStrandsLiveSubscriberTest (the
   production strand repro) passes in 587ms — the announcement still lands, without the
   router's identity. (A naive climb-past-the-router hung it for a full minute: in the
   Monolith host the router is the only parent, which is exactly why the spokesman exists.)

2. A StreamEndedEvent dropped because the target stream is ALREADY GONE is a typed no-op —
   its entire meaning is 'nothing more comes', which a departed subscriber knows by
   definition — yet it logged one WARN per released reader (Information+ ships to Loki).
   The drop is Debug for that one type; every other StreamMessage drop (a lost Full renders a
   region blank) stays a warning. Level change justified by type semantics, not convenience.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 31, 2026 23:37
@rbuergi
rbuergi enabled auto-merge August 31, 2026 23:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces ROUTER_TRAFFIC-related log floods by ensuring recycle announcements don’t originate from the mesh router hub and by downgrading benign StreamEndedEvent drops to Debug.

Changes:

  • Introduces a RouterCarrier “spokesman” abstraction and registers it in MeshBuilder to route router-adjacent infrastructure posts through the nodeops execution hub.
  • Updates Workspace recycle announcements to avoid router-stamped senders by resolving a non-router carrier (via RouterCarrier or a non-router parent).
  • Changes DataExtensions logging so StreamEndedEvent drops for already-gone subscribers log at Debug, while other dropped stream messages remain Warning.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/MeshWeaver.Messaging.Hub/RouterCarrier.cs Adds a router “spokesman” record used to select a non-router hub for infrastructure posting.
src/MeshWeaver.Mesh.Contract/MeshBuilder.cs Registers a default RouterCarrier mapping the router to NodeOperationExecutionHub().
src/MeshWeaver.Data/Workspace.cs Uses RouterCarrier (or a safe parent fallback) to avoid router-stamped recycle announcements.
src/MeshWeaver.Data/DataExtensions.cs Lowers log level for benign StreamEndedEvent drops while keeping other drop diagnostics at Warning.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +158 to +169
IMessageHub? spokesman = null;
try { spokesman = carrier.Configuration.Get<RouterCarrier>()?.Resolve(carrier); }
catch { /* teardown probing must never throw */ }
if (spokesman is null)
{
IMessageHub? up;
try { up = carrier.Configuration.ParentHub; }
catch { up = null; }
spokesman = up is null || ReferenceEquals(up, carrier)
|| string.Equals(up.Address.Type, AddressExtensions.MeshType, StringComparison.Ordinal)
? null : up;
}
@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

793 tests   789 ✅  3m 19s ⏱️
  7 suites    4 💤
  7 files      0 ❌

Results for commit 5cde0e3.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 136 tests   1 136 ✅  3m 52s ⏱️
    7 suites      0 💤
    7 files        0 ❌

Results for commit 5cde0e3.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 165 tests   1 165 ✅  4m 51s ⏱️
    7 suites      0 💤
    7 files        0 ❌

Results for commit 5cde0e3.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    8 files      8 suites   4m 17s ⏱️
1 492 tests 1 491 ✅ 1 💤 0 ❌
2 195 runs  2 194 ✅ 1 💤 0 ❌

Results for commit 5cde0e3.

♻️ This comment has been updated with latest results.

…retention edit — empty path, every prebuilt publisher red

The retention-days insertion (#2925) landed INSIDE the one multi-line 'path: |' block in the
lane, leaving upload-artifact with 'Input required and not supplied: path' — measured on the
first ec1ef1b lap (Plugins run 33451338438, Markdown.Collaboration; the same lap also proved
prepare-once live: 'platform image: cache HIT — docker load, no ACR' on both images).
retention-days moves below the block where it belongs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 152 tests   1 152 ✅  8m 30s ⏱️
    7 suites      0 💤
    7 files        0 ❌

Results for commit 5cde0e3.

♻️ This comment has been updated with latest results.

rbuergi and others added 3 commits September 1, 2026 01:53
… scripts, referenced from AGENTS.md

'unify buildprocess' + 'centralize scripts as best as possible' + 'document this big time and
reference in all the agents.md in all projects' (maintainer, 2026-09-01).

- Doc/Architecture/ModuleBuildArchitecture is now THE build process: the full pipeline
  (select → prepare-once → one-workspace build → pack/tests fan out → verify),
  content-addressed blob outputs (unchanged ⇒ no compile — incremental CI below the diff
  selector), the compiler doctrine, implementation-framework gates, quiet-CI policy, the
  night's measured numbers, the per-repo adoption contract, and the roadmap (one-workspace
  lane, in-image verbs, #2926 runners).
- compile-check.py is CENTRALIZED: the canonical copy lives at .github/scripts/ and the
  node-repo-compile-check lane fetches it at the pinned platform ref (the
  compose-sealed-modules.sh pattern — asserted RED when unavailable, no drifted-local
  fallback; three satellite copies had already diverged). Callers keep only their allow-file;
  MW_REPO_ROOT/MW_ALLOW_FILE carry the caller's tree into the platform's script, with the
  legacy beside-the-repo layout intact for unmigrated pins.
- Core AGENTS.md carries the doctrine reference; every satellite AGENTS.md gets its own in the
  rollout branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… and the leftover Debug pin is gone

Measured on shard 1 of this PR's own lap (job 99686210763): 356 'UpdateImpl: adds/updates/
deletes' Debug lines and the same 'Opening gate' repeated per update for the same path
dominated the console ('pls check github logs yourself and remove spam', maintainer,
2026-09-01). Three roots, three fixes:

- the gate-open ANNOUNCEMENT fires once per source (OpenGate itself stays idempotent per
  update); the same line per heartbeat carried no information after the first;
- per-beat counts are TRACE (version re-stamps make updates=1 on nearly every emission);
  Debug speaks only when the SHAPE changed (adds or deletes);
- test/MeshWeaver.Hosting.Monolith.Test/appsettings.json carried
  'MeshWeaver.Graph.MeshNodeTypeSource: Debug' — committed residue of a past investigation,
  now removed. (Query.Test's Debug pins STAY: deliberately scoped, documented with trx-size
  receipts.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e closure — no module waits for an artifact it does not reference

The first run that ever ACTIVATED prebuilt-modules (Plugins 33451338438) measured the #2922
wait indiscriminate: every container job — consumers and non-consumers alike — sat the bounded
wait for every wanted module, and with the producers' uploads broken by the path: split that
was a uniform ~680s of pure idling per job, poisoning the whole benchmark. The wait now runs
only for modules in THIS entry's transitive ProjectReference closure (computed by a 12-line
stdlib walk over csproj ProjectReferences — verified: Northwind's closure carries
MeshWeaver.Import; OgCard's carries neither AI nor Import and skips every wait). The rendered
bash and the embedded heredoc were proven by extraction + execution, not by eyeballing YAML
dedent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

3 059 tests   2 867 ✅  6m 53s ⏱️
    8 suites    192 💤
    8 files        0 ❌

Results for commit 5cde0e3.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Test Results

   44 files     44 suites   31m 44s ⏱️
8 797 tests 8 600 ✅ 197 💤 0 ❌
9 500 runs  9 303 ✅ 197 💤 0 ❌

Results for commit 5cde0e3.

@rbuergi
rbuergi added this pull request to the merge queue Sep 1, 2026
Merged via the queue into main with commit f1ed404 Sep 1, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants