Files
werkator/docs/prs/2026-09-30-PR#84-run-history-outlives-artifacts.md
miandmhoennig 2cc7a04987
werkator build succeeded after 01:56
werkator/deploy build succeeded after 00:07
run history now outlives artifacts (#84)
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #84
2026-09-30 11:16:57 +02:00

5.3 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.

The Problem

On vm4006 the History of hs.hsadmin.ng shows no duration trend for the release build. A trend needs five successful builds of a definition within 30 days. The repository sets artifacts.retentionPerBranch: 3, and retention dropped the result together with its artifacts. release runs on master only, so it never had more than three results. The default trend exists only because every branch keeps three results of its own, and the results of deleted branches stay since PR#59.

The 30-day window is a problem on its own:

  • A rarely built repository never collects five builds in 30 days.
  • A nightly build shows only its last month; a slow drift over weeks becomes the median, and "slower" never fires.

Non-Goals

  • No new configuration key; the number of kept records is fixed.
  • No change to what keeps artifacts: retentionPerBranch, retentionMaxAge and keepLatestGreen mean what they meant for the artifacts.
  • No paging in the History table.

The Scenarios

Feature: a build's record outlives its artifacts

Background

  • A record is the entry in build-results.json: status, commit, times, duration, phases.
  • The artifacts are the stored files and the log of a build.

Scenario#84.01: Retention takes the artifacts, not the record

So that a build with one branch only, like a release or nightly build, keeps a history of its durations.

  • Given a build name with more builds than retentionPerBranch, or builds older than retentionMaxAge
  • When retention runs
  • Then those builds lose their artifacts and are marked with artifactsPrunedAt
    • and their records stay, up to the newest 100 per build name
    • and a marked record is no name's latest and no latest green, so no permanent link leads to missing artifacts
  • And the mark stays on later passes: artifacts do not come back
Verified by
  • FileBuildResultRepositoryTest — artifacts of the retention count, records of all, the mark kept; records only beyond a hundred; the latest lookups skip marked records; the adapted count, cutoff, keep-latest-green and gone-branch tests
  • WatcherTest — keep-latest-green and the age limit take the artifacts, the records stay

Scenario#84.02: The History shows such a build without artifacts

So that the History is a history of the builds that ran.

  • Given a record whose artifacts retention removed
  • Then its History row shows status, commit, start and duration
    • and its artifact cell says n/a, titled "Artifacts removed by retention"
  • And the JSON carries artifactsPruned: true
  • And the History answers with its newest 200 rows, because the page polls the whole list
Verified by

Scenario#84.03: The trend shows the last 30 successful builds

So that a rarely built repository gets a trend, and a nightly build is compared with more than its last month.

  • Given the successful builds of a definition
  • Then its trend shows the newest 30 of them, however old they are
  • And five builds are still the minimum for a trend
Verified by

The Solution

BuildResult.artifactsPrunedAt is the second mark next to branchGoneAt; the extension hasArtifacts combines both. FileBuildResultRepository.prune now computes two sets. The artifact set is the old kept set, minus the records whose artifacts are gone already. The record set is the active builds plus the newest RECORDS_PER_NAME (100) of each name. A record outside the artifact set gets the mark, a record outside both is dropped. withArtifacts() excludes marked records, so the artifact store, which is pruned to it, deletes their files.

A hundred is a fixed constant, not a key: a record is a few hundred bytes, and a hundred covers three months of a nightly build. BuildDurationTrends.of takes the last RUNS (30) successful builds per definition instead of a window of days.

An older Werkator reading the file ignores the unknown field and sees the marked records as ordinary ones; its own retention then drops them as before.

Additional Changes

  • AGENTS.md, the architecture skill, docs/configuration.md, docs/llms.md, the init template and the ArtifactsConfig comments describe the new meaning of the retention keys.
  • BuildExecutorTest Scenario#65.04 failed once on this branch's CI build: its other build slept three seconds, and a slow machine finished it before the deployment. The other build now runs until the test opens a gate file.
  • Version 1.24.0 with its release note.