Kódovat s grainem: Dovednost, která zabrání AI kódovacím agentům nafouknout vaše repo

Publikováno dne Sep 22, 2026 od Štefan Moravík.
AI Agents Claude Code Developer Tools Code Quality
Na této stránce 8 min čtení
  1. 1Krátká verze
  2. 2Selhání, pro které to je
  3. 3Grain
  4. 4Ponytail
  5. 5Jak přimět agenta, aby to skutečně dělal
  6. 6Co se stalo v repu, včetně části, která nefungovala

Krátká verze

  • AI kódovací agenti jsou dobří v přidávání kódu a špatní v tom, aby si všimli, že ten kód už existuje. Ponecháni sami sobě dají repu druhého pomocníka ke každému prvnímu a každý čtenář musí zkontrolovat oba, aby mohl věřit kterémukoli.
  • Řešením jsou dva návyky zapsané tak, aby je agent dodržoval. Grain: najdi nejbližší existující příklad stejného druhu věci a zrcadli jeho tvar, umístění a názvy. Ponytail: vyšplhej po žebříku od „musí tohle vůbec existovat“ k „minimu, které funguje“, a zastav se na první příčce, která drží.
  • Obě jsou teď veřejnou dovedností pod licencí MIT: grain-skill na GitHubu . Obsahuje pravidla pro psaní, revizní průchod s pevnými tagy a podobu závěrečné řádky, která přiměje agenta pojmenovat, co zrcadlil.
  • V našem hlavním repu od srpna: 386 issue a pull requestů nese plánovací komentář pojmenovávající vzor, který se má zrcadlit, napříč 1 333 commity do stromu o zhruba 18 000 souborech. 35 míst v kódu nese komentář ponytail: pojmenovávající záměrné zjednodušení a jeho cestu k vylepšení.
  • Nepříjemné zjištění: agent vyvolal tu dovednost jako dovednost nula krát. Pravidla fungovala, protože jsem je dal i do projektového CLAUDE.md, do definic worker agentů a do revizního checklistu. Dejte pravidlo tam, kde agent už čte.

Psal jsem o tom už jednou a výsledek se mi nelíbil, protože popisoval princip, a ne mechanismus. Tohle je verze s mechanismem.

Selhání, pro které to je

Tři příklady z našeho backendu, všechny skutečné, všechny z pull requestů napsaných agentem, které prošly svými testy.

Nová REST routa, která si aplikační službu vytáhla přímo z továrny. Každá sourozenecká routa v repu jde route → controller → service → repository, každý skok injektovaný. Tahle zkolabovala řetěz na route → service. Fungovalo to. Také to znamenalo, že jediné místo, kde pro tuhle funkci žije validace požadavku a tvarování odpovědi, neexistovalo, a další agent, který se té funkce dotkne, měl na výběr ze dvou tvarů.

Příliš velká služba rozdělená na _payloads.py, _protocol.py a _tokens.py, každý modul holých async def, přičemž služba volala protocol.discover(...) přes import. Kontrola délky souboru zezelenala. Věc, které má dependency injection zabránit, se vrátila přesně: spolupracovník zvolený v době importu, nenahraditelný bez patchování modulu, chybějící v konstruktoru, který dokumentuje, na čem služba závisí.

Druhý normalizér. Už jeden existoval. Agent ho nenašel, napsal dalších osm řádků, které dělaly skoro totéž, a teď si dvě funkce s odlišným chováním v okrajových případech obě nárokují kanonizovat stejné pole.

Nic z toho není bug v tom smyslu, jaký zachytí test. Každé z toho je agent, který zvolil kód, jejž dokázal vygenerovat, místo kódu, který existoval. Udělejte to napříč několika sty pull requestů a repo přestane mít tvar.

FlowHunt Logo

Připraveni rozšířit své podnikání?

Začněte svou bezplatnou zkušební verzi ještě dnes a viďte výsledky během několika dní.

Grain

Nový kód by měl být nerozlišitelný od kódu kolem sebe. Dovednost mění tuhle větu na pět kroků, které agent udělá před psaním.

Nejdřív najdi vzor. Pro endpoint, službu, repozitář, query hook, store slice nebo komponentu najdi nejbližší existující a zkopíruj jeho tvar, umístění souboru a pojmenování. Nevymýšlej strukturu, když vzor existuje. V našem repu je slice branding pojmenovaným vzorem pro backend, protože je malý, čistý a pokrývá všechny vrstvy, a tabulka v dovednosti říká pro každý druh artefaktu, který soubor otevřít a co zrcadlit.

