Repository navigation
canonical(enums): 169 bare severity literals remain, 43 of them outside the canonical vocabulary #14956
Description
Split out of #13597, which had two halves. The enum half is done; this is the remainder,
with the population measured rather than estimated.
What is already landed
AlertSeverityandErrorSeverityare aliases of the canonical
autobot_shared.status_enums.Severity(landed before this change).repo_tests/enum_union_guard_test.pynow asserts both aliases still resolve toSeverity,
fails if either is re-declared as its own enum, and ratchets the literal count so the
population cannot grow while this issue is open.
What is left — the measured number
#13597 said 119. Re-measured on the tree at the time of filing: 169 bare
"severity": "<literal>" entries across 43 files under autobot-backend/ and
autobot_shared/. It grew by 50 between #13597 being filed and being worked.
Matcher used (the same one the ratchet uses):
["']severity["']\s*:\s*["'][A-Za-z_]+["']
Distribution of the literal values:
| value | count | in canonical Severity? |
|---|---|---|
high |
50 | yes |
critical |
32 | yes |
warning |
25 | no |
medium |
24 | yes |
low |
14 | yes |
HIGH |
9 | no (wrong case) |
info |
5 | yes |
CRITICAL |
3 | no (wrong case) |
MEDIUM |
3 | no (wrong case) |
unknown |
1 | yes |
error |
1 | no |
degraded |
1 | no |
Extreme |
1 | no |
43 of the 169 are already outside the canonical vocabulary. That is not a tidiness
finding — it is the typo class #13597 predicted, present and shipping. "Extreme"
(tests/test_osint_engine.py:366) is a value no consumer can match; the 15 uppercase ones
(code_analysis/auto-tools/security_deep_sanitizer.py, playwright_sanitizer.py) are the
same word in the wrong case, so any consumer comparing to Severity.HIGH.value silently
misses them.
Why the ratchet ships before the sweep
A guard that prevents regrowth is worth more than a partial sweep: the count moved
119 → 169 precisely because nothing stopped it. The ceiling in
repo_tests/enum_union_guard_test.py is pinned at 169 and is to be lowered as literals
convert, never raised.
Work
- Convert the in-process producers to
Severitymembers, using.valueat every
serialisation boundary (str()of a member is not its value) - Decide the fate of
warning/error/degraded— these look like a different
vocabulary (log level, probe state), not a mis-spelled severity. Either give them
their own enum or map them; do not silently fold them intoSeverity - Fix the 15 wrong-case and 1 nonsense (
Extreme) values first — they are live defects,
not debt - Leave values parsed from external scanner output alone (wire data, not domain values)
- Lower
SEVERITY_LITERAL_CEILINGwith each batch
Also from #13597, not delivered here
Scope item 3: Severity, TaskStatus, Priority, LLMProvider, OperationOutcome,
HealthStatus, AgentStatus are plain Enum while AgentLifecycleStatus and
ConnectionStatus are (str, Enum). Two serialisation behaviours in one module. Needs a
stated rule in the module docstring, and both existing (str, Enum) members have committed
wire formats so neither can simply be changed.
Refs #13597
Activity
mrveiss commentedon Aug 24, 2026
Discovered while collapsing the enum forks in #14957 (closes #13845, #13846, #13597, #13578). Not fixed there — see the PR's "Discovered and filed, not fixed here" section for why the blast radius differs.
fix(enums): keep the sweep out of docstring prose and regenerate the …
docs(enums): record the prose-editing trap in the guard's own trap li…
fix(enums): converge severity literals on the canonical enum and add …
github-actions commentedon Aug 24, 2026
PR #14990 (merged to
Dev_new_gui) references this issue with a close keyword.If this issue is fully resolved, close it manually. If work remains, no action is needed.
mrveiss commentedon Aug 24, 2026
Delivered and verified in
Dev_new_gui.e2daa78685989c92b5ec04d0460e67e947e0240bgit merge-base --is-ancestorcode-reviewerpass, verdict MERGEMeasured, not claimed
144 of 169 converted (85%). Verified in base:
SEVERITY_LITERAL_CEILING = 25,floor 20,
DELIBERATE_SEVERITY_LITERALSpresent,Severity.score_ladder()defined, and
knowledge_grounding.pycarries zeroSeverity.HIGH.value.The reviewer re-derived the population independently rather than trusting the PR
body — ran the guard's own matcher against both base and HEAD and got 169 hits
across 43 files at base, 25 at HEAD, distributed exactly as claimed.
The 43 out-of-vocabulary literals, decided per-literal
Extremeis NOAA/NWS CAP wire data passed through verbatim byNOAASourceThree rungs were genuinely missing and were added rather than folded away:
warningis a scraped Prometheus label (update_active_alerts("warning", ...),and shipped alert rules carry
severity: warning);degradedis serialised intothe causal-analysis API response and asserted by an existing test;
errorisgraded separately from
warningby the capability audit. Folding any of them toa neighbour would have renamed a scraped label or changed a wire value.
Severity.score_ladder()names the grading subset so the four bug-predictionsites building
{level.value: 0 for level in RiskLevel}— a shipped API key set —did not silently gain three always-zero buckets.
The sweep had corrupted documentation
A flake8
F401turned out to be a symptom: the regex had rewritten a literalinside an endpoint docstring's fenced JSON block, turning documented output
into
"severity": Severity.HIGH.value— uncopyable by any reader. Worse, thatdocstring is embedded as JSDoc in the OpenAPI-derived
src/types/generated/api.ts, so the mangled text had already propagated intoshipped frontend type definitions. The repo's auto-formatter had independently
deleted the orphaned import, which would have left the corruption shipping in
both artifacts with no lint error to show for it.
The literal was restored, the whole sweep was audited by AST for the same defect
(exactly one file affected), and a new guard
test_no_enum_read_was_written_inside_a_stringnow fails if an enum read everlands inside a string constant. Prose is exempt from the ratchet by rule, which is
why the allowlist now carries two qualifying reasons rather than one.
Guard robustness verified by mutation, not by reading
14 mutations, all caught. The reviewer independently ran three of them against the
live guard: breaking the matcher makes the population floor fail loudly with
"the sweep no longer finds what it is meant to scan — a broken matcher or a moved
root, not a clean tree" rather than reporting a false clean; dropping the
WARNINGmember fails the vocabulary-union test by name; makingscore_ladder()return the whole enum fails both consumer-shape tests.
Not closed by this
Refs). Its remaining scope — the plainEnumvs(str, Enum)rule and theAgentLifecycleStatus/ConnectionStatusreconciliation — is untouched here.
CausalSeverityis a fourth severity fork, plus 219 literals intwo shapes this matcher cannot see.
the same name-where-a-value-belongs defect this PR fixed in Python.
Closing.