Skip to content

A hub activated on the wrong NodeType now lets go of it - #1143

Merged
rbuergi merged 7 commits into
mainfrom
fix/1104-hub-pinned-wrong-nodetype
Aug 10, 2026
Merged

rbuergi merged 7 commits into
mainfrom
fix/1104-hub-pinned-wrong-nodetype

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1104

The defect, pinned

A per-node hub binds its HubConfiguration from its node's NodeType exactly once, during
activation, and is then pinned by address in HostedHubsCollection. RoutingServiceBase.RouteInMesh
short-circuits on Mesh.GetHostedHub(address, Never) for an already-hosted address, so a live hub
never resolves its path again and nothing re-reads its NodeType. A node that acquires — or
changes — its type while its hub is alive keeps serving the configuration it was born with, which
for a type-less node is the mesh DefaultNodeHubConfiguration.

The repro (NodeTypeRebindTest, test/MeshWeaver.Hosting.Monolith.Test) reproduces the issue's
decisive evidence deterministically, with neither the installer nor Roslyn: create a node,
activate its hub with PingRequest, retype the node, render an area the new type declares. With the
fix reverted:

No renderer is registered for area `RebindMarkerArea` on hub `type/Rebindcffe8718…`.
Available named areas: `$Data`, `$Model`, `$Schema`, `AccessControl`, `Catalog`, `Chat`, `Copy`,
`CourseNav`, `Create`, `Data`, `DataModel`, `Delete`, `Details`, `Edit`, `Export`, `Files`,
`GoToMyCopy`, `Groups`, `ImportMeshNodes`, `Invite`, `LayoutAreas`, `Learn`, `Move`, `NodeTypes`,
`Overview`, `Pin`, `PinnedThumbnail`, `Recycle`, `Search`, `Settings`, `StartExercise`, `StopSync`,
`Threads`, `Thumbnail`, `Unpin`, `VersionDiff`, `Versions`

— item for item ConfigureDefaultNodeHub's set, with not one area of the type the node carries.
That is the plugin gate's Store/Catalog RED, in a unit-of-work you can run in five seconds. Both
variants fail (type-less → typed, and type A → type B), so the pin is general, not install-specific.

Why #1055 did not close it

Not cached is not not-pinned. #1055 stopped PathResolutionService from caching the
fabricated partition-root placeholder, and PackageInstaller posts a DisposeRequest after its
retype. Both are necessary; neither is sufficient. Repairing resolution can only help the next
activation — it does nothing for a hub a bad resolution has already activated. And the
installer's recycle is fire-and-forget, conditional on the placeholder dance having run, and
available to no other writer, while any writer can retype a node (an import, a repair migration, a
user).

The design, and the alternatives weighed

The un-pin belongs at the hub, driven by an event:

  • Not in GetHostedHub. It is address-keyed and knows nothing about nodes; making it
    NodeType-aware would mean a path resolution per lookup — reinstating exactly the hot-path cost
    the RouteInMesh short-circuit exists to avoid (Monitor.Enter_Slowpath ← GetHub ← RouteStreamMessage, two prod incidents).
  • Not "re-read the NodeType at activation". Activation already reads the resolution's node, and
    post-Fix: a partition root permanently bound to the default hub configuration (plugin-gate Store/Catalog RED) #1055 that is not a cached fabrication. The problem is not the first read; it is that there
    is no later activation.
  • Not "make the installer's recycle reliable". That fixes one writer of many, and leaves the
    framework without the guarantee.

So: MeshNodeHubFactory.ResolveHubConfiguration — the single funnel every activation path (Monolith
routing and MessageHubGrain) goes through — wraps the resolved configuration with a
WithInitialization that arms NodeTypeRebindWatcher on the instance hub. It is the same
WithInitialization + self-DisposeRequest idiom as NodeTypeEnrichmentHelpers.ArmOverlaySelfHeal
and RecycleLayoutArea.

The signal is IMeshChangeFeed, not the hub's own node stream — the load-bearing choice. Both
see the retype, but only the feed sees it post-commit: StorageAdapterChangeFeedExtensions
publishes Created/Updated strictly after the storage adapter's write emits. Recycling off the hub's
in-memory commit would race its debounced persist, and since re-activation reads the node back
through routing (i.e. through storage), the fresh hub would resurrect the pre-retype node and
bind the wrong type again — silently, with the stale row now authoritative. That is the hazard
PackageInstaller.RootRetypePersisted already exists for. Using the feed makes the ordering airtight
for free: it is the very event that sweeps PathResolutionService's resolution cache, so by the time
we recycle, the resolver the re-activation will consult has already been invalidated.

