Comutarea pe codebase-ul unificat a reușit: app.elevarts.com răspunde normal, iar clienții nu simt nimic. Dar munca din git nu mai are pe unde ajunge în producție, iar o parte din automatizări s-au oprit. Mai jos: ce e rupt exact și cei 4 pași care repun totul.
Toți lucram pe același drum: George, Andreea, Florin, agenții. Astăzi drumul se termină la pasul 5.
ia-worktree <temă>
worktree propriu + branch din main
editezi doar acolo; guardrails blochează clona-sursă
ia-ship "mesaj"
rebase pe main + tsc + build + gate vizual
push → CI (lint, tsc, teste, build) → squash-merge automat
auto-deploy la 120s → build → rsync → restart → smoke test
merge neschimbat
merge neschimbat
merge neschimbat
merge. Azi a trecut PR #597, CI verde, 20 de commituri în main
tăiat. Deploy-ul construiește și copiază în ~/.local/share/internal-app, director care nu mai servește nimic, apoi încearcă să pornească com.elevarts.brain-app, serviciu care nu mai există.
main e corect, verificat — și inert
De ce contează: producția e acum /Users/Shared/elevarts-next, care nu e repo git. Codul nostru e acolo: fișierele coincid la bit. Dar copiat, nu tras. Deci nu există cale înapoi: nici PR, nici review, nici rollback pe o singură modificare. INAPOI.sh întoarce tot app-ul la varianta veche, nu o schimbare anume.
Practic, azi un coleg nu poate livra o funcționalitate: ori scrie cod care rămâne în main, ori editează direct în producție fără plasă.
Portul 3002 a rămas același, de-aia majoritatea n-a simțit nimic.
Baza elevarts a rămas live ca PG_BRAIN_URL. Payroll, sync catalog, briefuri, memoria, task runner, health-monitor: neatinse. Asta e majoritatea.
Scoase din PUBLIC în proxy.ts. Middleware-ul respinge înainte ca ruta să apuce să-și verifice x-cron-token.
Moarte azi: draft-uri CRM (crm-autodraft, pipeline-heal, reply-triage, auto-read), ingestia de taskuri, notificarea de mail nou din n8n.
Orbi: captura de erori /api/ops/client-error și telemetria de cron. Nu mai vedem erorile aplicației.
/api/parse-report dă 401, dar nu doare încă: în ultimele 14 zile 137 din 137 de rapoarte au intrat prin interfața web, zero prin Telegram. Florin a înregistrat ore și după comutare. Se repară odată cu restul.
internal-app-deploy (construia și copia în gol la fiecare 120s) și request-deploy (chema deploy-ul și aplica migrări pe producție autonom, pe baza veche). Ambele scrise în ops.changes, cu comanda de repornire.
În ordine. Primul deblochează tot restul.
/Users/Shared/elevarts-next să primească git init, remote pe 1elevarts/internal-app, un commit cu starea curentă, apoi merge cu main.
Fără pasul ăsta nimic din ce lucrăm nu mai ajunge la clienți, iar producția rămâne fără istoric și fără rollback pe modificare. În main te așteaptă deja 20 de commituri verificate: portalul de agenție, licențierea fonturilor, opțiunile de catalog.
Decizia de a scoate rutele de automatizare din PUBLIC e corectă: la noi erau apelabile fără sesiune. Dar cronurile trimit x-cron-token, nu cookie, iar middleware-ul le taie înainte. Cinci linii în proxy.ts, după verificarea PUBLIC și înainte de cerința de JWT, repun toată coloana:
// automatizări interne:
// token, nu sesiune
const t = req.headers
.get("x-cron-token");
if (t && t === process.env.CRON_TOKEN) {
return NextResponse.next();
}
.env.local are 54 de chei și ACL group:staff allow read, deci orice cont local le citește. Folderul e drwxrwxr-x dorin:staff, iar din staff fac parte elevartsbrain, florin și root: trei conturi pot rescrie codul live fără nicio urmă.
Recomandat: chmod 600 .env.local și chmod 750 pe folder.
Contul lui George a rămas cu o parolă trivială, trimisă și pe WhatsApp. De schimbat înainte ca aplicația să fie considerată deschisă pentru echipă.
Un lucru mărunt care ne-ar scuti o alarmă: comutarea a fost făcută manual, deci n-a lăsat urmă în ops.changes. Monitorizarea a văzut „un proces necunoscut a luat portul 3002" și era gata să propună repornirea aplicației vechi, adică anularea migrării. A oprit-o o poartă care escaladează orice atinge cuvântul „producție". Am completat noi intrarea acum; data viitoare, o linie în ops.changes înainte de comutare scutește exact alarma asta.
Ce e bine scris: COMUTARE.sh și INAPOI.sh verifică, nu șterg nimic și au drum de întoarcere. Iar comentariul din proxy.ts care explică de ce s-au închis rutele ne-a economisit o oră de diagnostic: era exact cauza.
Contextul de care ai nevoie ca să judeci. Nu e o listă de features, e ce se întâmplă singur pe mașină în fiecare noapte.
Un singur Postgres, 18 scheme, ~340 de tabele. app (business, oferte, taskuri), crm, core.events ca spinare a evenimentelor, dashboards (pontaj și salarii), ops (incidente, schimbări, rulări de cron), memory+mnemo (memorie semantică cu pgvector), digests.
Regula de bază: orice decizie sau eșec ajunge într-un tabel, nu doar în log. De-aia se poate audita dimineața ce a făcut mașina noaptea.
130 de joburi launchd (124 încărcate acum), 137 de scripturi în ~/.local/bin, 9 workflowuri n8n active. Cadențele merg de la 60s (scoring urgență CRM, notificări) prin 5 min (health monitor, secret reaper) până la ferestre nocturne.
n8n e ținut strict ca magistrală de evenimente: primește și rutează. Logica de business stă în aplicație și în scripturi, nu în noduri.
nightly-autopilot (00:00–05:00): ia toate taskurile pending eligibile din app.tasks și rulează Claude pe fiecare, secvențial, până se termină coada sau se face 05:00. Filtrul de eligibilitate exclude explicit infra/bug de prioritate 1, deci criticele nu se ating fără om. Raportul de dimineață se injectează în prima sesiune.
opus-autonomous-agent (01:30): agentul de auto-vindecare. Citește semnalele deschise: incidente, servicii nesănătoase, taskuri blocate, skilluri propuse de memorie, decide cu Opus și creează taskuri de îmbunătățire sau respinge zgomotul. Jurnal în ops.autonomous_decisions.
ralph loop: auto-corecție în stil TDD. Îi dai un task și o comandă care iese 0 la PASS; iterează maxim N ori, rescriind până trece criteriul. Folosit pe taskuri care au eșuat o dată.
advisor pe blocaje: când un task se blochează cerând input, un Opus cu unelte read-only răspunde în locul lui George. Instrucțiunea centrală e „verifică premisa înainte să răspunzi la ea". Foarte des întrebarea e prost pusă. Trece prin două porți; alarma din capul paginii a fost oprită exact de prima.
Observațiile din sesiune → buffer SQLite → Postgres (flush la 10 min). La 03:30 un LLM local le comprimă, nomic-embed-text le vectorizează, notele ajung în vault. Recall hibrid vector + full-text. 520 de fișiere de memorie acum.
Costul e ținut jos deliberat: volumul de clasificare rulează pe modele locale (0 lei), iar Claude real se folosește doar unde output-ul e proză pe care o citește un om. Acolo o cifră greșită arată exact ca una corectă.
Când George corectează ceva, corecția se distilează într-o regulă cu „de ce". 125 de reguli acum. Livrarea e deterministă, nu semantică: un hook injectează la fiecare prompt doar regulile al căror domeniu și cuvinte-cheie se potrivesc, iar altul le verifică la scriere de fișier.
Am încercat întâi căutare vectorială și am măsurat-o: scor mediu 0,0195 pe top-hit, sub pragul de zgomot. O regulă trebuie să se aplice de fiecare dată, nu probabilistic. De-aia potrivirea e pe cuvinte exacte.
Stratul care ține agenții în frâu: guardrails care blochează editarea directă a clonei de deploy și tiparele distructive (rm -rf, git branch -D, force-push), leak-check pe diff, CI într-un singur job, gate vizual la 390px, rebase obligatoriu în ia-ship, și poarta de escaladare de la advisor.
Au fost scrise una câte una, după câte un incident. Fiecare are un „de ce" datat.
Telegram pentru ce cere decizie (deliberat nu pentru progres, fiindcă notificările de pas ascund alertele reale), digest de dimineață, și Request Desk: o cerere formală intră prin /cere, un worker o lucrează autonom, iar livrarea trecea prin poller-ul de deploy, pasul care e oprit acum.
Scanată din sistem acum, nu desenată din memorie: fiecare plist citit, scriptul lui deschis, dependențele detectate din cod. 130 de joburi, 124 încărcate.
Câte din cele 130 ating fiecare resursă. Cifra care contează: aplicația e o dependență mică. Coloana vertebrală e altundeva.
Singurele afectate direct de comutare. Unul e rupt, restul merg pentru că portul a rămas 3002.
crm-autodraft30 min · 401telegram-notify-assignments1 minhealth-monitor5 mintelegram-listenerpermanentelebot-telegrampermanentcatalog-thumbs-warm02:40ui-uniformity-nightly03:15daily-brief-generate06:30request-digest08:00telegram-daily-reminder09:00dorin-activity-watch10:00daily-team-audit20:30Cele 80 de joburi cu oră fixă, pe 24 de ore. 44 dintre ele între 22:00 și 06:00.
Două lucruri de citit aici. Între 11:00 și 19:00 nu rulează nimic programat — ziua de lucru e liberă, deliberat. Și vârful de dimineață cade exact când George deschide laptopul: 13 joburi la 09:00, plus 8 la 06:00.
crm-autodraft, pe rută 401brain-app plus cele 3 de whisper, care pornesc pe programScanerul a dat inițial 2 rupte. Al doilea, iboga-waitlist-drain, lovea /api/health — dar pe portul 3015, dashboardul iboga, nu aplicația. Condiția corectă e „rută închisă și gazda aplicației", altfel orice serviciu cu un endpoint de health apare victimă a unei migrări care nu l-a atins.
Ce am aflat construind harta: aplicația se simțea ca centrul lumii, dar 12 din 130 o ating. Riscul real e concentrat în Docker și Postgres — 86 și 84 de joburi. Docker a picat o dată 9 ore printr-un blocaj de kernel, iar singura ieșire a fost repornirea mașinii; atunci n-a mers nimic din cele 86. Și Postgres e tocmai piesa pe care ai schimbat-o azi.
A doua descoperire: 5 scripturi din repo lovesc rute acum închise, dar nimic nu le programează — nici launchd, nici n8n. crm-pipeline-heal, crm-reply-triage, crm-auto-read, task-extractor, classify-replies. Ori sunt chemate de undeva ce n-am găsit, ori sunt cod mort care arată viu. Le-am lăsat neatinse până se lămurește care.
Ce e bun, ce e de îmbunătățit, ce e inutil, din punctul tău de vedere, ca profesionist. Începem cu slăbiciunile pe care le știm deja, ca să nu-ți pierzi timpul găsindu-le.
Când producția s-a mutat azi, am descoperit ce s-a rupt bucată cu bucată, prin sondare, nu dintr-un manifest care spune „jobul X depinde de serviciul Y și de ruta Z". La 124 de joburi, asta nu scalează.
Merită un registru de dependențe, sau e semn că sunt prea multe joburi?Găsit azi, cât pregăteam pagina: scriptul inserează în ops.changes coloane care nu există (kind, summary, details). Eroarea e înghițită de 2>/dev/null. Zero rânduri, de la început. Bucla rulează, urma e goală.
ops.changes se scrie, dar aproape nimeni nu-l citește
Două scripturi din 137 îl consultă. De-aia comutarea de azi a arătat ca „un proces necunoscut a luat portul": datele existau pe jumătate, dar nu în calea de decizie.
Un jurnal de schimbări pe care nu-l citește nimeni înainte să acționeze e o mincinoasă liniște. Cum l-ai lega?Porțile sunt locale plus CI. Nu există un loc unde aplicația să ruleze cu date reale înainte de producție. Probabil cea mai vizibilă gaură după migrarea de azi.
Merită un staging pe aceeași mașină, sau e mai ieftin să întărim rollback-ul?Toate buclele (autopilot, agent autonom, ralph, advisor, digesturi) consumă din același abonament, fără planificator global sau priorități. Când se termină fereastra, se opresc în ordine arbitrară.
Cum ai pune priorități peste ceva ce nu se poate măsura decât după consum?Regula există: o constatare care se repetă zilnic până iese din fereastra de timp nu mai e semnal. Dar e aplicată neuniform.
Merită impus ca ack-ul să fie obligatoriu la orice check nou?George nu scrie cod. Tot ce e mai sus a fost construit ca să compenseze asta: agenții scriu, porțile îi opresc, memoria le ține contextul, regulile le impun stilul.
Din experiența ta: unde e complexitatea justificată și unde ne-am construit un aparat mai mare decât problema?Cum ne răspunzi: pe WhatsApp, în cuvintele tale. N-avem nevoie de document. Dacă e ceva ce ai vrea să vezi în detaliu (un script, o schemă, un jurnal), zi-ne și îl deschidem pe masă.