From 7ec5b95d24e235ef72e81ab180a4f5e6a412ef08 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 6 Nov 2026 15:31:43 -0500 Subject: [PATCH] docs: point markdown backlogs at Gitea issues (status lives in Gitea) --- CLAUDE.md | 7 +++++++ archreview/plans/INTEGRATION-SWEEP-STATUS.md | 5 ++++- archreview/plans/STATUS.md | 7 +++++-- deferment.md | 5 ++++- docs/ScriptEditor.md | 15 +++++++++------ docs/Uns.md | 5 ++++- docs/VirtualTags.md | 5 ++++- docs/drivers/AbLegacy.md | 5 ++++- docs/drivers/Mqtt.md | 5 ++++- docs/drivers/Sql.md | 9 ++++++--- .../2026-10-10-questdb-historian-live-gate.md | 5 ++++- .../03-host-controlplane-security-config.md | 5 ++++- docs/reviews/2026-10-21/05-core.md | 5 ++++- docs/reviews/2026-10-21/06-drivers-session.md | 5 +++++ docs/reviews/2026-10-21/07-drivers-plc.md | 5 ++++- docs/security.md | 5 ++++- .../plans/2026-10-22-review-remediation.md | 11 +++++++---- docs/v3/twincat-backlog.md | 15 +++++++++------ 18 files changed, 92 insertions(+), 32 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c5e8ff56..48330704 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,6 +90,13 @@ stranded on a feature branch, cherry-pick them onto `main` rather than merging t > into the SQL Servers. Deploy steps that need a command on the box are blocked; see the umbrella > [`../scadaproj/CLAUDE.md`](../scadaproj/CLAUDE.md) for the current hold. +## Backlog / open work + +Open work lives in **Gitea issues** on `gitea.dohertylan.com/dohertj2/lmxopcua` (`tea issues list --repo dohertj2/lmxopcua`) — they are the single system of record for status. +The markdown backlogs — `deferment.md`, `docs/v3/twincat-backlog.md`, `archreview/plans/STATUS.md`, `archreview/plans/INTEGRATION-SWEEP-STATUS.md`, the `docs/reviews/2026-10-21/` reports + `docs/superpowers/plans/2026-10-22-review-remediation.md`, and the known-gaps/follow-ups sections of `docs/drivers/*.md`, `docs/ScriptEditor.md`, `docs/Uns.md`, `docs/security.md`, `docs/VirtualTags.md` — are historical/design records carrying `→ #N` pointers (2026-11-06 sweep, label `migrated`, #749–#759). Do not track status in them. +File every new follow-up, deferral or review finding as an issue with the family labels `type:{bug,feature,followup,review-finding,deploy-gate,debt,docs,test,infra}`, `priority:p0..p3`, plus `blocked` / `cross-repo` where they apply. +Reference issues from plans and docs as `#N` (cross-repo: `mxaccessgw#N`). + ## Subsystem guides (lazy-loaded — read before touching the area) Detailed gotchas live in path-scoped files under `.claude/rules/`, which load automatically when you work on diff --git a/archreview/plans/INTEGRATION-SWEEP-STATUS.md b/archreview/plans/INTEGRATION-SWEEP-STATUS.md index 473083c5..9fc5c07c 100644 --- a/archreview/plans/INTEGRATION-SWEEP-STATUS.md +++ b/archreview/plans/INTEGRATION-SWEEP-STATUS.md @@ -1,5 +1,8 @@ # Integration-test sweep — status + follow-ups (2026-07-14) +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + **Context:** ran the deferred `*.IntegrationTests` sweep (the round-2 STATUS.md "Full integration-suite sweep" item that discharges R2-02 T18, R2-03 T11, R2-07 T5/T6, R2-08 T14, R2-11 T24). **How:** tests run **on the Mac** (repo + .NET 10 SDK), pointing at **driver fixtures brought up on the @@ -21,7 +24,7 @@ need sudo to create new ones — I deployed FOCAS to `~/otopcua-focas` on the ho | **OpcUaClient.Browser** | ✅ 1/1 | — | | **S7** | ✅ 3/3 (2 skip) | skips = outage tests needing `S7_TIMEOUT_OUTAGE_*` env | | **Modbus** (profile=standard) | ✅ 6/6 | — | -| **Host** | ⚠️ **105/117** (7F, 5 skip) | 7 heavy E2E time out under amd64-emulated SQL (OPEN); LDAP image swapped OpenLDAP→GLAuth 2026-07-15 (PR #451). The 5 skips are driver-fixture-gated E2E, not LDAP | +| **Host** | ⚠️ **105/117** (7F, 5 skip) | 7 heavy E2E time out under amd64-emulated SQL (OPEN → #757); LDAP image swapped OpenLDAP→GLAuth 2026-07-15 (PR #451). The 5 skips are driver-fixture-gated E2E, not LDAP | | **TwinCAT** | ⏭️ 13 skip | no TC3 XAR fixture exists (expected clean-skip) | | **Modbus** (dl205 / mitsubishi / s7_1500 / exception_injection) | ✅ 16/13/10/17 (was 8F/5F/3F/8F) | **NOT drift** — fixture-cycling artifact, re-verified GREEN 2026-07-15 — see below | | **AbCip** | ✅ 10P/3skip (was 6F/1P/3skip) | static-init-order bug FIXED 2026-07-15 (`326b22a7`) — see below | diff --git a/archreview/plans/STATUS.md b/archreview/plans/STATUS.md index e5a5fe76..a8bb94f8 100644 --- a/archreview/plans/STATUS.md +++ b/archreview/plans/STATUS.md @@ -1,5 +1,8 @@ # Arch-Review Remediation — Status & Handoff +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + **Snapshot:** 2026-07-12 · **Review baseline:** master `9cad9ed0`; **re-review baseline:** master `f6eaa267` · this file is the single source of truth for remediation progress. Update it as work lands. @@ -161,9 +164,9 @@ removed from `v2-ci.yml`), 07/U-3 (5 alarm operator CLI commands documented — + blocker comment; upstream-blocked — no xunit.v3 Akka TestKit exists). 07/U-6 needed no action (the Wonderware husk dirs are untracked build artifacts, absent in a fresh worktree). Whole-solution build clean. **OPEN follow-ups:** (a) when a runner-reachable fleet + a real `tests/Server/…E2ETests` land, -add a fresh E2E workflow WITH a zero-match guard (fail if `Category=E2E` matches nothing); (b) S-2 +add a fresh E2E workflow WITH a zero-match guard (fail if `Category=E2E` matches nothing) → #757; (b) S-2 option #2 — a fixture-backed integration leg (opc-plc `services:` block, `MIN_EXECUTED=1`) covering -what the unit leg cannot; (c) migrate the 3 xunit v2 holdouts once Akka ships an xunit.v3 TestKit. +what the unit leg cannot → #757; (c) migrate the 3 xunit v2 holdouts once Akka ships an xunit.v3 TestKit → #756. ### Round-2 remediation landed diff --git a/deferment.md b/deferment.md index 93b16382..1b959598 100644 --- a/deferment.md +++ b/deferment.md @@ -1,5 +1,8 @@ # Deferment register — new drivers, open issues, deferred work +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + > **Scope.** A source-verified inventory of what is unfinished in this repo: the status of the > recently-added drivers, every open tracker issue, in-code deferrals, open live gates, and the > documentation that misstates any of it. @@ -60,7 +63,7 @@ So everything below is **untracked work**. Nothing in it is a data-loss or outag | ~~**G-7 residual** — Sql / MTConnect / Calculation / OpcUaClient have no Contracts-side `Inspect`~~ ✅ **CLOSED 2026-08-09** (`2947a2b9` + `1469fbb4`, merge `0695f682`). All four now ship a Contracts-side `*TagDefinitionFactory.Inspect` **wrapping the runtime parse, never mirroring the AdminUI model** — Sql wraps the pre-existing `SqlEquipmentTagParser`; the MTConnect / OpcUaClient / Calculation blob readers were **moved** into their Contracts assemblies (the drivers now call them), so never-stricter-than-runtime is structural. `Driver.Calculation.Contracts` was created (no Contracts assembly existed). The reflection coverage guard was falsified (map entry removed → red naming `SqlTagDefinitionFactory`), and `TagConfigAuthoringDeployParityTests` pins authoring-accepts ⇒ deploy-accepts. **Galaxy is now the sole unmapped type, deliberately + symmetrically** (no typed authoring editor either). ~~Found en route, left open: `MTConnectTagConfigModel` reads `dataType` leniently (misspelled name → silently `String`) while the runtime rejects it — authoring weaker than runtime, the *opposite* direction from G-7; documented inline in the parity tests.~~ ✅ Filed and FIXED same day as **#549** — and it was 6 models wide, not 1 (see the "2026-08-09 (later)" §9 entry); residuals **#550**/**#551** are ✅ **CLOSED 2026-08-10** (merges `b50523b1` + `333ea488`, see the 2026-08-10 §9 entry) — their own residuals are **#552** (Sql browse-commit emits a config the gate rejects) / **#553** (CSV typed-column merge silently rewrites typo'd enums pre-gate). | ~~The deploy gate is weaker than the authoring gate.~~ Config arriving by CSV import or API now meets the same checks the modal applies. | §1.2 | | ~~**3 carried-open archreview findings** — the failure-visibility trio~~ **Update 2026-08-07 (truth sweep): mostly CLOSED.** 01/S-1 **FIXED** — `AddressSpaceApplier.cs:95-97` tallies swallowed removal-pass failures into `AddressSpaceApplyOutcome.FailedNodes` (comment cites 01/S-1). 03/S4 **FIXED** — `PrimaryGatePolicy.cs` default-denies on unknown role when a driver peer exists (doc-comment "Fixes archreview 03/S4"), live-proven R2-04 T15. 06/S-1 **PARTIAL** — fail-closed advise path + `galaxy.writes.unconfirmed` meter shipped in `GatewayGalaxyDataWriter.cs`; residual = gateway-side WriteComplete correlation (cross-repo, mxaccessgw). **Update 2026-08-09: the gateway half SHIPPED** — mxaccessgw main `b948e69` correlates the worker's `OnWriteComplete` onto the unary `WriteSecured` reply (`statuses[0]`; bounded wait default **1.5 s** — deliberately under OtOpcUa's 2 s Tier A Write budget, configurable via `MxGateway:Worker:WriteCompletionWaitMilliseconds`; timeout falls back to today's empty-statuses shape, still metered unconfirmed). **Update 2026-08-09 (later): 06/S-1 CLOSED — live-gated end to end, and the gate found + fixed three defects.** The user-authorized redeploy exposed (1) the correlation covered `WriteSecured` only while OtOpcUa's dominant no-login path emits plain `Write` — fixed gateway-side (worker `53f69cd`, all four unary write kinds, redeployed both hosts); (2) OtOpcUa's `StatusCodeMap.FromMxStatus` truncated the correlated failure row's MxStatus detail code to a byte (`1007 & 0xFF = 239` → OPC DA Good band), mapping a REFUSED write to Good — fixed with the not-IsSuccess-never-Good invariant + category routing (`SECURITY_ERROR → BadUserAccessDenied`); (3) the writer's borrow-from-subscription-registry optimization could never commit (a subscription handle is plain-advised; MXAccess refuses the no-login write `SECURITY_ERROR 1008` AND the advise upgrade `E_INVALIDARG` — one advise per HANDLE, not per attribute) — borrow path REMOVED, writer always owns a supervisory-advised handle. Live legs all PASSED on windev: happy write Good off confirmed `statuses[0]` + both meters flat; read-only refusal surfaces Bad-family + node-revert input; `WriteCompletionWaitMilliseconds=0` control → provisional Good + metered unconfirmed (override removed after). Pinned by `Live_correlated_write_statuses_confirm_commit_and_surface_refusal`, `Live_write_to_subscribed_tag_commits_via_own_handle`, `GatewayGalaxyWriteMeterLiveTests`, and the new `StatusCodeMapTests` rows. | ~~Residual is only the 06/S-1 live leg~~ **CLOSED 2026-08-09** — nothing remains. | §6.3 | | ~~**1 genuinely unexecuted plan** — `2026-05-29-adminui-followups`, **19 pending / 0 completed**~~ ✅ **CLOSED 2026-08-07 by the overlap audit it asked for — it was bookkeeping drift, not backlog.** All 19 tasks are shipped, superseded by v3, or moot; **no code was needed.** The warning was right and then some: WS1's whole *premise* died with v3 (driver-embedded tag lists in `DriverConfig` are no longer the runtime source of truth — the artifact populates `RawTags`), and its capability was delivered in the v3 shape (`TagConfigEditorMap` + `DeviceFormMap`, both verified **reachable**, not merely present). WS2/WS3 shipped; WS4's target files were deleted by v3 Batch 2 + #542. Per-task evidence in the `.tasks.json`. | §6.2 | -| ~~**11 other open plan tasks**~~ **3 as of 2026-08-10** — ~~5 akka-hosting partials (likely v3-superseded)~~ *(verified superseded 2026-08-07 — F8/F9/F10/F13/F14 marked completed; `Phase7Composer`/`ScriptedAlarmActor` deleted, evaluators/SDK sinks/security policies all production-wired)*, ~~4+1 followups-batch (open *by choice*)~~ *(2026-08-10: Tasks 9 + 10 VERIFIED PASS live — Int64 authoring end-to-end on the rebuilt rig; probe E2E 12/12 with the "needs Windows-VM" premise corrected to TCP-reachability gating)*, ~~fixedtree Task 11~~ *(closed 2026-08-10: obsolete — the injection feature it gated was deleted with #507)*, ~~1 manual smoke checklist~~ *(closed 2026-08-10: obsolete — the driver pages it smokes were retired in v3 Batch 2)*. ~~**Remaining 3: F10b + SetSeverity (reconsider gates, decisions already taken — do not build without a fresh go-ahead) and the device-gated proofs (hardware-blocked).**~~ **Remaining 1 as of 2026-08-10 (later that day): F10b + SetSeverity reconsider gates put to the user and CLOSED decided-against (F10b re-open trigger: operator complaints about rebuild churn on data-type edits; SetSeverity is operationally invisible — engine overwrites on first eval); the device-gated proofs bundle trimmed to its 2 genuinely-gated sub-items (H6 native-ack→AVEVA + AbLegacy/TwinCAT/FOCAS probes).** | Hardware-blocked only. | §6.2, §9 2026-08-10 (sixth) | +| ~~**11 other open plan tasks**~~ **3 as of 2026-08-10** — ~~5 akka-hosting partials (likely v3-superseded)~~ *(verified superseded 2026-08-07 — F8/F9/F10/F13/F14 marked completed; `Phase7Composer`/`ScriptedAlarmActor` deleted, evaluators/SDK sinks/security policies all production-wired)*, ~~4+1 followups-batch (open *by choice*)~~ *(2026-08-10: Tasks 9 + 10 VERIFIED PASS live — Int64 authoring end-to-end on the rebuilt rig; probe E2E 12/12 with the "needs Windows-VM" premise corrected to TCP-reachability gating)*, ~~fixedtree Task 11~~ *(closed 2026-08-10: obsolete — the injection feature it gated was deleted with #507)*, ~~1 manual smoke checklist~~ *(closed 2026-08-10: obsolete — the driver pages it smokes were retired in v3 Batch 2)*. ~~**Remaining 3: F10b + SetSeverity (reconsider gates, decisions already taken — do not build without a fresh go-ahead) and the device-gated proofs (hardware-blocked).**~~ **Remaining 1 as of 2026-08-10 (later that day): F10b + SetSeverity reconsider gates put to the user and CLOSED decided-against (F10b re-open trigger: operator complaints about rebuild churn on data-type edits; SetSeverity is operationally invisible — engine overwrites on first eval); the device-gated proofs bundle trimmed to its 2 genuinely-gated sub-items (H6 native-ack→AVEVA + AbLegacy/TwinCAT/FOCAS probes).** (→ #614 H6; TwinCAT probe → #755) | Hardware-blocked only. | §6.2, §9 2026-08-10 (sixth) | | ~~**Review-coverage gap** — 10 shipped projects never reviewed (Calculation, Historian.Gateway, Mqtt +Browser/Contracts, MTConnect +Contracts, Sql +Browser/Contracts) while **3 modules still review the retired Wonderware backend**~~ **CLOSED 2026-08-10**: all 14 unreviewed projects (the 10 + Mqtt.Transport, Sql.Dialect, Calculation.Contracts, OpcUaClient.Session) reviewed by 6 parallel agents — 61 findings, 26 fixed, 35 Open in `findings.md`; Wonderware reviews banner-retired to history; README at 65 modules, `--check` clean. **The recorded Opens were themselves resolved 2026-08-10 (eighth round): fleet-wide 0 pending findings** | §9 2026-08-10 (seventh, eighth) | §6.3, §9 2026-08-10 (seventh) | | ~~**`RawBrowseModal` stale-open race** (added 2026-08-05) — `OnParametersSetAsync` never re-checks staleness after its awaits, so a superseded open's late resolve can install the wrong device's browse config. The identical shape was found by review and **fixed** in `RawTagModal`/`RawManualTagEntryModal` (post-await staleness re-check; see `docs/plans/2026-08-05-raw-modal-picker-live-config-design.md` §6); the browse modal was deliberately left out of that branch's scope.~~ ✅ **CLOSED 2026-08-09** (`87637e4e`, merge `0739f4b9`) — the sibling `_openGeneration` guard ported to every post-await write, **plus two departures the siblings did not need**: this modal resolves inside `try/finally`, so the `catch` (a stale open's failure would paint its error over the live device) and the `finally` (a stale `return` still runs the tail, dropping the LIVE open's spinner) are generation-guarded too. Four `RawBrowseModalStaleOpenTests` facts, each observed red pre-guard and each re-falsified by reverting only its own guard (1-failed/3-passed per revert — no redundant pin). | ~~Lower stakes than the fixed pair… but the fix pattern is proven and cheap to port.~~ Ported. | design doc §6 | diff --git a/docs/ScriptEditor.md b/docs/ScriptEditor.md index e039653c..b615531c 100644 --- a/docs/ScriptEditor.md +++ b/docs/ScriptEditor.md @@ -1,5 +1,8 @@ # Monaco Script Editor (Roslyn-backed IntelliSense) +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + The script editor gives AdminUI users a first-class C# authoring experience for virtual-tag scripts: completions, diagnostics, hover, signature help, document formatting, and context-aware tag-path completions — all backed by the same @@ -208,7 +211,7 @@ scripted-alarm predicates) runs scripts through the shared `ScriptRunGuard`: - **Allocation budget.** A single run allocating more than `Scripting:MaxAllocBytesPerRun` (64 MB) is quarantined. This is sampled after the run returns, so a run that OOMs the process first is not stopped — that needs an - out-of-process runner. + out-of-process runner (→ #750). - **Configuration keys and their ranges.** All three knobs are node-wide (the `Scripting` appsettings section, shared by virtual tags, Calculation tags and scripted alarms) and are clamped when bound, with one startup Warning per @@ -491,20 +494,20 @@ site. - **Tag-path completion shows resolvable keys only.** Surfacing the UNS browse path as a completion *detail* (a non-inserted hint shown alongside the - resolvable key) for discoverability is a tracked follow-up. + resolvable key) for discoverability is a tracked follow-up. → #759 - **Literal gate matches by method name, not receiver.** `anything.GetTag("…")` would also trigger tag-path completion (not just `ctx.GetTag("…")`). Harmless in the sandbox today — no other `GetTag` methods exist in the allowed reference - set — but tighter binding to `ctx` / `VirtualTagContext` is a follow-up. + set — but tighter binding to `ctx` / `VirtualTagContext` is a follow-up. (Reconcile: → #759) - **Analysis runs synchronous Roslyn (CPU-bound).** Each endpoint call builds a `CSharpCompilation` synchronously. This is intentional: the endpoints are admin-only, client-side debounced (~500 ms), and `Task.Run` offload adds unnecessary complexity for infrequent admin use. If concurrency becomes a concern under heavy admin usage, wrapping each `Analyze` call in `Task.Run` is - the correct fix. + the correct fix. (Reconcile: → #759) - **InlayHints is a stub.** `/api/script-analysis/inlay-hints` returns an empty list. Filling it in (parameter name hints from the semantic model) is a - follow-up task. + follow-up task. (Dropped — reconcile: → #759) - **Language providers register globally at Monaco load.** The six providers are registered for the `csharp` language on the global Monaco instance (loaded once per page in `App.razor`). There is no impact today because only @@ -514,7 +517,7 @@ site. results). - **Creating a new script from the VirtualTagModal inline panel** is deferred. The current inline panel loads and saves an existing `Script` row; creating a - brand-new script requires a separate create flow and is a follow-up. + brand-new script requires a separate create flow and is a follow-up. (Shipped — reconcile: → #759) --- diff --git a/docs/Uns.md b/docs/Uns.md index ce3dd85e..e735ecc2 100644 --- a/docs/Uns.md +++ b/docs/Uns.md @@ -1,5 +1,8 @@ # UNS — Global Unified-Namespace Management +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + The **UNS** page (`/uns` in the AdminUI) is the single surface for managing the Unified Namespace across the whole fleet. It replaces the old per-cluster **UNS**, **Equipment**, and **Tags** tabs and the standalone @@ -528,7 +531,7 @@ equipment page. Phase 6 did **not** implement per-driver-instance status rows on the Hosts page. This item (H7-runtime) is **F7-runtime-blocked**: the runtime plumbing needed to surface per-instance health rows is not yet in place. It remains on -the backlog. +the backlog. (Stale — reconcile: → #759) ## Bulk import diff --git a/docs/VirtualTags.md b/docs/VirtualTags.md index 2b7dc28a..69297dfe 100644 --- a/docs/VirtualTags.md +++ b/docs/VirtualTags.md @@ -1,5 +1,8 @@ # Virtual Tags +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + > ⚠️ **Correction (2026-07-28): `GenericDriverNodeManager` is NOT a production dispatch path.** It is test > scaffolding — its own source says so (`Core/OpcUa/GenericDriverNodeManager.cs:71`, "verified 07/#10: zero > production references"). The production chain is `OpcUaPublishActor` → `IOpcUaAddressSpaceSink` @@ -34,7 +37,7 @@ User scripts are compiled via `Microsoft.CodeAnalysis.CSharp` (regular compiler, #### Known resource limits (accepted trade-offs) -The sandbox cannot pre-empt a script. **Memory:** each run's own-thread allocation is sampled (`GC.GetAllocatedBytesForCurrentThread`) and a run over `Scripting:MaxAllocBytesPerRun` (default 64 MB) quarantines the script — but the sample is taken after the run returns, so a script that allocates fast enough (`new byte[int.MaxValue]` in a loop) can still OOM the process first. **CPU:** a tight loop keeps its pool thread after the timeout fires; `ScriptRunGuard` bounds that to one orphan per script by refusing further runs while it spins (Bad `"previous evaluation still running"`) and quarantines after three consecutive timeouts (a refusal is not a strike) — or immediately on a refusal behind an orphan that has run longer than `Scripting:StuckOrphanQuarantineFactor` (default 10) × the timeout, which is what quarantines a never-returning `while(true)`. Both are mitigations; pre-emptive CPU/memory limits need an out-of-process runner. Script authoring is gated behind the Admin permission as the primary control. See `docs/ScriptEditor.md` §"Runtime safety". +The sandbox cannot pre-empt a script. **Memory:** each run's own-thread allocation is sampled (`GC.GetAllocatedBytesForCurrentThread`) and a run over `Scripting:MaxAllocBytesPerRun` (default 64 MB) quarantines the script — but the sample is taken after the run returns, so a script that allocates fast enough (`new byte[int.MaxValue]` in a loop) can still OOM the process first. **CPU:** a tight loop keeps its pool thread after the timeout fires; `ScriptRunGuard` bounds that to one orphan per script by refusing further runs while it spins (Bad `"previous evaluation still running"`) and quarantines after three consecutive timeouts (a refusal is not a strike) — or immediately on a refusal behind an orphan that has run longer than `Scripting:StuckOrphanQuarantineFactor` (default 10) × the timeout, which is what quarantines a never-returning `while(true)`. Both are mitigations; pre-emptive CPU/memory limits need an out-of-process runner (→ #750). Script authoring is gated behind the Admin permission as the primary control. See `docs/ScriptEditor.md` §"Runtime safety". `System.Threading.Tasks` is denied (Core.Scripting-003), which prevents `Task.Run` / `Parallel` fan-out that would spawn background work outliving the timeout. diff --git a/docs/drivers/AbLegacy.md b/docs/drivers/AbLegacy.md index d2774cc0..2fcef03c 100644 --- a/docs/drivers/AbLegacy.md +++ b/docs/drivers/AbLegacy.md @@ -1,5 +1,8 @@ # AB Legacy Driver +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + In-process native-protocol driver that exposes legacy Allen-Bradley PLCs — **SLC 500**, **MicroLogix**, **PLC-5**, and Logix-via-PCCC — as OPC UA nodes. It runs inside the OtOpcUa server's .NET 10 AnyCPU process and speaks PCCC over @@ -274,7 +277,7 @@ Simulator evidence: [Wire evidence](#wire-evidence-native-pccc). - **PLC-5 native is N-file reads only.** Every other file type and all PLC-5 writes are refused before the wire and reach the client as `BadNotSupported` (handle kept, health unchanged; #571 closed). Native is opt-in - for every family; an explicit Native on PLC-5 / `logixpccc` logs a Warning at init. + for every family; an explicit Native on PLC-5 / `logixpccc` logs a Warning at init. PLC-5 hardware proof → #752 (SLC/MicroLogix evidence → #592). - Status-bit sub-element writes (e.g. `T4:0.DN`, `C5:0.DN`) are Unsupported natively (they would clobber the control word); nothing goes on the wire — the driver refuses before creating the tag's runtime, so not even an init read is sent (`NativeAbLegacyTagRuntime.WriteRefusal` is the single predicate, asked through diff --git a/docs/drivers/Mqtt.md b/docs/drivers/Mqtt.md index 5952b4fc..c205fd41 100644 --- a/docs/drivers/Mqtt.md +++ b/docs/drivers/Mqtt.md @@ -1,5 +1,8 @@ # MQTT Driver +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + In-process MQTT **broker-subscribe** driver. It holds one MQTTnet 5 client against one broker, subscribes to the topics its authored tags name, and forwards each PUBLISH straight to `ISubscribable.OnDataChange`. Ingest is **push, not poll**; writes go back out as ordinary publishes @@ -250,7 +253,7 @@ One subscription (`spBv1.0/{GroupId}/#`) feeds a per-edge-node state machine - **Unsupported datatypes are skipped with a warning, never guessed**: `PropertySetList`, `Unknown`, extension values, and any `Template`/`DataSet` that cannot be flattened honestly. `Int8`→`Int16` and `UInt8`→`UInt16` widen; `*Array` variants map to the element type with the array bit set — but - **array metrics are not read in v1** (warned once, never fed), so the browse picker offers them, and any + **array metrics are not read in v1** (warned once, never fed; → #754), so the browse picker offers them, and any metric whose type is unsupported, **without an address**: the browse-commit refuses them rather than committing a tag that waits for initial data forever (R06:M-5). - **Ingest state is reset at every session/authoring seam** — a reconnect or a `ReinitializeAsync` diff --git a/docs/drivers/Sql.md b/docs/drivers/Sql.md index 120d41c1..c41a2aac 100644 --- a/docs/drivers/Sql.md +++ b/docs/drivers/Sql.md @@ -1,5 +1,8 @@ # SQL Poll Driver +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + Getting-started guide for the `Sql` driver — a **read-only** Equipment-kind driver that polls rows out of a relational database and publishes them as OPC UA variables. For the design rationale read [`docs/plans/2026-07-15-sql-poll-driver-design.md`](../plans/2026-07-15-sql-poll-driver-design.md); @@ -16,7 +19,7 @@ already writes to. There is no protocol here and nothing to "connect" to a machi (`Npgsql`) and `Oracle` (ODP.NET managed — **Oracle 12.2 or later**, the floor the `FETCH FIRST` paging needs). `SqlProvider` also declares `MySql` and `Odbc`, but those remain **reserved names, not working backends** — authoring one fails the driver rather than silently -falling back. +falling back. → #753 Two provider-specific things are worth knowing before you author against them: @@ -30,7 +33,7 @@ Two provider-specific things are worth knowing before you author against them: **Read-only, structurally.** `SqlDriver` does not implement `IWritable`, so no configuration can produce a write. The `allowWrites` config field exists and is **inert**; the factory *warns* when it is authored `true` rather than failing, on the grounds that the flag cannot do harm but an operator -who believes writes are enabled can. +who believes writes are enabled can. → #753 ## Tag models @@ -106,7 +109,7 @@ canonical text form. SQL Server behaves the same way. authored `keyValue` refused the tag while the identically-bound `whereValue` was served — and the deploy gate reported the key as *missing* when it was right there in the blob.) -### `Query` — deferred to P3 +### `Query` — deferred to P3 (→ #753) Named arbitrary-`SELECT`. **The v1 parser rejects it**, and that refusal is enforced end-to-end — parser, validator, planner and the AdminUI editor all decline it rather than accepting the value and diff --git a/docs/plans/2026-10-10-questdb-historian-live-gate.md b/docs/plans/2026-10-10-questdb-historian-live-gate.md index a39d401a..e3055fa0 100644 --- a/docs/plans/2026-10-10-questdb-historian-live-gate.md +++ b/docs/plans/2026-10-10-questdb-historian-live-gate.md @@ -1,5 +1,8 @@ # QuestDB historian — live gate (T9), 2026-10-11 +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + **Verdict: PASSED.** All six legs of spec §7 passed on the docker-dev rig. The rig ran `feat/questdb-historian` at `2ebe0a42`, with the central pair on image `0701f6a833ea`, rebuilt for this gate. The QuestDB backend pointed at the overlay `questdb` service (`questdb/questdb:10.0.1`, `docker-dev/docker-compose.questdb.yml`), with default table @@ -8,7 +11,7 @@ prefix `hist_`. The gate also produced two findings, neither in the QuestDB backend. Both are described under "Gate findings" below: - A crash failover (SIGKILL of the recording Primary) leaves a recording gap of about 30 s. This is the #537 Primary-gated capture waiting for Akka downing; it is backend-independent. -- One `libgssapi_krb5.so.2` load error is printed per central boot. It is benign. +- One `libgssapi_krb5.so.2` load error is printed per central boot. It is benign. → #758 ## Setup diff --git a/docs/reviews/2026-10-21/03-host-controlplane-security-config.md b/docs/reviews/2026-10-21/03-host-controlplane-security-config.md index 1eb5d5d0..771c8253 100644 --- a/docs/reviews/2026-10-21/03-host-controlplane-security-config.md +++ b/docs/reviews/2026-10-21/03-host-controlplane-security-config.md @@ -1,5 +1,8 @@ # Review 03 — Host / ControlPlane / Security / Configuration +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + Repo: `/Users/dohertj2/Desktop/OtOpcUa` @ `d51d35bd` (master). Read-only audit of production code in `src/Server/ZB.MOM.WW.OtOpcUa.Host`, `src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane`, `src/Server/ZB.MOM.WW.OtOpcUa.Security`, `src/Core/ZB.MOM.WW.OtOpcUa.Configuration`. @@ -35,7 +38,7 @@ Severity: Critical / High / Medium / Low. Confidence: High / Medium. - **Suggested fix:** Track consecutive timeouts per `virtualTagId` in `VirtualTagHostActor`/evaluator and quarantine the tag (stop evaluating, publish Bad, log Error, surface on `/uns`) after N (e.g. 3) timeouts until the script is edited. Longer term: cooperative `CancellationToken` polling injected - into compiled scripts, or an out-of-process runner. + into compiled scripts, or an out-of-process runner (→ #750). ### 1.2 `Deployment` / `NodeDeploymentState` / `ConfigEdit` / `ConfigAuditLog` grow without bound — every deploy stores a full artifact blob forever - **Severity:** Medium **Confidence:** High diff --git a/docs/reviews/2026-10-21/05-core.md b/docs/reviews/2026-10-21/05-core.md index 9a7b9575..fabc6d4b 100644 --- a/docs/reviews/2026-10-21/05-core.md +++ b/docs/reviews/2026-10-21/05-core.md @@ -1,5 +1,8 @@ # Core slice review (Commons, Core, Core.Abstractions, Core.Scripting[.Abstractions], Core.ScriptedAlarms, Core.VirtualTags, Core.AlarmHistorian) +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + Reviewer 05. Read-only audit of production code. master @ d51d35bd. Scope note: this slice is unusually mature — collectible-ALC script unloading, per-generation @@ -100,7 +103,7 @@ closed and commented. The findings below are the residue. - Location: `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting/ScriptSandbox.cs:30-35` (class remarks acknowledge it) - Evidence: the sandbox comment states "cannot prevent a script from allocating unbounded memory or spinning in a tight loop — those are budget concerns, handled by the per-evaluation timeout". The timeout bounds wall-clock but not allocation; a `new byte[int.MaxValue]`-style allocation or a growing collection in a loop can OOM the whole in-process server before the 250 ms timeout fires. - Failure scenario: an authored script (buggy or hostile) allocates aggressively and OOMs the server. Only mitigated by operators authoring sane scripts and the preview harness. -- Suggested fix: documented trade-off; a true fix needs an out-of-process runner (noted as a v3 concern). Flagging for awareness. +- Suggested fix: documented trade-off; a true fix needs an out-of-process runner (noted as a v3 concern). Flagging for awareness. → #750 ### 12. `ScriptedAlarmEngine.OnUpstreamChange` writes `_valueCache` before the disposed check - Severity: Low · Confidence: High diff --git a/docs/reviews/2026-10-21/06-drivers-session.md b/docs/reviews/2026-10-21/06-drivers-session.md index bbe6accb..ba5e5c3b 100644 --- a/docs/reviews/2026-10-21/06-drivers-session.md +++ b/docs/reviews/2026-10-21/06-drivers-session.md @@ -1,5 +1,8 @@ # Review 06 — Drivers (session slice): Galaxy, OpcUaClient, Mqtt, MTConnect, Sql +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + Repo: `/Users/dohertj2/Desktop/OtOpcUa` @ `d51d35bd` (master). Production code only (`src/Drivers/...`). Read-only audit; every finding was verified by reading the cited lines (fork-sourced MQTT/MTConnect/SQL findings were spot-checked by the lead against the cited lines). Line numbers are as of this commit. Prefix key: **G** Galaxy, **O** OpcUaClient, **M** MQTT, **T** MTConnect, **S** Sql. Host context that shapes several findings (verified outside the slice): @@ -134,6 +137,7 @@ Host context that shapes several findings (verified outside the slice): - Location: `SparkplugIngestor.cs:1274-1285` (`if (binding.DataType.IsSparkplugArray()) { WarnOnce(...); return; }`; DataSet/Template/PropertySet at `:1287-1302`); `MqttBrowseSession.cs:387-392` lists the leaf with `IsArray: true`. - Impact: an operator can commit a tag the ingestor never feeds — warned once, then `BadWaitingForInitialData` forever. - Suggested fix: mark array leaves not-bindable in `DescribeMetric` as `mapped is null` already is. +- Tracking: the array-read capability itself → #754. ### T-2. MTConnect Agent-restart detection lives only on the `/sample` pump; `ReadAsync` discards `instanceId` - **Severity: Low** · **Confidence: High** @@ -144,6 +148,7 @@ Host context that shapes several findings (verified outside the slice): - **Severity: Low** · **Confidence: High** (all documented deferrals that fail loudly or warn once) - Location: `Driver.Sql.Contracts/SqlEquipmentTagParser.cs:75-76`, `SqlGroupPlanner.cs:336-339`; `SqlDriverFactoryExtensions.cs:214-225`, `SqlDriverProbe.cs:196-211`, `SqlDriverBrowser.cs:135-141`; `SqlDriverFactoryExtensions.cs:151-161` + `SqlDriver.cs:30-31` (no `IWritable`). - Note: a `model: "Query"` tag is silently skipped (ledger + `BadNodeIdUnknown`) rather than refused at the deploy gate. +- Tracking: the Query/MySql/Odbc/allowWrites features → #753. ### Dead parsed surface (Low, High confidence) - **T-3** `MTConnectObservation.SampleRate` parsed per observation (`MTConnectStreamsParser.cs:424-428`) and never read; `MTConnectTagDefinition.MtType/MtSubType/Units` carried but only `MtCategory` is read (`MTConnectDriver.cs:3493`). diff --git a/docs/reviews/2026-10-21/07-drivers-plc.md b/docs/reviews/2026-10-21/07-drivers-plc.md index d5fc13c9..5969e709 100644 --- a/docs/reviews/2026-10-21/07-drivers-plc.md +++ b/docs/reviews/2026-10-21/07-drivers-plc.md @@ -1,5 +1,8 @@ # Review 07 — PLC drivers (AbCip, AbLegacy, Ab.Transport, Ab.LibplctagShared, Modbus, S7, TwinCAT, FOCAS, Calculation, Cli) +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + Repo: `/Users/dohertj2/Desktop/OtOpcUa` @ `d51d35bd` (master). Production code only. Every finding below was verified by reading the cited lines; sub-reviewer candidates that could not be confirmed were dropped. Paths are relative to `src/Drivers/` unless absolute. Legend: **Sev** Critical/High/Medium/Low · **Conf** High/Medium. @@ -81,7 +84,7 @@ Default-configured device with 200 holding registers = 200 FC03 round-trips per **Sev Medium · Conf High** — `CalculationDriver.cs:178-186` (`_changeDependents` from `DependencyRefs` only), `:354-359`; `src/Core/ZB.MOM.WW.OtOpcUa.Configuration/Validation/DraftValidator.cs:395-456` (only Missing/NotFound/Cycle). `ctx.GetTag("Plant/" + line + "/Temp")` or `return ctx.Now.Hour;` with the default `changeTriggered:true` stays `BadWaitingForInitialData` forever while the driver reports Healthy. **Fix:** deploy-gate `CalculationNoEffectiveTrigger` when change-triggered && deps.Count == 0 && no timer; or evaluate once at init. ### U6. AbLegacy native: PLC-5 writes are not shipped, PLC-5 reads are N-file only, PLC-5 sub-elements refused (documented, surfaced correctly) -**Sev Low · Conf High** — `Ab.Transport/Pccc/PcccServices.cs:16-19` (`Plc5WriteVerified = false`), `:112-114`; `ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/NativeAbLegacyTagRuntime.cs:150, 261-263`. All surface as `AbStatus.Unsupported` → `BadNotSupported` with no health impact, and `Native` is opt-in for AbLegacy. Noted as a capability gap, not a defect. +**Sev Low · Conf High** — `Ab.Transport/Pccc/PcccServices.cs:16-19` (`Plc5WriteVerified = false`), `:112-114`; `ZB.MOM.WW.OtOpcUa.Driver.AbLegacy/NativeAbLegacyTagRuntime.cs:150, 261-263`. All surface as `AbStatus.Unsupported` → `BadNotSupported` with no health impact, and `Native` is opt-in for AbLegacy. Noted as a capability gap, not a defect. → #752 ### U8. FOCAS: `FocasDataType.String` is authorable but every read path publishes a number at Good **Sev Medium · Conf High** — editor lists every enum member (`AdminUI/Components/Shared/Uns/TagEditors/FocasTagConfigEditor.razor:18`); `ZB.MOM.WW.OtOpcUa.Driver.FOCAS/Wire/FocasConstants.cs:116` (`_ => FocasPmcDataType.Byte`), `FocasPmcCoalescer.cs:135` (`_ => (object)raw`, a `long`), `Wire/WireFocasClient.cs:392-393` (PARAM default reads Int32), `:456-458` (MACRO returns `double`). A String-typed OPC UA node receives a boxed `long`/`int`/`double` at Good. **Fix:** reject `String` in `FocasTagDefinitionFactory.FromTagConfig`/capability matrix and drop it from the editor. diff --git a/docs/security.md b/docs/security.md index fa8269a2..c6e44483 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,5 +1,8 @@ # Security +> **Status lives in Gitea.** Open follow-ups and known gaps noted in this doc are tracked as issues on +> gitea.dohertylan.com/dohertj2/lmxopcua (label `migrated`, 2026-11-06). This doc describes the system — do not track status here; file new work as an issue. + > **v2 status (2026-05-26).** The four security concerns below are unchanged in v2. > Paths + project names moved: `OtOpcUa.Server/Security/` → `OtOpcUa.Security/` > (`Ldap/`, `Jwt/`, `Endpoints/AuthEndpoints.cs`), `OtOpcUa.Admin` is gone (its @@ -264,7 +267,7 @@ guards in front of the store, bound from the same `Security:ApiKeys` section The budget is **process-wide, not per remote endpoint**: the OPC UA SDK's impersonation callback (the `IOpcUaUserAuthenticator` seam) does not carry the client's address, so per-endpoint accounting needs that -seam widened in the OpcUaServer project (a Wave 3 follow-up). Neither guard applies to a driver-only node: +seam widened in the OpcUaServer project (a Wave 3 follow-up → #751). Neither guard applies to a driver-only node: its `ArtifactApiKeyStore` is two in-memory dictionary probes and is never wrapped. ### LDAP bind flow (`OtOpcUaLdapAuthService`) diff --git a/docs/superpowers/plans/2026-10-22-review-remediation.md b/docs/superpowers/plans/2026-10-22-review-remediation.md index e7a5f4aa..92b5b2ce 100644 --- a/docs/superpowers/plans/2026-10-22-review-remediation.md +++ b/docs/superpowers/plans/2026-10-22-review-remediation.md @@ -1,5 +1,8 @@ # System Review Remediation Implementation Plan +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development (recommended) or superpowers-extended-cc:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Resolve every finding from the 2026-10-21 8-slice Fable system review of OtOpcUa (master `d51d35bd`): 1 Critical, ~25 High, ~60 Medium, ~85 Low. These are memory/resource leaks, unimplemented features, performance limiters, and concurrency/health-contract hazards. @@ -247,7 +250,7 @@ Merge order inside a wave is alphabetical by lane, rebasing as needed. Two lanes - **E2 Sandbox hardening** (`R05:10`, `R05:11`): - Deny `System.Threading` with an allow-list (`CancellationToken`, `Interlocked`, `Volatile`). - Deny `System.Type.GetType`/`GetMethod*`/`InvokeMember` and `System.Runtime.CompilerServices.RuntimeHelpers`. - - Add an allocation budget: sample `GC.GetAllocatedBytesForCurrentThread` around each run and quarantine on breach (`Scripting:MaxAllocBytesPerRun`, default 64 MB). Document that a pre-emptive OOM still needs an out-of-process runner, and file a follow-up issue for that. + - Add an allocation budget: sample `GC.GetAllocatedBytesForCurrentThread` around each run and quarantine on breach (`Scripting:MaxAllocBytesPerRun`, default 64 MB). Document that a pre-emptive OOM still needs an out-of-process runner, and file a follow-up issue for that (→ #750). - Tests: each forbidden form gets an `OTSCRIPT` diagnostic, and the Monaco editor parity test still matches. - **E3 Scripted-alarm engine** (`R05:1`, `3`, `6`, `7`, `9`, `12`): - Clear `_valueCache` in `LoadAsync`; move its write below the `_disposed` guard. @@ -335,7 +338,7 @@ Each lane owns `src/Drivers/*/**` + its `tests/Drivers/*`, its A - `NotSupportedException` ⇒ `BadNotSupported`, no health, reject at `FromTagConfig` (`H16`). - Table swap (`H13`); prune semaphores on index change (`L7`). - **Array write encode** (`U10`); `IBatchSubscribable`. -- Document the PLC-5 write capability gap with a hardware follow-up issue (`U6`). +- Document the PLC-5 write capability gap with a hardware follow-up issue (`U6`) (→ #752). ### Lane TwinCAT - Per-registration isolation with dead-registration retry (`R07:H11`). @@ -399,7 +402,7 @@ Each lane owns `src/Drivers/*/**` + its `tests/Drivers/*`, its A **MQTT:** - Failed SUBSCRIBE ⇒ pending set retried on reconnect/reconcile, `SubscribeAsync` throws on a transport failure (`R06:M-1`). - **Wire `birthObservationWindowSeconds`** into `MqttDriverBrowser.OpenBudget` + discovery UntilStable (`M-4`). -- Array leaves not bindable, plus a Sparkplug-array-read feature issue (`M-5`). +- Array leaves not bindable, plus a Sparkplug-array-read feature issue (`M-5`) (→ #754). - Prune `LastValueCache` on Register (`M-2`). - Dispose the failed connection (`M-3`). - Wildcard topic trie (`M-6`); static UTF-8 encoder (`M-7`). @@ -409,7 +412,7 @@ Each lane owns `src/Drivers/*/**` + its `tests/Drivers/*`, its A - Row cap with a `TOP/LIMIT N+1` dialect + config-error status (`S-1`). - Rate-limited warnings (`S-6`); plan cache (`S-7`). - Dispose `_gate` (`S-3`). -- `Query` model refused at the deploy gate via the parser + feature issues for MySql/Odbc/allowWrites (`S-4`). +- `Query` model refused at the deploy gate via the parser + feature issues for MySql/Odbc/allowWrites (`S-4`) (→ #753). - `IS_NULLABLE` in browse attributes (`S-5`). - Withhold JSON parser text (`S-8`); `IBatchSubscribable`. diff --git a/docs/v3/twincat-backlog.md b/docs/v3/twincat-backlog.md index 401075d6..053a5286 100644 --- a/docs/v3/twincat-backlog.md +++ b/docs/v3/twincat-backlog.md @@ -1,5 +1,8 @@ # TwinCAT driver — v3 backlog +> **Status lives in Gitea.** Open items from this file are tracked as issues on gitea.dohertylan.com/dohertj2/lmxopcua +> (label `migrated`, 2026-11-06). This file is a historical/design record — do not track status here; file new work as an issue. + The v2 TwinCAT driver is considered solid: 28 integration tests (14 `[TwinCATFact]` + 16-case `[TwinCATTheory]`) running live against the TCBSD fixture, 110 unit tests, three latent driver bugs shaken out (notification cycle units, `STRING(N)` mapper, @@ -27,7 +30,7 @@ user's approval, an `oee-tagdump-mac` route for the Mac's current VPN IP (`10.18 to **Z35474** (TC3) and **Z34834** (TC2, `5.69.178.220.1.1`, 10.201.45.197) by UDP-48899 AddRoute with the factory login. ⚠️ A same-name AddRoute does **not** replace on either runtime: both presses now carry TWO `oee-tagdump-mac` entries — the stale `10.181.198.189.1.1` and the new one (route table read back over ADS, -port 10000 / IG 803). Removing the stale one needs an ADS write and was not done. Every other press still +port 10000 / IG 803). Removing the stale one needs an ADS write and was not done (→ #755). Every other press still has only the stale route, so it refuses the Mac; the VPN IP changes again ⇒ the routes go stale again. ## Status of the original items @@ -38,14 +41,14 @@ has only the stale route, so it refuses the Mac; the VPN IP changes again ⇒ th 2. **Notification coalescing under load** — run the subscribe test while the PLC cycle is saturated. Needs the TCBSD fixture (loading a production press is out of the question) and the `GVL_Perf` / - `FB_PerfChurn` fixture project from the stranded `auto/driver-gaps` branch. Fixture-gated. + `FB_PerfChurn` fixture project from the stranded `auto/driver-gaps` branch. Fixture-gated. → #755 -3. **Multi-hop AMS route** — a test behind an IPC gateway with a chained route entry. No hardware. +3. **Multi-hop AMS route** — a test behind an IPC gateway with a chained route entry. No hardware. → #755 -4. **License-rotation automation** — XAR's 7-day trial expires on schedule. Ops item, not code. +4. **License-rotation automation** — XAR's 7-day trial expires on schedule. Ops item, not code. → #755 5. **Lab rig** — cheapest IPC on a dedicated network; the only route that covers real EtherCAT I/O - timing and cycle jitter under CPU load. Blocked on hardware + budget. + timing and cycle jitter under CPU load. Blocked on hardware + budget. → #755 ## Added by #612 / #645 @@ -69,4 +72,4 @@ has only the stale route, so it refuses the Mac; the VPN IP changes again ⇒ th returned 10,628 variables. No `SymbolsLoadMode` option is needed. 9. **Discovered struct/FB member writes (#612)** — deferred: writes, never on a press; fixture-only. - Likewise **sum-write** was deliberately NOT ported from the stranded branch. + Likewise **sum-write** was deliberately NOT ported from the stranded branch. → #755