Cod cu fibra: Abilitatea care oprește agenții de cod AI să-ți umfle repo-ul

Publicat la Sep 22, 2026 de Štefan Moravík.
AI Agents Claude Code Developer Tools Code Quality
Pe această pagină 9 min citire
  1. 1Versiunea scurtă
  2. 2Eșecul pentru care există
  3. 3Fibra
  4. 4Ponytail
  5. 5Cum faci un agent să facă asta cu adevărat
  6. 6Ce s-a întâmplat în repo, inclusiv partea care nu a funcționat

Versiunea scurtă

  • Agenții de cod AI sunt buni la a adăuga cod și slabi la a observa că respectivul cod există deja. Lăsați singuri, îi dau unui repo un al doilea helper pentru fiecare primul, iar fiecare cititor trebuie să verifice ambele ca să aibă încredere în vreunul.
  • Soluția sunt două obiceiuri scrise negru pe alb, ca să le respecte un agent. Fibra: găsește cel mai apropiat exemplu existent de același tip de lucru și oglindește-i forma, locul și numele. Ponytail: urcă o scară de la „chiar trebuie să existe asta” până la „minimul care funcționează” și oprește-te la prima treaptă care ține.
  • Ambele sunt acum o abilitate publică, licențiată MIT: grain-skill pe GitHub . Conține regulile de autorat, o trecere de revizuire cu etichete fixe și forma rândului de încheiere care face agentul să spună ce a oglindit.
  • În repo-ul nostru principal, din august: 386 de issue-uri și pull request-uri poartă un comentariu de plan care numește exemplarul de oglindit, pe parcursul a 1.333 de commit-uri într-un arbore de aproximativ 18.000 de fișiere. 35 de locuri din cod poartă un comentariu ponytail: care numește o simplificare deliberată și calea ei de upgrade.
  • Constatarea inconfortabilă: agentul a invocat abilitatea ca abilitate de zero ori. Regulile au funcționat pentru că le-am pus și în CLAUDE.md al proiectului, în definițiile agenților-worker și în checklist-ul de revizuire. Pune regula acolo unde agentul citește deja.

Am scris despre asta o dată înainte și nu mi-a plăcut rezultatul, pentru că descria principiul, nu mecanismul. Aceasta este versiunea cu mecanism.

Eșecul pentru care există

Trei exemple din backend-ul nostru, toate reale, toate din pull request-uri scrise de agent care au trecut testele.

O rută REST nouă care lua serviciul aplicației direct dintr-o fabrică. Fiecare rută soră din repo merge rută → controller → serviciu → repository, fiecare hop injectat. Aceasta a colapsat lanțul la rută → serviciu. A funcționat. A însemnat și că singurul loc unde trăiesc validarea cererii și modelarea răspunsului pentru acea funcționalitate nu exista, iar următorul agent care atinge funcționalitatea a avut două forme din care să aleagă.

Un serviciu supradimensionat împărțit în _payloads.py, _protocol.py și _tokens.py, fiecare un modul de async def-uri simple, cu serviciul apelând protocol.discover(...) prin import. Verificarea lungimii fișierului a trecut pe verde. Exact lucrul pe care injecția de dependențe există ca să-l prevină a revenit imediat: un colaborator ales la momentul importului, neînlocuibil fără a petici modulul, absent din constructorul care documentează de ce depinde serviciul.

Un al doilea normalizator. Există deja unul. Agentul nu l-a găsit, a scris încă opt rânduri care făceau aproape același lucru, iar acum două funcții cu comportament diferit pe cazuri-limită pretind amândouă că canonicalizează același câmp.

Niciuna dintre acestea nu este un bug în sensul pe care îl prinde un test. Fiecare este un agent care alege codul pe care îl putea genera în locul codului care exista. Fă asta pe câteva sute de pull request-uri și repo-ul nu mai are o formă.

Logo FlowHunt

Pregătit să îți dezvolți afacerea?

Începe perioada de probă gratuită astăzi și vezi rezultate în câteva zile.

Fibra

Codul nou ar trebui să fie indistinguibil de codul din jur. Abilitatea transformă acea propoziție în cinci pași pe care agentul îi face înainte de a scrie.

Găsește întâi exemplarul. Pentru un endpoint, serviciu, repository, query hook, slice de store sau componentă, localizează cel mai apropiat exemplu existent și copiază-i forma, locul fișierului și denumirea. Nu inventa structură când există un tipar. În repo-ul nostru, slice-ul branding este exemplarul numit pentru backend, pentru că e mic, curat și acoperă fiecare strat, iar tabelul abilității spune, pentru fiecare tip de artefact, ce fișier să deschizi și ce să oglindești.

