54. Zpráva commitu je posuzovaná hodnota, ne konvence a ne prompt
Kontext
Tento předmět existoval na třech místech a žádné o zbylých dvou nevědělo.
Repozitář uměl commit přečíst. release::commits od vzniku changelogu zpracovává konvenční předměty z logu, aby changelog sestavil, přičemž I1305 a M000 řeší proti tomu, co vrstva drží, a id, které se nikam nevyřeší, zahazuje.
Repozitář žádný nedokázal posoudit. project.conventional-commits ve verzi 1 uvádělo konvenci a jeho chování při selhání znělo „Žádný příkaz o tomto pravidle nerozhoduje; rozhoduje reviewer.“ Ta věta je tvar vady, kterou má zbytek této governance odstranit — uvedená invariance bez verdiktu za sebou — a měřitelně nefungovala. Přes 1161 ne-merge commitů je medián subjectu 73 znaků, 48 přesahuje i šířku, kterou tento projekt deklaruje pro řádek čehokoli, a 38 nese slovo typu, které neobsahuje žádný slovník. Nikdo si nevšiml, protože se nikdo nedíval. Pravidlo bylo jedním z těch vynucovaných reviewem, které ADR 0048 učinilo spočitatelnými: viditelné, uváděné při každém běhu a čekající, až někdo napíše případ.
Třetí místo bylo úplně mimo repozitář. Postup psaní commitu — odvodit scope z cesty, propojit práci, nevymýšlet číslo úkolu, oprava potřebuje test — žil v próze, kterou AI klient četl a prováděl ručně, jeden soubor na klienta, s tabulkou cesta-scope udržovanou člověkem. Pracovní postup, jehož sémantika žije v promptu, je neverzovaný, nevynutitelný, netestovatelný a pravdivý pro přesně jeden nástroj. Tabulka je také odvoditelná a nikdo to neřekl: každý commit v historii je ukázkovým příkladem toho, do kterého scope patří sada cest, rozhodnutým tím, kdo změnu udělal, a udržovaným tím, kdo ji zrecenzoval.
Chybějící nebyla nikdy gramatika. Byl to soudce, okamžik, kdy má soudce promluvit, a deklarace, ze které mluví.
Rozhodnutí
Zpráva commitu je typovaná hodnota spustitelného souboru s jednou gramatikou. commit::CommitHeader::parse je totální a nikdy neselže; changelog a soudce čtou stejný parse a liší se jen v tom, co s hlavičkou, která není conventional. Pro changelog to není selhání — commit, který nikdo nenapsal conventional, se přesto stal, a changelog, který by jej vynechal, by lhal o tom, co release obsahuje. Pro soudce je to jedno zjištění, commit.not_conventional, a jeho důsledky se záměrně nehlásí jako samostatná zjištění: kdo opravuje update stuff, nepotřebuje se dozvědět ještě i to, že jeho rozsah je neznámý. V hooku, kontrole ani skriptu není druhý regulární výraz.
Politika jsou data, deklarovaná jednou. commit: v .ai/repo/policy.yaml nese subject_max_chars a tři úrovně; schéma majordomus.policy/v1 vlastní její tvar a share/allow/policy.txt se z něj generuje. subject_max_chars: 100 se měří, nevolí se: konvence gitu je 72, medián této historie je 73 a její 95. percentil 98, a limit, který porušuje polovina historie, není limit, ale šum, který lidi učí kontrolu ignorovat. Slovník typů záměrně není v politice — je to ChangeKind, z něhož už changelog vykresluje nadpisy, a druhá kopie těch slov je duplicita, kterou toto rozhodnutí odstraňuje.
Slovník scope se pozoruje, nedeklaruje. commit.scopes se učí, které scope tento repozitář používá, jak často a k jakým adresářům, z nejvýše 1500 commitů vlastního logu, přičemž počítá spojení každého scope s prefixy adresářů do hloubky čtyř segmentů — čtyř, protože zdroj tohoto repozitáře žije v apps/majordomus-cli/src/<subsystem>/ a tři se zastaví o úroveň nad subsystémem, kde má tucet scope commity. Scope mimo slovník je varování, nikdy odmítnutí: odvozování nesmí odmítnout první commit subsystému, který nikdy neviděl. Tabulka nemá jak se mýlit; slovník čtený z historie je správný v den, kdy je subsystém commitnut, a sám zaniká, když se ho rok nikdo nedotkne.
Posouzení se děje tam, kde je jeho důkaz. .githooks/commit-msg se ptá commit.validate se staged cestami — jediný okamžik, kdy existuje zpráva i soubory, proto se fix_requires_test lze zeptat tam a nikde jinde. Kontrola scripts/ci/commit-policy čte historii přes commit.history a cesty nezná, takže tento nález neučiní vůbec: posouzení, jehož důkaz chybí, se neprovede, místo aby se hádalo. Hook nenese žádnou logiku; ptá se.
Čtyři schopnosti, deklarované jednou a promítnuté. commit.scopes, commit.plan, commit.validate a commit.history jsou deklarovány v apps/majordomus-cli/src/capability/builtin/commit.rs a příkazový řádek, nástroj a zdroj MCP, routa HTTP, operace OpenAPI, doplňování a referenční přehled schopností jsou z této deklarace odvozeny podle project.interfaces-are-projections. Provedení commitu mezi nimi záměrně není. git commit je mutace repozitáře a politika vystavení zastavuje každý strojový povrch u lokální mutace: agent se smí ptát, čím by commit byl a zda zpráva projde; commituje člověk. Plánovač a soudce jsou čisté funkce stromu a zprávy, což umožňuje ptát se jich odkudkoli.
Plán má otisk a uvádí vlastní zdůvodnění. Předmět, který plán navrhuje, je záměrně prázdný — co změna udělala, je jediné, co žádný důkaz ve stromu nemůže říct, a plánovač, který by napsal sebejistou větu, by do historie zapisoval fikci. Seskupení je doporučení s přiloženým zdůvodněním, nikdy automatické rozdělení. Otiskem je repozitář, worktree, HEAD a hash přes každou změnu s její fází a stavem, protože tu jeden checkout sdílí několik workerů a repozitář ještě víc, a plán spočtený na jednom HEAD a provedený na jiném by commitoval soubory, které někdo jiný stageoval, pod zprávou o práci, která už není tím, co se změnilo. Není to zámek a závodu nezabrání; odmítá být tím, kdo ho potichu prohraje.
Pravidlo se ve verzi 2 stává blokujícím nad zaznamenaným základem. 54 commitů této historie politiku nesplňuje. Všechny jsou zveřejněné, takže jejich přepsání nepřipadá v úvahu: počet žije v .ai/repo/commit-policy-baseline.txt, kontrola selže, když stoupne, a ohlásí, když klesne. Commity, které větev přidá, se posuzují podle politiky přímo, bez základu, protože hook tyto zprávy odmítl, když se psaly — selhání tam znamená, že hook byl obejit nebo se politika změnila.
Vlastní subjekty gitu jsou vyňaty, ne chybné. Merge …, Revert "…", fixup! a squash! skládá git a validátor, který by je odmítal, by odmítal git. Subjekt merge:, který napsal člověk, vyňat není: tento repozitář je píše záměrně pro integrace, takže merge je deklarované slovo typu a tyto commity podléhají stejné šířce a stejným odkazům jako všechny ostatní.
To neodporuje ADR 0051, které degradovalo conventional commits z autority nad verzí release na důkaz pro ni. Rozhodnutí se týkalo toho, co smí určit číslo verze: změřený veřejný kontrakt, nikoli věta, kterou někdo napsal. Toto se týká toho, zda je ta věta vůbec čitelná. Předmět commitu je špatná autorita a dobrý záznam, a dobrým záznamem je jen tehdy, když ho něco čte.
Zamítnuté alternativy
Ponechat postup jako prompt v každém AI klientovi. Status quo a nejsvůdnější možnost, protože už existovala a nestála nic, když se nechala být. Zamítnuto ze čtyř důvodů, z nichž každý je sám o sobě smrtelný: platí pro přesně jeden nástroj, takže každý klient potřebuje vlastní kopii a kopie se rozcházejí; není verzovaný, takže nikdo neumí říct, jaký postup byl v den, kdy byl commit napsán; je nevynutitelný, takže klient, který jej ignoruje, se nedá odlišit od klienta, který jej dodržuje; a je netestovatelný, takže nic nemůže selhat, když je špatně. Konkrétním důkazem byla tabulka cesta-rozsah uvnitř něj — udržoval ji člověk, v den napsání byla správná a přejmenování adresáře mohlo způsobit, že jmenovala rozsah, který historie přestala používat, aniž by si toho cokoli všimlo. Workflow, jehož sémantika žije v promptu, je workflow, které se tento repozitář rozhodl nemít.
Ponechat pravidlo poradní a nechat rozhodnout reviewera. Také status quo a poctivá verze je, že reviewer rozhodoval a měření říká, co z toho vzešlo: 48 předmětů přes šířku a 38 neznámých slov typu, kterých si nikdo nevšiml. Zamítnuto, protože vlastní chování pravidla při selhání — „Žádný příkaz toto pravidlo nerozhoduje; rozhoduje reviewer“ — nebylo deklarovanou výjimkou ve smyslu ADR 0048, ale popisem absence. Nejmenovalo žádný reviewed_because a neexistoval důvod, proč by mohlo: gramatika je mechanická, šířka je číslo a id záznamu se buď vyřeší, nebo ne. Pravidlo ADR 0048 platí přesně — spustitelný důkaz vždy vyhrává a pravidlo, které získá případ, přestává být vynucováno review. Ponechat ho poradní, aby se ušetřila cena případu, by znamenalo zvolit falešnou zelenou.
Klíč types: v politice. Nasnadě ležící tvar, který by dal slova typů vedle šířky, kde je čtenář čeká. Zamítnuto, protože je ChangeKind už drží pro changelog a druhá kopie je přesně ta duplicita, kterou toto rozhodnutí odstraňuje: obě by se rozešly při prvním rozšíření kterékoli z nich a nesoulad by se objevil jako commit, který soudce odmítl a changelog vykreslil.
Tabulka cesta-rozsah v repozitáři místo v promptu. Přesunutí tabulky z prózy do .ai/ by ji učinilo verzovanou a sdílenou, čímž jsou zodpovězeny dvě ze čtyř námitek. Zamítnuto, protože zůstává ta, na které záleží: tabulka nemá jak se mýlit. Je správná, když se napíše, a poté tiše zastarává, zatímco důkazy o tom, co by měla říkat — každý commit, který kdy vznikl — už v repozitáři jsou a nepotřebují údržbu.
Regulární výraz v hooku. Nejlevnější možný hook commit-msg a důvod, proč se konvence commitů většiny repozitářů vynucují dvakrát. Zamítnuto, protože by pak existovaly dvě gramatiky: ta, kterou čte changelog, a ta, kterou hook odmítá. Shodovaly by se v den, kdy byl hook napsán, a potom ne, a způsob selhání — commit přijatý při zápisu a vypuštěný z poznámek k vydání — je neviditelný, dokud někdo nečte changelog a nehledá práci, o které si pamatuje, že ji udělal.
Přepsat 54 selhávajících commitů. Každý je publikován, takže jejich přepsání přepisuje historii, kterou už drží jiné klony a každý odkaz z pull requestů. Zamítnuto; baseline je poctivá alternativa a ratchetuje.
Odmítnout neznámý rozsah. Symetrické s chybami, a špatné. Slovník je inference nad korpusem a první commit nového subsystému je přesně ten případ, kdy je korpus prázdný — odmítnutí by udělalo mechanismus nepřátelským v jediný okamžik, kdy nemá co říct. Varování je nález, který odpovídá míře jistoty.
Nechat předmět napsat plán. Plánovač, který vidí diff, o něm umí složit větu. Zamítnuto: byla by to fikce s autoritou nástroje a historie generovaných předmětů je historie, která neříká nic, co git diff neříká už.
Vystavit commit.apply — nechat agenta commitovat. Odmítnuto podle politiky vystavení, kterou tento repozitář už má: každá strojová plocha končí u lokální mutace. Hodnota tohoto subsystému je v tom, že je čistou funkcí stromu a zprávy — funguje offline, nemůže uspět napůl a každou odpověď lze přepočítat a zkontrolovat. Schopnost, která by spouštěla git commit, by neměla žádnou z těchto vlastností a byla by jedinou operací v registru, jejíž selhání zanechá repozitář ve stavu, o který volající nežádal.
Důsledky
Commit lze nyní odmítnout. Hook commit-msg skončí s exit 10 a git přeruší, přičemž vypíše každý nález s jeho kódem. Ta cena je ~0,8 s na commit a platí ji každý worker v tomto repozitáři, člověk i agent. Gate stojí 1720 ms na 1788 commitů. Oba splňují project.blocking-checks-cheap; hook byl dimenzován na okamžik, ve kterém běží, a ani jeden nesahá do sítě.
Hook je deklarován, ne předpokládán. Je registrován pod enforcement: v .ai/repo/policy.yaml jako commit-policy-on-message, takže jej majordomus doctor sladí: cesta se musí vyřešit, být spustitelná a být vyvolána .githooks/commit-msg, aniž by byl spolknut její exit kód. Checkout, který hook odpojí, je selhání doctor, ne checkout, který tiše přestal soudit.
Pravidlo opustilo množinu vynucenou review. project.conventional-commits se přesouvá z class: advisory bez důkazu na class: blocking jmenující test/cases/274_commit_policy.sh a scripts/ci/commit-policy. Podle řazení ADR 0048 to znamená, že je kontrolováno, a počet review_only klesne o jedna. Zvýšení verze na 2 je mechanismus, který říká, že se změnil význam pravidla, ne jeho znění.
Schéma politiky získalo sekci. commit: je nový klíč majordomus.policy/v1, takže share/allow/policy.txt a vedlejší schéma JSON se regenerují a politika používající klíč proti staršímu executable je odmítnuta jako neznámá. Toto odmítnutí je správné a k tomu slouží ochrana generování; je to také důvod, proč nelze tuto změnu aplikovat na strom bez předchozí regenerace.
Základna je dluh, který je nyní viditelný a ohraničený. 54 je číslo, které tento repozitář může sledovat. Může klesat — každá větev, která přistane, přidá commity držené politikou bez výjimky — a nemůže růst. Checkout bez trunku k porovnání hlásí, že nic neměřil, místo aby hlásil úspěch, takže kontrola nemůže tiše zezelenat v mělkém klonu.
Soudce je záměrně úzký. Nerozhoduje, zda je předmět dobrá věta, zda je změna atomická nebo zda stála práce za to. To rozhoduje recenzent a validátor, který by předstíral, že to umí, by byl validátor, kterému nikdo nevěří. Pravidlo proto zůstává širší než kontrola, což je poctivý vztah mezi nimi.
Jedno riziko je přijato u naučeného slovníku. Je to odvozování z vlastní minulosti repozitáře, takže reprodukuje rozsah, který historie používala špatně, a přejmenovaný subsystém nese svůj starý rozsah, dokud okno drží commity o něm. Obojí jsou varování a nic neodmítá, což je omezení škod; repozitář, který by chtěl staré chování zpět, by žádal o tabulku, a alternativy výše říkají, proč se nevrátí.
Otisk plánu nedělá plánování bezpečným, jen poctivým. Dva workeři mohou stále stagovat do jednoho checkoutu a jeden z nich prohraje. Změnilo se to, že poražený se jednou větou dozví, co se pohnulo — repozitář, worktree, HEAD, nebo sada změn — místo aby pod svou zprávou commitnul cizí stagované soubory.