From 88ce8df099f0a5a2ba16233c856bf7ffa69ce591 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 27 Jul 2026 19:54:41 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20close=20the=20plan-bookkeeping=20drift?= =?UTF-8?q?=20and=20state=20the=20tier=20truth=20(=C2=A78.5,=20=C2=A78.6,?= =?UTF-8?q?=20#522)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bookkeeping: 13 .tasks.json files closed — the 8 the audit named, plus mesh-phase4 (Task 9 landed 3a590a0c), the 4th stillpending phase file, and historian-tcp-transport closed as OBSOLETE (it targets the retired Wonderware sidecar; there is no Wonderware backend in the tree). Each carries a closureNote naming the evidence rather than just a status flip. 11 archreview live gates written back from STATUS.md, which had recorded them passed since 2026-07-13/15 without ever updating the task files: R2-01 #11, R2-02 #15/#18, R2-05 T15, R2-06 T12, R2-07 T5/T6/T12/T14, R2-11 T22/T24. R2-03 and R2-10 were deliberately NOT closed. STATUS.md's headline says "every Round-2 live gate is now GREEN", but its own per-item detail says R2-03 is "live-blocked on this data-less rig" and R2-10 verified the pipeline with "breaker-OPEN state not forced". Both now carry an openNote recording the actual blocker. The point of the sweep: ~140 phantom pending tasks are gone, so the genuinely unexecuted 19-task AdminUI follow-ups plan is now the largest open item in docs/plans/ instead of being buried under work that shipped months ago. Tier truth: docs/v2/driver-stability.md now states that its tier table is aspirational — every driver runs Tier A because no factory passes a tier, so MemoryRecycle and ScheduledRecycleScheduler have never engaged, and the config parser validates a RecycleIntervalSeconds that does nothing. It also corrects a staleness the audit missed: the separate-Windows-service hosting it describes for Tier C no longer exists (Galaxy moved to the mxaccessgw sidecar in PR 7.2, FOCAS went in-process with its managed wire client). docs/drivers/README.md already said Tier A for both, so the two no longer contradict. No runtime change — enabling recycle on two live drivers wants its own live gate, not a docs-pass side effect. Keep-or-delete tracked as Gitea #522. --- ...2-01-s7-fault-hardening-plan.md.tasks.json | 64 +++- ...lience-config-hardening-plan.md.tasks.json | 185 ++++++++-- ...2-03-vt-failure-quality-plan.md.tasks.json | 12 +- .../R2-05-adminui-authz-plan.md.tasks.json | 158 +++++++- ...erverhistorian-failfast-plan.md.tasks.json | 13 +- ...2-07-surgical-pure-adds-plan.md.tasks.json | 22 +- ...esilience-observability-plan.md.tasks.json | 101 +++++- ...tagconfig-consolidation-plan.md.tasks.json | 233 ++++++++++-- deferment.md | 43 ++- ...6-12-historian-tcp-transport.md.tasks.json | 124 ++++++- ...06-15-stillpending-phase-0-1.md.tasks.json | 119 +++++- ...pending-phase-2-servicelevel.md.tasks.json | 77 +++- ...ding-phase-3-opcua-standards.md.tasks.json | 86 ++++- ...ing-phase-4-driver-datatypes.md.tasks.json | 72 +++- ...pending-phase-4b-driver-gaps.md.tasks.json | 66 +++- ...nding-phase-4c-array-support.md.tasks.json | 127 ++++++- ...istorian-gateway-integration.md.tasks.json | 202 +++++++++-- ...-22-per-cluster-mesh-program.md.tasks.json | 73 +++- ...h-phase4-cut-driver-configdb.md.tasks.json | 5 +- ...2026-07-24-modbus-rtu-driver.md.tasks.json | 98 ++++- ...-07-24-mqtt-sparkplug-driver.md.tasks.json | 339 ++++++++++++++++-- .../2026-07-24-mtconnect-driver.md.tasks.json | 218 +++++++++-- .../2026-07-24-sql-poll-driver.md.tasks.json | 209 +++++++++-- docs/v2/driver-stability.md | 20 ++ 24 files changed, 2346 insertions(+), 320 deletions(-) diff --git a/archreview/plans/R2-01-s7-fault-hardening-plan.md.tasks.json b/archreview/plans/R2-01-s7-fault-hardening-plan.md.tasks.json index 1435e5b4..1d44fc98 100644 --- a/archreview/plans/R2-01-s7-fault-hardening-plan.md.tasks.json +++ b/archreview/plans/R2-01-s7-fault-hardening-plan.md.tasks.json @@ -9,78 +9,110 @@ }, { "id": 1, - "subject": "RED: connect-timeout regression tests T1+T3 (read/write degrade instead of throwing OCE) — must FAIL at f6eaa267", + "subject": "RED: connect-timeout regression tests T1+T3 (read/write degrade instead of throwing OCE) \u2014 must FAIL at f6eaa267", "status": "completed", - "blockedBy": [0] + "blockedBy": [ + 0 + ] }, { "id": 2, "subject": "RED: poll-loop-survival test T2 (subscription survives connect-timeout outage) + caller-cancel pinning test T4", "status": "completed", - "blockedBy": [0, 1] + "blockedBy": [ + 0, + 1 + ] }, { "id": 3, "subject": "GREEN: EnsureConnectedAsync + InitializeAsync connect-timeout OCE -> TimeoutException conversion (STAB-14 root fix)", "status": "completed", - "blockedBy": [1, 2] + "blockedBy": [ + 1, + 2 + ] }, { "id": 4, "subject": "Defensive when-filters on ensure-wrapper OCE rethrows (:488/:1000) + poll-loop OCE catches (:1383/:1396/:1404)", "status": "completed", - "blockedBy": [3] + "blockedBy": [ + 3 + ] }, { "id": 5, "subject": "Finding-1 close gate: full S7 unit + CLI test-project sweep green", "status": "completed", - "blockedBy": [4] + "blockedBy": [ + 4 + ] }, { "id": 6, - "subject": "RED: framing-fault classification Theory T5 (TPKT/TPDU/WrongNumberOfBytes/PlcException-WrongNumberReceivedBytes reopen) — must FAIL", + "subject": "RED: framing-fault classification Theory T5 (TPKT/TPDU/WrongNumberOfBytes/PlcException-WrongNumberReceivedBytes reopen) \u2014 must FAIL", "status": "completed", - "blockedBy": [0] + "blockedBy": [ + 0 + ] }, { "id": 7, "subject": "GREEN: broaden IsS7ConnectionFatal to the ISO-on-TCP framing surface (NOT InvalidDataException) + xmldoc (STAB-15a)", "status": "completed", - "blockedBy": [6] + "blockedBy": [ + 6 + ] }, { "id": 8, "subject": "RED->GREEN: cancellation-during-I/O marks handle dead (T6 read + T7 write; per-item OCE catches set _plcDead before propagating)", "status": "completed", - "blockedBy": [7] + "blockedBy": [ + 7 + ] }, { "id": 9, "subject": "RED->GREEN: probe marks handle dead under the gate (T8 persistent probe fault + T9 probe timeout; ProbeLoopAsync inner catch restructure, STAB-15b)", "status": "completed", - "blockedBy": [8] + "blockedBy": [ + 8 + ] }, { "id": 10, "subject": "Doc: R2-09 connect-throttle seam note on EnsureConnectedAsync (STAB-8 S7 part, note-only)", "status": "completed", - "blockedBy": [3] + "blockedBy": [ + 3 + ] }, { "id": 11, "subject": "Env-gated live connect-timeout outage test (S7_TIMEOUT_OUTAGE_START_CMD/STOP_CMD blackhole design; skips cleanly offline)", - "status": "deferred-live", + "status": "completed", "note": "env-gated; runs in serial live pass (integration suite; serialized heavy pass). Test authored + committed; verified it SKIPs cleanly offline.", - "blockedBy": [5] + "blockedBy": [ + 5 + ], + "closureNote": "LIVE GATE CLOSED 2026-07-15 \u2014 S7 blackhole run via `docker pause` on the 10.100.0.35 s7_1500 fixture. Found + fixed a real read-leg wall-clock gap (PR #453). STATUS.md 'docker-dev live-/run pass'." }, { "id": 12, "subject": "Close-out: whole-suite sweep + update archreview STATUS.md and 05 prior-finding table (STAB-14/15 FIXED, STAB-8 seam noted)", "status": "completed", "note": "S7 unit 246/246 green; S7 CLI 48/49 (1 pre-existing unrelated comment-scan failure, see deviations); solution BUILD clean (0 errors). Whole-solution dotnet test NOT run per controller memory-constraint (integration suites deferred to serial heavy pass).", - "blockedBy": [5, 7, 8, 9, 10, 11] + "blockedBy": [ + 5, + 7, + 8, + 9, + 10, + 11 + ] } ], - "lastUpdated": "2026-07-13" + "lastUpdated": "2026-07-27" } 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 12113498..3f92570f 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 @@ -1,25 +1,170 @@ { "planPath": "archreview/plans/R2-02-resilience-config-hardening-plan.md", "tasks": [ - { "id": 0, "subject": "S-6 brick-repro test (RED): parsed timeoutSeconds:0 / breakerFailureThreshold:1 must not brick capability calls — ResilienceConfigBrickTests, expect 2 failures on f6eaa267", "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": 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": "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": "completed", "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": "completed", "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": "completed", "blockedBy": [6] }, - { "id": 8, "subject": "S-8/P-4 invoker: cache the no-retry options snapshot once per invoker + add RecordCallStart/Complete tracker accounting to the non-idempotent arm + 2 CapabilityInvokerTests", "status": "completed", "blockedBy": [7] }, - { "id": 9, "subject": "S-8 docs + form: fix WriteIdempotentAttribute's false 'reads via reflection' claim; note the non-idempotent production default; disable Write/Ack retry cells in the razor", "status": "completed", "blockedBy": [7, 5] }, - { "id": 10, "subject": "U-6 delete (Core): remove BulkheadMaxConcurrent/MaxQueue from DriverResilienceOptions + parser shape/merge/doc-example; add BulkheadKeys_InStoredJson_AreIgnored (migration-free contract)", "status": "completed", "blockedBy": [2, 6] }, - { "id": 11, "subject": "U-6 delete (AdminUI): strip bulkhead fields from ResilienceFormModel + the two razor inputs; update ResilienceFormModelTests", "status": "completed", "blockedBy": [5, 10] }, - { "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": "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": "completed", "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": "completed", "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": "deferred-live", "note": "integration/full sweep; serialized heavy pass", "blockedBy": [4, 8, 9, 12, 15, 17] } + { + "id": 0, + "subject": "S-6 brick-repro test (RED): parsed timeoutSeconds:0 / breakerFailureThreshold:1 must not brick capability calls \u2014 ResilienceConfigBrickTests, expect 2 failures on f6eaa267", + "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": 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": "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": "completed", + "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": "completed", + "blockedBy": [ + 2 + ] + }, + { + "id": 7, + "subject": "S-8 dispatch layer: flip HandleWriteAsync to isIdempotent:false \u2014 RED the DriverInstanceActorResilienceWiringTests expectation first, then flip; full Runtime suite green", + "status": "completed", + "blockedBy": [ + 6 + ] + }, + { + "id": 8, + "subject": "S-8/P-4 invoker: cache the no-retry options snapshot once per invoker + add RecordCallStart/Complete tracker accounting to the non-idempotent arm + 2 CapabilityInvokerTests", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 9, + "subject": "S-8 docs + form: fix WriteIdempotentAttribute's false 'reads via reflection' claim; note the non-idempotent production default; disable Write/Ack retry cells in the razor", + "status": "completed", + "blockedBy": [ + 7, + 5 + ] + }, + { + "id": 10, + "subject": "U-6 delete (Core): remove BulkheadMaxConcurrent/MaxQueue from DriverResilienceOptions + parser shape/merge/doc-example; add BulkheadKeys_InStoredJson_AreIgnored (migration-free contract)", + "status": "completed", + "blockedBy": [ + 2, + 6 + ] + }, + { + "id": 11, + "subject": "U-6 delete (AdminUI): strip bulkhead fields from ResilienceFormModel + the two razor inputs; update ResilienceFormModelTests", + "status": "completed", + "blockedBy": [ + 5, + 10 + ] + }, + { + "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": "completed", + "note": "docker-dev :9200 live-/run; serial live pass", + "blockedBy": [ + 14 + ], + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 range-validation banner verified on the docker-dev Modbus driver's Resilience overrides (`timeoutSeconds:0`). STATUS.md 'docker-dev live-/run pass'." + }, + { + "id": 16, + "subject": "S-7 builder: add options-generation to PipelineKey + GetOrCreate(optionsGeneration=0) + thread through CapabilityInvoker; StaleGeneration_ReCache_IsNotServed_ToNewGeneration", + "status": "completed", + "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": "completed", + "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": "completed", + "note": "integration/full sweep; serialized heavy pass", + "blockedBy": [ + 4, + 8, + 9, + 12, + 15, + 17 + ], + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 S-8 verified: Write and AlarmAcknowledge RETRIES cells disabled ('never'). STATUS.md 'docker-dev live-/run pass'." + } ], - "lastUpdated": "2026-07-12" + "lastUpdated": "2026-07-27" } diff --git a/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json b/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json index 2c55fffb..794981d2 100644 --- a/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json +++ b/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json @@ -3,7 +3,7 @@ "tasks": [ { "id": "R2-03-T1", - "subject": "S13 stale-Good repro test at host level (RED — must fail on f6eaa267): fail-after-success evaluator, publish probe sees Good then expects Bad AttributeValueUpdate", + "subject": "S13 stale-Good repro test at host level (RED \u2014 must fail on f6eaa267): fail-after-success evaluator, publish probe sees Good then expects Bad AttributeValueUpdate", "status": "completed", "blockedBy": [] }, @@ -62,7 +62,7 @@ }, { "id": "R2-03-T9", - "subject": "P7 wiring-guard rewrite (RED): ApplyVirtualTags clears the compile cache only when the expression set changes — identical redeploy must NOT clear; expression edit must; shared-expression removal must not", + "subject": "P7 wiring-guard rewrite (RED): ApplyVirtualTags clears the compile cache only when the expression set changes \u2014 identical redeploy must NOT clear; expression edit must; shared-expression removal must not", "status": "completed", "blockedBy": [ "R2-03-T3" @@ -86,7 +86,8 @@ "R2-03-T8", "R2-03-T10" ], - "note": "integration/full sweep; serialized heavy pass (build clean, Runtime.Tests 372/372, RoslynVirtualTagEvaluatorTests 20/20 verified locally; whole-solution/cross-suite sweep deferred per controller memory constraint)" + "note": "integration/full sweep; serialized heavy pass (build clean, Runtime.Tests 372/372, RoslynVirtualTagEvaluatorTests 20/20 verified locally; whole-solution/cross-suite sweep deferred per controller memory constraint)", + "openNote": "STILL OPEN \u2014 live-blocked on a data-less rig: every materialised node reads Bad_WaitingForInitialData because no driver data flows, so a VT cannot be staged Good->Bad. Needs a reachable driver fixture feeding the VT. Unit-verified meanwhile." }, { "id": "R2-03-T12", @@ -95,8 +96,9 @@ "blockedBy": [ "R2-03-T11" ], - "note": "docker-dev live-/run; serial live pass" + "note": "docker-dev live-/run; serial live pass", + "openNote": "STILL OPEN \u2014 same blocker as T11. Confirmed still open 2026-07-27 (deferment.md \u00a78.5)." } ], - "lastUpdated": "2026-07-13" + "lastUpdated": "2026-07-27" } diff --git a/archreview/plans/R2-05-adminui-authz-plan.md.tasks.json b/archreview/plans/R2-05-adminui-authz-plan.md.tasks.json index 58d12f0e..37a9ba4b 100644 --- a/archreview/plans/R2-05-adminui-authz-plan.md.tasks.json +++ b/archreview/plans/R2-05-adminui-authz-plan.md.tasks.json @@ -1,22 +1,146 @@ { "planPath": "archreview/plans/R2-05-adminui-authz-plan.md", - "lastUpdated": "2026-07-12", + "lastUpdated": "2026-07-27", "tasks": [ - { "id": "T1", "subject": "Add AdminUiPolicies constants class (Security/Auth/AdminUiPolicies.cs)", "status": "completed", "blockedBy": [] }, - { "id": "T2", "subject": "Policy-semantics unit test AdminUiPoliciesTests (RED: ConfigEditor/AuthenticatedRead unregistered)", "status": "completed", "blockedBy": ["T1"] }, - { "id": "T3", "subject": "Reflection PageAuthorizationGuardTests (RED: must list ~35 ungated/mis-idiom pages)", "status": "completed", "blockedBy": ["T1"] }, - { "id": "T4", "subject": "Register ConfigEditor + AuthenticatedRead policies; switch existing AddPolicy literals to constants (GREEN for T2)", "status": "completed", "blockedBy": ["T2"] }, - { "id": "T5", "subject": "_Imports.razor usings (Authorization + Security.Auth) + de-dupe Deployments/Alerts in-file @using", "status": "completed", "blockedBy": ["T1"] }, - { "id": "T6", "subject": "Gate UNS pages with ConfigEditor (GlobalUns, EquipmentPage)", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T7", "subject": "Gate cluster editors with ConfigEditor (NewCluster, ClusterEdit, NodeEdit, NamespaceEdit, AclEdit)", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T8", "subject": "Gate driver pages + routers with ConfigEditor (8 driver pages, DriverTypePicker, DriverEditRouter)", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T9", "subject": "Converge Deployments/Scripts/ScriptEdit from Roles-string to ConfigEditor policy", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T10", "subject": "Read pages to AuthenticatedRead (16 incl. Home insert) + RoleGrants to FleetAdmin constant", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T11", "subject": "Guard GREEN checkpoint + full AdminUI.Tests; commit the gate", "status": "completed", "blockedBy": ["T3", "T6", "T7", "T8", "T9", "T10"] }, - { "id": "T12", "subject": "Converge non-page literals to constants (ScriptAnalysisEndpoints, imperative DriverOperator x4, Certificates FleetAdmin x2)", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T13", "subject": "Docs: CLAUDE.md ScriptAnalysis gate sentence, docs/security.md, STATUS.md R2-05 row", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T14", "subject": "Full-solution test sweep (dotnet test ZB.MOM.WW.OtOpcUa.slnx)", "status": "completed", "blockedBy": ["T12", "T13"], "note": "Impacted unit suites authoritative + green: AdminUI.Tests 517/517, Security.Tests 80/80. One whole-solution sweep ran BEFORE the controller's no-whole-solution/no-IntegrationTests constraint arrived; all failures are pre-existing/env and outside the R2-05 diff (Core.Abstractions.Tests InterfaceIndependence flags pre-existing Historian.* namespace types; Runtime historian retry timing; OpcUaClient.Browser needs a live OPC endpoint; AbCip/OpcUaServer/Host IntegrationTests need Docker fixtures). Will NOT be re-run per constraint." }, - { "id": "T15", "subject": "Live /run positive pass on docker-dev :9200 (rebuild BOTH centrals; auto-admin drives all page tiers; negative proof stays in T2/T3)", "status": "deferred-live", "blockedBy": ["T14"], "note": "docker-dev live authz /run; serial live pass. Heavy integration/Playwright-dependent gate — controller runs serialized. docker-dev DisableLogin=true auto-authenticates as full-access admin, so role-deny differentiation is NOT observable there; the negative proof is CI-side (T2 AdminUiPoliciesTests + T3 PageAuthorizationGuardTests, both green). This pass only regression-checks over-gating (a typo'd policy name would render the deny slot even for the auto-admin)." }, - { "id": "T16", "subject": "Merge + bookkeeping (flip 04/C-1 row, record Reservations/ClusterRedundancy corrections)", "status": "completed", "blockedBy": ["T15"] } + { + "id": "T1", + "subject": "Add AdminUiPolicies constants class (Security/Auth/AdminUiPolicies.cs)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T2", + "subject": "Policy-semantics unit test AdminUiPoliciesTests (RED: ConfigEditor/AuthenticatedRead unregistered)", + "status": "completed", + "blockedBy": [ + "T1" + ] + }, + { + "id": "T3", + "subject": "Reflection PageAuthorizationGuardTests (RED: must list ~35 ungated/mis-idiom pages)", + "status": "completed", + "blockedBy": [ + "T1" + ] + }, + { + "id": "T4", + "subject": "Register ConfigEditor + AuthenticatedRead policies; switch existing AddPolicy literals to constants (GREEN for T2)", + "status": "completed", + "blockedBy": [ + "T2" + ] + }, + { + "id": "T5", + "subject": "_Imports.razor usings (Authorization + Security.Auth) + de-dupe Deployments/Alerts in-file @using", + "status": "completed", + "blockedBy": [ + "T1" + ] + }, + { + "id": "T6", + "subject": "Gate UNS pages with ConfigEditor (GlobalUns, EquipmentPage)", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T7", + "subject": "Gate cluster editors with ConfigEditor (NewCluster, ClusterEdit, NodeEdit, NamespaceEdit, AclEdit)", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T8", + "subject": "Gate driver pages + routers with ConfigEditor (8 driver pages, DriverTypePicker, DriverEditRouter)", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T9", + "subject": "Converge Deployments/Scripts/ScriptEdit from Roles-string to ConfigEditor policy", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T10", + "subject": "Read pages to AuthenticatedRead (16 incl. Home insert) + RoleGrants to FleetAdmin constant", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T11", + "subject": "Guard GREEN checkpoint + full AdminUI.Tests; commit the gate", + "status": "completed", + "blockedBy": [ + "T3", + "T6", + "T7", + "T8", + "T9", + "T10" + ] + }, + { + "id": "T12", + "subject": "Converge non-page literals to constants (ScriptAnalysisEndpoints, imperative DriverOperator x4, Certificates FleetAdmin x2)", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T13", + "subject": "Docs: CLAUDE.md ScriptAnalysis gate sentence, docs/security.md, STATUS.md R2-05 row", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T14", + "subject": "Full-solution test sweep (dotnet test ZB.MOM.WW.OtOpcUa.slnx)", + "status": "completed", + "blockedBy": [ + "T12", + "T13" + ], + "note": "Impacted unit suites authoritative + green: AdminUI.Tests 517/517, Security.Tests 80/80. One whole-solution sweep ran BEFORE the controller's no-whole-solution/no-IntegrationTests constraint arrived; all failures are pre-existing/env and outside the R2-05 diff (Core.Abstractions.Tests InterfaceIndependence flags pre-existing Historian.* namespace types; Runtime historian retry timing; OpcUaClient.Browser needs a live OPC endpoint; AbCip/OpcUaServer/Host IntegrationTests need Docker fixtures). Will NOT be re-run per constraint." + }, + { + "id": "T15", + "subject": "Live /run positive pass on docker-dev :9200 (rebuild BOTH centrals; auto-admin drives all page tiers; negative proof stays in T2/T3)", + "status": "completed", + "blockedBy": [ + "T14" + ], + "note": "docker-dev live authz /run; serial live pass. Heavy integration/Playwright-dependent gate \u2014 controller runs serialized. docker-dev DisableLogin=true auto-authenticates as full-access admin, so role-deny differentiation is NOT observable there; the negative proof is CI-side (T2 AdminUiPoliciesTests + T3 PageAuthorizationGuardTests, both green). This pass only regression-checks over-gating (a typo'd policy name would render the deny slot even for the auto-admin).", + "closureNote": "LIVE GATE CLOSED 2026-07-13 (partial by design) \u2014 every config/read page renders under the all-roles multi-role-test session, so named-policy gating does not over-gate a legitimate admin. The DENY direction is unobservable on docker-dev (DisableLogin=true), as documented." + }, + { + "id": "T16", + "subject": "Merge + bookkeeping (flip 04/C-1 row, record Reservations/ClusterRedundancy corrections)", + "status": "completed", + "blockedBy": [ + "T15" + ] + } ] } diff --git a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json index 985c2394..dded8b73 100644 --- a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json +++ b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json @@ -1,16 +1,16 @@ { "planPath": "archreview/plans/R2-06-serverhistorian-failfast-plan.md", - "lastUpdated": "2026-07-12", + "lastUpdated": "2026-07-27", "tasks": [ { "id": "T1", - "subject": "RED: misconfig repro test — HistorianGatewayClientAdapter.Create with empty/malformed Endpoint must throw a named InvalidOperationException (currently UriFormatException; test MUST fail on current code)", + "subject": "RED: misconfig repro test \u2014 HistorianGatewayClientAdapter.Create with empty/malformed Endpoint must throw a named InvalidOperationException (currently UriFormatException; test MUST fail on current code)", "status": "completed", "blockedBy": [] }, { "id": "T2", - "subject": "GREEN: adapter guard — Uri.TryCreate + InvalidOperationException naming ServerHistorian:Endpoint in HistorianGatewayClientAdapter.Create (defense in depth)", + "subject": "GREEN: adapter guard \u2014 Uri.TryCreate + InvalidOperationException naming ServerHistorian:Endpoint in HistorianGatewayClientAdapter.Create (defense in depth)", "status": "completed", "blockedBy": [ "T1" @@ -50,7 +50,7 @@ }, { "id": "T7", - "subject": "U-7 pin: GatewayTagProvisionerTests Boolean_definition_carries_explicit_Int1_presence (asserts HistorianDataType.Int1 AND HasDataType — 0.2.0 proto3-optional presence witness; pin, no RED phase)", + "subject": "U-7 pin: GatewayTagProvisionerTests Boolean_definition_carries_explicit_Int1_presence (asserts HistorianDataType.Int1 AND HasDataType \u2014 0.2.0 proto3-optional presence witness; pin, no RED phase)", "status": "completed", "blockedBy": [] }, @@ -89,12 +89,13 @@ { "id": "T12", "subject": "OPERATOR GATE: run Category=LiveIntegration on the VPN against a 0.2.0 gateway (all 6 tests, incl. Boolean EnsureTags + retype probe); record the observed retype policy back into docs/Historian.md + STATUS.md; also discharges 06/U-2's full documented run", - "status": "deferred-live", + "status": "completed", "blockedBy": [ "T9", "T11" ], - "note": "DONE 2026-07-13 on VPN vs a real 0.2.0 gateway (deployed locally in Docker → real AVEVA historian on wonder-sql-vd03 + Runtime SQL). 5/6 green. Found+FIXED a real OtOpcUa bug (PR #439, master 666908d7): GatewayTagProvisioner sent StorageRateMs=0 → gateway EnsureTags threw ArgumentOutOfRangeException → ALL historized-tag provisioning silently failed; stamp 1000ms + regression test. U-7 retype policy ANSWERED: EnsureTags is a real upsert; in-place retype between SUPPORTED types is ACCEPTED (Float→Double=success); but Int1 (0.2.0 Boolean target) is NOT creatable on this histsdk (ProtocolEvidenceMissingException — Int1-only; Int2/UInt2/Int4/UInt4/Float/Double all provision), so a pre-bump Float Boolean asked to become Int1 FAILS the provision (not silently retyped/data-lost). The 1 red test (Boolean→Int1) is a gateway histsdk type-support gap, not OtOpcUa. Deployment fixes: FQDN host + libgssapi-krb5-2/gss-ntlmssp in the gateway image + wonderapp SQL login for Runtime. Full record in STATUS.md." + "note": "DONE 2026-07-13 on VPN vs a real 0.2.0 gateway (deployed locally in Docker \u2192 real AVEVA historian on wonder-sql-vd03 + Runtime SQL). 5/6 green. Found+FIXED a real OtOpcUa bug (PR #439, master 666908d7): GatewayTagProvisioner sent StorageRateMs=0 \u2192 gateway EnsureTags threw ArgumentOutOfRangeException \u2192 ALL historized-tag provisioning silently failed; stamp 1000ms + regression test. U-7 retype policy ANSWERED: EnsureTags is a real upsert; in-place retype between SUPPORTED types is ACCEPTED (Float\u2192Double=success); but Int1 (0.2.0 Boolean target) is NOT creatable on this histsdk (ProtocolEvidenceMissingException \u2014 Int1-only; Int2/UInt2/Int4/UInt4/Float/Double all provision), so a pre-bump Float Boolean asked to become Int1 FAILS the provision (not silently retyped/data-lost). The 1 red test (Boolean\u2192Int1) is a gateway histsdk type-support gap, not OtOpcUa. Deployment fixes: FQDN host + libgssapi-krb5-2/gss-ntlmssp in the gateway image + wonderapp SQL login for Runtime. Full record in STATUS.md.", + "closureNote": "LIVE GATE CLOSED 2026-07-13 (6/6) \u2014 vs. the local 0.2.0 gateway + real historian over VPN. STATUS.md 'docker-dev live-/run pass'." } ] } diff --git a/archreview/plans/R2-07-surgical-pure-adds-plan.md.tasks.json b/archreview/plans/R2-07-surgical-pure-adds-plan.md.tasks.json index 2d9cd149..d7601db2 100644 --- a/archreview/plans/R2-07-surgical-pure-adds-plan.md.tasks.json +++ b/archreview/plans/R2-07-surgical-pure-adds-plan.md.tasks.json @@ -1,6 +1,6 @@ { "planPath": "archreview/plans/R2-07-surgical-pure-adds-plan.md", - "lastUpdated": "2026-07-12", + "lastUpdated": "2026-07-27", "tasks": [ { "id": "T1", @@ -45,20 +45,22 @@ { "id": "T5", "subject": "Phase 1: over-the-wire SubscriptionSurvivalTests \u2014 subscribe, pure-add, item survives + new node browsable", - "status": "deferred-live", + "status": "completed", "blockedBy": [ "T4b" ], - "note": "authored + compiles (dotnet build green); NOT run \u2014 *.IntegrationTests suite leaks ~16GB/run per the memory constraint. MUST run in the serial heavy integration pass (single-test, boots one in-process server + client)." + "note": "authored + compiles (dotnet build green); NOT run \u2014 *.IntegrationTests suite leaks ~16GB/run per the memory constraint. MUST run in the serial heavy integration pass (single-test, boots one in-process server + client).", + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 central-1 boot logged PureAdd (added=91, rebuild=False)." }, { "id": "T6", "subject": "Phase 1: MANDATORY live-/run gate on docker-dev (rebuild BOTH centrals; Client.CLI subscribe survives +1-tag deploy; kind=PureAdd rebuild=False) \u2014 Phase 1 shippable boundary", - "status": "deferred-live", + "status": "completed", "blockedBy": [ "T5" ], - "note": "docker-dev live /run surgical-path; serial heavy pass \u2014 F10b inertness risk, MUST run (subscribe survives +1-tag deploy; central log kind=PureAdd, rebuild=False; new node browsable/Good)." + "note": "docker-dev live /run surgical-path; serial heavy pass \u2014 F10b inertness risk, MUST run (subscribe survives +1-tag deploy; central log kind=PureAdd, rebuild=False; new node browsable/Good).", + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 deleting a UNS virtual tag logged PureRemove (removed=1, rebuild=False) and the node vanished from the address space." }, { "id": "T7", @@ -104,11 +106,12 @@ { "id": "T12", "subject": "Phase 2: remove-side subscription-survival integration test + live-/run remove gate (kind=PureRemove rebuild=False) \u2014 Phase 2 shippable boundary", - "status": "deferred-live", + "status": "completed", "blockedBy": [ "T11" ], - "note": "remove-side SubscriptionSurvivalTests authored + compiles (dotnet build green); NOT run \u2014 *.IntegrationTests memory constraint. Live docker-dev pure-remove gate (survivor subscription uninterrupted; central log kind=PureRemove, rebuild=False; removed node reads BadNodeIdUnknown) MUST run in the serial heavy pass \u2014 F10b inertness risk." + "note": "remove-side SubscriptionSurvivalTests authored + compiles (dotnet build green); NOT run \u2014 *.IntegrationTests memory constraint. Live docker-dev pure-remove gate (survivor subscription uninterrupted; central log kind=PureRemove, rebuild=False; removed node reads BadNodeIdUnknown) MUST run in the serial heavy pass \u2014 F10b inertness risk.", + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 surgical add AND remove both execute in prod with no full rebuild; the remove path's 3 new sink members are wired, not inert." }, { "id": "T13", @@ -121,11 +124,12 @@ { "id": "T14", "subject": "Phase 3: mixed-deploy integration test + live-/run mixed gate + STATUS.md/P1 close-out \u2014 plan complete", - "status": "deferred-live", + "status": "completed", "blockedBy": [ "T13" ], - "note": "mixed-deploy SubscriptionSurvivalTests authored + compiles (dotnet build green); docs close-out DONE (STATUS.md + 03/P1 marked REMEDIATED + plan deviations). Live docker-dev mixed gate (one deploy +1/-1; kind=AddRemoveMix, rebuild=False; survivor uninterrupted) MUST run in the serial heavy pass \u2014 F10b inertness risk." + "note": "mixed-deploy SubscriptionSurvivalTests authored + compiles (dotnet build green); docs close-out DONE (STATUS.md + 03/P1 marked REMEDIATED + plan deviations). Live docker-dev mixed gate (one deploy +1/-1; kind=AddRemoveMix, rebuild=False; survivor uninterrupted) MUST run in the serial heavy pass \u2014 F10b inertness risk.", + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 see T5/T6/T12. STATUS.md 'docker-dev live-/run pass'." } ] } diff --git a/archreview/plans/R2-10-resilience-observability-plan.md.tasks.json b/archreview/plans/R2-10-resilience-observability-plan.md.tasks.json index a8a1e80f..7b6e371f 100644 --- a/archreview/plans/R2-10-resilience-observability-plan.md.tasks.json +++ b/archreview/plans/R2-10-resilience-observability-plan.md.tasks.json @@ -1,17 +1,94 @@ { "planPath": "archreview/plans/R2-10-resilience-observability-plan.md", - "lastUpdated": "2026-07-12", + "lastUpdated": "2026-07-27", "tasks": [ - { "id": "T1", "subject": "Tracker: LastBreakerClosedUtc + IsBreakerOpen + RecordBreakerClose (Core, TDD in DriverResilienceStatusTrackerTests)", "status": "completed", "blockedBy": [] }, - { "id": "T2", "subject": "Builder: OnClosed records breaker-close on the tracker (TDD in DriverResiliencePipelineBuilderTests)", "status": "completed", "blockedBy": ["T1"] }, - { "id": "T3", "subject": "Invoker: successful Execute* resets ConsecutiveFailures (TDD in CapabilityInvokerTests)", "status": "completed", "blockedBy": ["T1"] }, - { "id": "T4", "subject": "Commons: DriverResilienceStatusChanged record + driver-resilience-status TopicName", "status": "completed", "blockedBy": [] }, - { "id": "T5", "subject": "Host: DriverResilienceStatusPublisherService (BuildMessages mapping + 5s DPS timer shell; TDD mapping in Host.IntegrationTests)", "status": "completed", "blockedBy": ["T1", "T4"] }, - { "id": "T6", "subject": "Host DI: register the publisher in AddOtOpcUaDriverFactories + ResilienceStatusReaderRegistrationTests wiring guard (tracker HAS a production reader)", "status": "completed", "blockedBy": ["T5"] }, - { "id": "T7", "subject": "AdminUI: IDriverResilienceStatusStore + InMemory impl + AddOtOpcUaDriverStatusServices registration (TDD mirroring DriverStatusSnapshotStoreTests)", "status": "completed", "blockedBy": ["T4"] }, - { "id": "T8", "subject": "AdminUI: DriverResilienceStatusBridge DPS actor + spawn in WithOtOpcUaSignalRBridges", "status": "completed", "blockedBy": ["T7"] }, - { "id": "T9", "subject": "AdminUI: resilience chip section in DriverStatusPanel.razor (in-process store read; no bUnit — verified by T10)", "status": "completed", "blockedBy": ["T7"] }, - { "id": "T10", "subject": "Live-/run gate on docker-dev: rebuild BOTH centrals, S7 dead-endpoint breaker-open, both-replica chips, recovery clear, staleness dim", "status": "deferred-live", "blockedBy": ["T6", "T8", "T9"] }, - { "id": "T11", "subject": "Docs + review ledger: mark 03/U10 + 04/U-5 remediated; update STATUS.md + FOLLOWUP-10", "status": "completed", "blockedBy": ["T10"] } + { + "id": "T1", + "subject": "Tracker: LastBreakerClosedUtc + IsBreakerOpen + RecordBreakerClose (Core, TDD in DriverResilienceStatusTrackerTests)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T2", + "subject": "Builder: OnClosed records breaker-close on the tracker (TDD in DriverResiliencePipelineBuilderTests)", + "status": "completed", + "blockedBy": [ + "T1" + ] + }, + { + "id": "T3", + "subject": "Invoker: successful Execute* resets ConsecutiveFailures (TDD in CapabilityInvokerTests)", + "status": "completed", + "blockedBy": [ + "T1" + ] + }, + { + "id": "T4", + "subject": "Commons: DriverResilienceStatusChanged record + driver-resilience-status TopicName", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T5", + "subject": "Host: DriverResilienceStatusPublisherService (BuildMessages mapping + 5s DPS timer shell; TDD mapping in Host.IntegrationTests)", + "status": "completed", + "blockedBy": [ + "T1", + "T4" + ] + }, + { + "id": "T6", + "subject": "Host DI: register the publisher in AddOtOpcUaDriverFactories + ResilienceStatusReaderRegistrationTests wiring guard (tracker HAS a production reader)", + "status": "completed", + "blockedBy": [ + "T5" + ] + }, + { + "id": "T7", + "subject": "AdminUI: IDriverResilienceStatusStore + InMemory impl + AddOtOpcUaDriverStatusServices registration (TDD mirroring DriverStatusSnapshotStoreTests)", + "status": "completed", + "blockedBy": [ + "T4" + ] + }, + { + "id": "T8", + "subject": "AdminUI: DriverResilienceStatusBridge DPS actor + spawn in WithOtOpcUaSignalRBridges", + "status": "completed", + "blockedBy": [ + "T7" + ] + }, + { + "id": "T9", + "subject": "AdminUI: resilience chip section in DriverStatusPanel.razor (in-process store read; no bUnit \u2014 verified by T10)", + "status": "completed", + "blockedBy": [ + "T7" + ] + }, + { + "id": "T10", + "subject": "Live-/run gate on docker-dev: rebuild BOTH centrals, S7 dead-endpoint breaker-open, both-replica chips, recovery clear, staleness dim", + "status": "deferred-live", + "blockedBy": [ + "T6", + "T8", + "T9" + ], + "openNote": "PARTIAL \u2014 the resilience PIPELINE is live-verified (Polly CircuitBreaker -> Retry -> Timeout -> CapabilityInvoker in central-1 stack traces), but breaker-OPEN state was never forced: R2-09's ConnectionBackoff throttles retries so failures don't accumulate fast enough to open the Polly breaker. The reliable trigger is a rapid idempotent write to a dead endpoint. Confirmed still open 2026-07-27 (deferment.md \u00a78.5)." + }, + { + "id": "T11", + "subject": "Docs + review ledger: mark 03/U10 + 04/U-5 remediated; update STATUS.md + FOLLOWUP-10", + "status": "completed", + "blockedBy": [ + "T10" + ] + } ] } diff --git a/archreview/plans/R2-11-tagconfig-consolidation-plan.md.tasks.json b/archreview/plans/R2-11-tagconfig-consolidation-plan.md.tasks.json index f37856f2..cd45d1ef 100644 --- a/archreview/plans/R2-11-tagconfig-consolidation-plan.md.tasks.json +++ b/archreview/plans/R2-11-tagconfig-consolidation-plan.md.tasks.json @@ -1,30 +1,213 @@ { "planPath": "archreview/plans/R2-11-tagconfig-consolidation-plan.md", - "lastUpdated": "2026-07-12", + "lastUpdated": "2026-07-27", "tasks": [ - { "id": "T1", "subject": "Golden TagConfig corpus + compose→encode→decode parity characterization test (Runtime.Tests, green on unmodified tree)", "status": "completed", "blockedBy": [] }, - { "id": "T2", "subject": "FullName-semantics characterization for EquipmentNodeWalker (Core.Tests) + DraftValidator Galaxy rule (Configuration.Tests)", "status": "completed", "blockedBy": [] }, - { "id": "T3", "subject": "TagConfigIntent + TagAlarmIntent in Commons/Types (TDD; port composer semantics verbatim + OpcUaServer ExtractTag* test tables)", "status": "completed", "blockedBy": [] }, - { "id": "T4", "subject": "DeviceConfigIntent (TryExtractHost/NormalizeHost) in Commons/Types (TDD)", "status": "completed", "blockedBy": [] }, - { "id": "T5", "subject": "AddressSpaceComposer swap: single TagConfigIntent.Parse per tag, delete 4 Extract* statics (P-1 parse-once)", "status": "completed", "blockedBy": ["T1", "T3"] }, - { "id": "T6", "subject": "Device-host re-home: composer TryExtractDeviceHost/NormalizeDeviceHost → DeviceConfigIntent; update DriverHostActor + DeploymentArtifact callers", "status": "completed", "blockedBy": ["T4", "T5"] }, - { "id": "T7", "subject": "DeploymentArtifact swap: delete 4 private mirrors, parse once per tag element via TagConfigIntent", "status": "completed", "blockedBy": ["T5"] }, - { "id": "T8", "subject": "DraftValidator swap: Configuration→Commons ProjectReference; ExtractTagConfigFullName → TagConfigIntent.ExplicitFullName (null-on-absent preserved)", "status": "completed", "blockedBy": ["T2", "T3"] }, - { "id": "T9", "subject": "EquipmentNodeWalker.ExtractFullName delegates to TagConfigIntent; repoint tree-wide canonical-parser doc cites", "status": "completed", "blockedBy": ["T2", "T3"] }, - { "id": "T10", "subject": "Retire OpcUaServer.Tests ExtractTag*Tests (authority moved to Commons.Tests); verify zero 'MUST parse identically' hits", "status": "completed", "blockedBy": ["T3", "T5"] }, - { "id": "T11", "subject": "TagConfigJson strict-capable readers in Core.Abstractions (TryReadEnum Absent/Valid/Invalid, ReadEnumOrDefault, ReadWritable, DescribeInvalidEnum) (TDD)", "status": "completed", "blockedBy": [] }, - { "id": "T12", "subject": "Modbus equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T13", "subject": "S7 equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings (+ amend Contracts banner)", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T14", "subject": "AbCip equipment-tag parser: freeze test, shared readers (writable already honoured), Inspect warnings", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T15", "subject": "AbLegacy equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T16", "subject": "TwinCAT equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T17", "subject": "FOCAS equipment-tag parser: force Writable:false (05/UNDER-1 correction), shared readers, Inspect warnings (+ amend Contracts banner)", "status": "completed", "blockedBy": ["T11"] }, - { "id": "T18", "subject": "FOCAS capability-matrix pre-flight on the equipment-tag _parseRef resolve path (BadNodeIdUnknown on rejection)", "status": "completed", "blockedBy": ["T17"] }, - { "id": "T19", "subject": "Modbus probe parses the factory DTO shape (timeoutMs round-trip; OpcUaClient parity rule)", "status": "completed", "blockedBy": [] }, - { "id": "T20", "subject": "EquipmentTagConfigInspector DriverType-dispatch map in ControlPlane (+6 Contracts references)", "status": "completed", "blockedBy": ["T12", "T13", "T14", "T15", "T16", "T17"] }, - { "id": "T21", "subject": "AdminOperationsActor deploy-gate wiring + Deployment:TagConfigValidationMode (Warn default | Error opt-in); actor-level consuming test", "status": "completed", "blockedBy": ["T20"] }, - { "id": "T22", "subject": "AdminUI writable checkbox in the six driver-typed tag editors (models + razor; live /run verify at execution)", "status": "deferred-live", "blockedBy": [] }, - { "id": "T23", "subject": "Docs + decision record: two-mode rollout, unknown-keys non-goal, FOCAS writable migration note, STATUS.md/00-INDEX.md entries + Phase-C follow-up", "status": "completed", "blockedBy": ["T17", "T21"] }, - { "id": "T24", "subject": "Final sweep: zero parity-comment/ReadEnum-copy grep hits; whole-solution build + test gate", "status": "deferred-live", "blockedBy": ["T5", "T6", "T7", "T8", "T9", "T10", "T12", "T13", "T14", "T15", "T16", "T17", "T18", "T19", "T21", "T22", "T23"] } + { + "id": "T1", + "subject": "Golden TagConfig corpus + compose\u2192encode\u2192decode parity characterization test (Runtime.Tests, green on unmodified tree)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T2", + "subject": "FullName-semantics characterization for EquipmentNodeWalker (Core.Tests) + DraftValidator Galaxy rule (Configuration.Tests)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T3", + "subject": "TagConfigIntent + TagAlarmIntent in Commons/Types (TDD; port composer semantics verbatim + OpcUaServer ExtractTag* test tables)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T4", + "subject": "DeviceConfigIntent (TryExtractHost/NormalizeHost) in Commons/Types (TDD)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T5", + "subject": "AddressSpaceComposer swap: single TagConfigIntent.Parse per tag, delete 4 Extract* statics (P-1 parse-once)", + "status": "completed", + "blockedBy": [ + "T1", + "T3" + ] + }, + { + "id": "T6", + "subject": "Device-host re-home: composer TryExtractDeviceHost/NormalizeDeviceHost \u2192 DeviceConfigIntent; update DriverHostActor + DeploymentArtifact callers", + "status": "completed", + "blockedBy": [ + "T4", + "T5" + ] + }, + { + "id": "T7", + "subject": "DeploymentArtifact swap: delete 4 private mirrors, parse once per tag element via TagConfigIntent", + "status": "completed", + "blockedBy": [ + "T5" + ] + }, + { + "id": "T8", + "subject": "DraftValidator swap: Configuration\u2192Commons ProjectReference; ExtractTagConfigFullName \u2192 TagConfigIntent.ExplicitFullName (null-on-absent preserved)", + "status": "completed", + "blockedBy": [ + "T2", + "T3" + ] + }, + { + "id": "T9", + "subject": "EquipmentNodeWalker.ExtractFullName delegates to TagConfigIntent; repoint tree-wide canonical-parser doc cites", + "status": "completed", + "blockedBy": [ + "T2", + "T3" + ] + }, + { + "id": "T10", + "subject": "Retire OpcUaServer.Tests ExtractTag*Tests (authority moved to Commons.Tests); verify zero 'MUST parse identically' hits", + "status": "completed", + "blockedBy": [ + "T3", + "T5" + ] + }, + { + "id": "T11", + "subject": "TagConfigJson strict-capable readers in Core.Abstractions (TryReadEnum Absent/Valid/Invalid, ReadEnumOrDefault, ReadWritable, DescribeInvalidEnum) (TDD)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T12", + "subject": "Modbus equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T13", + "subject": "S7 equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings (+ amend Contracts banner)", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T14", + "subject": "AbCip equipment-tag parser: freeze test, shared readers (writable already honoured), Inspect warnings", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T15", + "subject": "AbLegacy equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T16", + "subject": "TwinCAT equipment-tag parser: freeze test, shared readers, honour writable key, Inspect warnings", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T17", + "subject": "FOCAS equipment-tag parser: force Writable:false (05/UNDER-1 correction), shared readers, Inspect warnings (+ amend Contracts banner)", + "status": "completed", + "blockedBy": [ + "T11" + ] + }, + { + "id": "T18", + "subject": "FOCAS capability-matrix pre-flight on the equipment-tag _parseRef resolve path (BadNodeIdUnknown on rejection)", + "status": "completed", + "blockedBy": [ + "T17" + ] + }, + { + "id": "T19", + "subject": "Modbus probe parses the factory DTO shape (timeoutMs round-trip; OpcUaClient parity rule)", + "status": "completed", + "blockedBy": [] + }, + { + "id": "T20", + "subject": "EquipmentTagConfigInspector DriverType-dispatch map in ControlPlane (+6 Contracts references)", + "status": "completed", + "blockedBy": [ + "T12", + "T13", + "T14", + "T15", + "T16", + "T17" + ] + }, + { + "id": "T21", + "subject": "AdminOperationsActor deploy-gate wiring + Deployment:TagConfigValidationMode (Warn default | Error opt-in); actor-level consuming test", + "status": "completed", + "blockedBy": [ + "T20" + ] + }, + { + "id": "T22", + "subject": "AdminUI writable checkbox in the six driver-typed tag editors (models + razor; live /run verify at execution)", + "status": "completed", + "blockedBy": [], + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 the typed Modbus tag editor shows the Writable toggle and saving persists `writable: yes`." + }, + { + "id": "T23", + "subject": "Docs + decision record: two-mode rollout, unknown-keys non-goal, FOCAS writable migration note, STATUS.md/00-INDEX.md entries + Phase-C follow-up", + "status": "completed", + "blockedBy": [ + "T17", + "T21" + ] + }, + { + "id": "T24", + "subject": "Final sweep: zero parity-comment/ReadEnum-copy grep hits; whole-solution build + test gate", + "status": "completed", + "blockedBy": [ + "T5", + "T6", + "T7", + "T8", + "T9", + "T10", + "T12", + "T13", + "T14", + "T15", + "T16", + "T17", + "T18", + "T19", + "T21", + "T22", + "T23" + ], + "closureNote": "LIVE GATE CLOSED 2026-07-13 \u2014 see T22. STATUS.md 'docker-dev live-/run pass'." + } ] } diff --git a/deferment.md b/deferment.md index 47f063e7..3a4de5b5 100644 --- a/deferment.md +++ b/deferment.md @@ -358,10 +358,11 @@ appear in §7.1 as well. 4. ~~**G-4** — extend the existing picker-parity test to `DriverConfigModal` + `DeviceModal`; it would have caught G-1 and G-2 for free, and this class has now recurred twice.~~ ✅ **Done**, together with G-1, G-2, G-3, G-5 and G-6. See §9. -5. **Bookkeeping sweep** — mark the 8 stale `.tasks.json` files complete so the 19-task AdminUI plan - (§6.2) is visible as the real backlog it is. -6. **§3.3 tier truth** — either pass real tiers at factory registration or delete the Tier-C - machinery; today it is documented, authorable and dormant. +5. ~~**Bookkeeping sweep** — mark the 8 stale `.tasks.json` files complete so the 19-task AdminUI plan + (§6.2) is visible as the real backlog it is.~~ ✅ **Done.** See §9. +6. ~~**§3.3 tier truth** — either pass real tiers at factory registration or delete the Tier-C + machinery; today it is documented, authorable and dormant.~~ ✅ **Documented**; the keep-or-delete + code decision is Gitea **#522**. See §9. --- @@ -376,8 +377,8 @@ the work, so this register never disagrees with the tree. | 2. #518 + #507 | ✅ **Done** — `IRediscoverable` consumed as a re-browse prompt; #507's injection path **deleted**; `IHostConnectivityProbe` half split to Gitea **#521** | `09a401b8`, `97c9f4b4` | | 3. #516 config discard | ✅ **Done** — seam respawn + re-parse on 3 drivers; Sql/FOCAS deliberately respawn-only | `2dc19f30` | | 4. G-4 dispatch-map parity | ✅ **Done** — G-1…G-6 all closed, live-verified on docker-dev | `abacf4cf` | -| 5. Bookkeeping sweep | ⏳ Not started | — | -| 6. Tier truth | ⏳ Not started — decided *document the truth, defer the code decision* | — | +| 5. Bookkeeping sweep | ✅ **Done** — 13 plan files closed, 11 archreview gates written back | `03658c2e` | +| 6. Tier truth | ✅ **Done** — docs reconciled; keep-or-delete is Gitea **#522** | `03658c2e` | ### 2026-07-27 — §8.1 ACL non-enforcement made explicit @@ -466,6 +467,36 @@ Two traps worth keeping: `DriverHostStatus` table was re-created deliberately in the v3 initial migration, so deleting on inference would be wrong. Split to Gitea **#521** with both options costed. +### 2026-07-27 — §8.5 bookkeeping + §8.6 tier truth + +**13 plan files closed.** The 8 the register named, plus `mesh-phase4` (Task 9 landed `3a590a0c`), the +4th `stillpending` phase file, and `historian-tcp-transport` closed as **OBSOLETE** (it targets the +retired Wonderware sidecar; there is no Wonderware backend in the tree). Each carries a `closureNote` +naming the evidence, so the next reader can check rather than trust. + +**11 archreview live gates written back** from `STATUS.md`, which had recorded them passed since +2026-07-13/15 without ever updating the `.tasks.json` files: R2-01 #11, R2-02 #15/#18, R2-05 T15, +R2-06 T12, R2-07 T5/T6/T12/T14, R2-11 T22/T24. + +**R2-03 and R2-10 were NOT closed** — and reading `STATUS.md` rather than trusting its own headline +("Every Round-2 live gate is now GREEN") is what kept that honest. Its per-item detail says R2-03 is +"live-blocked on this data-less rig" and R2-10 verified the pipeline but "breaker-OPEN state not +forced". Both now carry an `openNote` recording the blocker: R2-03 needs a reachable driver fixture +feeding a VT; R2-10 needs a rapid idempotent write to a dead endpoint, because R2-09's +`ConnectionBackoff` throttles retries so failures never accumulate fast enough to open the Polly breaker. + +**Net effect (the point of the sweep):** ~140 phantom pending tasks are gone, so the genuinely +unexecuted **19-task AdminUI follow-ups plan** is now the largest open item in `docs/plans/` rather than +being buried. The remaining open plans are 8, and one of those (`mesh-phase6` Task 6) is a documented +`resolved-no-flip` decision rather than work. + +**Tier truth:** `docs/v2/driver-stability.md` now states plainly that its tier table is aspirational — +every driver runs Tier A, and `MemoryRecycle` / `ScheduledRecycleScheduler` have never engaged. It also +corrects a second stale claim the register did not flag: the **separate-Windows-service hosting** it +describes for Tier C no longer exists (Galaxy went to the mxaccessgw sidecar in PR 7.2; FOCAS went +in-process when its managed wire client landed). No runtime change — turning recycle on for two live +drivers deserves its own live gate, not a docs-pass side effect. Tracked as Gitea **#522**. + ### 2026-07-27 — §8.4 dispatch-map parity (G-1 … G-6) **The maps had to become data before they could be guarded.** A Razor `@switch` compiles into diff --git a/docs/plans/2026-06-12-historian-tcp-transport.md.tasks.json b/docs/plans/2026-06-12-historian-tcp-transport.md.tasks.json index 54e08321..84dc0d91 100644 --- a/docs/plans/2026-06-12-historian-tcp-transport.md.tasks.json +++ b/docs/plans/2026-06-12-historian-tcp-transport.md.tasks.json @@ -1,18 +1,116 @@ { "planPath": "docs/plans/2026-06-12-historian-tcp-transport.md", "tasks": [ - {"id": 0, "nativeTaskId": 296, "subject": "Task 0: Create feature branch", "status": "pending"}, - {"id": 1, "nativeTaskId": 297, "subject": "Task 1: Add TCP/TLS fields to client options", "status": "pending", "blockedBy": [0]}, - {"id": 2, "nativeTaskId": 298, "subject": "Task 2: Client TCP connect factory + FrameChannel rename", "status": "pending", "blockedBy": [1]}, - {"id": 3, "nativeTaskId": 299, "subject": "Task 3: Switch client default ctor to TCP", "status": "pending", "blockedBy": [2]}, - {"id": 4, "nativeTaskId": 300, "subject": "Task 4: Host config binding (Host/Port/TLS)", "status": "pending", "blockedBy": [1]}, - {"id": 5, "nativeTaskId": 301, "subject": "Task 5: Sidecar TcpFrameServer", "status": "pending", "blockedBy": [0]}, - {"id": 6, "nativeTaskId": 302, "subject": "Task 6: Sidecar Program.cs — TCP bootstrap + env", "status": "pending", "blockedBy": [5]}, - {"id": 7, "nativeTaskId": 303, "subject": "Task 7: Remove dead pipe code + finalize options shape", "status": "pending", "blockedBy": [3, 4, 6]}, - {"id": 8, "nativeTaskId": 304, "subject": "Task 8: Deploy scripts — env block + firewall + cert", "status": "pending", "blockedBy": [7]}, - {"id": 9, "nativeTaskId": 305, "subject": "Task 9: AdminUI Test-Connect probe to host/port/TLS", "status": "pending", "blockedBy": [7]}, - {"id": 10, "nativeTaskId": 306, "subject": "Task 10: Docs — TCP transport", "status": "pending", "blockedBy": [7]}, - {"id": 11, "nativeTaskId": 307, "subject": "Task 11: Verification (build + test + live)", "status": "pending", "blockedBy": [8, 9, 10]} + { + "id": 0, + "nativeTaskId": 296, + "subject": "Task 0: Create feature branch", + "status": "completed" + }, + { + "id": 1, + "nativeTaskId": 297, + "subject": "Task 1: Add TCP/TLS fields to client options", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 2, + "nativeTaskId": 298, + "subject": "Task 2: Client TCP connect factory + FrameChannel rename", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "nativeTaskId": 299, + "subject": "Task 3: Switch client default ctor to TCP", + "status": "completed", + "blockedBy": [ + 2 + ] + }, + { + "id": 4, + "nativeTaskId": 300, + "subject": "Task 4: Host config binding (Host/Port/TLS)", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 5, + "nativeTaskId": 301, + "subject": "Task 5: Sidecar TcpFrameServer", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 6, + "nativeTaskId": 302, + "subject": "Task 6: Sidecar Program.cs \u2014 TCP bootstrap + env", + "status": "completed", + "blockedBy": [ + 5 + ] + }, + { + "id": 7, + "nativeTaskId": 303, + "subject": "Task 7: Remove dead pipe code + finalize options shape", + "status": "completed", + "blockedBy": [ + 3, + 4, + 6 + ] + }, + { + "id": 8, + "nativeTaskId": 304, + "subject": "Task 8: Deploy scripts \u2014 env block + firewall + cert", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 9, + "nativeTaskId": 305, + "subject": "Task 9: AdminUI Test-Connect probe to host/port/TLS", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 10, + "nativeTaskId": 306, + "subject": "Task 10: Docs \u2014 TCP transport", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 11, + "nativeTaskId": 307, + "subject": "Task 11: Verification (build + test + live)", + "status": "completed", + "blockedBy": [ + 8, + 9, + 10 + ] + } ], - "lastUpdated": "2026-06-12" + "lastUpdated": "2026-07-27", + "closureNote": "OBSOLETE \u2014 targets the bespoke Wonderware TCP/ArchestrA historian sidecar, retired when HistorianGateway became the sole historian backend. There is no Wonderware backend in the tree. Closed as obsolete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-15-stillpending-phase-0-1.md.tasks.json b/docs/plans/2026-06-15-stillpending-phase-0-1.md.tasks.json index 229b845a..9da566b6 100644 --- a/docs/plans/2026-06-15-stillpending-phase-0-1.md.tasks.json +++ b/docs/plans/2026-06-15-stillpending-phase-0-1.md.tasks.json @@ -1,18 +1,111 @@ { "planPath": "docs/plans/2026-06-15-stillpending-phase-0-1.md", "tasks": [ - {"id": 402, "subject": "SP Task 0: Feature branch feat/stillpending-phase-0-1", "status": "pending"}, - {"id": 403, "subject": "SP Task 1: Phase 0 — correct 7 stale code comments", "status": "pending", "blockedBy": [402]}, - {"id": 404, "subject": "SP Task 2: Phase 0 — fix docs/security.md + benign-residue comments", "status": "pending", "blockedBy": [402]}, - {"id": 405, "subject": "SP Task 3: Phase 0 — mark shipped .tasks.json completed", "status": "pending", "blockedBy": [402]}, - {"id": 406, "subject": "SP Task 4: Phase 1 H1a — Phase7Applier rebuilds on Changed*", "status": "pending", "blockedBy": [402]}, - {"id": 407, "subject": "SP Task 5: Phase 1 H1b — VirtualTagHostActor respawns changed child", "status": "pending", "blockedBy": [402]}, - {"id": 408, "subject": "SP Task 6: Phase 1 H5a — EquipmentVirtualTagPlan.Historize + composer", "status": "pending", "blockedBy": [402]}, - {"id": 409, "subject": "SP Task 7: Phase 1 H5b — DeploymentArtifact decodes Historize (byte-parity)", "status": "pending", "blockedBy": [408]}, - {"id": 410, "subject": "SP Task 8: Phase 1 H5c — VirtualTagHostActor invokes IHistoryWriter", "status": "pending", "blockedBy": [407, 409]}, - {"id": 411, "subject": "SP Task 9: Phase 1 H5d — thread IHistoryWriter through DriverHostActor + DI", "status": "pending", "blockedBy": [410, 403]}, - {"id": 412, "subject": "SP Task 10: Phase 1 — docs + follow-up bookkeeping", "status": "pending", "blockedBy": [411]}, - {"id": 413, "subject": "SP Task 11: Phase 0+1 — full build + test + integration review", "status": "pending", "blockedBy": [403, 404, 405, 406, 407, 408, 409, 410, 411, 412]} + { + "id": 402, + "subject": "SP Task 0: Feature branch feat/stillpending-phase-0-1", + "status": "completed" + }, + { + "id": 403, + "subject": "SP Task 1: Phase 0 \u2014 correct 7 stale code comments", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 404, + "subject": "SP Task 2: Phase 0 \u2014 fix docs/security.md + benign-residue comments", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 405, + "subject": "SP Task 3: Phase 0 \u2014 mark shipped .tasks.json completed", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 406, + "subject": "SP Task 4: Phase 1 H1a \u2014 Phase7Applier rebuilds on Changed*", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 407, + "subject": "SP Task 5: Phase 1 H1b \u2014 VirtualTagHostActor respawns changed child", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 408, + "subject": "SP Task 6: Phase 1 H5a \u2014 EquipmentVirtualTagPlan.Historize + composer", + "status": "completed", + "blockedBy": [ + 402 + ] + }, + { + "id": 409, + "subject": "SP Task 7: Phase 1 H5b \u2014 DeploymentArtifact decodes Historize (byte-parity)", + "status": "completed", + "blockedBy": [ + 408 + ] + }, + { + "id": 410, + "subject": "SP Task 8: Phase 1 H5c \u2014 VirtualTagHostActor invokes IHistoryWriter", + "status": "completed", + "blockedBy": [ + 407, + 409 + ] + }, + { + "id": 411, + "subject": "SP Task 9: Phase 1 H5d \u2014 thread IHistoryWriter through DriverHostActor + DI", + "status": "completed", + "blockedBy": [ + 410, + 403 + ] + }, + { + "id": 412, + "subject": "SP Task 10: Phase 1 \u2014 docs + follow-up bookkeeping", + "status": "completed", + "blockedBy": [ + 411 + ] + }, + { + "id": 413, + "subject": "SP Task 11: Phase 0+1 \u2014 full build + test + integration review", + "status": "completed", + "blockedBy": [ + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412 + ] + } ], - "lastUpdated": "2026-06-15" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-15-stillpending-phase-2-servicelevel.md.tasks.json b/docs/plans/2026-06-15-stillpending-phase-2-servicelevel.md.tasks.json index e7774c5e..c8c51972 100644 --- a/docs/plans/2026-06-15-stillpending-phase-2-servicelevel.md.tasks.json +++ b/docs/plans/2026-06-15-stillpending-phase-2-servicelevel.md.tasks.json @@ -2,15 +2,72 @@ "planPath": "docs/plans/2026-06-15-stillpending-phase-2-servicelevel.md", "branch": "feat/stillpending-phase-2-servicelevel", "tasks": [ - {"id": 414, "subject": "P2 Task 1: Move ServiceLevelCalculator to Core.Cluster", "status": "pending"}, - {"id": 415, "subject": "P2 Task 2a: OpcUaPublishActor calculator path (DB+stale+leader+Detached guard, legacy seam)", "status": "pending", "blockedBy": [414]}, - {"id": 416, "subject": "P2 Task 2b: OpcUaProbeOk from peer-probes-me (freshness + debounce)", "status": "pending", "blockedBy": [415]}, - {"id": 417, "subject": "P2 Task 3: HealthTick — periodic DB Ask/PipeTo + PreStart immediate refresh", "status": "pending", "blockedBy": [416]}, - {"id": 418, "subject": "P2 Task 4: PeerProbeSupervisor — one peer probe per driver peer", "status": "pending"}, - {"id": 419, "subject": "P2 Task 5: Wire WithOtOpcUaRuntimeActors (dbHealth ref + spawn supervisor)", "status": "pending", "blockedBy": [417, 418]}, - {"id": 420, "subject": "P2 Task 6: docs/Redundancy.md — calculator is WIRED", "status": "pending"}, - {"id": 421, "subject": "P2 Task 7: Full build + test + final integration review", "status": "pending", "blockedBy": [419, 420]}, - {"id": 422, "subject": "P2 Task 8: Live /run on the 2-node rig (acceptance gate)", "status": "pending", "blockedBy": [421]} + { + "id": 414, + "subject": "P2 Task 1: Move ServiceLevelCalculator to Core.Cluster", + "status": "completed" + }, + { + "id": 415, + "subject": "P2 Task 2a: OpcUaPublishActor calculator path (DB+stale+leader+Detached guard, legacy seam)", + "status": "completed", + "blockedBy": [ + 414 + ] + }, + { + "id": 416, + "subject": "P2 Task 2b: OpcUaProbeOk from peer-probes-me (freshness + debounce)", + "status": "completed", + "blockedBy": [ + 415 + ] + }, + { + "id": 417, + "subject": "P2 Task 3: HealthTick \u2014 periodic DB Ask/PipeTo + PreStart immediate refresh", + "status": "completed", + "blockedBy": [ + 416 + ] + }, + { + "id": 418, + "subject": "P2 Task 4: PeerProbeSupervisor \u2014 one peer probe per driver peer", + "status": "completed" + }, + { + "id": 419, + "subject": "P2 Task 5: Wire WithOtOpcUaRuntimeActors (dbHealth ref + spawn supervisor)", + "status": "completed", + "blockedBy": [ + 417, + 418 + ] + }, + { + "id": 420, + "subject": "P2 Task 6: docs/Redundancy.md \u2014 calculator is WIRED", + "status": "completed" + }, + { + "id": 421, + "subject": "P2 Task 7: Full build + test + final integration review", + "status": "completed", + "blockedBy": [ + 419, + 420 + ] + }, + { + "id": 422, + "subject": "P2 Task 8: Live /run on the 2-node rig (acceptance gate)", + "status": "completed", + "blockedBy": [ + 421 + ] + } ], - "lastUpdated": "2026-06-15" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-15-stillpending-phase-3-opcua-standards.md.tasks.json b/docs/plans/2026-06-15-stillpending-phase-3-opcua-standards.md.tasks.json index 60bf2d05..3c96da0d 100644 --- a/docs/plans/2026-06-15-stillpending-phase-3-opcua-standards.md.tasks.json +++ b/docs/plans/2026-06-15-stillpending-phase-3-opcua-standards.md.tasks.json @@ -2,16 +2,80 @@ "planPath": "docs/plans/2026-06-15-stillpending-phase-3-opcua-standards.md", "branch": "feat/stillpending-phase-3-opcua-standards", "tasks": [ - {"id": 423, "subject": "P3 Task 1: H2-bit — NodePermissions.HistoryUpdate + evaluator mapping", "status": "pending"}, - {"id": 424, "subject": "P3 Task 2: H6a — mark native conditions (isNative through the sink)", "status": "pending"}, - {"id": 425, "subject": "P3 Task 3: H4 — wire OnEnableDisable over OPC UA", "status": "pending", "blockedBy": [424]}, - {"id": 426, "subject": "P3 Task 4: H6b — AlarmAcknowledgeRequest.OperatorUser + Galaxy/ScriptedAlarmSource", "status": "pending"}, - {"id": 427, "subject": "P3 Task 5: H6c — NativeAlarmAckRouter seam + native OnAcknowledge routing", "status": "pending", "blockedBy": [424]}, - {"id": 428, "subject": "P3 Task 6: H6d — DriverHostActor inverse map + native-ack route to driver", "status": "pending", "blockedBy": [426, 427]}, - {"id": 429, "subject": "P3 Task 7: H6e — wire NativeAlarmAckRouter in host DI", "status": "pending", "blockedBy": [427, 428]}, - {"id": 430, "subject": "P3 Task 8: Docs — Enable/Disable + native-ack→AVEVA + HistoryUpdate bit", "status": "pending"}, - {"id": 431, "subject": "P3 Task 9: Full build + test + final integration review", "status": "pending", "blockedBy": [423, 425, 429, 430]}, - {"id": 432, "subject": "P3 Task 10: Live /run — H4 Enable/Disable + H6 native-ack route", "status": "pending", "blockedBy": [431]} + { + "id": 423, + "subject": "P3 Task 1: H2-bit \u2014 NodePermissions.HistoryUpdate + evaluator mapping", + "status": "completed" + }, + { + "id": 424, + "subject": "P3 Task 2: H6a \u2014 mark native conditions (isNative through the sink)", + "status": "completed" + }, + { + "id": 425, + "subject": "P3 Task 3: H4 \u2014 wire OnEnableDisable over OPC UA", + "status": "completed", + "blockedBy": [ + 424 + ] + }, + { + "id": 426, + "subject": "P3 Task 4: H6b \u2014 AlarmAcknowledgeRequest.OperatorUser + Galaxy/ScriptedAlarmSource", + "status": "completed" + }, + { + "id": 427, + "subject": "P3 Task 5: H6c \u2014 NativeAlarmAckRouter seam + native OnAcknowledge routing", + "status": "completed", + "blockedBy": [ + 424 + ] + }, + { + "id": 428, + "subject": "P3 Task 6: H6d \u2014 DriverHostActor inverse map + native-ack route to driver", + "status": "completed", + "blockedBy": [ + 426, + 427 + ] + }, + { + "id": 429, + "subject": "P3 Task 7: H6e \u2014 wire NativeAlarmAckRouter in host DI", + "status": "completed", + "blockedBy": [ + 427, + 428 + ] + }, + { + "id": 430, + "subject": "P3 Task 8: Docs \u2014 Enable/Disable + native-ack\u2192AVEVA + HistoryUpdate bit", + "status": "completed" + }, + { + "id": 431, + "subject": "P3 Task 9: Full build + test + final integration review", + "status": "completed", + "blockedBy": [ + 423, + 425, + 429, + 430 + ] + }, + { + "id": 432, + "subject": "P3 Task 10: Live /run \u2014 H4 Enable/Disable + H6 native-ack route", + "status": "completed", + "blockedBy": [ + 431 + ] + } ], - "lastUpdated": "2026-06-15" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-16-stillpending-phase-4-driver-datatypes.md.tasks.json b/docs/plans/2026-06-16-stillpending-phase-4-driver-datatypes.md.tasks.json index bc15b3cd..249f63ff 100644 --- a/docs/plans/2026-06-16-stillpending-phase-4-driver-datatypes.md.tasks.json +++ b/docs/plans/2026-06-16-stillpending-phase-4-driver-datatypes.md.tasks.json @@ -2,14 +2,68 @@ "planPath": "docs/plans/2026-06-16-stillpending-phase-4-driver-datatypes.md", "branch": "feat/stillpending-phase-4-driver-datatypes", "tasks": [ - {"id": 433, "subject": "P4 Task 1: Modbus Int64/UInt64 node DataType (MapDataType split)", "status": "pending"}, - {"id": 434, "subject": "P4 Task 2: FOCAS fail-fast factory (EnsureUsable at init)", "status": "pending"}, - {"id": 435, "subject": "P4 Task 3: FOCAS position scaling (PositionDecimalPlaces)", "status": "pending", "blockedBy": [434]}, - {"id": 436, "subject": "P4 Task 4: Historian Total aggregate (client-side Average x interval-seconds)", "status": "pending"}, - {"id": 437, "subject": "P4 Task 5: Historian poison-event dead-letter cap (maxAttempts)", "status": "pending"}, - {"id": 438, "subject": "P4 Task 6: Docs + bookkeeping", "status": "pending", "blockedBy": [433, 434, 435, 436, 437]}, - {"id": 439, "subject": "P4 Task 7: Full build + test + final integration review", "status": "pending", "blockedBy": [433, 434, 435, 436, 437, 438]}, - {"id": 440, "subject": "P4 Task 8: Live /run — Modbus Int64 (acceptance gate)", "status": "pending", "blockedBy": [439]} + { + "id": 433, + "subject": "P4 Task 1: Modbus Int64/UInt64 node DataType (MapDataType split)", + "status": "completed" + }, + { + "id": 434, + "subject": "P4 Task 2: FOCAS fail-fast factory (EnsureUsable at init)", + "status": "completed" + }, + { + "id": 435, + "subject": "P4 Task 3: FOCAS position scaling (PositionDecimalPlaces)", + "status": "completed", + "blockedBy": [ + 434 + ] + }, + { + "id": 436, + "subject": "P4 Task 4: Historian Total aggregate (client-side Average x interval-seconds)", + "status": "completed" + }, + { + "id": 437, + "subject": "P4 Task 5: Historian poison-event dead-letter cap (maxAttempts)", + "status": "completed" + }, + { + "id": 438, + "subject": "P4 Task 6: Docs + bookkeeping", + "status": "completed", + "blockedBy": [ + 433, + 434, + 435, + 436, + 437 + ] + }, + { + "id": 439, + "subject": "P4 Task 7: Full build + test + final integration review", + "status": "completed", + "blockedBy": [ + 433, + 434, + 435, + 436, + 437, + 438 + ] + }, + { + "id": 440, + "subject": "P4 Task 8: Live /run \u2014 Modbus Int64 (acceptance gate)", + "status": "completed", + "blockedBy": [ + 439 + ] + } ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-16-stillpending-phase-4b-driver-gaps.md.tasks.json b/docs/plans/2026-06-16-stillpending-phase-4b-driver-gaps.md.tasks.json index c1ba5a15..5b99ede0 100644 --- a/docs/plans/2026-06-16-stillpending-phase-4b-driver-gaps.md.tasks.json +++ b/docs/plans/2026-06-16-stillpending-phase-4b-driver-gaps.md.tasks.json @@ -4,16 +4,61 @@ "designCommit": "f90017bc", "baseMaster": "c081917a", "branch": "feat/stillpending-phase-4b-driver-gaps (merged to master 08a65513, deleted)", - "executionState": "COMPLETE — shipped + pushed to master 08a65513 (ff). Final integration review = SHIP. Build clean; AdminUI 450, Galaxy 279(+3 skip), FOCAS 200, Modbus 277, DriverProbeRegistration 2 — all green. Live: Modbus typed editor + Build-address proven on the rig's seeded MAIN-modbus-eq (Modbus reconcile, conclusive); Galaxy browse rendered a nested hierarchical root via the real driver path (corroborated, unit-proven 8 ways); FOCAS unit-proven (no CNC).", - "nativeTaskIds": {"1": 482, "2": 483, "3a": 484, "3b": 485, "4": 486, "5": 487, "6": 488}, + "executionState": "COMPLETE \u2014 shipped + pushed to master 08a65513 (ff). Final integration review = SHIP. Build clean; AdminUI 450, Galaxy 279(+3 skip), FOCAS 200, Modbus 277, DriverProbeRegistration 2 \u2014 all green. Live: Modbus typed editor + Build-address proven on the rig's seeded MAIN-modbus-eq (Modbus reconcile, conclusive); Galaxy browse rendered a nested hierarchical root via the real driver path (corroborated, unit-proven 8 ways); FOCAS unit-proven (no CNC).", + "nativeTaskIds": { + "1": 482, + "2": 483, + "3a": 484, + "3b": 485, + "4": 486, + "5": 487, + "6": 488 + }, "tasks": [ - {"id": 1, "subject": "Task 1: Modbus driver-type-string reconcile (canonicalize on \"Modbus\")", "status": "completed", "commit": "8b4675b1", "reviewFixCommit": "a40c77de"}, - {"id": 2, "subject": "Task 2: Galaxy nested gobject hierarchy", "status": "completed", "commit": "21c7645d", "reviewFixCommit": "bec37848"}, - {"id": "3a", "subject": "Task 3a: IFocasClient.GetPositionFiguresAsync (cnc_getfigure binding)", "status": "completed", "commit": "3fcbc70c"}, - {"id": "3b", "subject": "Task 3b: FOCAS driver auto-scale wiring (auto wins, manual fallback)", "status": "completed", "commit": "6855be28"}, - {"id": 4, "subject": "Task 4: Docs + bookkeeping", "status": "completed", "commit": "08a65513"}, - {"id": 5, "subject": "Task 5: Full build + test + final integration review", "status": "completed", "note": "final integration review = SHIP (no Critical/Important); 3 Minor non-blocking notes recorded"}, - {"id": 6, "subject": "Task 6: Live /run verification", "status": "completed", "note": "Modbus typed editor live-proven on rig; Galaxy nested root corroborated; FOCAS unit-proven"} + { + "id": 1, + "subject": "Task 1: Modbus driver-type-string reconcile (canonicalize on \"Modbus\")", + "status": "completed", + "commit": "8b4675b1", + "reviewFixCommit": "a40c77de" + }, + { + "id": 2, + "subject": "Task 2: Galaxy nested gobject hierarchy", + "status": "completed", + "commit": "21c7645d", + "reviewFixCommit": "bec37848" + }, + { + "id": "3a", + "subject": "Task 3a: IFocasClient.GetPositionFiguresAsync (cnc_getfigure binding)", + "status": "completed", + "commit": "3fcbc70c" + }, + { + "id": "3b", + "subject": "Task 3b: FOCAS driver auto-scale wiring (auto wins, manual fallback)", + "status": "completed", + "commit": "6855be28" + }, + { + "id": 4, + "subject": "Task 4: Docs + bookkeeping", + "status": "completed", + "commit": "08a65513" + }, + { + "id": 5, + "subject": "Task 5: Full build + test + final integration review", + "status": "completed", + "note": "final integration review = SHIP (no Critical/Important); 3 Minor non-blocking notes recorded" + }, + { + "id": 6, + "subject": "Task 6: Live /run verification", + "status": "completed", + "note": "Modbus typed editor live-proven on rig; Galaxy nested root corroborated; FOCAS unit-proven" + } ], "reviewFollowUps": [ "FOCAS minor: no upper-bound clamp on a misbehaving cnc_getfigure value (Math.Pow overflow; same latent risk the manual path had)", @@ -21,5 +66,6 @@ "FOCAS: live auto-fetch on the real backend needs a cnc_getfigure command added to the managed FocasWireClient wire protocol (WireFocasClient returns empty today)", "Galaxy minor: 3-node chain cycle provably-correct but not directly tested (2-node mutual cycle is tested)" ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-16-stillpending-phase-4c-array-support.md.tasks.json b/docs/plans/2026-06-16-stillpending-phase-4c-array-support.md.tasks.json index 89ce9ad1..24a808cc 100644 --- a/docs/plans/2026-06-16-stillpending-phase-4c-array-support.md.tasks.json +++ b/docs/plans/2026-06-16-stillpending-phase-4c-array-support.md.tasks.json @@ -4,28 +4,121 @@ "designCommit": "efccd8d1", "baseMaster": "050164b2", "branch": "feat/stillpending-phase-4c-array-support (merged to master 0f92e9e2, deleted)", - "executionState": "COMPLETE — shipped + pushed to master 0f92e9e2 (ff, 23 commits). Big-bang all 5 drivers + full AbLegacy. Build clean; OpcUaServer 230, Runtime 272, AdminUI 472, Modbus 289, AbCip 292, TwinCAT 153, S7 143, AbLegacy 188 — all green. Bundle integration review caught the cross-driver isArray-gating divergence (C-1/C-2 Modbus+AbLegacy ignored isArray; I-1/I-2/I-3 the arrayLength=1 edge); final integration review = SHIP WITH MINOR FOLLOW-UPS (I-1 S7 clarity, I-2 AbCip degenerate-scalar, I-3 AbLegacy >256 author-validation, M-1 array nodes read-only) — all closed. LIVE /run PASS: a Modbus array equipment tag (HR[0..3], UInt16[4]) materialises as ValueRank=OneDimension/ArrayDimensions=[4] and a Good array value flows end-to-end from the live sim (read over the wire, Status 0x00000000, Value System.UInt16[]); the address-100 first attempt correctly surfaced the sim's Illegal-Data-Address as Bad (faithful device status). S7/AbCip/TwinCAT/AbLegacy unit-proven (fixtures down).", + "executionState": "COMPLETE \u2014 shipped + pushed to master 0f92e9e2 (ff, 23 commits). Big-bang all 5 drivers + full AbLegacy. Build clean; OpcUaServer 230, Runtime 272, AdminUI 472, Modbus 289, AbCip 292, TwinCAT 153, S7 143, AbLegacy 188 \u2014 all green. Bundle integration review caught the cross-driver isArray-gating divergence (C-1/C-2 Modbus+AbLegacy ignored isArray; I-1/I-2/I-3 the arrayLength=1 edge); final integration review = SHIP WITH MINOR FOLLOW-UPS (I-1 S7 clarity, I-2 AbCip degenerate-scalar, I-3 AbLegacy >256 author-validation, M-1 array nodes read-only) \u2014 all closed. LIVE /run PASS: a Modbus array equipment tag (HR[0..3], UInt16[4]) materialises as ValueRank=OneDimension/ArrayDimensions=[4] and a Good array value flows end-to-end from the live sim (read over the wire, Status 0x00000000, Value System.UInt16[]); the address-100 first attempt correctly surfaced the sim's Illegal-Data-Address as Bad (faithful device status). S7/AbCip/TwinCAT/AbLegacy unit-proven (fixtures down).", "scope": "Big-bang all 5 drivers (AskUserQuestion) + full AbLegacy array read (AskUserQuestion). 1-D read-surface only; array writes / multi-dim / array historization out of scope (array nodes forced read-only at the applier per review M-1).", - "nativeTaskIds": {"1": 495, "2": 496, "3": 497, "4": 498, "5": 499, "6": 500, "7": 501, "8": 502, "9": 503, "10": 504, "11": 505, "12": 506}, + "nativeTaskIds": { + "1": 495, + "2": 496, + "3": 497, + "4": 498, + "5": 499, + "6": 500, + "7": 501, + "8": 502, + "9": 503, + "10": 504, + "11": 505, + "12": 506 + }, "tasks": [ - {"id": 1, "subject": "Sink contract — EnsureVariable array params", "classification": "high-risk", "status": "completed", "commit": "a7928202", "reviewFixCommit": "3172b7bd"}, - {"id": 2, "subject": "EquipmentTagPlan IsArray/ArrayLength + composer + applier", "classification": "high-risk", "status": "completed", "commit": "71cc4171", "reviewFixCommit": "584e9f2a"}, - {"id": 3, "subject": "DeploymentArtifact decode byte-parity", "classification": "high-risk", "status": "completed", "commit": "0a747c34", "reviewFixCommit": "eb8a8dc1"}, - {"id": 4, "subject": "AdminUI driver-agnostic isArray/arrayLength control", "classification": "standard", "status": "completed", "commit": "c2006dfb"}, - {"id": 5, "subject": "Modbus String/BitInRegister array decode + resolver", "classification": "small", "status": "completed", "commit": "8d3dc321", "reviewFixCommit": "49ac1392"}, - {"id": 6, "subject": "AbCip libplctag array read + IsArray", "classification": "standard", "status": "completed", "commit": "f4d5a5ee", "reviewFixCommit": "94e8c55b+5f7a2acd"}, - {"id": 7, "subject": "TwinCAT ADS array read + IsArray (reference impl, no fix needed)", "classification": "standard", "status": "completed", "commit": "3e742395"}, - {"id": 8, "subject": "S7 block-read array + decode loop + IsArray", "classification": "high-risk", "status": "completed", "commit": "a82c22c6", "reviewFixCommit": "3bbe39c1+d30fb77e"}, - {"id": 9, "subject": "AbLegacy PCCC multi-element array read + IsArray", "classification": "high-risk", "status": "completed", "commit": "95006939", "reviewFixCommit": "ce5d46be+0f92e9e2"}, - {"id": 10, "subject": "Docs + bookkeeping", "classification": "small", "status": "completed", "commit": "05c7e86f"}, - {"id": 11, "subject": "Full build + test + final integration review", "classification": "standard", "status": "completed", "note": "final integration review = SHIP WITH MINOR FOLLOW-UPS; 4 review fixes applied (d30fb77e/5f7a2acd/0f92e9e2/3bb2031d)"}, - {"id": 12, "subject": "Live /run acceptance + finish branch", "classification": "standard", "status": "completed", "note": "Modbus array tag live-proven (Good UInt16[4] from sim HR[0..3], over the wire); others unit-proven; root-caused the address-100 Bad as a sim Illegal-Data-Address (not a code bug)"} + { + "id": 1, + "subject": "Sink contract \u2014 EnsureVariable array params", + "classification": "high-risk", + "status": "completed", + "commit": "a7928202", + "reviewFixCommit": "3172b7bd" + }, + { + "id": 2, + "subject": "EquipmentTagPlan IsArray/ArrayLength + composer + applier", + "classification": "high-risk", + "status": "completed", + "commit": "71cc4171", + "reviewFixCommit": "584e9f2a" + }, + { + "id": 3, + "subject": "DeploymentArtifact decode byte-parity", + "classification": "high-risk", + "status": "completed", + "commit": "0a747c34", + "reviewFixCommit": "eb8a8dc1" + }, + { + "id": 4, + "subject": "AdminUI driver-agnostic isArray/arrayLength control", + "classification": "standard", + "status": "completed", + "commit": "c2006dfb" + }, + { + "id": 5, + "subject": "Modbus String/BitInRegister array decode + resolver", + "classification": "small", + "status": "completed", + "commit": "8d3dc321", + "reviewFixCommit": "49ac1392" + }, + { + "id": 6, + "subject": "AbCip libplctag array read + IsArray", + "classification": "standard", + "status": "completed", + "commit": "f4d5a5ee", + "reviewFixCommit": "94e8c55b+5f7a2acd" + }, + { + "id": 7, + "subject": "TwinCAT ADS array read + IsArray (reference impl, no fix needed)", + "classification": "standard", + "status": "completed", + "commit": "3e742395" + }, + { + "id": 8, + "subject": "S7 block-read array + decode loop + IsArray", + "classification": "high-risk", + "status": "completed", + "commit": "a82c22c6", + "reviewFixCommit": "3bbe39c1+d30fb77e" + }, + { + "id": 9, + "subject": "AbLegacy PCCC multi-element array read + IsArray", + "classification": "high-risk", + "status": "completed", + "commit": "95006939", + "reviewFixCommit": "ce5d46be+0f92e9e2" + }, + { + "id": 10, + "subject": "Docs + bookkeeping", + "classification": "small", + "status": "completed", + "commit": "05c7e86f" + }, + { + "id": 11, + "subject": "Full build + test + final integration review", + "classification": "standard", + "status": "completed", + "note": "final integration review = SHIP WITH MINOR FOLLOW-UPS; 4 review fixes applied (d30fb77e/5f7a2acd/0f92e9e2/3bb2031d)" + }, + { + "id": 12, + "subject": "Live /run acceptance + finish branch", + "classification": "standard", + "status": "completed", + "note": "Modbus array tag live-proven (Good UInt16[4] from sim HR[0..3], over the wire); others unit-proven; root-caused the address-100 Bad as a sim Illegal-Data-Address (not a code bug)" + } ], "reviewFollowUps": [ - "Foundation array test asserts in-process variable.Value only — add a wire-level Session.ReadValueAsync assertion over an array node (the live /run proved it works, but it's not unit-covered)", - "Client.CLI read/subscribe prints an array value's type name (System.UInt16[]) not its elements — add array element formatting", + "Foundation array test asserts in-process variable.Value only \u2014 add a wire-level Session.ReadValueAsync assertion over an array node (the live /run proved it works, but it's not unit-covered)", + "Client.CLI read/subscribe prints an array value's type name (System.UInt16[]) not its elements \u2014 add array element formatting", "Array WRITES (inbound client->device), multi-dimensional arrays, array historization remain out of scope (named deferrals)", "Live array read for S7/AbCip/TwinCAT/AbLegacy is fixture-gated/operator-gated (unit-proven only); the 5 AbLegacy libplctag PCCC array-read assumptions need a real SLC/MicroLogix to confirm" ], - "lastUpdated": "2026-06-16" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped \u2014 the stillpending backlog audit cites the landing commits (1e95856b, ada01e1a, fc8121cb, 3e609a2b, 70e6d3d2, c236263e, bd8fee61, 5e27b5f7, fcb38014). Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-06-26-otopcua-historian-gateway-integration.md.tasks.json b/docs/plans/2026-06-26-otopcua-historian-gateway-integration.md.tasks.json index 753a5a51..169b5632 100644 --- a/docs/plans/2026-06-26-otopcua-historian-gateway-integration.md.tasks.json +++ b/docs/plans/2026-06-26-otopcua-historian-gateway-integration.md.tasks.json @@ -1,27 +1,185 @@ { "planPath": "docs/plans/2026-06-26-otopcua-historian-gateway-integration.md", "tasks": [ - { "id": 0, "subject": "Task 1: Consume gateway packages + scaffold Gateway driver project", "status": "pending", "blockedBy": [] }, - { "id": 1, "subject": "Task 2: HistoryAggregateType->RetrievalMode mapper (matrix-guarded)", "status": "pending", "blockedBy": [0] }, - { "id": 2, "subject": "Task 3: DriverDataType->HistorianDataType mapper + write-gap fallbacks (matrix-guarded)", "status": "pending", "blockedBy": [0] }, - { "id": 3, "subject": "Task 4: HistorianSample/Aggregate->DataValueSnapshot + quality mapper", "status": "pending", "blockedBy": [0] }, - { "id": 4, "subject": "Task 5: HistorianEvent->HistoricalEvent mapper (+ severity)", "status": "pending", "blockedBy": [0] }, - { "id": 5, "subject": "Task 6: AlarmHistorianEvent->HistorianEvent mapper (SendEvent)", "status": "pending", "blockedBy": [0] }, - { "id": 6, "subject": "Task 7: GatewayHistorianDataSource read paths (raw/processed/at-time)", "status": "pending", "blockedBy": [1, 3] }, - { "id": 7, "subject": "Task 8: GetHealthSnapshot via Probe/GetConnectionStatus", "status": "pending", "blockedBy": [6] }, - { "id": 8, "subject": "Task 9: Reshape ServerHistorianOptions to gateway form", "status": "pending", "blockedBy": [0] }, - { "id": 9, "subject": "Task 10: Swap AddServerHistorian factory in Program.cs (READ CUTOVER)", "status": "pending", "blockedBy": [6, 8] }, - { "id": 10, "subject": "Task 11: ReadEventsAsync alarm-history on the data source", "status": "pending", "blockedBy": [6, 4] }, - { "id": 11, "subject": "Task 12: GatewayAlarmHistorianWriter (SendEvent + outcome mapping)", "status": "pending", "blockedBy": [9, 5] }, - { "id": 12, "subject": "Task 13: Swap AddAlarmHistorian factory in Program.cs", "status": "pending", "blockedBy": [11] }, - { "id": 13, "subject": "Task 14: IHistorianProvisioning + GatewayTagProvisioner (EnsureTags)", "status": "pending", "blockedBy": [9, 2] }, - { "id": 14, "subject": "Task 15: Hook provisioning into AddressSpaceApplier.Apply()", "status": "pending", "blockedBy": [13] }, - { "id": 15, "subject": "Task 16: FasterLog historization outbox store", "status": "pending", "blockedBy": [9] }, - { "id": 16, "subject": "Task 17: ContinuousHistorizationRecorder actor", "status": "pending", "blockedBy": [15, 9] }, - { "id": 17, "subject": "Task 18: Wire recorder into DI + hosted lifecycle", "status": "pending", "blockedBy": [16] }, - { "id": 18, "subject": "Task 19: Retire Wonderware historian projects", "status": "pending", "blockedBy": [9, 12, 17, 19] }, - { "id": 19, "subject": "Task 20: Env-gated live validation vs wonder-sql-vd03", "status": "pending", "blockedBy": [9, 10, 12, 17] }, - { "id": 20, "subject": "Task 21: Documentation (CLAUDE.md, appsettings, README)", "status": "pending", "blockedBy": [18] } + { + "id": 0, + "subject": "Task 1: Consume gateway packages + scaffold Gateway driver project", + "status": "completed", + "blockedBy": [] + }, + { + "id": 1, + "subject": "Task 2: HistoryAggregateType->RetrievalMode mapper (matrix-guarded)", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 2, + "subject": "Task 3: DriverDataType->HistorianDataType mapper + write-gap fallbacks (matrix-guarded)", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 3, + "subject": "Task 4: HistorianSample/Aggregate->DataValueSnapshot + quality mapper", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 4, + "subject": "Task 5: HistorianEvent->HistoricalEvent mapper (+ severity)", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 5, + "subject": "Task 6: AlarmHistorianEvent->HistorianEvent mapper (SendEvent)", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 6, + "subject": "Task 7: GatewayHistorianDataSource read paths (raw/processed/at-time)", + "status": "completed", + "blockedBy": [ + 1, + 3 + ] + }, + { + "id": 7, + "subject": "Task 8: GetHealthSnapshot via Probe/GetConnectionStatus", + "status": "completed", + "blockedBy": [ + 6 + ] + }, + { + "id": 8, + "subject": "Task 9: Reshape ServerHistorianOptions to gateway form", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 9, + "subject": "Task 10: Swap AddServerHistorian factory in Program.cs (READ CUTOVER)", + "status": "completed", + "blockedBy": [ + 6, + 8 + ] + }, + { + "id": 10, + "subject": "Task 11: ReadEventsAsync alarm-history on the data source", + "status": "completed", + "blockedBy": [ + 6, + 4 + ] + }, + { + "id": 11, + "subject": "Task 12: GatewayAlarmHistorianWriter (SendEvent + outcome mapping)", + "status": "completed", + "blockedBy": [ + 9, + 5 + ] + }, + { + "id": 12, + "subject": "Task 13: Swap AddAlarmHistorian factory in Program.cs", + "status": "completed", + "blockedBy": [ + 11 + ] + }, + { + "id": 13, + "subject": "Task 14: IHistorianProvisioning + GatewayTagProvisioner (EnsureTags)", + "status": "completed", + "blockedBy": [ + 9, + 2 + ] + }, + { + "id": 14, + "subject": "Task 15: Hook provisioning into AddressSpaceApplier.Apply()", + "status": "completed", + "blockedBy": [ + 13 + ] + }, + { + "id": 15, + "subject": "Task 16: FasterLog historization outbox store", + "status": "completed", + "blockedBy": [ + 9 + ] + }, + { + "id": 16, + "subject": "Task 17: ContinuousHistorizationRecorder actor", + "status": "completed", + "blockedBy": [ + 15, + 9 + ] + }, + { + "id": 17, + "subject": "Task 18: Wire recorder into DI + hosted lifecycle", + "status": "completed", + "blockedBy": [ + 16 + ] + }, + { + "id": 18, + "subject": "Task 19: Retire Wonderware historian projects", + "status": "completed", + "blockedBy": [ + 9, + 12, + 17, + 19 + ] + }, + { + "id": 19, + "subject": "Task 20: Env-gated live validation vs wonder-sql-vd03", + "status": "completed", + "blockedBy": [ + 9, + 10, + 12, + 17 + ] + }, + { + "id": 20, + "subject": "Task 21: Documentation (CLAUDE.md, appsettings, README)", + "status": "completed", + "blockedBy": [ + 18 + ] + } ], - "lastUpdated": "2026-06-26" + "lastUpdated": "2026-07-27", + "closureNote": "Shipped as PR #423 and live-validated against the gateway on wonder-sql-vd03. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-22-per-cluster-mesh-program.md.tasks.json b/docs/plans/2026-07-22-per-cluster-mesh-program.md.tasks.json index b78ab816..0c788a89 100644 --- a/docs/plans/2026-07-22-per-cluster-mesh-program.md.tasks.json +++ b/docs/plans/2026-07-22-per-cluster-mesh-program.md.tasks.json @@ -2,14 +2,69 @@ "planPath": "docs/plans/2026-07-22-per-cluster-mesh-program.md", "note": "PROGRAM plan: each task = write that phase's detailed plan (writing-plans), execute it (executing-plans), run its exit gate, update the tracking tables. Prereq task 0 is the separate selfform-fallback plan in this repo.", "tasks": [ - {"id": 0, "subject": "Prereq: execute 2026-07-22-selfform-fallback-and-manual-failover.md (7 tasks)", "status": "completed", "note": "Shipped as self-first seed ordering + AkkaClusterOptionsValidator, NOT the planned SelfFormAfter watchdog (live gate caught it islanding a failed-over node). Merged a78425ea."}, - {"id": 1, "subject": "Phase 1: ClusterNode Akka+gRPC address columns; ConfigPublishCoordinator ack set from DB", "status": "completed", "note": "Merged 7654f24d. Escape hatch is ClusterNode.MaintenanceMode, not Enabled=0."}, - {"id": 2, "subject": "Phase 2: comm actors + receptionist + ClusterClient transport (deploy notify/acks, driver-control)", "status": "in_progress", "blockedBy": [1]}, - {"id": 3, "subject": "Phase 3: config fetch-and-cache from central; LocalDb steady-state config store (live gate)", "status": "pending", "blockedBy": [2]}, - {"id": 4, "subject": "Phase 4: cut driver-side ConfigDb — EfAlarmConditionStateStore to LocalDb, DbHealthProbeActor ServiceLevel input, OpcUaPublishActor audit (live gate)", "status": "pending", "blockedBy": [3]}, - {"id": 5, "subject": "Phase 5: gRPC oneof stream contract; migrate 7 observability topics; AdminUI reconnect story", "status": "pending", "blockedBy": [2]}, - {"id": 6, "subject": "Phase 6: mesh partition — per-pair seeds, cluster-scoped roles/singletons, co-location rig rewrite, secrets replication re-scope", "status": "pending", "blockedBy": [0, 4, 5]}, - {"id": 7, "subject": "Phase 7: failover drill per pair (incl. shared-VM failure) + close auto-down 1v1 and self-first cold-start-alone live gates + operator runbook", "status": "pending", "blockedBy": [6]} + { + "id": 0, + "subject": "Prereq: execute 2026-07-22-selfform-fallback-and-manual-failover.md (7 tasks)", + "status": "completed", + "note": "Shipped as self-first seed ordering + AkkaClusterOptionsValidator, NOT the planned SelfFormAfter watchdog (live gate caught it islanding a failed-over node). Merged a78425ea." + }, + { + "id": 1, + "subject": "Phase 1: ClusterNode Akka+gRPC address columns; ConfigPublishCoordinator ack set from DB", + "status": "completed", + "note": "Merged 7654f24d. Escape hatch is ClusterNode.MaintenanceMode, not Enabled=0." + }, + { + "id": 2, + "subject": "Phase 2: comm actors + receptionist + ClusterClient transport (deploy notify/acks, driver-control)", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "subject": "Phase 3: config fetch-and-cache from central; LocalDb steady-state config store (live gate)", + "status": "completed", + "blockedBy": [ + 2 + ] + }, + { + "id": 4, + "subject": "Phase 4: cut driver-side ConfigDb \u2014 EfAlarmConditionStateStore to LocalDb, DbHealthProbeActor ServiceLevel input, OpcUaPublishActor audit (live gate)", + "status": "completed", + "blockedBy": [ + 3 + ] + }, + { + "id": 5, + "subject": "Phase 5: gRPC oneof stream contract; migrate 7 observability topics; AdminUI reconnect story", + "status": "completed", + "blockedBy": [ + 2 + ] + }, + { + "id": 6, + "subject": "Phase 6: mesh partition \u2014 per-pair seeds, cluster-scoped roles/singletons, co-location rig rewrite, secrets replication re-scope", + "status": "completed", + "blockedBy": [ + 0, + 4, + 5 + ] + }, + { + "id": 7, + "subject": "Phase 7: failover drill per pair (incl. shared-VM failure) + close auto-down 1v1 and self-first cold-start-alone live gates + operator runbook", + "status": "completed", + "blockedBy": [ + 6 + ] + } ], - "lastUpdated": "2026-07-22T00:00:00Z" + "lastUpdated": "2026-07-27", + "closureNote": "Program COMPLETE (Phases 0a-7 + bootstrap guard), origin/master d1dac87f. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-23-mesh-phase4-cut-driver-configdb.md.tasks.json b/docs/plans/2026-07-23-mesh-phase4-cut-driver-configdb.md.tasks.json index 20e1ce31..29114768 100644 --- a/docs/plans/2026-07-23-mesh-phase4-cut-driver-configdb.md.tasks.json +++ b/docs/plans/2026-07-23-mesh-phase4-cut-driver-configdb.md.tasks.json @@ -100,7 +100,7 @@ { "id": 9, "subject": "Task 9: Drop dead ConfigDb ScriptedAlarmState table + retire EfAlarmConditionStateStore", - "status": "deferred", + "status": "completed", "blockedBy": [ 6 ], @@ -128,5 +128,6 @@ "result": "LIVE GATE PASSED \u2014 deploy sealed green w/ 4 DB-less site nodes acking; ServiceLevel 240 w/ central SQL down; restart booted last-known-good from LocalDb pointer; grep proof; alarm_condition_state table live+replicated. Doc: 2026-07-23-mesh-phase4-live-gate.md" } ], - "lastUpdated": "2026-07-23" + "lastUpdated": "2026-07-27", + "closureNote": "Task 9 landed 3a590a0c; live gate PASSED 1281aebf. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-24-modbus-rtu-driver.md.tasks.json b/docs/plans/2026-07-24-modbus-rtu-driver.md.tasks.json index dc74d3de..a17f041c 100644 --- a/docs/plans/2026-07-24-modbus-rtu-driver.md.tasks.json +++ b/docs/plans/2026-07-24-modbus-rtu-driver.md.tasks.json @@ -1,17 +1,91 @@ { "planPath": "docs/plans/2026-07-24-modbus-rtu-driver.md", "tasks": [ - {"id": 0, "subject": "Task 0: Add the ModbusTransportMode enum (Tcp | RtuOverTcp)", "status": "pending"}, - {"id": 1, "subject": "Task 1: ModbusCrc CRC-16/MODBUS helper + golden vectors", "status": "pending"}, - {"id": 2, "subject": "Task 2: ModbusRtuFraming ADU build + FC-aware length-less deframe", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: Extract ModbusSocketLifecycle from ModbusTcpTransport (behaviour-preserving)", "status": "pending"}, - {"id": 4, "subject": "Task 4: ModbusRtuOverTcpTransport (RTU framing over socket lifecycle)", "status": "pending", "blockedBy": [2, 3]}, - {"id": 5, "subject": "Task 5: ModbusTransportFactory.Create switch on Transport mode", "status": "pending", "blockedBy": [0, 4]}, - {"id": 6, "subject": "Task 6: Bind Transport on options/DTO/factory (string-enum guard)", "status": "pending", "blockedBy": [0]}, - {"id": 7, "subject": "Task 7: Route driver default closure + probe through ModbusTransportFactory", "status": "pending", "blockedBy": [5, 6]}, - {"id": 8, "subject": "Task 8: AdminUI Transport selector on ModbusDriverForm", "status": "pending", "blockedBy": [6]}, - {"id": 9, "subject": "Task 9: Docker rtu_over_tcp fixture + RTU-over-TCP integration test", "status": "pending", "blockedBy": [4, 7]}, - {"id": 10, "subject": "Task 10: Live /run verification on docker-dev", "status": "pending", "blockedBy": [8, 9]} + { + "id": 0, + "subject": "Task 0: Add the ModbusTransportMode enum (Tcp | RtuOverTcp)", + "status": "completed" + }, + { + "id": 1, + "subject": "Task 1: ModbusCrc CRC-16/MODBUS helper + golden vectors", + "status": "completed" + }, + { + "id": 2, + "subject": "Task 2: ModbusRtuFraming ADU build + FC-aware length-less deframe", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "subject": "Task 3: Extract ModbusSocketLifecycle from ModbusTcpTransport (behaviour-preserving)", + "status": "completed" + }, + { + "id": 4, + "subject": "Task 4: ModbusRtuOverTcpTransport (RTU framing over socket lifecycle)", + "status": "completed", + "blockedBy": [ + 2, + 3 + ] + }, + { + "id": 5, + "subject": "Task 5: ModbusTransportFactory.Create switch on Transport mode", + "status": "completed", + "blockedBy": [ + 0, + 4 + ] + }, + { + "id": 6, + "subject": "Task 6: Bind Transport on options/DTO/factory (string-enum guard)", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 7, + "subject": "Task 7: Route driver default closure + probe through ModbusTransportFactory", + "status": "completed", + "blockedBy": [ + 5, + 6 + ] + }, + { + "id": 8, + "subject": "Task 8: AdminUI Transport selector on ModbusDriverForm", + "status": "completed", + "blockedBy": [ + 6 + ] + }, + { + "id": 9, + "subject": "Task 9: Docker rtu_over_tcp fixture + RTU-over-TCP integration test", + "status": "completed", + "blockedBy": [ + 4, + 7 + ] + }, + { + "id": 10, + "subject": "Task 10: Live /run verification on docker-dev", + "status": "completed", + "blockedBy": [ + 8, + 9 + ] + } ], - "lastUpdated": "2026-07-24" + "lastUpdated": "2026-07-27", + "closureNote": "Merged 0f38f486 (#495); live gate PASSED. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-24-mqtt-sparkplug-driver.md.tasks.json b/docs/plans/2026-07-24-mqtt-sparkplug-driver.md.tasks.json index 2abe8b56..3c0e831a 100644 --- a/docs/plans/2026-07-24-mqtt-sparkplug-driver.md.tasks.json +++ b/docs/plans/2026-07-24-mqtt-sparkplug-driver.md.tasks.json @@ -1,34 +1,315 @@ { "planPath": "docs/plans/2026-07-24-mqtt-sparkplug-driver.md", - "note": "Wave 2 MQTT/Sparkplug B driver. Two phases both in-scope: P1 (plain MQTT, Tasks 0-14) is a complete shippable milestone; P2 (Sparkplug B ingest, Tasks 15-26) builds on it and every P2 task chains back to Task 14. Write-through (NCMD/DCMD/plain publish) is DEFERRED (design P3). Hard rules: Task 0 (MQTTnet-5/net10 restore+build under central pinning with transitive pinning OFF) is the mandatory FIRST gate. Hand-roll Tahu proto over ONE MQTTnet-5 client (NOT SparkplugNet — its MQTTnet-4.x transitive pin collides with the repo's deliberately-OFF CentralPackageTransitivePinning). DriverType string is 'Mqtt' everywhere (grep-enforced). Enum-serialization trap: every JSON seam uses shared JsonSerializerOptions with JsonStringEnumConverter. Fixtures enforce TLS+auth, never anonymous/public-broker. Google.Protobuf (3.34.1) + Grpc.Tools (2.76.0) already pinned; MQTTnet is the one new pin. High-risk tasks: 4 (hand-rolled reconnect loop), 18/19 (alias/seq state-machine components), 21 (the full 3.6 ingest matrix).", + "note": "Wave 2 MQTT/Sparkplug B driver. Two phases both in-scope: P1 (plain MQTT, Tasks 0-14) is a complete shippable milestone; P2 (Sparkplug B ingest, Tasks 15-26) builds on it and every P2 task chains back to Task 14. Write-through (NCMD/DCMD/plain publish) is DEFERRED (design P3). Hard rules: Task 0 (MQTTnet-5/net10 restore+build under central pinning with transitive pinning OFF) is the mandatory FIRST gate. Hand-roll Tahu proto over ONE MQTTnet-5 client (NOT SparkplugNet \u2014 its MQTTnet-4.x transitive pin collides with the repo's deliberately-OFF CentralPackageTransitivePinning). DriverType string is 'Mqtt' everywhere (grep-enforced). Enum-serialization trap: every JSON seam uses shared JsonSerializerOptions with JsonStringEnumConverter. Fixtures enforce TLS+auth, never anonymous/public-broker. Google.Protobuf (3.34.1) + Grpc.Tools (2.76.0) already pinned; MQTTnet is the one new pin. High-risk tasks: 4 (hand-rolled reconnect loop), 18/19 (alias/seq state-machine components), 21 (the full 3.6 ingest matrix).", "tasks": [ - {"id": 0, "subject": "Task 0 (P1): Dependency-validation spike — MQTTnet-5 pin + net10 restore/build under central pinning", "status": "pending", "classification": "standard", "parallelizableWith": []}, - {"id": 1, "subject": "Task 1 (P1): .Contracts enums + MqttDriverOptions DTO (name-serialized)", "status": "pending", "classification": "small", "parallelizableWith": [], "blockedBy": [0]}, - {"id": 2, "subject": "Task 2 (P1): .Contracts MqttTagDefinition + MqttEquipmentTagParser (plain, strict enum)", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [1]}, - {"id": 3, "subject": "Task 3 (P1): .Driver + MqttConnection connect/TLS/auth (bounded deadline)", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [1]}, - {"id": 4, "subject": "Task 4 (P1): MqttConnection hand-rolled reconnect loop (backoff + resubscribe)", "status": "pending", "classification": "high-risk", "parallelizableWith": [], "blockedBy": [3]}, - {"id": 5, "subject": "Task 5 (P1): LastValueCache + IReadable (per-ref no-data)", "status": "pending", "classification": "small", "parallelizableWith": [6], "blockedBy": [3]}, - {"id": 6, "subject": "Task 6 (P1): ISubscribable plain topic subscribe→OnDataChange + retained seed", "status": "pending", "classification": "standard", "parallelizableWith": [5], "blockedBy": [2, 4]}, - {"id": 7, "subject": "Task 7 (P1): MqttDriver shell — IDriver + authored-only ITagDiscovery (Once) + probe interface", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [5, 6]}, - {"id": 8, "subject": "Task 8 (P1): MqttDriverProbe CONNECT handshake", "status": "pending", "classification": "small", "parallelizableWith": [10], "blockedBy": [3]}, - {"id": 9, "subject": "Task 9 (P1): Factory + DriverTypeNames.Mqtt + Host factory/probe registration", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [7, 8]}, - {"id": 10, "subject": "Task 10 (P1): .Browser bespoke #-observation browser (passive)", "status": "pending", "classification": "standard", "parallelizableWith": [8], "blockedBy": [2]}, - {"id": 11, "subject": "Task 11 (P1): Register MqttDriverBrowser (bespoke-first for Mqtt)", "status": "pending", "classification": "trivial", "parallelizableWith": [12], "blockedBy": [10]}, - {"id": 12, "subject": "Task 12 (P1): Typed AdminUI editor + validator (plain shape)", "status": "pending", "classification": "standard", "parallelizableWith": [11], "blockedBy": [2]}, - {"id": 13, "subject": "Task 13 (P1): Mosquitto+JSON-publisher fixture (TLS+auth) + env-gated live suite", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [9]}, - {"id": 14, "subject": "Task 14 (P1): Live /run verify on docker-dev — P1 MILESTONE COMPLETE", "status": "pending", "classification": "small", "parallelizableWith": [], "blockedBy": [9, 11, 12, 13]}, - {"id": 15, "subject": "Task 15 (P2): Vendor Tahu sparkplug_b.proto + Grpc.Tools codegen", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [14]}, - {"id": 16, "subject": "Task 16 (P2): SparkplugCodec decode + golden payload vectors", "status": "pending", "classification": "standard", "parallelizableWith": [17], "blockedBy": [15]}, - {"id": 17, "subject": "Task 17 (P2): SparkplugTopic parse/format + SparkplugDataType.ToDriverDataType map", "status": "pending", "classification": "small", "parallelizableWith": [16], "blockedBy": [15]}, - {"id": 18, "subject": "Task 18 (P2): BirthCache + AliasTable — bind-by-name, rebuild-per-birth", "status": "pending", "classification": "high-risk", "parallelizableWith": [19], "blockedBy": [16, 17]}, - {"id": 19, "subject": "Task 19 (P2): SequenceTracker seq-gap + bdSeq death-tie", "status": "pending", "classification": "high-risk", "parallelizableWith": [18], "blockedBy": [16, 17]}, - {"id": 20, "subject": "Task 20 (P2): RebirthRequester NCMD encode", "status": "pending", "classification": "small", "parallelizableWith": [], "blockedBy": [16]}, - {"id": 21, "subject": "Task 21 (P2): Sparkplug ingest state machine — the 3.6 matrix", "status": "pending", "classification": "high-risk", "parallelizableWith": [], "blockedBy": [18, 19, 20]}, - {"id": 22, "subject": "Task 22 (P2): ITagDiscovery UntilStable + IRediscoverable (rediscover-on-DBIRTH)", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [21]}, - {"id": 23, "subject": "Task 23 (P2): Sparkplug browser tree + AttributesAsync + scoped RequestRebirthAsync", "status": "pending", "classification": "standard", "parallelizableWith": [24], "blockedBy": [20, 21]}, - {"id": 24, "subject": "Task 24 (P2): AdminUI editor Sparkplug mode shape + validator", "status": "pending", "classification": "small", "parallelizableWith": [23], "blockedBy": [12, 17]}, - {"id": 25, "subject": "Task 25 (P2): C# edge-node simulator fixture + 3.6 live matrix", "status": "pending", "classification": "standard", "parallelizableWith": [], "blockedBy": [21, 22, 23]}, - {"id": 26, "subject": "Task 26 (P2): Live /run verify Sparkplug — P2 COMPLETE", "status": "pending", "classification": "small", "parallelizableWith": [], "blockedBy": [23, 24, 25]} + { + "id": 0, + "subject": "Task 0 (P1): Dependency-validation spike \u2014 MQTTnet-5 pin + net10 restore/build under central pinning", + "status": "completed", + "classification": "standard", + "parallelizableWith": [] + }, + { + "id": 1, + "subject": "Task 1 (P1): .Contracts enums + MqttDriverOptions DTO (name-serialized)", + "status": "completed", + "classification": "small", + "parallelizableWith": [], + "blockedBy": [ + 0 + ] + }, + { + "id": 2, + "subject": "Task 2 (P1): .Contracts MqttTagDefinition + MqttEquipmentTagParser (plain, strict enum)", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "subject": "Task 3 (P1): .Driver + MqttConnection connect/TLS/auth (bounded deadline)", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 1 + ] + }, + { + "id": 4, + "subject": "Task 4 (P1): MqttConnection hand-rolled reconnect loop (backoff + resubscribe)", + "status": "completed", + "classification": "high-risk", + "parallelizableWith": [], + "blockedBy": [ + 3 + ] + }, + { + "id": 5, + "subject": "Task 5 (P1): LastValueCache + IReadable (per-ref no-data)", + "status": "completed", + "classification": "small", + "parallelizableWith": [ + 6 + ], + "blockedBy": [ + 3 + ] + }, + { + "id": 6, + "subject": "Task 6 (P1): ISubscribable plain topic subscribe\u2192OnDataChange + retained seed", + "status": "completed", + "classification": "standard", + "parallelizableWith": [ + 5 + ], + "blockedBy": [ + 2, + 4 + ] + }, + { + "id": 7, + "subject": "Task 7 (P1): MqttDriver shell \u2014 IDriver + authored-only ITagDiscovery (Once) + probe interface", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 5, + 6 + ] + }, + { + "id": 8, + "subject": "Task 8 (P1): MqttDriverProbe CONNECT handshake", + "status": "completed", + "classification": "small", + "parallelizableWith": [ + 10 + ], + "blockedBy": [ + 3 + ] + }, + { + "id": 9, + "subject": "Task 9 (P1): Factory + DriverTypeNames.Mqtt + Host factory/probe registration", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 7, + 8 + ] + }, + { + "id": 10, + "subject": "Task 10 (P1): .Browser bespoke #-observation browser (passive)", + "status": "completed", + "classification": "standard", + "parallelizableWith": [ + 8 + ], + "blockedBy": [ + 2 + ] + }, + { + "id": 11, + "subject": "Task 11 (P1): Register MqttDriverBrowser (bespoke-first for Mqtt)", + "status": "completed", + "classification": "trivial", + "parallelizableWith": [ + 12 + ], + "blockedBy": [ + 10 + ] + }, + { + "id": 12, + "subject": "Task 12 (P1): Typed AdminUI editor + validator (plain shape)", + "status": "completed", + "classification": "standard", + "parallelizableWith": [ + 11 + ], + "blockedBy": [ + 2 + ] + }, + { + "id": 13, + "subject": "Task 13 (P1): Mosquitto+JSON-publisher fixture (TLS+auth) + env-gated live suite", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 9 + ] + }, + { + "id": 14, + "subject": "Task 14 (P1): Live /run verify on docker-dev \u2014 P1 MILESTONE COMPLETE", + "status": "completed", + "classification": "small", + "parallelizableWith": [], + "blockedBy": [ + 9, + 11, + 12, + 13 + ] + }, + { + "id": 15, + "subject": "Task 15 (P2): Vendor Tahu sparkplug_b.proto + Grpc.Tools codegen", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 14 + ] + }, + { + "id": 16, + "subject": "Task 16 (P2): SparkplugCodec decode + golden payload vectors", + "status": "completed", + "classification": "standard", + "parallelizableWith": [ + 17 + ], + "blockedBy": [ + 15 + ] + }, + { + "id": 17, + "subject": "Task 17 (P2): SparkplugTopic parse/format + SparkplugDataType.ToDriverDataType map", + "status": "completed", + "classification": "small", + "parallelizableWith": [ + 16 + ], + "blockedBy": [ + 15 + ] + }, + { + "id": 18, + "subject": "Task 18 (P2): BirthCache + AliasTable \u2014 bind-by-name, rebuild-per-birth", + "status": "completed", + "classification": "high-risk", + "parallelizableWith": [ + 19 + ], + "blockedBy": [ + 16, + 17 + ] + }, + { + "id": 19, + "subject": "Task 19 (P2): SequenceTracker seq-gap + bdSeq death-tie", + "status": "completed", + "classification": "high-risk", + "parallelizableWith": [ + 18 + ], + "blockedBy": [ + 16, + 17 + ] + }, + { + "id": 20, + "subject": "Task 20 (P2): RebirthRequester NCMD encode", + "status": "completed", + "classification": "small", + "parallelizableWith": [], + "blockedBy": [ + 16 + ] + }, + { + "id": 21, + "subject": "Task 21 (P2): Sparkplug ingest state machine \u2014 the 3.6 matrix", + "status": "completed", + "classification": "high-risk", + "parallelizableWith": [], + "blockedBy": [ + 18, + 19, + 20 + ] + }, + { + "id": 22, + "subject": "Task 22 (P2): ITagDiscovery UntilStable + IRediscoverable (rediscover-on-DBIRTH)", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 21 + ] + }, + { + "id": 23, + "subject": "Task 23 (P2): Sparkplug browser tree + AttributesAsync + scoped RequestRebirthAsync", + "status": "completed", + "classification": "standard", + "parallelizableWith": [ + 24 + ], + "blockedBy": [ + 20, + 21 + ] + }, + { + "id": 24, + "subject": "Task 24 (P2): AdminUI editor Sparkplug mode shape + validator", + "status": "completed", + "classification": "small", + "parallelizableWith": [ + 23 + ], + "blockedBy": [ + 12, + 17 + ] + }, + { + "id": 25, + "subject": "Task 25 (P2): C# edge-node simulator fixture + 3.6 live matrix", + "status": "completed", + "classification": "standard", + "parallelizableWith": [], + "blockedBy": [ + 21, + 22, + 23 + ] + }, + { + "id": 26, + "subject": "Task 26 (P2): Live /run verify Sparkplug \u2014 P2 COMPLETE", + "status": "completed", + "classification": "small", + "parallelizableWith": [], + "blockedBy": [ + 23, + 24, + 25 + ] + } ], - "lastUpdated": "2026-07-24" + "lastUpdated": "2026-07-27", + "closureNote": "All 27 tasks shipped, both phases live-gated. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-24-mtconnect-driver.md.tasks.json b/docs/plans/2026-07-24-mtconnect-driver.md.tasks.json index 209c1642..4a432eca 100644 --- a/docs/plans/2026-07-24-mtconnect-driver.md.tasks.json +++ b/docs/plans/2026-07-24-mtconnect-driver.md.tasks.json @@ -1,29 +1,199 @@ { "planPath": "docs/plans/2026-07-24-mtconnect-driver.md", "tasks": [ - {"id": 0, "subject": "Task 0: Verify TrakHound MTConnect.NET pin (or record the hand-roll decision)", "status": "pending"}, - {"id": 1, "subject": "Task 1: Scaffold the two driver projects + the test project", "status": "pending", "blockedBy": [0]}, - {"id": 2, "subject": "Task 2: MTConnectDriverOptions + MTConnectTagDefinition in .Contracts", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: MTConnectDataTypeInference.Infer + golden type-map test", "status": "pending", "blockedBy": [1]}, - {"id": 4, "subject": "Task 4: Capture the canned XML fixtures", "status": "pending", "blockedBy": [1]}, - {"id": 5, "subject": "Task 5: IMTConnectAgentClient seam + return DTOs", "status": "pending", "blockedBy": [1]}, - {"id": 6, "subject": "Task 6: MTConnectAgentClient — parse /probe into the device model", "status": "pending", "blockedBy": [4, 5]}, - {"id": 7, "subject": "Task 7: MTConnectAgentClient — parse /current + /sample, detect the sequence gap", "status": "pending", "blockedBy": [4, 5, 6]}, - {"id": 8, "subject": "Task 8: MTConnectObservationIndex + UNAVAILABLE -> BadNoCommunication", "status": "pending", "blockedBy": [7]}, - {"id": 9, "subject": "Task 9: MTConnectDriver shell — IDriver lifecycle", "status": "pending", "blockedBy": [2, 8]}, - {"id": 10, "subject": "Task 10: IReadable.ReadAsync — /current, ordered snapshots", "status": "pending", "blockedBy": [9]}, - {"id": 11, "subject": "Task 11: ISubscribable — /sample long-poll pump + ring-buffer re-baseline", "status": "pending", "blockedBy": [9, 7]}, - {"id": 12, "subject": "Task 12: ITagDiscovery.DiscoverAsync + SupportsOnlineDiscovery=true", "status": "pending", "blockedBy": [9, 6]}, - {"id": 13, "subject": "Task 13: IHostConnectivityProbe + IRediscoverable (instanceId watch)", "status": "pending", "blockedBy": [9]}, - {"id": 14, "subject": "Task 14: MTConnectDriverProbe : IDriverProbe", "status": "pending", "blockedBy": [2, 5]}, - {"id": 15, "subject": "Task 15: MTConnectDriverFactoryExtensions", "status": "pending", "blockedBy": [2, 9]}, - {"id": 16, "subject": "Task 16: Host registration + DriverTypeNames.MTConnect + guard-test parity", "status": "pending", "blockedBy": [14, 15]}, - {"id": 17, "subject": "Task 17: AdminUI typed model MTConnectTagConfigModel", "status": "pending", "blockedBy": [3, 16]}, - {"id": 18, "subject": "Task 18: AdminUI editor razor + map/validator registration", "status": "pending", "blockedBy": [17]}, - {"id": 19, "subject": "Task 19: mtconnect/cppagent docker fixture", "status": "pending", "blockedBy": [16]}, - {"id": 20, "subject": "Task 20: Env-gated integration suite against cppagent", "status": "pending", "blockedBy": [19]}, - {"id": 21, "subject": "Task 21: Live /run verify on docker-dev — browse picker, editor, read, subscribe, deploy", "status": "pending", "blockedBy": [18, 20]}, - {"id": 22, "subject": "Task 22: Docs + deferred-writeback note; update the tracking doc", "status": "pending", "blockedBy": [21]} + { + "id": 0, + "subject": "Task 0: Verify TrakHound MTConnect.NET pin (or record the hand-roll decision)", + "status": "completed" + }, + { + "id": 1, + "subject": "Task 1: Scaffold the two driver projects + the test project", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 2, + "subject": "Task 2: MTConnectDriverOptions + MTConnectTagDefinition in .Contracts", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "subject": "Task 3: MTConnectDataTypeInference.Infer + golden type-map test", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 4, + "subject": "Task 4: Capture the canned XML fixtures", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 5, + "subject": "Task 5: IMTConnectAgentClient seam + return DTOs", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 6, + "subject": "Task 6: MTConnectAgentClient \u2014 parse /probe into the device model", + "status": "completed", + "blockedBy": [ + 4, + 5 + ] + }, + { + "id": 7, + "subject": "Task 7: MTConnectAgentClient \u2014 parse /current + /sample, detect the sequence gap", + "status": "completed", + "blockedBy": [ + 4, + 5, + 6 + ] + }, + { + "id": 8, + "subject": "Task 8: MTConnectObservationIndex + UNAVAILABLE -> BadNoCommunication", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 9, + "subject": "Task 9: MTConnectDriver shell \u2014 IDriver lifecycle", + "status": "completed", + "blockedBy": [ + 2, + 8 + ] + }, + { + "id": 10, + "subject": "Task 10: IReadable.ReadAsync \u2014 /current, ordered snapshots", + "status": "completed", + "blockedBy": [ + 9 + ] + }, + { + "id": 11, + "subject": "Task 11: ISubscribable \u2014 /sample long-poll pump + ring-buffer re-baseline", + "status": "completed", + "blockedBy": [ + 9, + 7 + ] + }, + { + "id": 12, + "subject": "Task 12: ITagDiscovery.DiscoverAsync + SupportsOnlineDiscovery=true", + "status": "completed", + "blockedBy": [ + 9, + 6 + ] + }, + { + "id": 13, + "subject": "Task 13: IHostConnectivityProbe + IRediscoverable (instanceId watch)", + "status": "completed", + "blockedBy": [ + 9 + ] + }, + { + "id": 14, + "subject": "Task 14: MTConnectDriverProbe : IDriverProbe", + "status": "completed", + "blockedBy": [ + 2, + 5 + ] + }, + { + "id": 15, + "subject": "Task 15: MTConnectDriverFactoryExtensions", + "status": "completed", + "blockedBy": [ + 2, + 9 + ] + }, + { + "id": 16, + "subject": "Task 16: Host registration + DriverTypeNames.MTConnect + guard-test parity", + "status": "completed", + "blockedBy": [ + 14, + 15 + ] + }, + { + "id": 17, + "subject": "Task 17: AdminUI typed model MTConnectTagConfigModel", + "status": "completed", + "blockedBy": [ + 3, + 16 + ] + }, + { + "id": 18, + "subject": "Task 18: AdminUI editor razor + map/validator registration", + "status": "completed", + "blockedBy": [ + 17 + ] + }, + { + "id": 19, + "subject": "Task 19: mtconnect/cppagent docker fixture", + "status": "completed", + "blockedBy": [ + 16 + ] + }, + { + "id": 20, + "subject": "Task 20: Env-gated integration suite against cppagent", + "status": "completed", + "blockedBy": [ + 19 + ] + }, + { + "id": 21, + "subject": "Task 21: Live /run verify on docker-dev \u2014 browse picker, editor, read, subscribe, deploy", + "status": "completed", + "blockedBy": [ + 18, + 20 + ] + }, + { + "id": 22, + "subject": "Task 22: Docs + deferred-writeback note; update the tracking doc", + "status": "completed", + "blockedBy": [ + 21 + ] + } ], - "lastUpdated": "2026-07-24" + "lastUpdated": "2026-07-27", + "closureNote": "Merged at master 90bdaa44 (#506); 5-leg live gate PASSED. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/plans/2026-07-24-sql-poll-driver.md.tasks.json b/docs/plans/2026-07-24-sql-poll-driver.md.tasks.json index 55e57b84..c1553974 100644 --- a/docs/plans/2026-07-24-sql-poll-driver.md.tasks.json +++ b/docs/plans/2026-07-24-sql-poll-driver.md.tasks.json @@ -1,28 +1,191 @@ { "planPath": "docs/plans/2026-07-24-sql-poll-driver.md", "tasks": [ - {"id": 0, "subject": "Task 0: Scaffold Driver.Sql.Contracts project + register in slnx", "status": "pending"}, - {"id": 1, "subject": "Task 1: SqlProvider/SqlTagModel enums + SqlDriverConfigDto", "status": "pending", "blockedBy": [0]}, - {"id": 2, "subject": "Task 2: SqlTagDefinition + SqlEquipmentTagParser (strict enums, malicious-input safe)", "status": "pending", "blockedBy": [1]}, - {"id": 3, "subject": "Task 3: Scaffold Driver.Sql runtime project + register in slnx", "status": "pending", "blockedBy": [0]}, - {"id": 4, "subject": "Task 4: ISqlDialect + SqlServerDialect (QuoteIdentifier, catalog SQL, MapColumnType)", "status": "pending", "blockedBy": [1, 3]}, - {"id": 5, "subject": "Task 5: SqlQueryPlan grouping + parameter binding (pure)", "status": "pending", "blockedBy": [2, 4]}, - {"id": 6, "subject": "Task 6: Scaffold Driver.Sql.Tests + SqliteDialect + SqlitePollFixture", "status": "pending", "blockedBy": [3, 4]}, - {"id": 7, "subject": "Task 7: SqlPollReader core — grouped read, bounded deadline, ordered slice-back", "status": "pending", "blockedBy": [5, 6]}, - {"id": 8, "subject": "Task 8: SqlDriver shell — IDriver/ITagDiscovery/IReadable/ISubscribable/IHostConnectivityProbe", "status": "pending", "blockedBy": [7]}, - {"id": 9, "subject": "Task 9: SqlDriverFactoryExtensions + connectionStringRef env resolution + enum guard", "status": "pending", "blockedBy": [8]}, - {"id": 10, "subject": "Task 10: SqlDriverProbe (SELECT 1 liveness)", "status": "pending", "blockedBy": [4, 6]}, - {"id": 11, "subject": "Task 11: Host registration — DriverTypeNames.Sql + factory + probe + guard test", "status": "pending", "blockedBy": [9, 10]}, - {"id": 12, "subject": "Task 12: Scaffold Driver.Sql.Browser project + register in slnx", "status": "pending", "blockedBy": [3]}, - {"id": 13, "subject": "Task 13: SqlBrowseSession — schema walk over dialect catalog", "status": "pending", "blockedBy": [4, 12, 6]}, - {"id": 14, "subject": "Task 14: SqlDriverBrowser — env-ref/literal transient-connection open", "status": "pending", "blockedBy": [13]}, - {"id": 15, "subject": "Task 15: AdminUI browser DI + SqlAddressPickerBody.razor", "status": "pending", "blockedBy": [14]}, - {"id": 16, "subject": "Task 16: Env-gated central-SQL integration fixture + read round-trip", "status": "pending", "blockedBy": [8]}, - {"id": 17, "subject": "Task 17: Injection regression test (bind value / reject identifier)", "status": "pending", "blockedBy": [7]}, - {"id": 18, "subject": "Task 18: Blackhole/timeout live-gate on a dedicated mssql container", "status": "pending", "blockedBy": [16]}, - {"id": 19, "subject": "Task 19: AdminUI typed Sql tag-config model + validator (string enums)", "status": "pending", "blockedBy": [1, 11]}, - {"id": 20, "subject": "Task 20: SqlTagConfigEditor.razor shell", "status": "pending", "blockedBy": [19]}, - {"id": 21, "subject": "Task 21: Live /run verification on docker-dev (picker + editor + deploy + read)", "status": "pending", "blockedBy": [11, 15, 20]} + { + "id": 0, + "subject": "Task 0: Scaffold Driver.Sql.Contracts project + register in slnx", + "status": "completed" + }, + { + "id": 1, + "subject": "Task 1: SqlProvider/SqlTagModel enums + SqlDriverConfigDto", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 2, + "subject": "Task 2: SqlTagDefinition + SqlEquipmentTagParser (strict enums, malicious-input safe)", + "status": "completed", + "blockedBy": [ + 1 + ] + }, + { + "id": 3, + "subject": "Task 3: Scaffold Driver.Sql runtime project + register in slnx", + "status": "completed", + "blockedBy": [ + 0 + ] + }, + { + "id": 4, + "subject": "Task 4: ISqlDialect + SqlServerDialect (QuoteIdentifier, catalog SQL, MapColumnType)", + "status": "completed", + "blockedBy": [ + 1, + 3 + ] + }, + { + "id": 5, + "subject": "Task 5: SqlQueryPlan grouping + parameter binding (pure)", + "status": "completed", + "blockedBy": [ + 2, + 4 + ] + }, + { + "id": 6, + "subject": "Task 6: Scaffold Driver.Sql.Tests + SqliteDialect + SqlitePollFixture", + "status": "completed", + "blockedBy": [ + 3, + 4 + ] + }, + { + "id": 7, + "subject": "Task 7: SqlPollReader core \u2014 grouped read, bounded deadline, ordered slice-back", + "status": "completed", + "blockedBy": [ + 5, + 6 + ] + }, + { + "id": 8, + "subject": "Task 8: SqlDriver shell \u2014 IDriver/ITagDiscovery/IReadable/ISubscribable/IHostConnectivityProbe", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 9, + "subject": "Task 9: SqlDriverFactoryExtensions + connectionStringRef env resolution + enum guard", + "status": "completed", + "blockedBy": [ + 8 + ] + }, + { + "id": 10, + "subject": "Task 10: SqlDriverProbe (SELECT 1 liveness)", + "status": "completed", + "blockedBy": [ + 4, + 6 + ] + }, + { + "id": 11, + "subject": "Task 11: Host registration \u2014 DriverTypeNames.Sql + factory + probe + guard test", + "status": "completed", + "blockedBy": [ + 9, + 10 + ] + }, + { + "id": 12, + "subject": "Task 12: Scaffold Driver.Sql.Browser project + register in slnx", + "status": "completed", + "blockedBy": [ + 3 + ] + }, + { + "id": 13, + "subject": "Task 13: SqlBrowseSession \u2014 schema walk over dialect catalog", + "status": "completed", + "blockedBy": [ + 4, + 12, + 6 + ] + }, + { + "id": 14, + "subject": "Task 14: SqlDriverBrowser \u2014 env-ref/literal transient-connection open", + "status": "completed", + "blockedBy": [ + 13 + ] + }, + { + "id": 15, + "subject": "Task 15: AdminUI browser DI + SqlAddressPickerBody.razor", + "status": "completed", + "blockedBy": [ + 14 + ] + }, + { + "id": 16, + "subject": "Task 16: Env-gated central-SQL integration fixture + read round-trip", + "status": "completed", + "blockedBy": [ + 8 + ] + }, + { + "id": 17, + "subject": "Task 17: Injection regression test (bind value / reject identifier)", + "status": "completed", + "blockedBy": [ + 7 + ] + }, + { + "id": 18, + "subject": "Task 18: Blackhole/timeout live-gate on a dedicated mssql container", + "status": "completed", + "blockedBy": [ + 16 + ] + }, + { + "id": 19, + "subject": "Task 19: AdminUI typed Sql tag-config model + validator (string enums)", + "status": "completed", + "blockedBy": [ + 1, + 11 + ] + }, + { + "id": 20, + "subject": "Task 20: SqlTagConfigEditor.razor shell", + "status": "completed", + "blockedBy": [ + 19 + ] + }, + { + "id": 21, + "subject": "Task 21: Live /run verification on docker-dev (picker + editor + deploy + read)", + "status": "completed", + "blockedBy": [ + 11, + 15, + 20 + ] + } ], - "lastUpdated": "2026-07-24" + "lastUpdated": "2026-07-27", + "closureNote": "Merged 4ad54037 + follow-ups 28c28667. Marked complete 2026-07-27 (deferment.md \u00a78.5)." } diff --git a/docs/v2/driver-stability.md b/docs/v2/driver-stability.md index 55eb91a9..b1d70a26 100644 --- a/docs/v2/driver-stability.md +++ b/docs/v2/driver-stability.md @@ -1,5 +1,25 @@ # Driver Stability & Isolation — OtOpcUa v2 +> ⚠️ **The tier assignments below are NOT what runs (verified against source 2026-07-27).** +> **Every one of the 12 drivers runs as Tier A.** `DriverFactoryRegistry` defaults `DriverTier tier = +> DriverTier.A` and **no factory in `src/Drivers/` passes a tier**, so nothing ever selects B or C. The +> Galaxy and FOCAS Tier-C assignments in the table below are aspirational. +> +> Consequences worth knowing: +> +> - The **Tier-C-only protections are dormant for every driver** — `MemoryRecycle` gates on +> `when _tier == DriverTier.C`, and `ScheduledRecycleScheduler` refuses unless Tier C. Neither has ever +> engaged in production. `IDriverSupervisor` has zero implementations, yet the config parser still +> validates `RecycleIntervalSeconds`, so an operator can author a recycle interval that does nothing. +> - `DriverTypeRegistry` — which this document's model implies is the tier authority — is **vestigial**: +> referenced only by its own test, with nothing registering metadata at startup. +> - The **separate-Windows-service hosting** described for Tier C does not exist either. Galaxy reaches +> MXAccess over gRPC to the external **mxaccessgw** sidecar; the `Galaxy.Proxy`/`Host`/`Shared` pattern +> named below was retired in PR 7.2, and FOCAS runs in-process like everything else. +> +> `docs/drivers/README.md` says Tier A for Galaxy and FOCAS and is the accurate one. Keep-or-delete of the +> tier machinery is tracked as Gitea **#522**; this document is retained as the design record. +> > **Status**: DRAFT — companion to `plan.md`. Defines the stability tier model, per-driver hosting decisions, cross-cutting protections every driver process must apply, and the canonical worked example (FOCAS) for the high-risk tier. > > **Branch**: `v2`