Respektuj vrstvy. Prezentace volá aplikaci, aplikace volá doménu a infrastruktura implementuje doménu. Nový import přes tyhle linie neprojde kontrolou architektury. Přečti si dokument o vrstvách, neodvozuj ho znovu.

Lokální grain proti grainu repa. Drž se svých sourozenců. Ale pokud se sami sourozenci odchylují od kanonického řetězu, drž se kanonického řetězu a odchylku poznamenej, místo abys kopíroval drift. „Odpovídá to ostatním pěti routám v tomhle souboru“ nestačí, když všech šest přeskočí vrstvu, kterou kanonický slice používá. Porovnával jsi příliš lokálně.

Použij kanonického pomocníka. Pokud pomocník, typ nebo vzor už v repu žije, použij ho. Dva normalizéry nutí každého čtenáře diffovat oba.

Názvy nesou význam. Znovu použij slovník, který už v kódu je. Nové jméno pro existující koncept je bug čekající na čtenáře.

Test na konci je ten, který dávám recenzentům: pokud poznáš jen podle tvaru, které řádky napsal agent, šel proti grainu.

Ponytail

Líný znamená efektivní, ne nedbalý. Nejlepší kód je kód, který se nikdy nenapíše. Nejdřív problém plně pochop, každý soubor, kterého se změna dotkne, skutečný end-to-end tok, a pak vyšplhej po tomto žebříku a zastav se na první příčce, která drží.

Žebřík lenosti: musí to existovat, už je to v kódové základně, stdlib, nativní platforma, nainstalovaná závislost, jeden řádek, a teprve pak minimální kód, který funguje

S žebříkem jdou dvě pravidla.

Oprava bugu je příčina, ne symptom. Ticket pojmenovává symptom. Projdi grepem každého volajícího funkce, které se chystáš dotknout. Jedna pojistka ve sdílené funkci je menší diff než pojistka v každém volajícím a záplatování jen té cesty, kterou ticket pojmenovává, nechá sourozenecké volající rozbité.

Udrž diff minimální. Diff je to, co revize čte. Žádné refaktory mimochodem, žádná přejmenování, žádné přeformátování, žádná vylepšení typu „když už jsem tady“, žádné sahání do souborů, které úkol nevyžadoval. Každé z nich zahrabe skutečnou změnu. Pokud je diff větší než problém, zkrať ho. Vylepšení, která jsi zahlédl, jsou follow-up issue, ne commity navíc v této větvi.

A limity, protože „líný“ je slovo, které se nejspíš přečte špatně. Nikdy nezjednodušuj pryč validaci vstupu na hranici důvěry, ošetření chyb, které zabraňuje ztrátě dat, bezpečnost, základy přístupnosti ani nic, o co uživatel požádal. Nikdy nebuď líný v porozumění: žebřík zkracuje řešení, nikdy čtení. Malý diff na špatném místě je druhý bug. Netriviální logika po sobě nechá jednu spustitelnou kontrolu, tu nejmenší věc, která selže, když se logika rozbije.

Když agent vědomě zvolí příčku a zná její strop, řekne to v kódu:

# ponytail: global lock, per-account locks if throughput matters

V našem repu jich teď je 35. Každý je rozhodnutí, které by jinak musel budoucí čtenář rekonstruovat, a značka, kam se podívat, až se na strop narazí.

Jak přimět agenta, aby to skutečně dělal

Princip v dokumentu není chování. Tři mechanismy z těchto dvou udělaly chování a seřadil bych je v tomto pořadí.

Zadání nese vzor. V architektonickém vzoru píše hlavní model zadání pro workera a pole Design v zadání pojmenovává nejbližší existující vzor, který se má zrcadlit, a minimální změnu řešící příčinu. Worker nemusí grain hledat. Dostane ho. V našem repu to je to, co znamená těch 491 plánovacích komentářů u 386 issue a pull requestů: plánovací krok, který pojmenuje, co zrcadlit, než kdokoli začne psát.

Agent končí tím, co zrcadlil. Report každého implementátora končí jednou řádkou, mirrored: <path>, nebo pojmenuje nový vzor a proč se žádný sourozenec nehodil. Je to levné a je to nejlepší revizní pomůcka, jakou jsem přidal. Řádka mirrored: ukazující na špatný druh souboru vám řekne, že diff je špatný, ještě než ho otevřete. Chybějící řádka vám řekne, že se agent nedíval.

Revize vynucuje stejnou laťku, v pevném pořadí. Naše revizní dovednost má dva průchody s pevnými tagy a právě tagy dělají nálezy srovnatelné mezi recenzenty a mezi agenty.

