Files
rar-autopass/docs/prd/prd-5.14-mapare-llm-distilata.md
Claude Agent 3fc53534e2 feat(5.15+5.14): CLOSE — fix-uri code-review + embeddings functional
5.15 (propagare design + dashboard editare) si 5.14 (mapare LLM distilata)
inchise dupa /code-review high. 8 buguri reparate TDD:

- HIGH modal nu se deschidea pe randul slim (base.html: trimitere-slim)
- HIGH /repune trunchia prestatii (declaratie incompleta la RAR) -> iterare
  peste existing, codes pozitional
- HIGH embeddings incarca model ~230MB degeaba pe corpus gol -> poarta has_corpus()
- HIGH picker chips gol pe re-render eroare -> conn/account_id pe toate ramurile
- MED obs re-derivat dupa stergere explicita -> _merge_override pastreaza obs=''
- MED mapare salvata fara denumire poluă GOLD -> _record_gold_validation guard
- MED typo nome_prestatie -> nume_prestatie in select /repune
- MED bucketare timp +3h gresita iarna -> SQLite localtime + TZ=Europe/Bucharest

Embeddings WIRE-uit functional (PRD #15, decizie user): ensure_embeddings_corpus
construieste corpus din nomenclator, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default
off). Marime model corectata ~50MB->~230MB (estimare PRD gresita).

Cleanup: hoist load_* din bucla bulk-fix; import re la top.
Regresie: 1256 passed, 1 deselected (live), 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 20:48:34 +00:00

28 KiB
Raw Blame History

PRD 5.14 — Mapare automata operatii service prin distilare LLM

