{
  "planPath": "archreview/plans/R2-04-failure-visibility-plan.md",
  "lastUpdated": "2026-07-12",
  "tasks": [
    {
      "id": "T1",
      "subject": "Extend AddressSpaceApplyOutcome with RebuildFailed/FailedNodes + SafeRebuild returns bool (01/S-1)",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "T2",
      "subject": "Safe* helpers return bool; Apply tallies removal-pass failures into FailedNodes",
      "status": "completed",
      "blockedBy": [
        "T1"
      ]
    },
    {
      "id": "T3",
      "subject": "Materialise* passes return swallowed-failure counts (void -> int, five methods)",
      "status": "completed",
      "blockedBy": [
        "T2"
      ]
    },
    {
      "id": "T4",
      "subject": "OpcUaApplyFailed counter + OpcUaPublishActor Error/meter surfacing of degraded applies",
      "status": "completed",
      "blockedBy": [
        "T3"
      ]
    },
    {
      "id": "T5",
      "subject": "Galaxy internal IMxWriteOps seam under GatewayGalaxyDataWriter (sealed SDK session untestable) (06/S-1)",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "T6",
      "subject": "Galaxy fail-closed write on AdviseSupervisory failure -> BadCommunicationError, no raw write",
      "status": "completed",
      "blockedBy": [
        "T5"
      ]
    },
    {
      "id": "T7",
      "subject": "Galaxy counters galaxy.writes.advise_failed + galaxy.writes.unconfirmed (empty-statuses Good metered)",
      "status": "completed",
      "blockedBy": [
        "T6"
      ]
    },
    {
      "id": "T8",
      "subject": "Galaxy skip-gated live smoke: normal advised write still returns Good post-change",
      "status": "completed",
      "blockedBy": [
        "T6"
      ]
    },
    {
      "id": "T9",
      "subject": "PrimaryGatePolicy pure class + truth-table tests + otopcua.redundancy.primary_gate_denied counter (03/S4)",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "T10",
      "subject": "Wire DriverHostActor gates (:977/:1039/:1095) through PrimaryGatePolicy with member-count provider + S5 log-once",
      "status": "completed",
      "blockedBy": [
        "T9"
      ]
    },
    {
      "id": "T11",
      "subject": "DriverHostActorPrimaryGateTests: deny-on-unknown multi-node, allow single-node, ack/emit variants, meter tags",
      "status": "completed",
      "blockedBy": [
        "T10"
      ]
    },
    {
      "id": "T12",
      "subject": "ScriptedAlarmHostActor alerts-emit gate adopts PrimaryGatePolicy (consistency extension)",
      "status": "completed",
      "blockedBy": [
        "T9"
      ]
    },
    {
      "id": "T13",
      "subject": "In-process 2-node delivery-path test (TwoNodeClusterHarness): DPS-delivered snapshot drives the gate",
      "status": "completed",
      "note": "DONE 2026-07-14: PrimaryGateFailoverTests PASS 1/1 (3s), ran without SQL (gate test needs only the in-process 2-node cluster + DPS snapshot). Delivered snapshot promotes exactly one Primary; negative-control (broken subscribe) times out. See STATUS.md.",
      "blockedBy": [
        "T11"
      ]
    },
    {
      "id": "T14",
      "subject": "docs/Redundancy.md: gate policy table, boot-window client experience, new meters, Galaxy fail-closed semantics",
      "status": "completed",
      "blockedBy": []
    },
    {
      "id": "T15",
      "subject": "LIVE GATE: 2-node docker-dev rig verify (ServiceLevel 240/100, boot-window write rejected+reverted, single /alerts row)",
      "status": "completed",
      "note": "DONE 2026-07-14: docker-dev 2-node rig rebuilt from master 365a90e6 + Modbus sim up. ServiceLevel 250/240; boot-window write rejected \"not primary (role unknown)\" (meter reason=role-unknown,site=write); steady-state secondary rejected \"not primary\" + node reverted (read-back 1234 not 9999, meter reason=secondary); primary write reaches device; clean apply (no apply.failed). Full record in STATUS.md.",
      "blockedBy": [
        "T10",
        "T11",
        "T12",
        "T13"
      ]
    }
  ]
}