Dva revizní průchody: průchod strukturou taguje diverge, duplicate, layer, misplaced, reach-in, inconsistent, own a chain; průchod škrty taguje delete, stdlib, native, yagni a shrink. Struktura jde první, protože konzistence vítězí nad škrty

Nejdřív struktura. Odchyluje se to od nejbližšího vzoru, duplikuje to existujícího pomocníka, přechází to vrstvu, sedí to na špatném místě, sahá to do vnitřností jiného modulu, používá to nekonzistentní názvy, přebírá to vlastnictví něčeho, co by mělo injektovat, nebo to láme kanonický volací řetěz? Pak škrty. Co by se dalo smazat, nahradit standardní knihovnou, nahradit nativní funkcí, zrušit jako spekulativní nebo zmenšit?

Pořadí je pravidlo, ne preference. Konzistence vítězí nad škrty. Škrt, po kterém kód dál vybočuje ze svých sourozenců, není výhra, protože čtenář se teď musí naučit dva tvary, aby ušetřil osm řádků. Pusť strukturu první a průchod škrty se dotkne jen kódu, který už odpovídá.

Co se stalo v repu, včetně části, která nefungovala

Od 1. srpna naše hlavní repo přijalo 1 333 commitů, většinu z nich napsaných agentem pod revizí. 386 z issue a pull requestů v tom období nese plánovací komentář, který pojmenovává vzor, jenž se má zrcadlit, celkem 491 takových komentářů. Kanonický řetěz je výchozí tvar nového endpointu a dvě legacy routy, které obcházejí kontroler, jsou v dokumentu uvedeny jako odchylka, která se ruší, ne jako druhý styl.

Těch 35 komentářů ponytail: je menší číslo, než jsem čekal, a myslím, že je to správně. Většina zjednodušení si komentář nezaslouží. Ta, která ano, jsou ta se stropem.

A teď část, kterou bych raději nehlásil. Vydal jsem grain a ponytail jako dovednost pro Claude Code: popis v indexu dovedností, tělo, které se načítá na vyžádání, a řádku v CLAUDE.md říkající, aby ji před psaním kódu vyvolal. Za pět týdnů přepisů ji agent vyvolal jménem nula krát. Závěrečná řádka mirrored: se objevila v sedmi odpovědích.

Pravidla přesto zapadla. Zapadla proto, že stejný text byl také na třech místech, která agent čte, aniž by si je volil: v projektovém CLAUDE.md, který je v kontextu od prvního tahu; v definicích worker agentů, které říkají „zrcadli tvar, umístění a pojmenování nejbližšího existujícího příkladu“ a „sáhni po nejlínějším řešení, které skutečně funguje“ jako pravidla, ne jako dovednost k zvážení; a v revizním checklistu, jehož tagy pošlou pull request, který je ignoruje, do kytek. Tělo dovednosti se stalo kanonickou referencí, na kterou ty tři ukazují. Jako spouštěč neudělalo nic.

Takže lekce, kterou bych předal každému, kdo balí konvence pro agenta, je tato. Dovednost je dobré místo pro dlouhou verzi pravidla. Je to špatné místo pro pravidlo samotné, protože agent se musí rozhodnout ji načíst a většinou to neudělá. Dejte jedno-odstavcovou verzi do souboru, který je vždy v kontextu, dejte imperativní verzi do definice agenta, který ten kód píše, a dejte tu kontrolu do revize, která hlídá merge. Pak na tu dovednost všechny tři namiřte kvůli detailu.

Veřejná verze na github.com/LivinTribunal/grain-skill je uspořádaná právě tak: krátký SKILL.md s pravidly, která agent udrží v hlavě, plný dokument s žebříkem a anti-vzory, revizní tagy a README, které říká, který odstavec patří do vašeho CLAUDE.md a který do definice vašeho implementátora. Nahraďte zpracovaný příklad svým vlastním kanonickým vzorem a je hotovo.

Často kladené otázky

Štefan je AI a softwarový inženýr, který vyvíjí FlowHunt. Kromě samotného produktu navrhuje agentic software-engineering workflows pro vývojáře, které snižují náklady na vývoj a zvyšují kvalitu kódu.

Štefan Moravík
Štefan Moravík
AI & Softwarový inženýr

Dejte svým agentům konvence, které váš tým už dodržuje

FlowHunt vám umožní připojit pravidla, příklady a kroky revize, které má agent dodržovat, ke každému workflow, takže jeho výstup odpovídá kódu, do kterého přistává.