One SonarQube rule is usually one root cause spread across many files. S2208 appears in three Ariadne modules and the cognitive-complexity rule in dozens, and a sweep with no memory of what it already proposed would open a near-identical pull request for every instance. Thirty of those get read as none, which costs more than proposing nothing. The sweep now skips any rule that already has an open proposal for that project. The rules under review are read back from the open pull requests' own titles rather than from a stored index: the pull requests are the thing that actually exists, an index could disagree with them, and disagreeing is the one failure mode that matters here. Once the open one is dealt with, the next instance of that rule becomes eligible again. This is not the root-cause collapse - it does not make one pull request fix every instance of a rule, it just stops proposing the same rule repeatedly. The collapse needs multi-file patch sets, which the frozen patch contract cannot express yet. Fails open like every other duplicate check here: an unreadable list yields no known rules, so a lookup failure costs one extra proposal rather than silently dropping a whole rule. Issues now link their run id into the Hermes console, matching what pull requests already do. Both artifacts claim a model made the call; both should let a reader open the page where that call is visible. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ariadne
Ariadne is the Atlas admin and account automation service.
It sits behind the portal and handles the jobs that are annoying or risky to do by hand: approving access, syncing account state, rotating service passwords, cleaning stale Kubernetes work, checking platform health, and keeping a few service integrations lined up.
How it works
Ariadne is a FastAPI service with a small scheduler. It talks to Keycloak, Vault, Mailu, Nextcloud, Wger, Firefly, Jenkins, Metis, Kubernetes, and a few Atlas-specific services through focused adapters under ariadne/services/.
The API is split between admin routes, account self-service routes, internal event hooks, and Prometheus metrics. Background jobs store run history in the Ariadne database so failures can be inspected later instead of vanishing into logs.
The following are notes for future Brad.
Bring-up dependencies
Ariadne needs:
- Kubernetes API, service DNS, and Ariadne's service account/RBAC
- the Ariadne database, plus the portal database if portal/account sync is enabled
- Vault or the Kubernetes secrets that Vault normally feeds it
- Keycloak/OIDC, because auth and profile sync assume it exists
- ingress/proxy plumbing if humans are going to use it through the portal
- the services for whatever jobs are enabled: Mailu, Nextcloud, Vaultwarden, Wger, Firefly, Jenkins, Metis, OpenSearch, and the comms/game-mode pieces
It can start before every integration is perfect, but the matching scheduled jobs will fail or no-op until their service is actually alive. In a total bring-up, wait for storage, Flux, Postgres, Vault, Keycloak, and ingress first. Afterwards Ariadne becomes useful glue.
Useful routes:
GET /healthGET /metricsGET /api/admin/cluster/statePOST /api/admin/access/requests/{username}/approvePOST /api/account/mailu/rotatePOST /api/account/wger/resetPOST /api/account/firefly/resetPOST /events
Development
python -m pytest
ruff check .
Most runtime behavior is configured through environment variables in ariadne/settings.py. Service-specific logic is in the small adapter modules; ariadne/app.py is focused on request flow and task orchestration.