Skip to content

Filter framework-internal message types from observability surfaces#2524

Merged
jeremydmiller merged 1 commit intomainfrom
fix/filter-system-messages-2520
Apr 16, 2026
Merged

Filter framework-internal message types from observability surfaces#2524
jeremydmiller merged 1 commit intomainfrom
fix/filter-system-messages-2520

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Summary

Fixes #2520. ServiceCapabilities and IWolverineObserver (MessageRouted, MessageCausedBy) were leaking framework-internal message types — IAgentCommand impls from Wolverine.RDBMS, ISideEffect/IMartenOp/IPolecatOp return types — into observability tooling alongside user message types. The previous filter only checked an assembly-level [ExcludeFromServiceCapabilities], which catches Wolverine.dll itself but misses persistence assemblies and provides no per-type opt-out.

Audit findings (the holes)

  • ServiceCapabilities.readMessageTypes() — only assembly-level filter; persistence-assembly IAgentCommand impls leaked
  • Observer.MessageRouted() (WolverineRuntime.Routing.cs:158) — emitted unconditionally; CritterWatch's observer filters by assembly attr + ICritterWatchMessage but not by IAgentCommand
  • Observer.MessageCausedBy() (MessageHandler.RecordCauseAndEffect()) — zero filtering; reported every message including system commands

Approach

  • New IInternalMessage marker interface — fast per-type filtering (x is IInternalMessage over reflection)
  • New internal SystemMessageTypeExtensions.IsSystemMessageType() helper centralizing four checks:
    • IInternalMessage (new)
    • IAgentCommand (catches all internal agent commands)
    • INotToBeRouted (catches ISideEffect, ICritterWatchMessage, acknowledgements, OutgoingMessages, ValidationOutcome)
    • Assembly-level [ExcludeFromServiceCapabilities] (preserves existing behavior)
  • Apply the filter at all three emission sites (one source of truth)
  • Mark Wolverine-owned persistence assemblies with [assembly: ExcludeFromServiceCapabilities] as defense in depth (RDBMS, Postgresql, SqlServer, MySql, Sqlite, Marten, Polecat, EntityFrameworkCore, Oracle)

Files

  • New: IInternalMessage.cs, SystemMessageTypeExtensions.cs, system_message_type_filtering.cs (tests)
  • Modified: ServiceCapabilities.cs, WolverineRuntime.Routing.cs, MessageHandler.cs, 9 persistence AssemblyAttributes.cs

Test plan

  • IsSystemMessageType recognizes IInternalMessage, IAgentCommand, INotToBeRouted; returns false for normal user messages and null
  • service_capabilities_excludes_system_message_types — end-to-end: build a host, assert all three system markers are filtered while a normal user message appears
  • observer_does_not_receive_message_routed_for_system_types — install a recording observer, route 4 types, assert only the user type was reported
  • Existing 5 exporting_service_capabilities tests still pass
  • 35 routing/observer/causation tests still pass

🤖 Generated with Claude Code

…2520)

ServiceCapabilities and IWolverineObserver hooks (MessageRouted,
MessageCausedBy) were leaking framework-internal message types —
IAgentCommand implementations from Wolverine.RDBMS, ISideEffect/IMartenOp/
IPolecatOp return types, etc. — alongside user message types. The previous
filter only checked an assembly-level [ExcludeFromServiceCapabilities],
which catches Wolverine.dll itself but misses persistence assemblies and
provides no per-type opt-out.

Changes:
- Add IInternalMessage marker interface for fast per-type filtering
  (preferred to attribute lookup — single runtime type-test)
- Add internal SystemMessageTypeExtensions.IsSystemMessageType() helper
  centralizing the four checks: IInternalMessage, IAgentCommand,
  INotToBeRouted (covers ISideEffect, ICritterWatchMessage, etc.),
  assembly attribute
- Apply the filter in three emission sites:
  * ServiceCapabilities.readMessageTypes() — replaces assembly-only check
  * WolverineRuntime.Routing — gates Observer.MessageRouted()
  * MessageHandler.RecordCauseAndEffect — gates Observer.MessageCausedBy()
    on both incoming and outgoing message types
- Mark Wolverine-owned persistence assemblies with
  [assembly: ExcludeFromServiceCapabilities] (defense in depth):
  RDBMS, Postgresql, SqlServer, MySql, Sqlite, Marten, Polecat,
  EntityFrameworkCore, Oracle
- Add tests verifying the helper and end-to-end filtering through both
  ServiceCapabilities and IWolverineObserver

Closes #2520

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Exclude internal/framework message types from ServiceCapabilities message discovery

1 participant