diff --git a/CLAUDE.md b/CLAUDE.md index 5465302f..ee6d2f81 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -188,6 +188,13 @@ line and injected nothing. Gone with it: `HandleDiscoveredNodes`, `PartitionDisc browse picker drives it through `Commons/Browsing/DiscoveryDriverBrowser`, which never went through the actor. +⛔ **Tag-set drift detection is slated for REMOVAL (Gitea #523).** It is in `master` today and the +description below is accurate, but the periodic device re-browse is being taken back out: it is +recurring, unconfigurable (interval is a constructor parameter, no appsettings key) and never +live-gated traffic to real PLCs/CNCs/Agents. After removal, **AbCip and FOCAS have no tag-change signal +at all** (they implement no `IRediscoverable`); TwinCAT and MTConnect keep their native ones. The +`IRediscoverable` consumption above is unaffected. + **Tag-set drift detection (2026-07-28) — the second signal, and it is GATED.** `DriverInstanceActor` re-browses on a slow timer (`DefaultDriftCheckInterval`, 5 min) and compares what the remote offers against what an operator authored, raising the same re-browse prompt. This matters most for **AbCip and diff --git a/deferment.md b/deferment.md index c3e619af..2566fe0f 100644 --- a/deferment.md +++ b/deferment.md @@ -368,17 +368,27 @@ appear in §7.1 as well. ## 9. Execution log -Remediation of §8, on branch `feat/deferment-remediation`. Status is updated in the **same commit** as -the work, so this register never disagrees with the tree. +Remediation of §8. **MERGED to `master` and pushed 2026-07-28, merge `914d70bf`** (branch +`feat/deferment-remediation`, 8 commits, merged `--no-ff` so each stage stays individually reviewable and +revertable). Status was updated in the **same commit** as the work, so this register never disagreed with +the tree. + +> ⚠️ **The commit column was wrong until 2026-07-28 and is now corrected.** Each stage's SHA was captured +> with `git rev-parse HEAD` and then written into this file by amending that same commit — which changes +> the SHA. Every value recorded was therefore the *pre-amend* one: a dangling object reachable only through +> the reflog, on no branch, and due to vanish at the next `gc`. The register cited commits that effectively +> did not exist. The SHAs below are verified against `git log e08855fb..914d70bf`. | §8 item | Status | Commit | |---|---|---| -| 1. ACL enforcement decision | ✅ **Done** — decided *make non-enforcement explicit*; Gitea **#520** tracks the wire-up | `d1e88dc4` | -| 2. #518 + #507 | ✅ **Done** — `IRediscoverable` consumed as a re-browse prompt; #507's injection path **deleted**; `IHostConnectivityProbe` half split to Gitea **#521** | `09a401b8`, `97c9f4b4` | -| 3. #516 config discard | ✅ **Done** — seam respawn + re-parse on 3 drivers; Sql/FOCAS deliberately respawn-only | `2dc19f30` | -| 4. G-4 dispatch-map parity | ✅ **Done** — G-1…G-6 all closed, live-verified on docker-dev | `abacf4cf` | -| 5. Bookkeeping sweep | ✅ **Done** — 13 plan files closed, 11 archreview gates written back | `03658c2e` | -| 6. Tier truth | ✅ **Done** — docs reconciled; keep-or-delete is Gitea **#522** | `03658c2e` | +| 1. ACL enforcement decision | ✅ **Done** — decided *make non-enforcement explicit*; Gitea **#520** tracks the wire-up | `53ede679` | +| 2. #518 + #507 | ✅ **Done** — `IRediscoverable` consumed as a re-browse prompt; #507's injection path **deleted**; `IHostConnectivityProbe` half split to Gitea **#521** | `09a401b8`, `adce27e7` | +| 3. #516 config discard | ✅ **Done** — seam respawn + in-place re-parse; completed for all 12 drivers by the follow-up below | `d32d89c3`, `5184a2e1` | +| 4. G-4 dispatch-map parity | ✅ **Done** — G-1…G-6 all closed, live-verified on docker-dev | `6a01358b` | +| 5. Bookkeeping sweep | ✅ **Done** — 13 plan files closed, 11 archreview gates written back | `88ce8df0` | +| 6. Tier truth | ✅ **Done** — docs reconciled; keep-or-delete is Gitea **#522** | `88ce8df0` | +| follow-up A | ✅ **Done** — Sql/FOCAS in-place re-parse made **atomic** | `5184a2e1` | +| follow-up B | ⛔ **Shipped, then reverted by decision** — tag-set drift detection; removal tracked as Gitea **#523** | `e77c8a35` | ### 2026-07-27 — §8.1 ACL non-enforcement made explicit @@ -532,6 +542,22 @@ guarantee. Sql.Tests 226 / FOCAS.Tests 275 pass. ### 2026-07-28 — the two things §8.3 deliberately did NOT build (2 of 2) +> ⛔ **SUPERSEDED SAME DAY — the periodic re-browse is to be REMOVED (owner decision, Gitea #523).** +> It ships in `master` (`e77c8a35`, merged `914d70bf`) and this entry stays as the record of what was +> built and why, but it is **not the intended end state**. The objection is the one this entry does not +> weigh: the check is recurring, **unconfigurable** (the interval is a constructor parameter with no +> appsettings key) and **never live-gated** outbound traffic to real PLCs, CNCs and MTConnect Agents. +> Frequency of an unsolicited device browse is an operational decision, and the design treated it as an +> implementation detail. +> +> **Consequence to carry forward:** AbCip and FOCAS browse a live backend but implement no +> `IRediscoverable`, so after removal they have **no change signal at all** — a PLC re-download is +> invisible until someone re-browses by hand. TwinCAT and MTConnect keep their native event-driven +> signals and lose only redundant polling. The `IRediscoverable` consumption itself (`09a401b8`) is +> **not** affected: the driver tells us, we do not poll, and it costs nothing when nothing changes. +> If AbCip/FOCAS coverage matters later, the shape to reach for is event-driven — or a check that runs +> only when something else already indicates change (a reconnect, a deploy), not a wall-clock timer. + **The discovery-drift detector is built, and gated.** The original objection stands and is now *encoded* rather than used as a reason not to build: Modbus, S7, MQTT, AbLegacy and Sql echo authored config from `DiscoverAsync`, so a diff for them is a tautology. The missing piece was a discriminator — and the