Files
rar-autopass/docs/prd/prd-5.18-corpus-knn-exemple-etichetate.md
Claude Agent 12021eb269 feat(5.18): VERIFY+CLOSE — US-007 badge sursa + fix findings code-review
VERIFY PASS pe corpus k-NN exemple etichetate (seed real 17181 Haiku, comis
in 756f777): suita 1392 passed, 1 deselected (live); smoke init_db seeder
(17181/NUL=2200/idempotent); toate codurile in nomenclator.

US-007 (cerere user la CLOSE) — badge sursa pe sugestia fuzzy din editor:
- _mapari.html: chip confirmat (GOLD) / similar (SILVER+k-NN) / non-operatie (NUL)
- base.html: .sugg-sursa--{confirmat,similar,nul} pe tokeni de tema (color-mix)
- routes.py: cheia `nul` adaugata in surse_sugestie default (finding cross-file)
- tests/test_web_badge_sursa.py: gold/silver/nul/fara-sursa (4 teste)
- E2E render live verificat in serverul real (/_fragments/mapari)

CLOSE /code-review high (main..HEAD, 3 finder x 8 unghiuri) — runtime curat,
invariant #13 intact; 3 findings low/cosmetic REPARATE + lock-uite:
- shared_store.seed_suggestions: cod whitespace -> NULL (era ''), + test lock
- genereaza_seed.py: with open(...) in loc de open().read() (FD leak tool offline)
- embeddings.py: docstring-uri aliniate la [{cod, is_nul, similaritate}]

ROADMAP: 5.18 LIVRAT. PRD: raport VERIFY/CLOSE scris.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 07:29:14 +00:00

30 KiB

PRD 5.18 — Corpus k-NN din exemple reale etichetate (mapare operatii service)

Stare: aprobat + revizuit /autoplan (2026-06-28; intrebari deschise rezolvate de user — vezi §5 Decizii; cerinte user D4/D5 + 10 constatari Eng incorporate — vezi GSTACK REVIEW REPORT la final)

