The History page showed three builds of one afternoon and nothing before
them. Retention dropped every result whose branch is no longer on origin,
and a merged pull request's branch is deleted right after the squash-merge
-- so the build that decided whether the merge was safe disappeared within
one poll cycle. The record of a branch was tied to that branch existing,
which leaves a "history" that can only ever show work in progress.
prune now marks such a result with BuildResult.branchGoneAt instead of
dropping it, and clears the mark should the branch come back. The entry
stays the record of a build that happened, within retentionPerBranch and
retentionMaxAge like any other. Only what made sense while the branch
lived ends with it: the exemption that keeps a dormant branch's last
status, keepLatestGreen (its permanent link serves artifacts that are
gone), and the stored artifacts themselves -- the artifact store is pruned
to history().withArtifacts(), in the watcher's cycle and in
DELETE /api/builds/{key} alike. PENDING and RUNNING entries are never
marked: a branch deleted while its last build is still in flight keeps
that build in every view that drives it.
latestFor, latestGreenFor and latestPerName skip marked entries, so the
branches view, the repository overviews, the watcher's recovery and the
permanent latest-green links behave exactly as before. history() is the
one accessor that returns them, which is the point.
prune was rewritten to work on positions rather than on a set of values:
two runs of one build can be equal in every field, and a value set would
keep or drop them together. The returned "removed" list reports the
entries as they were stored -- one dropped in the very pass that would
have marked it never carried the mark.
The three are one commit because they cannot be green apart: a marked
entry whose artifacts survive is the wrong behaviour, not a smaller step.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A row that survives its branch must not promise pages that cannot exist:
the artifacts were dropped with the branch, and the Gitea branch page is
gone too. BuildResultDto.branchGone carries the fact to the browser and
BuildRowView drops the branch link for such a row; builds.html and
werkator.js render the same "n/a", titled "Branch deleted from origin --
artifacts dropped".
Status, branch name, commit, start and duration stay as they are, and the
commit keeps its Gitea link -- a commit survives the branch it was on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three was one afternoon of merges -- the very afternoon that made the
History page look empty. The default moves to 5 in the data class, the
init templates, docs/configuration.md and docs/llms.md, which the
invariant asks to be kept in sync.
This repository merges through short-lived branches, so its own
.werkator.yml also sets retentionMaxAge: 90d. That age limit is what
bounds the surviving entries of deleted branches, which no longer expire
with their branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The PR-doc carries the scenarios and their Verified-by links. Its number
is the PR#000 placeholder until the pull request exists -- the filename,
the scenario IDs and the references in code, AGENTS.md and the skill all
have to be renamed together once it does.
AGENTS.md and the architecture skill state the new retention invariant:
which accessor returns marked entries, which do not, and what ends with
the branch after all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Thymeleaf condition on the artifacts cell was the one part of this
change that no test touched, and a broken expression fails at render time,
not at compile time. The page is now rendered through MockMvc with a
marked build: the branch name is there without a Gitea link, the artifact
link is gone, the dropped-artifacts note is there, and the commit still
links -- a commit outlives the branch it was on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The placeholder was in the PR-doc's filename and scenario IDs, in the
release notes, in AGENTS.md, in the architecture skill, in the Kdoc of
the classes that carry the new behaviour, in werkator.js and in two test
names -- the rename covers all of them at once, which is what PR#50
learned the hard way when its rename missed the stylesheet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mi
merged commit c26a4c7e32 into main2026-09-13 11:47:57 +02:00
mi
deleted branch history-survives-branch2026-09-13 11:47:57 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The History page showed three builds of one afternoon and nothing before them. Retention dropped every result whose branch is no longer on origin, and a merged pull request's branch is deleted right after the squash-merge -- so the build that decided whether the merge was safe disappeared within one poll cycle. The record of a branch was tied to that branch existing, which leaves a "history" that can only ever show work in progress. prune now marks such a result with BuildResult.branchGoneAt instead of dropping it, and clears the mark should the branch come back. The entry stays the record of a build that happened, within retentionPerBranch and retentionMaxAge like any other. Only what made sense while the branch lived ends with it: the exemption that keeps a dormant branch's last status, keepLatestGreen (its permanent link serves artifacts that are gone), and the stored artifacts themselves -- the artifact store is pruned to history().withArtifacts(), in the watcher's cycle and in DELETE /api/builds/{key} alike. PENDING and RUNNING entries are never marked: a branch deleted while its last build is still in flight keeps that build in every view that drives it. latestFor, latestGreenFor and latestPerName skip marked entries, so the branches view, the repository overviews, the watcher's recovery and the permanent latest-green links behave exactly as before. history() is the one accessor that returns them, which is the point. prune was rewritten to work on positions rather than on a set of values: two runs of one build can be equal in every field, and a value set would keep or drop them together. The returned "removed" list reports the entries as they were stored -- one dropped in the very pass that would have marked it never carried the mark. The three are one commit because they cannot be green apart: a marked entry whose artifacts survive is the wrong behaviour, not a smaller step. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>