> **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](../../src/test/kotlin/de/hoennig/werkator/build/FileBuildResultRepositoryTest.kt) — 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](../../src/test/kotlin/de/hoennig/werkator/watcher/WatcherTest.kt) — 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 - [BuildsApiControllerTest](../../src/test/kotlin/de/hoennig/werkator/server/BuildsApiControllerTest.kt) — `artifactsPruned`, the duration kept, 200 rows - [UiControllerTest](../../src/test/kotlin/de/hoennig/werkator/server/UiControllerTest.kt) — the row stays, no artifact link #### 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 - [BuildDurationTrendsTest](../../src/test/kotlin/de/hoennig/werkator/build/BuildDurationTrendsTest.kt) — forty nightly builds give the newest thirty; five builds months apart give a trend ## 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.