# Incremental Sync How a change reaches the running server's address space in v3. There is exactly one path that changes the served address space — the **deploy artifact** — and one advisory signal (`IRediscoverable`) that never changes it. ## The deploy-artifact path 1. **Compose.** `ConfigComposer` (`src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs`) snapshots the whole Config DB into one gzip-compressed JSON blob (`Deployment.ArtifactBlob`) whose `RevisionHash` is the SHA-256 of those exact bytes. A deployment is fleet-wide; each node self-scopes the artifact (`DeploymentArtifact.ResolveClusterScope`). 2. **Dispatch.** `ConfigPublishCoordinator` (`.../ControlPlane/Coordinators/`) sends each enabled `ClusterNode` a payload-free dispatch and waits for acks until the apply deadline. A node obtains the bytes either straight from central SQL (`ConfigSource:Mode=Direct`) or over gRPC with a SHA-256 check (`FetchAndCache`) — see `docs/Configuration.md` §`ConfigSource`. 3. **Apply.** `DriverHostActor` (`src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/`) opens the artifact and reconciles in two halves: - **Drivers** — `DriverSpawnPlanner.Compute` yields `ToStop` / `ToApplyDelta` / `ToSpawn`. A changed driver config reaches the live driver as `IDriver.ReinitializeAsync` on its `DriverInstanceActor`; a removed driver is stopped. An unreadable or empty blob is "no answer": the apply fails and the running children are left alone (never an empty configuration). - **Address space** — `AddressSpaceComposer` builds an `AddressSpaceComposition` from the artifact; `AddressSpacePlan` diffs it against the last applied composition; `AddressSpaceChangeClassifier` picks the delta class; `AddressSpaceApplier` (`src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/`) mutates `OtOpcUaNodeManager` through the sink. 4. **Classes** (`AddressSpaceChangeKind`): `Empty`, `AttributeOnly`, `PureAdd`, `PureRemove`, `AddRemoveMix` — all applied surgically, so unrelated MonitoredItems survive — and `Rebuild`, the default-closed fallback for anything the classifier does not whitelist. Any surgical `false`/throw also ratchets to a safe rebuild, and a degraded non-rebuild apply does not advance the last-applied composition (the next deploy re-diffs and re-ensures). The whitelist, the UNS-mirror repair rule and the add/remove announcements are described in the "surgical deploys" paragraph of `CLAUDE.md` and `docs/plans/2026-08-15-surgical-deploys.md`. Virtual tags and scripted alarms ride the same artifact and the same plan; a script edit is just another changed record in the composition (see `docs/VirtualTags.md`, `docs/ScriptedAlarms.md`). ## Driver-backend rediscovery is advisory Drivers whose backend signals a tag-set change implement `IRediscoverable` (`src/Core/ZB.MOM.WW.OtOpcUa.Core.Abstractions/IRediscoverable.cs`; `OnRediscoveryNeeded` carries a `Reason` and an optional `ScopeHint`). Raisers today: **Galaxy** (`DeployWatcher`, new `time_of_last_deploy`), **TwinCAT** (ADS symbol-version-changed), **MQTT/Sparkplug** and **MTConnect**. AbCip, AbLegacy, FOCAS, Modbus, S7 and the rest raise nothing. `DriverInstanceActor` is the only production consumer: it subscribes in `PreStart`, detaches in `PostStop`, and folds the raise into the driver-health snapshot (`DriverHealthChanged.RediscoveryNeededUtc` / `.RediscoveryReason`), which the AdminUI `/hosts` page shows as a **re-browse** chip. **It never rebuilds or edits the served address space** — v3 raw tags are authored explicitly in `/raw`, so an operator re-browses the device and commits, and the result reaches the server as an ordinary deployment. `ScopeHint` is not read by the actor. (The older runtime injection path was removed, #507 closed as superseded.) ## Not part of the v3 path - `sp_PublishGeneration` / `sp_ComputeGenerationDiff` are stubs that `RAISERROR` in the v3 baseline migration; there is no generation-publish flow and no `DraftRevisionToken`. - `GenericDriverNodeManager` (`Core/OpcUa/`) is retained scaffolding with no production caller; the diff / teardown / subscription-replay "rebuild flow" described by earlier versions of this page does not exist in `src/`. ## Key source files - `src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/AdminOperations/ConfigComposer.cs` — artifact composition + hash - `src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/Coordinators/ConfigPublishCoordinator.cs` — dispatch + ack set - `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs` — apply + driver reconcile - `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs` — node-side artifact parsing - `src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/AddressSpaceComposer.cs`, `AddressSpacePlan.cs`, `AddressSpaceChangeClassifier.cs`, `AddressSpaceApplier.cs` — compose, diff, classify, apply - `src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs` — `IRediscoverable` consumer - `src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/ISurgicalAddressSpaceSink.cs` — in-place update seam