docs: R2-11 TagConfig consolidation + strictness rollout decisions
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
{ "id": "T20", "subject": "EquipmentTagConfigInspector DriverType-dispatch map in ControlPlane (+6 Contracts references)", "status": "completed", "blockedBy": ["T12", "T13", "T14", "T15", "T16", "T17"] },
|
||||
{ "id": "T21", "subject": "AdminOperationsActor deploy-gate wiring + Deployment:TagConfigValidationMode (Warn default | Error opt-in); actor-level consuming test", "status": "completed", "blockedBy": ["T20"] },
|
||||
{ "id": "T22", "subject": "AdminUI writable checkbox in the six driver-typed tag editors (models + razor; live /run verify at execution)", "status": "deferred-live", "blockedBy": [] },
|
||||
{ "id": "T23", "subject": "Docs + decision record: two-mode rollout, unknown-keys non-goal, FOCAS writable migration note, STATUS.md/00-INDEX.md entries + Phase-C follow-up", "status": "pending", "blockedBy": ["T17", "T21"] },
|
||||
{ "id": "T23", "subject": "Docs + decision record: two-mode rollout, unknown-keys non-goal, FOCAS writable migration note, STATUS.md/00-INDEX.md entries + Phase-C follow-up", "status": "completed", "blockedBy": ["T17", "T21"] },
|
||||
{ "id": "T24", "subject": "Final sweep: zero parity-comment/ReadEnum-copy grep hits; whole-solution build + test gate", "status": "pending", "blockedBy": ["T5", "T6", "T7", "T8", "T9", "T10", "T12", "T13", "T14", "T15", "T16", "T17", "T18", "T19", "T21", "T22", "T23"] }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -175,6 +175,8 @@ what the unit leg cannot; (c) migrate the 3 xunit v2 holdouts once Akka ships an
|
||||
| **R2-03** | 02/S13 (rank 5), 02/S12, 02/P7 | `r2/03-vt-failure-quality` (off `1676c8f4`) | `b9775485`…`6bb91e8d` (12 task commits) | **Code-complete, offline-verified.** S13: VT script failure/timeout degrades the node to `OpcUaQuality.Bad` (carrying last-known value) instead of freezing on last-Good forever — additive `EvaluationResult.Quality`, bridge historizes `0x80020000u` BadInternalError, published once per Good→Bad transition gated on all deps arrived (inputs-ready, no cold-start flash), recovery force-publishes next Good. S12: apply-boundary `ClearCompiledScripts` racing an in-flight eval into ObjectDisposedException retried once in `RoslynVirtualTagEvaluator` (re-fetch→recompile into current generation); stress guard reproduces pre-fix, deterministically green post-fix. P7: apply-boundary clear gated on a changed deployed expression set (`HashSet.SetEquals`, ordinal) — identical redeploy / rename-with-same-expressions no longer forces full VT recompile. Runtime.Tests 372/372, RoslynVirtualTagEvaluatorTests 20/20. **Deferred (T11/T12):** full cross-suite sweep + docker-dev live-`/run` (fail→Bad→recover + P7 no-recompile spot-check). |
|
||||
| **R2-02** | 01/S-6 (High, rank 2), 01/U-6, 01/S-8=03/S12 (rank 4), 04/C-7, 01/S-7=03/S13 | `r2/02-resilience-config-hardening` (off `1676c8f4`) | `4be13429`…`dd82e740` (17 task commits) | **Code-complete, offline-verified.** **S-6:** parser clamps timeout/retry/breaker overrides (timeout≤0→tier default, breaker==1→2, caps) with diagnostics + belt-and-braces `Build` clamp so pipeline construction can never throw Polly `ValidationException` (new `ResiliencePolicyRanges` in Core.Abstractions); brick-repro + `Build_NeverThrows_ForHostileDirectOptions` + factory-seam wiring proof; AdminUI `Validate()` range warnings. **S-8:** parser forces Write/AlarmAck retry→0 (spec invariant) **and** dispatch flips to `isIdempotent:false` (RED-first on the Runtime wiring test); non-idempotent arm caches the no-retry snapshot once/invoker (01/P-4) + tracker in-flight accounting; `WriteIdempotentAttribute` false claim corrected; retry cells disabled. **U-6:** bulkhead knob deleted end-to-end (migration-free; stored keys ignored) + `Options_properties_are_exactly_the_pipeline_wired_set` inertness guard. **C-7:** `ResilienceFormModel` JsonObject-bag round-trip preserves unknown keys; malformed JSON → `ParseFailed` lock + verbatim `ToJson` + discard button. **S-7:** per-`Create` `Interlocked` options-generation in the pipeline-cache key kills the respawn stale-pipeline race. Core resilience 121/121, AdminUI form 10/10, Analyzers 32/32, Runtime 364/364; 0 production OTOPCUA0001. **Deferred (T15/T18):** docker-dev `:9200` `/run` (warning banner, input lockout, unknown-key survival) + whole-solution sweep. |
|
||||
|
||||
| **R2-11** | 01/C-1, 01/P-1 + 05/CONV-2, UNDER-1, UNDER-6 | `r2/11-tagconfig-consolidation` (off `46fedda3`) | first `2e14fe1f` … (24 task commits) | **Code-complete, offline-verified.** **Part 1 (C-1/P-1):** single `TagConfigIntent.Parse` + `DeviceConfigIntent` in Commons replace the four byte-parity TagConfig parse copies (composer/artifact/walker/DraftValidator) — composer now parses once per tag (P-1); a permanent `Runtime.Tests/TagConfigCorpusParityTests` (30-blob corpus) guards compose≡decode; `grep "MUST parse identically" src/` = 0. **Part 2 (CONV-2):** shared strict-capable `TagConfigJson` readers in Core.Abstractions replace the six copy-pasted `ReadEnum`s; all six driver parsers gain `Inspect()` warnings + honour the `writable` key (FOCAS **forced read-only**, 05/UNDER-1); FOCAS equipment tags run the capability-matrix pre-flight (05/UNDER-6); Modbus probe parses the factory DTO shape (`timeoutMs` no longer dropped). New `EquipmentTagConfigInspector` in ControlPlane + `AdminOperationsActor` deploy-gate wiring with `Deployment:TagConfigValidationMode` (**Warn default | Error opt-in**), proven through the actor (F10b consuming test). AdminUI: `writable` checkbox in the six typed editors. **Phase-C follow-up (deferred, recorded here):** flip the runtime parsers to strict (typo'd enum ⇒ `BadNodeIdUnknown` instead of wrong-width `Good`) once fleets have run `Error` mode clean — behavior change deliberately not shipped in this plan. **Deferred (T22/T24):** docker-dev `/run` of one typed editor's Writable checkbox + whole-solution serial sweep. |
|
||||
|
||||
**Report anchor drifts to fold into the next report refresh** (from the R2-02 verification pass): (1)
|
||||
`01-core-composition.md` U-6 anchor `DriverResilienceOptions.cs:23-25` → the two bulkhead properties sat at
|
||||
`:23` and `:29` (now deleted); (2) `01-core-composition.md` U-6 anchor
|
||||
|
||||
+47
@@ -231,6 +231,53 @@ Combined with historization (values are arrays — history of the whole array sn
|
||||
|
||||
See the individual driver docs under `docs/drivers/` for per-driver implementation details.
|
||||
|
||||
## Writable equipment tags (`writable` key)
|
||||
|
||||
An equipment tag is **writable by default** — the materialised OPC UA node is created
|
||||
`CurrentReadWrite` and inbound client writes are routed to the device (subject to the
|
||||
`WriteOperate` LDAP role gate). Setting the optional `"writable": false` key in a tag's
|
||||
`TagConfig` makes the node **read-only** at the driver seam. The six typed tag editors
|
||||
(Modbus, S7, AB CIP, AB Legacy, TwinCAT, FOCAS) expose this as a **Writable** checkbox
|
||||
(checked by default); it can also be hand-authored in the raw-JSON textarea.
|
||||
|
||||
| Condition | Node |
|
||||
|---|---|
|
||||
| `writable` absent | Writable (driver default) — existing configs are unchanged |
|
||||
| `writable: false` | Read-only |
|
||||
| `writable: true` | Writable |
|
||||
|
||||
The key is honoured by Modbus, S7, AB CIP, AB Legacy, and TwinCAT. **FOCAS forces
|
||||
read-only regardless** — FOCAS PMC writes are unsupported in the wire client, so its
|
||||
editor shows the checkbox disabled/unchecked and any authored `writable:true` is ignored
|
||||
(and flagged at the deploy gate below). Node-level authz (LDAP `WriteOperate`) remains the
|
||||
effective write gate above this flag.
|
||||
|
||||
## Deploy-time TagConfig validation (`Deployment:TagConfigValidationMode`)
|
||||
|
||||
The deploy gate inspects every equipment tag's `TagConfig` per its bound driver type and
|
||||
surfaces **strictness warnings** — a present-but-invalid enum value (e.g. a typo'd
|
||||
`dataType`, which the lenient runtime silently defaults to the wrong width with `Good`
|
||||
status), a structurally-unparseable TagConfig, or (FOCAS) an authored `writable:true`.
|
||||
|
||||
The behaviour is controlled by `Deployment:TagConfigValidationMode` in `appsettings.json`:
|
||||
|
||||
| Mode | Behaviour |
|
||||
|---|---|
|
||||
| `Warn` (default) | Non-blocking: warnings are logged and appended to the deployment result message; the deploy still succeeds. Existing configs never break. |
|
||||
| `Error` | Blocking: warnings fold into the draft-gate reject list — a config with a typo'd enum (or unparseable TagConfig) cannot be re-deployed until fixed. Opt-in per environment once the fleet deploys warning-clean. |
|
||||
|
||||
Running servers are untouched either way — the gate only sees re-deploys. Unmapped drivers
|
||||
(Galaxy, OpcUaClient) are skipped. **Unknown keys are never warned on**: the `TagConfig`
|
||||
blob is a shared namespace (platform intent keys coexist with per-driver address keys and
|
||||
forward-compat keys the typed editors preserve), so there is no single schema to validate
|
||||
against — parsers read known fields and ignore the rest by design.
|
||||
|
||||
> **Note (runtime strictness is deferred).** In this release the runtime parsers stay
|
||||
> *lenient* (a typo'd enum still silently defaults) — only the deploy gate reports it.
|
||||
> Flipping the runtime parsers to strict (a typo'd enum ⇒ `BadNodeIdUnknown` instead of a
|
||||
> wrong-width `Good`) is a deliberate future phase, to be taken only after fleets have run
|
||||
> `Error` mode clean. Tracked in `archreview/plans/STATUS.md`.
|
||||
|
||||
## Galaxy address picker — native-alarm pre-fill
|
||||
|
||||
When the Galaxy address picker selects an attribute that is itself an alarm
|
||||
|
||||
@@ -15,6 +15,16 @@ OtOpcUa is **read-only** against FOCAS; all reads go over the native wire
|
||||
protocol using the documented command IDs. Writes return
|
||||
`BadNotWritable` by design.
|
||||
|
||||
> **Equipment tags are forced read-only (R2-11, migration note).** The equipment-tag parser
|
||||
> (`FocasEquipmentTagParser`) now hard-codes `Writable: false` regardless of any authored
|
||||
> `"writable": true` — previously such a tag advertised a writable node whose write always failed at
|
||||
> the wire with `BadNotWritable`; now it fails one step earlier at the driver seam (same family of Bad
|
||||
> status, no successful operation changes). The AdminUI FOCAS editor shows the Writable checkbox
|
||||
> disabled/unchecked, and the deploy gate warns when a config requests `writable:true`. Additionally,
|
||||
> equipment-tag references now run the same capability-matrix pre-flight authored tags get: a reference
|
||||
> whose address violates the device series' matrix fails to resolve (`BadNodeIdUnknown`) instead of
|
||||
> reaching the wire. Honour `writable` here once PMC writes ship in the wire client.
|
||||
|
||||
> **PDU version (v3 / FANUC 30i/31i).** The wire client **accepts inbound PDU versions `{1, 3}`**
|
||||
> (`FocasWireProtocol`) while still emitting v1 on requests. Older controls + the docker mock answer
|
||||
> v1; modern controls (Makino Pro 5 / FANUC **31i-B**) answer v3. **Validated live against a real
|
||||
|
||||
@@ -43,5 +43,9 @@
|
||||
"DrainIntervalSeconds": 5,
|
||||
"Capacity": 1000000,
|
||||
"DeadLetterRetentionDays": 30
|
||||
},
|
||||
"Deployment": {
|
||||
"_comment": "R2-11 (05/CONV-2): deploy-gate TagConfig strictness. Warn (default) = non-blocking warnings logged + appended to the deployment result message; Error = a config with a typo'd enum or unparseable TagConfig is rejected at the draft gate. Running servers are untouched; the gate only sees re-deploys.",
|
||||
"TagConfigValidationMode": "Warn"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user