I2472 — How long CI takes is measured, from when each run started to when it completed, and the CI/CD screen draws it
Record when each check run on an open pull request's head started, beside when it completed, and answer how long CI takes from that record with a typed capability (integration.timings): per check its measured runs, failures, median, 90th percentile and longest duration; per open head its wall time and slowest check; and how many runs could not be measured. The CI/CD screen of majordomus ui draws it in place of the panel that said no capability records CI timings.
DONE wave 4 · p1 · implementation profile · runs alone
Part of development-runtime — The runtime has a development surface.
Objective
Record when each check run on an open pull request's head started, beside when it completed, and answer how long CI takes from that record with a typed capability (integration.timings): per check its measured runs, failures, median, 90th percentile and longest duration; per open head its wall time and slowest check; and how many runs could not be measured. The CI/CD screen of majordomus ui draws it in place of the panel that said no capability records CI timings.
Why
The owner asked on 2026-10-11 to implement every view of the UI screenshots (pack v3 M08; reference 07-ci-cd shows build times and throughput). d3's CI/CD screen (I2462) says no capability records per-run CI timings, and none did: the forge observation kept when a check completed but not when it started, although the GitHub CLI's rollup answers both. CI time is the latency the integration lane waits on for every pull request, and nothing measured it.
Current state
A check observation holds its name, state, kind, app and completion time. The CI/CD screen's build-timings panel says nothing is measured.
Desired state
The forge observation records started_at for every check run (empty for a status context or a run that has not started; an observation recorded before is read with it empty, so the schema stays 4). integration.timings (GET /api/v1/pull-requests/timings, MCP majordomus_ci_timings) measures, from the recorded observation and without asking the forge, every check's runs with a start and an end: passed, failed, median and 90th percentile (nearest rank) and longest seconds, the slowest median first; every open head's wall time from its first check's start to its last check's completion and its slowest check, the longest wait first; and how many runs it could not measure, which are counted and never measured as zero. The CI/CD screen draws each check's median as a bar against the slowest with its numbers beside it, and the ten longest waits each leading to its pull request on the PR Drain; without an observation it says nothing is measured.
Scope
- apps/majordomus-cli/src/integration/model.rs
- apps/majordomus-cli/src/integration/forge.rs
- apps/majordomus-cli/src/integration/timings.rs
- apps/majordomus-cli/src/integration/mod.rs
- apps/majordomus-cli/src/capability/builtin/integration.rs
- apps/majordomus-cli/src/ui/timings.rs
- apps/majordomus-cli/src/ui/delivery.rs
- apps/majordomus-cli/src/ui/mod.rs
- .ai/repo/project/issues/I2472.yaml
- docs/MAJORDOMUS_UI.md
- test/cases/1077_ci_timings_are_measured.sh
Dependencies
What waits on this
Acceptance criteria
- check_of records startedAt as started_at, and an observation without it still reads
- integration.timings answers each check's measured runs, failures, median, 90th percentile and longest seconds, and each open head's wall time and slowest check, from the recorded observation alone
- a run without a start or an end, and a status context, is counted as unmeasured and never measured as zero
- the CI/CD screen draws the timings, and a head leads to its pull request; without an observation it says nothing is measured
Validation
- cargo test --lib integration::timings
- cargo test --lib ui::
- test/run.sh 1077_ci_timings_are_measured
Evidence required
- integration_timings_unit_tests
- ci_timings_case_1077
Evidence
| covers | type | command | result | at commit |
|---|---|---|---|---|
| integration_timings_unit_tests | ci | cargo test --lib integration::timings (validate run 38223440282 on 722228ef3c, the v0.21.0 top) | 7 integration::timings tests ok, 5 ui::timings tests ok | 722228e |
| ci_timings_case_1077 | ci | test/run.sh 1077_ci_timings_are_measured (validate run 38223440282 on 722228ef3c, the v0.21.0 top) | case 1077_ci_timings_are_measured ok | 722228e |
Timeline
- started
- 2026-10-12T11:03:04Z
- verified
- 2026-10-12T11:03:13Z
- completed
- 2026-10-12T11:03:32Z
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/I2472.yaml. Read it back with majordomus plan show I2472.