55. Disk je pátá sama se hromadící veličina
Kontext
project.accumulation-is-measured jmenuje čtyři veličiny, které rostou, protože nikdo nerozhodl, že mají: otevřené pull requesty, propojené worktrees, lokální branche, běžící servery. Každá má měřicí příkaz, práh a příkaz ke snížení.
2026-09-12 tento stroj dosáhl nuly volných bajtů na svazku 926 GiB. Každá na něm běžící relace umřela najednou — ne řádně, ale s tím, že harness nemohl zapsat vlastní soubory úloh, což každá relace čte spíš jako rozbité nástroje než jako plný disk. Dvě relace ohlásily měření na nástěnce peerů během několika sekund, obě správně odmítly smazat cokoli, co nevytvořily, protože na tomto stroji nebylo nic, co by k tomu bylo oprávněno.
Příčina byla změřena hned poté. ~/dev/prismatic-majordomus-wt zabíralo 180 GB, z toho ~145 GB tvořilo apps/majordomus-cli/target ve worktrees, jejichž branche už byly sloučeny do origin/master. feature/canonical-versioning přistálo jako PR #242 před hodinami a stále drželo 32 GiB. Nikdo to nikdy nehodlal ručně získat zpět: adresář buildu worktree je jediný artefakt dokončené práce, který žádný krok jejího dokončení neodstraní.
Disk je tedy pátá veličina a má stejný tvar jako ostatní čtyři — neviditelná z místa, odkud se člověk dívá, roste z každé běžné akce, z žádné neklesá — až na to, že se hromadí rychleji než všechny a její útes je strmější. Nahromaděné pull requesty snižují propustnost. Plný svazek ukončí každou relaci na stroji najednou, ve stavu, kdy diagnózu (df) nelze spustit.
Prostor kolem něj už řídí dvě pravidla a žádné ho nepokrývá:
project.reclaim-only-what-you-ownzakazuje workeru odstraňovat build výstup jiného worktree, poté co to relace v jednom dni udělala dvakrát a spustitelný soubor kolegy zmizel uprostřed běhu. Končí větou „tlak na disk se hlásí, neřeší se odebráním nástrojů někoho jiného“ a nechává nepojmenováno, kdo ho smí řešit.project.destructive-sweeps-fail-closedexistuje kvůli dřívějšímu pokusu o tento úklid: skriptu, který čistiltarget/podlenow - mtime, kde tři nečitelná volánístatdaly stáří ~496938 hodin a tři aktivní worktrees byly vyčištěny jako nejstarší kandidáti v množině.
Rozhodnutí
Disk se přidává do project.accumulation-is-measured jako jeho pátá veličina (verze pravidla 2), měří se a redukuje ho reaper, který už omezuje čtvrtou, a omezuje se na straně poptávky před zahájením buildu.
Odstraňuje target/ a nikdy worktree
Na tom stojí celý návrh. Výstup buildu je regenerovatelný; worktree drží autorské soubory, které mohou existovat jen tam — v přehledu 2026-09-09 bylo nalezeno 66 necommitnutých zdrojových souborů v jednom worktree a 68 v jiném, bez reflogu a bez remote. Falešně pozitivní nález tohoto čističe někoho stojí nový build a nemůže stát ani bajt zdrojového kódu. To je hranice, která činí čistič bezpečným pro neobsluhovaný běh, a nesmí se později uvolnit na čističe worktrees.
Tři podmínky, všechny povinné, žádná z nich není ohlášení
- sloučeno — špička worktree je předkem
origin/master. - není živé — žádný proces tuto cestu nejmenuje a žádný proces v ní nestojí, čteno z
ps -Ao command=a z pracovního adresáře každého procesu. - nečinné —
git statusje prázdné.
Podmínka 2 nese návrh. Samotná sloučenost by byla katastrofou a ten případ byl na stroji toho rána, kdy to bylo psáno: feature/entities-are-routable zabíralo 11,8 GB, jeho relace ještě necommitovala, takže jeho špička byla přesně tam, kde ji merge nechal, a merge-base je hlásilo jako plně sloučené — zatímco v něm pracoval živý peer.
Nástěnka peerů záměrně není jedním ze signálů. Oznámení patří připojení a CLAUDE.md uvádí, že worker, který se znovu připojí, si ponechá svou práci, ale ztratí své místo na nástěnce — což je přesně okno, ve kterém běží reaper. ps a pracovní adresář jsou to, co stroj ví, aniž by mu to kdokoli řekl.
mtime také záměrně není signál. Je to predikát, který použil dřívější incident, find -newermt na macOS tiše nic neodpovídá a průchod stavu přepisuje mtime indexů a falšuje živost každého stromu, kterého se dotkne — což je také důvod, proč kontrola nečinnosti čte stav pomocí --no-optional-locks, aby tento reaper nefalšoval živost pro dalšího čtenáře.
Selhává uzavřeně a odmítnutí je úplné, ne po kandidátech
project.destructive-sweeps-fail-closed vyžaduje, aby predikát, jehož vstup nešlo přečíst, byl nepravdivý. Živost je jiného druhu než vstupy jednotlivých kandidátů: pokud ps nebo čtení pracovního adresáře vrátí prázdno, úklid nedokáže rozlišit živé od mrtvých pro nikoho, takže neuvolní vůbec nic a řekne to, místo aby pokračoval nad kandidáty, které nedokáže klasifikovat. Totéž platí, když nelze rozlišit origin/master. Nečitelné vstupy jednotlivých kandidátů — velikost, která nešla změřit, pracovní strom, který git nepřečetl, adresář bez značky cargo — daného kandidáta vyloučí a započítají se do řádku skipped zprávy.
Primární checkout je vyloučen jménem, ne predikátem
Sedí na masteru, je čistý častěji než ne a drží spustitelný soubor, který si jiné checkouty půjčují přes MAJORDOMUS_BIN. Predikát by ho uvolnil v klidné odpoledne. project.reclaim-only-what-you-own tu dostává svou chybějící polovinu: worker stále uvolňuje jen své vlastní a pojmenovaná výjimka je jeden commitnutý příkaz, který každého kandidáta klasifikuje dřív, než jedná.
Sestavení je omezeno volným místem tak, jako je čekání omezeno časem
project.every-wait-is-bounded: čekání bez timeoutu je zaseknutí s hezčím jménem. Build bez limitu na disk, který smí spotřebovat, má stejný tvar a skončil stejně.
df v doctor nebo v úvodním banneru by pomohl příští osobě a nemohl by pomoci nikomu, kdo uvízl, protože když se svazek zaplnil, nikdo nemohl spustit vůbec nic. Pomůže odmítnutí startu: mj_rust_space_check v lib/rust_bin.sh, volané třemi místy, která spouštějí sestavení jménem workera — launcherem, scripts/derive a just build / build-release.
Hranice je 5120MB a je změřena, ne zvolena: plný cargo build --locked pro apps/majordomus-cli do prázdného target/ s teplým sccache zapsal 2 566 652 KiB (2,45 GiB) dne 2026-09-12. Hranice jsou dvě takové hodnoty — dost na dokončení buildu, který začíná, i na artefakty release nebo testů, které stejná relace obvykle přidá navrch. Měření je zapsáno vedle čísla v lib/rust_bin.sh; práh bez zdůvodnění vedle sebe je vada, za kterou už tento repozitář jednou zaplatil.
Chování meze při selhání je záměrně opačné než u úklidu. Mazací predikát, který nemůže změřit svůj vstup, musí být nepravdivý. Mez, která nemůže změřit svůj vstup, nesmí odmítnout: znemožnit každé sestavení na stroji, jehož df je nečitelné, zastaví veškerou práci kvůli hypotéze. Neměřitelné volné místo to tedy řekne na stderr a nechá sestavení běžet a odmítne se jen změřené a nedostatečné. MAJORDOMUS_MIN_FREE_MB=0 ji zruší.
Proč jsou reaper a mez jedna změna
Jsou to dvě strany jedné veličiny a žádná sama nestačí: samotný reaper dovolí jedinému sestavení spotřebovat posledních pár gigabajtů mezi dvěma běhy a samotná mez nechá na disku navždy 140 GB přistálého výstupu sestavení, zatímco nikomu nedovolí pracovat. Sdílejí měření (df na svazku, na který sestavení zapisuje), doktrínu (toto pravidlo), kontrolu a případ. Jejich rozdělení by vytvořilo dva poloviční mechanismy s jedním příběhem mezi nimi.
Důsledky
scripts/reap-orphansmá dva subjekty a dva oddělené souhlasy:--killpro servery,--reclaimpro výstup buildu. Volající, který už spouští--kill, nezačne mazat sto gigabajtů, s nimiž nikdy nesouhlasil.- Zkušební běh na tomto stroji dne 2026-09-12 klasifikoval 34 build adresářů: 24 k uvolnění (140,3 GB), 10 ponecháno (2 jako vlastní tohoto checkoutu, 5 nesloučených, 2 s necommitnutou prací, 1 s živým workerem), 0 přeskočeno pro nečitelný vstup.
- Sestavení teď může odmítnout začít. To je smysl a je to změna chování pro každého volajícího
bin/majordomus-cli,scripts/deriveajust buildna stroji pod 5 GB. scripts/ci/backlog-checkzískává strukturální nálezy páté veličiny: reaper stále odstraňuje jen build adresáře, stále drží všechny tři podmínky a stále odmítne, když nelze změřit živost.- Zde se nerozhoduje: zda má reaper běžet na časovači. Je z konstrukce bezpečné ho spouštět bez dohledu, ale nic na tomto stroji zatím nic neplánuje a plánovač je vlastní šestá veličina.