Continuation of wave A — five more
items from the same critique round, plus one structural unification so
two sources of upstream-code truth cannot drift again.
Auto-stop status now shares its cache across uvicorn workers (#18).
The api sidecar runs 2 uvicorn workers per replica; a browser polling
/api/aks/autostop/status at 60 s round-robins between them. Before
this change each worker recomputed the entire Table+ARM+evaluator
pipeline on every cache miss because the in-process L1 cache was
invisible to its sibling. Now an L2 Redis cache (autostop:status:*,
TTL=5 s) sits behind the L1 so a sibling worker serves the cached
body on its very next poll. Redis unreachable degrades gracefully to
L1-only — no regression for local-dev without a broker.
PLS transition banner picks up the active theme automatically (#20.6).
The banner used to hard-code rgba(255, 196, 0, 0.5) / …0.08 so a
light-mode or high-contrast theme rotation would leave the banner
stuck on its hard amber. It now derives both border and fill from
the --warning theme token via color-mix(in srgb, var(--warning) X%, transparent).
vnet-peering audit interrupted write surfaces in the log when it fails (#16).
Previously a complete audit-backend outage during an apply could
leave an Audit row stuck in started forever with zero log
breadcrumb. The interrupted-write now logs ERROR with the audit
job id so an operator chasing the phantom row can grep the api log.
OPENAPI_NESTED_UPSTREAM_CODES is now derived, not hand-rolled (#20.5).
The set is computed from OPENAPI_UPSTREAM_ACTIONS.keys() minus the
explicit top-level wrapper set ({"openapi_unreachable"}). Adding a
new sibling /v1/ready code now requires touching one place — the
actions table — instead of two parallel lists.
External BLAST in-flight wait has an explicit, env-tunable retry cap (#20.12).
The previous "wait → re-check → try-become-leader → wait → re-check →
fall through" pattern was hand-counted at 2 attempts. It is now a
bounded loop driven by _READY_INFLIGHT_MAX_WAIT_ROUNDS (env
OPENAPI_READY_INFLIGHT_MAX_WAIT_ROUNDS, default 2). Total worst-case
wait = rounds × _READY_INFLIGHT_WAIT_SECONDS, so a pathological
leader-swap loop cannot pin a single caller for an unbounded duration.
Two-tier cache (_STATUS_L1_TTL_SECONDS=2 in-process, _STATUS_L2_TTL_SECONDS=5 Redis); _status_redis_get/set/delete helpers; PUT/extend now drop L2 too (#18)
$ uv run pytest -q api/tests
............................................................... [100%]
1886 passed, 3 skipped in 36.17s
$ cd web && npm test -- --run
Test Files 55 passed (55)
Tests 428 passed (428)
Duration 4.03s
$ uv run ruff check api
All checks passed!
Consumer search for _STATUS_TTL_SECONDS (now an alias) confirmed no
test outside this module compares the value; the alias is kept for
one-cycle backward compat with anything that imports the name.
_OPENAPI_TOP_LEVEL_CODES documented inline — any future addition
there narrows the nested set without touching tests.
_status_redis_client is wrapped in try/except at every call site so
Redis outage degrades to L1-only without raising.
_record_audit_event already swallows all backend errors and returns
False; the new ERROR log path is reachable in unit tests by
monkeypatching the helper to return False.