diff --git a/api/tools/equipment.py b/api/tools/equipment.py index a1f63d4..8afd093 100644 --- a/api/tools/equipment.py +++ b/api/tools/equipment.py @@ -79,11 +79,19 @@ def reject_retired_tag(term: str) -> None: """Raise if `term` is one of the retired PS_* names. See the note above.""" candidate = term.strip().upper() if _RETIRED_TAG.match(candidate) and candidate not in _POLL_GROUPS: + # ContractViolation takes (rule, detail). main.py logs the rule, returns + # 422, and deliberately does NOT put the detail in the operator-facing + # message - an error is not a side channel for content that failed a + # check. So the explanation below lands in the log and in Langfuse, not + # on the screen. That is the right trade here: an operator asking in + # plain English never types one of these names, so the audience for this + # error is a developer or a stale integration, and both read logs. raise ContractViolation( + "retired_tag_name", f"{candidate} is a retired name and is not a tag in this system. " "It came from a superseded SCADA point list; CI Server never used it. " "Use the CI Server item name instead, for example AID.WRPS.STN.LEVEL. " - "See db/seed/scada-source/README.md." + "See db/seed/scada-source/README.md.", )