Skip to content

fix(#1549): every NodeType release-failure class logs under its own EventId - #6326

Merged
rbuergi merged 5 commits into
mainfrom
fix/1549-release-failure-classes-own-event-ids
Oct 9, 2026
Merged

rbuergi merged 5 commits into
mainfrom
fix/1549-release-failure-classes-own-event-ids

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Refs #1549.

#1549's original defect (per-node hubs silently forwarding for 2 h 20 m) was fixed by #1990 / 5cd54d3f2 and verified on the deployed portal in August. The issue stayed open because later, unrelated release failures kept folding into it. This PR stops that folding.

Root cause

Fix

LogReleaseRefusal logs each class under ReleaseFailureEventId(failure) = an explicit, append-only code per class (ReleaseFailureEventIds, 7400–7415), named NodeTypeRelease<Class>.

  • The codes are never the enum ordinal (review), so inserting a class cannot renumber a deployed site.
  • An undeclared value takes Unclassified's id, the same way it takes that class's template.
  • Templates and levels are unchanged.
  • The 7400 range is unused in core; the disposal verdicts use 73xx.

Test

test/MeshWeaver.Graph.Test/ReleaseFailureClassIsInTheTemplateTest.cs → EveryFailureClass_LogsUnderAnEventIdOfItsOwn.

  • It asserts that no class logs under id 0, that the ids are distinct per class, and that each id's name is its class.
  • Negative control: with the ids removed from the calls, the test fails, because every class logs under the one shared id.
  • The class has 10 tests and all pass. MeshWeaver.Graph and MeshWeaver.Graph.Test build clean with -c Release -warnaserror.

Effect on incidents

Each class now opens its own incident site; ids are permanent explicit codes 7400–7415, pinned by TheEventIdOfEveryClass_IsPinned. The classes that are not defects (NodeMissing, CompileDenied, OwnerRecycling) stay at Error. Deciding their levels is a separate question, as the remarks in LogReleaseRefusal say.

Doc: Doc/Architecture/ReleaseFailureClasses → "…and its own EVENT ID, because a recurrence folds by SITE".

Pairs-with: none — no public surface removed (one internal helper and one internal const added).

🤖 Generated with Claude Code

rbuergi and others added 2 commits October 8, 2026 18:37
…ventId

#4932 put each release-failure class into its own template, but the incident
watcher folds every fingerprint of one log SITE into one issue, and a site is
category + EventId + exception type. All classes shared category, EventId 0 and
no exception, so NO NODE EXISTS (2026-09-24) and OWNING HUB REACHED NO VERDICT
(2026-09-25) still arrived on #1549 as "a different fingerprint of the same log
site" - causes that issue (silent forwards, fixed by #1990) was never about.

LogReleaseRefusal now logs each class under EventId 7400 + class, named
NodeTypeRelease<Class>; an undeclared value takes the Unclassified id.

Test: EveryFailureClass_LogsUnderAnEventIdOfItsOwn. Negative control (ids
removed): fails with one id shared by every class.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…currences fold by site

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 8, 2026 16:38

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.

🟡 Changes recommended

Implicit enum ordinals can renumber or reuse deployed incident-site IDs when failure classes are inserted.

1 open finding
What changed in this PR

Separates NodeType release failures into distinct incident sites using per-class event IDs.

Changes:

  • Adds event IDs to release-refusal logs.
  • Tests event-ID uniqueness and naming.
  • Documents incident-site folding behavior.
File Description
src/​MeshWeaver.Graph/​NodeTypeRecompileExtensions.cs Assigns event IDs to failure logs.
test/​MeshWeaver.Graph.Test/​ReleaseFailureClassIsInTheTemplateTest.cs Verifies IDs are nonzero and distinct.
src/​MeshWeaver.Documentation/​Data/​Architecture/​ReleaseFailureClasses.md Documents event-ID-based separation.

🧠 Review effort: Balanced


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

/// <returns>The event id that failure is logged under.</returns>
internal static EventId ReleaseFailureEventId(NodeTypeReleaseFailure failure) =>
Enum.IsDefined(failure)
? new EventId(ReleaseFailureEventIdBase + (int)failure, $"NodeTypeRelease{failure}")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed: an id is a deployed incident site, so it can't be derived from an ordinal. Fixed in 7d673cc.

  • ReleaseFailureEventIds is an explicit, append-only mapping from each class to a permanent code in 7400–7415 (Unclassified=7400 … HostLeaving=7415). These are the values the derived scheme would have produced, so nothing that has already shipped moves. An unlisted or undeclared value logs under Unclassified's id, the same way it takes that class's template.
  • TheEventIdOfEveryClass_IsPinned pins every class to its exact code and fails if any enum member has no entry. A class inserted mid-enum therefore can't take a historical site, and a new class has to be given its own code. EveryFailureClass_LogsUnderAnEventIdOfItsOwn still checks that the ids are distinct.
  • Negative control: I removed HostLeaving's row from the mapping, and both id tests fail. The suite passes 11/11 with the row restored, and the build is clean with -c Release -warnaserror.
  • The doc (ReleaseFailureClasses) now says the codes are permanent and append-only, and that they are never the ordinal.

…inned by a test

Review on #6326: an id derived from the enum ordinal renumbers every later
class when one is inserted mid-enum, handing a new cause a deployed site.
Each class now has an explicit append-only code (ReleaseFailureEventIds), and
TheEventIdOfEveryClass_IsPinned pins the whole mapping. Negative control: a
class with no code fails both id tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   14 files   -    10     14 suites   - 10   40m 49s ⏱️ - 8m 38s
9 680 tests  - 1 468  9 680 ✅  - 1 275  0 💤  - 193  0 ❌ ±0 
9 693 runs   - 1 468  9 693 ✅  - 1 275  0 💤  - 193  0 ❌ ±0 

Results for commit 3adb660. ± Comparison against base commit 1007a35.

This pull request removes 1501 and adds 9 tests. Note that renamed tests count towards both.

   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---, expected: True)
   --- End of inner exception stack trace ---, isDenial: True)
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
 ---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
 ---> (Inner Exception #1) System.InvalidOperationException: boom<---
 ---> (Inner Exception #1) System.InvalidOperationException: source B is misconfigured<---
 ---> (Inner Exception #1) System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known<---
…
Memex.Portal.Shared.Test.InstanceIdRulesMatchTheRegistryTest ‑ TheSetupHostAgreesWithTheRegistry(candidate: "8c610fb3-f168-46a0-96a0-0413dfbb26f8")
Memex.Portal.Shared.Test.SessionDenialIsAnAnswerTest ‑ OnlyAVerdictReadsAsADenial(shape: "the same verdict nested, as a late denial dispatch"···, failure: System.InvalidOperationException: write failed
 ---> System.UnauthorizedAccessException: Access denied
   --- End of inner exception stack trace ---, isDenial: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's FIRST member", fault: System.AggregateException: One or more errors occurred. (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall as an aggregate's SECOND member", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> System.TimeoutException: The operation has timed out.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---
, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall nested in an aggregate inside a wrapper", fault: System.InvalidOperationException: outer
 ---> System.AggregateException: One or more errors occurred. (Value does not fall within the expected range.) (Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.)
 ---> System.ArgumentException: Value does not fall within the expected range.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.<---

   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "a stall wrapped as InnerException", fault: System.InvalidOperationException: outer
 ---> MeshWeaver.Mesh.QueryProviderStalledException: Query provider(s) [pg] did not emit an Initial within the query fan-in's 16s bound for query 'nodeType:NodeType' (user 'system'). The merged Initial gates on EVERY provider, so this query has NO snapshot to answer with — it is reported as unavailable (retryable) rather than left hanging with no error. This is an availability failure, never a permission verdict: a consumer deciding access must fail CLOSED and say it could not establish the answer. Fix the stalled provider; never bump the consumer's timeout.
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Compiler.Pipeline.Test.SourcesWatcherStallBackoffTest ‑ The_production_classifier_recognises_a_stall_through_every_wrapping(because: "an aggregate of unrelated faults", fault: System.AggregateException: One or more errors occurred. (The operation has timed out.) (Value does not fall within the expected range.)
 ---> System.TimeoutException: The operation has timed out.
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.ArgumentException: Value does not fall within the expected range.<---
, expected: False)
MeshWeaver.Graph.Test.ReleaseFailureClassIsInTheTemplateTest ‑ EveryFailureClass_LogsUnderAnEventIdOfItsOwn
MeshWeaver.Graph.Test.ReleaseFailureClassIsInTheTemplateTest ‑ TheEventIdOfEveryClass_IsPinned

♻️ This comment has been updated with latest results.

@rbuergi
rbuergi merged commit 3d9989c into main Oct 9, 2026
@rbuergi
rbuergi deleted the fix/1549-release-failure-classes-own-event-ids branch October 10, 2026 13:10
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