I3202 — CI timings measure the wait before a run starts beside the run, and count what cancelled runs spent
Read the Actions workflow-runs and jobs API over a declared window in prs refresh, so integration.timings gives each run its queue time (created to started) beside its execution time (started to completed) and tallies the runner minutes of cancelled and superseded runs, and the one-at-a-time pool's wait is measured instead of inferred.
BLOCKED wave 0 · p1 · implementation profile · parallel safe
Blocked. This issue cannot start until milestone:delivery-flow-is-measured is done. The status is derived from that, not declared.
Objective
Read the Actions workflow-runs and jobs API over a declared window in prs refresh, so integration.timings gives each run its queue time (created to started) beside its execution time (started to completed) and tallies the runner minutes of cancelled and superseded runs, and the one-at-a-time pool's wait is measured instead of inferred.
Why
Audits read validate wall times of 59-98 minutes on 2026-10-10 and 90-115 minutes on 2026-10-15..17, and 41 of 67 validate runs on 2026-10-11 cancelled (c199#14); a 2026-10-13 proposal asked that queue time, execution time and wasted minutes be told apart (c255#7). Those were timestamp readings by a reviewer, not repository measurements. Proposed in ADR 0196 (Delivery latency targets are budgets of benchmark policy, measured per phase, and never a correctness gate on runners the repository does not own) (proposed; it carries no authority until the owner accepts it). Sources: chatgpt-majordomus:c199#14, chatgpt-majordomus:c255#7.
Current state
apps/majordomus-cli/src/integration/timings.rs measures completed_at - started_at of an open pull request's head checks and counts queued runs without measuring them (I2472 DONE). The forge observation (forge.rs) reads statusCheckRollup CheckRun{name status conclusion startedAt completedAt} for the current heads of open pull requests only: it holds no created or queued time, no run of a superseded head and no run of a merged pull request. The /ui CI/CD & Releases screen (ui/timings.rs, ui/delivery.rs) draws integration.timings.
Desired state
integration.timings reports per run: queued, started, completed, queue seconds, run seconds, conclusion; per window: p50/p95 of each, cancelled and superseded run minutes, sample size and observation time. A run still queued is pending, never zero.
Scope
- apps/majordomus-cli/src/integration/timings.rs
- apps/majordomus-cli/src/integration/forge.rs
- apps/majordomus-cli/src/capability/builtin/integration.rs
- docs/CI.md
- apps/majordomus-cli/src/ui/timings.rs
- apps/majordomus-cli/src/ui/delivery.rs
Out of scope
- Changing what CI runs
- Publishing a target before it is measured
- A persisted history of runs beyond the window one refresh reads
Dependencies
None. This issue is a root of the graph.
What waits on this
- I3203BLOCKED Delivery lead time is measured per phase, from a pull request's opening to its verified publication
- I3206BLOCKED The delivery phases have budgets in benchmark policy, and a phase over its budget is a finding with its trend
- I3379BLOCKED Each issue's CI reruns and rounds are counted from the Actions run record, attributed through plan.delivery
Acceptance criteria
- A three-run fixture yields the stated queue and run seconds for each run
- A cancelled run's minutes are counted as spent and its conclusion stays cancelled
- Re-reading the same observation creates no duplicate rows
- Queue and run times come from the Actions workflow-runs and jobs API (created_at, run_started_at, updated_at, status, conclusion, run_attempt, head_sha), read by prs refresh with pagination over a declared window, never from statusCheckRollup
- The window is written into the existing forge observation, which each refresh replaces; no other file or store holds runs
- A run is identified by run id and attempt: reading the same window twice counts each run once
- A run is superseded when its head_sha is no longer its branch's head; its minutes are counted as spent
- The /ui CI/CD & Releases screen renders the new reading's shape, and a case or test holds the screen to it
Validation
- cargo test --manifest-path apps/majordomus-cli/Cargo.toml --lib integration::timings
- majordomus plan validate
Evidence required
- test_integration_timings_queue
Evidence
None recorded. Every token above needs a command or an artifact behind it before this issue can be completed; narrative is refused.
Timeline
- started
- —
- verified
- —
- completed
- —
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/I3202.yaml. Read it back with majordomus plan show I3202.