The duration of a single build has been recorded since the bash predecessor, but never compared with the other runs of the same build. The History page now carries one card per build definition: the window's successful runs as a line, the median as a reference, and the latest run measured against it — amber once it needs a quarter longer. Grouped by definition with the branches pooled, not per branch: a branch leaves two or three measurements and disappears. The median rather than the average is what makes that pooling affordable, since one cold cache or one branch that redefined its build no longer colours a month. Revises Scope Decision 2 of plan step 20, which decided the opposite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #49
7.2 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
Related Links
docs/plan/20-build-duration-tracking.md— the plan step this implements; its Scope Decision 2 is revised here, see Additional Changes.
The Problem
The duration of a single build has been recorded since the bash predecessor — build-results.tsv carried a duration(MM:SS) column, and today the History view, the JSON API and the CLI all show it.
What has never existed is the comparison of those numbers with each other.
A single number answers "how long did the last run take", never "is this build getting slower". A build that used to take six minutes and now takes nine is the first visible sign of trouble, usually well before anything fails, and at that point the per-build figure still looks perfectly ordinary.
Non-Goals
- No phase breakdown. Where the time inside a build goes is Step 14's subject; this is the whole execution time.
- No stored time series. The durations are already recorded; deriving the trend on read keeps the result schema untouched and stays correct as retention prunes old runs.
- No alerting. The trend is shown; pushing it to Gitea or a webhook is a separate decision.
- No CLI output.
status --historyalready prints the per-build duration, and a chart is not a thing the console needs.
The Scenarios
Feature: how long a build has been taking
Background
- Every
BuildResultcarriesbranch, the definition it ran (build), and the measureddurationfromrunningSince— the queue wait is not part of it. - The trend window is the last 30 days.
Scenario#49.01: The trend belongs to the build definition, not to the branch
So that a series exists at all — a branch lives for days and leaves two or three measurements.
- Given builds of the definition
defaultonmainand on three feature branches - Then they form one trend, pooled, not four
- and a second definition
jvmforms its own trend - and the default definition is listed first, the named ones after it in alphabetical order
- and a second definition
- Given the points of a trend
- Then they are chronological, oldest first, and the latest point is the newest build
Verified by
Scenario#49.02: The window is described by its median
So that one unusual run does not colour a whole month.
- Given a window of build times
- Then the trend reports the median, the fastest and the slowest run
- When a single build takes ten times as long as the others
- Then the median is unmoved — which an average would not be
- Given the History page
- Then it shows one card per definition, with the series as a line, the median as a reference, and the figures beside it
- and the branches view shows none: the trend belongs to the history
Verified by
Scenario#49.03: A build getting slower says so in words
So that the page states the finding instead of leaving it to be read off a line.
- Given a latest build a quarter or more above the median
- Then the card is marked
slowerand turns amber - Given a latest build within five percent of the median
- Then the card reads at the median — ordinary scatter is not a finding
- Given any other distance
- Then it is named as a percentage over or under the median, worded identically by the server and by the script
Verified by
Scenario#49.04: Only what actually measures a build time counts
- Given a failed, interrupted or cancelled build
- Then it is left out — its duration is how long it took to fail, which answers a different question
- Given a build that never left the queue, or one older than the window
- Then it is left out as well
- Given a definition with fewer than five runs in the window
- Then it has no trend at all, and the page says why
Verified by
Scenario#49.05: A private repository has no visible shape
- Given a private repository and a visitor without the control token
- When the trend is requested
- Then the answer is empty, not redacted — how often a build runs, when, and how long it takes is itself what must not be learned
Verified by
The Solution
BuildDurationTrends.of is the whole computation, pure over the recorded history and a now: filter to successful builds with a measured duration inside the window, group by definition, sort by time, and describe each group by its median, min, max, and the latest run's ratio to the median.
The level (ok/slower) is decided there and travels through the API, so the server-rendered card and the script-rendered one can never disagree about which build is worth looking at.
The median rather than the average is the load-bearing choice. Pooling branches into one line means a branch that redefined its build, or a run against a cold cache, lands in the same series as everything else; an average would carry such a run for weeks, while the middle value shrugs it off. That is what makes the pooling affordable.
The page draws the series with the x axis counting builds, not time. The question is whether the build is getting slower from run to run; spacing by time would squeeze a busy week against the right-hand edge and stretch a quiet one across the card.
Open Questions
- The threshold is a quarter above the median, and five percent is the band that counts as at the median. Both are guesses at what feels like a finding; only use will tell.
- The window is a constant, not a config key. A repository that builds twice a month has no trend under it — visible as the "not enough runs" note, which is at least honest about why.
- A build that has been slow for the whole window looks normal, because the median moved with it. Catching a slow drift needs a comparison against a period further back, which this does not do.
Additional Changes
docs/plan/20-build-duration-tracking.md: Scope Decision 2 is replaced. It grouped byname, giving every branch its own line; the reason it gave — a lagging branch pollutes a shared average — is now answered by the median instead of by the grouping. The step is ticked off indocs/plan/README.md.- Version 1.7.0 and its release-notes entry. It assumes PR#48 (build timeout, 1.6.0) lands first; if the order changes, the number does.