{
  "planPath": "archreview/plans/R2-09-driver-fleet-batch-plan.md",
  "lastUpdated": "2026-07-12",
  "notes": "Sub-batches B1/B2/B5/B6 are mutually independent PRs; B4 depends on B3 tasks B3.1-B3.2. Ordering inside a sub-batch is encoded via blockedBy. Concurrency tasks are high-risk (see plan Classification fields).",
  "tasks": [
    {
      "id": "B1.1",
      "subject": "B1: failing Modbus transport-desync tests (stall / wrong TxId / truncated header / caller-cancel pin) in ModbusTcpReconnectTests",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B1.2",
      "subject": "B1: ModbusTransportDesyncException + teardown-before-propagate in SendOnceAsync (timeout-OCE + InvalidDataException) \u2014 STAB-3",
      "status": "completed",
      "blockedBy": [
        "B1.1"
      ]
    },
    {
      "id": "B1.3",
      "subject": "B1: full Modbus suites green + optional exception_injector live gate + merge PR 1",
      "status": "completed",
      "blockedBy": [
        "B1.2"
      ]
    },
    {
      "id": "B2.1",
      "subject": "B2: failing AbCipRuntimeConcurrencyTests (reentrancy-guarded FakeAbCipTag hammer) \u2014 STAB-4 repro",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B2.2",
      "subject": "B2: port _runtimeLocks + GetRuntimeLock + dispose onto AbCip DeviceState (AbLegacy pattern)",
      "status": "completed",
      "blockedBy": [
        "B2.1"
      ]
    },
    {
      "id": "B2.3",
      "subject": "B2: wrap ReadSingleAsync/ReadGroupAsync/WriteAsync critical sections in the per-runtime lock; AbCip suite green",
      "status": "completed",
      "blockedBy": [
        "B2.2"
      ]
    },
    {
      "id": "B2.4",
      "subject": "B2: failing FocasFixedTreeConcurrencyTests (writer-vs-reader stress on the four DeviceState caches) \u2014 STAB-5 repro",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B2.5",
      "subject": "B2: convert the four FOCAS caches to ConcurrentDictionary; FOCAS suite green; merge PR 2",
      "status": "completed",
      "blockedBy": [
        "B2.3",
        "B2.4"
      ]
    },
    {
      "id": "B3.1",
      "subject": "B3: ConnectionBackoff primitive in Core.Abstractions (static ComputeDelay ported from S7 + instance attempt-throttle) + ConnectionBackoffTests \u2014 STAB-8 seam, hand-off to R2-01",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B3.2",
      "subject": "B3: PollGroupEngine v2 \u2014 backoffCap + consecutiveFailures + caller-token-filtered OCE catches (STAB-14-class engine guard) + engine tests FAIL->PASS",
      "status": "completed",
      "blockedBy": [
        "B3.1"
      ]
    },
    {
      "id": "B3.3",
      "subject": "B3: wire onError->health + 30s backoffCap into Modbus/AbCip/AbLegacy engine ctors (tests first per driver) \u2014 STAB-9",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B3.4",
      "subject": "B3: wire onError->health + 30s backoffCap into TwinCAT/FOCAS engine ctors (tests first per driver) \u2014 STAB-9",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B3.5",
      "subject": "B3: failing S7PollEngineMigrationTests (array-cadence PERF-3, rapid sub/unsub STAB-6, sustained-failure health+backoff)",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B3.6",
      "subject": "B3: retire the S7 poll fork onto PollGroupEngine (delete PollLoopAsync/PollOnceAsync/HandlePollFailure/SubscriptionState; ComputeBackoffDelay -> ConnectionBackoff) \u2014 CONV-1; cross-ref R2-01",
      "status": "completed",
      "blockedBy": [
        "B3.3",
        "B3.4",
        "B3.5"
      ]
    },
    {
      "id": "B3.7",
      "subject": "B3: engine + all five driver suites green; s7 fixture bounce live gate; merge PR 3",
      "status": "completed",
      "blockedBy": [
        "B3.6"
      ]
    },
    {
      "id": "B4.1",
      "subject": "B4: TwinCAT per-device connect-attempt backoff (data-path gate, probe bypass + instant reset) tests FAIL->PASS \u2014 STAB-8",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B4.2",
      "subject": "B4: FOCAS per-device connect-attempt backoff across the three loops, tests FAIL->PASS \u2014 STAB-8",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B4.3",
      "subject": "B4: AbCip evict-recreate (Forward Open) create-path throttle, cache-hit unaffected, tests FAIL->PASS \u2014 STAB-8",
      "status": "completed",
      "blockedBy": [
        "B3.2"
      ]
    },
    {
      "id": "B4.4",
      "subject": "B4: S7 seam comment pointing R2-01 at ConnectionBackoff; suites green; merge PR 4",
      "status": "completed",
      "blockedBy": [
        "B4.1",
        "B4.2",
        "B4.3"
      ]
    },
    {
      "id": "B5.1",
      "subject": "B5: failing ResolveHost tests in AbCip/AbLegacy/TwinCAT/FOCAS (equipment ref for device B must key B; fallback regression pins) \u2014 CONV-4 repro",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B5.2",
      "subject": "B5: rewrite the four multi-device ResolveHost bodies via EquipmentTagRefResolver + empty-host guard",
      "status": "completed",
      "blockedBy": [
        "B5.1"
      ]
    },
    {
      "id": "B5.3",
      "subject": "B5: Modbus \u2014 optional unitId in ModbusEquipmentTagParser + ResolveHost via resolver (per-unit breaker key); scope-note vs R2-11",
      "status": "completed",
      "blockedBy": [
        "B5.1"
      ]
    },
    {
      "id": "B5.4",
      "subject": "B5: all five driver suites green; merge PR 5",
      "status": "completed",
      "blockedBy": [
        "B5.2",
        "B5.3"
      ]
    },
    {
      "id": "B6.1",
      "subject": "B6: failing ReplayFailure_EmitsBadQuality_AndDegradesHealth in TwinCATReconnectReplayTests \u2014 STAB-16 repro",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "B6.2",
      "subject": "B6: NativeRegistration HasLiveHandle/MarkHandleDead/OnUnavailable + replay-catch Bad-quality emit + health degrade \u2014 STAB-16",
      "status": "completed",
      "blockedBy": [
        "B6.1"
      ]
    },
    {
      "id": "B6.3",
      "subject": "B6: RetryDeadRegistrationsAsync on successful probe tick (under ConnectGate), tests FAIL->PASS \u2014 STAB-16",
      "status": "completed",
      "blockedBy": [
        "B6.2"
      ]
    },
    {
      "id": "B6.4",
      "subject": "B6: post-SwapHandle ownership re-check retracts the fresh ADS notification when unsubscribe raced the replay, tests FAIL->PASS \u2014 STAB-17",
      "status": "completed",
      "blockedBy": [
        "B6.3"
      ]
    },
    {
      "id": "B6.5",
      "subject": "B6: RecycleClientAsync takes the probe ct (no CancellationToken.None gate wait) + in-catch OCE wrap, tests FAIL->PASS \u2014 STAB-12 compounded part",
      "status": "completed",
      "blockedBy": [
        "B6.4"
      ]
    },
    {
      "id": "B6.6",
      "subject": "B6: full TwinCAT suite green (fake-client authoritative, no TC3 fixture); merge PR 6",
      "status": "completed",
      "blockedBy": [
        "B6.5"
      ]
    }
  ]
}