Files
lmxopcua/docs/plans/2026-06-15-stillpending-backlog-design.md
T

8.5 KiB
Raw Blame History

Still-Pending Backlog — phased completion design

Status: approved 2026-06-15. Source backlog: stillpending.md (system-wide audit, master 151b7165). Scope: the full actionable backlog — stillpending.md §1–§5 + §7/§9 cleanup. Excludes §6 (by-design / backend-gated limitations) and §8 (live-/run gates, which are user-driven), plus the recommended-defer list below. Planning cadence: plan-and-execute one phase at a time — author each phase's implementation plan when we reach it (no up-front 60-task monolith).

Goal

Close every genuine deferred / partial / missing functional gap catalogued in stillpending.md, in priority order, without a schema migration and without regressing the actor model.

Delivery approach

  • Phased feature branches. One phase = one branch off master, merged back after green dotnet build + dotnet test + the classification-driven review chain (trivial→implementer only; small→code review; standard→spec ∥ code review; high-risk→serial reviews + final integration review).
  • Live /run gates stay user-driven. The user drives docker-dev; the agent never signs in. Razor/JS changes are proven only by live /run, never bUnit.
  • Plan-and-execute phase-by-phase. Later phases (drivers, AdminUI) depend on what the early phases settle; authoring all plans up front would be stale on arrival.

Rejected alternatives: one long-lived mega-branch (un-reviewable, conflict magnet); parallel per-area branches (the deploy-path and driver phases share Phase7* / DriverHostActor, so they collide).

Hard constraints (carried into every phase)

  • NO Configuration entity / EF migration. The audit confirms the whole backlog is achievable without one (e.g. VirtualTag.Historize already exists; the HistoryUpdate bit is a Core [Flags] enum, not EF; isHistorized / native HistorizeToAveva ride the TagConfig JSON blob).
  • Stage by path — never git add .. Never stage sql_login.txt, src/Server/.../Host/pki/, pending.md, current.md, docker-dev/docker-compose.yml. Never echo or commit secrets. No force-push, no --no-verify.
  • Tests: xUnit + Shouldly, TDD fail-then-pass; in-memory EF where DB-backed. No bUnit.

Phased roadmap

Phase 0 — Hygiene & no-risk cleanup (trivial / small)

  • §9 stale comments (7 spots: DriverHostActor.cs:45, OpcUaPublishActor.cs:246-252, ServiceCollectionExtensions.cs:66-67, IScriptLogPublisher.cs:8 + ScriptLogTopicSink.cs:10, ScriptAnalysisService.cs:21-23, the Galaxy guard-message refs) + the docs/security.md write-outcome section (B1 already shipped both halves).
  • §7 — mark the confirmed-shipped .tasks.json files completed so future audits don't re-flag them.
  • §3 low-consequence residue — document/verify-benign DraftSnapshotFactory placeholders and Cluster LeaderChanged no-op (no behavioral change expected).

Phase 1 — Silent-deploy bugs (H1 + H5) (high-risk)

  • H1 (conservative): add the Changed* counts to Phase7Applier.needsRebuild; make VirtualTagHostActor.OnApply stop+respawn children whose plan changed (so an edited Expression / DependencyRefs takes effect). A rebuild repopulates from the persisted artifact and is idempotent.
  • H5: thread VirtualTag.Historize through the equipment-namespace path (Phase7ComposerEquipmentVirtualTagPlanVirtualTagHostActorVirtualTagActor) and wire a real production IHistoryWriter (today drops to NullHistoryWriter).
  • Verify on the 2-node / docker-dev rig (user-driven /run).

Phase 2 — Redundancy ServiceLevel (H3) (high-risk)

  • Feed DbHealthProbeActor / PeerOpcUaProbeActor health into ServiceLevelCalculator.Compute (already written, never invoked) → published Server.ServiceLevel byte via RedundancyStateActor. A DB-unreachable / probe-failed primary must drop below its role-based level.
  • Unit tests can't catch the wiring (per memory) — verify on the 2-node rig.

Phase 3 — OPC UA standards completeness (H4 + H2-bit + H6) (high-risk)

  • H4: OnEnableDisable node-manager seam → alarm-commands DPS topic (engine + AdminUI already handle Enable/Disable; only the OPC UA half is missing).
  • H2 (permission bit only): add NodePermissions.HistoryUpdate and fix the TriePermissionEvaluator:86 mapping (today a HistoryRead grant would also authorize a future HistoryUpdate). The full HistoryUpdate service is deferred (infra-gated — no backend RPC, like modified-value history).
  • H6: inbound native-alarm AcknowledgeIAlarmSource.AcknowledgeAsync → AVEVA, recording the authenticated principal rather than a generic "opcua-client".

