Skip to content

fix: four silent defects from the issue sweep (#2729, #2731, #2793, #2797) - #2800

Merged
rbuergi merged 5 commits into
mainfrom
chore/issue-sweep-0830
Aug 30, 2026
Merged

rbuergi merged 5 commits into
mainfrom
chore/issue-sweep-0830

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Two silent production defects from the issue sweep, both of the shape "the code acted on a value nobody wrote".

1. A pinned install rolled on every pod restart — #2731, #2797

CreatePolicySource prepended the deployment's configured default to the policy stream before the persisted Admin/UpdatePolicy value arrived, and the startup pass ran a full evaluation on that seed: registry listing, candidate selection, Deployment PATCH.

memex-cloud has read policy: None since 2026-08-28 and still rolled twice on 08-30 (ci.6664 11:54Z, ci.6739 15:03Z). The node's own history records the sequence — a Startup verdict that evaluated a candidate, then seconds later a PolicyChange verdict reading "updates are disabled on this install", after the roll had been issued.

The seed is deleted. Stage 1 (EnsureExists) already guarantees the node exists, so the live read is the first emission on its own. ReadPolicyStream also drops a null node instead of parsing it — UpdatePolicyContent.Policy defaults to Continuous and Continuous is enum 0, so an absent node parsed to exactly the verdict a pinned install must never reach.

Pinned by a test that asserts on the registry listing, not a log-line spelling: the None branch of RunOnce returns its verdict without listing, so acr.Calls == 0 proves which policy decided. Mutation-checked — restoring the seed fails it with but found 1.

ABuildCompletionDrivenCheck_NeverWarnsAboutTheEventChannel published its build before the watch was subscribed, so the publication was absorbed as the watch's baseline; it passed only because the synthetic seed made that subscription fast. It now waits for the startup check line first — the same positive signal the resilience test already documents.

2. Node content read as raw JSON on any hub but its own — #2729

A node type's WithContentType<T>() configures its own hub. Seven built-in types declared one and registered its $type discriminator nowhere else, so a reader elsewhere in the mesh got an untyped JsonElement and a silent null — no exception, no log; the value renders empty and reactive waits time out.

memex logged it at boot for Admin/_GraphSubscription/inbox. That node carries the inbound-mail subscription state, so the renewal could not see its own record and inbound mail was dead on an install whose every signal was green.

Registered: EaCredential, GraphSubscriptionState, Invitation, MemexClientContent, NotificationChannel, NotificationRule, TeamsConversation.

NodeTypeContentDiscriminatorGuard keeps them registered. The two halves of this contract are joined by nothing, and omitting one compiles, boots, serves and passes every test. The guard reads both halves out of the source, carries a floor so it cannot pass on a broken scan, and self-tests its own classifier. Mutation-checked — deleting one registration fails it by name.

Verification

dotnet build src/MeshWeaver.Graph            -c Release -warnaserror   0 Error(s)
dotnet build memex/Memex.Portal.Shared       -c Release -warnaserror   0 Error(s)
dotnet build test/MeshWeaver.Documentation.Test  -c Release -warnaserror   0 Error(s)
dotnet build test/MeshWeaver.Hosting.Monolith.Test -c Release -warnaserror 0 Error(s)

dotnet test test/MeshWeaver.Hosting.Monolith.Test --filter SelfUpdate        29 passed, 0 failed
dotnet test test/MeshWeaver.Documentation.Test  --filter <the three guards>   3 passed, 0 failed

Fixes #2729
Fixes #2731
Fixes #2797

🤖 Generated with Claude Code

https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK


3. A retry test read a scheduler pause as the regression — #2793

StreamAttachTransientRetryTest polled the attempt counter and treated two equal readings 25 ms apart as settled. The attach retry hops through the thread-pool scheduler between attempts, so on a loaded shard that hop exceeds 25 ms — the poll reads mid-hop and returns 1, which is exactly the #2633 regression signature the test documents. Observed on main at f52ff52e3 (run 33318685970, shard 3), on a merge that cannot reach Orleans.

RegisterStream already records the real condition: subscriptionReady[address] completes when the attach has attached, given up, or been cancelled, and never faults. Exposed as the internal AttachSettled(address) seam (same InternalsVisibleTo discipline as AttachBackoff), awaited with a 30 s backstop — real elapsed time is milliseconds. Captured before the readiness gate opens so the attach cannot finish unobserved, and a missing seam throws rather than silently falling back to a poll.

dotnet build test/MeshWeaver.Hosting.Orleans.Test -c Release -warnaserror   0 Error(s)
dotnet test --filter StreamAttachTransientRetryTest    4 passed, 0 failed, 39 ms

Fixes #2793

…e content on every hub

Two silent production defects, both "the code acted on a value nobody wrote".

## A pinned install rolled on every pod restart (#2731, #2797)

`CreatePolicySource` prepended the deployment's CONFIGURED DEFAULT to the policy
stream before the persisted `Admin/UpdatePolicy` value arrived, and the startup
pass ran a full evaluation on that seed — registry listing, candidate selection,
Deployment PATCH. memex-cloud has read `policy: None` since 2026-08-28 and still
rolled twice on 08-30 (ci.6664 at 11:54Z, ci.6739 at 15:03Z); the node's history
records a `Startup` verdict that evaluated a candidate followed seconds later by
a `PolicyChange` verdict reading "updates are disabled on this install", after
the roll had been issued.

The seed is gone. Stage 1 already guarantees the node EXISTS, so the live read is
the first emission on its own — the poller now waits for a policy it has actually
read. `ReadPolicyStream` also drops a null node rather than parsing it:
`UpdatePolicyContent.Policy` defaults to `Continuous` and `Continuous` is enum 0,
so an absent node parsed to exactly the verdict a pinned install must never reach.

Pinned by `StartupCheck_OnAPinnedInstall_RunsUnderThePersistedPolicy_...`, which
asserts on the REGISTRY LISTING rather than a log-line spelling: the `None` branch
returns its verdict without listing, so `acr.Calls == 0` proves which policy
decided. Mutation-checked — restoring the seed fails it with "found 1".

`ABuildCompletionDrivenCheck_NeverWarnsAboutTheEventChannel` published its build
before the watch was subscribed, so the publication was absorbed as the watch's
BASELINE; it passed only because the synthetic seed made the subscription fast.
It now waits for the startup check line first — the same positive signal the
resilience test already documents.

## Node content read as raw JSON on any hub but its own (#2729)

A node type's `WithContentType<T>()` configures its own hub. Seven built-in types
declared one and registered its `$type` discriminator nowhere else, so a reader
elsewhere in the mesh got an untyped `JsonElement` and a SILENT null — no
exception, no log, the value renders empty and reactive waits time out.

memex logged it at boot for `Admin/_GraphSubscription/inbox`: that node carries
the inbound-mail subscription state, so the renewal could not see its own record
and inbound mail was dead on an install whose every signal was green.

`EaCredential`, `GraphSubscriptionState`, `Invitation`, `MemexClientContent`,
`NotificationChannel`, `NotificationRule` and `TeamsConversation` now register.

`NodeTypeContentDiscriminatorGuard` keeps them registered: the two halves of this
contract are joined by nothing and omitting one compiles, boots, serves and passes
every test. It reads both halves out of the source, carries a floor so it cannot
pass on a broken scan, and self-tests its own classifier. Mutation-checked —
deleting one registration fails it by name.

Fixes #2729
Fixes #2731
Fixes #2797

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
Copilot AI lite review requested due to automatic review settings August 30, 2026 16:15

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 two classes of silent production defects: (1) self-update checks running under a synthetic default policy before the persisted Admin/UpdatePolicy value is actually read (causing pinned installs to roll on restart), and (2) node content types declared via WithContentType<T>() not being registered mesh-wide, causing cross-hub reads to deserialize as untyped JSON and yield silent nulls.

Changes:

  • Self-update policy source now waits for a real Admin/UpdatePolicy stream emission (and ignores null nodes) instead of prepending the configured default into the decision stream.
  • Registers $type discriminators mesh-wide for the affected built-in node content types so any hub can read them typed.
  • Adds/strengthens tests and introduces a governance guard to prevent future drift; adds “What’s New” entries documenting both fixes.

Reviewed changes

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

Show a summary per file
File Description
memex/Memex.Portal.Shared/SelfUpdate/SelfUpdateHostedService.cs Removes default-policy seeding from the live policy stream and gates parsing on non-null node emissions to avoid wrong-policy startup decisions.
test/MeshWeaver.Hosting.Monolith.Test/SelfUpdatePollerResilienceTest.cs Tightens assertions to ensure the startup pass never evaluates tags under a non-persisted policy.
test/MeshWeaver.Hosting.Monolith.Test/SelfUpdateCheckVerdictTest.cs Ensures build-completion publication happens only after the watch is live; adds a pinned-install startup test asserting zero registry listings under None.
test/MeshWeaver.Documentation.Test/NodeTypeContentDiscriminatorGuard.cs New governance test that enforces “declared content type implies discriminator registered” across src/ + memex/.
src/MeshWeaver.Graph/Configuration/EaCredentialNodeType.cs Registers EaCredential discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/GraphSubscriptionNodeType.cs Registers GraphSubscriptionState discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/InvitationNodeType.cs Registers Invitation discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/MemexClientNodeType.cs Registers MemexClientContent discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/NotificationChannelNodeType.cs Registers NotificationChannel discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/NotificationRuleNodeType.cs Registers NotificationRule discriminator mesh-wide.
src/MeshWeaver.Graph/Configuration/TeamsConversationNodeType.cs Registers TeamsConversation discriminator mesh-wide.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-30-a-pinned-install-stays-pinned.md Documents the pinned-install restart behavior fix.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-30-node-content-reads-typed-on-every-hub.md Documents the mesh-wide discriminator registration fix and the new guard.

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

…pause

StreamAttachTransientRetryTest polled ThrowingStreamProvider.GetStreamCalls and
treated two equal readings 25 ms apart as "settled". The attach retry hops through
the thread-pool scheduler between attempts (AttachBackoff is collapsed to zero in
the test, but a zero-delay Observable.Timer still needs a pool thread), so on a
loaded shard that hop exceeds 25 ms, the poll reads the count mid-hop and returns.

The value it returns is 1 — which is exactly the #2633 regression signature the
test documents. A false RED that spells out the regression's own signature is
worse than no test: the correct response to it is to hunt a bug that is not there.
Observed on main at f52ff52 (run 33318685970, shard 3), on a merge that cannot
reach Orleans.

"Unchanged for 25 ms" is not the condition under test; "the attach gave up" is.
RegisterStream already records that: subscriptionReady[address] completes when the
attach has attached, given up on an exhausted budget, or been cancelled, and it
never faults. Exposed as the internal AttachSettled(address) seam — the same
InternalsVisibleTo discipline as AttachBackoff — and awaited with a 30 s BACKSTOP
(not the measurement: real elapsed time is milliseconds). The counter is read once,
after the terminal.

Captured before the readiness gate opens, so there is no window in which the attach
could finish unobserved, and a missing seam throws rather than silently falling back
to a poll.

    dotnet build test/MeshWeaver.Hosting.Orleans.Test -c Release -warnaserror  0 Error(s)
    dotnet test  --filter StreamAttachTransientRetryTest   4 passed, 0 failed, 39 ms

Fixes #2793

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
@rbuergi rbuergi changed the title fix(#2729,#2731,#2797): decide under the policy you read, and type the content on every hub fix: four silent defects from the issue sweep (#2729, #2731, #2793, #2797) Aug 30, 2026
…switch, and guard the arithmetic

MeshWeaver.PluginCatalog.Test measured 315/320/315 s across three consecutive
runs — 66-72% of the `timeout 8m` = 480 s per-project cap — then went 480 s
exit=124 TIMEOUT on e12697e (run 33302269352, shard 0), with the SAME tree
passing at 320 s on the very next run. No change was responsible. The five "test
failures" that came with it were timing casualties of the kill, so the red named
tests that had not failed and cost a full investigation to dismiss — and per this
repo's rules it must not simply be re-run.

Raising the cap is not the fix: the cap is what turns a wedge into a bounded,
attributable failure. Splitting lowers the numerator, and costs nothing because
the parts land on different runners.

## The rule that missed it

shard-assign.sh already had a split rule and it is about BALANCE — split when a
project is the binding long pole. PluginCatalog passed that rule comfortably
(348 s against a ~421 s ideal shard load) and was still one slow runner from
exit=124. Headroom against the cap is an INDEPENDENT trigger; the header now
states both, and says which one bites in production.

## The guard found the second one

ShardWeightHeadroomGuard divides each entry's weight by its part count and fails
above 60% of the cap. On its first run it named Hosting.Monolith.Test: 663 s ÷ 2
= 332 s per half, 69% of the cap — the same zone that killed PluginCatalog, and
recorded in the file's own header as the sharding floor. Split into 3 (~221 s).

The cap is READ from dotnet-test.yml, never restated, so the guard cannot quietly
disagree with the budget it guards; the parse of both the cap and the parts column
is self-tested, and a short parse fails rather than passing on an empty table.

    PluginCatalog  348 s -> 2 parts, ~174 s each  (36% of cap)
    Monolith       663 s -> 3 parts, ~221 s each  (46% of cap)
    shard loads    380 379 379 379 379 379        (was 380 379 379 379 379 378)
    all five heavy units land on five distinct shards

Fixes #2747

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

787 tests   - 391   783 ✅  - 202   4m 23s ⏱️ - 1m 5s
  7 suites +  2     4 💤  - 189 
  7 files   +  2     0 ❌ ±  0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 1178 and adds 787 tests. Note that renamed tests count towards both.
MeshWeaver.Documentation.Test.BareToObservableOnReadPathGuard ‑ NoReadPathFile_UsesTheParameterlessToObservable
MeshWeaver.Documentation.Test.BlockingBridgeInTestRatchetGuard ‑ NoNewTestBridgesAnObservableToABlockingWait
MeshWeaver.Documentation.Test.BlockingBridgeInTestRatchetGuard ‑ TheScannerFindsTheShapeItIsRatcheting
MeshWeaver.Documentation.Test.BlockingBridgeSelfDeadlockMechanismTest ‑ AwaitingInstead_UnderTheSameCapturedContext_CompletesPromptly
MeshWeaver.Documentation.Test.BlockingBridgeSelfDeadlockMechanismTest ‑ BlockingBridge_UnderASingleThreadedCapturedContext_Deadlocks
MeshWeaver.Documentation.Test.BrowserProvisioningGateGuard ‑ EveryBrowserProvisioner_BanksTheDownloadBeforeApt
MeshWeaver.Documentation.Test.BrowserProvisioningGateGuard ‑ EveryBrowserProvisioner_TreatsADependencyFailureAsAWarning
MeshWeaver.Documentation.Test.CdImageVersionsShareOneSourceGuard ‑ EveryVersionIsComputedFromThisRepository
MeshWeaver.Documentation.Test.CdImageVersionsShareOneSourceGuard ‑ TheWorkflowActuallyYieldsVersionComputations
MeshWeaver.Documentation.Test.CessionExampleTest ‑ CedeIntoLayer_AllSampleClaims_CorrectTotals
…
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ PathResolver_BothPaths_ResolveToExactNodeNoRemainder
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ RawJsonDeserialize_BothFiles_ProduceMeshNode
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ ReadNode_BothPaths_ReturnNonNull
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ ReadNode_ReverseOrder_BothPathsReturnNonNull
MeshWeaver.Acme.Test.NodeTypeProgressAreaTest ‑ ColdCycle_TriggersCompile_StreamsTransitions_LandsAssemblyOnDisk
MeshWeaver.Acme.Test.NodeTypeProgressAreaTest ‑ WarmCycle_AfterCompile_NoRecompile_FirstEmissionIsCachedOk
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ AllTasks_ShouldRenderWithData
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ Backlog_ShouldRenderWithData
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ MeshQuery_ShouldFilterByNodeType
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ MeshQuery_ShouldFindAll21TodoNodes
…
This pull request removes 193 skipped tests and adds 4 skipped tests. Note that renamed tests count towards both.
MeshWeaver.Portal.E2E.AgentFilesToolingTest ‑ EveryTool_RunsForReal_AndTheFileIsARealMeshNode
MeshWeaver.Portal.E2E.AgentFilesToolingTest ‑ TheWorkingArea_IsScopedToItsOwnThread
MeshWeaver.Portal.E2E.BlazorVsNextCompareTest ‑ Compare_Blazor_Vs_Next_AcrossScreens
MeshWeaver.Portal.E2E.ChatAtAutocompleteTest ‑ TypingAt_SurfacesLocalPartitionNodes_AndSlashExpandsPartitions
MeshWeaver.Portal.E2E.ChatClearSkillNewComposerTest ‑ ClearSkill_InSidePanel_ReplacesThreadWithComposer_AndLeavesMainAlone
MeshWeaver.Portal.E2E.ChatComposerRefocusTest ‑ Composer_AcceptsTyping_AfterClickingAwayAndBack
MeshWeaver.Portal.E2E.ChatComposerSafariFocusReproTest ‑ Composer_AcceptsTyping_AfterFocusStealDuringStream
MeshWeaver.Portal.E2E.ChatComposerStreamingFocusTest ‑ Composer_KeepsFocus_WhileResponseStreams
MeshWeaver.Portal.E2E.ChatComposerSwitchSelectionTest ‑ SlashCommands_OpenPicker_AndSelectionUpdatesStatusRow
MeshWeaver.Portal.E2E.ChatDelegationTest ‑ Coordinator_DelegatesToWorker_SubThreadSpawns_ResultFlowsBack_AndRendersWhenOpened
…
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_BootsAndServesHealthy
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsMeshApi
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsNoBusinessRulesAssembly_ScopesComeFromThePlugin
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsPluginRegistry

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

    7 files   -     3      7 suites   - 3   5m 32s ⏱️ -8s
1 085 tests  - 1 999  1 085 ✅  - 1 999  0 💤 ±0  0 ❌ ±0 
1 085 runs   - 2 664  1 085 ✅  - 2 664  0 💤 ±0  0 ❌ ±0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 2683 and adds 684 tests. Note that renamed tests count towards both.
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ PathResolver_BothPaths_ResolveToExactNodeNoRemainder
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ RawJsonDeserialize_BothFiles_ProduceMeshNode
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ ReadNode_BothPaths_ReturnNonNull
MeshWeaver.Acme.Test.AcmePathResolutionDiagnosticTest ‑ ReadNode_ReverseOrder_BothPathsReturnNonNull
MeshWeaver.Acme.Test.NodeTypeProgressAreaTest ‑ ColdCycle_TriggersCompile_StreamsTransitions_LandsAssemblyOnDisk
MeshWeaver.Acme.Test.NodeTypeProgressAreaTest ‑ WarmCycle_AfterCompile_NoRecompile_FirstEmissionIsCachedOk
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ AllTasks_ShouldRenderWithData
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ Backlog_ShouldRenderWithData
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ MeshQuery_ShouldFilterByNodeType
MeshWeaver.Acme.Test.ProjectTodoViewsTest ‑ MeshQuery_ShouldFindAll21TodoNodes
…
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_AlwaysIncludesIdentityClaims
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_DropsEmptyRoleEntries
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_TokenWithRoles_StampsEachRoleAsRoleClaim
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_TokenWithoutRoles_HasNoRoleClaims
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ LoadDbRoles_WithRealBudget_ResolvesEmptyForUserWithNoGrants
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ ReadThatReachesNoVerdict_IsUnavailable_NotEmptyRoles
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ RoleStoreReachable_StillAuthenticates
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ RoleStoreUnreachable_ChallengeIs503WithRetryAfter_NotAReducedPrincipal
MeshWeaver.Auth.Test.ApiTokenServiceStaleReadTest ‑ DeleteToken_ImmediatelyAfterCreate_RemovesTheNewToken
MeshWeaver.Auth.Test.ApiTokenServiceStaleReadTest ‑ RevokeToken_AfterImmediateValidate_BlocksFutureValidation
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

963 tests   - 25   963 ✅  - 25   5m 2s ⏱️ - 1m 3s
  7 suites + 2     0 💤 ± 0 
  7 files   + 2     0 ❌ ± 0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 844 and adds 819 tests. Note that renamed tests count towards both.
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ Every_localization_key_the_about_tab_reads_exists_in_the_catalog
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ StartedAt_is_rendered_in_the_viewers_zone_not_utc
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ StartedAt_renders_nothing_when_the_start_time_is_unknown
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ ToRows_maps_manifests_to_display_rows
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ UpdateStatusMarkdown_renders_both_verdicts_and_nothing_when_unknown
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ ATokenContainingAColonSurvives
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicWithoutAColonYieldsNoToken
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"\")
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"anything\")
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"nuget\")
…
MeshWeaver.Autocomplete.Test.AutocompleteDelegationDeadlockTest ‑ DelegatedAutocomplete_ConcurrentRequests_DoNotDeadlock
MeshWeaver.Autocomplete.Test.AutocompleteDelegationDeadlockTest ‑ DelegatedAutocomplete_FanOutAcrossPartitions_DoesNotDeadlock
MeshWeaver.Autocomplete.Test.AutocompleteDelegationDeadlockTest ‑ DelegatedAutocomplete_NonexistentTarget_DoesNotHang
MeshWeaver.Autocomplete.Test.AutocompleteDelegationDeadlockTest ‑ DelegatedAutocomplete_RelativeContextSelfDelegation_ResolvesQuickly
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_BasePathWithPrefix_ReturnsIcons
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_BasePathWithPrefix_SearchesWithinPath
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_CaseInsensitive_MatchesLowerInput
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_ContainsMatch_FindsBySubstring
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_EmptyPrefix_ReturnsIconsWhereAvailable
MeshWeaver.Autocomplete.Test.AutocompleteIconTests ‑ Autocomplete_LimitIsRespected
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

2 998 tests  +2 374   2 806 ✅ +2 182   6m 1s ⏱️ ±0s
    8 suites +    2     192 💤 +  192 
    8 files   +    2       0 ❌ ±    0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 294 and adds 2668 tests. Note that renamed tests count towards both.
MeshWeaver.ContentCollections.Indexing.Test.ChunkNavigationStoreTest ‑ GetChunkCount_EqualsNumberOfChunks
MeshWeaver.ContentCollections.Indexing.Test.ChunkNavigationStoreTest ‑ GetChunkCount_UnknownFile_IsZero
MeshWeaver.ContentCollections.Indexing.Test.ChunkNavigationStoreTest ‑ GetChunk_OutOfRange_ReturnsNull
MeshWeaver.ContentCollections.Indexing.Test.ChunkNavigationStoreTest ‑ GetChunk_ReturnsChunkAtIndex
MeshWeaver.ContentCollections.Indexing.Test.ChunkNavigationStoreTest ‑ GetChunk_UnknownFile_ReturnsNull
MeshWeaver.ContentCollections.Indexing.Test.ContentChunkSearchTest ‑ Grammar_EmptyText_ReturnsHint_NoHits
MeshWeaver.ContentCollections.Indexing.Test.ContentChunkSearchTest ‑ Grammar_NamespaceFromDefault_WhenNoNamespaceToken
MeshWeaver.ContentCollections.Indexing.Test.ContentChunkSearchTest ‑ Grammar_NamespaceTokenWins_OverDefault
MeshWeaver.ContentCollections.Indexing.Test.ContentChunkSearchTest ‑ Grammar_NoNamespaceAndNoDefault_ReturnsHint_NoHits
MeshWeaver.ContentCollections.Indexing.Test.ContentChunkSearchTest ‑ Grammar_NoScope_DefaultsToSubtree
…
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ ADeniedRoleIsNotAWriteGrant
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ ASelfConsistentRootGrant_IsAllowedAtTheWriteBoundary_ButNeverOfferedInTheUi
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ AnEntitlementIsStillAllowed(role: \"Commenter\")
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ AnEntitlementIsStillAllowed(role: \"Viewer\")
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ AnOrdinarySpaceIsUntouched
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ AssignmentOffAGrantPath_IsIgnored
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ BijectiveSync_IsNotSystemOwned
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ CanGrantAt_APartitionContext_IsAllowed(scope: \"Admin\")
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ CanGrantAt_APartitionContext_IsAllowed(scope: \"AgenticEngineering\")
MeshWeaver.Graph.Test.AccessAssignmentGuardTest ‑ CanGrantAt_APartitionContext_IsAllowed(scope: \"Store/Plugin\")
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    8 files   -   2      8 suites   - 2   6m 1s ⏱️ -24s
1 521 tests  - 663  1 520 ✅  - 660  1 💤  - 3  0 ❌ ±0 
2 194 runs  + 10  2 193 ✅ + 13  1 💤  - 3  0 ❌ ±0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 1472 and adds 809 tests. Note that renamed tests count towards both.
MeshWeaver.Content.Test.CompilationErrorTest ‑ Overview_ShouldShowCompilationError_WhenCodeIsBroken
MeshWeaver.Content.Test.CompileErrorOverviewTest ‑ BrokenNodeType_InstanceOverview_ComesBack_SayingCompilationError
MeshWeaver.Content.Test.CompileErrorOverviewTest ‑ BrokenNodeType_InstanceOverview_StillAsksForACodeFix_AndLinksTheCompileLog
MeshWeaver.Content.Test.CompileErrorOverviewTest ‑ OneBrokenNodeType_DoesNotBreak_OtherNodes
MeshWeaver.Content.Test.CompileErrorOverviewTest ‑ TimedOutNodeType_InstanceOverview_ReadsAsRetry_AndLinksTheCompileLog
MeshWeaver.Content.Test.ContentAutocompleteInChatTest ‑ ContentAutocompleteProvider_ReturnsFileItems
MeshWeaver.Content.Test.ContentAutocompleteInChatTest ‑ ContentCompletions_CollectionNames_ShownAtFirstLevel
MeshWeaver.Content.Test.ContentAutocompleteInChatTest ‑ ContentCompletions_FilesInCollection_ReturnedAfterColon
MeshWeaver.Content.Test.ContentAutocompleteInChatTest ‑ ContentFiles_FilterByPartialName
MeshWeaver.Content.Test.ContentAutocompleteInChatTest ‑ ContentFiles_HaveExpectedUnifiedPathFormat
…
MeshWeaver.Kernel.Test.KernelScriptReferencesFreshnessTest ‑ AnAssemblyLoadedAfterTheFirstCall_IsStillInTheReferenceSet
MeshWeaver.Kernel.Test.NotebookParserTest ‑ NotebookCellControl_Code_ShouldHaveCorrectProperties
MeshWeaver.Kernel.Test.NotebookParserTest ‑ NotebookCellControl_Markdown_ShouldHaveCorrectProperties
MeshWeaver.Kernel.Test.NotebookParserTest ‑ NotebookControl_EditCell_ThenSerialize_ShouldReflectChanges
MeshWeaver.Kernel.Test.NotebookParserTest ‑ NotebookControl_WithCellsFromParser_ShouldStoreCells
MeshWeaver.Kernel.Test.NotebookParserTest ‑ NotebookControl_WithCells_ShouldStoreCells
MeshWeaver.Kernel.Test.NotebookParserTest ‑ ParseMarkdown_EmptyString_ShouldCreateEmptyCodeCell
MeshWeaver.Kernel.Test.NotebookParserTest ‑ ParseMarkdown_ThenSerialize_ShouldRoundtrip
MeshWeaver.Kernel.Test.NotebookParserTest ‑ ParseMarkdown_WithCodeBlock_ShouldCreateCodeCell
MeshWeaver.Kernel.Test.NotebookParserTest ‑ ParseMarkdown_WithMarkdownContent_ShouldCreateMarkdownCell
…
This pull request removes 4 skipped tests and adds 1 skipped test. Note that renamed tests count towards both.
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_BootsAndServesHealthy
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsMeshApi
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsNoBusinessRulesAssembly_ScopesComeFromThePlugin
MeshWeaver.PluginImage.Test.PortalImageFacility ‑ InjectedImage_ShipsPluginRegistry
MeshWeaver.Testcontainers.Test.MemexContainerTest ‑ ARealMemexStartsBesideAPostgres_AndAnswersHealthz

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

1 153 tests  +709   1 153 ✅ +709   7m 46s ⏱️ + 3m 22s
    7 suites +  1       0 💤 ±  0 
    7 files   +  1       0 ❌ ±  0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 385 and adds 1094 tests. Note that renamed tests count towards both.
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_AlwaysIncludesIdentityClaims
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_DropsEmptyRoleEntries
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_TokenWithRoles_StampsEachRoleAsRoleClaim
MeshWeaver.Auth.Test.ApiTokenAuthenticationHandlerTests ‑ BuildClaims_TokenWithoutRoles_HasNoRoleClaims
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ LoadDbRoles_WithRealBudget_ResolvesEmptyForUserWithNoGrants
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ ReadThatReachesNoVerdict_IsUnavailable_NotEmptyRoles
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ RoleStoreReachable_StillAuthenticates
MeshWeaver.Auth.Test.ApiTokenRoleResolutionUnavailableTests ‑ RoleStoreUnreachable_ChallengeIs503WithRetryAfter_NotAReducedPrincipal
MeshWeaver.Auth.Test.ApiTokenServiceStaleReadTest ‑ DeleteToken_ImmediatelyAfterCreate_RemovesTheNewToken
MeshWeaver.Auth.Test.ApiTokenServiceStaleReadTest ‑ RevokeToken_AfterImmediateValidate_BlocksFutureValidation
…
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ Every_localization_key_the_about_tab_reads_exists_in_the_catalog
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ StartedAt_is_rendered_in_the_viewers_zone_not_utc
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ StartedAt_renders_nothing_when_the_start_time_is_unknown
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ ToRows_maps_manifests_to_display_rows
Memex.Portal.Shared.Test.AboutSettingsTabTest ‑ UpdateStatusMarkdown_renders_both_verdicts_and_nothing_when_unknown
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ ATokenContainingAColonSurvives
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicWithoutAColonYieldsNoToken
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"\")
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"anything\")
Memex.Portal.Shared.Test.ApiTokenBasicAuthTest ‑ BasicYieldsThePasswordHalf(username: \"nuget\")
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   44 files  + 2     44 suites  +2   34m 48s ⏱️ +43s
8 507 tests + 5  8 310 ✅ + 5  197 💤 ±0  0 ❌ ±0 
9 180 runs  +13  8 983 ✅ +13  197 💤 ±0  0 ❌ ±0 

Results for commit 5556794. ± Comparison against base commit a7cc0a7.

This pull request removes 1 and adds 6 tests. Note that renamed tests count towards both.
MeshWeaver.Documentation.Test.HandWovenGateRatchetGuard ‑ TheTestTreeInventoryOnlyShrinks
MeshWeaver.Documentation.Test.HandWovenGateRatchetGuard ‑ TheTestTreeAllowFileStaysDeleted
MeshWeaver.Documentation.Test.NodeTypeContentDiscriminatorGuard ‑ EveryDeclaredContentType_AlsoRegistersItsDiscriminator
MeshWeaver.Documentation.Test.NodeTypeContentDiscriminatorGuard ‑ TheGuardDetectsAnUnregisteredType
MeshWeaver.Documentation.Test.ShardWeightHeadroomGuard ‑ NoScheduledUnitRunsCloseToThePerProjectCap
MeshWeaver.Documentation.Test.ShardWeightHeadroomGuard ‑ TheGuardParsesTheCapAndThePartsColumn
MeshWeaver.Hosting.Monolith.Test.SelfUpdateCheckVerdictTest ‑ StartupCheck_OnAPinnedInstall_RunsUnderThePersistedPolicy_NotTheConfiguredDefault

♻️ This comment has been updated with latest results.

@rbuergi
rbuergi added this pull request to the merge queue Aug 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 30, 2026
@rbuergi
rbuergi added this pull request to the merge queue Aug 30, 2026
rbuergi added a commit that referenced this pull request Aug 30, 2026
… ejected #2800 from the queue

PodHubClaimLifetimeTest.WaitUntilSettled was the SAME settle-by-silence poll this
PR removes from StreamAttachTransientRetryTest — its own comment cited that one as
precedent ("the same technique as StreamAttachTransientRetryTest"). The retry hops
through the thread-pool scheduler between attempts, so on a loaded shard that hop
exceeds the 25 ms window, the poll reads the count during a PAUSE and returns it.

Measured, not inferred: this PR's own merge-queue build (run 33322976985, shard 0,
gh-readonly-queue/main/pr-2800-afb5a5ec9) failed with

    AClaimThatCannotLand_WhereNoGrainCanBeHosted_StopsAtTheInitialBudget_AtDebug
    Expected value to be 6 ... but found 4

and the queue ejected the PR. 4 is a number this test would otherwise force someone
to interpret as a real defect in the claim's budget.

"Unchanged for 25 ms" was never the condition under test; "the claim ended" is. The
production code already says so: the impossibility terminal logs "did not land in
this process" at Debug from the attach's terminal onError, after every permitted
attempt is spent. The test has a RecordingLogger, so that line is available as a
positive signal — wait for it, then read the count once. The loop bound is a
backstop against a hang, not the measurement.

The sibling helper WaitForAttempts is left alone: it waits for `count >= target`,
which is already a positive signal.

    dotnet build test/MeshWeaver.Hosting.Orleans.Test -c Release -warnaserror  0 Error(s)
    dotnet test  --filter PodHubClaimLifetimeTest|StreamAttachTransientRetryTest
                 9 passed, 0 failed, 1 s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
rbuergi and others added 2 commits August 30, 2026 19:03
…t a pause (#2793 sibling)

The SECOND settle-by-silence poll, found by the merge queue itself:
PodHubClaimLifetimeTest.AClaimThatCannotLand_WhereNoGrainCanBeHosted_
StopsAtTheInitialBudget_AtDebug failed the queue entry for #2800 with
"Expected value to be 6 … but found 1" — the identical shape, and the
identical value, as StreamAttachTransientRetryTest in #2793.

Its WaitUntilSettled polled factory.AttachCalls every 25 ms and called two
equal readings "settled". The claim's RetryWhen hops through the thread-pool
scheduler between attempts; on a loaded runner that hop exceeds 25 ms, so the
poll reads the counter MID-HOP and returns 1 — which is exactly the value
unfixed code produces. A false RED that spells the regression's own signature
is worse than no test, and the file's own doc comment even said "there is no
completion signal to await for a claim that ends".

There is one now. OrleansRoutingService.PodHubClaimSettled(address) is the
sibling of #2800's AttachSettled: a per-address AsyncSubject<Unit>, an
INSTANCE field on the routing service (never static), armed BEFORE the claim
is subscribed so it cannot terminate unobserved, and completed in BOTH
terminal arms of the claim's Subscribe — it landed, or it hit the one
terminal that is impossibility rather than a budget (a process that cannot
host a grain can never win the claim). A claim that is still retrying never
completes it, which is the honest answer for a lifetime with no give-up:
that case keeps WaitForAttempts, which was already a positive signal. The
subject is removed with the registration.

Verified:
- 5/5 green, and in 1 s rather than the poll's seconds-per-test.
- MUTATION-PROVED, not merely observed green: collapsing the budget
  (`attempt >= PodHubAttachRetries` → `attempt >= 0`) makes the test FAIL
  with "found 1", so the new wait still detects the regression it exists for.
  Restored and re-verified after the probe.

Folded into this PR rather than opened as its own, because the queue builds
each entry on the ones ahead of it: #2800 alone still meets the unfixed
pod-hub test and ejects (16:48Z), while #2803 alone meets the unfixed stream
attach and ejected too (17:00Z). Neither sibling can land without the other,
so they belong in ONE entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi removed this pull request from the merge queue due to a manual request Aug 30, 2026
@rbuergi

rbuergi commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Folded in a fifth defect: the OTHER settle-by-silence poll (555679425)

This PR's own merge-queue entry ejected at 16:48Z — not on anything in its diff, but on the sibling of the very test it fixes:

MeshWeaver.Hosting.Orleans.Test.PodHubClaimLifetimeTest
  .AClaimThatCannotLand_WhereNoGrainCanBeHosted_StopsAtTheInitialBudget_AtDebug [FAIL]
  Expected value to be 6 … but found 1

Same shape, same value, same cause as #2793: WaitUntilSettled polled factory.AttachCalls every 25 ms and called two equal readings "settled", while the claim's RetryWhen hops through the thread-pool scheduler between attempts. Its own doc comment said the quiet part out loud — "there is no completion signal to await for a claim that ends".

There is one now. OrleansRoutingService.PodHubClaimSettled(address) is the sibling of this PR's AttachSettled: a per-address AsyncSubject<Unit>, an instance field (never static), armed before the claim is subscribed so it cannot terminate unobserved, completed in both terminal arms — it landed, or it hit the terminal that is impossibility rather than a budget. A claim that is still retrying completes nothing, which is the honest answer for a lifetime with no give-up; that test keeps its existing positive WaitForAttempts.

Why it had to go in this PR, not its own

The queue builds each entry on the entries ahead of it, so the two fixes were ejecting each other:

entry failed on fixed by
pr-2800 (16:48Z) PodHubClaimLifetimeTest only this commit
pr-2803 (17:00Z) StreamAttachTransientRetryTest only this PR

Neither sibling could land alone. One entry, both fixes.

Verified

  • 5/5 green in 1 s (the poll took seconds per test).
  • Mutation-proved, not merely observed green: collapsing the budget (attempt >= PodHubAttachRetries → attempt >= 0) makes the test FAIL with found 1 — the new wait still detects the regression it exists for. Source restored and re-verified afterwards.
  • The What's New entry now covers both siblings rather than one.

I dequeued this PR to push (GitHub declines a push to a branch with a live queue entry) — please re-queue it; it is CLEAN and the required check was green on the previous head.

rbuergi added a commit that referenced this pull request Aug 30, 2026
Superseded by a better fix on #2800, from the session that owns the queue.

We both found the same defect from opposite ends within minutes — I found it
because it ejected #2800, they found it because it and StreamAttachTransientRetryTest
were deadlocking the queue against each other (an entry is built only on the entries
ahead of it, so pr-2800 ejected on PodHubClaimLifetimeTest and pr-2803 ejected on
StreamAttachTransientRetryTest; neither could land alone).

Theirs is the one to keep. Mine waited for the production code's Debug line "did not
land in this process" — a positive terminal, but one that pins the test to a LOG
MESSAGE SPELLING, which is precisely the contract shape #2795 (in this PR) exists to
warn about. Theirs adds OrleansRoutingService.PodHubClaimSettled(address): a per-address
AsyncSubject on an INSTANCE field, armed before the claim is subscribed and completed in
both terminal arms — the exact sibling of AttachSettled, and symmetric with it.

Reverting here so main takes one version rather than resolving a conflict between two
correct fixes to the same method. #2800 must land before this PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
@rbuergi
rbuergi added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit dbf7204 Aug 30, 2026
25 checks passed
rbuergi added a commit that referenced this pull request Aug 30, 2026
…emoves

CI red on run 33324723607:

    InertContentIndexTest.Search_WithInertServices_ReturnsTheReason_InBothQueryShapes
    Assert.Contains() Failure: Sub-string not found

It asserted `"count":0` on the envelope for an inert index — i.e. for a search that
never ran. That assertion IS the defect #2741 reports, written down as a
requirement: `count: 0` is byte-identical to "I searched and found nothing", and
`count` is the field a caller reads, so the AGENTS-mandated pre-deletion sweep
returned a PASS having verified nothing.

Updated to the new contract — no `count` at all, `searched:false`, the
`search-not-performed` discriminator, and the reason still present, since an
envelope that says nothing was searched must still say what to configure.

## How it got past me

I ran `--filter ContentChunkSearchTest` — my own new tests — and not the project.
The whole point of a contract change is that it breaks the OTHER tests holding the
old contract, which is exactly the set a filter excludes. Corrected here by running
each touched project in full:

    MeshWeaver.ContentCollections.Indexing.Test    62 passed, 0 failed
    MeshWeaver.Messaging.Hub.Test                 341 passed, 0 failed
    MeshWeaver.Data.Test                          403 passed, 0 failed
    MeshWeaver.Documentation.Test                 121 passed, 0 failed

## Merged main (dbf7204)

The other reds on that run were `MeshWeaver.Hosting.Monolith.Test (part 1/2)
exit=124 TIMEOUT (8m wall-clock cap hit) elapsed=480s` and its downstream noise —
"part 1/2", so the run predates the 3-way split that #2800 landed. Merging main
takes the branch to `1/3` at ~46% of the cap. This is a re-sync for a reason the
diff cannot reach, not a routine catch-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FhP7Jg65q8cAATnFfeaTK
rbuergi added a commit that referenced this pull request Aug 30, 2026
…ecting releases on them (#2777, #2700)

Since the merge queue went live every entry builds on a loaded runner BY
CONSTRUCTION, and three releases have now been ejected by three different
tests whose assertion is a bound rather than a condition:

  #2800  PodHubClaimLifetimeTest        (fixed, merged)
  #2803  StreamAttachTransientRetryTest (fixed by #2800)
  #2803  DataContextInitFaultedTest     ← this commit, ejected 17:32Z

1. SelfUpdateAvailabilityGateTest (#2777)

   NeverRolled_WithTheGateUnavailable_StillHolds waited up to 30s for a HELD
   marker to land on Admin/UpdatePolicy. The service is event-driven, so "the
   hold is not there yet" and "the first check has not run yet" are
   indistinguishable from outside — and on a loaded shard it is the second.
   The issue's own diagnosis, adopted verbatim: make the first poll
   deterministic rather than widen the bound.

   SelfUpdateHostedService now publishes ChecksReported — one emission per
   check that has been evaluated AND reported, an INSTANCE Subject (never
   static), completed in StopAsync so a subscriber on a stopped service is
   released rather than left hanging. The tests await the first emission, then
   assert on the state it produced. Production never subscribes; nothing about
   what a check does or when it runs changes.

   The member is protected internal and Hosting.Monolith.Test is not in
   Memex.Portal.Shared's InternalsVisibleTo list, so the test's own subclass
   forwards it (`Evaluations`) — cheaper than widening the production
   assembly's friend list for one file.

2. DataContextInitFaultedTest.FaultedInit_ReachesTerminalFailedState_AndAnswersFast

   🚨 ONE bound was doing TWO jobs that want OPPOSITE values. The test asserts
   liveness ("is a faulted init answered at all" — wants a generous bound, so
   a timeout means NEVER) and latency (the AndAnswersFast in its name — wants
   a tight one). A single 15s wait conflated them: a loaded runner pushed the
   rejection past it, and the assertion read the resulting TimeoutException as
   "the gate is shut" — the defect's own signature, for a defect that was not
   there. It ejected #2803, a PR touching only Mesh.Contract/Security and an
   operator script, which cannot reach a DataContext.

   Merely widening to 60s would have DELETED the latency half in silence: the
   test would still pass if the answer took 55s, which is precisely what its
   name guards. So they are separated — a generous 60s wait (a timeout now
   means never), plus an elapsed-time assertion of its own against a threshold
   DERIVED FROM THE MECHANISM rather than a feeling of "fast": the hub's own
   30s request budget, which is what a request that FELL THROUGH the faulted
   arm waits out. A slow answer and an absent one now fail with different
   messages, because they are different defects.
   (The split is owed to the reviewing session, who caught that my first pass
   would have quietly dropped the latency half.)

Verified: Memex.Portal.Shared, Hosting.Monolith.Test and Data.Test each
-c Release -warnaserror → 0 Error(s); SelfUpdateAvailabilityGateTest 6/6 and
FaultedInit_ReachesTerminalFailedState 1/1 green.

🚨 Trap worth recording: `--filter "FullyQualifiedName~DataContextInitWatchdogTest"`
matched NOTHING and exited 0 — the file is DataContextInitWatchdogTest.cs but
the CLASS is DataContextInitFaultedTest. A filter that matches nothing is a
silent pass; filter on the method name when the class name is inferred from a
filename.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi
rbuergi deleted the chore/issue-sweep-0830 branch September 13, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment