The SSD's one moment at 90 % kept a 16 GiB quota on `upgrade` for the thirty days it stayed in the window; the HDD's three imports on three days read as "growing 0.41 GiB/day". A disk is now judged by the 95th percentile of the last week's quarter-hour maxima, the growth is the median of the daily deltas, and the projection starts from the level. The window maximum is still named and still vetoes a downsize. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #78
6.6 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
The System page of the production host advised an upgrade for both disks on 2026-09-17, and both verdicts were wrong.
The SSD read upgrade | peak 14.46 of 16.00 GiB (90 %) in 9 d.
That peak was one quarter hour out of 885, the moment the old per-repository Gradle caches and the new shared one lay side by side during the migration of PR#76.
The disk held 8.4 GiB before and 11 GiB after.
A disk was sized by the highest quarter hour of the 30-day window, so that one moment would have kept the quota on upgrade for a month.
A one-off — an accident, a deliberate action of one afternoon — must not ask for a raise, least of all for thirty days.
The HDD read upgrade | growing 0.41 GiB/day, 90 % of the 20.00 GiB group quota in 22 days at 45 % of its quota.
The growth was three steps: a rootfs imported on 09.09, Testcontainers images on 11.09, another rootfs on 16.09, and six days of nothing in between.
The trend estimate of PR#42 took the smaller regression slope of the two window halves, which catches one step and not three.
The projection also started from the window peak, 8.97 GiB, while the disk held 7.05 GiB.
Non-Goals
- No change to RAM and CPU sizing.
- No change to the health verdict; the live gauges keep showing the moment.
- No change to the thresholds (
UPGRADE_RATIO,WATCH_RATIO,DOWNSIZE_RATIO) or to the bookable steps.
The Scenarios
Feature: a disk is sized by the level it holds and the growth it shows on most days
Background
- The level of a disk is the 95th percentile of the quarter-hour maxima of the last seven days — the percentile the CPU watch band has used since PR#42. A quarter hour is a moment; a level is what the disk holds in more than a twentieth of the week.
- The growth of a disk is the median of its daily deltas over the last seven days. A step is a delta on one day; a trend is a delta on most days.
- The peak stays the highest quarter hour of the whole window. It is named in the reason, and it can veto a downsize, but it cannot ask for a raise.
Scenario#78.01: A moment is not a level
- Given a 16 GiB quota holding 8.4 GiB for ten days, and one quarter hour at 14.46 GiB
- When the disk is sized
- Then the verdict is
fits- and the reason names both:
holds 8.40 of 16.00 GiB (53 %), peak 14.46 (90 %) in 10 d
- and the reason names both:
- But a disk at 85 % throughout the last two days is
tight, because a level held is a level
Verified by
- CapacitySizingTest — one quarter hour at 90 % is a moment, not a level: the production SSD of 2026-09-17
- CapacitySizingTest — a level held for two days is a level
Scenario#78.02: The window maximum is named and vetoes a downsize, nothing more
- Given a disk that held 93 % for two days a week ago and 50 % since
- When the disk is sized
- Then the verdict is
fits, the reason names the peak - Given a disk at 25 % with one quarter hour at 60 %
- Then the verdict is
fits, notdownsize: a moment cannot prove the slack a shrink needs
Verified by
- CapacitySizingTest — a level the disk no longer holds is named, not judged
- CapacitySizingTest — a moment vetoes a downsize the level alone would allow
Scenario#78.03: Steps are not growth
- Given a 20 GiB quota with steps of 1.9, 1.6 and 1.8 GiB on three of ten days and no change on the others
- When the disk is sized
- Then the verdict is
fitsand no growth is reported
Verified by
Scenario#78.04: Growth is projected from the level
- Given a disk growing 0.1 GiB on every day and one quarter hour at 88 % on the first day
- When the disk is sized
- Then the projection starts from the level, not from that moment, and the verdict is
fits - Given fewer than four daily deltas
- Then no growth is reported at all
Verified by
- CapacitySizingTest — growth is what most days show, and it is projected from the level, not from a moment
- CapacitySizingTest — a trend needs a majority of the week's days
- CapacitySizingTest — disk growing towards 90 % within thirty days is an upgrade even below the peak threshold
The Solution
CapacitySizing.disk no longer goes through byPeak.
It reads the level with sustainedLevel — SUSTAINED_PERCENTILE (0.95) of the maxima column of the last DISK_LEVEL_DAYS (7) — and judges upgrade and tight from it; the recommendation is the level with a third free, rounded to the bookable step.
The window maximum is still computed, named in the reason (holds … , peak …), and used for the downsize test as before: slack is proven against the worst moment, a raise against the level.
growthPerDay takes the level at the end of each of the last seven days from the quarter-hour averages, forms the daily deltas, and returns their median; fewer than MIN_TREND_DAYS (4) deltas or a negative median count as no growth.
The projection is the level plus thirty days of that growth, and the days left are counted from the level.
The two-halves regression and MIN_TREND_POINTS are gone.
CPU's two percentile calls now use the shared SUSTAINED_PERCENTILE instead of a literal.
The level is read from the last week rather than the whole window on purpose: what a disk held three weeks ago and has since been cleaned up is not what it holds, and the verdict should follow a cleanup within the week, not within the month.
Open Questions
- A disk that grows on most days and is pruned once a month shows its growth phase as a trend; whether retention pruning needs to be accounted for is left until it shows on a host.
Additional Changes
- Release notes and version bump to v1.21.1.
AGENTS.mdinvariant and the architecture skill describe the disk rule.