Blast radius

Disposing a live hub is not free — in-flight subscriptions are torn down and clients re-subscribe —
so the watcher is deliberately narrow:

  • Fires only on a genuine change of this node's own type. Ordinary content writes (the
    overwhelming majority) republish the node with its type unchanged and are no-ops; so are writes to
    satellites, children and siblings.
  • Fires once (Take(1)). After the recycle the new hub's baseline is the new type, so the
    predicate cannot re-fire — there is no recycle loop, and a flapping writer cannot make one.
  • Deleted never fires (the delete path tears the hub down itself and the event carries no
    authoritative type); a case-only difference never fires (enrichment resolves type paths
    case-insensitively, so it would be churn onto the identical configuration).
  • The feed is a hot subject with no replay, so a freshly-armed watcher structurally cannot fire
    on the state it was armed on — the replay-and-recycle hot loop that forced the overlay watcher's
    version gate cannot occur here.
  • Arming costs one filtered subscription on the process-wide feed subject. No per-hub upstream
    stream, no poll, no timer.
  • The handler runs synchronously on the publisher's thread (inside the storage write's post-commit
    Do), so the recycle is wrapped: a fault there could otherwise fail an unrelated caller's save.
    DisposeRequest is [SystemMessage]/[CanBeIgnored], so it needs no AccessContext on a thread
    that carries none.

PackageInstaller's DisposeRequest stays — it recycles immediately rather than on the feed hop —
but it is no longer load-bearing, and its comment ledger now says so.

Verification

result
NodeTypeRebindTest (2 integration tests), fix reverted 2 failed, 120.9 s of timeouts, the area list above
NodeTypeRebindTest, fix restored 2 passed, 5.0 s
NodeTypeRebindWatcherTest (8 unit tests, firing contract) 8 passed
MeshWeaver.Graph.Test (full) 907 passed, 0 failed
MeshWeaver.PluginCatalog.Test (full) 227 passed, 0 failed
MeshWeaver.Hosting.Monolith.Test (full) 527 passed, 0 failed (4 macOS-only ClrMD skips)

One review catch worth calling out, because it is the kind of wrap that has bitten this codebase
before: the watcher must leave a null HubConfiguration null. Both activation sites branch on it
to activate the fail-fast NACK-fallback hub (a typed DeliveryFailure naming the node type, plus
DeactivateOnIdle); wrapping unconditionally would have killed that branch and swapped fail-fast for
a bare hub that Ignores typed requests — the park class. NodeTypeEnrichmentHelpers.ApplyStreamResult
states the same rule for its own wrap. Pinned by NullHubConfiguration_IsLeftNull.

Comment ledgers updated at both sites the issue names: PathResolutionService.SynthesizePartitionRoot
(not pinning a fabrication is one half; letting go of one is the other) and PackageInstaller (why
its Post is now a fast path and not the guarantee).

🤖 Generated with Claude Code

rbuergi and others added 4 commits August 10, 2026 14:03
A per-node hub binds its HubConfiguration from the node's NodeType exactly once,
at activation, then is pinned by address in HostedHubsCollection. Nothing re-reads
the NodeType, so a node that acquires (or changes) its type while its hub is alive
keeps serving the configuration it was born with -- the mesh DEFAULT when the node
had no type at activation.

The test reproduces the mechanism with neither the installer nor Roslyn: create a
node with no NodeType, activate its hub, retype it to a static NodeType that
declares a distinctive area, then render that area through the layout client.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes the mechanism behind #1104. A per-node hub binds its HubConfiguration from
its node's NodeType exactly once, during activation, and is then pinned by address
in HostedHubsCollection -- routing short-circuits on GetHostedHub for an already-
hosted address, so the hub never resolves its path again and nothing re-reads the
NodeType. A node that acquires or changes its type while its hub is alive keeps
serving the configuration it was born with: the mesh DEFAULT when it had no type.

Not cached is not not-pinned. #1055 stopped PathResolutionService from CACHING the
fabricated partition-root placeholder, and the installer posts a DisposeRequest
after its retype -- both necessary, neither sufficient. Repairing resolution cannot
help a hub a bad resolution already activated, and the installer's recycle is
fire-and-forget, conditional on the placeholder dance, and available to no other
writer. The guarantee belongs in the framework, at the hub.

