mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 13:35:12 +09:00
The sweep was a fixed sequence of core producers, so an extension had nowhere to say "the thing I manage is unhealthy, and here is what to do about it" — on the page an operator opens to ask exactly that. A plugin now registers a provider and its checks appear beside the core ones, with a working Repair button, and no frontend change: the panel's status pill, repair button and diff toggle all key off fields rather than a known list of check ids. Because these run third-party code, the registry gives them two things core's own producers don't have: - an error boundary. run() calls its producers bare — each hand-rolls its own try/except and anything that escapes 500s the whole sweep. A provider that raises becomes one warning attributed to the plugin instead. - a time budget. Nothing bounds a core producer today, so a hung one hangs the synchronous POST /doctor/run for whoever asked. A provider that overruns is reported as a warning and the sweep moves on. Note the executor is shut down with wait=False on purpose: its context manager joins the worker on exit, which would have made the timeout worthless — a test pins that down. Providers run in a worker thread with the app context carried across, so a check can query the database the way a core one does. Contributed keys are namespaced, statuses outside ok/warn/fail are reported as warnings rather than rendering as a blank pill, and a check claiming to be repairable without a registered handler is demoted — showing a button that dispatches nowhere is the state two core checks are already in, and it isn't worth reproducing. Repairs route back through the registry, which is itself the allowlist: a crafted ref for an unregistered namespace reaches nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>