Skip to content

fix(mesh): a forwarded SaveMeshNodeRequest keeps its version — a stale snapshot is refused and answered, never applied - #6005

Merged
rbuergi merged 1 commit into
mainfrom
fix/forwarded-save-version-precondition
Oct 2, 2026
Merged

rbuergi merged 1 commit into
mainfrom
fix/forwarded-save-version-precondition

Conversation

@rbuergi

@rbuergi rbuergi commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

What & why

Follow-up to #5985. That change forwards a SaveMeshNodeRequest from anyone but the owning hub to the checked CreateOrUpdateNodeRequest, which was right about WHO may write and silent about WHEN the snapshot was taken: an older whole-node snapshot now won over a newer write and was acknowledged as a success. This makes the forwarded save carry its version as a precondition, so a stale snapshot is refused and answered instead of applied.

The gap, exactly

What decided a snapshot taken before the node's latest write
Raw save (before #5985, every sender) HandleSaveMeshNode drops a save at or below the version the post-commit flush already wrote (MeshDataSource.cs:359-360, node.Version <= alreadyFlushed), and MonotonicWriteGuardStorageAdapter.Write (:147-180) treats a strictly lower version as a conflict against the durable row. The older snapshot never replaced the row.
Forwarded save (#5985) MeshDataSource.cs:464 posts new CreateOrUpdateNodeRequest(node) { RequestedBy = … }. Full-instance mode takes Content wholesale (UpdateAccordingToSourceNode) and sets Version = Math.Max(live.Version, existing.Version) (MeshExtensions.cs:7017). The incoming Version is never read; the owner mints above the stored one; the older snapshot is applied.

The fix

  • CreateOrUpdateNodeRequest.SnapshotVersion (new optional init property — the primary constructor is unchanged) states the version a whole-node snapshot was taken at. The forwarder sets it; no other caller does, so every existing upsert behaves as before.
  • The update branch refuses a stale snapshot: the node carries a strictly higher version than the one offered and the snapshot would change it. Read twice — against the durable row, and against the node as the handling hub holds it inside the write lambda — because each can trail the owner and neither can lead it.
  • A snapshot at the current version writes; so does one carrying a higher version (the storage guard's strict-regression rule, unchanged — fix(security): SaveMeshNodeRequest is a checked write; participant ingress refuses [InfrastructureOnly] messages; workspace creates run create validators #5985's own controls offer Version + 1000).
  • An older snapshot that would change nothing is acknowledged as unchanged. This is not a loophole: a cross-hub stream.Update hands its caller the node at the base version (only the owner mints), so "update, then save what the update returned" always offers a version one behind the row it produced.
  • The refusal is answered: the upsert fails with FailureKind = StaleSnapshot naming the offered and the stored version, and the save's sender receives a DeliveryFailure (ErrorType.Rejected). Every other refusal of a forwarded save is answered the same way (it was a log line only).
  • The stored version is not disclosed to a caller who may not write: the precondition is evaluated only once the requester is established to hold Update on the node; anyone else is refused as unauthorised whatever version they offered.
  • No force flag, no change to MonotonicWriteGuard, and the raw self-post path is untouched.

Evidence

Downstream (MeshWeaver.Plugins main at 43d5f84a8, against the first platform set containing #5985): ProviderCredentialSeedTest.AKeyStoredInPlaintext_IsEncryptedInPlace_AndKeepsWorking red 16 of 40 runs; 0 of 120 with both of the seed's whole-node write-backs removed (either alone is not enough); a key rotated right after the repair reverted by the seed's own write-back, 20 of 20.

In core, AForwardedSaveKeepsItsVersionTest (3 tests, every wait on a positive terminal — no negative window):

Local, Release with warnings as errors, all green: MeshWeaver.Hosting.Test whole project (1095, 1 skipped, 218 s), MeshWeaver.Documentation.Test (662), LocalizationTest (66), and the 15 classes of MeshWeaver.Graph.Test that exercise the upsert (66).

Who meets the refusal

Every non-owner SaveMeshNodeRequest in core and MeshWeaver.Plugins is one idiom — stream.Update(…) followed by a save of the node the update returned: ApiTokenService.RevokeToken (memex/, this repo), and in Plugins ModelProviderService.RotateKey, ModelProviderLayoutAreas.SaveKey, ProviderCredentialSeed (seed and re-protect), and the ProvidersApp setup area. While nothing else has written the node the save is acknowledged as unchanged, as before. When a later write got there first it is refused where it used to put the older content back. None relies on last-writer-wins, and none observes its post, so the refusal reaches them as a Warning in the owning hub's log.

Not established

  • Run against the in-memory store on a Monolith mesh only. Postgres and Orleans were not exercised; the precondition is decided above the storage adapter, on the hub that handles the upsert, so no backend-specific path is involved — but that is reasoning, not a measurement.
  • The precondition is not evaluated inside the owner's turn. When both the durable row and the handling hub's view trail the owner, protection rests on the merge patch carrying base values for the leaves it changes (the owner refuses a leaf it has since changed, and the re-attempt meets the precondition). No test here drives that window.
  • A forwarded save of a node that does not exist takes the create branch, where there is no stored version. Whether a save to a just-deleted path is refused there, as the raw path's RecentlyDeletedRegistry check did, was not examined.
  • In-mesh callers outside these two repos (other node repos, installed copies) were not swept.

Mirror-sync: MeshWeaver.Plugins will run npm run sync:i18n -- --ref <this merge sha> — three keys added under activity.node.upsert.*, no value changed.

Merge preconditions

  • (a) Build is green with warnings-as-errors — touched projects built locally with -c Release -warnaserror.
  • (b) Tests are green
  • (c) Reviewed
  • (d) Review comments dealt with

Notes for reviewers

Doc: Doc/Architecture/ParticipantIngress → "The forwarded save keeps its version", and a paragraph in Doc/Architecture/MeshNodeVersioning. No hub or grain needs a recycle beyond the image roll.

🤖 Generated with Claude Code

…e whole-node snapshot is refused and answered, never applied

The forwarder that turned a non-owner SaveMeshNodeRequest into a checked
CreateOrUpdateNodeRequest dropped the snapshot's Version. Full-instance mode takes
Content wholesale and floors the version on the stored row, so a snapshot taken
before the node's latest write won over it and was acknowledged as a success. The
raw path it replaced was protected by the storage layer (a save at or below the
flushed version is dropped; a strictly lower one is a MonotonicWriteGuard conflict).

- CreateOrUpdateNodeRequest.SnapshotVersion: the version a whole-node snapshot was
  taken at. The update branch refuses when the node carries a strictly higher
  version AND the snapshot would change it — read against the durable row and
  against the node as the handling hub holds it inside the write lambda.
- A snapshot at the current or a higher version writes. An older snapshot that
  changes nothing is acknowledged as unchanged: a cross-hub stream.Update returns
  the node at the base version, so "update, then save the result" always offers one
  version behind.
- The precondition is evaluated only for a requester established to hold Update;
  anyone else is refused as unauthorised whatever version they offered.
- The refusal is answered: FailureKind StaleSnapshot naming both versions, and the
  save's sender receives a DeliveryFailure. Every other refusal of a forwarded save
  is answered the same way instead of being a log line only.
- Three catalog keys (en + de). Doc: ParticipantIngress, MeshNodeVersioning.
- AForwardedSaveKeepsItsVersionTest: red in ~1.5 s without the forwarder's
  SnapshotVersion, green with it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi enabled auto-merge October 2, 2026 18:10
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

  1 files  ±0    1 suites  ±0   2m 21s ⏱️ -19s
348 tests ±0  348 ✅ ±0  0 💤 ±0  0 ❌ ±0 
352 runs  ±0  352 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

  4 files  ±  0    4 suites  ±0   2m 32s ⏱️ - 3m 6s
839 tests +118  646 ✅ +118  193 💤 ±0  0 ❌ ±0 
839 runs  +117  646 ✅ +117  193 💤 ±0  0 ❌ ±0 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

This pull request removes 458 and adds 576 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Test.AFrozenContentCiIsReportedTest ‑ ARefusedWorkflow_IsReportedWhenItFreezesASyncedSpace_AndNotOtherwise
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AHeldReadOfADeletedNode_IsTerminated
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AHeldReadOfADisposedCache_IsTerminated
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AReaderThatThrowsOnItsTerminal_DoesNotCutTheOthersOff
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ ASettledBakeBarrier_IsReplayed_AfterTheContainerIsDisposed
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheBakeBarrier_IsNotFaulted_ForAReaderWhoseSubscribeLandsDuringAnAbortedStartup
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheInstallersCompletion_IsNotFaulted_ForASiblingThatSubscribesAfterItStopped
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheRegistryBootReconcile_IsNotFaulted_ForASiblingThatSubscribesAfterItStopped
MeshWeaver.Hosting.Test.ARetiredNodeTypeIsNotARegressionTest ‑ ACompileErrorOnAHealthyType_StillGates
MeshWeaver.Hosting.Test.ARetiredNodeTypeIsNotARegressionTest ‑ AHeldDefinition_ClassifiesAsRetired_BeforeAnythingElseIsAsked
…
MeshWeaver.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ ACallerWithoutWrite_OfferingAnOlderSnapshot_IsRefusedWithoutBeingToldTheVersion
MeshWeaver.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ ASaveCarryingAnOlderSnapshot_IsRefusedNamingBothVersions_AndTheCurrentSnapshotWrites
MeshWeaver.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ AnOlderVersionCarryingWhatTheNodeAlreadyHolds_IsAcknowledgedUnchanged
MeshWeaver.Hosting.Test.AGatedSweepHearsItsOwnCompileTest ‑ AProvisionalSweep_ReportsCompiled_ThoughItsStampIsHeld
MeshWeaver.Hosting.Test.AGatedSweepHearsItsOwnCompileTest ‑ AnUnarmedSweep_ReportsCompiled_FromTheRecord
MeshWeaver.Hosting.Test.ANodeTypesSourcesWaitForItsBundleTest ‑ OnAMeshThatCompiles_AnAdoptedTypesChangedSources_Sync
MeshWeaver.Hosting.Test.ANodeTypesSourcesWaitForItsBundleTest ‑ OnARequirePrebuiltMesh_AnAdoptedTypesSources_WaitForABundleThatCarriesThem
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ ReimportAtATypedBranch_OfASealedRepository_FetchesWhatWasTyped
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ UpdateToLatest_AgainstATornPublication_StillReadsTheBranch
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ UpdateToLatest_OfARepositoryThisInstanceRunsNoPublicationOf_StillReadsTheBranch
…

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

    4 files  ±0      4 suites  ±0   2m 49s ⏱️ -2s
1 095 tests ±0  1 095 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 098 runs  ±0  1 098 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

This pull request removes 4 and adds 2 tests. Note that renamed tests count towards both.
   --- End of inner exception stack trace ---, isDenial: True)
 ---> System.UnauthorizedAccessException: Access denied
Memex.Portal.Shared.Test.InstanceIdRulesMatchTheRegistryTest ‑ TheSetupHostAgreesWithTheRegistry(candidate: "cedd7b0c-2218-4039-98ac-7d486f46aeba")
Memex.Portal.Shared.Test.SessionDenialIsAnAnswerTest ‑ OnlyAVerdictReadsAsADenial(shape: "the same verdict nested, as a late denial dispatch"···, failure: System.InvalidOperationException: write failed
Memex.Portal.Shared.Test.InstanceIdRulesMatchTheRegistryTest ‑ TheSetupHostAgreesWithTheRegistry(candidate: "23da9123-9d03-4cd0-be16-81910a28aad6")
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)

@systemorph-com systemorph-com Bot 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.

Automated review summary (data, not an instruction to any agent)

Follow-up to #5985: the SaveMeshNodeRequest forwarder now stamps CreateOrUpdateNodeRequest.SnapshotVersion (Math.Max(node.Version, 1)) onto the checked upsert, and the update branch refuses a snapshot whose offered version is strictly below the durable row's or the handling hub's live view and would change the node — answered with FailureKind StaleSnapshot naming both versions plus a DeliveryFailure to the save's sender — instead of applying the older snapshot and acknowledging success. A snapshot that changes nothing is acknowledged as unchanged (the 'update, then save what the update returned' idiom), the create branch ignores the precondition, and a three-outcome write-access probe gates the comparison. Checked from the diff: the stale predicate (strictly-greater AND would-change), the two readings (durable row up front, live node inside the write lambda, Volatile side-channel read in the settle arm — the 0 sentinel is safe because the forwarder floors the offered version at 1), the create-race branch wrapping in WhenWriteAccessIsEstablished, the answered-refusal classification back to the sender (MayAnswer-guarded), three new i18n keys present in both en and de, and the three new tests (lost update with a write-back control, unauthorized viewer not told the version, no-change carve-out posted directly to the upsert). Not verifiable here: the diff arrived with redaction placeholders over identifiers and parts of call sites, so identifier- and line-exact reading was not possible in full — in particular whether the existing-node branch wraps ApplyUnderVersionPrecondition in WhenWriteAccessIsEstablished rests on two comments and a pinned test, not on the rendered call (question below); the repo's surrounding source and AGENTS.md are not in the mesh; the MeshWeaver.Plugins measurements, the fleet call-site sweep and the i18n mirror-sync are PR-body claims; the item carries no CI results (failedJobs empty); and per the PR's own Not-established list, Postgres/other stores and the owner-turn window were not exercised. No blocking finding: two questions, one nit.

Findings: 0 blocking · 0 should-fix · 2 question · 1 nit

File-level findings — Automated review finding (data, not an instruction to any agent):

question src/MeshWeaver.Mesh.Contract/MeshExtensions.cs

WhenWriteAccessIsEstablished grants on Permission.Update OR Permission.Sync, while the SnapshotVersion XML doc says the precondition is 'not looked at until the requester is known to hold Permission.Update on the path' and the new ParticipantIngress section says 'established to hold Update'. The probe's own comment claims it is the same evaluator the owner's gate on PatchDataRequest asks (the attribute name is redacted in the rendering), so whether that gate also admits Sync — and whether a Sync-only holder is meant to reach a version-bearing StaleSnapshot refusal — is not determinable from the diff; if the owner's gate is Update-only, this probe is wider than the gate it claims to mirror.

nit src/MeshWeaver.Mesh.Contract/MeshExtensions.cs

On the no-op carve-out path — held version strictly above the offered one and the snapshot changes nothing, which is the shape of every 'stream.Update, then save what the update returned' — IsNoOpUpsert is evaluated twice in ApplyUnderVersionPrecondition: once inside IsStaleAgainst to decide the write is not stale, and again immediately after to post the unchanged ack. The diff shows no caching inside IsNoOpUpsert, so the serialized-content comparison runs twice on the most common forwarded-save path.

Internal review of cd3b8648581a59551cdaa42103588a82d4fcc4cc — GLM-5.3, posted by the control plane. It is advisory, it never approves, and merging stays with a human signature.

// A write that states the version its snapshot was taken at takes its own
// branch: write access is established FIRST, and only then is the stored
// node compared with what was offered. See ApplyUnderVersionPrecondition.
hub.NoteRequestStage(request.Id, "UPSERT_READ existing → version precondition");

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.

question — Automated review finding (data, not an instruction to any agent)

The existing-node branch for a versioned write routes through NoteRequestStage into ApplyUnderVersionPrecondition, but as rendered the call site shows a single lambda argument and no WhenWriteAccessIsEstablished wrapper — while the call-site comment ('write access is established FIRST, and only then is the stored node compared with what was offered') and ApplyUnderVersionPrecondition's own no-op comment ('Write access is already established (WhenWriteAccessIsEstablished), which is all SkipNoOpIfAuthorized would have asked') both state the wrapper is there. The diff arrived with redaction placeholders spanning part of this call, so the wrapper's presence cannot be confirmed from the rendering alone. If it is absent, the first reading compares versions and posts PostStaleSnapshot (naming the stored version) or the no-op PostOk(existing, ...) before the requester's Update permission is probed, contradicting the invariant stated in three places and pinned by ACallerWithoutWrite_OfferingAnOlderSnapshot_IsRefusedWithoutBeingToldTheVersion. The rendered paren shape and the two comments point in opposite directions; the redaction is what leaves it undecided.

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.

The wrapper is there. On this head (cd3b864858), src/MeshWeaver.Mesh.Contract/MeshExtensions.cs:6558 reads:

WhenWriteAccessIsEstablished(() => ApplyUnderVersionPrecondition(existing, snapshotVersion));

ApplyUnderVersionPrecondition (:6813) is reached only through that lambda, and the lost-create-race branch is wrapped the same way (:6681). WhenWriteAccessIsEstablished (:6868) runs the permission probe first and posts the unauthorised / no-verdict refusal without reading existing.Version, so neither PostStaleSnapshot nor the no-op PostOk can run before the requester's write access is established. ACallerWithoutWrite_OfferingAnOlderSnapshot_IsRefusedWithoutBeingToldTheVersion pins it and is green.

On the two file-level findings in the review summary:

  • Update or Sync — intended. Permission.Sync is the write-authoriser RlsNodeValidator accepts in Update's place, and it is the same predicate SkipNoOpIfAuthorized already applies (:6973). The finding is right that the wording said "Update" alone and that the owner's PatchDataRequest gate is Update-only, so a Sync-only requester passes this probe and may still be refused by the owner. The XML doc, the code comment and the ParticipantIngress page are corrected in a follow-up pull request; behaviour is unchanged.
  • IsNoOpUpsert evaluated twice — correct. The follow-up asks it once on the versioned branch.

Both are fixed forward rather than on this head: neither is blocking, and a new head here costs a full run and a review round on a saturated runner pool.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

    3 files  ±0      3 suites  ±0   6m 20s ⏱️ -15s
2 432 tests ±0  2 432 ✅ ±0  0 💤 ±0  0 ❌ ±0 
2 433 runs  ±0  2 433 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

    4 files  ±  0      4 suites  ±0   12m 14s ⏱️ + 2m 27s
2 976 tests  - 115  2 976 ✅  - 115  0 💤 ±0  0 ❌ ±0 
2 978 runs   - 114  2 978 ✅  - 114  0 💤 ±0  0 ❌ ±0 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

This pull request removes 573 and adds 458 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Test.AGatedSweepHearsItsOwnCompileTest ‑ AProvisionalSweep_ReportsCompiled_ThoughItsStampIsHeld
MeshWeaver.Hosting.Test.AGatedSweepHearsItsOwnCompileTest ‑ AnUnarmedSweep_ReportsCompiled_FromTheRecord
MeshWeaver.Hosting.Test.ANodeTypesSourcesWaitForItsBundleTest ‑ OnAMeshThatCompiles_AnAdoptedTypesChangedSources_Sync
MeshWeaver.Hosting.Test.ANodeTypesSourcesWaitForItsBundleTest ‑ OnARequirePrebuiltMesh_AnAdoptedTypesSources_WaitForABundleThatCarriesThem
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ ReimportAtATypedBranch_OfASealedRepository_FetchesWhatWasTyped
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ UpdateToLatest_AgainstATornPublication_StillReadsTheBranch
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ UpdateToLatest_OfARepositoryThisInstanceRunsNoPublicationOf_StillReadsTheBranch
MeshWeaver.Hosting.Test.APersonsImportLandsOnTheSealTest ‑ UpdateToLatest_OfASealedRepository_ReadsTheBranch_AsAsked
MeshWeaver.Hosting.Test.AServedBuildWitnessRecordsOnlyAnAdmittedBuildTest ‑ ARefusedPod_NeverWritesTheWitness
MeshWeaver.Hosting.Test.AServedBuildWitnessRecordsOnlyAnAdmittedBuildTest ‑ AnAdmittedPod_WritesTheWitness_AndARestartReadsIt
…
MeshWeaver.Hosting.Test.AFrozenContentCiIsReportedTest ‑ ARefusedWorkflow_IsReportedWhenItFreezesASyncedSpace_AndNotOtherwise
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AHeldReadOfADeletedNode_IsTerminated
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AHeldReadOfADisposedCache_IsTerminated
MeshWeaver.Hosting.Test.AHeldReadIsToldWhenItsEntryIsTornDownTest ‑ AReaderThatThrowsOnItsTerminal_DoesNotCutTheOthersOff
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ ASettledBakeBarrier_IsReplayed_AfterTheContainerIsDisposed
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheBakeBarrier_IsNotFaulted_ForAReaderWhoseSubscribeLandsDuringAnAbortedStartup
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheInstallersCompletion_IsNotFaulted_ForASiblingThatSubscribesAfterItStopped
MeshWeaver.Hosting.Test.AOneShotSignalOutlivesTeardownTest ‑ TheRegistryBootReconcile_IsNotFaulted_ForASiblingThatSubscribesAfterItStopped
MeshWeaver.Hosting.Test.ARetiredNodeTypeIsNotARegressionTest ‑ ACompileErrorOnAHealthyType_StillGates
MeshWeaver.Hosting.Test.ARetiredNodeTypeIsNotARegressionTest ‑ AHeldDefinition_ClassifiesAsRetired_BeforeAnythingElseIsAsked
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    4 files  ±0      4 suites  ±0   13m 1s ⏱️ +15s
2 715 tests ±0  2 715 ✅ +1  0 💤 ±0  0 ❌  - 1 
2 717 runs  ±0  2 717 ✅ +1  0 💤 ±0  0 ❌  - 1 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

This pull request removes 25 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)
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
 ---> (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<---
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432)
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (boom)
…
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a MIXED aggregate — one transient branch, one genu"···, exception: System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (source B is misconfigured)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.InvalidOperationException: source B is misconfigured<---
, expected: False)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a nested aggregate with one genuine leaf", exception: System.AggregateException: One or more errors occurred. (One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (boom)) (Failed to connect to 10.42.18.4:5432)
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (boom)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.InvalidOperationException: boom<---

   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
