From 012cca402a295f07660021ff3368c1800514a10c Mon Sep 17 00:00:00 2001 From: Logan Cusano Date: Sun, 27 Sep 2026 14:14:42 -0400 Subject: [PATCH] Secondary SDR panel: never present an unreported SDR count or run state as fact QA (drb-qa-review) blockers: sdr_count defaulted to 1 for nodes that never sent it, so the panel claimed 'reports 1 SDR (0 spare)'. It is now None until reported, and the count is only quoted alongside a real secondary_sdr_running report. Rows read 'Not reported' instead of 'Waiting for SDR' when the node hasn't said what's running (closes #187). Co-Authored-By: Claude Opus 5.5 --- drb-c2-core/app/internal/mqtt_handler.py | 2 +- drb-c2-core/app/models.py | 2 +- .../components/SecondarySdrPriority.tsx | 23 +++++++++++++++---- drb-frontend/lib/types.ts | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drb-c2-core/app/internal/mqtt_handler.py b/drb-c2-core/app/internal/mqtt_handler.py index a9e9901..de9bc65 100644 --- a/drb-c2-core/app/internal/mqtt_handler.py +++ b/drb-c2-core/app/internal/mqtt_handler.py @@ -114,7 +114,7 @@ class MQTTHandler: "secondary_sdr_mode": payload.get("secondary_sdr_mode", "none"), "secondary_sdr_priority": payload.get("secondary_sdr_priority", []), "secondary_sdr_running": payload.get("secondary_sdr_running"), - "sdr_count": payload.get("sdr_count", 1), + "sdr_count": payload.get("sdr_count"), # None until reported, never a guessed 1 "enforce_override_timeout": payload.get("enforce_override_timeout", True), "is_overridden": False, "override_system_id": None, diff --git a/drb-c2-core/app/models.py b/drb-c2-core/app/models.py index 9ea0359..81bd047 100644 --- a/drb-c2-core/app/models.py +++ b/drb-c2-core/app/models.py @@ -68,7 +68,7 @@ class NodeRecord(BaseModel): # checkin, which is the source of truth; set via PATCH /nodes/{id}. secondary_sdr_priority: List[str] = [] secondary_sdr_running: Optional[List[str]] = None # what the node reports actually running - sdr_count: int = 1 # self-reported by the node's checkin, best-effort + sdr_count: Optional[int] = None # self-reported by the node's checkin; None = never reported enforce_override_timeout: bool = True is_overridden: bool = False override_system_id: Optional[str] = None diff --git a/drb-frontend/components/SecondarySdrPriority.tsx b/drb-frontend/components/SecondarySdrPriority.tsx index 1c26863..2fa6d4f 100644 --- a/drb-frontend/components/SecondarySdrPriority.tsx +++ b/drb-frontend/components/SecondarySdrPriority.tsx @@ -22,6 +22,9 @@ function rowsFrom(priority: string[]): Row[] { export function SecondarySdrPriority({ node, canEdit }: { node: NodeRecord; canEdit: boolean }) { const priority = node.secondary_sdr_priority ?? []; + // null/absent = the node has never reported (older firmware, container down + // at checkin): unknown, not "nothing running" (server-26#187). + const reported = node.secondary_sdr_running != null; const running = node.secondary_sdr_running ?? []; const [rows, setRows] = useState(() => rowsFrom(priority)); const [dirty, setDirty] = useState(false); @@ -62,8 +65,9 @@ export function SecondarySdrPriority({ node, canEdit }: { node: NodeRecord; canE } } - const sdrCount = node.sdr_count ?? 1; - const spare = Math.max(sdrCount - 1, 0); + // Only quote a count the node actually sent alongside its running list; a + // bare sdr_count may be the Firestore default, not a report. + const sdrCount = reported ? node.sdr_count : undefined; let rank = 0; return ( @@ -71,13 +75,24 @@ export function SecondarySdrPriority({ node, canEdit }: { node: NodeRecord; canE

Secondary SDRs

OP25 always keeps its own SDR. Every other SDR runs the next enabled item, top first. - {" "}This node reports {sdrCount} SDR{sdrCount === 1 ? "" : "s"} ({spare} spare). + {" "} + {sdrCount != null + ? `This node reports ${sdrCount} SDR${sdrCount === 1 ? "" : "s"} (${Math.max(sdrCount - 1, 0)} spare).` + : "This node hasn't reported its SDRs yet."}

{rows.map((row, i) => { const meta = MODES.find((x) => x.mode === row.mode)!; const isRunning = running.includes(row.mode); - const state = !row.enabled ? "Off" : dirty ? "Unsaved" : isRunning ? "Running" : "Waiting for SDR"; + const state = !row.enabled + ? "Off" + : dirty + ? "Unsaved" + : !reported + ? "Not reported" + : isRunning + ? "Running" + : "Waiting for SDR"; return (
{row.enabled ? ++rank : ""} diff --git a/drb-frontend/lib/types.ts b/drb-frontend/lib/types.ts index 527e192..2cf9546 100644 --- a/drb-frontend/lib/types.ts +++ b/drb-frontend/lib/types.ts @@ -58,7 +58,7 @@ export interface NodeRecord { secondary_sdr_priority?: string[]; /** What the node's last checkin reported actually running. */ secondary_sdr_running?: string[] | null; - sdr_count?: number; + sdr_count?: number | null; // null = never reported enforce_override_timeout?: boolean; is_overridden?: boolean; override_system_id?: string | null;