#507 was filed as "injection inert in v3, re-migrate onto the raw subtree".
Reading it, the retained code is not revivable: it resolves equipment from
EquipmentNode.DriverInstanceId UNION EquipmentTags, and BOTH are structurally
empty in v3 (AddressSpaceComposer always constructs EquipmentNode with a null
DriverInstanceId; DeploymentArtifact hard-codes an empty EquipmentTags set).
Removing the guard would have changed a log line and injected nothing. So it
is deleted rather than fixed, and #507 closes as superseded by /raw
browse-commit.
Removed: HandleDiscoveredNodes, PartitionDiscoveredByDeviceHost,
ShouldWarnPartition, PlansRoutingEqual, ApplyDiscoveredPlansForDriver,
_discoveredByDriver, the redeploy re-inject tail, DiscoveredNodeMapper,
DiscoveredInjection, AddressSpaceApplier.MaterialiseDiscoveredNodes,
OpcUaPublishActor.MaterialiseDiscoveredNodes, and the Runtime-local copies of
CapturingAddressSpaceBuilder/DiscoveredNode.
The connect-time discovery loop goes too (StartDiscovery, RediscoverTick,
HandleRediscoverAsync, DiscoveredNodesReady, TriggerRediscovery). With
injection gone it had no consumer, and leaving it would either dead-letter or
keep browsing real devices up to ~15x per connect to drop the result.
ITagDiscovery itself stays — the /raw browse picker drives it through
Commons/Browsing/DiscoveryDriverBrowser, which never went through the actor,
so the picker is unaffected.
deferment.md §4.4 called the 18 DiscoveryInjectionDormantV3 tests "Real —
blocked on #507". They are not: they assert an equipment-rooted graft
(EquipmentRootNodeId == "EQ-1") that v3 cannot produce, so they could never
have unskipped as written. Deleted along with DiscoveredNodeMapperTests, the
CapturingAddressSpaceBuilder tests, and the MaterialiseDiscoveredNodes tests
in AddressSpaceApplierTests/OpcUaPublishActorTests. Runtime.Tests skipped:
31 -> 13.
IHostConnectivityProbe is deliberately NOT resolved here. GetHostStatuses()
has no production call site and nothing writes a DriverHostStatus row, but the
table was re-created in the v3 initial migration, so deleting on inference
would be wrong. Split to Gitea #521 with both options costed.
CLAUDE.md, docs/drivers/{Galaxy,TwinCAT,MTConnect}.md updated — they described
the seam as dead.
Build clean; Runtime.Tests 476 passed / 13 skipped; OpcUaServer.Tests 362
passed / 4 skipped.
H1 (HIGH): write-routing key now (AddressSpaceRealm, bareId), not bare-only.
A raw s=<RawPath> and a UNS s=<Area/Line/Equip/Eff> can collide as bare
strings; the bare-only key let a colliding raw+UNS pair route to the WRONG
driver ref (last-writer-wins). The realm the node manager resolves (RealmOf)
is now threaded through IOpcUaNodeWriteGateway.WriteAsync -> RouteNodeWrite ->
_driverRefByNodeId keyed by (realm, bareId). New regression test:
Colliding_raw_and_uns_bare_ids_route_to_their_own_driver_by_realm.
M1 (MEDIUM): discovered-node injection made coherently DORMANT. HandleDiscoveredNodes
hard-short-circuits (single enforcement point; _discoveredByDriver never
populates so the re-inject tail is inert too), with a clear log pointing at the
/raw browse-commit flow. New pin: Discovered_nodes_are_ignored_dormant_in_v3;
the 16+2 v2 injection scenarios re-pointed to an accurate skip reason
(DiscoveryInjectionDormantV3).
M2 (MEDIUM): realm-qualified dual-node self-correction tests —
Failed_uns_write_reverts_uns_node_and_leaves_raw_node_untouched +
Raw_realm_revert_reverts_raw_node_only (the second fails if the realm is dropped).
L1: removed the = AddressSpaceRealm.Uns defaults from the consequential
node-manager mutation methods (WriteValue/WriteAlarmCondition/MaterialiseAlarmCondition/
EnsureFolder/EnsureVariable/UpdateFolderDisplayName/UpdateTagAttributes/
RaiseNodesAddedModelChange/Remove*/RevertOptimisticWriteIfNeeded) + the
AttributeValueUpdate/AlarmStateUpdate records, so the compiler forces explicit
realm; read-only accessors + internal builders retain their defaults.
L3: fixed the stale VirtualTagHostActor class comment (V3NodeIds.Uns, not the
retired EquipmentNodeIds.Variable).
Also: DeploymentArtifactRawUnsParityTests — Raw/UNS node-set byte-parity
round-trip between AddressSpaceComposer.Compose and DeploymentArtifact.ParseComposition.
Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
Fix TESTS only (production already green) so the four in-scope test projects
compile + pass under the v3 dark address space:
- Core.Abstractions.Tests: drop AllowedNamespaceKinds/NamespaceKindCompatibility
from DriverTypeMetadata; rewrite EquipmentTagRefResolver tests to the single-func
RawPath-lookup contract (no parseRef/transient cache).
- Core.Tests: rewrite EquipmentNodeWalkerTests to EquipmentNamespaceContent(Areas,
Lines,Equipment,VirtualTags?,ScriptedAlarms?) — folders + VirtualTags +
ScriptedAlarms only; raw-tag variables dark (skipped Batch-4 placeholder). Drop
retired Equipment.DriverInstanceId.
- Runtime.Tests: rewrite golden corpus + TagConfigCorpusParityTests to the v3
RawPath/RawTagEntry round-trip (byte + TagConfigIntent parity; EquipmentTags dark).
Salvage VirtualTag + ScriptedAlarm artifact parity to the new Compose signature.
Retire the equipment-tag-materialization parity files (Array/Historize/Alias) and
the equipment-device-binding DeviceHost parity to documented skipped placeholders.
Skip 34 dark equipment-tag routing/value/alarm/write/discovery actor tests with a
shared DarkAddressSpaceReasons constant. Re-key cluster-scoped tests to v3 UNS
line->area attribution.
- ControlPlane.Tests: ConfigComposer round-trips re-keyed to RawFolder/Device (no
Namespaces); tag-config gate resolves driver via Device; deploy-gate collision test
re-keyed to UnsEffectiveNameCollision; drop retired BadCrossClusterNamespaceBinding
case.