Proces: docs/ROADMAP.md §5. Contract RAR: docs/api-rar-contract.md. Construieste peste infrastructura 5.14 (straturi GOLD/SILVER/embeddings, app/embeddings.py, app/shared_store.py, mapping_suggestions). NU re-deschide deciziile 5.14 (#11-#19); le foloseste.

0. Context si motivatie (de ce acest PRD)

5.14 a livrat embeddings in-proces, dar corpusul indexat = cele 18 denumiri RAR generice din nomenclator (nume_prestatie -> cod_prestatie). O operatie reala ("inlocuit lubrifiant la propulsor") se potriveste semantic slab cu etichete generice scurte ("INTRETINERE", "REPARATIE"). In plus, stratul SILVER (mapping_suggestions) e populat DOAR in teste — in productie e gol, deci nu produce nicio sugestie (LLM-ul nu e chemat la runtime).

Acest PRD muta corpusul de la cele 18 categorii la operatiile reale etichetate (k-NN peste exemple): o operatie noua se potriveste semantic cu o operatie deja vazuta si MOSTENESTE codul ei.

Masuratori care justifica directia (vezi memorie test-precizie-knn-embeddings, rulat 2026-06-28):

  • k-NN peste exemple etichetate: 94.3% acord cu LLM pe operatii distincte (baseline "mereu OE-1" = 86.2%).
  • Acoperire IEFTINA: pe volumul real total (155.195 aparitii, 17.181 operatii distincte): 148 operatii = 50% volum, 1.380 = 80%, 4.368 = 90%, 9.422 = 95%.
  • Punct slab masurat: NUL recall 64% (ITP/discount/plata scapa ca OE-1) -> de aici pre-filtrul (US-001).
  • Etichetarea offline cu Qwen3-4B local (LM Studio, GPU RX 6600M) + prompt procedural in 3 pasi: 91% pe batch greu, 20/20 pe batch de validare, ambele NUL prinse. Debit ~1.5-2h pentru ~13.5k operatii.

1. Obiectiv

Inlocuieste corpusul embeddings (18 categorii generice) cu corpusul de operatii reale etichetate (exemplu -> cod RAR), populat dintr-un seed comis in repo, plus un pre-filtru determinist pentru non-operatii (NUL). Rezultat: sugestii de mapare semnificativ mai precise in editor, fara LLM la runtime.

Pasul 1 (bootstrap offline, fundatia intregului PRD) = etichetare cu LLM via LM Studio local. Tot restul (seeder, corpus embeddings, enrich) consuma artefactul produs aici. Pasul are doua garantii non-negociabile:

  1. LM Studio = backend implicit aprobat pentru rularea v1 (Qwen3-4B local, GPU RX 6600M, json_schema strict — json_object e respins de LM Studio). Groq/OpenRouter raman fallback-uri interschimbabile, dar NU sunt calea aprobata pentru bootstrap-ul v1 (vezi D4).
  2. Dedup INAINTE de orice apel LLM. Cele 4 fisiere (docs/operatii-service/*.csv) contin 19.456 randuri brute -> 17.181 operatii distincte dupa normalize_for_match (gain de doar 254 fata de dedup exact-string, pentru ca datele sunt deja majuscule, fara diacritice — normalize_for_match colapseaza spatii + scoate diacritice, NU scoate punctuatie). Din cele 17.181, 3.662 sunt deja etichetate (in spatiu normalizat) in labels-groq-partial.json. Trimitem la LLM EXACT cele 13.519 operatii distincte ne-etichetate, niciodata un duplicat normalizat, o cheie normalizata vida sau o operatie deja etichetata (vezi D5). Economie: 31% mai putine apeluri vs randuri brute. (Castigul real al pipeline-ului nu e atat normalizarea — 254 chei — cat reuse-ul etichetelor existente + agregarea frecventei; motivul principal pentru spatiul normalizat e consistenta end-to-end cu cheia DB/k-NN, vezi F1/F3 din review.)

2. Non-Goals (anti scope-creep)

  • NU auto-send peste GOLD propriu. Toate sursele (k-NN, exact, NUL pre-filtru) raman SUGGESTION-ONLY, niciodata in resolve_prestatii/load_mapping (invariant #13, #11 din 5.14). Singura cale spre queued ramane operations_mapping (GOLD propriu confirmat de om).
  • NU LLM la runtime. Etichetarea LLM se face O SINGURA DATA, offline; runtime = doar embeddings + exact + reguli.
  • NU validare temporala / re-etichetare automata. Seedul e static; reimprospatarea e un re-run manual al tool-ului.
  • NU schimbare UI majora. Editorul (_mapari.html) consuma deja sugestie_principala; doar sursa se schimba. (Un badge optional de sursa = US-007, jos.)
  • NU eshantion etichetat de om in acest PRD (doar mentionat la Riscuri ca recomandare — Decision #19).

3. Stories atomice

Fiecare story = cea mai mica unitate care lasa sistemul functional. Refoloseste mapping_suggestions (SILVER) ca tabela-corpus (are deja: denumire_normalizata, cod_prestatie, is_nul, source, confidence) — populata acum si in productie, nu doar in teste.

US-001: Pre-filtru determinist non-operatii (NUL)

Ca operator vreau ca gunoiul evident (ITP, plata, discount, nr. inmatriculare, tractare) sa fie marcat NUL inainte de k-NN pentru ca masuratoarea arata recall NUL doar 64% (scapa ca OE-1).

  • Depinde de: —
  • Fisiere: app/mapping.py (functie noua prefiltru_nul(denumire) -> bool), tests/test_prefiltru_nul.py (~2 fisiere)
  • Test intai (RED): tests/test_prefiltru_nul.pytest_itp_e_nul, test_plata_discount_nul, test_nr_inmatriculare_nul, test_operatie_reala_nu_e_nul
  • Acceptance criteria:
    • Reguli text/regex deterministe (ITP, ACHITAT/PLATA, DISCOUNT/REDUCERE, NR INMATRICULARE + pattern placuta, TRACTARE, TAXA)
    • prefiltru_nul("13 X ITP") / ("DISCOUNT FIDELITATE 10%") -> True; ("INLOCUIT PLACUTE FRANA") -> False
    • Zero fals-pozitiv pe un set de 20 operatii reale (din docs/operatii-service)
    • python3 -m pytest tests/test_prefiltru_nul.py -q verde
  • Verificare E2E: — (pur backend, acoperit de teste)

US-002: Etichetator offline multi-backend cu prompt procedural

Ca dezvoltator vreau un tool care eticheteaza operatii->coduri RAR via LM Studio local / Groq / OpenRouter, cu prompt procedural in 3 pasi si json_schema strict pentru ca LM Studio respinge json_object si promptul nou ridica precizia (91% vs 80%).

  • Depinde de: —
  • Fisiere: tools/mapare-llm/eticheteaza.py (NOU, backend-uri interschimbabile), tests/test_eticheteaza_tool.py (mock HTTP) (~2 fisiere)
  • Test intai (RED): tests/test_eticheteaza_tool.pytest_construieste_prompt_3pasi, test_parseaza_json_schema, test_backend_selectabil_env, test_scrub_pii_inainte_de_request
  • Acceptance criteria:
    • Backend selectabil prin env (ETICHETARE_BACKEND=lmstudio|groq|openrouter, endpoint+model configurabile); default = lmstudio (backend-ul aprobat pentru bootstrap v1, D4). Groq/OpenRouter = fallback.
    • response_format = json_schema strict cu envelope complet {"type":"json_schema","json_schema":{"name":...,"strict":true,"schema":{...}}} (NU {"type":"json_object"} ca or_common.py:57/label_common.py:24); cod = enum peste cele 19 ALL_LABELS (18 + NUL), cod invalid/lipsa -> ? (F8 din review). Etichetatorul nou NU reutilizeaza request-ul vechi, doar promptul/codurile/scrub-ul.
    • Dezactiveaza explicit "thinking"-ul Qwen3 (/no_think sau reasoning off) — altfel modelul emite <think> si umfla tokeni/latenta sub structured output strict (F8).
    • Garda de truncare: daca raspunsul are mai putine iteme decat batch-ul sau JSON invalid -> log + marcheaza ? pe pozitiile lipsa, NU le ascunde tacit (la batch 40 + prompt 3 pasi, n_ctx=4096 e stramt — F8).
    • Promptul = procedura 3 pasi + ancore (mapare parte caroserie->OE-C etc.), versionat in fisier
    • Scrub PII (nr. inmatriculare, VIN) inainte de orice request (refoloseste or_common.scrub, #3)
    • Setari conservatoare documentate in tool (batch 32-40, n_parallel=1, n_ctx=4096) — vezi Riscuri
    • python3 -m pytest tests/test_eticheteaza_tool.py -q verde (fara retea reala)
  • Verificare E2E: rulare manuala 1 batch pe LM Studio local (http://<tailscale>:1234), confirmare JSON valid

US-003: Generare seed etichetat in faze pe frecventa

Ca dezvoltator vreau sa generez un fisier seed operatii-etichetate.json (operatie->cod) pornind de la operatiile existente + cele deja etichetate, in ordinea frecventei pentru ca 1.380 operatii prind 80% din volum.

  • Depinde de: US-002
  • Fisiere: tools/mapare-llm/genereaza_seed.py (NOU), app/data/operatii-etichetate.json (artefact comis), tests/test_genereaza_seed.py (~3 fisiere)
  • Test intai (RED): tests/test_genereaza_seed.pytest_dedup_normalizat, test_zero_duplicate_trimis_la_llm, test_rerun_zero_apeluri_llm, test_reuse_conflict_determinist, test_skip_cheie_normalizata_vida, test_reuse_in_spatiu_normalizat, test_ordine_pe_frecventa, test_format_seed_valid
  • Pipeline dedup (ordinea e obligatorie, INAINTE de orice apel LLM):
    1. Agrega cele 4 CSV-uri -> pentru fiecare rand (denumire, NR). Parseaza NR tolerant (skip rand pe NR ne-numeric, nu zero-weight — F9).
    2. cheie = normalize_for_match(denumire) — ACEEASI functie ca DB/k-NN (app/mapping.py:40), NU .strip() exact. Arunca randurile cu cheie == "" (gunoi gen "...", " ") inainte de dedup — altfel se bat pe slotul UNIQUE gol (F6).
    3. Dedup pe cheie: un singur reprezentant per cheie, freq = suma NR pe toate aparitiile/fisierele.
    4. Construieste harta cheie_normalizata -> cod (NU doar un set) din TOATE sursele de etichete deja existente: labels-groq-partial.json (cheiat pe text BRUT) PLUS seedul comis anterior operatii-etichetate.json (cheiat normalizat). Reuse + scaderea se fac in spatiu normalizat. Rezolvare conflict determinista cand acelasi cheie are coduri diferite pe variante raw (masurat: 1 azi — CURATAT CATALIZATOR OE-2 vs OE-1): castiga varianta cu freq (suma NR) maxima, tie-break pe cod sortat (F3).
    5. de_etichetat = {cheie in corpus} - {cheie in harta etichete}. Lista (distincta, ne-etichetata, sortata desc pe freq) = SINGURUL input catre LLM.
  • Acceptance criteria:
    • test_zero_duplicate_trimis_la_llm (within-run): backend LLM mock care inregistreaza fiecare denumire primita; input cu duplicate intentionate (spatii/case + cross-file) -> mock-ul nu vede NICIODATA doua chei normalizate egale, nicio cheie deja etichetata, nicio cheie vida.
    • test_rerun_zero_apeluri_llm (cross-run, criteriul real de idempotenta, F2/F7): ruleaza tool-ul de doua ori cu acelasi input; a doua rulare consuma seedul comis ca cache -> 0 apeluri LLM, seed identic byte-cu-byte.
    • test_reuse_conflict_determinist (F3/F7): doua variante raw ale aceleiasi chei cu coduri diferite -> codul ales e determinist (freq-max, tie-break cod).
    • Dedup pe normalize_for_match (colapseaza spatii + diacritice, NU punctuatie; gain real ~254 chei vs exact-string — valoarea principala e consistenta cu cheia DB/k-NN, nu volumul); NU reutiliza or_common.corpus_by_freq() ca atare (dedup exact-string).
    • Eticheteaza DOAR ce lipseste, in ordine descrescatoare de frecventa, cu --target-volum 0.9 (oprire la prag) sau --all
    • Seed format [{denumire, denumire_normalizata, cod, is_nul, source, confidence}], UTF-8, comis in repo; denumire_normalizata unica + ne-vida in seed (oglindeste UNIQUE din mapping_suggestions; test_format_seed_valid asserta non-empty)
    • python3 -m pytest tests/test_genereaza_seed.py -q verde
  • Verificare E2E: rulare --target-volum 0.5 pe date reale -> ~150 etichete noi, fisier valid; log-ul tool-ului raporteaza explicit "{brute} randuri -> {distincte} dupa normalizare -> {de_etichetat} trimise la LLM"

US-004: Seeder corpus etichetat in DB (mapping_suggestions)

Ca sistem vreau sa incarc seedul etichetat in mapping_suggestions la init (INSERT OR IGNORE) pentru ca SILVER e gol in productie si trebuie populat ca sa dea sugestii exact-match + corpus k-NN.

  • Depinde de: US-003
  • Fisiere: app/operatii_seed.py (NOU, dupa modelul nomenclator_seed.py), app/db.py (apel la init), tests/test_operatii_seed.py (~3 fisiere)
  • Test intai (RED): tests/test_operatii_seed.pytest_seed_populeaza_mapping_suggestions, test_insert_or_ignore_nu_clobber_uman, test_is_nul_din_seed, test_idempotent_la_reinit
  • Acceptance criteria:
    • La init_db, daca seedul exista si tabela permite, INSERT OR IGNORE randurile (idempotenta re-seed: nu dubla / nu clobber un rand seedat sau de embedding deja prezent). NB (F10): confirmarile UMANE stau in shared_mappings (record_human_validation), NU in mapping_suggestions — deci INSERT OR IGNORE pastreaza TACIT codul LLM vechi la re-seed; daca vrei refresh pe coduri LLM invechite, e decizie explicita upsert-vs-ignore (v1 = ignore)
    • is_nul=1 -> cod_prestatie=NULL (respecta CHECK-ul existent); source='llm_seed', confidence din seed
    • Idempotent: a doua initializare nu dubleaza si nu modifica randuri existente
    • python3 -m pytest tests/test_operatii_seed.py -q verde
  • Verificare E2E: pornire app pe DB gol -> SELECT count(*) FROM mapping_suggestions > 0

US-005: Embeddings indexeaza corpusul etichetat (nu nomenclatorul)

Ca sistem vreau ca ensure_embeddings_corpus sa indexeze operatiile etichetate (denumire->cod, cu is_nul) pentru ca k-NN peste exemple reale e net mai precis decat peste 18 categorii generice.

  • Depinde de: US-004
  • Fisiere: app/mapping.py (ensure_embeddings_corpus schimba sursa), app/embeddings.py (suggest_nearest intoarce si is_nul), tests/test_embeddings_corpus_etichetat.py (~3 fisiere)
  • Test intai (RED): tests/test_embeddings_corpus_etichetat.pytest_corpus_din_mapping_suggestions, test_suggest_nearest_intoarce_is_nul, test_semnatura_corpus_pe_seed, test_degradare_gratioasa_pastrata
  • Acceptance criteria:
    • Corpusul = mapping_suggestions (denumire_normalizata -> cod, is_nul), NU nomenclator_rar
    • Simetrie corpus/query (F1, HIGH): corpusul e text denumire_normalizata; deci enrich_suggestions trebuie sa interogheze suggest_nearest(normalize_for_match(denumire), ...), NU denumire brut. Altfel corpus normalizat vs query brut degradeaza cosine si NU e configul sub care s-a masurat 94.3%. test_query_normalizat_ca_si_corpusul o asserta.
    • suggest_nearest intoarce [{cod, is_nul, similaritate}]; un vecin NUL -> semnal de supresie, nu cod
    • Re-index doar la schimbarea semnaturii corpusului (cache pastrat, #16b degradare gratioasa neschimbata)
    • Gated pe AUTOPASS_EMBEDDINGS_ENABLED (acum default True — vezi 5.14 CLOSE); off in teste (conftest)
    • python3 -m pytest tests/test_embeddings_corpus_etichetat.py -q verde
  • Verificare E2E: cu flag on + seed incarcat, suggest_nearest("schimbat uleiul motor") -> cod revizie/intretinere real

US-006: enrich_suggestions = pre-filtru NUL + k-NN pe corpus etichetat

Ca operator vreau ca editorul sa imbine pre-filtrul NUL, exact-match si k-NN semantic in ordinea de precedenta corecta pentru ca vreau sugestia cea mai buna fara junk.

  • Depinde de: US-001, US-005
  • Fisiere: app/mapping.py (enrich_suggestions), tests/test_enrich_corpus_etichetat.py (~2 fisiere)
  • Test intai (RED): tests/test_enrich_corpus_etichetat.pytest_prefiltru_nul_supreseaza_inainte_de_knn, test_precedenta_gold_exact_embedding, test_prag_similaritate, test_abtinere_sub_prag
  • Acceptance criteria:
    • Ordine: pre-filtru NUL -> daca NUL, fara sugestie de cod (marcat non-operatie); altfel GOLD partajat > exact (SILVER) > k-NN embeddings
    • k-NN sub EMB_MIN_SIMILARITATE -> abtinere (embedding=None), nu sugestie incerta
    • Vecin k-NN cu is_nul=1 -> tratat ca supresie, nu cod (consecventa cu pre-filtrul)
    • Invariant #13 pastrat: nimic din asta nu intra in resolve_prestatii/load_mapping (test de regresie)
    • python3 -m pytest tests/test_enrich_corpus_etichetat.py -q verde + suita 5.14 (test_mapare_integrare_l14.py) ramane verde
  • Verificare E2E: browser HTMX pe /_fragments/mapari — operatie parafraza primeste cod corect pre-selectat din k-NN

US-007 (optional): Badge sursa sugestie in editor

Ca operator vreau sa vad de unde vine sugestia (confirmat de om / exemplu similar / non-operatie) pentru ca acum nu pot distinge sursa si nu stiu cata incredere sa am.

  • Depinde de: US-006
  • Fisiere: app/web/templates/_mapari.html, tests/test_web_badge_sursa.py (~2 fisiere)
  • Test intai (RED): tests/test_web_badge_sursa.pytest_badge_gold, test_badge_embedding, test_badge_nul
  • Acceptance criteria:
    • Chip mic langa sugestie: "confirmat" (gold), "similar" (embedding/silver), "non-operatie" (NUL)
    • Fara sursa -> fara chip; nu rupe layoutul 5.15/5.16
    • python3 -m pytest tests/test_web_badge_sursa.py -q verde
  • Verificare E2E: browser — chip vizibil si corect colorat pe randul de mapare

4. Riscuri

  • Calitate etichetare model local (Qwen3-4B Q4) < model mare (Groq 70b). Masurat: bun pe cap (frecvent, clar), mai slab pe coada rara/ambigua (ADAS calibrare, chei, "doar nume piesa"). Mitigare: pre-filtru NUL (US-001) + optiunea unui al doilea pas de verificare cloud DOAR pe esantionul cu cod rar/incert.
  • Hardware GPU-box instabil sub sarcina (shutdown observat 2026-06-29). La config-ul rulant erau ~4GB VRAM liberi -> cauza probabil termica/alimentare, NU memorie. Mitigare OBLIGATORIE pentru pasul de etichetare: n_parallel=1, n_ctx=4096, batch 32-40, monitorizare temperatura GPU. NU mari batch/context fara headroom termic.
  • Ground-truth = eticheta LLM, nu om. 94.3% e ACORD cu LLM, nu acuratete reala; LLM impinge 86% in OE-1 (posibil prea agresiv). Recomandare (Decision #19): inainte de a creste increderea/orice auto-send, ruleaza heldout_eval.py cu un esantion etichetat de OM. Ramane in afara scope-ului acestui PRD, dar e poarta pentru orice 5.x viitor de auto-send.
  • mapping_suggestions populat schimba comportamentul testelor care presupuneau SILVER gol. Mitigare: seederul ruleaza doar daca seedul exista; conftest poate dezactiva seedul in testele care nu-l vor (ca la embeddings).
  • Coada lunga ramane needs_mapping. Chiar la 90% volum acoperit, 76% din operatiile DISTINCTE raman neetichetate (frecventa 1). Asteptare corecta: bootstrap-ul reduce mult volumul, dar editorul uman ramane necesar pe coada.
  • (F1, review) Simetrie corpus/query la embeddings. Corpusul k-NN devine text NORMALIZAT (denumire_normalizata), deci query-ul TREBUIE normalizat la fel inainte de embedding (US-005 AC). Daca raman asimetrice (corpus normalizat, query brut), similaritatea scade si nu mai e configul masurat (94.3%). Risc de regresie tacuta — acoperit de test in US-005.
  • (F2, review) Idempotenta cross-run a etichetarii. Etichetele noi produse de o rulare trebuie sa devina cache pentru urmatoarea (seedul comis = sursa de etichete, nu doar labels-groq-partial.json), altfel re-run-ul re-trimite tot la LLM. Acoperit de test_rerun_zero_apeluri_llm (US-003).

5. Decizii (intrebari deschise rezolvate la aprobare, 2026-06-28)

Erau intrebari deschise; rezolvate de user la poarta de aprobare PRD. Devin constrangeri de executie.

  • D1 — Tinta de acoperire la etichetare: 90% din volum (--target-volum 0.9, ~4.368 operatii distincte). Restul (coada lunga, 76% din operatiile distincte dar doar ~10% din volum) ramane pe editorul uman. US-003 implementeaza exact acest default; --all ramane disponibil dar NU e calea aprobata pentru v1.
  • D2 — Verificare cloud pe esantionul incert: NU in acest PRD. Toate sursele sunt suggestion-only (blast radius mic: o sugestie gresita = omul alege altceva in editor). Pre-filtrul NUL (US-001) acopera punctul slab masurat. Codurile rare/avarii grave sunt volum mic; un pas de verificare cloud adauga un backend in plus pentru castig marginal. Se reia DOAR daca esantionul uman (Decision #19, vezi Riscuri) arata ca erorile pe coduri rare sunt o problema reala. source/confidence din seed raman in DB pentru o eventuala flag-uire ulterioara.
  • D3 — Pastram exact-match (SILVER) separat de k-NN. Exact-match (lookup_suggestion pe text normalizat) = instant, 100% pe text identic; k-NN = generalizare semantica pentru texte nevazute. Precedenta confirmata: GOLD partajat > exact (SILVER) > k-NN embedding (US-006). k-NN NU inlocuieste exact-match.
  • D4 — Bootstrap-ul v1 ruleaza pe LM Studio local (Qwen3-4B, json_schema strict), nu pe Groq/OpenRouter. Motiv: zero cost per-token, date pe hardware propriu (PII service local), masurat 91% pe batch greu + 20/20 validare. Groq/OpenRouter raman in tool ca fallback interschimbabil (US-002), dar nu sunt calea aprobata pentru v1. Cerinta user, 2026-06-28.
  • D5 — Dedup pe normalize_for_match INAINTE de orice apel LLM, cu reuse in spatiu normalizat. Nu se trimite la LLM niciun duplicat normalizat si nicio operatie deja etichetata. Garantat de test_zero_duplicate_trimis_la_llm (within-run) + test_rerun_zero_apeluri_llm (cross-run, idempotenta) — US-003. Motiv: ~31% randuri redundante (19.456 brute -> 13.519 de etichetat: cross-file + variatii spatii + reuse labels existente); fara dedup-ul corect platim apeluri LLM inutile si riscam etichete inconsistente pe acelasi text logic. Cerinta user, 2026-06-28.

6. Valuri de executie (graful de dependente)

PASUL 1 — BOOTSTRAP ETICHETE OFFLINE (LM Studio LLM) — fundatia, ruleaza prima:
  Val 1: [US-002] [US-001]        ← US-002 (etichetator LM Studio) = pasul 1; US-001 (pre-filtru NUL) paralel, fisiere disjuncte
  Val 2: [US-003]                 ← deblocat de US-002: dedup normalizat -> trimite la LLM -> seed comis
PASUL 2 — CONSUM SEED (fara LLM):
  Val 3: [US-004]                 ← deblocat de US-003 (owns schema/seed loader)
  Val 4: [US-005]                 ← deblocat de US-004
  Val 5: [US-006]                 ← deblocat de US-001 + US-005
  Val 6: [US-007] (optional)      ← deblocat de US-006

Raport VERIFY (2026-06-29) — PASS

Faza VERIFY + CLOSE rulata pe feat/5.18-corpus-knn-exemple-etichetate, commit-uri 756f777 (5.18 core + seed) + 308fee6 (fix lateral start-test ONNX). Seed-ul real produs cu subagenti Haiku (decizie user 2026-06-29), NU LM Studio (GPU jos) si NU Groq — vezi nota la "Seed real" mai jos. Abaterea de la D4 (LM Studio = backend bootstrap v1) e documentata si justificata: motorul de etichetare s-a schimbat, garantiile de calitate (validare 157 op Haiku vs Groq) sunt mai bune, restul pipeline-ului (US-003..006) e neatins.

PASS/FAIL per story

Story Stare Dovada
US-001 pre-filtru NUL PASS tests/test_prefiltru_nul.py verde; seed contine 2200 NUL (is_nul=1, cod=NULL)
US-002 etichetator offline PASS tests/test_eticheteaza_tool.py verde (json_schema envelope, enum cod, scrub PII, no_think)
US-003 generare seed pe frecventa PASS tests/test_genereaza_seed.py verde (dedup normalizat, zero-duplicat, idempotenta cross-run, conflict determinist)
US-004 seeder DB PASS tests/test_operatii_seed.py verde; smoke init_db pe DB gol -> mapping_suggestions=17181, NUL=2200, re-seed = 0 inserate (idempotent)
US-005 embeddings pe corpus etichetat PASS tests/test_embeddings_corpus_etichetat.py verde (corpus din mapping_suggestions, query normalizat simetric, is_nul propagat)
US-006 enrich = NUL + exact + k-NN PASS tests/test_enrich_corpus_etichetat.py verde (precedenta NUL>GOLD>exact>k-NN, abtinere sub prag, invariant #13 regresie)
US-007 badge sursa (optional) PASS tests/test_web_badge_sursa.py verde (4 teste); E2E render live confirma chip confirmat/similar/non-operatie. Implementat la cererea user (2026-06-29)

Dovezi agregat

  • Suita completa: python3 -m pytest -q -m "not live" -> 1387 passed, 1 deselected (live), 0 failed (142.77s).
  • Cele 6 fisiere de test 5.18 rulate izolat: 36 passed (test_prefiltru_nul, test_eticheteaza_tool, test_genereaza_seed, test_operatii_seed, test_embeddings_corpus_etichetat, test_enrich_corpus_etichetat).
  • Smoke seeder (init_db pe DB gol, AUTOPASS_SEED_OPERATII_ENABLED=true): 17181 randuri in mapping_suggestions, 2200 NUL, source='haiku_seed', re-seed idempotent (0 inserate).
  • Validare nomenclator: toate codurile distincte din seed (OE-1..OE-8, OE-I/R, AITLV, R-ODO) sunt in FALLBACK_NOMENCLATOR — zero cod gunoi care ar da HTTP 500 / ORA-12899 la RAR.

Seed real (abatere de la D4, aprobata de user)

Seed-ul app/data/operatii-etichetate.json rescris de la 3758 (Groq partial) la 17181 operatii distincte (toate, ordine frecventa), source="haiku_seed", prin subagenti Haiku in Claude Code (blocantul GPU LM Studio rezolvat fara GPU). Validare la dezacorduri Haiku vs Groq pe 157 operatii: Haiku corect ~22/30, Groq ~0 (ex: CHIRIE ANVELOPE->NUL, ADAPTARE electronica->OE-7, INLOCUIT PLACUTE FRANA->OE-1). Distributie: OE-1=13764 (cap, asteptat), NUL=2200, restul sparse. Calitate estimata la scara ~95%; codurile rare (avarii grave OE-C/S/D/F/A, OE-5/6) sunt sparse si pot avea erori de margine ne-verificate uman — ramane recomandarea Decision #19 (esantion uman) inainte de orice crestere de incredere / auto-send.

CLOSE — /code-review high (main..HEAD, 3 finder x 8 unghiuri)

Calea de runtime in productie = curata. Verificat intact:

  • Invariant #13: nimic din SILVER/k-NN/NUL nu intra in resolve_prestatii/load_mapping (suggestion-only).
  • suggest_nearest/enrich_suggestions semnatura noua (is_nul) consumata corect de unicul apelant.
  • Worker keepalive RAR (308fee6/c05fa00): fara race (worker single-thread), heartbeat actualizat doar pe login reusit.
  • Config embeddings_enabled=True + seed_operatii_enabled=True default: teste neafectate (conftest override).

Findings (toate low / cosmetic, niciun bug de runtime) — REPARATE in faza CLOSE:

  1. tools/mapare-llm/genereaza_seed.py (_incarca_seed/construieste_harta_etichete): json.loads(open(...).read()) fara context manager -> FD leak in tool offline. Fix: with open(...).
  2. app/shared_store.py seed_suggestions: cod=" " (whitespace) -> '' in loc de NULL pe rand non-NUL. Fix: str(...).strip().upper() or None INAINTE de truthiness. Lock: test_seed_suggestions_cod_whitespace_devine_null.
  3. app/embeddings.py (2 docstring-uri): ziceau [{cod, similaritate}], real [{cod, is_nul, similaritate}]. Fix: docstring-uri aliniate.

Concluzie VERIFY: PASS. US-001..006 livrate cu dovezi; zero bug de corectitudine in runtime; cele 3 findings de cleanup reparate + lock-uite.

CLOSE — US-007 implementat (cerere user 2026-06-29)

User a cerut la poarta CLOSE sa includem badge-ul direct pe sugestiile sistemului fuzzy. Implementat: chip in coloana "Sugestii" din _mapari.html, mapat din sugestie_principala.sursa: confirmat (GOLD partajat) / similar (SILVER exact + k-NN embeddings) / non-operatie (pre-filtru NUL / vecin NUL). CSS .sugg-sursa--{confirmat,similar,nul} pe tokeni de tema (--ok/--accent/--muted cu color-mix), nu rupe layoutul. Suggestion-only (#13). Fix lateral: surse_sugestie default in routes.py a primit cheia nul (lipsea — finding cross-file). Teste: tests/test_web_badge_sursa.py (gold/silver/nul/fara-sursa). Render verificat in serverul real (/_fragments/mapari): OP-REV->confirmat, OP-REP->similar, OP-ITP->non-operatie, OP-XYZ->fara chip. Suita: 1392 passed, 1 deselected (live).


GSTACK REVIEW REPORT (/autoplan — Eng focus, 2026-06-28)

Scope review: Eng (CEO premise gate + Eng dual-voice). Design/DX sarite (UI = doar badge optional US-007, tool intern mono-dezvoltator). Voce Eng: subagent-only — Codex a lovit limita de utilizare (degradare conform matricei).

Premise confirmate (poarta umana): (1) k-NN peste exemple reale > 18 categorii generice (94.3% vs 86.2% masurat); (2) etichetare LLM o singura data, offline, zero LLM la runtime; (3) SILVER populat in productie din seed comis; (4) pre-filtru NUL necesar (recall 64%); (5) LM Studio Qwen3-4B = calitate acceptabila pt bootstrap (91% batch greu / 20/20 validare).

Cerinte user incorporate: D4 (LM Studio = backend default v1), D5 (dedup pe normalize_for_match + reuse normalizat, INAINTE de LLM).

Decision Audit Trail

# Faza Decizie Clasif. Principiu Rationament
1 CEO Restructurare valuri: Pasul 1 = bootstrap LM Studio (US-002->US-003) Mecanic P1 Cerinta user explicita; reflecta dependenta reala
2 Eng F1: query embedding normalizat ca si corpusul (US-005 AC + test) Mecanic P5 Corectitudine; altfel 94.3% nereproductibil. Blast radius (US-005)
3 Eng F2: seed comis = cache de etichete cross-run (US-003 pipeline + test_rerun_zero_apeluri_llm) Mecanic P1 Criteriul "0 apel LLM la re-run" altfel nesatisfiabil
4 Eng F3: harta normalizat->cod cu tie-break determinist (freq-max) Mecanic P5 1 conflict real azi (CURATAT CATALIZATOR); altfel cod nedeterminist
5 Eng F4/F5: corectie cifre (17.181 distinct, 13.519 de etichetat, 31%) + claim "fara punctuatie" Mecanic P5 Cifre verificate cu normalize_for_match real
6 Eng F6: arunca cheie normalizata vida inainte de dedup Mecanic P1 Coliziune pe slot UNIQUE gol
7 Eng F7: teste two-run + conflict adaugate Mecanic P1 Testul single-run nu acopera idempotenta/determinismul
8 Eng F8: envelope json_schema strict + enum cod + dezactivare thinking Qwen3 + garda truncare Mecanic P1 Realism integrare LM Studio (cerinta user #1)
9 Eng F9: parsare NR toleranta (skip, nu zero-weight) Mecanic P3 Date curate azi; ieftina robustete
10 Eng F10: re-justificare INSERT OR IGNORE (confirmari umane = shared_mappings) Mecanic P5 Evita inducerea in eroare a unui mentainer

Zero decizii de gust (taste) si zero user-challenge: toate constatarile au intarit directia user, nu au contrazis-o.