# v3 Batch 1 — greenfield schema + RawPath identity rewiring

**Branch:** `v3/batch1-schema-identity` → `master`
**Plan:** `docs/plans/2026-07-15-v3-batch1-schema-identity-plan.md` · **Design:** `docs/plans/2026-07-15-raw-uns-two-subtree-v3-design.md`

Greenfield, compatibility-breaking. The config schema and every identity seam now speak **RawPath**
(`<Folder/…>/<Driver>/<Device>/<TagGroup/…>/<Tag>`). The full solution builds and all macOS-safe
tests pass; the OPC UA address space is **deliberately dark** (drivers connect, no tag variables
materialize) until Batch 4.

## What changed

- **Schema (greenfield):** new `RawFolder` / `TagGroup` / `UnsTagReference`; reshaped `DriverInstance`
  (−`NamespaceId`, +`RawFolderId?`), `Tag` (raw-only: req `DeviceId`, +`TagGroupId?`,
  −`DriverInstanceId`/`EquipmentId`/`FolderPath`, kept `WriteIdempotent`), `Equipment` (−driver/device
  binding); `Namespace` / `NamespaceKind` / `EquipmentImportBatch` retired. Single squashed `V3Initial`
  migration (stored procs audited — the vestigial generation-model procs were rewritten to v3-valid
  stubs; `sp_ComputeGenerationDiff` diffs the new tables).
- **Identity:** new `Commons/Types/RawPaths.cs` helper; `TagConfigIntent` sheds `FullName`;
  `EquipmentTagRefResolver<TDef>` is byName(RawPath)-only (blob-fallback deleted); new
  `RawTagEntry(RawPath, TagConfig, WriteIdempotent, DeviceName)` delivery record. All 8 drivers
  re-keyed: the per-driver `*EquipmentTagParser` became `*TagDefinitionFactory.FromTagConfig`, options
  `Tags` → `RawTags`, `TagConfig` key normalization (`nodeId` / `attributeRef`; `deviceHostAddress`
  dropped).
- **Endpoint → DeviceConfig:** new `DriverDeviceConfigMerger` (Commons) merges DriverConfig + per-device
  DeviceConfig; multi-device drivers reconstruct their `Devices[]` from Device rows; the artifact injects
  the merged config + `rawTags` at spawn.
- **Pipeline:** `ConfigComposer` snapshots the new tables; `DeploymentArtifact` flattens per-driver
  `RawTagEntry` lists keyed by RawPath (via `RawPathResolver`) + emits an empty (dark) equipment-tag
  plan set; `AddressSpaceComposer`/walker compose folders + VirtualTags + ScriptedAlarms only;
  `DriverHostActor` maps re-keyed to RawPath; `DraftValidator` gains `RawNameInvalid` /
  `HistorianTagnameTooLong` / `UnsEffectiveNameCollision`.
- **AdminUI (compile-only stubs):** Namespace pages → retired banners; driver pages dropped the
  pre-declared Tags editor + NamespaceId; `UnsTreeService` tag-authoring returns "moved to the Raw tree
  (/raw) in v3 Batch 2". (Batch 2/3 build the real `/raw` + reference UI.)

## Verification gate (all four items)

1. **Build + test green.** Full solution builds (0 errors). macOS-safe unit suites: Commons 196,
   Core 260, Core.Abstractions 134, **Configuration 107**, Runtime 355 (42 dark-Batch-4 skips),
   ControlPlane 78, OpcUaServer 335 (4 skips), AdminUI 507 (3 skips), + all 8 driver unit suites
   (Modbus 315, S7 264, AbCip 342, AbLegacy 213, TwinCAT 192, FOCAS 272, OpcUaClient 138, Galaxy 313)
   and the 6 driver IntegrationTests projects compile. Enum-serialization round-trip coverage preserved.
2. **Fresh-DB migration.** `V3Initial` applies clean to a scratch DB; `SchemaComplianceTests` 8/8.
3. **docker-dev boot (live).** Volume wiped → `V3Initial` applied → both central nodes reached Akka
   Up → a seeded Modbus driver **spawned and connected** to the `10.100.0.35:5020` fixture
   (`DriverInstance MAIN-modbus: connected`), deployment **sealed (acks=2)**; AdminUI HTTP 200 on
   `/`, `/clusters`, `/uns` with 0 unhandled exceptions.
4. **Dark address space (Client.CLI browse).** Recursive browse of `opc.tcp://localhost:4840`: the raw
   tag `HR200` and the driver have **0 occurrences**; the custom namespace `ns=2` holds only the empty
   `OtOpcUa` root — no authored variables materialize (Batch 4 lights them up).

## AdminUI Batch-1 stubs (Batch 2/3 replace)

Namespace pages (`NamespaceEdit`, `ClusterNamespaces`) → banners; the 8 driver pages' pre-declared Tags
editors + Namespace bindings removed; `DriverIdentitySection` Namespace dropdown removed;
`UnsTreeService.CreateTagAsync`/`UpdateTagAsync` return the "moved to /raw" failure; per-equipment tag
lists empty.

## Deferred (owned by later batches, not regressions)

- `/raw` project tree + reference-picker UI, dual `ns=Raw`/`ns=UNS` namespaces + tag-variable
  materialization, `{{equip}}` reference-relative resolution — Batches 2–4 (address space intentionally
  dark now).
- User-facing docs beyond the `Configuration.md` v3 note (they describe the not-yet-built UI/address
  space) — updated as Batches 2–4 land.
- Live driver IntegrationTests + one pre-existing `AbCip_Green_AgainstSim` fixture-probe failure
  (AB CIP sim handshake — environment/fixture gate, untouched).