Respectă straturile. Prezentarea apelează aplicația, aplicația apelează domeniul, iar infrastructura implementează domeniul. Un import nou peste aceste linii pică verificarea de arhitectură. Citește documentul straturilor, nu-l re-deriva.

Fibra locală versus fibra repo-ului. Potrivește-te cu frații tăi. Dar dacă frații înșiși diverg de la lanțul canonic, urmează lanțul canonic și notează divergența în loc să copiezi deriva. „Se potrivește cu celelalte cinci rute din acest fișier” nu e suficient dacă toate șase sar un strat pe care slice-ul canonic îl folosește. Ai comparat prea local.

Refolosește helperul canonic. Dacă un helper, tip sau tipar există deja în repo, folosește-l. Doi normalizatori obligă fiecare cititor să le diffeze pe amândouă.

Numele poartă sens. Refolosește vocabularul care există deja în cod. Un nume nou pentru un concept existent este un bug care așteaptă un cititor.

Testul de la final este cel pe care îl dau recenzenților: dacă poți spune doar după forma lor ce rânduri a scris agentul, a luptat împotriva fibrei.

Ponytail

Leneș înseamnă eficient, nu neglijent. Cel mai bun cod este codul care nu a fost scris niciodată. Înțelege mai întâi pe deplin problema, fiecare fișier pe care îl atinge schimbarea, fluxul real end-to-end, apoi urcă această scară și oprește-te la prima treaptă care ține.

Scara lenei: trebuie să existe, există deja în cod, biblioteca standard, platforma nativă, dependență instalată, un rând, și abia apoi minimul de cod care funcționează

Două reguli merg împreună cu scara.

O remediere de bug este o cauză-rădăcină, nu un simptom. Un ticket numește un simptom. Fă grep pe fiecare apelant al funcției pe care ești pe punctul de a o atinge. O gardă în funcția partajată este un diff mai mic decât o gardă în fiecare apelant, iar peticirea doar a căii pe care o numește ticketul lasă apelanții-frați stricați.

Păstrează diff-ul minim. Diff-ul este ceea ce citește revizuirea. Fără refactorizări din trecere, fără redenumiri, fără reformatări, fără îmbunătățiri „cât timp sunt aici”, fără atingerea fișierelor pe care sarcina nu le-a cerut. Fiecare dintre ele îngroapă schimbarea reală. Dacă diff-ul e mai mare decât problema, taie-l înapoi. Îmbunătățirile pe care le-ai observat sunt un issue de continuare, nu commit-uri în plus pe această ramură.

Și limitele, pentru că „leneș” este cuvântul cel mai probabil să fie citit greșit. Nu simplifica niciodată eliminând validarea inputului la o graniță de încredere, tratarea erorilor care previne pierderea de date, securitatea, elementele de bază ale accesibilității sau orice a cerut utilizatorul. Nu fi niciodată leneș în înțelegere: scara scurtează soluția, niciodată lectura. Un diff mic în locul greșit este un al doilea bug. Logica netrivială lasă în urmă o verificare rulabilă, cel mai mic lucru care pică dacă logica se strică.

Când agentul ia o treaptă intenționat și îi cunoaște plafonul, spune asta în cod:

# ponytail: lacăt global, lacăte per cont dacă debitul contează

Acum sunt 35 dintre acestea în repo-ul nostru. Fiecare este o decizie pe care altfel un cititor viitor ar trebui să o reconstruiască și un marker pentru unde să te uiți când plafonul este atins.

Cum faci un agent să facă asta cu adevărat

Un principiu într-un document nu este un comportament. Trei mecanisme le-au transformat pe acestea două în comportamente și le-aș ordona în această ordine.

Brief-ul poartă exemplarul. În tiparul arhitect modelul principal scrie un brief pentru un worker, iar câmpul Design al brief-ului numește cel mai apropiat tipar existent de oglindit și schimbarea minimă de cauză-rădăcină. Workerul nu trebuie să găsească fibra. I se înmânează. În tot repo-ul nostru, asta sunt cele 491 de comentarii de plan de pe 386 de issue-uri și pull request-uri: un pas de planificare care numește ce trebuie oglindit înainte ca cineva să scrie.

Agentul încheie cu ce a oglindit. Raportul fiecărui implementator se termină cu un rând, mirrored: <path>, sau numește noul tipar și de ce nu s-a potrivit niciun frate. Este ieftin și este cel mai bun ajutor de revizuire pe care l-am adăugat. Un rând mirrored: care arată spre tipul greșit de fișier îți spune că diff-ul e greșit înainte să-l deschizi. Unul lipsă îți spune că agentul nu s-a uitat.

