fix(r2-02): factory-seam guard — hostile ResilienceConfig cannot brick the spawn path (01/S-6 wiring proof)

This commit is contained in:
Joseph Doherty
2026-07-13 09:57:17 -04:00
parent 8d306a1bd2
commit a8af9cc48b
2 changed files with 26 additions and 1 deletions
@@ -5,7 +5,7 @@
{ "id": 1, "subject": "Add ResiliencePolicyRanges constants to Core.Abstractions (single source of truth for parser/builder/AdminUI, derived from Polly.Core 8.6.6 validation)", "status": "completed", "blockedBy": [] }, { "id": 1, "subject": "Add ResiliencePolicyRanges constants to Core.Abstractions (single source of truth for parser/builder/AdminUI, derived from Polly.Core 8.6.6 validation)", "status": "completed", "blockedBy": [] },
{ "id": 2, "subject": "S-6 parser: clamp timeout/retry/breaker overrides with appending diagnostics (timeout<=0 -> tier default; breaker==1 -> 2; caps) + 8 parser tests", "status": "completed", "blockedBy": [0, 1] }, { "id": 2, "subject": "S-6 parser: clamp timeout/retry/breaker overrides with appending diagnostics (timeout<=0 -> tier default; breaker==1 -> 2; caps) + 8 parser tests", "status": "completed", "blockedBy": [0, 1] },
{ "id": 3, "subject": "S-6 builder: belt-and-braces clamp in Build so pipeline construction can never throw ValidationException + Build_NeverThrows_ForHostileDirectOptions (turns task 0 green)", "status": "completed", "blockedBy": [0, 1] }, { "id": 3, "subject": "S-6 builder: belt-and-braces clamp in Build so pipeline construction can never throw ValidationException + Build_NeverThrows_ForHostileDirectOptions (turns task 0 green)", "status": "completed", "blockedBy": [0, 1] },
{ "id": 4, "subject": "S-6 production-wiring guard: DriverCapabilityInvokerFactory.Create with hostile JSON yields a working invoker + logged clamp diagnostic; re-run ResilienceInvokerFactoryRegistrationTests", "status": "pending", "blockedBy": [2, 3] }, { "id": 4, "subject": "S-6 production-wiring guard: DriverCapabilityInvokerFactory.Create with hostile JSON yields a working invoker + logged clamp diagnostic; re-run ResilienceInvokerFactoryRegistrationTests", "status": "completed", "blockedBy": [2, 3] },
{ "id": 5, "subject": "S-6 AdminUI: ResilienceFormModel.Validate() range messages (via ResiliencePolicyRanges through the transitive Core.Abstractions ref) + warning block in DriverResilienceSection", "status": "pending", "blockedBy": [1] }, { "id": 5, "subject": "S-6 AdminUI: ResilienceFormModel.Validate() range messages (via ResiliencePolicyRanges through the transitive Core.Abstractions ref) + warning block in DriverResilienceSection", "status": "pending", "blockedBy": [1] },
{ "id": 6, "subject": "S-8 parser layer: force Write/AlarmAcknowledge retryCount overrides to 0 with diagnostic (spec invariant, honest dead-knob surfacing) + tests + GetTierDefaults doc", "status": "pending", "blockedBy": [2] }, { "id": 6, "subject": "S-8 parser layer: force Write/AlarmAcknowledge retryCount overrides to 0 with diagnostic (spec invariant, honest dead-knob surfacing) + tests + GetTierDefaults doc", "status": "pending", "blockedBy": [2] },
{ "id": 7, "subject": "S-8 dispatch layer: flip HandleWriteAsync to isIdempotent:false — RED the DriverInstanceActorResilienceWiringTests expectation first, then flip; full Runtime suite green", "status": "pending", "blockedBy": [6] }, { "id": 7, "subject": "S-8 dispatch layer: flip HandleWriteAsync to isIdempotent:false — RED the DriverInstanceActorResilienceWiringTests expectation first, then flip; full Runtime suite green", "status": "pending", "blockedBy": [6] },
@@ -136,6 +136,31 @@ public sealed class DriverCapabilityInvokerFactoryTests
logger.Entries.ShouldContain(e => e.Level == LogLevel.Warning && e.Message.Contains("resilience config", StringComparison.OrdinalIgnoreCase)); logger.Entries.ShouldContain(e => e.Level == LogLevel.Warning && e.Message.Contains("resilience config", StringComparison.OrdinalIgnoreCase));
} }
/// <summary>
/// 01/S-6 production-wiring proof: the exact <c>DriverHostActor.SpawnChild</c> path
/// (<c>factory.Create(id, type, hostileJson)</c>) must yield a WORKING invoker whose capability
/// call succeeds, and must log the clamp diagnostic as a warning — a hostile ResilienceConfig can
/// never brick the spawn path.
/// </summary>
[Fact]
public async Task Create_with_out_of_range_config_yields_working_invoker()
{
var logger = new CapturingLogger();
var factory = MakeFactory(new DriverResiliencePipelineBuilder(), logger: logger);
var invoker = factory.Create("i1", "Modbus",
resilienceConfigJson: "{\"capabilityPolicies\":{\"Subscribe\":{\"timeoutSeconds\":0,\"breakerFailureThreshold\":1}}}");
var result = await invoker.ExecuteAsync(
DriverCapability.Subscribe, "host-1", _ => ValueTask.FromResult(99), CancellationToken.None);
result.ShouldBe(99, "a hostile ResilienceConfig must not brick the Subscribe capability call");
logger.Entries.ShouldContain(e =>
e.Level == LogLevel.Warning &&
e.Message.Contains("resilience config", StringComparison.OrdinalIgnoreCase) &&
(e.Message.Contains("timeoutSeconds") || e.Message.Contains("breakerFailureThreshold")));
}
private sealed class CapturingLogger : ILogger private sealed class CapturingLogger : ILogger
{ {
public List<(LogLevel Level, string Message)> Entries { get; } = new(); public List<(LogLevel Level, string Message)> Entries { get; } = new();