Files
rar-autopass/docs/prd/prd-5.8-ux-tabel-trimiteri-reguli-text.md
Claude Agent 51dc504f1d feat(5.8): reguli mapare pe text (substring/cont) + UX tabel trimiteri (detaliu inline, fara scroll, cod RAR)
Reguli text per cont (operation_text_rules), resolve_prestatii cu param aditiv
text_rules + precedenta stricta, threadat pe toate cele 6 callsite-uri + valid_codes
+ seam classify_prezentare. UI Mapari: sectiune reguli + preview pre-salvare + overlap
+ telemetrie text_rule_hit. UX tabel: cod_rar sub operatie, pill eticheta scurta, fara
scroll orizontal (scopat .tabel-trimiteri + carduri <768px), detaliu inline expandabil
(a11y + pauza poll). code-review: reparat regula auto_send=0 care trimitea automat la RAR
in loc sa tina randul pentru review. 814 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:47:37 +00:00

37 KiB

PRD 5.8 — UX tabel trimiteri (detaliu inline, fara scroll, cod RAR) + reguli mapare pe text

Stare: verify-pass

Proces: docs/ROADMAP.md §5. Contract RAR (sursa de adevar): docs/api-rar-contract.md. Starea trece: draft → aprobat → in-executie → verify-pass → inchis.

1. Obiectiv

