docs(v3): Batch 2 — /raw authoring tree guide + CLAUDE.md + PR description
v2-ci / build (pull_request) Successful in 3m25s
v2-ci / unit-tests (pull_request) Failing after 8m37s

docs/Raw.md (the /raw project tree: lazy tree, endpoint→DeviceConfig split, manual/CSV/browse
authoring, Calculation driver, routed-flow retirement); CLAUDE.md AdminUI section notes /raw +
DriverTypeNames rewire + Calculation driver; PR body with the 7-item live-gate evidence.

Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
This commit is contained in:
Joseph Doherty
2026-07-16 05:27:31 -04:00
parent c598978d77
commit 33915c9e4d
3 changed files with 167 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
# v3 Batch 2 — `/raw` project-tree AdminUI + `Calculation` driver
Implements `docs/plans/2026-07-15-v3-batch2-raw-ui-calculation-plan.md` (Track 0 + WP1WP8),
executed via the `/v3-batch 2` coordinator: contracts-first fan-out, worktree-isolated Opus
agents per wave, a code-reviewer pass + green build/test after each wave, then the
non-negotiable 7-item live gate on docker-dev.
## What landed
- **Track 0** — reusable `ContextMenu` (right-click + `⋯`, keyboard/focus); RFC-4180
`CsvParser`/`CsvWriter`; `DriverTypeNames` constants + a reflection-discovery guard;
`CalculationEvaluator` core (VirtualTag-evaluator parity).
- **Wave A** — `IRawTreeService`/`RawTreeService` (lazy tree + mutations, integrity guards,
rename-warnings); `/raw` page + lazy `RawTree` with per-node context menus.
- **Wave B** — driver→embeddable-form refactor + `DriverConfigModal`/`DeviceModal`
(endpoint→`DeviceConfig`, Test-connect via merged config); manual tag entry + tag edit
modal + `Calculation` tag editor; CSV import (staged review grid) / export; all wired into
the tree (name/confirm/driver-type dialogs + post-mutation refresh).
- **Wave C** — browse re-target (two-tier gate, merged-config feed, commit → raw tags with
address-field mapping + folder mirroring); `Calculation` driver (`IDependencyConsumer` +
mux adapter, timer + change triggers, `DraftValidator` scriptId + cycle gates);
`DriverTypeNames` rewire + retirement of the routed driver flow.
**Address space stays dark** (values light up in Batch 4). Full solution builds 0 errors;
`AdminUI.Tests` 638 passed / 3 skipped, `Runtime.Tests` 357 passed, driver + guard suites green.
## Reviewer findings fixed in-branch
- **Wave B H1** — driver forms still serialized the endpoint into `DriverConfig`; stripped
the endpoint keys (Modbus/S7/OpcUaClient) so `DeviceConfig` is the sole source (a 2nd
device on a single-Host driver no longer reverts to `127.0.0.1`).
- **Wave C HIGH** — `CalculationDriver.ReinitializeAsync` ignored the new config, so calc
tag/script edits were silently inert after a redeploy and the re-register loop was
decorative. Now rebuilds the tag/dependency table on reinit and re-registers deps after
the delta applies (`DeltaApplied` notification, race-free). New redeploy test proves it.
- Wave-A/B/0 mediums (auto-expand, friendly create-race/delete failures, discovery-driven
guard, CSV comment). Deferred (documented): pre-existing Modbus/S7 form `TimeSpan``*Ms`
DTO mismatch; CSV alarm-subfield/flag-precedence edge cases; OpcUaClient endpoint
precedence; unused `LoadMergedProbeConfigAsync`; refresh collapses sibling expansion.
## Live `/run` gate (docker-dev `:9200`, both central nodes rebuilt)
1. **Authoring + Test-connect**`/raw` → New Modbus driver `gate-modbus` (form body renders,
`PLC family`/`MELSEC` enum dropdowns correct) → Device1 endpoint `10.100.0.35:5020` in
`DeviceConfig`**Test connect OK · 94 ms** against the live Modbus fixture; SQL confirms
endpoint in `DeviceConfig` (`{"Host":"10.100.0.35","Port":5020,"UnitId":1}`).
2. **Historian-tagname deploy block** — a historized tag with a 260-char effective tagname →
`POST /api/deployments` **422** `[HistorianTagnameTooLong] tag 'TAG-gatelong' … is 260 chars
(max 255)`; shortened override → **202 Accepted**.
3. **CSV round-trip + bad-enum** — export produces the exact column dictionary
(`…,Region,Address,ModbusDataType,ByteOrder,…`); importing 1 valid + 1 bad-enum row →
review grid "2 rows, 1 valid, 1 invalid", row-2 error *"Column 'ModbusDataType':
'NOTATYPE' is not a valid ModbusDataType (expected one of: Bool, Int16, UInt16, …)"*,
**Commit disabled** (all-or-nothing; SQL confirms nothing committed); a valid-only import →
Commit → `ImportedTag` lands with typed columns assembled into `TagConfig`.
4. **Browse-commit** — OpcUaClient device → opc-plc (`opc.tcp://10.100.0.35:50000`): two-tier
gate enabled browse, merged config connected, real tree walked (NetworkSet/…/OpcPlc);
multi-selected 2 leaves with "create matching tag-groups" → raw tags land under the
mirrored `Basic` group with DataType + the browse ref in the `nodeId` address field
(`nsu=…;s=AlternatingBoolean`), not an identity key.
5. **Calculation deploy + cycle gate** — a calc tag reading a Modbus RawPath deploys **202**;
a 2-cycle (`calc1/Engine/A``calc1/Engine/B`) → **422** `[CalculationDependencyCycle] …
(members: calc1/Engine/A, calc1/Engine/B)`; clean redeploy 202, no crash loops (both
central nodes stable; only a deploy-reject WRN in the log).
6. **Rename warning** — renaming `gate-modbus``gate-modbus-renamed` fired the warning modal
*"1 historized tag beneath this node will change its RawPath (historian tagname)…"*.
7. **Retired routes**`/clusters/{id}/drivers`, `…/new`, `…/new/{slug}`, `…/{id}` all
return **404**; `/raw` + `/uns` still 200.
## Docs
`docs/Raw.md` (new — the `/raw` authoring tree + endpoint split + CSV + Calculation +
retirement note); `CLAUDE.md` AdminUI section updated; the Calculation mini-design remains the
driver's authority.