Bugul nu era în cod.
Era în help.
Raportul spunea că voost tasks create nu poate trimite pachetul de dovezi și că agenții sunt forțați să folosească curl. Adevărul: flag-urile existau de o lună — dar singurul loc unde un agent putea afla de ele nu le pomenea. Pagina asta arată bugul real, fixul, și cele trei feluri diferite în care infrastructura a încercat apoi să oprească un fix de 10 rânduri.
Raportul: „CLI-ul nu are flag-urile”
Orice task creat de un agent (--created-by neo) trebuie să vină cu un pachet Agent Work Evidence: de ce există task-ul, ce rezultat se așteaptă, ce risc are, din ce a fost creat și pe ce țintește. Fără pachet, API-ul refuză cu 422. Așa arăta experiența oricărui agent:
validateAgentWorkEvidence din API verifică 6 lucruri pentru creatorii-agent: why, expected_outcome, created_from, risk_level, target_entity și cel puțin o dovadă-sursă. Lipsește oricare → 422 cu lista missing[]. Asta e regula corectă — problema era altundeva.Ambele butoane rulează aceeași comandă de bază — diferă doar flag-urile. Al doilea buton e spoiler-ul actului 2.
Verificarea: flag-urile existau. Din 11 iunie.
Primul pas nu a fost să scriu cod, ci să citesc ce există. Și surpriza: tot ce cerea raportul era deja implementat — commit 0a452459, 11 iunie 2026, mergeuit pe main și live în producție:
Handler-ul mapa deja --why, --expected-outcome, --risk, --created-from, --target în pachetul agent_work, iar --evidence/--link contau ca dovezi-sursă. Deci comanda „imposibilă” funcționa — ai văzut-o la butonul verde din actul 1.
Atunci de unde raportul? Dacă un feature există dar toți utilizatorii lui jură că nu există, problema nu e feature-ul.
Bugul real: singura ușă pe care intră agenții era nemarcată
Un agent nu citește codul sursă al CLI-ului. Un agent citește help-ul: voost tasks --help, registrul de comenzi, inventarul PP. Iar linia de create din registru arăta așa, înainte și după fix:
Singurul loc unde flag-urile apăreau înainte: mesajul de eroare afișat doar dacă uitai --title. Dacă titlul era pus corect, nu aflai niciodată.
De aici cercul vicios: agentul citește help-ul → nu vede flag-urile → conchide că CLI-ul nu poate → primește 422 → cade pe curl direct în API, exact ce interzice regula CLI-first. Raportul din care a pornit sesiunea era acest cerc, povestit ca bug de feature.
Încearcă regula pe viu. Mai jos e chiar validatorul din API, portat în pagină. Aprinde flag-urile și vezi exact ce răspunde POST /api/tasks:
--why → agent_work.why · --expected-outcome → agent_work.expected_outcome · --risk → agent_work.risk_level · --created-from/--target → referințe de entitate type:id · --evidence sau --link → dovezi-sursă (un doc:<id> e suficient). Lista missing[] de mai sus e calculată cu aceeași logică ca validateAgentWorkEvidence din src/lib/tasks/agent-work-evidence.ts.Pornește cu zero flag-uri — starea în care erau toți agenții. Aprinde-le pe rând și urmărește cum se golește missing[].
Fixul: fă ușa vizibilă și pune un lacăt pe drift
PR #1300 nu adaugă niciun feature — face vizibil ce exista și blochează repetarea:
- Help-ul din registru arată acum toată suprafața reală a lui
create, plus o notă „Agent Work Evidence” care explică 422-ul și interzice explicit fallback-ul pe curl. - Un test nou prin runner-ul public, cu trei pin-uri (al treilea a fost roșu înainte de fix — exact TDD): maparea flag →
agent_worke verificată contra validatorului API însuși, deci CLI-ul și API-ul nu mai pot devia separat; un create fără flag-uri rămâne respins; iar help-ul trebuie să conțină flag-urile — dacă cineva le scoate din help, pică testul, nu agenții. - docs/API.md + skill-ul voost-crm-sales aduse la zi — skill-ul chiar susținea, fals, că
--opportunitynu există.
De ce testul de help e partea importantă, nu linia de help
Linia de help se putea repara și cu un edit de 30 de secunde. Dar drift-ul dintre handler și help s-a produs deja o dată fără să-l observe nimeni timp de 34 de zile — pentru că nimic nu-l verifica. În repo-ul ăsta testele sunt singurul code review care există; un pin pe textul help-ului transformă „documentația e la zi” dintr-o speranță într-o proprietate garantată de CI.
Apoi CI-ul a picat de trei ori. Niciodată din cauza diff-ului.
Fixul era gata și verde local (toată suita, 4 lane-uri hermetice). Dar merge gate-ul e CI-ul — și mașina pe care rulează el avea alte planuri. Trei rulări, trei povești:
5a. Discul intern trăiește fix pe pragul FREEZE
Guard-ul de stocare al mașinii refuză orice lucru mare când discul intern are sub 30 GiB liberi. Discul avea… 29. Regula de mai jos e cea reală, din mac-mini-agent-storage-guard.sh — trage de slider:
code_sign_clone). Cât timp handle-urile sunt active, directorul nu poate fi curățat. Peste 30 GiB liberi asta e doar un WARNING; sub 30 devine FREEZE — curățarea e urgentă dar blocată. În sesiune am recuperat ~900 MB din cache-uri regenerabile (updater-ul Claude Desktop, node-gyp, un profil Chrome de automatizare rămas de la un ticket vechi) ca să trecem pragul.5b. Două suite, același director, același token
A doua rulare a trecut de guard… și a picat un singur test, cu o eroare stranie: refusing symlink ancestor '/private/tmp/voost-control-tests/0123…'. Cauza: eu rulam suita completă local în același minut în care CI-ul rula pe aceeași mașină — iar testul cu pricina scrie într-un director partajat, cu un token hardcodat:
Ambele suite scriu în /private/tmp/voost-control-tests/<token>. Cu tokenul fix 0123456789abcdef… scriu în același loc. Fixul (chip separat): token aleator per rulare.
5c. CI-ul își sabota singur preflight-ul
A treia rulare a picat iar pe FREEZE — la două minute după ce discul arăta 30,1 GiB liberi. Căutând cine consumă marja, am găsit vinovatul structural: runner-ul CI însuși stă pe discul intern (~/.voost-ci-runner, ~2,2 GiB în _work). Fiecare rulare consumă exact marja pe care propriul preflight o cere:
_work pe SSD-ul extern cu symlink, după modelul deja folosit pentru go-build și Homebrew — e infrastructură partajată, deci am lăsat-o ca chip cu comenzile gata scrise, la aprobarea ta. Rularea finală a trecut pentru că macOS a eliberat singur spațiu „purgeable” (34,8 GiB liberi), iar un monitor aștepta exact momentul ăla ca să redea drumul CI-ului.Bonus: pista falsă de 274 GB
În timpul investigației, du raporta 274 GB în /private/tmp — aparent dezastrul secolului pe un disc cu 29 GiB liberi. În realitate: o altă sesiune montase un share SMB de pe NAS (nas-laura, transferul pozelor) chiar sub scratchpad-ul ei, iar du număra conținutul NAS-ului ca și cum ar fi local. Lecția: înainte de orice concluzie despre disc, mount.
Unde am rămas
PR #1300 — verde pe CI, MERGEABLE
Suita completă verde local și în CI. Merge-ul e decizia ta; după merge: pnpm dev:teardown cli-task-evidence.
Ce mai așteaptă la tine — două chips din sesiune:
- Mutarea
_workal runner-ului CI pe SSD — fixul durabil pentru 5a + 5c, cu comenzile pregătite; infrastructură partajată, cere aprobarea ta. - Token aleator în testele supervisor-ului — fixul pentru 5b, ca CI-ul și o rulare locală să nu se mai calce.
Morala, pe criteriile tale. Regula CLI-first funcționează doar dacă help-ul e contractul complet: pentru un agent, un flag nedocumentat nu există, oricât de corect ar fi implementat. Iar într-un repo unde nimeni nu citește codul, singura documentație care nu minte e cea pe care o pică un test.
Cronologia serii, cu minutele reale
cli-task-evidence; testul nou e roșu exact pe pin-ul de help/private/tmp cu suita mea locală