From 5c73cd32b34b8aff4e863d836b578546bccc099c Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 25 Aug 2026 12:55:11 +0200 Subject: [PATCH] fix(config): bind the instance-identity settings to the env vars the chart sets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pydantic-settings derives the env var from the field name, so `instance_role` bound INSTANCE_ROLE and silently ignored the chart's STATE_HUB_INSTANCE_ROLE. The value reached the pod and was discarded: central reported "unknown" while its ConfigMap said "primary". That is the same failure this workplan closes — configuration declared but never reaching what it configures — reintroduced while building the guard against it. Rendering the key in `helm template` was mistaken for evidence that it bound. Renames to state_hub_instance_role / state_hub_instance_label, matching the existing state_hub_report_dir precedent, so the env var the chart already sets is the one that binds. tests/test_instance_identity.py asserts the env var *name* binds, which is the check that would have caught this before deploy, plus the unknown default and rejection of invalid roles. Refs CUST-WP-0067-T03 Co-Authored-By: Claude Opus 5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 2583210@bnt-lap001 Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006 --- api/config.py | 7 ++++-- api/routers/state.py | 4 ++-- tests/test_instance_identity.py | 38 +++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/test_instance_identity.py diff --git a/api/config.py b/api/config.py index 99c1c8f..7bcb11e 100644 --- a/api/config.py +++ b/api/config.py @@ -17,9 +17,12 @@ class Settings(BaseSettings): # that need the authoritative hub must be able to tell the difference. # Answering on a port is not evidence of authority — that assumption cost # seven weeks of onboarding (CUST-WP-0067-T03, ADR-010). - instance_role: Literal["primary", "cache", "unknown"] = "unknown" + state_hub_instance_role: Literal["primary", "cache", "unknown"] = "unknown" + # Env vars are STATE_HUB_INSTANCE_ROLE / STATE_HUB_INSTANCE_LABEL — the + # field name *is* the binding, so renaming either breaks the deployment + # silently. Covered by tests/test_instance_identity.py. # Free-form label to name *which* instance answered, e.g. "railiance01". - instance_label: str | None = None + state_hub_instance_label: str | None = None debug: bool = False state_hub_report_dir: str = "reports/recently-on-scope" state_hub_markitect_cli_path: str | None = None diff --git a/api/routers/state.py b/api/routers/state.py index 319c849..bbc231e 100644 --- a/api/routers/state.py +++ b/api/routers/state.py @@ -1113,8 +1113,8 @@ async def health_check(session: AsyncSession = Depends(get_session)) -> dict: "status": "ok", "db": "connected", # Identity, so a caller can verify it reached the hub it meant to. - "instance_role": settings.instance_role, - "instance_label": settings.instance_label, + "instance_role": settings.state_hub_instance_role, + "instance_label": settings.state_hub_instance_label, } except Exception as exc: return JSONResponse( diff --git a/tests/test_instance_identity.py b/tests/test_instance_identity.py new file mode 100644 index 0000000..6ed5b4e --- /dev/null +++ b/tests/test_instance_identity.py @@ -0,0 +1,38 @@ +"""The instance-identity env vars must actually bind (CUST-WP-0067-T03). + +The chart sets STATE_HUB_INSTANCE_ROLE. pydantic-settings derives the env var +from the *field name*, so a field called `instance_role` silently ignores it and +the hub reports "unknown" while the ConfigMap says "primary" — config that is +declared but never reaches what it configures, which is the entire failure this +workplan exists to close. +""" + +from __future__ import annotations + +import pytest + +from api.config import Settings + + +@pytest.mark.parametrize( + "env_name,attr,value", + [ + ("STATE_HUB_INSTANCE_ROLE", "state_hub_instance_role", "primary"), + ("STATE_HUB_INSTANCE_LABEL", "state_hub_instance_label", "railiance01"), + ], +) +def test_env_var_binds_to_setting(monkeypatch, env_name, attr, value): + monkeypatch.setenv(env_name, value) + assert getattr(Settings(), attr) == value + + +def test_role_defaults_to_unknown(monkeypatch): + """An instance that has not declared itself is not the primary.""" + monkeypatch.delenv("STATE_HUB_INSTANCE_ROLE", raising=False) + assert Settings().state_hub_instance_role == "unknown" + + +def test_role_rejects_unknown_values(monkeypatch): + monkeypatch.setenv("STATE_HUB_INSTANCE_ROLE", "authoritative") + with pytest.raises(Exception): + Settings()