{
  "planPath": "docs/plans/2026-06-17-stillpending-bit-rmw-writes.md",
  "designPath": "docs/plans/2026-06-17-stillpending-bit-rmw-writes-design.md",
  "designCommit": "cf231a78",
  "baseMaster": "67da6d4f",
  "branch": "feat/stillpending-bit-rmw-writes",
  "scope": "Inbound bit-index RMW writes. AbLegacy: drop the B/I/O exclusion so B/I/O-file bit writes route through the existing WriteBitInWordAsync RMW (input-image write is device-gated, faithful status). TwinCAT: driver-level BOOL-within-word RMW in TwinCATDriver.WriteAsync (read parent as UDInt->uint, flip bit, write back, per-parent SemaphoreSlim) + remove the AdsTwinCATClient throw. Closes 2 stillpending.md §2 lines. NO Commons/wire/proto/EF change; NO bUnit. Live = fixture-gated/unit-proven.",
  "dependencyGraph": "{T1 ∥ T2} → T3 → T4 → T5 (T1 AbLegacy ∥ T2 TwinCAT touch disjoint projects)",
  "tasks": [
    {"id": 1, "subject": "AbLegacy B/I/O-file bit writes (drop the WriteBitInWordAsync exclusion) + tests", "classification": "standard", "status": "completed", "commit": "5c6b7cd6", "reviewFixCommit": "c48f2999", "review": "spec ✅; code APPROVE. Drop-the-exclusion routes B/I/O (16-bit Int parent) through the unchanged WriteBitInWordAsync; T/C/R can't reach (parser rejects subElement+bitIndex). Review-fix c48f2999 added a device-rejection-status test via a test-only WriteStatusOverride hook on FakeAbLegacyTag (read stays Good, write returns ErrorTimeout→BadTimeout)."},
    {"id": 2, "subject": "TwinCAT BOOL-within-word driver-level RMW (read/flip/write parent as UDInt, per-parent lock) + remove client throw + tests", "classification": "standard", "status": "completed", "commit": "a73e20fb", "reviewFixCommit": "340c145e", "review": "spec ✅; code APPROVE. Driver-level RMW (fake-testable; real AdsClient non-injectable). Review-fix 340c145e: dispose+clear _bitRmwLocks on ShutdownAsync+Dispose; null-parent-after-Good guard → BadCommunicationError (was silently zeroing the word). Removed the AdsTwinCATClient throw."},
    {"id": 3, "subject": "Docs (AbLegacy.md + TwinCAT.md) + plan-record §2 clear (do NOT stage stillpending.md)", "classification": "small", "status": "completed", "commit": "56ccaa79", "review": "code ✅ accurate, no overclaim (single-bit BOOL-within-word only)."},
    {"id": 4, "subject": "Full build + AbLegacy + TwinCAT tests + final integration review", "classification": "standard", "status": "completed", "reviewFixCommit": "098adf43", "review": "build 0 errors; AbLegacy 195, TwinCAT 159 green. Final integration review = SHIP, all 6 checks PASS. Closed its one Important follow-up (098adf43): AbLegacy DeviceState._rmwLocks (+_creationLocks/_runtimeLocks) now disposed+cleared in DisposeRuntimes() with a reinit regression test — symmetry with the TwinCAT fix."},
    {"id": 5, "subject": "Live /run best-effort (fixture-gated) + finish branch (merge to master + push) + memory", "classification": "standard", "status": "completed", "live": "AbLegacy LIVE-PROVEN ON THE WIRE vs a local ab_server SLC500 sim (built from the AbLegacy.IntegrationTests/Docker fixture, --plc=SLC500 --tag=B3[10], localhost:44818) via the AbLegacy CLI (same AbLegacyDriver codec, gateway ab://localhost:44818/1,0): zeroed B3:0 → set B3:0/0 → set B3:0/3 → read word=9 (0b1001, RMW set preserved bit 0) → read B3:0/3=true → clear B3:0/0 → read word=8 (0b1000, RMW clear preserved bit 3); all Good 0x0. Pre-branch the first B3:0/0 bit write was BadNotSupported. TwinCAT live stays OPERATOR-GATED (no local ADS runtime — Windows-only); unit-proven (159)."}
  ],
  "executionState": "COMPLETE — merged to master + pushed. AbLegacy B/I/O bit writes (drop exclusion → existing RMW) + TwinCAT BOOL-within-word driver-level RMW shipped. Build 0 errors; AbLegacy 195, TwinCAT 159 green. AbLegacy B-file bit RMW LIVE-PROVEN on the wire vs a local ab_server SLC500 sim (set/clear/preserve compose); TwinCAT unit-proven (operator-gated live). NO contract/wire/proto/EF change.",
  "reviewFollowUps": [
    "AbLegacy I/O-image (I/O-file) bit writes are enabled but device-gated (the PLC may reject an Input-image write) — unit-proven; not exercised live (ab_server SLC500 seeds no I/O image files, only N/B/F/L). Confirm against a real SLC/MicroLogix.",
    "TwinCAT BOOL-within-word live /run is OPERATOR-GATED — needs a real TwinCAT/ADS target (Windows-only runtime); unit-proven only.",
    "Both: the per-parent RMW lock serialises the driver's own bit-writers but cannot guard against the PLC program writing the same word mid-RMW (inherent to RMW; documented)."
  ],
  "lastUpdated": "2026-06-17"
}
