Repository navigation
fix(startup): the Python-version floor check has no callers and never runs #13738
Description
Discovered while root-causing #13727 (PR #13737).
startup_validator.py declares the platform's minimum Python version:
# autobot-backend/startup_validator.py:320
if sys.version_info < (3, 12):
self.result.add_error(f"Python 3.12+ required, found {sys.version}")That check has zero callers repo-wide. Neither validate_startup_dependencies nor StartupValidator is referenced from any .py, .sh, .yml, or .yaml outside the module that defines them:
$ grep -rn "startup_validator\|validate_startup_dependencies\|StartupValidator" \
--include=*.py --include=*.sh --include=*.yml --include=*.yaml . \
| grep -v node_modules | grep -v "^./autobot-backend/startup_validator.py"
(no output)
Why it matters
The floor is not decorative — code depends on it. llc/scheduler/base.py calls asyncio.Task.cancelling(), which is Python 3.11+ and has no fallback. On a sub-floor interpreter every LLC poll loop (LivenessMonitor, BudgetWatchdog, SessionCheckpointer, CommunityClusteringScheduler) dies after exactly one tick, with an AttributeError that is never retrieved because nothing awaits the task. Measured in #13727: 5 of 19 scheduler tests fail on 3.10 and pass on 3.11+.
So the failure mode is silent and total for background scheduling, and the one guard meant to catch it never runs. This is the same shape as the other unwired-code findings: the check exists, is correct, and is not connected to anything.
_validate_system_requirements also checks disk space, so more than the version gate is dark.
Fix
Wire the validator into startup (the lifespan path is the natural place) so a sub-floor interpreter or an out-of-space host fails loudly at boot instead of degrading silently. Per the repo's standing rule this is a wire-in, not a deletion — the check is right, it just has no caller.
Acceptance criteria
-
validate_startup_dependencies()(orStartupValidator) is invoked on a real startup path, with a test proving it runs. - A sub-floor Python version produces a loud, actionable startup error naming the required version.
- Verified by deliberate failure: a simulated sub-floor
sys.version_infomakes startup fail, and the supported version still boots.
Related
- test(llc): 5 poll-loop scheduler tests failing on Dev_new_gui #13727 / PR test(llc): gate the poll-loop cancellation tests on the API they need (#13727) #13737 — the poll-loop tests whose root cause exposed this
- bug(backend): LLC background schedulers (BudgetWatchdog/LivenessMonitor/CommunityClusterer) not reliably cancelled when tests exercise the real app lifespan — ~10min trailing wall-clock #13085, bug(llc/scheduler): unbounded aclose() can hang graceful shutdown; new drains unguarded in cleanup_services (regressions from #13182) #13203 — the cancellation guard that depends on the 3.11+ API
Activity
mrveiss commentedon Aug 8, 2026
Related: PR #13750 raises the floor this check enforces from 3.12 to 3.14, so once it is wired the number is correct. Wiring it is still this issue's job — the check has no callers either way. Context: epic #13743.
docs(audit): correct the Python floor drift and record the 3.14 consi…
docs(audit): correct the Python floor drift and record the 3.14 consi…
docs(audit): correct the Python floor drift and record the 3.14 consi…
fix(startup): run the Python floor and disk gate at boot (#13738) (#1…
mrveiss commentedon Aug 9, 2026
Delivered and merged: PR #13781, squashed as
57d4b30.The obvious fix breaks boot, which is worth recording. Before wiring anything I tried the lists
validate_alltravels with:All six are hard errors in
validate_all. A check nobody ran is also a check nobody kept true: thesrc.*layout is long gone andaioredishas not been a dependency since the move toredis.asyncio. Callingvalidate_startup_dependencies()from the lifespan would have replaced a silent failure with a backend that refuses to start, reporting errors about the repo rather than the host.So the wiring is split by what each half is:
validate_all()additionally does a RedisPINGand a 5s HTTP GET to Ollama's/api/tags. That makes Ollama a boot dependency (a degraded-capability condition, not a reason to refuse to start) and duplicates existing health probes — filed as chore(startup): validate_all still has no caller — give it a diagnostics home, not the boot path #13780 with the reasoning.The gate sits immediately after
configure_logging(), beforeinitialize_critical_services: on a sub-floor host an operator gets "Python 3.14+ required" rather than a Redis error, which is the diagnosis cost #13727 paid.test_the_lifespan_calls_the_gateandtest_the_gate_runs_before_any_service_is_touched, the latter driving the real lifespan context manager and assertinginitialize_critical_serviceswas never reached.version_inforaises and the message names 3.14, so an operator learns what to install; the floor itself passes. The disk gate gets the same treatment: zero free bytes raises, 3 GB warns and boots.Three mutation dimensions: reverting both files → collection error; reverting only
lifespan.py→ both wiring tests red; restoringaioredis/src.unified_config→ both parametrised list tests red. That last one is the point of those tests — they are what stops the lists drifting back into the state that made this check useless.The floor constant needed no change: it already read 3.14, matching CI,
.python-version, mypy, both Dockerfiles and the Ansible roles.Closing.