Revizuirea impune aceeași ștachetă, într-o ordine fixă. Abilitatea noastră de revizuire are două treceri cu etichete fixe, iar etichetele sunt ceea ce face constatările comparabile între recenzenți și între agenți.

Două treceri de revizuire: trecerea de structură etichetează diverge, duplicate, layer, misplaced, reach-in, inconsistent, own și chain; trecerea de tăieri etichetează delete, stdlib, native, yagni și shrink. Structura rulează prima pentru că consistența bate tăierile

Structura prima. Divergă asta de la cel mai apropiat exemplar, duplică un helper existent, traversează un strat, stă în locul greșit, ajunge în interiorul altui modul, folosește nume inconsistente, își asumă proprietatea asupra a ceva ce ar trebui injectat sau rupe lanțul canonic de apeluri? Apoi tăierile. Ce ar putea fi șters, înlocuit cu biblioteca standard, înlocuit cu o funcționalitate nativă, eliminat ca speculativ sau micșorat?

Ordinea este o regulă, nu o preferință. Consistența bate tăierile. O tăiere care lasă codul să divergă de frații lui nu este o victorie, pentru că cititorul trebuie acum să învețe două forme ca să economisească opt rânduri. Rulează structura prima și trecerea de tăieri atinge doar cod care se potrivește deja.

Ce s-a întâmplat în repo, inclusiv partea care nu a funcționat

Din 1 august, repo-ul nostru principal a primit 1.333 de commit-uri, majoritatea scrise de agent sub revizuire. 386 dintre issue-urile și pull request-urile din acea perioadă poartă un comentariu de plan care numește exemplarul de oglindit, 491 de astfel de comentarii în total. Lanțul canonic este forma implicită a unui endpoint nou, iar cele două rute legacy care sar controllerul sunt listate în document ca o divergență în curs de retragere, nu ca un al doilea stil.

Cele 35 de comentarii ponytail: sunt un număr mai mic decât mă așteptam și cred că este corect. Majoritatea simplificărilor nu merită un comentariu. Cele care merită sunt cele cu un plafon.

Acum partea pe care aș prefera să nu o raportez. Am livrat grain și ponytail ca abilitate Claude Code: o descriere în indexul de abilități, un corp care se încarcă la cerere și un rând în CLAUDE.md care spune să o invoce înainte de a scrie cod. Pe parcursul a cinci săptămâni de transcrieri, agentul a invocat-o pe nume de zero ori. Rândul de încheiere mirrored: a apărut în șapte răspunsuri.

Regulile au ajuns totuși la destinație. Au ajuns pentru că același text era și în trei locuri pe care agentul le citește fără să aleagă: CLAUDE.md al proiectului, care este în context din prima rundă; definițiile agenților-worker, care spun „oglindește forma, locul și denumirea celui mai apropiat exemplu existent” și „ajunge la cea mai leneșă soluție care chiar funcționează” ca reguli, nu ca o abilitate de luat în considerare; și checklist-ul de revizuire, ale cărui etichete fac să pice un pull request care le ignoră. Corpul abilității a devenit referința canonică spre care arată aceste trei. Ca trigger, nu a făcut nimic.

Deci lecția pe care aș transmite-o oricui împachetează convenții pentru un agent este aceasta. O abilitate este un loc bun pentru versiunea lungă a unei reguli. Este un loc prost pentru regula în sine, pentru că agentul trebuie să decidă să o încarce și, de cele mai multe ori, nu o va face. Pune versiunea de un paragraf în fișierul care este mereu în context, pune versiunea imperativă în definiția agentului care scrie codul și pune verificarea în revizuirea care blochează merge-ul. Apoi îndreaptă-le pe toate trei spre abilitate pentru detaliu.

Versiunea publică la github.com/LivinTribunal/grain-skill este structurată astfel: un SKILL.md scurt cu regulile pe care un agent le poate ține în cap, documentul complet cu scara și anti-tiparele, etichetele de revizuire și un README care spune ce paragraf merge în CLAUDE.md al tău și care merge în definiția implementatorului tău. Înlocuiește exemplul lucrat cu propriul tău exemplar canonic și gata.

Întrebări frecvente

Ștefan este un inginer AI și software care construiește FlowHunt. Dincolo de produs, el proiectează fluxuri de lucru agentic de inginerie software pentru dezvoltatori care reduc costurile de dezvoltare și ridică calitatea codului.

Štefan Moravík
Štefan Moravík
Inginer AI și Software

Dă agenților tăi convențiile pe care echipa ta le respectă deja

FlowHunt îți permite să atașezi regulile, exemplele și pașii de revizuire pe care un agent ar trebui să-i urmeze la fiecare flux de lucru, astfel încât rezultatul lui să se potrivească cu codul în care ajunge.