Run the RGAA audit yourself — the demo path
rgaa-audit (Acci) audits a product against the RGAA 4.1.2 standard and produces a dated French report: conformance rate, findings per criterion, a page grid, a coverage annex and the verdict ledger. The engine (ultra11y, pinned and hash-verified) owns the judgement; iterion orchestrates the LLM adjudication around it.
The fastest full tour audits this repository's own studio — the dogfood target the sample and the committed ledger were built for.
task demo:rgaa-auditPrerequisites
- The repo toolchain:
devbox install(node ≥ 22.18, go, pnpm — the Taskfile drives everything else). - An authenticated
claudeCLI: the run's agents delegate through theclaude_codebackend, so they spend YOUR Claude budget. A full first run is ~$20-30 (RGAA_DEMO_BUDGEToverrides the ceiling); the run replays the committed ledger (.ultra11y/verdicts/rgaa.json), so criteria that did not change are not re-adjudicated. - A free port 4899 (the task fails fast when something already listens there — a leftover studio e2e serve, for instance).
- ~10-20 minutes of wall clock, most of it in the LLM rounds.
What the task does
- Builds the studio SPA and the iterion binary with the bot catalogue embedded.
- Resolves the pinned ultra11y engine (
scripts/ultra11y-engine.sh— it prefers a matching global npm install; otherwise the first use installs the engine under~/.cache/iterion/ultra11yafter verifying the pin's size and sha256). - Installs the chromium build the engine's rendered scan navigates with (a browser already on the machine is named instead with the bot's
browser_pathvar orULTRA11Y_BROWSER_PATH, which the engine takes as--browser-path; the sandbox browser image names its/usr/bin/chromiumthat way). - Runs
iterion run bots/rgaa-auditwithrendered=required: the studio is served locally (studio/e2e/serve.mjs, seeded workspace), the 21-page sample (.ultra11yrc.json) is captured with Chromium, and every layer — static audit, rendered capture, ledger replay, LLM adjudication, counter-reading — runs to the final report.
The report, page grid, coverage annex and ledger land under .rgaa-demo/<UTC-timestamp>/; the run's own state lives in .rgaa-demo/store, home and run. The rendered captures are the exception: the engine writes them under the audited checkout's .ultra11y/pages/ (gitignored, like the e2e workspace serve.mjs seeds under studio/e2e/.tmp/) — so don't run the demo while a studio e2e session is live. A previous demo's captures are not inherited: static_audit purges that directory before the audit, so a second run on the same checkout never ingests the first's stale probes. The audit reads the checkout AS IT IS: uncommitted edits to studio/src are part of what gets judged.
Options and variants
RGAA_DEMO_BUDGET=50 task demo:rgaa-audit— raise the spend ceiling.Post findings to the run's native board instead of stopping at the report: add
--var post_to_board=trueto theiterion runline (the board lives in the--store-dirstore,.rgaa-demo/store— the GitHub 203 bridge is a separate, operator-owned push).Audit a DIFFERENT repo: copy the task's
iterion runline, pointscope_globsat the target, setrendered=offif it has no page server, and write your ownscope_notes. The committed ledger only covers this studio; another repo starts from an empty ledger — expect ~$60 for ~100 criteria, so raise the ceiling accordingly (RGAA_DEMO_BUDGET=80or--max-cost-usd 80).iterion resume --run-id <id> --store-dir .rgaa-demo/store …resumes an interrupted run (add--loop-budget-guard offif the guard trips on the resumed budget). The measured ceiling of a full run is the bot's TOKEN budget, not dollars: the default 3M ran out at 91% before the counter-reading — the task ships--max-tokens5M (RGAA_DEMO_TOKENSoverrides), and a resume that worked for real:sh.rgaa-demo/bin/iterion resume --run-id <id> --store-dir .rgaa-demo/store \ --max-tokens 4500000 --max-cost-usd 40 --loop-budget-guard off
Limits
- The task is local-only by design: it pins
rendered=required, and a missing browser underrequiredFAILS the run (typed, not degraded). On a machine without chromium, drop to the static layer explicitly with--var rendered=off— the report then says so in its annex. --sandbox noneships in the task because the rendered layer needs a local browser and a local server; the bot's own nodes are read-only (no shell, no writes), so the exposure is the page capture, not your tree.- The sample covers the studio's seeded pages; cloud-only screens (login, admin, teams) are out of sample.
- The weekly CI advisory (
ci/a11y-advisory) runs the deterministic layers only (no LLM); a red advisory names the criteria the next run will re-adjudicate.
The rgaa sandbox image class
A sandboxed run of this bot needs two things no stock default image carries: a browser (Chromium) and the ultra11y engine. Both now exist as a published image class (#2632, decision (a)): the rgaa variant — sandbox/rgaa/Dockerfile, built FROM the browser base (Chromium + Playwright MCP + ULTRA11Y_BROWSER_PATH=/usr/bin/chromium) with the pinned ultra11y engine installed globally (npm install -g ultra11y@<pin>, the pin held equal to bots/rgaa-audit/ultra11y.pin by bots/rgaa_audit_sandbox_rgaa_image_test.go).
A bot opts in by declaring the class on its sandbox block:
sandbox:
image: rgaa ## resolves at launch to ghcr.io/socialgouv/iterion-sandbox-rgaa:<version>A bare class name (slim, full, sec, browser, rgaa) resolves to the published variant pinned to the running iterion; anything else keeps its meaning (a full registry/repo:tag reference is your own image; an unknown bare word is verbatim, with the compiler's C325 saying so).
Until the bot itself declares the class, its prepare node keeps its engine resolution ladder: ULTRA11Y_BIN → a matching global npm install → ULTRA11Y_HOME → a per-run npm install ultra11y@<pin> (fingerprint verified either way). On the rgaa image the second arm answers, so the per-run install disappears the day the bot declares image: rgaa — the fallback stays as the honest answer on every other image.
Running on iterion.cloud — what a sandboxed cloud run gets, and what it still does not
A cloud run executes inside the run's sandbox pod, not in the runner image. Both decisions of #2632 are delivered: (a) the rgaa image class, above, and (b) the sandboxed board write path, below. What remains is the honest interim state of the image until the bot declares the class.
- The engine pre-install does not reach a run on the default image. The runner image pre-installs the pinned ultra11y (
Dockerfile.runner-devbox:ARG ULTRA11Y_VERSION, installed under/opt/iterion/ultra11yand exported asULTRA11Y_HOME), but the sandbox pod defaults toghcr.io/socialgouv/iterion-sandbox-slim(pkg/runtime/sandbox_default_image.go) — node + devbox + Nix, no ultra11y (sandbox/slim/Dockerfile).prepare's candidate walk (ULTRA11Y_BIN→ global npm →ULTRA11Y_HOME,bots/rgaa-audit/main.bot) therefore misses, and every cloud run on the default image pays onenpm install ultra11y@<pin>into its run directory (engine_source: "npm-install") — a per-run network fetch from the registry, fingerprinted like any other engine source. Thergaaclass carries the engine pre-installed. - No browser on the default image, so
rendered=offthere. The slim image ships no Chromium, so the rendered layer cannot run on iterion.cloud while the bot rides the default: launch withrendered=off(or letautofind nothing) and the coverage annex says the layer did not run. Thebrowserclass and thergaaclass both ship Chromium at/usr/bin/chromium, named to the bot throughULTRA11Y_BROWSER_PATH. - Board filing works in the sandbox (since #2632 (b)).
publish/publish_verifywrite through the board MCP transport (BoardHTTPEndpoint+BoardRunToken,pkg/backend/delegate/claude_code_hooks.go,wireBoardMCP). On the cloud the runner pod serves it: the pod mints a per-node token scoped to the node'sboard.*capabilities and pinned to the run's tenant (pkg/runner,executorSpec), and the engine's per-run gateway listener answers the sandbox at the runner pod IP, dispatching to the tenant's Mongo board (pkg/hostmcp, resolved per grant — the server side's twin isboardMCPServiceOptioninpkg/server/server.go).post_to_board=truecan file from a sandboxed cloud run; withpost_to_board=falsethe issue plan stays a run artefact (issues-<date>.json). The remaining cloud gap of the family: theconnectorscapability's facade is served by the server only, so a sandboxed cloud node declaringconnectorsgets no endpoint — the delegate warns and disables its connector tools, exactly the pre-#2632 degradation (never an endpoint that would 404 per call). - Credentials. A cloud launch spends the credentials of the account or team that launched it (BYOK); no platform key is provisioned for this bot.