Every activation now arms NodeTypeRebindWatcher on the instance hub (the same
WithInitialization + self-DisposeRequest idiom as ArmOverlaySelfHeal): it watches
IMeshChangeFeed for its own path and recycles once, the first time an event reports
a different NodeType. The change feed -- not the hub's own stream -- because
StorageAdapterChangeFeedExtensions publishes strictly POST-COMMIT, so the
re-activation's routing read (which goes through storage) sees the new type;
recycling off the in-memory commit would race the debounced persist and resurrect
the pre-retype node. The feed is also a hot subject with no replay, so a freshly
armed watcher structurally cannot fire on the state it was armed on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…write

The change-feed handler runs synchronously on the PUBLISHER's thread -- inside the
storage write's post-commit Do -- so a throw from the recycle would propagate into
that write and fail an unrelated caller's save. Guard it and log instead, the same
best-effort contract ArmOverlaySelfHeal declares.

Also records the negative control: with the wiring reverted the two integration
tests fail in 120.9s with 'No renderer is registered for area RebindMarkerArea',
listing exactly ConfigureDefaultNodeHub's area set; with it restored both pass in
5.0s.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 10, 2026 12:19
rbuergi and others added 2 commits August 10, 2026 14:21
One subscription per live hub means every published change walks every armed
watcher. Say so, and say why that is preferable to a shared path-keyed registry
(nothing to keep in step with hub lifetimes, nothing to leak) -- the same trade
PathResolutionService.OnMeshChange already documents.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tivates

Both activation sites (MonolithRoutingService.CreateHub and MessageHubGrain) branch
on enriched.HubConfiguration == null to activate the fail-fast NACK-fallback hub,
whose UnhandledMessageNack answers every message with a typed DeliveryFailure naming
the node type and which DeactivateOnIdle's so the next access retries. Wrapping
unconditionally made it non-null always, killing that branch and swapping fail-fast
for a bare hub that Ignores typed requests -- the park class, where senders wait out
their whole budget instead of getting a diagnostic.

This is the same rule NodeTypeEnrichmentHelpers.ApplyStreamResult already states for
its own wrap. Nothing is lost: a hub with no configuration at all already retries on
the next access, so it was never pinned.

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

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

Fixes a framework-level routing/activation defect where a per-node hub would permanently keep the HubConfiguration derived from the node’s NodeType at activation time, even if the node’s NodeType later arrives or changes while the hub remains hosted (pinned by address). The fix makes already-hosted hubs “let go” of the wrong NodeType by self-recycling on the first post-commit change-feed event that indicates a NodeType change for their own path.

Changes:

  • Introduces NodeTypeRebindWatcher and wires it into MeshNodeHubFactory.ResolveHubConfiguration so every instance hub arms a one-shot change-feed watcher that posts a self-DisposeRequest when its node’s NodeType changes.
  • Adds deterministic integration + unit coverage for the rebind/recycle contract (NodeTypeRebindTest, NodeTypeRebindWatcherTest).
  • Updates comment ledgers and adds a WhatsNew entry describing the user-visible behavior change.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/MeshWeaver.Hosting.Monolith.Test/NodeTypeRebindTest.cs Integration repro asserting an already-activated hub rebinds after the node’s NodeType arrives/changes.
test/MeshWeaver.Graph.Test/NodeTypeRebindWatcherTest.cs Unit contract tests for the watcher predicate and “post Dispose exactly once” behavior.
src/MeshWeaver.PluginCatalog/PackageInstaller.cs Updates installer commentary to reflect the recycle post is now a fast path, not the guarantee.
src/MeshWeaver.Hosting/PathResolutionService.cs Updates the resolution ledger to document the second-half guarantee being the rebind watcher.
src/MeshWeaver.Graph/Configuration/NodeTypeRebindWatcher.cs Implements the change-feed-driven, one-shot self-recycle watcher.
src/MeshWeaver.Graph/Configuration/MeshNodeHubFactory.cs Hooks rebind arming into the shared activation funnel.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-10-a-page-picks-up-its-type-without-a-restart.md WhatsNew entry describing the behavior change for users/operators.

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

Two more unit tests: a null HubConfiguration passes through untouched (the fail-fast
NACK-fallback branch keys on it), and a non-null one is COMPOSED rather than replaced
so the type's own areas survive the wrap. 8/8 green.

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

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

701 tests  ±0   701 ✅ ±0   15m 37s ⏱️ + 2m 30s
 10 suites ±0     0 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 773 tests  +4   1 581 ✅ +4   5m 23s ⏱️ -39s
   11 suites ±0     192 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 193 tests   - 36   1 193 ✅  - 36   6m 1s ⏱️ -26s
   11 suites ± 0       0 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

