# Code Review — Core.Scripting.Abstractions | Field | Value | |---|---| | Module | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions` | | Reviewer | Claude Code | | Review date | 2026-06-19 | | Commit reviewed | `7286d320` | | Status | Reviewed | | Open findings | 2 | ## Checklist coverage A comprehensive review completes every category, recording "No issues found" where a category produced nothing rather than leaving it blank. | # | Category | Result | |---|---|---| | 1 | Correctness & logic bugs | 1 finding (Core.Scripting.Abstractions-004) | | 2 | OtOpcUa conventions | No issues found | | 3 | Concurrency & thread safety | No issues found | | 4 | Error handling & resilience | No issues found | | 5 | Security | No issues found | | 6 | Performance & resource management | No issues found | | 7 | Design-document adherence | 1 finding (Core.Scripting.Abstractions-007) | | 8 | Code organization & conventions | No issues found | | 9 | Testing coverage | No issues found | | 10 | Documentation & comments | 5 findings (001–003, 005–006) | ## Findings ### Core.Scripting.Abstractions-001 | Field | Value | |---|---| | Severity | Low | | Category | Documentation & comments | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/AlarmPredicateContext.cs:43,46` | | Status | Resolved | **Description:** `AlarmPredicateContext.GetTag` uses the magic constant `0x80340000u` twice with no inline comment. The sibling `VirtualTagContext.GetTag` annotates both sites with `/* BadNodeIdUnknown */`, making the intent immediately clear. The inconsistency makes the alarm context harder to audit for correctness. **Recommendation:** Add `/* BadNodeIdUnknown */` inline comments matching the pattern in `VirtualTagContext.cs:55,58`. **Resolution:** Applied inline — added `/* BadNodeIdUnknown */` comments to both `0x80340000u` literals in `AlarmPredicateContext.GetTag`. Verified by build (no test project for this module). 2026-06-19. --- ### Core.Scripting.Abstractions-002 | Field | Value | |---|---| | Severity | Low | | Category | Documentation & comments | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/AlarmPredicateContext.cs:14` | | Status | Resolved | **Description:** The `` block opens with "Per Phase 7 plan Shape A decision...". The project-wide rename (master commit `40e8a23e`) retired the "Phase 7" prefix for the address-space pipeline; comments referencing it by that name are stale and confuse new readers. The authoritative plan document is `docs/v2/implementation/phase-7-scripting-and-alarming.md` (decision row 4, Shape A), so the relevant fix is to cite the design doc rather than the internal plan-phase label. **Recommendation:** Replace "Per Phase 7 plan Shape A decision" with "Per the scripting design (see `docs/v2/implementation/phase-7-scripting-and-alarming.md`, decision row 4)". **Resolution:** Applied inline — updated the stale "Phase 7 plan Shape A decision" reference to cite the design doc directly. Verified by build (no test project for this module). 2026-06-19. --- ### Core.Scripting.Abstractions-003 | Field | Value | |---|---| | Severity | Low | | Category | Documentation & comments | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/ScriptContext.cs:9,10` | | Status | Resolved | **Description:** The `ScriptContext` summary comment reads "Phase 7 plan decision #6: scripts can read any tag, write only to virtual tags...". The "Phase 7 plan" label is stale after the global rename. Decision #6 in `docs/v2/plan.md` remains the correct reference, but the wording should match the current naming convention. **Recommendation:** Replace "Phase 7 plan decision #6" with "plan decision #6 (see `docs/v2/plan.md`)". **Resolution:** Applied inline — updated the stale "Phase 7 plan decision #6" reference to use the current naming convention. Verified by build (no test project for this module). 2026-06-19. --- ### Core.Scripting.Abstractions-004 | Field | Value | |---|---| | Severity | Low | | Category | Correctness & logic bugs | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/ScriptContext.cs:84` | | Status | Open | **Description:** `ScriptContext.Deadband` has no guard or documentation for invalid `tolerance` values. A negative `tolerance` makes `Math.Abs(current - previous) > tolerance` trivially `true` for all finite inputs (any absolute value exceeds a negative number), so `Deadband(x, y, -1.0)` always reports a change regardless of the actual difference — silently inverting the intended semantics. A NaN `tolerance` makes the comparison always `false` (no change ever detected). Neither case is caught by the compiler and there is no runtime warning, so script authors have no feedback when they supply an invalid threshold. **Recommendation:** Document the behavior clearly in the `` XML doc. If a design decision allows it, add an `ArgumentOutOfRangeException` guard for `tolerance < 0`, but this changes the public API contract for authored scripts and needs a plan-level decision. Minimum fix (doc-only, zero-risk): ``` /// The minimum change magnitude to detect (must be >= 0). /// Negative values cause the function to always return true; NaN always returns false. ``` **Resolution:** _(Open — the doc-only fix is zero-risk but the tolerance param is part of the locked script API surface; the decision on whether to also add a guard belongs at the plan level. Deferred until that call is made. No test project exists for this module.)_ --- ### Core.Scripting.Abstractions-005 | Field | Value | |---|---| | Severity | Low | | Category | Documentation & comments | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/VirtualTagContext.cs:21` | | Status | Resolved | **Description:** The `` second paragraph ends with "Mutable state across runs is a future decision (e.g. a dedicated `ctx.Memory` dictionary); not in scope for Phase 7." The "Phase 7" label is stale (see findings 002 and 003). The forward-looking note itself is still valid. **Recommendation:** Replace "not in scope for Phase 7" with "not currently in scope". **Resolution:** Applied inline — removed the stale Phase 7 label. Verified by build (no test project for this module). 2026-06-19. --- ### Core.Scripting.Abstractions-006 | Field | Value | |---|---| | Severity | Low | | Category | Documentation & comments | | Location | `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/ScriptContext.cs:35-36,55-58` | | Status | Resolved | **Description:** The `` for `ScriptContext.GetTag` describes the path syntax as "forward-slash delimited, matching the Equipment-namespace browse tree" but makes no mention of the `{{equip}}` equipment-relative token introduced in master commit `27c34a55`. Operators reading the XML doc for IntelliSense will not know the token exists. The `SetVirtualTag` remarks similarly omit it. **Recommendation:** Add a note to the `GetTag` (and `SetVirtualTag`) ``: "The reserved `{{equip}}` token may appear in the path literal; it is substituted with the owning equipment's tag-base prefix at deployment time. See `docs/ScriptEditor.md` — Equipment-relative tag paths." **Resolution:** Applied inline — added `{{equip}}` token mention to `GetTag` and `SetVirtualTag` remarks in `ScriptContext.cs`. Verified by build (no test project for this module). 2026-06-19. --- ### Core.Scripting.Abstractions-007 | Field | Value | |---|---| | Severity | Low | | Category | Design-document adherence | | Location | `docs/ScriptedAlarms.md:298` | | Status | Open | **Description:** `docs/ScriptedAlarms.md` line 298 lists the file path as `src/Core/ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms/AlarmPredicateContext.cs`. The file was moved to `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/AlarmPredicateContext.cs` as part of the A0 memory-reduction plan (`docs/plans/2026-06-07-virtualtag-script-memory.md`, step 1 `git mv` list). The stale path sends readers to a non-existent location. The namespace was intentionally preserved (`Core.ScriptedAlarms`) so no `using` changes were needed — the file location is the only thing that changed. **Recommendation:** Update `docs/ScriptedAlarms.md` line 298: ``` - `src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/AlarmPredicateContext.cs` — script-side `ScriptContext` (read-only, write rejected) ``` **Resolution:** _(Open — out of scope for this module's src-only fix pass. Should be fixed in a docs-cleanup commit targeting `docs/ScriptedAlarms.md`.)_