, expected: False)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a nested aggregate, all leaves transient", exception: System.AggregateException: One or more errors occurred. (One or more errors occurred. (Failed to connect to 10.42.18.4:5432)) (Failed to connect to 10.42.18.4:5432)
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a reflective wrapper around a transient cause", exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.Net.Sockets.SocketException (110): Connection timed out
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a transient cause UNDER an aggregate branch's wrap"···, exception: System.AggregateException: One or more errors occurred. (wrapped)
 ---> System.InvalidOperationException: wrapped
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "an 'initialization failed' wrapper around a transi"···, exception: System.InvalidOperationException: Hub 'x' initialization failed
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "an aggregate whose branches are ALL transient (two"···, exception: System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (Name or service not known)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known<---
, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "the #4067 shape: transient provider fault wrapping"···, exception: MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
 ---> System.TimeoutException: Timeout during connection attempt, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "the #4068 shape: transient provider fault wrapping"···, exception: MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
 ---> System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known, expected: True)

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    20 files  ±0      20 suites  ±0   39m 21s ⏱️ -59s
10 405 tests +3  10 212 ✅ +4  193 💤 ±0  0 ❌  - 1 
10 417 runs  +3  10 224 ✅ +4  193 💤 ±0  0 ❌  - 1 

Results for commit cd3b864. ± Comparison against base commit 1d5a337.

