From fe4cead4c6fc290fb4e72697b90563579c659d61 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 7 Aug 2026 01:43:48 -0300 Subject: [PATCH] feat(ariadne): let the triage tick be run on demand The scheduler runs it every minute, which is cron's finest granularity and still up to sixty seconds of silence after a build goes red. That is the single largest delay between a failure happening and the system visibly reacting to it, and no amount of polling on the client side shortens it. The same tick can now be run immediately. It is idempotent - incidents dedupe on job and build number - so an extra run can only be a no-op, never a second incident for the same build. Both the authenticated admin route and the internal one follow the shape the testing-triage routes already established. Co-Authored-By: Claude Opus 5 --- ariadne/app_admin_routes.py | 21 +++++++++++++++ tests/unit/app/test_app_admin_routes.py | 36 +++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/ariadne/app_admin_routes.py b/ariadne/app_admin_routes.py index 175287a..ac33279 100644 --- a/ariadne/app_admin_routes.py +++ b/ariadne/app_admin_routes.py @@ -217,6 +217,27 @@ def _register_admin_routes(app: FastAPI, require_auth: Callable, deps: Callable[ module = deps() return JSONResponse(module.run_testing_triage_diagnosis(module.storage)) + # The scheduler runs this every minute, which is cron's finest granularity + # and still up to sixty seconds of silence after a build goes red. That is + # the single largest delay between a failure happening and the system + # visibly reacting to it. Running the same tick on demand collapses it; + # the tick is idempotent - incidents dedupe on job and build number - so + # an extra run can only ever be a no-op. + @app.post("/api/admin/hermes/autotriage/run") + def run_hermes_autotriage_now(ctx: AuthContext = Depends(require_auth)) -> JSONResponse: + """Run one Hermes auto-triage tick immediately.""" + + module = deps() + module._require_admin(ctx) + return JSONResponse(module.run_hermes_autotriage(module.storage)) + + @app.post("/api/internal/hermes/autotriage/run") + def run_hermes_autotriage_now_internal() -> JSONResponse: + """Run one Hermes auto-triage tick for trusted internal callers.""" + + module = deps() + return JSONResponse(module.run_hermes_autotriage(module.storage)) + @app.post("/api/admin/access/requests/{username}/approve") async def approve_access_request( username: str, diff --git a/tests/unit/app/test_app_admin_routes.py b/tests/unit/app/test_app_admin_routes.py index ecc79f2..c724b85 100644 --- a/tests/unit/app/test_app_admin_routes.py +++ b/tests/unit/app/test_app_admin_routes.py @@ -443,3 +443,39 @@ def test_retry_access_request_reports_update_failure(monkeypatch) -> None: resp = client.post("/api/access/requests/REQ1/retry") assert resp.status_code == 502 + +def test_hermes_autotriage_can_be_run_on_demand(monkeypatch) -> None: + """The cron tick is once a minute; a demo should not wait for it.""" + + ctx = AuthContext(username="bstein", email="", groups=["admin"], claims={}) + client = _client(monkeypatch, ctx) + calls = [] + monkeypatch.setattr( + app_module, + "run_hermes_autotriage", + lambda storage: calls.append(storage) or {"status": "ok", "jobs": {}}, + ) + + admin_run = client.post( + "/api/admin/hermes/autotriage/run", + headers={"Authorization": "Bearer token"}, + ) + internal_run = client.post("/api/internal/hermes/autotriage/run") + + assert admin_run.status_code == 200 + assert internal_run.status_code == 200 + assert admin_run.json()["status"] == "ok" + assert len(calls) == 2 + + +def test_hermes_autotriage_on_demand_requires_admin(monkeypatch) -> None: + ctx = AuthContext(username="nobody", email="", groups=[], claims={}) + client = _client(monkeypatch, ctx) + monkeypatch.setattr(app_module, "run_hermes_autotriage", lambda storage: {"status": "ok"}) + + resp = client.post( + "/api/admin/hermes/autotriage/run", + headers={"Authorization": "Bearer token"}, + ) + + assert resp.status_code == 403