docs: point markdown backlogs at Gitea issues (status lives in Gitea)
v2-ci / unit-tests (push) Waiting to run

This commit is contained in:
Joseph Doherty
2026-11-06 15:31:43 -05:00
parent 2f7f891636
commit 7ec5b95d24
18 changed files with 92 additions and 32 deletions
+9 -6
View File
@@ -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)
---
+4 -1
View File
@@ -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
+4 -1
View File
@@ -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.
+4 -1
View File
@@ -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
+4 -1
View File
@@ -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`
+6 -3
View File
@@ -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
@@ -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
@@ -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
+4 -1
View File
@@ -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
@@ -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`).
+4 -1
View File
@@ -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.
+4 -1
View File
@@ -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`)
@@ -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/<Driver>*/**` + its `tests/Drivers/<Driver>*`, 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/<Driver>*/**` + its `tests/Drivers/<Driver>*`, 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/<Driver>*/**` + its `tests/Drivers/<Driver>*`, 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`.
+9 -6
View File
@@ -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