# 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
  (`Phase7Composer` → `EquipmentVirtualTagPlan` → `VirtualTagHostActor` → `VirtualTagActor`) 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 `Acknowledge` → `IAlarmSource.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 (4a–4i), 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 3–10 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.

## Recommended defer (YAGNI) — flagged, not silently dropped

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`.