Phase 4 — Driver data-type & structure coverage (standard, parallelizable per-driver)

Splits into per-driver sub-plans (4a4i), each independent:

  • S7 wide types (Int64/UInt64/LReal/String/DateTime) + Timer/Counter areas.
  • Modbus Int64/UInt64 node DataType (add Int64 to DriverDataType) + String/BitInRegister arrays.
  • Array IsArray discovery for S7 / AbCip / AbLegacy / TwinCAT.
  • AbCip + TwinCAT UDT member-path reads/writes.
  • AbLegacy + TwinCAT BOOL-within-word (bit-index) writes (read-modify-write).
  • FOCAS position scaling (10^DecimalPlaces divide) + Unimplemented…Factory fail at config time, not per-read.
  • Galaxy nested gobject hierarchy + writer item-handle cache shared with the subscription registry.
  • Historian.Wonderware Total aggregate + poison-event dead-letter (don't retry forever).
  • OpcUaClient IHistoryProvider.ReadEventsAsync event-history passthrough.

Phase 5 — Test-Connect protocol probes (small)

  • Replace TCP-only probes with real handshakes: Modbus FC, FOCAS cnc_allclibhndl3, TwinCAT ADS-state, OpcUaClient session-open, historian handshake (§2 probes + plan 2026-05-28-adminui-driver-pages Phase 7 + 2026-06-12-historian-tcp-transport task 9).

Phase 6 — AdminUI typed editors, pickers & UX (standard; Razor → live-/run only, NO bUnit)

  • OpcUaClient + Historian.Wonderware typed TagConfig editors in the /uns TagModal.
  • Driver-tag isHistorized / historianTagname first-class field.
  • Native-alarm HistorizeToAveva opt-out (TagConfig + UI) — §5.
  • Galaxy picker pre-fills alarm fields from DriverAttributeInfo.IsAlarm — §5.
  • Typed address pickers for Modbus / S7 / AbCip / AbLegacy / TwinCAT / FOCAS (plan Phase 9).
  • UNS-tree Delete for Enterprise / Cluster node kinds.
  • Hosts page per-driver-instance rows.
  • Monaco ctx.SetVirtualTag(...) write-target completions/hover; create-new-script from the inline panel.

Phase 7 — Client.UI alarm controls (small)

  • AlarmsView per-row Acknowledge + Shelve + Confirm commands/buttons (backends + CLI already exist).

Phase 8 — Per-cluster scoping (high-risk, standalone)

  • Execute tasks 310 of the existing 2026-06-07-per-cluster-scoping plan (SubscribeBulk cluster-filtering, OpcUaPublishActor scoping on rebuild, multi-cluster E2E harness, docker-dev rewrite, verify, docs). Reference/execute that plan rather than re-designing here.

Forward-looking reservations or out-of-repo; left out unless explicitly pulled back in:

  • AuthorizationVerdict.Denied — v2.1 explicit-deny; no authoring path exists.
  • NamespaceKind.Simulated, Script.Language — future driver / scripting engine that don't exist yet.
  • Monaco InlayHints — parameter-name hints; likely intended-permanent stub.
  • The full HistoryUpdate service — infra-gated (no backend insert/replace/delete RPC).
  • Galaxy ExecuteWrite fire-and-forget write-outcome — lives in the mxaccessgw sibling repo, not here.
  • Durable AVEVA virtual-tag history sink (the concrete IHistoryWriter behind H5) — infra-gated: the Wonderware historian sidecar exposes only HistoryRead + alarm-event writes, no live-data WriteDataValues RPC. H5 (Phase 1) wired the injectable seam with a NullHistoryWriter default; the durable sink needs a sidecar RPC first (same class of constraint as the HistoryUpdate service).

Verification per phase

  • Each phase: red→green unit tests for the load-bearing logic; dotnet build clean (production projects are TreatWarningsAsErrors); full dotnet test green before merge.
  • High-risk phases (1, 2, 3, 8) additionally gated on a user-driven live /run on docker-dev (and the 2-node rig for redundancy in Phase 2).
  • AdminUI (Phase 6) and Client.UI (Phase 7) Razor/XAML changes proven only by live /run.