I2489 — A screen over its budget fails the build: the UI's latency budgets are benchmark policy, and bench --check holds them
Make the owner's UI latency budget something CI enforces: the benchmark policy declares absolute bounds, bench --check holds them with or without a baseline and on every host, and the ui job runs that check on every change that can slow a screen.
DONE wave 2 · p1 · implementation profile · parallel safe
Part of development-runtime — The runtime has a development surface.
Objective
Make the owner's UI latency budget something CI enforces: the benchmark policy declares absolute bounds, bench --check holds them with or without a baseline and on every host, and the ui job runs that check on every change that can slow a screen.
Why
ADR 0142, part 4. The owner's pack set the budgets (a cached capability under 50 ms at p95, a warm server-rendered page under 100 ms, a live update under 250 ms) and said they are 'measured, never invented'. Today nothing holds them: the bench times the UI's screens since the UI-performance batch, and reports; it refuses nothing. The UI was eight seconds slow on master for a day before a person said so.
Current state
majordomus bench times every screen of the UI (system.http.ui.* over a pinned context, system.http.live.ui.* over the live view) and bench --check compares a run with a baseline of the same host. A target that was always too slow never regresses, and a platform with no baseline compares nothing, so nothing held a screen to a bound. Measured before this change, debug build at load 57: every warm screen at 1-21 ms p95 except /ui/command at 400-660 ms.
Desired state
.ai/repo/benchmarks/rust/policy.yaml gains budgets: each a prefix of target keys, a cache mode, a metric, a bound in milliseconds and a minimum number of samples, every key but the last required and a malformed one a policy error that names it. Check::compare judges every measurement a budget bounds, whether or not a baseline exists and whatever host recorded it, as PASS, OVER or SHORT; bench --check prints the budget lines with the target, its value and its bound, says within or over budget, and exits 10 when anything is over. The policy declares a warm screen of the UI within 100 ms at p95 for both views. The ui job of validate runs bench system.http. --check before the browser audit, as a step of the ui-audit gate. /ui/command no longer walks the registry once per command: it indexes command lines once per screen. Not done here: measuring the first request after the repository moved, which is I2485.
Scope
- apps/majordomus-cli/src/bench/baseline.rs
- apps/majordomus-cli/src/ui/command.rs
- apps/majordomus-cli/tests/bench_units.rs
- .ai/repo/benchmarks/rust/policy.yaml
- .ai/repo/ci/gates.yaml
- .github/workflows/validate.yml
- .ai/repo/benchmarks/rust/README.md
- docs/MAJORDOMUS_UI.md
- test/cases/1099_a_screen_over_its_budget_fails.sh
- .ai/repo/project/issues/I2489.yaml
Dependencies
What waits on this
- I2485READY The bench measures the first request after the repository moved, and a budget bounds it
- I2663READY The published UI is not read from nothing on every deployment
Acceptance criteria
- A warm screen measured over its budget fails bench --check with exit 10 and no baseline to compare, and the report names the screen, its value and its bound
- A budget bounds only the prefix and the cache mode it names, and a measurement of fewer samples than its minimum is SHORT and does not fail
- A budget with a required key missing, or a cache mode that is not one, is a policy error that names the key
- The repository's policy declares the UI's budget for the pinned and the live view, and every screen is within it
- The ui job runs the check before the browser audit, under a bound smaller than the job's
Validation
- bin/majordomus plan validate
- cargo test --test bench_units
- MJ_TEST_JOBS=2 bash test/run.sh 1099_a_screen_over_its_budget_fails
- majordomus bench system.http. --transport system --profile ci --no-write --check
Evidence required
- bench_budget_unit_tests
- ui_budget_gate_case_1099
- ui_budget_bench_results
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| bench_budget_unit_tests | test | cargo test --manifest-path apps/majordomus-cli/Cargo.toml --test bench_units | test result: ok. 7 passed; 0 failed, on a tree equal to master 3442ce68c5: a result over its budget fails the check and is named, one within it passes, a result with too few samples is reported and not judged | 3442ce6 |
| ui_budget_gate_case_1099 | test | bash test/run.sh 1099_a_screen_over_its_budget_fails | ok 1099_a_screen_over_its_budget_fails, on a tree equal to master 3442ce68c5: a screen over its budget fails the check and is named, one within it passes, the policy declares both views | 3442ce6 |
| ui_budget_bench_results | ci | majordomus bench system.http. --transport system --profile ci --no-write --check | bench --check: within budget, in the ui job of validate run 38379385624 on master e506c041a4 (job 115235815811), a GitHub-hosted Linux runner; every system.http.ui. and system.http.live.ui. result warm, p95, at or under 100 ms with 20 samples. Not repeated on 3442ce68c5: a local run on a Mac at load 125 could not hold its own server (exit 13) | 3442ce6 |
Timeline
- started
- 2026-10-13T12:50:07Z
- verified
- 2026-10-13T12:50:24Z
- completed
- 2026-10-13T12:50:44Z
Those three fields, the evidence above and the state of the dependencies are all the status is made of. There is no status field to disagree with them.
Canonical record: .ai/repo/project/issues/I2489.yaml. Read it back with majordomus plan show I2489.