ci: price every CI job's wall clock against a reserve (#285) #289
No reviewers
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index!289
Loading…
Reference in a new issue
No description provided.
Delete branch "fix-285-lane-wall-clock-headroom"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #285.
The native-Windows lane ran 2h53m38s GREEN, seven minutes short of a ceiling nobody had written down. Three commits later it was CANCELLED at 3h00m04s, and the cost was attributed to the commit that spent the last of the margin rather than to the pre-existing cost that had eaten the rest of it. An unmeasured margin does not merely fail late, it misattributes.
This project prices SQLite work per indexing pass, startup payload in tokens with a reserve, and seven payload ceilings through
headroom::Ceiling— every one built because an unmeasured number moved and nobody noticed. Wall clock per CI lane had none of it.What this adds
.forgejo/lane-budgets.json— the ceiling, the reserve and the basis for both..forgejo/scripts/lane_headroom.sh— reads the forge API, prices every job, prints aHEADROOMline on every run including green ones.--decideis the same rule with the measurement handed in, so the test executes it (require_free_disk.sh's precedent).lane-headroomjob inci.yml.crates/indexer/tests/ci_lane_headroom.rs— 6 tests.The ceiling is per JOB, not per lane
A workflow's
durationis a critical path over parallel jobs and nothing cancels it; what cancelled run 824 was the runner's per-job timeout. Pricing a 14-job workflow's total against a per-job ceiling would be a category error that reads as reassuring. Every job is priced instead — which also gives #285's requirement 3 (attribution) for free: the report names where the wall clock went.updated_atis not a completion time, and this cost a rewriteA job's duration looks derivable as
updated_at - run_started_at, and for recent rows it is EXACT: runs 854 and 852 derive 3301s and 3485s against the runs endpoint's authoritative 3301 and 3485.It is the ROW's last-modified time. This forge bulk-touched old rows: 736 of 907 success rows carry
2026-09-18T00:00:00+02:00exactly, derivingcargo fmtdurations of up to eight days against a real 56s.Every derived duration is therefore cross-checked against the run's own authoritative
duration: a job cannot outlast its run. Run without the clause against the live API, the reporting path prints1432.46% consumed, -143906s left — OVERfor four jobs whose real durations are minutes. A gate that loud and that wrong is worse than the silence it replaced, so the clause is pinned by a test rather than trusted.It warns and exits 0, by decision
House precedent points the other way (
headroom::Ceiling::gradeasserts;startup_payload_budget_e2efails with "TRIM, do not raise"), so the divergence is recorded at the exit, in the job, and in the test, and pinned bythe_gate_warns_rather_than_failing— flipping it is a deliberate edit, not a silent change of policy.The reasoning: a lane's wall clock is a shared, slowly-drifting cost, and the person whose push would redden is almost never the person who spent the margin — the same misattribution the instrument exists to prevent. The obligation that comes with warn-only is that the warning is actionable, so it names the jobs and what they cost.
Verification
Measured on the live feed: 25 jobs across three lanes, every one inside its reserve; slowest is
ci-windowsat 3485s of 10800s (32.27%). A ceiling lowered to 3600s reproduces the incident at 96.81%, so the instrument is known to fire.8 mutations, all run, each RED on exactly the named test, every restore md5-verified — including the behavioural half of the cross-check mutation.
Gates: fmt, clippy,
cargo test --workspace(3976 passed, 0 failed, 369 suites — exactly +6 for the new tests), daemon E2E (826/0),precision_gate7/7 phantoms=0,corpus_ratchetwithbaseline.jsonunmoved, workspace rustdoc,guest_gates.sh.CI: run 857 on
0503ae4— every push-gated job green. Thelane-headroomjob is verified on a runner: it measured (zeroUNMEASURED), priced 23 jobs across all three lanes, and covered 78 of 101 recent job rows. One nightly-only job (plugin-path-cost, gated onschedule || workflow_dispatch) was still running at merge time and does not run on a master push.One honest note: jq turned out to already be present in
ci-rust, so the install step short-circuited. It stays as insurance against an image change, matchingrelease.yml, but it did not fix anything today.🤖 Generated with Claude Code
https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu