Repository navigation
Give every creation validator exactly one bounded verdict (#6391) - #6423
systemorph[bot] wants to merge 1 commit into
Conversation
RunCreationValidatorsObs chained validators with Concat and no per-validator terminal: a silent or never-completing validator held stage 'validators' until the 30 s create deadline, and an empty completion passed. Each validator now runs Defer(Validate).Take(1) under MeshOperationOptions.NestedTimeout; silence and empty completion are Unavailable refusals naming the validator. A copy whose create leg is Unavailable answers with CopyUnavailablePrefix. Refs #6391
Test Results0 tests 0 ✅ 0s ⏱️ Results for commit 311b928. |
There was a problem hiding this comment.
🟡 Changes recommended
Relative per-validator deadlines can still lose to the create deadline, and new user-facing failures bypass localization.
2 open findings
What changed in this PR
Adds bounded, single-verdict handling for creation validators and propagates unavailable create outcomes through copy/move operations.
Changes:
- Bounds validator silence and empty completion as
Unavailable. - Stops live validators after their first verdict.
- Adds integration coverage for validator terminal behaviors.
| File | Description |
|---|---|
src/MeshWeaver.Mesh.Contract/MeshExtensions.cs |
Adds bounded validator verdicts and copy availability propagation. |
test/MeshWeaver.Hosting.Test/CreationValidatorAlwaysReachesAVerdictTest.cs |
Tests silent, empty, live, slow, and refusing validators. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var noVerdict = NodeValidationResult.Unavailable( | ||
| $"CreateNode of '{path}' could not be validated: the creation validator '{name}' gave no " | ||
| + $"verdict within {seconds}s at stage 'validators'. Nothing was written; the create may be retried."); | ||
| var completedEmpty = NodeValidationResult.Unavailable( | ||
| $"CreateNode of '{path}' could not be validated: the creation validator '{name}' completed " |
| var validatorBudget = (hub.ServiceProvider.GetService<MeshOperationOptions>() ?? new MeshOperationOptions()).NestedTimeout; | ||
|
|
||
| return validators | ||
| .Select(v => v.Validate(context)) | ||
| .Select(v => BoundedCreationVerdict(v, context, node.Path, validatorBudget)) | ||
| .Concat() |
|
Closed by maintainer decision: the per-validator timeout in this PR is a band-aid. It turns a stalled validator into a refusal one rung earlier without finding why the validator does not answer, and since #6421 the create deadline already names the stalled validator. The two genuine fixes here ( |

What changed
RunCreationValidatorsObs(src/MeshWeaver.Mesh.Contract/MeshExtensions.cs) runs each creation validator through the newBoundedCreationVerdict:Defer(Validate).Take(1)under its own bound,MeshOperationOptions.NestedTimeout(rung 2 of the budget ladder, strictly inside the create's own deadline, which stays the backstop).Unavailablerefusal that names the validator type and thevalidatorsstage. Nothing is written. A first emission is the verdict, so a validator that answers and stays subscribed no longer holds the stage.HandleCopyNodeRequest: a create leg refusedUnavailableis answered withCopyUnavailablePrefix, so a move whose copy leg is refused that way still reportsUnavailable.Why
Issue #6391: the platform-startup boot recording (
Admin/PlatformVersion/_Activity/startup-380439f5) on memex got no verdict for 30 s, stalled at stagevalidators. The runner chained the validators withConcatand gave none of them a terminal. A validator not armed yet at boot (never answers), or one that emits from a live fold and never completes, held the stage until the create's deadline, which names the stage but not the validator. One that completed empty was read as a pass (fail-open, against the #2742 rule). The stall came before the write stage, so that request did not write the boot's audit row. Which validator stalled on memex cannot be proven from the single log line; after this change the refusal names it.How tested
New
test/MeshWeaver.Hosting.Test/CreationValidatorAlwaysReachesAVerdictTest.cs(rung 1 = 10 s, so each validator's bound is 5 s):Unavailable, names the validator, answered before the create deadline, nothing stored. On main it is answered only by the deadline and the validator is not named.Unavailable, nothing stored. On main the node is created.The existing
LifecycleRequestsAlwaysReachAVerdictTestshould still hold: the create refusal still names the path and thevalidatorsstage, and the move case still reportsUnavailablethrough the copy change. There is no local build here; CI is the build.Scope:
src/MeshWeaver.Mesh.Contractandtestonly.Refs #6391
Bug-Thread: Hosting/Triage/_Thread/bug-systemorph-meshweaver-6391
Opened by Dispatch's control plane on behalf of
Essentials/Agent/bug-triage(auto · Provider/OpenRouterEU/anthropic/claude-opus-5.5), from the bug's own branchbugfix/systemorph-meshweaver-6391. It is never merged by Dispatch: the merge is the governed activitydev.merge, signed by a person once the checks and the internal review are green.