Skip to content

feat(notifications): delivery channels ride the module lane - #1665

Merged
rbuergi merged 3 commits into
mainfrom
feat/notification-channels-module
Aug 16, 2026
Merged

feat(notifications): delivery channels ride the module lane#1665
rbuergi merged 3 commits into
mainfrom
feat/notification-channels-module

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

The notification delivery-channels slice leaves Memex.Portal.Shared and rides the module lane as src/MeshWeaver.Notifications.Channels: the user-authored NotificationRule / NotificationChannel node types plus the NotificationTriageService watcher that escalates in-app notifications per each recipient's rules. The bell, NotificationService (deterministic dispatch + email preferences), NotificationCenter*, the Notification/NotificationSettings node types, and the settings tab stay platform, untouched.

Scoping answers (each could have falsified — none did)

  1. Can a module-registered IHostedService actually start? Yes, in both hosts. MeshHostApplicationBuilder's ServiceConfig is x => x.Invoke(Host.Services) — module ConfigureServices writes straight into the HOST's service collection, and InstallAssemblies runs inside ConfigureMemexMesh during UseMeshWeaver / UseOrleansMeshServer, synchronously before builder.Build() in both Memex.Portal.Monolith and Memex.Portal.Distributed. MeshWeaver.Observability is the shipped precedent: its LogIncidentControlPlane registers via AddSingleton<IHostedService>(sp => …) from the module attribute and runs in production off Modules:Assemblies.
  2. Do the Rule/Channel node types have consumers that break on delisting? Repo sweep (all file types incl. markdown/JSON in content/, samples/, test/, clients/) + live-mesh search found: zero test consumers, zero in-mesh code callers, one live data node (rbuergi/_NotificationChannel/Email), three agent-instruction markdown files (NotificationTriage.md, Assistant.md, ExecutiveAssistant.md — literal strings, degrade gracefully), and one platform compile-time dependency: NotificationService.HasRoutingRules (src/MeshWeaver.Graph) reads NotificationRuleNodeType consts to defer its deterministic email to triage. Graph cannot reference the module, so the const classes stay in Graph as compiled residue (same shape as the Slide/Deck residue documented in AddGraph); only the registrations moved. Delisting semantics are documented in the module README/attribute: types leave create/search contexts, triage stops; a recipient with existing rule nodes falls back to in-app only.
  3. Does NotificationTriageService depend on Portal.Shared-only types? No. Its only questionable dependency was PortalApplication (src/MeshWeaver.Blazor), used solely to obtain a hub. Replaced with the root IMessageHub from DI — the exact shape LogIncidentControlPlane ships with (including hub.StartThread on the root hub). One internal surface followed it: IMeshQueryCore (system-level unfiltered query; the watch spans all users' notifications) — the module joined the interface's existing InternalsVisibleTo whitelist, the same grant Memex.Portal.Shared held.

What moved / what changed

  • New module src/MeshWeaver.Notifications.Channels/ — assembly-level NotificationChannelsModuleAttribute (MeshNodeProviderAttribute) and AddNotificationChannels() sharing one configure path (OgCard shape): registers AddNotificationRuleType() + AddNotificationChannelType(), binds EmailOptions via AddOptions<T>().BindConfiguration(...), registers the triage watcher as a mesh-scoped singleton + IHostedService forward.
  • NotificationTriageService moved from memex/Memex.Portal.Shared/Notifications/ (deleted there); the Email:Enabled registration-time gate became a startup self-skip (module install time has no IConfiguration); content read switched to the sanctioned ContentAs<Notification>.
  • AddGraph() no longer registers the two types (comment points at the module); MemexConfiguration no longer registers the hosted service.
  • Activation (double-ship state): Modules:Assemblies in both hosts' appsettings + ProjectReference from Memex.Portal.Shared + MeshModulesPublish.targets entry + slnx.
  • Pin test test/Memex.Portal.Shared.Test/NotificationChannelsModuleTest.cs (ObservabilityBootPackTest shape): InstallAssemblies registers the watcher singleton with an IHostedService FORWARD (never a second construction) and both node types on the static-node provider.
  • Docs: Modules.md inventory row (+ Email config column), EmailIngestionAndNotifications.md registration paragraph, Notifications.md module note.
  • The NotificationTriage agent stays platform content (content/ai/Agent/), named by string — the same split Observability uses for LogTriage (moving it would break the MeshWeaver.Plugins agent-parity check).

Verification

  • dotnet build -c Release -warnaserror, one project per invocation: MeshWeaver.Notifications.Channels, Memex.Portal.Monolith, Memex.Portal.Distributed — all 0 Warning(s) 0 Error(s).
  • Full suites (build then --no-build, fresh .trx verified): MeshWeaver.Graph.Test 1183/1183 passed, Memex.Portal.Shared.Test 366/366 passed (incl. the new pin test).
  • MeshWeaver.Documentation.Test DocumentationLinkIntegrityTest — passed (rebuilt so the embedded docs carry the edits).

🤖 Generated with Claude Code

The NotificationRule/NotificationChannel node types and the
NotificationTriageService watcher move out of AddGraph()/Memex.Portal.Shared
into a new module, src/MeshWeaver.Notifications.Channels — assembly-level
MeshNodeProviderAttribute + AddNotificationChannels() sharing one configure
path (the Observability shape: options via AddOptions<T>().BindConfiguration,
mesh-scoped singleton + IHostedService forward).

The watcher now takes the root IMessageHub from DI instead of a scoped
PortalApplication (the LogIncidentControlPlane precedent, incl. StartThread on
the root hub), self-skips unless Email:Enabled (was a registration-time gate),
and reads content via the sanctioned ContentAs<Notification>. IMeshQueryCore
stays internal; the module joins its InternalsVisibleTo whitelist — the same
grant Memex.Portal.Shared held, for the same system-level all-users watch.

Compiled residue deliberately kept in the platform: the NodeType const classes
and NotificationService.HasRoutingRules (Graph cannot reference the module).
The NotificationTriage agent stays platform content, named by string — the
LogTriage split. Activation is the double-ship state: Modules:Assemblies in
both hosts + Portal.Shared ProjectReference + MeshModulesPublish.targets.

Pin test: NotificationChannelsModuleTest (InstallAssemblies registers the
watcher with an IHostedService FORWARD, never a second construction, plus both
node types on the static-node provider). Docs: Modules.md inventory row,
EmailIngestionAndNotifications.md registration paragraph, Notifications.md
module note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 16, 2026 09:36

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 modularizes the “notification delivery channels” lane by moving NotificationRule/NotificationChannel node types and the NotificationTriageService watcher out of Memex.Portal.Shared into a new module assembly (MeshWeaver.Notifications.Channels) that is activated via Modules:Assemblies.

Changes:

  • Introduces src/MeshWeaver.Notifications.Channels as a module (builder extension + assembly attribute) and moves NotificationTriageService into it.
  • Updates host/module wiring so the lane is activated via Modules:Assemblies (plus double-ship project/publish references) and removes the old AddGraph()/MemexConfiguration registrations.
  • Adds a pinning test for the boot-pack/module installation path and updates documentation to reflect the new module boundary.

Reviewed changes

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

Show a summary per file
File Description
test/Memex.Portal.Shared.Test/NotificationChannelsModuleTest.cs Adds a pin test for module installation registering node types + triage watcher.
src/MeshWeaver.Notifications.Channels/README.md Documents the module purpose, activation, and delisting semantics.
src/MeshWeaver.Notifications.Channels/NotificationTriageService.cs Moves triage watcher into module and adjusts DI/config gating + ContentAs usage.
src/MeshWeaver.Notifications.Channels/NotificationChannelsModuleAttribute.cs Adds module attribute and AddNotificationChannels() registration surface (node types + hosted service forward + options binding).
src/MeshWeaver.Notifications.Channels/MeshWeaver.Notifications.Channels.csproj New project for the module and its dependencies.
src/MeshWeaver.Mesh.Contract/Services/IMeshQueryCore.cs Grants internals access to the new module assembly.
src/MeshWeaver.Graph/Configuration/GraphConfigurationExtensions.cs Removes rule/channel type registration from core AddGraph() path (now module-owned).
src/MeshWeaver.Documentation/Data/Architecture/Notifications.md Notes that rules/channels + triage watcher ship as a module.
src/MeshWeaver.Documentation/Data/Architecture/Modules.md Adds module inventory entry for Notifications.Channels.
src/MeshWeaver.Documentation/Data/Architecture/EmailIngestionAndNotifications.md Updates registration narrative: bell stays core; rules/channels are module-owned.
MeshWeaver.slnx Adds the new module project to the solution.
memex/MeshModulesPublish.targets Ensures the module ships as part of mesh module publish.
memex/Memex.Portal.Shared/MemexConfiguration.cs Removes compiled-in hosted service registration; points to module activation.
memex/Memex.Portal.Shared/Memex.Portal.Shared.csproj Adds a project reference for the module (double-ship transition).
memex/Memex.Portal.Monolith/appsettings.json Activates the module via Modules:Assemblies.
memex/aspire/Memex.Portal.Distributed/appsettings.json Activates the module via Modules:Assemblies.
Suppressed comments (1)

src/MeshWeaver.Notifications.Channels/NotificationTriageService.cs:29

  • The doc comment says "all failures are logged", but the implementation deliberately swallows some faults (e.g., the rules probe subscription ignores timeout/error). Please adjust the comment to match the actual behavior so future readers don't assume comprehensive logging/alerting.

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

Comment thread test/Memex.Portal.Shared.Test/NotificationChannelsModuleTest.cs Outdated
…nnels-module

# Conflicts:
#	memex/Memex.Portal.Monolith/appsettings.json
#	memex/MeshModulesPublish.targets
#	memex/aspire/Memex.Portal.Distributed/appsettings.json
#	src/MeshWeaver.Documentation/Data/Architecture/Modules.md
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 0)

919 tests   918 ✅  11m 33s ⏱️
 10 suites    1 💤
 10 files      0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 5)

1 371 tests   1 370 ✅  7m 6s ⏱️
   11 suites      1 💤
   11 files        0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 3)

   11 files     11 suites   5m 53s ⏱️
2 196 tests 2 005 ✅ 191 💤 0 ❌
2 558 runs  2 367 ✅ 191 💤 0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 4)

1 566 tests   1 560 ✅  6m 8s ⏱️
   11 suites      6 💤
   11 files        0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 2)

2 326 tests   2 322 ✅  7m 14s ⏱️
   11 suites      4 💤
   11 files        0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results (shard 1)

2 247 tests   2 144 ✅  7m 56s ⏱️
   11 suites    103 💤
   11 files        0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown

Test Results

    65 files      65 suites   45m 52s ⏱️
10 625 tests 10 319 ✅ 306 💤 0 ❌
10 987 runs  10 681 ✅ 306 💤 0 ❌

Results for commit 531b24d.

♻️ This comment has been updated with latest results.

…gleton factory descriptor

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rbuergi
rbuergi merged commit 02c3a5b into main Aug 16, 2026
22 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