Stare: inchis (2026-06-28; CLOSE dupa /code-review high -> embeddings „mort dar scump" reparat + WIRE functional la decizia user: corpus din nomenclator gated pe AUTOPASS_EMBEDDINGS_ENABLED; marime model corectata ~50MB->~230MB; regresie 1256 passed)

Stories de executie (decompozitie lead, 2026-06-28)

PRD-ul a fost aprobat prin /autoplan ca DESIGN (Decision Audit Trail #11-20). Aici lead-ul il sparge in stories atomice executabile (ROADMAP §5.4), FARA a re-deschide deciziile. Secventiere fata de 5.15 (D9 + cerinta user "prioritate design 5.15"): partile DISJUNCTE de fisier ruleaza in PARALEL cu 5.15 acum; integrarea in editor (mapping.py/routes.py) ASTEAPTA 5.15 si se aplica PESTE designul 5.15, fara sa-l suprascrie.

Story Tip Fisiere (disjunct?) Depinde de
L14-S1 Layer 1 etichetator offline tool tools/mapare-llm/or_label.py + teste (mock OpenRouter) — DISJUNCT
L14-S2 Temporal holdout (GATE Premisa 1) tool tools/mapare-llm/holdout.py + raport — DISJUNCT
L14-S3 Schema suggestions + shared store backend app/schema.sql (aditiv), store module nou, seeder, teste — owns schema.sql
L14-S4 Modul embeddings in-proces backend app/embeddings.py NOU + teste — DISJUNCT (modul; fara wiring)
L14-S5 Set held-out eval (BLOCANT auto-send) tool tools/mapare-llm/heldout_eval.py + metodologie — DISJUNCT
L14-S6 Integrare Layer 2/3 in editor backend+UI app/mapping.py, app/web/routes.py (editor) — DUPA 5.15 L14-S3,S4; 5.15 US-007/US-009

Invariante de respectat (din Decision Audit Trail): auto-send DOAR GOLD propriu (F-A/#11); silver in tabela SEPARATA, niciodata in resolve_prestatii (#13); seeder INSERT OR IGNORE, nu clobber uman (#2); scrub PII inainte de LLM (#3); NUL = ancore negative + supresie (#4); provenance source/confidence (#5); embeddings doar SUGESTIE + degradare gratioasa (#16b); held-out etichetat de OM = blocant pt orice auto-send peste GOLD (#19); tier "Inalta" sters din v1 (#17).

Rezultat GATE Premisa 1 (L14-S2, 2026-06-28) — VERDICT: SLABA. Validarea temporala STRICTA e imposibila (CSV-urile docs/operatii-service/*.csv au doar frecvente agregate, fara timestamp). Proxy Zipf + leave-first-out pe 155.195 operatii: pentru 90% acoperire de volum e nevoie de 4.368 denumiri distincte (25.4% din total), nu "cateva sute"; leave-first-out (limita superioara de stationaritate) = 88.9% agregat, SUB 90%. Implicatie: etichetarea offline (L14-S1) trebuie sa proceseze ordine de MII de denumiri per client; coada needs_mapping ramane semnificativa chiar dupa bootstrap. Premisa nu e falsa, dar randamentul auto-rezolvarii e mai mic decat estima PRD-ul. NU blocheaza build-ul (piesele sunt utile + auto-send ramane conservator pe GOLD), dar recalibreaza asteptarile de acoperire. Tool: tools/mapare-llm/holdout.py.

Raport VERIFY 5.14 (subagent independent context curat, 2026-06-28) — VERDICT: PASS, zero FAIL, zero regresie 5.15. pytest -q -m "not live"1245 passed, 0 failed. Invariante confirmate cu cod+test:

  • F1/#11/#17 auto-send DOAR GOLD propriu: load_mapping citeste EXCLUSIV operations_mapping al contului; resolve_prestatii nu atinge DB (primeste mapping dict); singura cale spre queued = GOLD propriu. SILVER/GOLD-partajat/embedding = sugestie. Teste test_f1_* PASS. Tier "Inalta" sters (#17).
  • #13 separare structurala: grep confirma — shared_store/mapping_suggestions/shared_mappings apar DOAR in enrich_suggestions (apelat din pending_unmapped), niciodata in resolve_prestatii/load_mapping.
  • #16b degradare gratioasa: is_available()=Falsesuggest_nearest=[] fara exceptie; ingestia nu se blocheaza.
  • #2 seeder INSERT OR IGNORE (nu clobber uman); #4 NUL nu devine cod; #5 provenance source/confidence; #3 scrub PII nr/VIN inainte de LLM (or_common.scrub); #19 held-out cu cod_gold GOL + kill-criterion (wrong_code_rate<0.5% AND coverage>50%) — toate PASS cu teste.
  • GATE Premisa 1: verdict SLABA documentat onest (proxy Zipf, fara pretentie de validare temporala).
  • fastembed 0.8.0 INSTALAT; testul real de embedding trece.

Riscuri reziduale (LOW, non-blocant): (1) fastembed 0.8.0 foloseste mean-pooling (warning) — relevant doar daca se persista corpusul de vectori intre versiuni (acum re-indexat la nevoie din nomenclator); (2) record_human_validation ON CONFLICT nu suprascrie cod_prestatie (by design — corectie = override per-cont sau DELETE explicit); (3) lazy-load fastembed la prima cerere /mapari cand AUTOPASS_EMBEDDINGS_ENABLED=true (~230MB, cateva zeci de secunde daca modelul nu e in cache — acceptat la decizia CLOSE). CLOSE 2026-06-28: embeddings WIRE-uit functional (era „mort dar scump"): ensure_embeddings_corpus(conn) construieste corpusul din nomenclator (nume_prestatie->cod_prestatie), apelat in pending_unmapped + _nemapate_pentru_submission inainte de bucla, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default OFF). Re-index doar la schimbarea semnaturii nomenclatorului. Corpusul se construieste din nomenclator (18 coduri largi), NU per-confirmare umana — sugestia embedding e similaritate denumire-prezentare vs. nume_prestatie RAR.


Problema

La ingestie (canal API si import web), o prestatie poate veni cu cod_op_service

  • denumire in loc de cod_prestatie RAR. Daca nu exista mapare, submission-ul intra in needs_mapping si asteapta confirmare umana. Service-urile reale au volume mari de denumiri particulare (masurat: 17.435 denumiri DISTINCTE in 4 CSV-uri de clienti reali — automotive 13.170, sigma 3.743, clever 1.668, south 875). Maparea manuala a acestora, prin editorul needs_mapping, e prohibitiva: zeci de mii de operatii × confirmare umana.

Nomenclatorul RAR are doar 18 coduri foarte largi (REPARATIE, INTRETINERE, REVIZIE PERIODICA, etc. — nomenclator_seed.py). Deci problema nu e potrivire de sinonime, ci clasificare a mii de operatii granulare in 18 categorii abstracte

  • detectare de „gunoi" (linii care nu sunt operatii: ITP CT 12 ABC, DISCOUNT MATERIALE 5%, MANOPERA, nr. inmatriculare).

Viziune (pivot 2026-06-28)

LLM-ul NU ruleaza la runtime. Rol unic: etichetator offline care construieste un set de date (denumire -> cod). La runtime ruleaza un clasificator local mic, fara API (similaritate / fuzzy / embeddings), „distilat" din etichetele LLM + maparile validate de oameni. Trei straturi:

  1. Etichetare offline (LLM, periodic): acopera denumirile cu cele mai multe aparitii (frecventa) si grupeaza denumirile asemanatoare ca sa eticheteze ieftin.
  2. Clasificator runtime (fara AI): exact -> fuzzy/substring -> similaritate semantica (embeddings) peste baza de cunostinte. Zero cost per cerere, ruleaza pe LXC.
  3. Baza de cunostinte PARTAJATA: maparile validate de oameni din TOATE conturile de service contribuie la clasificare (strat „gold" comun), peste etichetele LLM („silver" bootstrap). Munca de validare a unui service ajuta toate service-urile.

Viitor (nu acum): un LLM generativ local pe LXC. Pasul curent foloseste un model de embedding (nu generativ): mic, CPU, milisecunde/text.

Premise

  1. Volumul de denumiri distincte e finit si se schimba lent. Odata etichetate, 90%+ din traficul viitor sunt repetari ale acelorasi denumiri (service-ul refoloseste propriul vocabular). Lege Zipf: top 100 denumiri = 43.6% volum, top 500 = 67.7%, top 1000 = 76.2% (din 155.195 operatii totale).
  2. RAR accepta NUMAI coduri din nomenclator. Un cod necunoscut -> HTTP 500 (ORA-12899) + record PARTIAL FINALIZATA (terminal). Deci orice cod propus de un sistem automat TREBUIE validat fata de nomenclator inainte de enqueue (invariant existent in resolve_prestatii(..., valid_codes)).
  3. Maparea gresita are cost asimetric: un cod gresit trimis = FINALIZATA ireversibil la RAR. Deci pragul de auto-trimitere ramane conservator; incertul ramane needs_mapping cu om in bucla. Etichetele LLM NEVALIDATE = sugestie, nu auto-trimitere (vezi scara de incredere).
  4. Hardware LLM local generativ e prea lent acum (masurat: Ollama LXC 104 generativ 180-320s/op). Embeddings locale insa sunt rapide pe CPU si suficiente pentru similaritate la runtime.
  5. Datele nu sunt sensibile (confirmat utilizator): denumirile de operatii pot merge la API-uri cloud pentru etichetare. PII incidental (nr. inmatriculare/VIN) se face scrub inainte de trimitere (F3).

Masuratori

Bootstrap (anterior, Groq)

  • Groq llama-3.3-70b: 28ms/op, acord 94% cu heuristica pe cazuri clare, detectare gunoi excelenta (NUL). Abandonat ca furnizor: cap zilnic free atins + cheie expusa.

OpenRouter free — NVIDIA Nemotron (masurat 2026-06-28)

Furnizor nou pentru etichetare: cheie utilizator, modele GRATUITE, date ne-sensibile.

  • Capcane de cont (rezolvate): modelele free dau initial 404 No allowed providers din cauza unui allowlist de provideri pe cont (venice/together/fireworks/ atlas-cloud) — open-inference/google-ai-studio/nvidia erau excluse. Fix: eliminat restrictia in Settings -> Preferences + activat toggle-ul de privacy „free endpoints may publish/train". WAF: User-Agent Mozilla/5.0 obligatoriu.

  • Set fiabil = familia NVIDIA Nemotron. Restul modelelor sunt 429 (rate-limited upstream, partajat global: llama-3.3-70b, qwen3-next, gemma, hermes, dolphin) sau 404 (gpt-oss). Cap free tier ~50 cereri/zi fara credit.

  • Test ensemble pe top 120 dupa frecventa (46.4% din volum), 2026-06-28:

    Model ms/op parse-fail acord vs Groq (overlap)
    nemotron-3-super-120b 1463 0 100%
    nemotron-nano-9b-v2 1248 0 100%
    nemotron-3-ultra-550b 6450 0 100%

    Acord ensemble ponderat pe volum: 3/3 unanim = 87% volum, 2/3 = 13%, dezacord total = 0%. Din unanim: 7 NUL (gunoi), 100 coduri reale.

  • Decizie model: pastram super-120b + nano-9b; aruncam ultra-550b (4-5x mai lent, zero castig de acuratete). Caveat: ensemble din aceeasi familie NVIDIA -> acordul supraestimeaza increderea fata de un ensemble cross-family.

  • Dezacordurile (13%) sunt cazuri de granita taxonomica reala, nu zgomot: REGLAT DIRECTIE/FARURI (OE-2 intretinere vs OE-4 reglare), MANOPERA TINICHIGERIE (NUL vs OE-1), DEZECHIPAT usa/bara (pas de demontare), INLOCUIT FILTRU AER (OE-1 vs OE-3). Astea trebuie sa cada in needs_mapping.

Solutia

Stratul 1 — Etichetare offline (LLM, fara cod runtime)

Tool CLI (tools/mapare-llm/, stil tools/apikey). Etichetatorul OpenRouter (or_common.py + or_label.py) clasifica denumirile in cele 18 coduri RAR + NUL:

  1. Prioritizare pe FRECVENTA (NR), nu alfabetic. Etichetam intai denumirile cu cele mai multe aparitii (acopera cel mai mult volum per apel).
  2. Grupare pe similaritate inainte de etichetare. Denumirile aproape identice (REGLAT DIRECTIE / REGLAT DIRECTIA / REGLARE DIRECTIE) se grupeaza; LLM eticheteaza doar reprezentantul grupului, codul se propaga la tot grupul. Maximizeaza acoperirea per apel LLM (critic pe cap free de ~50 cereri/zi).
  3. Ensemble NVIDIA (super-120b + nano-9b): acord -> incredere mai mare; dezacord -> ramane pentru needs_mapping. Vot pe coduri, nu self-confidence.
  4. Scrub PII (regex nr. inmatriculare/VIN) inainte de trimitere (F3, exista).
  5. Output: dataset etichetat cu denumire, cod, sursa, confidence (provenienta). NUL marcat separat (ancore negative + supresie), NU se promoveaza la cod RAR.

Prompt cu reguli explicite (avarii grave DOAR la accident; vopsire = reparatie; ulei+filtru = revizie; gunoi -> NUL). Batch mare (cap free tier), retry/backoff pe 429, respecta Retry-After.

Stratul 2 — Clasificator runtime (FARA AI, fara API)

Pentru o denumire din prezentare (canal API sau import), in app/mapping.py:

  1. Exact in baza de cunostinte (operations_mapping + strat partajat) -> cod direct.
  2. Fuzzy/substring (operation_text_rules, rapidfuzz) — exista deja.
  3. Similaritate semantica (embeddings) — NOU: model multilingv mic (ex. intfloat/multilingual-e5-small sau paraphrase-multilingual-MiniLM), CPU. Vectorizam baza etichetata o data; la runtime vectorizam denumirea noua si luam cel mai apropiat vecin (sau top-k cu vot). Optional: clasificator scikit-learn (regresie logistica / kNN) antrenat pe (embedding -> cod) pentru generalizare dincolo de vecinul exact. „Antrenarea pe datele de test" = acest pas, secunde, ruleaza oriunde.
  4. Cod propus -> validat OBLIGATORIU valid_codes (garda ORA-12899). Peste pragul de incredere -> conform scarii; altfel needs_mapping.

Decizie de gazduire runtime: ramane deschisa pentru reviziile plan (in-proces in gateway vs microserviciu pe LXC/Flowise). Default propus: in-proces (cel mai simplu).

Stratul 3 — Baza de cunostinte PARTAJATA cross-account

Schimbare fata de versiunea anterioara (care izola corpusul per cont):

  • Strat GOLD partajat: maparile validate de oameni (din needs_mapping, in ORICE cont) intra intr-un store partajat denumire_normalizata -> cod. Astfel validarea facuta de un service ridica increderea pentru toate. Cheia = denumire normalizata (scrub PII, lower, strip), nu textul brut.
  • Strat SILVER: etichetele LLM (bootstrap) — sugestii, NU auto-trimitere.
  • Override per-cont: daca un cont mapeaza explicit o denumire la alt cod decat cel partajat (conflict legitim de vocabular), override-ul contului castiga pentru acel cont. Conflictele inter-cont se rezolva cu provenienta + (optional) majoritate.

Confirmarile umane curg organic prin folosirea normala a editorului needs_mapping — FARA sesiune separata de adjudecare manuala (cerinta utilizator).

Scara de incredere (runtime, per operatie din prezentare)

Treapta Sursa Actiune Frictiune
Certa exact in stratul GOLD (validat de om, orice cont) sau override cont auto-trimite zero
Inalta embedding NN cu similaritate FOARTE inalta la o mapare GOLD + ensemble LLM unanim auto-trimite (prag calibrat) zero
Medie LLM silver / similaritate medie needs_mapping cu sugestie pre-completata -> 1 click minima
Joasa similaritate slaba / coduri apropiate needs_mapping manual normala
NUL non-operatie (ITP, discount, nr. inmatriculare) marcat „nu e operatie", suprimat

Invariant F1 (pastrat): o eticheta pur-LLM NEVALIDATA nu auto-trimite singura; auto-send cere ori GOLD (validat de om), ori treapta „inalta" calibrata. Tensiunea centrala (utilizatorul se bazeaza pe LLM, dar FINALIZATA e ireversibil) = intrebarea cheie pentru reviziile plan: unde fix se aseaza bara treptei „inalta".

Integrare

  • Stratul 1: tool CLI offline tools/mapare-llm/ (exista: or_common.py, or_modeltest.py; de adaugat or_label.py cu grupare + propagare).
  • Stratul 2: similaritate embeddings in app/mapping.py (enrich_suggestions -> suggest_nearest), apelata in pending_unmapped / _nemapate_pentru_submission pentru sugestia din editor. Corpusul se construieste din nomenclator via ensure_embeddings_corpus (gated pe AUTOPASS_EMBEDDINGS_ENABLED, default off): lazy-load model fastembed/ONNX (~230MB) la prima cerere /mapari cand flagul e activ, re-index doar la schimbarea nomenclatorului (semnatura). Off -> no-op (cade pe GOLD/SILVER + fuzzy). SUGGESTION-ONLY: NU intra in resolve_prestatii/enqueue (#13).
  • Stratul 3: store partajat (tabela noua shared_mappings sau coloana de scope pe operations_mapping), seed la confirmare umana; override per-cont.
  • Validare valid_codes pe tot lantul (exista).

Non-obiective

  • Nu inlocuim confirmarea umana pentru cazuri incerte.
  • Nu trimitem automat coduri sub prag / etichete LLM nevalidate.
  • Nu adaugam dependenta cloud la RUNTIME (LLM doar offline pentru etichetare).
  • Nu antrenam un LLM generativ local acum (viitor).

Riscuri

  • Etichete LLM gresite tratate ca adevar daca scapa garda F1 (seed direct in GOLD).
  • Ensemble aceeasi familie (NVIDIA) -> acord corelat-gresit; supraestimare incredere.
  • Strat partajat cross-account: o denumire poate insemna lucruri diferite la service-uri diferite -> conflict; mitigat prin override per-cont + provenienta.
  • Drift: denumiri noi neacoperite; embeddings ajuta dar nu elimina.
  • Free tier OpenRouter flaky (429/404, cap 50/zi) -> etichetarea bulk e lenta; e offline, deci tolerabil, dar nu pe calea critica de productie.
  • Model embedding ales: calitate pe limba romana de verificat empiric.

Decision Audit Trail

# Faza Decizie Clasificare Principiu Rationament Respins
1 Eng Seed-ul NU intra direct in stratul auto-send; etichetele LLM = strat SILVER (sugestii). Auto-send cere GOLD (validat de om) sau treapta inalta calibrata TASTE (critic) P1, P5 resolve_prestatii->queued direct => seed auto = AUTO-TRIMITERE ghiciri la FINALIZATA ireversibil (Premisa 3) seed direct in auto-send
2 Eng Seeder = INSERT OR IGNORE / refuza overwrite pe randuri validate de om MECHANICAL P1 re-rularea ar clobber-ui maparile umane cu ghiciri LLM ON CONFLICT UPDATE
3 Eng Scrub regex (nr. inmatriculare/VIN) inainte de trimitere la LLM TASTE P1 gunoiul contine ITP CT 12 ABC = nr. inmatriculare = PII trimitere text brut
4 Eng NUL = ancore negative in corpus + lista supresie MECHANICAL P1 altfel gunoiul recurent reintra mereu in needs_mapping si fuzzy ii da cod gresit exclude NUL
5 Eng Coloana source/confidence (provenienta) pe baza de cunostinte MECHANICAL P1 audit + rollback batch model prost + safe re-seed fara provenienta
6 Eng Runtime = embeddings + clasificator mic (sklearn), NU LLM generativ TASTE P3, P5 LLM generativ local prea lent (Premisa 4); embeddings CPU suficiente + rapide LLM la runtime
8 Eng SUPERSEDED: corpus partajat cross-account (strat GOLD comun), NU per-cont izolat; override per-cont pe conflict TASTE P1, P2 cerinta utilizator: validarea unui service ajuta toate; muncă compusa. Conflictul de vocabular rezolvat prin override + provenienta (vechi: corpus strict per-cont)
9 Eng Furnizor etichetare = OpenRouter free, ensemble NVIDIA (super-120b + nano-9b); aruncat ultra-550b MECHANICAL P3 masurat 2026-06-28: doar NVIDIA routeaza fiabil; ultra 4-5x lent fara castig Groq (cap atins) / ultra
10 Eng Etichetare prioritizata pe frecventa + grupare pe similaritate (eticheteaza reprezentant, propaga) MECHANICAL P2 acopera mult mai mult volum per apel; critic pe cap free ~50/zi etichetare alfabetica
11 CEO F-A: cross-account GOLD = suggestion-only, nu auto-send cross-cont; doar GOLD PROPRIU (validat de omul contului) auto-trimite GATE (user) P1 prima-intalnire cross-cont = FINALIZATA gresit ireversibil; override per-cont e post-hoc cross-account auto-send (PRD scris)
12 CEO Premisa 1 (90% repeat) validata cu temporal holdout INAINTE de build GATE (user) P1 concentrare-in-corpus != future-repeats-past; ieftin de verificat build pe asumtie
13 Eng Strat SILVER in TABELA SEPARATA (mapping_suggestions), citita DOAR de suggest_codes/pending_unmapped; NICIODATA de load_mapping/resolve_prestatii MECHANICAL P5,P1 scope-column pe operations_mapping auto-trimite silver (8+ call-site); separare structurala scope column pe operations_mapping
14 Eng Shared store = tabela noua pe cheia denumire_normalizata (NU coloana pe operations_mapping: cheie diferita cod_op_service + UNIQUE) MECHANICAL P5 spatii de chei diferite; conflict UNIQUE scope column
15 Eng Embeddings Layer 2 RAMANE in v1 (utilizatorul a respins amanarea la gate; mentine Decision #6). Recomandarea ambelor voci era amanare la v2 USER CHALLENGE -> override user P3,P5 voci: 2GB pe ipoteza nemasurata, 18 clase acoperite de exact+fuzzy. User: vrea castig pe coada RO + control infra (amanare v2)
16 Eng Embeddings = IN-PROCES fastembed/ONNX (~230MB pe disc, ONNX quantizat, fara torch; estimarea initiala de ~50MB a fost gresita — modelul multilingv paraphrase-multilingual-MiniLM-L12-v2 are ~231MB chiar quantizat), in procesul API; model BAKED in imaginea Docker (sau volum cache) -> ZERO dependenta de retea la runtime. NU serviciu separat. Lazy-load la pornire, nu pe /healthz; worker NU incarca modelul TASTE (user, revizuit) P5,P3 user: "embedding in interiorul aplicatiei, nu mai depind de alte resurse". Mai simplu + mai robust decat serviciu HTTP; ruleaza identic local si in Docker/LXC serviciu separat Ollama/HTTP (revocat) / sentence-transformers+torch
16b Eng Degradare gratioasa: daca modelul nu se incarca -> ingestia NU se blocheaza, NU auto-trimite; cade pe exact+fuzzy, incertul -> needs_mapping. Embeddings raman doar SUGESTIE (consecinta F-A), in afara verdictului de enqueue (invariant dry-run/commit, Eng-F8) MECHANICAL P1 esecul incarcarii modelului nu trebuie sa rupa coada; fara retea la runtime block ingest pe model lipsa
17 Eng Tier "Inalta" auto-send STERS din v1; GOLD auto-trimite, restul (silver/NN/LLM-unanim) = needs_mapping 1-click MECHANICAL P1 fara ground-truth; unanimitate same-family = eroare corelata, nu validitate tier Inalta pe unanimitate LLM
18 Eng sklearn classifier scos din v1 MECHANICAL P5 al doilea artefact antrenabil + pickle, castig marginal pe 18 clase sklearn in v1
19 Eng Set held-out etichetat de OM = BLOCANT pt orice tier auto-send peste GOLD propriu MECHANICAL P1 "antrenare pe test" invalideaza orice precizie raportata prag din etichete LLM
20 CEO OpenRouter: free OK pt bootstrap unic; credit mic ($5-20) pt drift steady-state (nu arhitecta pe cap 50/zi) TASTE P3 juggling free > cost credit in timp eng totul pe free tier

Istoric review (pre-pivot)

Versiunea anterioara a trecut prin /autoplan (mod SELECTIVE EXPANSION, subagent-only, Codex indisponibil). Constatari portante atunci: F1 CRITIC (seed=auto-send), F2/F3/F4 HIGH (idempotenta seed, scrub PII, ancore NUL), F5/F6/F7/F8 MEDIUM. Acele decizii sunt incorporate in Decision Audit Trail de mai sus. Pivotul 2026-06-28 (LLM offline-only + runtime embeddings + strat partajat cross-account) NECESITA o noua rulare de review (CEO / Eng / Design) — de aceea sectiunea GSTACK REVIEW REPORT e goala momentan si se completeaza la urmatoarea rulare.

GSTACK REVIEW REPORT

Rulat prin /autoplan 2026-06-28 (SELECTIVE EXPANSION). Voci: Claude subagent independent (CEO + Eng) + analiza orchestrator pe cod. Codex INDISPONIBIL (usage limit, reset 18 iul) -> mod single-reviewer. UI scope: NU (editorul needs_mapping exista deja). DX scope: borderline (CLI intern operator) -> Phase 3.5 sarit, considerente DX in Eng.

Decizii GATE (confirmate de utilizator)

  • F-A: cross-account = suggestion-only. Maparile validate de orice cont PRE-COMPLETEAZA editorul needs_mapping (1-click) dar NU auto-trimit. Doar exact-match pe GOLD-ul PROPRIU (validat de omul contului) auto-trimite. Elimina riscul de FINALIZATA gresit cross-tenant.
  • Premisa 1 validata cu temporal holdout INAINTE de build (corpus primele N luni/client -> hit-rate exact pe lunile urmatoare). Ieftin, datele exista.

Consens CEO (single-reviewer; Codex N/A)

Dimensiune Claude Verdict
Premise valide NO (P1, P5) flagged
Problema corecta PARTIAL flagged
Scope calibrat NO (over-eng) flagged
Alternative explorate NO flagged
Riscuri piata PARTIAL flagged
Traiectorie 6 luni AT RISK flagged

Consens Eng (single-reviewer; Codex N/A)

Dimensiune Claude Verdict
Arhitectura PARTIAL flagged
Acoperire teste NO flagged
Footprint/perf NO (2GB torch) flagged
Siguranta F1 INTENT-OK flagged
Cai de eroare PARTIAL flagged
Risc deploy NO flagged

Constatari portante (severitate)

  • F-A / Eng-F1 (CRITIC): auto-send DOAR pe GOLD. Strat SILVER in TABELA SEPARATA (mapping_suggestions), citita doar de suggest_codes/pending_unmapped, NICIODATA de load_mapping/resolve_prestatii. auto_send col e moarta (mapping.py:436); singura cale spre queued (auto-send, mapping.py:414) trebuie sa fie GOLD. Separarea = structurala.
  • F-B (CRITIC): toate masuratorile sunt ACORD (100% vs Groq, 87% unanim), nu ACURATETE vs ground-truth. Same-family NVIDIA = eroare corelata. Niciun tier auto-send peste GOLD pana nu exista set held-out etichetat de OM (esantion aleator stratificat).
  • Eng-F2 (HIGH): shared store pe cheia denumire_normalizata (NU cod_op_service) -> tabela noua obligatorie; precedenta override pinnata: account override > account GOLD > shared GOLD > text rules > unmapped.
  • F-C / Eng-F3 (HIGH): embeddings Layer 2 = over-engineering pe 18 clase Zipf-head. AMANAT v2. Daca se construieste: fastembed/ONNX (~230MB pe disc, ONNX quantizat; estimarea initiala de ~50MB a fost gresita), API-process-only, lazy, nu pe /healthz. NU in resolve_prestatii (altfel worker-ul ar avea nevoie de torch).
  • Eng-F4 (HIGH): tier "Inalta" sters din v1 (consecinta F-A + lipsa ground-truth).
  • F-D (HIGH): Premisa 1 nevalidata temporal -> gate (rezolvat).
  • F-E (HIGH): fara metrica de succes/baseline/kill-criterion -> de instrumentat (% linii auto-rezolvate la rata cod-gresit < 0.X%).
  • MEDIUM: NUL short-circuit inainte de suggest_codes + structura separata (Eng-F6); OpenRouter 429 resumabil + group radius conservator + provenance (Eng-F7); divergenta dry-run/commit (Eng-F8); credit mic vs free-tier (F-F); omisiune silentioasa NUL (F-G); calitate embedding RO de verificat (F-H); versionare cheie normalizare; drop sklearn v1.

Teme cross-faza (semnalate independent in ambele faze)

  1. Auto-send DOAR GOLD; silver/embeddings/unanimitate-LLM = sugestie (CEO F-A/F-B + Eng F1/F4).
  2. Embeddings over-engineered pe 18 clase; amana sau fastembed (CEO F-C + Eng F3).
  3. Fara set ground-truth; masoara precizia inainte de orice tier auto-send (CEO F-B/F-E + Eng F4).

NU in scope (amanat)

  • sklearn classifier peste embeddings (v2; embeddings raman doar NN suggestion in v1).
  • Orice tier auto-send peste exact-match GOLD propriu (pana la set held-out).
  • LLM generativ local la runtime (deja non-obiectiv PRD).
  • Tier "Inalta" calibrat (re-introdus doar cu eval cross-family + ground-truth).

Embeddings Layer 2 RAMANE in v1 (override user la gate), IN-PROCES (fastembed/ONNX, model baked in imagine), DOAR sugestie, cu fallback gratios pe exact+fuzzy daca modelul nu incarca. Zero dependenta de retea la runtime. Vezi audit #15/#16/#16b.

Ce exista deja (de refolosit, nu rescris)

  • resolve_prestatii / classify_prezentare / reresolve_account (mapping.py): precedenta cod direct > exact mapping > text rules > unmapped; garda valid_codes (ORA-12899).
  • suggest_codes (rapidfuzz token_sort) + pending_unmapped: punct de injectie sugestii.
  • operation_text_rules (substring) + operations_mapping (GOLD per-cont).
  • tools/mapare-llm/ (or_common.py, or_modeltest.py) + pattern *-partial.json resumabil.
  • Scrub PII (F3), normalize_for_match, seed nomenclator (18 coduri).

Artefact test plan

~/.gstack/projects/romfast-rar-autopass/mmarius-main-test-plan-20260628.md (test F1-regression CRITIC + precedenta override + NUL + idempotenta seed + held-out eval).

Stare review

Aprobat prin /autoplan (vezi Decision Audit Trail #11-20 + #16b). Plan livrabil: v1 = Layer 1 (etichetare offline) + Layer 2 (embeddings ca SERVICIU SEPARAT configurabil, doar sugestie, fallback gratios) + Layer 3 (GOLD propriu auto-send + shared suggestion-only)

  • exact/fuzzy existent + temporal holdout + metrica de succes + set held-out (blocant pt orice auto-send peste GOLD). v2 = sklearn classifier (dupa masurare).