Patru imbunatatiri UX, fara schimbarea fluxului de trimitere:

  1. Panoul de detaliu al unei trimiteri apare sub randul selectat (rand expandabil), nu la baza tabelului (azi #trimitere-detaliu traieste dupa <table> in _coada.html:72).
  2. Tabelul de trimiteri nu mai are scroll orizontal (confirmat live: .tablewrap scrollWidth=1189 > clientWidth=1010 la 1280px). Solutie agreata: mut Motiv in detaliu, stiveasc cod RAR sub codul operatiei in coloana Operatie, VIN sub nr. inmatriculare (deja stivuit) si scurtez etichetele de Stare.
  3. In coloana Operatie apare si codul de operatie RAR rezolvat (cod_prestatie), nu doar codul/denumirea venita din API/import.
  4. Pagina Mapari capata reguli automate pe text (substring): ex. operatie care contine „verificare" → cod RAR OE-2. Se aplica la ingestie si la re-rezolvarea blocajelor.

2. Non-Goals (anti scope-creep)

  • Fara match_type multiplu (starts_with / regex). Doar contains (substring), insensibil la diacritice si majuscule. Daca apare nevoia, e alt PRD.
  • Fara modificari la masina de stari, la worker, la contractul RAR sau la idempotenta.
  • Fara schimbarea logicii de mapare exacta cod_op_service → cod_prestatie (ramane, are precedenta).
  • Fara redesign general al dashboard-ului; doar tabelul de trimiteri + sectiunea noua din Mapari.
  • Fara reguli text per-import sau globale: regulile sunt per cont (ca operations_mapping).

3. Stories atomice

Backend + UI pentru acelasi comportament = stories separate. Regulile pe text (US-001..004) si UX-ul tabelului (US-005..008) sunt independente — se pot livra in paralel.


US-001: Schema + persistenta reguli text de mapare

Ca dezvoltator vreau o tabela operation_text_rules pentru ca regulile pe substring sa fie durabile per cont, langa operations_mapping.

  • Depinde de: —
  • Fisiere: app/schema.sql, app/mapping.py, tests/test_mapping_text_rules.py
  • Test intai (RED): tests/test_mapping_text_rules.pytest_save_text_rule_persista, test_load_text_rules_per_cont, test_delete_text_rule, test_unic_per_cont_pattern
  • Acceptance criteria:
    • Tabela noua: id, account_id (NOT NULL, FK accounts ON DELETE CASCADE), pattern TEXT NOT NULL, cod_prestatie TEXT NOT NULL, auto_send INTEGER NOT NULL DEFAULT 0, priority INTEGER NOT NULL DEFAULT 0, created_at, cu UNIQUE(account_id, pattern).
    • auto_send DEFAULT 0 (decizie CEO 2026-06-24, siguranta): o regula pe substring are blast radius mai mare decat o mapare exacta (potriveste si operatii viitoare nevazute), iar FINALIZATA e ireversibil la RAR. O regula noua rezolva codul dar TINE randul pentru verificare umana (needs_mapping) pana cand operatorul activeaza explicit „In coada". UI-ul (US-004) reflecta toggle-ul pe 0 implicit.
    • load_text_rules(conn, account_id) intoarce lista ordonata [{pattern, cod_prestatie, auto_send, priority}] (ordine: priority ASC, id ASC), aplicand account_or_default.
    • save_text_rule(conn, account_id, pattern, cod_prestatie, auto_send) face upsert pe (account_id, pattern); delete_text_rule(...) sterge.
    • Schema e idempotenta (CREATE TABLE IF NOT EXISTS) si nu strica DB-uri existente.
    • python3 -m pytest tests/test_mapping_text_rules.py -q trece.
  • Verificare E2E: —

US-002: resolve_prestatii aplica reguli text (substring, dupa maparea exacta)

Ca integrator vreau ca o operatie nemapata sa primeasca cod RAR din prima regula text care da match pentru ca sa nu mai intre in editor degeaba.

  • Depinde de: US-001
  • Fisiere: app/mapping.py, tests/test_mapping.py
  • Test intai (RED): tests/test_mapping.pytest_regula_text_contains_rezolva, test_mapare_exacta_bate_regula_text, test_regula_text_insensibila_diacritice_caz, test_regula_text_cod_invalid_in_nomenclator_ramane_nemapat, test_prima_regula_dupa_priority_castiga
  • Acceptance criteria:
    • resolve_prestatii(prestatii, mapping, valid_codes=None, text_rules=None) — semnatura aditiva (param nou optional, default None = comportament actual neschimbat).
    • Precedenta stricta: cod_prestatie direct valid > mapare exacta cod_op_service > reguli text > nemapat. Regula text se incearca DOAR cand nu exista cod valid si op nu e in mapping.
    • Match = substring pe textul operatiei (denumire daca exista, altfel cod_op_service), normalizat cu normalize_for_match (fara diacritice, uppercase, spatii colapsate) — pe ambele parti (pattern si text).
    • Daca regula da match dar cod_prestatie-ul ei nu e in valid_codes → operatia ramane nemapata (nu trimitem cod invalid; coerent cu regula „RAR accepta doar coduri din nomenclator", mapping.py:101-105).
    • La match multiplu castiga prima dupa ordinea load_text_rules (priority, id).
    • python3 -m pytest tests/test_mapping.py -q trece (inclusiv testele vechi).
  • Verificare E2E: —

US-003: Reguli text active la ingestie + la re-rezolvare

Ca service vreau ca regulile text sa actioneze pe prezentari noi (API + import) si sa deblocheze randurile needs_mapping existente cand salvez o regula noua.

  • Depinde de: US-002
  • Fisiere: app/mapping.py (classify_prezentare + reresolve_account), app/api/v1/router.py, app/api/v1/import_router.py, app/web/routes.py, tests/test_reresolve_text_rules.py
  • Test intai (RED): tests/test_reresolve_text_rules.pytest_ingestie_api_aplica_regula_text, test_ingestie_import_aplica_regula_text, test_corectie_web_aplica_regula_text, test_salvare_regula_rerezolva_blocate
  • Acceptance criteria:
    • TOATE cele 6 apeluri resolve_prestatii primesc text_rules SI valid_codes (decizie eng D-eng-1): mapping.py:276 (in classify_prezentare), mapping.py:441 (reresolve_account), import_router.py:204, import_router.py:1079, routes.py:984 (corectie web needs_data), routes.py:2278 (import web). Cele 4 care azi paseaza valid_codes=None incep sa paseze valid_codes=load_nomenclator_codes(conn) or None — altfel AC-ul de validare (US-002) nu se onoreaza pe acele cai.
    • classify_prezentare capata param text_rules (A3): seam-ul partajat API + /valideaza (invariant 5.2) il primeste si ambii apelanti (create_prezentari + ruta /valideaza) fac load_text_rules(...) si il paseaza — altfel dry-run-ul diverge de trimiterea reala.
    • text_rules/valid_codes se incarca o data per cerere/batch, NU per rand (T2): routes.py:2278 (loop import) si reresolve_account (loop randuri) le incarca inainte de bucla.
    • O prezentare API/import a carei operatie da match pe o regula intra queued (sau respectand auto_send), NU needs_mapping.
    • La salvarea unei reguli noi, reresolve_account re-evalueaza blocajele si deblocheaza randurile care acum dau match (acelasi mecanism ca la save_mapping).
    • cod_prestatie-ul rezolvat din regula respecta validarea fata de nomenclator (US-002).
    • python3 -m pytest -q trece integral.
  • Verificare E2E: POST /v1/prezentari cu operatie „Verificare X" (fara mapare exacta), regula contine "verificare" → OE-2 salvata in prealabil → raspuns cu status=queued, fara nemapate.

US-004: UI Mapari — sectiune „Reguli automate (text)"

Ca operator vreau sa adaug/sterg reguli text din pagina Mapari pentru ca sa automatizez maparea fara cod intern per operatie.

  • Depinde de: US-001 (UI poate merge in paralel cu US-002/003; rezolvarea efectiva cere US-003)
  • Fisiere: app/web/routes.py, app/web/templates/_mapari.html, tests/test_web_mapari_text_rules.py
  • Test intai (RED): tests/test_web_mapari_text_rules.pytest_post_regula_text_salveaza_si_rerezolva, test_post_sterge_regula, test_regula_text_scoped_pe_cont_sesiune, test_csrf_necesar
  • Acceptance criteria:
    • Sectiune noua in _mapari.html (a 4-a, langa „De rezolvat" / „Mapari salvate" / „Formate"): tabel cu coloanele Daca operatia contine (text) | Cod RAR (select din nomenclator) | In coada (toggle auto_send, implicit OFF — vezi US-001 decizia CEO) | Actiuni (sterge), plus un rand de adaugare.
    • Formularul foloseste auto_send-toggle si select din nomenclator existente (reuse macro), cu csrf_token; rutele POST /mapari/reguli-text si POST /mapari/reguli-text/sterge sunt account-scoped pe sesiune (require_login), ca rutele Mapari actuale.
    • La salvare se cheama save_text_rule + reresolve_account, cu mesaj „Regula salvata. Deblocate: N" si trigger trimiteriChanged (refresh lista), exact ca maparea inline (5.7).
    • Textul afisat e clar ca match-ul e „contine" (substring), nu egalitate.
    • Empty state (nicio regula): o linie explicativa cu exemplu — „Inca nu ai reguli. Ex: operatia contine «verificare» → OE-2. Mapeaza automat operatii similare fara cod intern." — urmata de randul de adaugare gata afisat ca placeholder. (Pass 2)
    • Stari: succes = mesaj „Regula salvata. Deblocate: N"; eroare cod invalid in nomenclator = mesaj inline „Cod RAR necunoscut in nomenclator", fara salvare. (Pass 2)
    • python3 -m pytest tests/test_web_mapari_text_rules.py -q trece.
  • Verificare E2E: gstack browser pe /?tab=mapari — adaug regula verificare → OE-2, confirm ca apare in lista si ca un rand De rezolvat cu „verificare" dispare dupa salvare.

US-005: cod_rar distinct in datele de afisare ale randului

Ca dezvoltator vreau un camp separat pentru codul RAR rezolvat pentru ca sa-l pot afisa sub codul operatiei fara sa-l confund cu cod_op_service.

  • Depinde de: —
  • Fisiere: app/payload_view.py, tests/test_payload_view.py
  • Test intai (RED): tests/test_payload_view.pytest_cod_rar_prezent_cand_mapat, test_cod_rar_gol_cand_nemapat, test_operatie_ramane_denumire_sau_op
  • Acceptance criteria:
    • prezentare_din_payload intoarce in plus cod_rar = cod_prestatie (uppercase, strip „.0") sau EMPTY cand lipseste. operatie si cod raman neschimbate (compat).
    • Cand operatia e nemapata (cod_prestatie None), cod_rar == EMPTY (nu cade pe cod_op_service).
    • python3 -m pytest tests/test_payload_view.py -q trece.
  • Verificare E2E: —

US-006: Etichete de Stare compacte (pill)

Ca operator vreau etichete scurte in coloana Stare pentru ca randul sa fie ingust si lizibil.

  • Depinde de: —
  • Fisiere: app/web/labels.py, tests/test_labels.py
  • Test intai (RED): tests/test_labels.pytest_eticheta_scurta_pentru_fiecare_stare, test_eticheta_lunga_ramane_pentru_subtext
  • Acceptance criteria:
    • Eticheta scurta expusa ca functie noua separata eticheta_scurta(status) -> str (dict propriu + garda KeyError), NU ca al 4-lea element in tuple-ul Eticheta (3 elemente azi, despachetat in template-uri — l-ar rupe). eticheta_stare ramane neatins (text lung pentru subtext/tooltip in detaliu). Set propus: queued→"In coada", sending→"Se trimite", sent→"Finalizat", needs_mapping→"De mapat", needs_data→"Date lipsa", error→"Eroare".
    • Pill-ul din tabel foloseste eticheta scurta; clasele CSS s-* (culorile) raman.
    • Stare neacoperita ridica KeyError (ca azi, ca sa prinda stari noi).
    • python3 -m pytest tests/test_labels.py -q trece.
  • Verificare E2E: —

US-007: Tabel trimiteri fara scroll orizontal (coloane re-aranjate)

Ca operator vreau sa vad tot randul fara scroll lateral pentru ca sa citesc starea si operatia dintr-o privire.

  • Depinde de: US-005, US-006
  • Fisiere: app/web/templates/_submissions.html, app/web/routes.py (builder rand), app/web/templates/base.html (CSS), tests/test_web_submissions.py
  • Test intai (RED): tests/test_web_submissions.pytest_tabel_nu_are_coloana_motiv, test_operatie_contine_cod_rar, test_pill_eticheta_scurta
  • Acceptance criteria:
    • Coloana Motiv eliminata din <thead>/<tbody> (continutul ei se vede in detaliu — _trimitere_detaliu.html:36-39 deja afiseaza Motiv).
    • Coloana Operatie: linia 1 = prez.operatie (denumire/cod API), linia 2 = prez.cod_rar muted (cod RAR: XXX); cand nemapat afiseaza „nemapat" muted.
    • Coloana Vehicul: nr. inmatriculare + VIN scurt stivuit (deja exista, pastrat).
    • Pill Stare = eticheta scurta (US-006).
    • CSS: la 1280px .tablewrap nu mai depaseste (scrollWidth <= clientWidth); se permite white-space: normal controlat pe coloanele text si latimi rezonabile, fara a rupe alte tabele care folosesc .tablewrap (scopare prin clasa, ex. .tabel-trimiteri).
    • Pill compact pastreaza info lunga: eticheta scurta (US-006) e in pill; textul lung (subtext din eticheta_stare) ramane disponibil ca title= (tooltip) pe pill si in panoul de detaliu — nu se pierde informatie. (Pass 5)
    • Responsive (Pass 6): >=1024px toate cele 8 coloane; 768-1024px ascund coloana Actualizat (e in detaliu) → 7 coloane fara scroll; <768px randurile devin carduri (eticheta:valoare stivuit, pill + chevron sus), nu tabel — fara scroll orizontal.
    • Secundarul „cod RAR" / „nemapat" e >=12px cu contrast >=4.5:1 (coerent cu vin_scurt existent); nu se transmite stare DOAR prin culoare (textul „nemapat" o spune). (Pass 6)
    • python3 -m pytest tests/test_web_submissions.py -q trece.
  • Verificare E2E: gstack browser pe / la 1280px, 1024px, 900px si 375px — scrollWidth <= clientWidth la fiecare (sau carduri sub 768px); coloana Operatie arata ambele coduri.

US-008: Detaliu trimitere ca rand expandabil sub randul selectat

Ca operator vreau ca detaliul sa apara imediat sub randul pe care l-am dat click pentru ca sa nu mai caut la baza tabelului.

  • Depinde de: US-007
  • Fisiere: app/web/templates/_submissions.html, app/web/templates/_coada.html, app/web/templates/_trimitere_detaliu.html, app/web/routes.py, tests/test_web_detaliu_inline.py
  • Test intai (RED): tests/test_web_detaliu_inline.pytest_fragment_detaliu_se_randeaza_in_container_pe_rand, test_un_singur_detaliu_deschis
  • Acceptance criteria:
    • Fiecare rand are un rand-sibling de detaliu <tr class="detaliu-rand"> cu <td colspan=N><div id="detaliu-{{r.id}}"></div></td>, ascuns implicit.
    • Click pe rand face hx-get="/_fragments/trimitere/{id}" cu hx-target="#detaliu-{id}", hx-swap="innerHTML"; la deschidere se inchid celelalte detalii deschise (un singur rand expandat o data).
    • Indicator vizual (Pass 1/4): chevron / la inceputul randului (rotit cand e deschis) + randul deschis primeste fundal evidentiat (#1d212b dark / echivalent light). Detaliul se leaga prin fundal subtil + border-top (stilul existent al sectiunii de mapare inline _trimitere_detaliu.html), NU border-left accent (evita pattern AI-slop).
    • Stare loading (Pass 2): la click, pana raspunde HTMX, containerul randului arata un placeholder discret („Se incarca…") prin hx-indicator, ca operatorul sa vada ca s-a inregistrat clicul.
    • Accesibilitate (Pass 6): randul clickabil e focusabil la tastatura (tabindex="0", role="button", aria-expanded sincronizat); Enter/Space deschid/inchid; tinta de atins (chevron/checkbox) >=44px pe touch.
    • Butonul „Inchide" din _trimitere_detaliu.html goleste containerul randului curent (nu mai tinteste #trimitere-detaliu global) si readuce focusul pe randul declansator.
    • #trimitere-detaliu global din _coada.html:72 este eliminat sau golit de rol.
    • Poll-ul de refresh (15s, _coada.html) se pune pe pauza cat timp un rand e expandat si se reia la inchidere (decizie eng D-eng-2). Fara flicker, fara pierdere de scroll/focus in timpul citirii; lista nu se misca sub operator. (NU re-fetch dupa swap, NU excludere de rand.)
    • python3 -m pytest tests/test_web_detaliu_inline.py -q trece.
  • Verificare E2E: gstack browser pe / — click pe randul #N: detaliul apare imediat sub el (detaliuTop intre rowTop si randul urmator), nu la baza; click pe alt rand muta detaliul.

Stories de expansiune (CEO review 2026-06-24, SELECTIVE EXPANSION). US-009..011 acceptate in scope. Cresc increderea si observabilitatea regulilor text; nu schimba fluxul de trimitere. Livrabile in Val 4.

US-009: Preview pre-salvare regula text (cate operatii potriveste)

Ca operator vreau sa vad cate operatii potriveste o regula INAINTE sa o salvez pentru ca sa nu salvez un pattern prea lacom (perechea naturala a auto_send=0).

  • Depinde de: US-001 (reuse agregarea pending_unmapped); independent de US-002/003
  • Fisiere: app/web/routes.py, app/web/templates/_mapari.html, tests/test_web_mapari_preview_regula.py
  • Test intai (RED): tests/test_web_mapari_preview_regula.pytest_preview_numara_potriviri, test_preview_intoarce_exemple, test_preview_pattern_gol, test_preview_scoped_pe_cont
  • Acceptance criteria:
    • Ruta POST /mapari/reguli-text/preview (account-scoped pe sesiune, CSRF) primeste pattern, normalizeaza cu normalize_for_match, numara operatiile distincte nemapate ale contului (needs_mapping, reuse pending_unmapped) al caror text contine pattern-ul + intoarce pana la 3 exemple {cod_op_service, denumire}. NU salveaza nimic.
    • Fragment HTMX afisat sub randul de adaugare la schimbarea pattern-ului (hx-trigger="keyup delay:400ms"): „Potriveste N operatii nemapate: «...», «...»" sau „Nicio potrivire acum".
    • Pattern gol → fara apel/fragment gol (nu numara „tot").
    • python3 -m pytest tests/test_web_mapari_preview_regula.py -q trece.
  • Verificare E2E: gstack browser pe /?tab=mapari — tastez „verificare" in pattern → vad „Potriveste N operatii: ..." inainte de Salveaza.

US-010: Telemetrie hit regula text in app_events

Ca operator/admin vreau sa stiu ce regula a rezolvat ce submission pentru ca sa pot raspunde la „de ce a primit randul asta codul OE-2?".

  • Depinde de: US-002 (rezolvare) + US-003 (active la ingestie)
  • Fisiere: app/mapping.py (adnotare item rezolvat-prin-regula), app/observ.py (reuse log_event), apelantii cu conn (create_prezentari, reresolve_account, import), tests/test_text_rule_telemetry.py
  • Test intai (RED): tests/test_text_rule_telemetry.pytest_hit_regula_emite_app_event, test_mapare_exacta_nu_emite_text_rule_hit, test_event_redactat_si_scoped_pe_cont
  • Acceptance criteria:
    • resolve_prestatii adnoteaza itemul rezolvat-prin-regula cu pattern-ul sursa (camp aditiv pe dict, ex. cod_sursa="text_rule:<pattern>"; payload-harmless — RAR citeste doar cod_prestatie).
    • Apelantii cu conn emit log_event("text_rule_hit", {...}) in app_events cu {submission_id, account_id, pattern, cod_prestatie}, redactat prin app/security (fara PII). Maparea exacta NU emite text_rule_hit.
    • Vizibil in tab-ul „Jurnal" (5.6), scoped pe cont pentru non-admin (mecanismul existent).
    • python3 -m pytest tests/test_text_rule_telemetry.py -q trece.
  • Verificare E2E: POST /v1/prezentari cu operatie ce da match pe regula → eveniment text_rule_hit vizibil in Jurnal cu pattern-ul si codul.

US-011: Avertizare overlap intre reguli text (neblocant)

Ca operator vreau un avertisment cand o regula noua se suprapune cu una existenta pentru ca sa inteleg de ce o regula „castiga" inaintea alteia.

  • Depinde de: US-001 (load) + US-004 (UI salvare)
  • Fisiere: app/mapping.py (helper pur text_rules_overlap), app/web/routes.py, app/web/templates/_mapari.html, tests/test_mapping_overlap.py, tests/test_web_mapari_overlap.py
  • Test intai (RED): tests/test_mapping_overlap.pytest_overlap_substring_ambele_directii, test_fara_overlap, test_overlap_normalizat_diacritice; tests/test_web_mapari_overlap.pytest_salvare_cu_overlap_arata_avertisment_dar_salveaza
  • Acceptance criteria:
    • Helper pur text_rules_overlap(pattern, existing_rules) -> list[dict]: overlap = un pattern normalizat e substring al celuilalt (oricare directie). Determinist, fara DB.
    • La salvare (ruta US-004), daca exista overlap, mesaj inline neblocant: „Se suprapune cu regula «X» → COD; ordinea (priority, id) decide care se aplica prima." Salvarea continua (avertisment, NU eroare).
    • python3 -m pytest tests/test_mapping_overlap.py tests/test_web_mapari_overlap.py -q trece.
  • Verificare E2E: gstack browser — adaug „verificare", apoi „verificare faruri" → avertisment overlap, ambele reguli salvate.

4. Riscuri

  • Poll de 15s vs. detaliu deschis (US-008): _coada.html reincarca periodic #submissions-wrap; un re-render naiv ar inchide detaliul expandat. Mitigare: pastreaza id-ul randului deschis si re-cere fragmentul dupa swap, sau exclude randul deschis din inlocuire. De decis la executie.
  • white-space: normal global (US-007): .tablewrap/table sunt partajate de mai multe tabele (Mapari, Formate). Schimbarile CSS trebuie scopate la tabelul de trimiteri ca sa nu strice celelalte. Verificare vizuala pe toate tab-urile.
  • Cod RAR invalid intr-o regula text (US-002): daca nomenclatorul nu e inca populat, valid_codes poate fi gol → regula n-ar rezolva nimic. Coerent cu load_nomenclator_codes (nu valida cand e gol); de confirmat ca seed-ul fallback (18 coduri) acopera cazul uzual.
  • Colizii de reguli text (mai multe pattern-uri match): rezolvat determinist prin ordine (priority, id); fara ordine, comportamentul ar fi nedeterminist.

5. Intrebari deschise

Se rezolva cu utilizatorul INAINTE de executie (poarta de aprobare PRD).

  • Indiciu „rezolvat prin regula text" pe rand? REZOLVAT (design review): suficient codul RAR; re-evaluat post-livrare (vezi §8).
  • Pastram „Actualizat" in tabel la 1024px? REZOLVAT (Pass 6): ascuns la <=1024px, ramane in detaliu (vezi §7 Responsive).
  • Ordonarea regulilor: priority editabila? REZOLVAT: v1 = ordine de creare, fara editare priority (vezi §8).

6. Valuri de executie (graful de dependente)

Val 1: [US-001] [US-005] [US-006]      ← fara dependente, fisiere distincte → paralel
Val 2: [US-002] [US-007]               ← US-002 dep US-001; US-007 dep US-005+US-006
Val 3: [US-003] [US-004] [US-008]      ← US-003 dep US-002; US-004 dep US-001; US-008 dep US-007
Val 4: [US-009] [US-010] [US-011]      ← expansiuni CEO; US-009 dep US-001; US-010 dep US-002+US-003;
                                          US-011 dep US-001+US-004

7. Decizii de design (din /plan-design-review, 2026-06-23)

Arhitectura informatiei (tabel trimiteri)

Ierarhie pe rand, stanga→dreapta: Stare (pill colorat, ancora vizuala) → Vehicul (nr. mare + VIN muted dedesubt) → Operatie (denumire/cod API + „cod RAR: X" muted). Coloana Operatie devine purtatoarea celor doua coduri; restul (Data, Nr. RAR, Actualizat) sunt context secundar. Motiv iese din tabel (zgomot pe randurile OK) si traieste in detaliu.

Tabel stari interactiune (Pass 2)

FEATURE                 | LOADING            | EMPTY            | ERROR                  | SUCCESS              | PARTIAL
------------------------|--------------------|------------------|------------------------|----------------------|--------------------
Celula Operatie         | —                  | „nemapat" (red)  | —                      | „cod RAR: X" muted    | „nemapat" pana la mapare
Rand expandabil (detaliu)| placeholder „Se   | n/a              | fragment de eroare HTMX| detaliu sub rand,     | n/a
                        | incarca…" hx-indic.|                  | in container           | chevron rotit + fundal|
Sectiune reguli text    | —                  | explicatie+exemplu| „Cod RAR necunoscut"  | „Regula salvata.      | —
                        |                    | + rand adaugare  | (inline, fara salvare) | Deblocate: N"         |

Responsive (Pass 6)

  • >=1024px: 8 coloane complete, fara scroll orizontal.
  • 768-1024px: ascunde Actualizat (e in detaliu) → 7 coloane, fara scroll.
  • <768px: card per rand (eticheta:valoare stivuit, pill+chevron sus). Cardurile sunt justificate aici pentru ca cardul ESTE interactiunea (tap → expand), nu decor.

Accesibilitate (Pass 6)

Randul expandabil: tabindex=0, role="button", aria-expanded, activare Enter/Space, focus readus la inchidere, tinta touch >=44px. Secundarul muted >=12px, contrast >=4.5:1; starea nu se comunica DOAR prin culoare.

Aliniere stil (Pass 4/5) — clasificat APP UI

Tabel dens, limbaj utilitar, putine culori, fara mozaic de carduri decorative. Conectorul detaliului = fundal subtil + border-top (vocabular existent), NU border-left accent (pattern AI-slop). Pill-urile pastreaza clasele s-*; eticheta lunga ramane in title=/detaliu.

8. NU in scope (design — deferat explicit)

  • match_type multiplu (starts_with/regex) la reguli text — substring acopera „contine". Alt PRD.
  • Editare priority reguli in UI (drag/numar) — v1 e ordine de creare.
  • Redesign vizual al celorlalte tab-uri/tabele — doar tabelul de trimiteri + sectiunea Mapari noua.
  • Indicator „rezolvat prin regula text" pe rand — codul RAR e suficient (de re-evaluat post-livrare).
  • Animatii de expand/collapse elaborate — un toggle simplu (rotire chevron) e destul pentru un APP UI.

9. Ce exista deja (de reutilizat, nu reinventat)

  • Sistem de culori prin CSS variables + teme dark/light (base.html), clase pill s-*.
  • Macro toggle auto_send + select nomenclator + csrf_token (_mapari.html, _trimitere_detaliu.html) — refolosite la sectiunea reguli text.
  • reresolve_account (re-rezolvare blocaje), save_mapping (pattern upsert+rerez), mesaj „Deblocate: N" + trigger trimiteriChanged din maparea inline (5.7).
  • eticheta_stare (labels.py) — extins cu eticheta scurta, fara a pierde textul lung.
  • prezentare_din_payload (payload_view.py) — extins cu cod_rar.

Approved Mockups

Screen/Section Mockup Path Direction Notes
Tabel trimiteri (desktop + mobil) ~/.gstack/projects/romfast-rar-autopass/designs/trimiteri-tabel-20260623/mockup-trimiteri.png Tabel dens cu pill compact, „cod RAR" stivuit sub operatie, rand expandat inline cu chevron+fundal; card per rand sub 768px Conectorul detaliului in implementare = fundal subtil + border-top (NU border-left accent ca in mockup); teme calibrate pe CSS vars existente

Raport VERIFY

Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6.

VERDICT: PASS (toate US-001..011), dupa 1 fix critic descoperit la /code-review (CLOSE).

Suita

python3 -m pytest -q814 passed, 1 skipped (skip = tests/test_live_rar.py, live RAR opt-in). Toate testele numite in PRD exista si trec.

PASS/FAIL per story (verificator independent, context curat)

  • US-001..011: PASS, cu dovezi de cod (vezi mai jos). Subset 5.8 = 81+ teste verzi.
  • US-001 PASS — operation_text_rules UNIQUE(account_id,pattern), auto_send DEFAULT 0, FK ON DELETE CASCADE, CREATE TABLE IF NOT EXISTS; load/save/delete corecte (ordine priority,id).
  • US-002 PASS — semnatura aditiva resolve_prestatii(...,text_rules=None); precedenta stricta cod valid > mapare exacta > regula text > nemapat; match substring normalizat ambele parti; cod regula invalid -> ramane nemapat; prima dupa ordine castiga.
  • US-003 PASS — toate 6 callsite resolve_prestatii primesc text_rules + valid_codes; classify_prezentare param nou + ambii apelanti (create_prezentari + /valideaza) incarca; T2 incarcare o data per cerere/batch.
  • US-004 PASS — rute /mapari/reguli-text (+sterge) scoped+CSRF; save+reresolve+„Deblocate: N"+trimiteriChanged; empty state; cod invalid respins inline; sectiune a 4-a cu „contine".
  • US-005 PASS — cod_rar uppercase/strip „.0" sau EMPTY; nemapat -> EMPTY (fara fallback pe cod_op_service).
  • US-006 PASS — eticheta_scurta functie separata + dict propriu + KeyError; tuple Eticheta ramane 3.
  • US-007 PASS — Motiv eliminata; Operatie cu „cod RAR: X"/„nemapat"; pill scurt + title lung; CSS scopat .tabel-trimiteri; responsive (ascunde Actualizat ≤1024, carduri <768).
  • US-008 PASS — <tr class="detaliu-rand"> colspan=8; click hx-target=#detaliu-{id}; single-open + poll-pauza via htmx:beforeRequest preventDefault; Enter/Space; Inchide goleste+focus; #trimitere-detaliu global golit.
  • US-009 PASS — /mapari/reguli-text/preview scoped+CSRF, numara distinct nemapate ce contin pattern, max 3 exemple (HTML-escaped), zero scriere; pattern gol -> fragment gol.
  • US-010 PASS — cod_sursa="text_rule:<pattern>" doar pe hit; _emite_text_rule_hits din TOATE caile (API create, reresolve, import_router commit, + corectie web routes.py:1001 si import web commit routes.py:2482 — paritate adaugata post-VERIFY); maparea exacta nu emite.
  • US-011 PASS — text_rules_overlap pur (substring ambele directii, exclude identic); mesaj neblocant, salvarea continua.

E2E

Browser pixel-level neprobat (sandbox ucide serverul persistent; dashboard in spatele /login). Compensat cu E2E functional prin HTTP (TestClient, login real): tab Mapari S4 + empty state; US-009 preview „Potriveste N..."; US-004 save → trimiteriChanged + „Deblocate:" + lista; US-011 overlap; cod invalid respins; fragment submissions fara Motiv + detaliu-rand colspan=8 + pill role=button + „cod RAR/nemapat". Live RAR FINALIZATA neprobat (lipsa creds) — backend trimitere NEATINS.

Regresia de aur (Non-Goals)

CONFIRMAT: worker / idempotency / validation / reconcile / crypto / auth NEMODIFICATE (git). schema.sql = pur aditiv (tabela noua + index). Masina de stari si fluxul de trimitere neatinse.

Fix critic la /code-review high (CLOSE) — 1 bug real reparat

  • Regula text cu auto_send=0 (DEFAULT, decizia CEO) trimitea automat la RAR. has_no_auto_send inspecta DOAR operations_mapping, nu si regula text care a rezolvat itemul → un rand rezolvat prin regula cu auto_send=0 trecea pe queued (trimis automat), incalcand AC-ul central de siguranta US-001/US-003/US-004 (blast radius substring + FINALIZATA ireversibil). Repro confirmat: classify_prezentare(..., auto_send=0)queued. Reparat (app/mapping.py, TDD, tests/test_text_rule_autosend.py): _rezolva_din_reguli_text intoarce si auto_send; resolve_prestatii marcheaza itemul cu regula_fara_autosend cand regula are auto_send falsy SI curata adnotarile stale (cod_sursa/flag) la fiecare rezolvare (repara si o telemetrie falsa latenta la re-rezolvare prin mapare exacta); has_no_auto_send prinde flagul. Acum auto_send=0 → needs_mapping (review), auto_send=1 → queued. Operatorul elibereaza randul comutand „In coada" pe regula (US-004) → reresolve cu auto_send=1 → queued. pytest -q 814 passed. VERIFY-ul initial (context curat) a RATAT acest caz (nu testase hold-ul pe auto_send=0); prins la code-review.

GSTACK REVIEW REPORT

Review Trigger Why Runs Status Findings
CEO Review /plan-ceo-review Scope & strategy 1 clean SELECTIVE EXPANSION: 1 fix siguranta (auto_send default 0) + 3 stories acceptate (US-009/010/011)
Codex Review /codex review Independent 2nd opinion 0
Eng Review /plan-eng-review Architecture & tests (required) 1 findings 4 arhitectura (A1-A4) + 3 code/test (C1, T1-T2); 2 decizii deschise (D-eng-1/2)
Design Review /plan-design-review UI/UX gaps 1 clean score: 6/10 → 9/10, 6 decisions
DX Review /plan-devex-review Developer experience gaps 0

Pasele 1-7 evaluate cu mockup tintit (calibrat pe tema reala a app-ului). Decizii adaugate in plan: empty state reguli text, conector detaliu (fundal+border-top, nu border-left slop), banda responsive 768-1024 (ascunde Actualizat), card per rand <768px, chevron+fundal pe rand expandat, keyboard a11y (role=button/aria-expanded/Enter-Space) + pastrarea textului lung de stare in title=/detaliu.

  • VERDICT (design): DESIGN CLEARED (9/10).

Eng Review (2026-06-24)

Plan corect si bine descompus (8 stories atomice, 3 valuri, fisiere disjuncte). Referintele de linie din PRD sunt exacte. Constatari, verificate fata de cod:

  • A1 (corectitudine) — US-003 numara gresit callsite-urile resolve_prestatii. Sunt 6, nu 4: pe langa mapping.py:276 (in classify_prestatie), mapping.py:441, import_router.py:204, import_router.py:1079, mai exista routes.py:984 (corectie web needs_data) si routes.py:2278 (import web, intr-un loop pe randuri). Daca nu se threadeaza text_rules si acolo, regulile text NU se aplica pe caile web. → D-eng-1.
  • A2 — valid_codes=None pe 4 din 6 callsite-uri (import_router 204/1079, routes 984/2278) intra in conflict cu AC-ul US-003 „cod rezolvat din regula respecta validarea fata de nomenclator": pe acele cai validarea e oprita. Pentru a onora AC-ul trebuie threadat si valid_codes. Nota (pre-existent, semnalat, nereparat aici): promovarea codului direct necunoscut (garda ORA-12899) ruleaza azi DOAR pe calea API, pentru ca import/web rezolva fara valid_codes.
  • A3 — face explicit seam-ul classify_prezentare. mapping.py:276 e in classify_prezentare (helperul partajat care garanteaza ca /valideaza da acelasi verdict ca trimiterea reala — invariant 5.2). Threading text_rules cere param nou pe classify_prezentare + incarcare in AMBII apelanti (create_prezentari + ruta /valideaza), altfel dry-run diverge. PRD spune „create_prezentari" — de corectat in US-003.
  • A4 (decizia principala) — detaliu inline (US-008) vs poll 15s. Azi detaliul traieste in afara #submissions-wrap (_coada.html:72) tocmai ca poll-ul every 15s sa nu-l stearga. Inline reintroduce stergerea-la-poll. → D-eng-2.
  • C1 — US-006: nu mari tuple-ul Eticheta (3 elemente, despachetat in template-uri). Adauga eticheta_scurta(status)->str separat (dict propriu + garda KeyError).
  • T1 — adauga stories/teste pentru cele 2 callsite-uri web (corectie + import) care aplica o regula text.
  • T2 — incarca text_rules/valid_codes o data per cerere/batch, NU per rand (routes.py:2278 e in loop; la fel disciplina in reresolve_account).

Decizii rezolvate cu utilizatorul (2026-06-24):

  • D-eng-1 → TOATE cele 6 callsite-uri + valid_codes. Incorporat in US-003 (Fisiere += routes.py; AC-uri pentru cele 6 apeluri + classify_prezentare param + incarcare per-cerere/batch).
  • D-eng-2 → pauza poll cat e un rand deschis. Incorporat in US-008 AC.

A2/A3/C1/T1/T2 incorporate in stories (US-003 callsite-uri+validare+per-batch, US-006 eticheta_scurta separat, US-008 pauza poll, teste web pe corectie/import).

  • VERDICT (eng): PLAN SOUND. Cele 2 decizii rezolvate, constatarile incorporate in stories.

CEO Review (2026-06-24) — SELECTIVE EXPANSION

Premisa corecta: regulile text sunt levierul de adoptie al Etapei 5 (mai putine mapari manuale = onboarding mai rapid pentru service-urile din VFP). Scop bine subtras, reuse puternic peste operations_mapping/reresolve_account. Abordare confirmata (tabela separata + threading text_rules, varianta A). Constatare principala (inversiune/blast-radius) + 3 cherry-pick-uri, toate rezolvate cu utilizatorul:

  • Fix siguranta — auto_send DEFAULT 0 (nu 1). O regula pe substring potriveste si operatii viitoare nevazute; FINALIZATA e ireversibil la RAR. Regula noua rezolva codul dar TINE randul pentru verificare umana pana cand operatorul activeaza „In coada". Incorporat in US-001 + US-004.
  • US-009 (acceptat) — preview pre-salvare: „aceasta regula potriveste N operatii: ..." inainte de commit. Perechea naturala a auto_send=0 → onboarding aproape fara risc.
  • US-010 (acceptat) — telemetrie hit regula in app_events: ce pattern a rezolvat ce submission.
  • US-011 (acceptat) — avertizare overlap neblocanta intre reguli.

Cele 3 expansiuni livrabile in Val 4 (dupa nucleul US-001..008). Plan CEO persistat in ~/.gstack/projects/.../ceo-plans/.

  • VERDICT (ceo): SCOPE CLEARED. 4 decizii incorporate in stories. Ramane poarta umana de aprobare PRD inainte de EXECUTE.

NO UNRESOLVED DECISIONS