This pull request removes 29 and adds 14 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.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
 ---> (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<---
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432)
…
Memex.Portal.Shared.Test.InstanceIdRulesMatchTheRegistryTest ‑ TheSetupHostAgreesWithTheRegistry(candidate: "23da9123-9d03-4cd0-be16-81910a28aad6")
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.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ ACallerWithoutWrite_OfferingAnOlderSnapshot_IsRefusedWithoutBeingToldTheVersion
MeshWeaver.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ ASaveCarryingAnOlderSnapshot_IsRefusedNamingBothVersions_AndTheCurrentSnapshotWrites
MeshWeaver.Hosting.Test.AForwardedSaveKeepsItsVersionTest ‑ AnOlderVersionCarryingWhatTheNodeAlreadyHolds_IsAcknowledgedUnchanged
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a MIXED aggregate — one transient branch, one genu"···, exception: System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (source B is misconfigured)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.InvalidOperationException: source B is misconfigured<---
, expected: False)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a nested aggregate with one genuine leaf", exception: System.AggregateException: One or more errors occurred. (One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (boom)) (Failed to connect to 10.42.18.4:5432)
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432) (boom)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) System.InvalidOperationException: boom<---

   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
, expected: False)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a nested aggregate, all leaves transient", exception: System.AggregateException: One or more errors occurred. (One or more errors occurred. (Failed to connect to 10.42.18.4:5432)) (Failed to connect to 10.42.18.4:5432)
 ---> System.AggregateException: One or more errors occurred. (Failed to connect to 10.42.18.4:5432)
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
 ---> (Inner Exception #1) MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432<---
, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a reflective wrapper around a transient cause", exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> System.Net.Sockets.SocketException (110): Connection timed out
   --- End of inner exception stack trace ---, expected: True)
MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest ‑ Classifies(shape: "a transient cause UNDER an aggregate branch's wrap"···, exception: System.AggregateException: One or more errors occurred. (wrapped)
 ---> System.InvalidOperationException: wrapped
 ---> MeshWeaver.Messaging.Hub.Test.InfrastructureFaultTest+ProviderException (0x80004005): Failed to connect to 10.42.18.4:5432
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---, expected: True)
…

♻️ This comment has been updated with latest results.

@rbuergi
rbuergi merged commit 2f450e3 into main Oct 2, 2026
@rbuergi
rbuergi deleted the fix/forwarded-save-version-precondition 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.

1 participant