This pull request removes 102 and adds 66 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ CompileFailure_LeavesNoReleaseNode
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ Editor_WithCompile_CreatesRelease_StampedToUser_CompileActivityIsSystem
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ SystemCompile_FillsCache_OnReadOnlyPartition_WhereUserCannotWrite
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ Viewer_WithoutCompile_IsRefused_NoReleaseTriggered
MeshWeaver.Hosting.Monolith.Test.NodeVersionCountsRealChangesTest ‑ IdenticalUpdateNode_LeavesVersionUntouched
MeshWeaver.Hosting.Monolith.Test.NodeVersionCountsRealChangesTest ‑ NoOpUpdate_LeavesVersionAndLastModifiedUntouched
MeshWeaver.Hosting.Monolith.Test.NodeVersionCountsRealChangesTest ‑ RealChange_AdvancesVersionByExactlyOne
MeshWeaver.Hosting.Monolith.Test.ObserveQueryFreshnessTest ‑ ObserveQuery_AccessAssignment_AddedEventArrives_AfterCreateNode
MeshWeaver.Hosting.Monolith.Test.ObserveQueryFreshnessTest ‑ ObserveQuery_AccessAssignment_InitialIncludesPriorCreate
MeshWeaver.Hosting.Monolith.Test.ObserveQueryFreshnessTest ‑ ObserveQuery_AfterCreate_ReturnsTheJustCreatedNode
…
MeshWeaver.Hosting.Monolith.Test.NodeTypeRebindTest ‑ NodeTypeArrivesAfterActivation_HubRebindsToIt
MeshWeaver.Hosting.Monolith.Test.NodeTypeRebindTest ‑ NodeTypeChangedAfterActivation_HubRebindsToIt
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseTest ‑ CompilationPending_CreatesReleaseMeshNode_WithNotes
MeshWeaver.Hosting.Monolith.Test.NodeVersionRegressionOnRecycleTest ‑ NodeVersion_DoesNotRegress_WhenHubClockIsBelowNodeVersion_Proves325Fix
MeshWeaver.Hosting.Monolith.Test.OverlaySelfHealInstanceRecycleTest ‑ OverlaidInstance_SelfRecycles_WhenTypeCompilesGreen
MeshWeaver.Hosting.Monolith.Test.OverwritePropagationTest ‑ Overwrite_ReplacesFullNode_PropagatesViaOwner
MeshWeaver.Hosting.Monolith.Test.OwnerDisposalNackTest ‑ DisposingOwner_BeforeMergeTurnRuns_NacksOwnerDisposing
MeshWeaver.Hosting.Monolith.Test.OwnerlessPartitionRepairTest ‑ OwnerlessPartition_AStrangerGetsNothing_OnlyTheOriginalCreatorIsRestored
MeshWeaver.Hosting.Monolith.Test.OwnerlessPartitionRepairTest ‑ OwnerlessPartition_RestoresTheOriginalCreatorsGrant_ForANonAdminOwner
MeshWeaver.Hosting.Monolith.Test.PartitionRootBootstrapTest ‑ BothIntact_SecondChild_IsIdempotent_NoDuplicateNoRecreate
…

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 329 tests  ±0   1 324 ✅ ±0   10m 12s ⏱️ +31s
   11 suites ±0       5 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   11 files  ±0     11 suites  ±0   9m 18s ⏱️ -8s
1 984 tests ±0  1 876 ✅ ±0  108 💤 ±0  0 ❌ ±0 
2 157 runs  +1  2 049 ✅ +1  108 💤 ±0  0 ❌ ±0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

2 016 tests  +46   2 014 ✅ +46   6m 36s ⏱️ +4s
   11 suites ± 0       2 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

This pull request removes 64 and adds 110 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseTest ‑ CompilationPending_CreatesReleaseMeshNode_WithNotes
MeshWeaver.Hosting.Monolith.Test.NodeVersionRegressionOnRecycleTest ‑ NodeVersion_DoesNotRegress_WhenHubClockIsBelowNodeVersion_Proves325Fix
MeshWeaver.Hosting.Monolith.Test.OverlaySelfHealInstanceRecycleTest ‑ OverlaidInstance_SelfRecycles_WhenTypeCompilesGreen
MeshWeaver.Hosting.Monolith.Test.OverwritePropagationTest ‑ Overwrite_ReplacesFullNode_PropagatesViaOwner
MeshWeaver.Hosting.Monolith.Test.OwnerDisposalNackTest ‑ DisposingOwner_BeforeMergeTurnRuns_NacksOwnerDisposing
MeshWeaver.Hosting.Monolith.Test.OwnerlessPartitionRepairTest ‑ OwnerlessPartition_AStrangerGetsNothing_OnlyTheOriginalCreatorIsRestored
MeshWeaver.Hosting.Monolith.Test.OwnerlessPartitionRepairTest ‑ OwnerlessPartition_RestoresTheOriginalCreatorsGrant_ForANonAdminOwner
MeshWeaver.Hosting.Monolith.Test.PartitionRootBootstrapTest ‑ BothIntact_SecondChild_IsIdempotent_NoDuplicateNoRecreate
MeshWeaver.Hosting.Monolith.Test.PartitionRootBootstrapTest ‑ ConcurrentFirstWrites_BothSucceed_OneRootOneGrant
MeshWeaver.Hosting.Monolith.Test.PartitionRootBootstrapTest ‑ FreshPartition_CreatingChild_CreatesSpaceRootAndCreatorGrant
…
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ CaseOnlyDifference_IsNotARebind
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ Delete_NeverRecycles
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ DisposingHub_StopsTheWatcher
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ HubAlreadyDisposing_IsNotPosted
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ NonNullHubConfiguration_IsComposedNotReplaced
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ NullHubConfiguration_IsLeftNull
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ RetypeOfThisNode_RecyclesTheHub_ExactlyOnce
MeshWeaver.Graph.Test.NodeTypeRebindWatcherTest ‑ TypeArrivingOnATypelessNode_RecyclesTheHub
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ CompileFailure_LeavesNoReleaseNode
MeshWeaver.Hosting.Monolith.Test.NodeTypeReleaseGateTest ‑ Editor_WithCompile_CreatesRelease_StampedToUser_CompileActivityIsSystem
…

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

   65 files  ± 0     65 suites  ±0   53m 10s ⏱️ + 1m 54s
8 996 tests +14  8 689 ✅ +14  307 💤 ±0  0 ❌ ±0 
9 169 runs  +15  8 862 ✅ +15  307 💤 ±0  0 ❌ ±0 

Results for commit 716f74f. ± Comparison against base commit 83db1d7.

@rbuergi
rbuergi merged commit 16da939 into main Aug 10, 2026
18 checks passed
rbuergi added a commit that referenced this pull request Aug 10, 2026
Three conflicts, all in files #1143/#1102 also touched.

PathResolutionService + PackageInstaller (the two comment ledgers): merged, not
picked. Main's account is the correct spine — #1143 resolved the residual my
version posed as open, so "check THREE things … is the live hub the one
activated inside the provisioning window?" is deleted in favour of
NodeTypeRebindWatcher being the un-pin. What mine contributed and main did not
is the FINGERPRINT: the available-areas list being exactly
ConfigureDefaultNodeHub's set (AddDefaultLayoutAreas + Invite) and not one area
of the node's real type, plus the gate run (31361446933) it was observed on.
That is what lets someone recognise this defect instead of re-deriving it, so it
is folded into main's paragraphs.

PluginGateRunner: main's design wins outright. #1102 deleted the
shipped-instance preference — the probe host is ALWAYS a freshly created
GateProbe, which binds the just-verified release by construction. My
ResolveTestsHost and its two-branch "shipped vs probe" description are obsolete
and are gone with it; the branch they described no longer exists.

Kept on top of it, because neither is about branch selection:
  • the host still goes into the report (TestsHost). With one branch it no
    longer discriminates a cause, but "No renderer … on hub X" is still
    unreadable without knowing which node X was — that ambiguity is what made
    the original RED get read as "the probe landed on a type path".
  • the probe creation is still BOUNDED. CreateNode is a round-trip with no
    budget of its own; unbounded, a wedge there spends the whole 12-min job
    timeout and reports nothing.

Also drops a stale duplicate <summary> on CreateTestsProbe left by #1102 — the
old "Prefers an instance the package ships" text survived above the new one,
contradicting it, with two <summary> elements on one member.

Verified after the merge: 33/33 tester tests, including all ten GateSummaryTest
cases — the two-sided ones (TestsFailure_NamesTests_AndNeverCompile, and the
same with the allowlist) confirmed present in the trx, so the misattribution
cannot have crept back. mw-plugin-test run end-to-end still emits the CI-facing
line: GATE FAILED — fatal: …. Release -warnaserror clean for
MeshWeaver.PluginTester (+ Hosting and PluginCatalog, built transitively).
@rbuergi
rbuergi deleted the fix/1104-hub-pinned-wrong-nodetype branch August 19, 2026 10:32
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.

A hub activated on the wrong NodeType stays wrong — fixing resolution does not un-pin it

2 participants