{ "planPath": "archreview/plans/R3-04-one-role-authority-plan.md", "lastUpdated": "2026-08-20", "tasks": [ { "id": "T1", "subject": "EffectiveRoles value type: one factory, one precedence (Cluster:Roles > OTOPCUA_ROLES), two-layer normalization, both-source validation (01/S-12, 01/C-9)", "status": "completed", "blockedBy": [] }, { "id": "T2", "subject": "AddOtOpcUaCluster post-configures AkkaClusterOptions.Roles from the registered EffectiveRoles instance (structural identity, no second derivation)", "status": "completed", "blockedBy": [ "T1" ] }, { "id": "T3", "subject": "Program.cs derives hasAdmin/hasDriver/roleSuffix/banner from the authority, not OTOPCUA_ROLES alone (01/S-12 = 03/C5)", "status": "completed", "blockedBy": [ "T2" ] }, { "id": "T4", "subject": "All four role-scoped validators consume EffectiveRoles: SplitTopology/MeshTransport/Telemetry/ConfigSource (01/C-9)", "status": "completed", "blockedBy": [ "T2" ] }, { "id": "T5", "subject": "RoleParser.GossipedRolesHave adopted at the six ordinal gossip predicates + ClusterRoleInfo index (Primary election, R2-04 member count, /fleet)", "status": "completed", "blockedBy": [ "T1" ] }, { "id": "T6", "subject": "Warning when Cluster:Roles and OTOPCUA_ROLES disagree; boot banner names the winning source", "status": "completed", "blockedBy": [ "T3" ] }, { "id": "T7", "subject": "AkkaClusterOptionsValidator fails start when a late config layer changes Cluster:Roles (role overlays may not set it)", "status": "completed", "blockedBy": [ "T2" ] }, { "id": "T8", "subject": "RoleAuthorityGuardTests: source scan proving no production file outside the authority reads OTOPCUA_ROLES, Cluster:Roles, or a bare role literal", "status": "completed", "blockedBy": [ "T3", "T4", "T5" ] }, { "id": "T9", "subject": "ClusterRolesOnlyBootTests: a Cluster:Roles-only node wires the driver stack (with a pre-fix negative control)", "status": "completed", "blockedBy": [ "T3" ] }, { "id": "T10", "subject": "Docs sweep: Configuration.md roles section (rewrite the two-knobs note), ServiceHosting.md, Redundancy.md, v2/Cluster.md, install script comment, CLAUDE.md", "status": "completed", "blockedBy": [] }, { "id": "T11", "subject": "docker-dev: site-b-2 becomes Cluster:Roles-only so the rig can exhibit the two-authority split (theme 2 rig-fidelity)", "status": "completed", "blockedBy": [] }, { "id": "T12", "subject": "docker-dev: site-a-2 pins a miscased Cluster__Roles__0 = 'Driver' as the standing 01/C-9 pin", "status": "completed", "blockedBy": [] }, { "id": "T13", "subject": "LIVE GATE: docker-dev six-node rig \u2014 pre-fix baseline, per-node banner vs /hosts gossip parity, site-a Primary election, deploy seals Applied incl. site-b-2, unknown-role fail-fast", "status": "completed", "blockedBy": [ "T3", "T4", "T5", "T6", "T7", "T8", "T9", "T11", "T12" ], "note": "PASS 2026-08-20 on docker-dev: pre-fix control at 7c77a8c7 FAILED as required (site-b-2 roles=[], no OPC UA endpoint, deploy TimedOut 5/6; site-a-2 gossiped `Driver`, ServiceLevel 0, \"carries none of the active roles\"); post-fix master image: six banners source=Configured, deploy Sealed 6/6 in 224 ms, one 250/240 split per pair, T6 disagreement Warning fires, Cluster__Roles__0=operator refuses to start. See the plan's \"Live gate result (2026-08-20)\"." } ] }