Grupul "Maria Test", 2026-09-01, ambele mesaje au primit raspunsuri inventate:
- "am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE"
(cosinus 0,768) — mesajul nu spune CE eroare e; Maria a explicat cum se reface
o factura stearsa si a trimis omul sa verifice drepturile pe CIF in SPV.
- captura cu `<Error errorMessage="CUI cumparator incorect"/>` (0,778) — eroarea
nu exista in niciun document; Maria a inventat o procedura cu "ID de descarcare",
Borderou eFactura si retrimitere manuala.
Cauza: `RANK_STRONG_COSINE=0,70` inseamna "cosinusul singur e destul", dar toate
chunk-urile eFactura seamana intre ele — la 0,7x cosinusul nu mai distinge "e in
documente" de "e despre eFactura". Aceeasi greseala ca la codurile Oracle
(ORA-06550 vs ORA-12541, b7d75f0), doar ca fara cod pe care sa te sprijini.
- RANK_STRONG_COSINE 0,70 -> 0,80: peste atat trec doar potrivirile aproape
textuale, restul cad pe dovada lexicala (termenii rari ai intrebarii chiar in
chunk-uri). Ambele mesaje escaladeaza acum la suport.
- LLM_TEMPERATURE=0 la apelul modelului. Fara el llama.cpp foloseste 0,8 —
creativitate exact acolo unde vrem doar ce scrie in context.
- Cele doua cazuri reale sunt adaugate in ops/calibrate-rank.py: 25/25.
Teste: 99 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Puntea a tacut ~7 ore fara ca nimic sa semnaleze: discord.py memoreaza
`resume_gateway_url` primit la ultimul READY si il refoloseste la fiecare
reconectare. Cand gatewayul regional (gateway-us-east-1a) a inceput sa dea
503 la handshake, botul a reincercat la infinit acelasi host mort, cu backoff
pana la ~15 minute. Procesul era viu, deci nici systemd nici dashboardul nu
aveau ce vedea, iar `gateway.discord.gg` raspundea normal tot timpul.
GatewayWatchdog numara de cat timp e legatura jos (on_connect / on_resumed /
on_ready o ridica, on_disconnect o coboara, iar reincercarile esuate nu
reseteaza ceasul). Peste `GATEWAY_WATCHDOG_S` (implicit 300s) alerteaza, iese
cu codul 3 si lasa systemd sa reporneasca — restartul e singurul lucru care
forteaza un IDENTIFY nou pe gateway.discord.gg. Un prag <= 0 il dezactiveaza.
O iesire la 300s ramane sub StartLimitBurst=5/5min, deci bucla de restart nu
poate ajunge sa lase unitul `failed`. `_hard_exit_after` acopera cazul in care
`close()` se blocheaza pe socketul mort.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Din firul testat in grup: la "Urgent este", "Nu m-a contactat nimeni. Cat mai
dureaza?" si "Tot nimic", Maria trimitea de fiecare data "Caut informatia, revin
imediat...", calcula un embedding pe care il arunca, si raspundea acelasi sablon
("am adaugat si asta la M-..."). La intrebarea "cat mai dureaza" nu raspundea deloc,
iar "urgent" nu schimba nimic nicaieri.
- Completarea se decide INAINTEA confirmarii si a cautarii: raspunsul vine instant,
deci nici promisiune, nici embedding degeaba (~1-2s per mesaj).
- triaj.fel_completare: urgenta / stare / detaliu.
- urgenta -> escaladarea se marcheaza si suportul primeste un mesaj cu URGENT;
- stare -> raspuns cu ce stim (de cat timp asteapta) si reamintire catre echipa,
cel mult una la 15 minute, ca sa nu sune telefonul la fiecare "tot nimic";
- detaliu -> confirmare scurta, cu formularea rotita.
- Peste 30 de minute fara raspuns Maria o spune pe fata si indruma spre telefon
(SUPPORT_PHONE), in loc sa repete ca "echipa vede detaliul".
- Gramatica: "acum 10 minute", nu "acum 10 de minute".
Teste: 99 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Fiecare mesaj pornea de la zero. Masurat pe indexul viu, dupa o escaladare pentru
ORA-06550: "da, ma blocheaza complet" primea raspuns despre ordinul de plata la
Trezorerie (cosinus 0,642, "acoperit"), "eram la salvarea unei facturi" despre
corectia unei eFacturi (0,742), iar "am incercat si tot nu merge" cerea din nou
detaliile tocmai date. Patru cuvinte fara context seamana cu ceva din documente,
iar cautarea nu avea de unde sti ca sunt raspunsul la intrebarea Mariei.
rag/fir.py tine ancora (textul erorii), codurile ei, referinta escaladarii si
ultimele 6 schimburi, in ~/.maria-bridge/conversations/ (expira, ca si capturile).
La o continuare: cautarea e pe ancora + mesajul nou, modelul primeste istoricul,
mesajul se adauga la escaladarea deschisa de cate ori e nevoie, iar "mesaj prea
vag" nu se mai aplica. Escaladarea duce firul intreg la suport, nu un mesaj rupt
din context.
Firul se rupe doar la o captura noua sau un cod de eroare diferit — schimbarea
subiectului in cuvinte e prea usor de confundat cu o continuare.
In plus:
- Maria tace 60 min cand preia un om, dar automat doar in grupuri cu >= 2
participanti: in self-chat totul e `fromMe`. Comenzi explicite oriunde
("Maria, stop" / "Maria, continua").
- ALLOWED_GROUP_JIDS: testarea se muta in grupul "Maria Test"
(120363409761730101@g.us), ca sa nu mai poluam chatul "Eu". Filtru si in punte,
si in consumer. NU echo-test: puntea lui Echo nu filtreaza fromMe in grupuri,
deci cei doi boti ar intra in bucla.
Teste: 94 pass. Calibrare 23/23, cu doua cazuri de continuare.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Pe captura cu ORA-06550, avand in context chiar intrarea din dictionar care spune
"nu se rezolva din aplicatie", modelul i-a raspuns unui contabil sa verifice
schema de date si sa foloseasca "procedura alternativa MI_pack_parteneri_old" —
inventata. Si nu escalada, fiindca escaladarea se declansa doar cand NU exista
acoperire in documente, iar aici exista.
rag/triaj.py, trei decizii inaintea modelului:
- **Mesaj prea vag.** "Am o eroare" -> Maria cere operatiunea, ecranul si textul
erorii, in loc sa ghiceasca. Regula e ingusta: text scurt, fara captura, fara cod.
- **Erori marcate "Intotdeauna"/"Imediat" in dictionar** (9 din 29) -> raspunsul se
compune din campurile dictionarului, fara model, si pleaca automat la suport cu
referinta si captura. Textul de acolo e scris pentru clienti; parafrazarea lui nu
adauga nimic si poate strica tot.
- **Urgenta se intreaba, nu se deduce.** Raspunsul ("ma blocheaza" / "pot continua")
se ataseaza escaladarii deschise, nu deschide alta referinta. Fereastra 30 min.
In plus: SYSTEM_PROMPT interzice explicit vocabularul tehnic (procedura, schema,
tabela, PL/SQL, compilare, sesiune) si propunerile de verificat baza de date; cele
mai tehnice cinci intrari din dictionar rescrise in limbaj de client.
Teste: 85 pass. Calibrare 21/21. Reindexare: 5 embeddings noi, 164 refolosite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
O captura cu ORA-06550 / PLS-00906 primea raspunsul despre ORA-12541 (TNS no
listener) si nu pleca la suport. Cauza: doua erori Oracle diferite se scriu
aproape la fel ("[Oracle][ODBC][Ora]ORA-..."), asa ca chunk-ul gresit a iesit la
cosinus 0,736 — peste RANK_STRONG_COSINE, care scurtcircuita orice alta dovada.
Chunk-ul corect din dictionar exista, dar statea pe pozitia 4, in afara TOP_K:
OCR-ul unei ferestre intregi aduce zeci de tokeni de zgomot, iar BM25 ineca tocmai
codul.
- `rank.codes()` scoate codurile de oriunde din text, nu doar ca token intreg —
OCR-ul le lipeste de ce e langa ("OraJORA-06550").
- `rank.by_codes()` e un al treilea clasament in fuziune: chunk-ul care poarta
codul cerut urca peste cele care doar seamana. Acum iese primul.
- `rank.assess()`: cand intrebarea are coduri, decid doar ele. Niciun cod gasit =
escaladare la suport, oricat de mare ar fi cosinusul.
- `consumer.search()` judeca acoperirea pe cosinusul chunk-urilor date chiar
modelului, nu pe cel mai bun din tot indexul.
Calibrare 21/21 (cele 19 cazuri + captura reala + ORA-00600, cod care nu e in
documente si trebuie sa plece la suport). Teste: 79 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Cheia de refolosire a embeddings-urilor era doar textul chunk-ului. La schimbarea
lui EMBED_MODEL indexul ar fi ramas un amestec de vectori din doua modele, iar
cautarea ar fi dat rezultate aiurea fara nici un mesaj de eroare. Acum fiecare
intrare poarta modelul cu care a fost calculata si se refolosesc doar cele cu
modelul curent; intrarile vechi, fara camp, se recalculeaza o singura data.
Indexul de pe container a fost stampilat manual cu `nomic-embed-text` (singurul
folosit pana acum), deci nu s-au recalculat cele 169 de chunk-uri.
`GET /groups` listeaza grupurile contului cu JID, nume si numar de participanti.
JID-ul unui grup ("120363...@g.us") nu se vede nicaieri in WhatsApp, iar el e
singurul mod de a scrie SUPPORT_JID pentru un grup de suport.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Dictionarul de erori Oracle pus in `documents/` ar fi disparut in 10 minute:
`rclone sync` sterge de acolo tot ce nu exista in Drive. Aceeasi problema o avea
si butonul de adaugare document din dashboard — documentul traia pana la
urmatorul tur de sincronizare, ceea ce README-ul mentiona ca pe o ciudatenie, nu
ca pe un bug.
Acum sunt doua directoare cu un singur spatiu de nume: `documents/` (oglinda
Drive) si `documents-local/` (ce nu vine din Drive). La acelasi nume castiga
Drive-ul — e sursa comuna a echipei, iar copia locala poate fi o versiune veche
uitata acolo. Dashboard-ul scrie in cel local si marcheaza documentele „local".
Recalibrat cu dictionarul indexat (169 chunk-uri): 19/19, cele patru intrebari
Oracle noi ies la 0,73-0,75. Reindexarea a durat cat cele 29 de chunk-uri noi,
nu cat toate 169 — refolosirea vectorilor isi face treaba (log: "29 embeddings
noi, 140 refolosite").
Verificat pe canalul viu: o escaladare cu captura chiar pleaca pe WhatsApp,
imagine + rezumat + referinta, si apare in dashboard.
73 pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Trei probleme legate, gasite testand cu capturi reale.
1. RASPUNSURI GENERICE. Cand raspunsul nu era in documente, modelul primea
oricum top-3 chunk-uri si compunea ceva plauzibil — utilizatorul pleca cu
impresia ca a primit ajutor. Acum, daca nu avem acoperire, modelul nu mai e
intrebat deloc: intrebarea pleaca la suport, cu referinta (M-260831-A1B2) si
confirmare onesta — "am trimis" doar daca notificarea chiar a plecat, altfel
"am inregistrat, dar nu am putut trimite". Jurnalul se scrie intotdeauna, in
~/.maria-bridge/escalations/, si se vede in dashboard. Daca a fost o captura,
imaginea insasi se retrimite la suport (endpoint nou /send-image, limitat la
MEDIA_DIR).
2. ORDONARE (rank.py). Un prag pe cosinus nu putea decide "avem raspunsul?":
masurat pe indexul viu, intrebarile bune dau 0,600-0,816 si cele straine
0,534-0,685 — intervale care SE SUPRAPUN. Lipseau codurile: ORA-01722, D406,
CIF sunt tokeni exacti pe care cautarea semantica ii topeste. Acum se ordoneaza
de doua ori — embeddings si BM25 — si se fuzioneaza clasamentele (RRF), iar
decizia de acoperire se ia pe dovezi: cosinus mare, sau cosinus de mijloc cu o
fractiune din termenii distinctivi gasita chiar in chunk-uri. Fractiunea conteaza:
cu un singur termen, "cum imi resetez parola de la Windows" trecea drept
acoperita fiindca "parola" apare in documente.
Pragurile sunt masurate, nu alese: ops/calibrate-rank.py ruleaza un set de cazuri
pe indexul real si matura grila. 15/15 la strong=0.70 weak=0.58 ratio=0.5.
3. DICTIONAR DE ERORI ORACLE. 29 de erori uzuale, scrise pentru utilizatorul din
fata aplicatiei: ce inseamna, ce poate incerca singur, cand sa sune. Fara nume
de servere, IP-uri sau pasi de administrare — Maria e chatbot pentru clienti.
Fisierul din git e SAMANTA: depozitul e oglinda Drive-ului, deci trebuie pus in
document_store ca sa nu dispara la sincronizare.
Pe drum, doua lucruri gasite in log:
- fiecare mesaj din self-chat sosea de DOUA ori, pe @s.whatsapp.net si pe @lid,
deci Maria raspundea de doua ori. Dedup pe key.id in punte.
- reindexarea reface toate embeddings-urile (~7 s fiecare pe CPU): adaugarea unui
document la 170 de chunk-uri insemna 20 de minute. Acum refoloseste vectorii
chunk-urilor nemodificate din indexul precedent, fara vreun fisier nou de stare.
Textul OCR se logheaza acum INTEGRAL, cu interogarea de cautare si sursele alese
cu scorurile lor — fara asta un raspuns gresit nu se poate explica: nu stiai daca
a citit prost captura sau a cautat prost in documente.
6 fisiere de test noi/extinse, 68 pass (de la 42).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Doua lucruri cerute de utilizator, ambele pe acelasi drum: sa poti lega
puntea fara sa ai un ecran de scanat, si sa poti trimite Mariei o poza cu
eroarea in loc sa transcrii mesajul.
Asociere prin cod de 8 caractere (alternativa la QR):
- endpoint-ul /pair exista, dar nu era folosibil: fara `browser` explicit
WhatsApp refuza codul, iar dupa introducerea lui corecta serverul cere un
restart (515) care consuma din bugetul de reincercari si putea opri puntea.
Acum descriptorul e Browsers.ubuntu('Chrome') si restartRequired reconecteaza
imediat, fara sa numere.
- codul are TTL de 3 minute, iar /status il da doar cat timp e valabil —
un cod expirat afisat in dashboard trimite omul sa tasteze degeaba.
- dashboard: camp pentru numar + buton, in acelasi card cu QR-ul.
Imagini cu erori (capturi de ecran):
- puntea descarca imaginile in ~/.maria-bridge/media/ (imageMessage sau
document cu mimetype image/*, si prin ambalajele efemer/"vezi o data" —
fara despachetare pareau mesaje fara continut si se aruncau tacut).
- rag/ocr.py: tesseract ron+eng. Modelul de raspuns e strict text, deci OCR
nu e o optiune de calitate, e singura cale.
- cautarea in index merge DOAR pe liniile care arata a eroare; o fereastra
intreaga de meniuri si totaluri dilueaza embedding-ul si scoate chunk-uri
fara legatura. Modelul primeste captura intreaga, marcata ca text OCR.
- capturile se sterg imediat dupa citire (pot contine date de client).
- cand nu se citeste nimic si nu exista legenda, Maria cere textul erorii
in loc sa raspunda in gol.
16 teste noi (42 in total). install.sh verifica tesseract; README documenteaza
ambele metode de asociere si drumul unei capturi.
Separat, in docs/chatboti-si-punti.md: chatul "Eu" e vazut de AMBELE punti de pe
numar. Puntea lui Echo (LXC 110) e asociata ca dispozitiv :11 al aceluiasi cont,
momentan nelegata dar pornita — daca se reasociaza, raspunde in "Eu" langa Maria.
Notat si ca serviciile lui Echo sunt unitati de UTILIZATOR: `systemctl is-active`
ca root raspunde "inactive" desi botul ruleaza.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Documentatia de ieri descria LXC 301 ca un container oprit cu `onboot: 1` care
ar fura 10.0.20.37 de la VM 109 la reboot-ul lui pveelite. Configul are insa
`template: 1`: un template nu poate fi pornit, iar `onboot` e ignorat pentru el.
Riscul reapare doar daca e convertit inapoi in container.
Verificat si ca `basevol-301-disk-0@__base__` nu are niciun clon, ceea ce
infirma cealalta grija din pagina — se poate sterge in siguranta (~916 MB).
Recomandarea `pct set 301 -onboot 0` din handover e marcata infirmata, nu
stearsa, ca sa nu reapara intr-o sesiune viitoare.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SGMEagPnRavdLhWFqJHdja
LXC 301 (docker-portainer-template, pveelite, oprit) e predecesorul lui VM 109:
vechiul DR Oracle 19c, inlocuit de VM-ul Windows. Nu era documentat nicaieri.
Important: si-a pastrat IP-ul static 10.0.20.37 — acelasi pe care VM 109 l-a
mostenit de la el ("IP: 10.0.20.37 (same as current LXC)", din planul de
implementare DR) — si are onboot=1. La un reboot al lui pveelite porneste singur
si ocupa adresa. VM 109 are onboot=0, deci nimeni nu observa nimic pana la
urmatorul test DR, cand VM 109 gaseste adresa luata.
Recomandarea (nefacuta, e decizia utilizatorului): `pct set 301 -onboot 0`.
Stergerea completa cere verificat intai ca basevol-301-disk-0 nu e sablonul din
care s-au clonat alte containere.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Dupa asocierea de azi, puntea nu raspundea la niciun mesaj desi `connected: true`
si consumer-ul rula. WhatsApp livreaza self-chat-ul pe conturile noi cu un JID de
tip LID (`51947713372214@lid`), nu cu `<numar>@s.whatsapp.net`, iar filtrul
`TEST_MODE_SELF_CHAT_ONLY` compara doar cu al doilea — deci arunca tot.
- `ownLid` calculat din `sock.user.lid` (cu rezerva pe `creds.me.lid`), fara
sufixul de dispozitiv: credentialele au "51947713372214:13@lid", dar
remoteJid-ul mesajelor vine fara ":13".
- `isSelfChat` accepta acum ambele formate.
- Logul de conectare si `/status` arata si LID-ul.
Cauza pentru care a fost greu de gasit: mesajele respinse dispareau fara nicio
urma. Acum fiecare motiv de respingere se logheaza (nu e self-chat, fromMe in
afara self-chat, fara continut text), cu JID-urile comparate. Exact linia asta a
aratat problema in 30 de secunde:
[whatsapp] ignorat (nu e self-chat): remoteJid=51947713372214@lid ownJid=40723197939@s.whatsapp.net
Verificat live: "token efactura expirat" -> preluat, cautat in RAG, raspuns trimis.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
LXC 171 a fost asociat la WhatsApp (+40723197939, dispozitiv nou al aceluiasi
cont), iar pe 104 `maria-whatsapp-bridge` si `maria-whatsapp-consumer` au fost
oprite si dezactivate ca sa nu raspunda amandoua la acelasi mesaj.
`llama-qwen35.service` ramane pornit acolo: el e LLM-ul de raspuns, folosit acum
de 171 prin retea.
docs/chatboti-si-punti.md a fost scris cu cateva ore inainte de mutare si spunea
ca instanta vie e cea de pe 104 — actualizat peste tot (tabel, sectiuni, porturi,
comenzi de verificare), plus un avertisment sa nu se reporneasca prototipul ca
"reparatie": ar raspunde in paralel, pe acelasi numar, cu un index de 24 de
chunk-uri.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Pe 2026-08-31 s-a pierdut aproape o ora de diagnostic fiindca Maria raspundea pe
WhatsApp, dar verificarile se faceau pe containerul gresit: pe LXC 171 totul parea
rupt (niciun LLM pe 8091, WhatsApp neasociat, log fara activitate) in timp ce
raspunsurile veneau de pe LXC 104. Nicio pagina nu spunea ca exista doua instalari.
docs/chatboti-si-punti.md, verificat live: tabel scurt "cine e cine", cate o
sectiune per instanta (Maria pe Flowise, Maria WhatsApp de pe 104 care e cea vie,
Maria din git de pe 171 care nu e asociata, Echo pe 110, puntea Discord), tabel de
porturi cu notarea ca 8099 si 11434 exista pe ambele containere cu continut
diferit, comenzi de verificat cu cine vorbesti, si capcanele (prototipul de pe 104
nu e in git, depozitul e oglinda a Drive-ului, reindexarea dureaza ~21 min,
granita Mariei fata de infrastructura).
Faptul care lega totul: WhatsApp-ul lui Echo (110) si al Mariei (104) sunt legate
la acelasi numar, +40723197939, ca dispozitive diferite ale aceluiasi cont.
Documentul e legat din CLAUDE.md si dintr-un citat pus in capul celor cinci
README-uri implicate, ca sa fie gasit de cine aterizeaza direct acolo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Prototipul a ramas sa ruleze pe LXC 104 (/root/maria-whatsapp-bridge/), in afara
git-ului, si aceea era Maria conectata la WhatsApp — nu cea din git, de pe 171.
Indexul ei avea 24 de chunk-uri dintr-un singur fisier, deci inventa raspunsuri
la orice iesea din subiectul roaauto.
Pe 171 totul parea rupt (nimic pe 8091, WhatsApp neasociat, rag.log fara
activitate) desi utilizatorul primea raspunsuri pe telefon. Notat in README cum
se verifica intai care instanta e conectata, ca sa nu se mai piarda timp asa.
Notat si ca LLM-ul de raspuns (llama.cpp, Qwen3.5-2B-Q4, port 8091) ruleaza pe
LXC 104 si se foloseste prin retea; pe 171 Ollama are doar embeddings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Descoperit la prima sincronizare reala din Drive: `maria-sync.timer` a pornit
peste rularea manuala si doua procese faceau embeddings in paralel pe acelasi
Ollama, ambele urmand sa scrie acelasi rag_index.json. Embedding-ul a incetinit
de la ~7s la ~20s din concurenta, iar ultimul care termina ar fi suprascris
munca celuilalt.
- config.exclusive(): lacat `flock` intre procese, luat la intrarea in sync.py si
indexer.py. Nu asteapta — a doua rulare iese curat cu "o reindexare e deja in
curs", fiindca ar reface exact acelasi lucru. Verificat pe procese reale.
- indexer scrie indexul atomic (tmp + os.replace): consumer-ul reciteste fisierul
la 30s si putea prinde un JSON pe jumatate scris.
- build() intoarce `warnings` pentru XML-urile care nu se pot parsa, iar rularea
din linia de comanda le scrie in stderr. Pana acum, un XML invalid se indexa
tacut ca text simplu, cu o singura linie pierduta in log.
Context de performanta, masurat pe LXC 171 fara alta incarcare: un embedding
`nomic-embed-text` ia ~7,3s, deci o reindexare completa a celor 173 de chunk-uri
dureaza ~21 de minute — mai mult decat intervalul timer-ului. Nu e o problema
practica (amprenta reindexeaza doar la schimbare, iar lacatul opreste
suprapunerea), dar explica de ce prima rulare pare blocata.
docs/rclone-google-drive-headless.md: procedura de conectare a unui container
headless la Drive prin `rclone authorize`, cu transcriptul rularii reale de pe
Windows, capcanele (sync e distructiv pe destinatie, connection string in loc de
cale pe nume, unde stau secretele) si de ce nu contul de serviciu. Indexata in
CLAUDE.md.
6 teste noi (lacat, eliberare la exceptie, scriere atomica, avertismente).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Pasii 2-4 din README (creare remote, verificare acces, scriere DRIVE_REMOTE,
prima sincronizare) erau de facut de mana, cu ID-ul dosarului copiat corect de
fiecare data. Scriptul ii face pe toti dintr-un token `rclone authorize`:
valideaza ca tokenul e JSON, refuza devreme daca rclone sau env-ul lipsesc,
face backup la env inainte de a-l rescrie, si e idempotent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Dosarul document_store din Drive are 3 surse .xml pe care depozitul le ignora
complet, fiindca store.py accepta doar .txt/.md. La d406_saft_knowledge exista
ambele formate, iar .xml e cu trei luni mai nou (2026-01-28 vs 2025-10-15) si cu
50% mai mare (64 KB vs 41 KB) — deci indexam varianta mai saraca.
- store.py devine sursa unica pentru extensii (DOC_EXTENSIONS = .txt/.md/.xml).
Cand acelasi nume de baza exista in mai multe formate, la indexare intra unul
singur, cel mai bogat (.xml > .md > .txt); celalalt ramane pe disc, marcat
`shadowed_by`. Fara asta, acelasi raspuns ar aparea de doua ori in rezultate.
`list_documents()` arata tot (dashboard), `documents_for_index()` doar
castigatorii (indexer).
- indexer.py taie XML-ul altfel: un chunk per element de nivel 1, adica o
problema = un chunk, cu <mesaj_eroare> si <rezolvare> impreuna. Taierea pe
linii goale le-ar separa si cautarea ar returna eroarea fara raspuns.
Etichetele raman prefixe lizibile ("mesaj eroare: ..."), fara paranteze
unghiulare care doar dilueaza embedding-ul. XML invalid nu opreste indexarea:
cade pe taierea obisnuita, cu o linie in log. Elementele peste 4000 de
caractere se taie mai departe pe granite de cuvant — `chunk_text` imparte doar
pe linii goale, deci un element scris ca un paragraf lung ar fi ramas intreg
(prins de test).
- sync.py: amprenta si `rclone --include` derivate din DOC_EXTENSIONS.
- dashboard: acelasi filtru si aceeasi preferinta (copie, fiindca nu poate
importa `store` — coliziune de nume pe `config`), plus marcajul "umbrit de X"
in tabelul de documente si numarul de documente chiar indexate.
- README: sectiunea Drive rescrisa pe `rclone authorize` (autorizezi pe o masina
cu browser, muti tokenul) in loc de cont de serviciu — mai putini pasi, fara
consola Google Cloud. Documentat si ca `sync` sterge local ce nu mai e in Drive.
tests/ nou (20 de teste, fara retea si fara Ollama): preferinta de format,
vizibilitatea in dashboard, taierea XML, entitati, comentarii, XML invalid,
elemente uriase. Suita puntii Discord: 426 pass, neafectata.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Puntea trimitea doar `content`-ul mesajului; orice atasament disparea tacut, iar
un mesaj fara text (doar poza) era respins ca "empty". Acum mesajul de utilizator
se construieste ca blocuri, in formatul pe care CLI-ul il accepta pe
`--input-format stream-json` (verificat pe CLI real: Claude descrie corect o
imagine trimisa asa).
- imagini png/jpeg/gif/webp -> blocuri `image` base64, max 4, max 3,5 MB brut
(base64 umfla cu ~4/3, iar API-ul refuza peste ~5 MB codate)
- fisiere text (mime `text/*`, `application/json`, sau extensie cunoscuta) ->
continutul intra in prompt, max 4, trunchiat la 100 KB
- restul (PDF, Office, arhive, svg, heic) -> doar numite, cu motivul
Detalii care conteaza:
- `image/jpg` si `image/png; charset=...` se normalizeaza; cand Discord nu
trimite content_type cadem pe extensie
- marimea se verifica de doua ori: cea declarata (ca sa nu descarcam degeaba) si
cea reala dupa descarcare
- nimic nu dispare tacut: ce n-a putut fi citit apare in prompt ca
"Atasamente ignorate: ..."; o imagine stricata nu anuleaza restul mesajului
- merge si mid-tur: o poza trimisa in timpul unui tur intra pe stdin ca steering
31 de teste noi in tests/test_attachments.py (normalizare tipuri, limite,
trunchiere, erori de descarcare, integrare prin punte, steering). Suita: 426 pass.
README: sectiune "Atasamente" + limitarea veche corectata (ramane doar vocea).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Maria raspunde clientilor ERP ROA. Depozitul ei de documente contine doar
material orientat spre client final, dar adaugam si o bariera in SYSTEM_PROMPT:
refuza intrebarile despre servere, Proxmox, containere, IP-uri, baze de date,
parole sau chei, chiar daca ceva de acest fel ajunge in context.
Regula completa (ce are voie sa intre in ~/.maria-bridge/documents/) e scrisa
in /workspace/claude-agent/CLAUDE.md, sectiunea "Maria (RAG) - GRANITA
OBLIGATORIE".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
Ollama (+ nomic-embed-text) is now installed locally on LXC 171 for
embeddings — previously OLLAMA_URL was an unverified placeholder pointing
at nothing. rclone is installed too, but DRIVE_REMOTE still needs a Google
service account the user must create in GCP Console; noted the
document_store folder ID found via Drive search to save that step later.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drop the standalone maria-dashboard.service — one common panel now
controls both bridges, at the operator's request. discord-bridge's
dashboard/api.py gains a Maria section (fixed-unit start/stop/restart
for maria-whatsapp/maria-rag, WhatsApp status+QR proxy, document CRUD,
reindex, Drive sync trigger, log tail) reached via subprocess/HTTP —
no cross-module imports, to avoid colliding with discord-bridge's own
`config` module name. index.html gets a matching "Maria — WhatsApp +
RAG" section. maria-whatsapp-bridge loses its dashboard/ folder and
DASHBOARD_* env keys; install.sh/README point at the shared panel.
Co-Authored-By: Claude Agent <noreply@anthropic.com>
The unit file lives in ops/ (alongside the other three units), not in
dashboard/ — install.sh linked the wrong path, so `enable --now` for the
dashboard failed silently. Caught by running the installer for real.
Co-Authored-By: Claude Agent <noreply@anthropic.com>
Move the /tmp prototype (Baileys bridge + RAG consumer) into git as a
proper sibling project to discord-bridge/: own systemd --user units
(whatsapp bridge, rag consumer, dashboard, periodic Drive sync timer),
a filesystem document store with a stdlib control dashboard (start/
stop/restart, document CRUD, reindex, Google Drive sync via rclone),
and an idempotent ops/install.sh following the same conventions.
Co-Authored-By: Claude Agent <noreply@anthropic.com>
Pe VADECO (instalat 25.08.2026) salvarea in istoric coduri fiscale pica cu
PLS-00905: VADECO.PACK_PARTENERI invalid. Cauza: pachetul isi declara parametrii
ca ISTORIC_CODURI_FISCALE.<col>%TYPE, nume necalificat, iar sinonimul public
lipsea. Serverul avea 81 de sinonime publice catre CONTAFIN_ORACLE, productia
10.0.20.36 are 100 -- lipseau exact 20.
Gaura apare pentru ca sinonimele publice nu fac parte dintr-un export
schema-mode (impdp nu le aduce), singurul loc care le creeaza este o lista
enumerata manual, iar CONTAFIN_ORACLE.VERSIUNE vine cu DMP-ul si marcheaza
scripturile co_*/sys_* drept aplicate, deci nici ROAACTUALIZARI nu le mai
ruleaza. Din acelasi motiv lipsea si SYS.NEWSCHEMAPROGRESS, o functie din 2014.
- synonyms-public.sql: cele 20 de sinonime (81 -> 101 CREATE)
- sys-objects.sql: pas [9b/10], SYS.NEWSCHEMAPROGRESS (sys_2014_11_06_01_FIRMA)
- sys-grants.sql: SYN_NEWSCHEMAPROGRESS in [3/6]; granturi directe SELECT pe
SYS.DBA_DATAPUMP_JOBS si SYS.AUTH_SERII in [1/6] (sys_2013_01_23_02)
- docs/refresh-scripturi-dupa-instalare.md: de ce completarea listelor e paliativ
si ce ar trebui sa faca un pas de refresh rulat dupa instalare, nu la instalare
- sys-updates/README.md, CLAUDE.md: trimiteri catre documentul nou
Aplicat pe VADECO: PACK_PARTENERI si PACK_IMPORT_COMENZI sunt VALID pe VADECO,
DANUBE, LACERTA si SPACE. Obiectele ramase invalide sunt cele preexistente,
invalide si pe 10.0.20.36.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J7vUBDUY5hvLwRZ45Uqzcf
Trei lucruri observate in logurile de productie dupa restartul precedent.
1. Ecoul propriilor mesaje umplea bot.log cu WARNING. Propriile mesaje au si
ele `author.bot == True`, iar verificarea generica de bot venea INAINTEA
celei pe `self_id` — deci raspunsurile botului se jurnalizau ca "bot
strain", la fiecare mesaj. Verificarea pe `self_id` trece prima (motivul e
acum precis), iar refuzurile de rutina — propriile mesaje si ceilalti boti —
merg la DEBUG. Guild / canal / utilizator strain si webhook raman WARNING:
alea chiar sunt semnal de securitate si erau inecate in zgomot.
2. `tool_progress` (heartbeat la 30s cat timp o unealta ruleaza) devine
eveniment `ToolProgress`. Mesajul live arata acum "⏳ ruleaza de 2m30s" sub
unealta curenta — singurul semn ca un tur lung lucreaza si nu a inghetat.
3. `rate_limit_event` devine eveniment `RateLimit` si apare in `/status` la
randul `utilizare`. Cum nu exista plafon de cost (abonament, nu API),
fereastra de utilizare e singura limita reala; se avertizeaza in log o data
per schimbare de stare, nu la fiecare eveniment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Confirmarea per comanda devenea obositoare intr-o sesiune care lucreaza pe
acelasi host: `ssh pvemini ...` de zece ori la rand insemna zece butoane.
Butonul de confirmare are acum trei variante: Allow / Allow (tot firul) / Deny.
"Allow (tot firul)" memoreaza tiparul `(rule, reason)` produs de clasificator,
nu comanda: dupa o aprobare pe `ssh pvemini uptime`, orice comanda catre ACEL
host trece singura, dar `ssh 10.0.20.36` sau un `rm -rf` cer din nou
confirmare. Aprobarile stau in ~/.claude-discord/approvals/grants/<fir>.json.
Domeniul e firul Discord, nu `session_id`: acela se schimba la `--resume`, iar
aprobarile ar disparea exact cand omul se astepta sa tina.
Expirare: `/new` le sterge (sesiune noua = permisiuni noi), `/permisiuni
revoca:True` la cerere, TTL implicit 12h (CLAUDE_DISCORD_GRANT_TTL), iar
CLAUDE_DISCORD_SESSION_GRANTS=off dezactiveaza complet mecanismul.
Fail-closed peste tot, ca restul hook-ului: fara CLAUDE_DISCORD_THREAD_ID (hook
rulat in afara puntii), cu fisierul de aprobari corupt, cu un thread_id care nu
arata a id (`../`, punct la inceput, peste 128 de caractere) sau la orice
exceptie, has_grant() raspunde False si se cere confirmare in Discord.
Adaugat si `/permisiuni [revoca:True]` (listare/revocare) plus butonul
echivalent in dashboard (`decision: "allow_session"`).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Costul raportat de CLI e pretul echivalent la API; pe abonament nu se
factureaza, deci plafonul zilnic oprea puntea fara motiv (5.71 / 5.00 USD).
- limits.parse_cap(): `off`/`none`/`nelimitat`/`0`/gol/gunoi => fara plafon
- stopped()/record_cost() nu mai opresc si nu mai alerteaza cand e dezactivat
- implicit devine `off`; /status arata „(fara plafon)"
- dashboard: /api/status si doctor nu mai crapa pe valoare ne-numerica
- README + ops/env.example explica de ce ramane off pe abonament
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Cerut explicit: tokenul nu se retine. Cu DASHBOARD_AUTH=off nu mai exista login,
/login.html duce inapoi la panou, iar butonul "Iesi" dispare.
Se sprijina pe doua lucruri si nu are sens fara ele: serviciul e legat de
127.0.0.1, deci din retea ajunge la el doar tailscaled; iar tailscale serve il
publica tainet only, unde accesul e deja autentificat de Tailscale.
Compensatie partiala pentru ce se pierde: fiecare start/stop/restart se scrie in
logs/dashboard.log cu identitatea din antetul Tailscale-User-Login pus de
tailscale serve (verificat: ajunge pana la noi). Antetul e DOAR pentru jurnal —
nu decide accesul, fiindca un proces local l-ar putea fabrica.
Implicitul ramane cu token: doar off/none/0/false scot login-ul, orice alta
valoare il pastreaza (are test).
Sase teste noi, 41 pe dashboard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Simptom: pagina se incarca prin tailscale, dar niciun API nu era cerut; in
dashboard.log se vedea doar GET / si nimic altceva.
Cauza: --set-path taie prefixul, deci /claude si /claude/ ajung la server
identic, ca "/". Fara slash final, URL-urile relative se rezolvau la radacina
hostului (https://host/api/status), unde proxy-ul nu trimite nimic incoace.
Cererile nici nu ajungeau la noi, iar pagina ramanea goala fara nicio eroare.
Redirectul 301 adaugat anterior nu putea ajuta: serverul nu vede forma
originala a adresei.
Paginile se servesc acum printr-un handler propriu care pune <base href> din
DASHBOARD_PREFIX, plus Cache-Control: no-store, fiindca HTML-ul poarta de acum
configuratie si o copie veche ar trimite cererile aiurea.
Patru teste noi. Verificat in browser pe cazul reprodus (pagina servita pe
radacina, ca prin proxy): toate cererile pleaca cu /claude/ si datele se incarca.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Pe un ecran de 390px pagina se intindea pe 2621px si derula orizontal. Cauza:
copiii de grid au min-width:auto, deci liniile lungi din pre.log si tabelul de
fire dilatau coloana lui .wrap, si odata cu ea tot documentul. Rezolvat cu
min-width:0 explicit; derularea ramane inauntrul containerelor cu overflow.
Sub 640px:
- tabelul de fire devine carduri stivuite (capul de tabel dispare, eticheta vine
din data-label) — 5 coloane pe 390px insemnau derulare la fiecare rand;
- butoanele stau doua pe rand, cu tinta de atins de 44px; sub 380px, unul pe rand;
- verificarile si confirmarile se aseaza pe verticala, comenzile lungi se rup;
- antetul trece pe doua randuri, notificarile se intind pe toata latimea;
- corpul trece la 16px, ca iOS sa nu mai dea zoom la focus pe input.
Verificat la 360, 390 si 1440px: zero depasiri pe orizontala, butoanele de
confirmare egale si de 44px inaltime, desktopul neschimbat.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
https://claude-agent.tailf7372d.ts.net/punte — acelasi tipar ca /echo de pe
moltbot: procesul ramane legat de 127.0.0.1, tailscaled il proxeaza si pune HTTPS.
Montarea sub prefix a cerut doua schimbari:
- toate URL-urile din pagini sunt acum relative, fiindca --set-path TAIE prefixul
inainte de a proxa (serverul vede /api/status, browserul cere /punte/api/status).
DASHBOARD_PREFIX ramane necesar doar pentru redirectul de login, si e acceptat
si intact pe intrare, ca sa mearga si curl direct pe localhost.
- adresa fara slash final (/punte) primeste 301 catre /punte/: altfel URL-urile
relative s-ar rezolva la radacina hostului, unde proxy-ul nu trimite nimic
incoace, si panoul ar arata gol fara nicio eroare vizibila.
ops/install.sh configureaza serve-ul daca sudo permite; altfel spune comanda.
Sase teste noi pentru montarea sub prefix (31 in total pe dashboard).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Bloc de status gasit necomis in arborele de lucru pe LXC 171 si pastrat ca atare;
verificat azi pe 10.0.20.173: unitatile active sunt echo-core, echo-taskboard si
echo-whatsapp-bridge.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Panou web pe 127.0.0.1:18790, unit systemd separat de al puntii. Server stdlib
(fara dependinte noi), tokenii de design si tiparul de endpoint-uri preluate din
/home/moltbot/echo-core/dashboard (handlers/eco.py) de pe LXC 110.
Arata: starea unitatii (uptime, PID, memoria cgroup, restarturi), firele din
state.json cu tur in zbor si cost, costul zilei fata de plafon, confirmarile
PreToolUse in asteptare (aprobabile direct din pagina), bot.log / infra.log si
opt verificari de diagnostic.
Face: start / stop / restart pe punte, cautarea si curatarea orfanilor prin
cleanup.py, repornirea propriului serviciu.
Garantii, cu teste:
- unitatea controlata e fixa in cod; un {"unit": "ssh.service"} in cerere nu
schimba nimic, altfel panoul ar fi systemctl remote fara parola;
- stop/restart intorc 409 cu lista firelor active si cer force explicit, fiindca
KillMode=control-group taie tururile in desfasurare;
- state.json se citeste fara lock: panoul nu are voie sa blocheze botul;
- diagnosticul pica daca reapare Bash(ssh:*) in deny (regresia de azi).
Uptime-ul se calculeaza din time.monotonic(), nu din /proc/uptime: in LXC acela
e virtualizat de lxcfs si da diferenta negativa fata de monotonic-ul systemd.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Regulile deny au precedenta peste --permission-mode bypassPermissions si
opresc turul INAINTE de hook-ul PreToolUse, deci puntea nu putea ajunge la
niciun host prin ssh (simptom: agentul raporta "permisiunea a fost respinsa"
pentru ssh moltbot@10.0.20.173, desi cheia, DNS-ul si reteaua erau in regula).
Nota din README care sustinea ca deny e doar strat cosmetic era gresita:
verificarea de atunci folosise /usr/bin/ssh -V si bash -c "ssh -V", care
ocolesc potrivirea pe prefix, nu forma normala ssh host cmd.
Bariera reala ramane confirm_hook.py + wrapper-ul infra.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Doua defecte, al doilea gasit la verificarea primului.
1. Procesul `claude` al unui fir isi porneste serverele MCP (npm/sh/node pentru
playwright), care nu se numesc `claude` si scapau cautarii. Masurat pe firul viu:
claude 300 MB + MCP 137 MB = ~440 MB per fir, nu 300. find_orphans merge acum pe
arbore, iar kill_orphans opreste copiii inaintea parintilor (altfel se reparenteaza
si scapa).
2. PERICULOS: definitia initiala a orfanului ("orice `claude` absent din state.json")
prindea sesiunile Claude interactive de pe container. Rularea seaca propunea 25 de
procese / ~3864 MB — sesiunile de lucru din tmux, inclusiv cea din care ar fi fost
data comanda. `/cleanup force:True` din Discord si-ar fi omorat propria sesiune.
Apartenenta la cgroup-ul `claude-discord.service` devine conditie necesara pentru
toate familiile, fail-closed la cgroup necitibil. Dupa fix: zero procese raportate.
Test de regresie pe instantaneul real (doua sesiuni in tmux-spawn-*.scope, una in
claude-discord.service): se raporteaza doar a treia. Verificat prin mutant ca testul
musca — cu filtrul scos pica 3 teste.
3. INTERFACES.md cerea pid_start_time in ticks, dar session_store scrie secunde (si
state.json viu contine secunde). Contractul era imprecis, nu codul: un consumator
care compara ticks nu s-ar potrivi niciodata, iar cum acea comparatie protejeaza
turul in desfasurare, esecul ar fi fost tacut si ar fi facut eligibil exact ce
trebuia protejat. Documentat, cu avertismentul explicit.
Bug colateral prins de agent: raportul cu rezultate de omorare ajungea la 2289 caractere,
peste limita Discord — mesajul ar fi fost respins exact la `/cleanup force:True`. Buget
de caractere adaugat.
Suita: 322 passed cu discord.py, 319 passed + 3 skipped fara. Rulare seaca pe container: curat.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Comenzile devin application commands inregistrate pe guild (sync instantaneu,
spre deosebire de cel global care dureaza ~1h): /new [fork], /cd <cale>,
/model <sonnet|opus> cu Choice, /status, /stop, /cleanup [force], /help.
- allowlist-ul se aplica identic la interactiuni (check_ids comun, ca sa nu
existe a doua implementare care diverge); refuz efemer, fara executie
- fiecare comanda face defer() inainte de lucru — altfel Discord marcheaza
interactiunea esuata dupa 3s desi comanda a rulat
- sync tolerant: la esec (lipsa scope applications.commands) botul porneste
normal si logheaza linkul de reinvitare necesar
- mesajele obisnuite raman neschimbate, inclusiv steering-ul mid-tur
- linkul de invitatie primeste scope=bot%20applications.commands; referintele
la ! din ops/ si documentatie trecute pe /
Verificat in productie: 7 comenzi inregistrate pe guild, citite inapoi din API.
Suita: 296 passed cu discord.py, 293 passed + 3 skipped fara.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Unitul avea PATH explicit doar pentru CLI-ul claude din nvm. Wrapperul `infra`
se leaga in ~/bin, care lipsea, deci botul nu l-ar fi gasit — stratul 4 de
securitate ar fi fost prezent pe disc si inutilizabil in practica.
Verificat: infra refuza un host din afara listei cu exit 3.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
- DEFAULT_CWD trece de la /workspace la /workspace/claude-agent, un spatiu cu git
propriu, ca fisierele facute din Discord sa aiba istoric separat de proiectele
reale. `!cd <cale>` ramane disponibil oriunde in /workspace, deci decizia din
plan (fara allowlist de proiecte) nu se schimba.
- setup_logging: sub systemd unitul redirecteaza deja stdout in bot.log
(StandardOutput=append:), iar FileHandler-ul scria fiecare linie a doua oara
in acelasi fisier. FileHandler ramane doar la rulare manuala.
Verificat in productie: bot conectat, un tur real incheiat curat (inflight null,
cost $0.0742 contabilizat), restart fara orfani. Suita: 275 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Application ID 1543576449624186880 (aplicatie dedicata, creata 2026-08-30).
Linkul e util la reinvitare, cand botul a fost scos din server sau i s-au
schimbat permisiunile. Nu e secret: Application ID e public, spre deosebire
de DISCORD_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
- permisiunile de invitatie omiteau *View Channel*, fara de care botul nu vede
canalul deloc, oricat de permis ar fi in allowlist
- link de invitatie gata calculat (permissions=309237763136), fiindca bifele din
URL Generator sunt greu de nimerit pe telefon
- *Bot -> Add Bot* nu mai exista: portalul creeaza user-ul bot odata cu aplicatia
- MESSAGE CONTENT INTENT are nevoie de *Save Changes*; fara apasare setarea se pierde
- pasii de copiere ID: pe telefon e apasare lunga, nu click dreapta
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Implementeaza planul claude-master-plan-discord-bridge-20260830 (15 taskuri,
3 lane-uri paralele) — un bot subtire discord.py peste CLI-ul `claude`, cu
proces persistent per fir alimentat pe stdin cu --input-format stream-json.
Nucleu: runner (proces persistent + reaper 20min + respawn --resume), stream
(parser tolerant), session_store (scriere atomica, lock per fir, detectare PID
reuse, recovery), limits (max 4 procese, timeout tur, rate per user, plafon cost
pe zi), render (un loop de editare per canal, interval adaptiv).
Adaptor: allowlist guild/canal/user fail-closed cu respingerea webhook-urilor,
comenzi !new/!cd/!model/!status/!stop/!cleanup, cost si model in subsolul
fiecarui raspuns. Mesajul sosit in timpul unui tur devine steering, nu tur nou.
Securitate: hook PreToolUse fail-closed care cere confirmare in Discord pentru
operatiuni ireversibile, wrapper `infra` cu lista explicita de hosturi. Deny
rules raman strat cosmetic, nu bariera (verificat: /usr/bin/ssh trece pe langa).
Ops: alerte email pe conventia repo-ului, !cleanup pentru orfani, unit systemd
user cu KillMode=control-group si limite de memorie, install.sh idempotent.
Verificat: 275 teste fara retea/Discord/API (10.8s), identic cu si fara
discord.py instalat; e2e pe CLI real confirma steering-ul mid-tur (mesaj la 6s
intr-un tool call de 25s schimba raspunsul final).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B29CApsP1JkSdjYaGaHpE7
Ring1 aplicat si verificat pe cluster live: config_version 17, ring1_addr pe
10.10.10.20x la fiecare nod, interface{linknumber:1}. Validat cu `corosync -t`
inainte de instalare. Testat prin oprirea reala a lui ring0 pe pveelite: link0
disconnected, link1 connected, cvorum 3/3 neatins - exact scenariul care pe
27 august a lasat nodul mort 16 ore.
Test de repornire completa a clusterului, cu Wake-on-LAN (trezire in ~15s).
Insula a urcat singura pe toate trei nodurile; ipoteza enumerarii tarzii a
USB-ului nu s-a materializat. Unealta noua: wake-cluster.ps1.
Testul a scos la iveala o linie ramasa in /etc/fstab pe pve1 si pveelite, care
monta storage-ul NFS de pe IP-ul de productie inaintea lui pvestatd. Backup-ul
trecea tacut pe reteaua gresita, cu storage.cfg corect. cluster-startup verifica
acum asta automat, impreuna cu IP-urile de insula si conectivitatea reala.
Patru bug-uri gasite prin rulare pe cluster live:
- sonda Oracle nu avea timeout: un sqlplus agatat pe o instanta in pornire a
blocat cluster-startup 14 minute, fara mesaj, cu propriul prag de 600s
nefolosit, fiindca bucla n-a apucat o iteratie;
- `bash -c` in loc de `bash -lc`: sqlplus lipsea din PATH, deci baza nu se
oprea si containerul s-ar fi inchis peste ea;
- PowerShell 5.1 pierde ghilimelele duble catre exe-uri native, deci comanda
ajungea rupta pe nod - trecut pe trimitere codificata base64;
- backup-ul de crontab si fisierul de stare se rescriau la o a doua rulare,
lasand monitorizarea oprita permanent si lista de repornit goala.
Documentatia de oprire planificata pornea de la o afirmatie devenita falsa
("nu exista datacenter.cfg") si de la o comanda care ar fi adaugat o a doua
linie `ha:`. Actualizata, impreuna cu inventarul de guest-uri (VM 304 lipsea
din toate listele de ordine si cadea in maturarea de dupa Oracle).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJFQZcA7uerXRaL1NrsXpS
Replicarea, migrarea si backup-ul NFS trec acum pe o insula 10.10.10.0/24
(switch 1, fara gateway), iar vmbr0 a fost mutat de pe dongle-urile USB
Realtek pe placile Intel onboard.
Castigul nu e viteza. Masurat: replicarea mergea deja la 272 MB/s, adica
~95% din firul de 2.5G, iar criptarea SSH nu era limita. 10G e imposibil
cat timp pve1 si pveelite au doar dongle-uri USB de 2.5G si niciun slot
PCIe liber; placa X710 din pvemini e SFP+ cu cage-urile goale, iar switch-ul
are doar porturi RJ45. Replicarea ruleaza si strict secvential
(Replication.pm:138, un singur lock, fara fork), deci nici agregarea de
linkuri nu ar ajuta.
Castigul e ca IP-ul de cluster si corosync ring0 nu mai stau pe dongle-ul
USB care a lasat pveelite invizibil 16 ore pe 2026-08-27 - fara sa fie
nevoie de vreo modificare in corosync.conf, fiindca IP-urile au ramas
aceleasi. Plus izolarea replicarii de productie, WoL persistat pe onboard,
si o cale out-of-band catre noduri prin insula.
Aplicat si verificat: cvorum pe 3 pe tot parcursul, 262-273 MB/s pe insula,
job real de replicare confirmat cu numarare de octeti pe interfete
(4959 KB pe insula vs 1553 KB pe productie), NFS activ si scriibil.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJFQZcA7uerXRaL1NrsXpS
La VADECO, backupora.exe rula in fiecare noapte, se termina cu cod 0 si scria
"DONE" pentru fiecare schema - dar directorul zilei ramanea gol. Intre 25 si
28.08.2026 nu a existat niciun export.
Cauza: schema.txt cere "@XEPDB1", iar 01-setup-database.ps1 scria in tnsnames.ora
doar aliasul ROA. expdp cadea instant cu ORA-12154, iar backupora.exe nu-i
verifica codul de iesire. Doua scripturi din acelasi kit nu erau de acord asupra
numelui bazei, si nimic nu tipa.
Indiciul din log, daca reapare: fiecare schema dura exact 16 secunde, indiferent
de marime. Timp uniform = expdp moare la conectare, nu exporta.
Reparatii, ca sa nu se repete la alt client:
- 01-setup-database.ps1 scrie acum doua aliasuri, ROA si numele serviciului,
in ambele tnsnames.ora. Curatarea dinaintea rescrierii parcurge fiecare alias
gestionat de noi, altfel al doilea s-ar dubla la fiecare rulare. Aliasurile
generate de Oracle (XE, LISTENER_XE, ORACLR_CONNECTION_DATA) raman neatinse.
- 11-setup-backup-export.ps1 face tnsping inainte de a scrie schema.txt si
opreste instalarea daca aliasul nu se rezolva. Mai bine o instalare care se
plange decat un backup care nu exista.
- lectii-actualizare-roa-alias-tns.md capata sectiunea despre a doua victima a
aceleiasi cauze. Lectia generala: unde un script lanseaza un proces extern
Oracle, verifica efectul, nu raportul procesului care l-a lansat.
Include si blocul NTS din config/sqlnet.ora, ramas necomis: autentificarea OS
cere si apartenenta la ORA_OraDB21Home1_DBA, si setarea din sqlnet.ora.
Nota: CLAUDE.md apare ca modificat integral - blob-ul din git avea CRLF, iar
core.autocrlf=true il normalizeaza la LF. Modificarea reala e o singura linie.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9Sa54oFKpE2XhD6A7NTch
Constatat la VADECO pe 2026-08-28: pe serverul acela actualizarea ROA nu
aplicase NICIODATA un script, desi jobul raporta succes de la instalare.
PACK_UPDATE nu aplica el scripturile - genereaza D:\DMPDIR\script_master.sql
si lanseaza un sqlplus EXTERN, apoi se termina. Deci UPDATEROA_ZILNIC
raporteaza starea lansarii, nu a actualizarii: SUCCEEDED in ~5 secunde chiar
si cand nu s-a aplicat nimic. Scriptul generat incepe cu
"CONNECT CONTAFIN_ORACLE/...@ROA" (aliasul vine din NOM_FIRME.NUME_SERVER)
si are WHENEVER SQLERROR EXIT, deci iese la prima linie daca aliasul nu se
rezolva.
Lantul cauzal: sqlplus-ul lansat de baza mosteneste mediul serviciului
Oracle. La o instalare noua instanta porneste INAINTE ca 01-setup-database
sa scrie TNS_ADMIN de masina, deci serviciul nu il are si cade pe
tnsnames.ora din Oracle Home. La 21c XE home-ul e read-only, deci fisierul
real e in product\21c\homes\OraDB21Home1\network\admin, iar acolo Oracle
genereaza doar XE, LISTENER_XE si ORACLR_CONNECTION_DATA - fara ROA.
Rezultat: ORA-12154, tacut. Perfid: tnsping ROA REUSESTE dintr-o sesiune
interactiva, pentru ca aceea are TNS_ADMIN.
Eroarea era mascata dublu - jobul zicea SUCCEEDED, iar emailul de raportare
nu pleaca oricum (ORA-29279, SMTP-ul romfast nu anunta AUTH).
01-setup-database.ps1 scrie acum aliasul si in tnsnames.ora al Oracle
Home-ului, cu acelasi IP din LAN. Blocul se IMBINA in fisierul existent:
XE, LISTENER_XE si ORACLR_CONNECTION_DATA raman neatinse, pentru ca de ele
depind extproc si inregistrarea instantei la listener. Cu backup si
idempotent - regexul consuma si comentariile lipite deasupra lui "ROA =",
altfel antetul se dubla la fiecare rulare (prins de test).
Testat unitar (fisier Oracle fara ROA, idempotenta peste 4 rulari, ROA
preexistent cu alt IP la mijloc, fisier inexistent, paranteze echilibrate,
fara BOM) si end-to-end pe o copie a fisierului real de pe serverul VADECO:
tnsping ROA OK, sqlplus CONTAFIN_ORACLE@ROA conectat in XEPDB1, zero ORA-,
XE si LISTENER_XE inca se rezolva.
docs/lectii-actualizare-roa-alias-tns.md are diagnosticul complet, inclusiv
cum verifici ca actualizarea chiar s-a aplicat: script_master.log si
SCHEMA.versiune, NU statusul jobului. Contine si capcana ca randurile din
SCHEMA.versiune vin cu dump-ul la import, deci o schema proaspat importata
pare la zi fara ca actualizarea sa fi rulat vreodata local.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X9Sa54oFKpE2XhD6A7NTch
Commit-ul precedent (1c6ab0f) sustinea ca resetarile adaptorului USB LAN sunt
cauzate de un mouse defect care se re-enumera de ~1000 de ori pe zi pe acelasi
controller xHCI. Testul o infirma.
Portul mouse-ului dezactivat la 11:24:04; la 11:31:52 adaptorul s-a resetat
oricum, cu 0 re-enumerari de mouse si aceeasi semnatura de eroare
"xhci_hcd 0000:00:14.0: WARN Set TR Deq Ptr". Corelatia initiala era
coincidenta - mouse-ul se re-enumera la fiecare ~60 s, deci orice eveniment de
pe nod pica la cateva secunde dupa unul.
Ce ramane adevarat: mouse-ul e defect (1127 re-enumerari fata de 1 a tastaturii
pe acelasi hub) si merita inlocuit, dar e o problema separata.
Cauza resetarilor r8152 redevine necunoscuta - 6 resetari spontane in 20 h,
fara tipar. Suspecti netestati: adaptorul, portul/cablul USB3, controllerul,
alimentarea pe USB3. Mutarea pe eno1 redevine reparatia principala, fiindca
ocoleste intrebarea cu totul.
Sectiunea e pastrata cu ipoteza si infirmarea ei, ca sa nu fie reluata.
Nota buna: la resetul din 11:31:52 hotplug-ul a reatasat interfata in 4
secunde si corosync a reformat membership 1.1f4 cu 3 membri - un hopa de 10
secunde in loc de 16 ore.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAEUqrv26jCM5KwAPtkHb8