diff --git a/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json b/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json index 55e05832..4c921399 100644 --- a/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json +++ b/archreview/plans/R2-02-resilience-config-hardening-plan.md.tasks.json @@ -16,7 +16,7 @@ { "id": 12, "subject": "U-6 doc sweep (seam xmldocs, OTOPCUA0001 message + analyzer tests, operator docs; leave migrations/WedgeDetector/historical plans) + Options_properties_are_exactly_the_pipeline_wired_set knob-inertness guard", "status": "completed", "blockedBy": [10, 11] }, { "id": 13, "subject": "C-7 RED tests: unknown top-level key / unknown capability / unknown per-policy field survive round-trip; malformed JSON sets ParseFailed and ToJson returns the original text", "status": "completed", "blockedBy": [11] }, { "id": 14, "subject": "C-7 implement: JsonObject-bag FromJson/ToJson (TagConfigJson idiom), ParseFailed + RawStoredJson, blank->null contract preserved, parser-interop test green", "status": "completed", "blockedBy": [13] }, - { "id": 15, "subject": "C-7 razor: ParseFailed warning banner + input lockout + explicit discard button; raw pane shows the stored ResilienceConfig; live-/run on docker-dev :9200 (rebuild BOTH centrals; SQL-mangle + unknown-key survival checks; also drive task 5 warnings and task 9 disabled cells)", "status": "pending", "blockedBy": [14] }, + { "id": 15, "subject": "C-7 razor: ParseFailed warning banner + input lockout + explicit discard button; raw pane shows the stored ResilienceConfig; live-/run on docker-dev :9200 (rebuild BOTH centrals; SQL-mangle + unknown-key survival checks; also drive task 5 warnings and task 9 disabled cells)", "status": "deferred-live", "note": "docker-dev :9200 live-/run; serial live pass", "blockedBy": [14] }, { "id": 16, "subject": "S-7 builder: add options-generation to PipelineKey + GetOrCreate(optionsGeneration=0) + thread through CapabilityInvoker; StaleGeneration_ReCache_IsNotServed_ToNewGeneration", "status": "pending", "blockedBy": [3] }, { "id": 17, "subject": "S-7 factory: Interlocked per-Create generation stamp + Respawn_interleaved_with_old_invoker_call_still_applies_new_options wiring proof; update Invalidate comment (cleanup, not correctness)", "status": "pending", "blockedBy": [16] }, { "id": 18, "subject": "Wrap-up: whole-solution build (0 warnings, analyzer silent) + full dotnet test gate; record the pass + the two report anchor drifts in archreview/plans/STATUS.md", "status": "pending", "blockedBy": [4, 8, 9, 12, 15, 17] } diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/DriverResilienceSection.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/DriverResilienceSection.razor index 4326c428..ca3e8df7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/DriverResilienceSection.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/DriverResilienceSection.razor @@ -6,9 +6,23 @@

Blank fields use the driver type's stability-tier defaults (see docs/v2/driver-stability.md). Set only what you need to override.

+ @if (_m.ParseFailed) + { + + } +
-
+
@@ -21,9 +35,9 @@ var writeShaped = cap is "Write" or "AlarmAcknowledge"; @cap - - - + + + } @@ -46,7 +60,9 @@
Raw JSON (advanced) -
@(_m.ToJson() ?? "(null — all tier defaults)")
+ @* Render the bound STORED value, not the re-serialized model — shows the truth in both the + healthy and the malformed case. *@ +
@(ResilienceConfig ?? "(null — all tier defaults)")
@@ -70,9 +86,22 @@ private async Task EmitAsync() { + // Never emit while the stored config is unparseable — the section is locked, and an unrelated + // keystroke must never silently overwrite what we couldn't read (04/C-7). + if (_m.ParseFailed) return; + var json = _m.ToJson(); _lastParsed = json; ResilienceConfig = json; await ResilienceConfigChanged.InvokeAsync(json); } + + private async Task DiscardAsync() + { + // Explicit, visible destruction — replace the unparseable blob with a blank (tier-default) config. + _m = new ResilienceFormModel(); + _lastParsed = null; + ResilienceConfig = null; + await ResilienceConfigChanged.InvokeAsync(null); + } }