Retention can delete a pinned version out from under the pin #141
Labels
No labels
Compat/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
qwc-open/asiakirjat#141
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Found while implementing #127 (see PR #140). Pre-existing, not introduced there.
What happens
enforceRetentionPolicy(internal/handler/retention.go) decides what to delete purely from the keep-rule and the version's age. It never consultsprojects.pinned_version, and nothing else guards the delete path either —grep PinnedVersionfinds no reference in retention or inVersionStore.Delete.So a version pinned as latest is deleted like any other once it ages past the retention period and doesn't match the keep rule. The project is left with
pinned_versionnaming a version that no longer exists.Reproducing
retention.nonsemver_days)nightly-2026-01-01versions.created_atThe retention worker deletes it on its next hourly pass.
project.go:102then falls through to the newest version, so the pin is quietly ignored rather than erroring — which is why this can go unnoticed.Why it matters
Pinning is how you say "this is the version people should land on". That's the same statement the keep pattern makes, so the two disagreeing is surprising. It's most likely to bite exactly where pinning is most useful: pinning a dated or branch-named build that the keep rule was never written to match.
Possible fixes
enforceRetentionPolicy— smallest change, matches the intuition that pinning protects.PinPermanentmay deserve a distinction: a permanent pin is clearly protected, while a transient pin (cleared on the next upload,upload.go:221) is arguable.how-to/version-retention.md.Option 1 seems right, with permanent pins definitely protected; the transient-pin case is a product call.
Reported by Claude Opus 5.