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>
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:
- Panoul de detaliu al unei trimiteri apare sub randul selectat (rand expandabil), nu la
baza tabelului (azi
#trimitere-detaliutraieste dupa<table>in_coada.html:72). - Tabelul de trimiteri nu mai are scroll orizontal (confirmat live:
.tablewrapscrollWidth=1189 > clientWidth=1010la 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. - In coloana Operatie apare si codul de operatie RAR rezolvat (
cod_prestatie), nu doar codul/denumirea venita din API/import. - 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_typemultiplu (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.py—test_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, cuUNIQUE(account_id, pattern). auto_sendDEFAULT 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), iarFINALIZATAe 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), aplicandaccount_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 -qtrece.
- Tabela noua:
- 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.py—test_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, defaultNone= comportament actual neschimbat).- Precedenta stricta:
cod_prestatiedirect valid > mapare exactacod_op_service> reguli text > nemapat. Regula text se incearca DOAR cand nu exista cod valid si op nu e inmapping. - Match = substring pe textul operatiei (
denumiredaca exista, altfelcod_op_service), normalizat cunormalize_for_match(fara diacritice, uppercase, spatii colapsate) — pe ambele parti (pattern si text). - Daca regula da match dar
cod_prestatie-ul ei nu e invalid_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 -qtrece (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.py—test_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_prestatiiprimesctext_rulesSIvalid_codes(decizie eng D-eng-1):mapping.py:276(inclassify_prezentare),mapping.py:441(reresolve_account),import_router.py:204,import_router.py:1079,routes.py:984(corectie webneeds_data),routes.py:2278(import web). Cele 4 care azi paseazavalid_codes=Noneincep sa pasezevalid_codes=load_nomenclator_codes(conn) or None— altfel AC-ul de validare (US-002) nu se onoreaza pe acele cai. classify_prezentarecapata paramtext_rules(A3): seam-ul partajat API +/valideaza(invariant 5.2) il primeste si ambii apelanti (create_prezentari+ ruta/valideaza) facload_text_rules(...)si il paseaza — altfel dry-run-ul diverge de trimiterea reala.text_rules/valid_codesse incarca o data per cerere/batch, NU per rand (T2):routes.py:2278(loop import) sireresolve_account(loop randuri) le incarca inainte de bucla.- O prezentare API/import a carei operatie da match pe o regula intra
queued(sau respectandauto_send), NUneeds_mapping. - La salvarea unei reguli noi,
reresolve_accountre-evalueaza blocajele si deblocheaza randurile care acum dau match (acelasi mecanism ca lasave_mapping). cod_prestatie-ul rezolvat din regula respecta validarea fata de nomenclator (US-002).python3 -m pytest -qtrece integral.
- TOATE cele 6 apeluri
- Verificare E2E:
POST /v1/prezentaricu operatie „Verificare X" (fara mapare exacta), regulacontine "verificare" → OE-2salvata in prealabil → raspuns custatus=queued, faranemapate.
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.py—test_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 (toggleauto_send, implicit OFF — vezi US-001 decizia CEO) | Actiuni (sterge), plus un rand de adaugare. - Formularul foloseste
auto_send-toggle siselectdin nomenclator existente (reuse macro), cucsrf_token; rutelePOST /mapari/reguli-textsiPOST /mapari/reguli-text/stergesunt 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 triggertrimiteriChanged(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 -qtrece.
- Sectiune noua in
- Verificare E2E: gstack browser pe
/?tab=mapari— adaug regulaverificare → OE-2, confirm ca apare in lista si ca un randDe rezolvatcu „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.py—test_cod_rar_prezent_cand_mapat,test_cod_rar_gol_cand_nemapat,test_operatie_ramane_denumire_sau_op - Acceptance criteria:
prezentare_din_payloadintoarce in pluscod_rar=cod_prestatie(uppercase, strip „.0") sauEMPTYcand lipseste.operatiesicodraman neschimbate (compat).- Cand operatia e nemapata (
cod_prestatieNone),cod_rar==EMPTY(nu cade pecod_op_service). python3 -m pytest tests/test_payload_view.py -qtrece.
- 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.py—test_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 + gardaKeyError), NU ca al 4-lea element in tuple-ulEticheta(3 elemente azi, despachetat in template-uri — l-ar rupe).eticheta_stareramane 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 -qtrece.
- Eticheta scurta expusa ca functie noua separata
- 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.py—test_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-39deja afiseaza Motiv). - Coloana Operatie: linia 1 =
prez.operatie(denumire/cod API), linia 2 =prez.cod_rarmuted (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
.tablewrapnu mai depaseste (scrollWidth <= clientWidth); se permitewhite-space: normalcontrolat 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 catitle=(tooltip) pe pill si in panoul de detaliu — nu se pierde informatie. (Pass 5) - Responsive (Pass 6):
>=1024pxtoate cele 8 coloane;768-1024pxascund coloana Actualizat (e in detaliu) → 7 coloane fara scroll;<768pxrandurile 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_scurtexistent); nu se transmite stare DOAR prin culoare (textul „nemapat" o spune). (Pass 6) python3 -m pytest tests/test_web_submissions.py -qtrece.
- Coloana Motiv eliminata din
- Verificare E2E: gstack browser pe
/la 1280px, 1024px, 900px si 375px —scrollWidth <= clientWidthla 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.py—test_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}"cuhx-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 (#1d212bdark / echivalent light). Detaliul se leaga prin fundal subtil +border-top(stilul existent al sectiunii de mapare inline_trimitere_detaliu.html), NUborder-leftaccent (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-expandedsincronizat); Enter/Space deschid/inchid; tinta de atins (chevron/checkbox) >=44px pe touch. - Butonul „Inchide" din
_trimitere_detaliu.htmlgoleste containerul randului curent (nu mai tinteste#trimitere-detaliuglobal) si readuce focusul pe randul declansator. #trimitere-detaliuglobal din_coada.html:72este 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 -qtrece.
- Fiecare rand are un rand-sibling de detaliu
- Verificare E2E: gstack browser pe
/— click pe randul #N: detaliul apare imediat sub el (detaliuTopintrerowTopsi 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.py—test_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) primestepattern, normalizeaza cunormalize_for_match, numara operatiile distincte nemapate ale contului (needs_mapping, reusepending_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 -qtrece.
- Ruta
- 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(reuselog_event), apelantii cuconn(create_prezentari,reresolve_account, import),tests/test_text_rule_telemetry.py - Test intai (RED):
tests/test_text_rule_telemetry.py—test_hit_regula_emite_app_event,test_mapare_exacta_nu_emite_text_rule_hit,test_event_redactat_si_scoped_pe_cont - Acceptance criteria:
resolve_prestatiiadnoteaza itemul rezolvat-prin-regula cu pattern-ul sursa (camp aditiv pe dict, ex.cod_sursa="text_rule:<pattern>"; payload-harmless — RAR citeste doarcod_prestatie).- Apelantii cu
connemitlog_event("text_rule_hit", {...})inapp_eventscu{submission_id, account_id, pattern, cod_prestatie}, redactat prinapp/security(fara PII). Maparea exacta NU emitetext_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 -qtrece.
- Verificare E2E:
POST /v1/prezentaricu operatie ce da match pe regula → evenimenttext_rule_hitvizibil 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 purtext_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.py—test_overlap_substring_ambele_directii,test_fara_overlap,test_overlap_normalizat_diacritice;tests/test_web_mapari_overlap.py—test_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 -qtrece.
- Helper pur
- 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.htmlreincarca 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: normalglobal (US-007):.tablewrap/tablesunt 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_codespoate fi gol → regula n-ar rezolva nimic. Coerent cuload_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_typemultiplu (starts_with/regex) la reguli text — substring acopera „contine". Alt PRD.- Editare
priorityreguli 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 pills-*. - Macro toggle
auto_send+selectnomenclator +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" + triggertrimiteriChangeddin maparea inline (5.7).eticheta_stare(labels.py) — extins cu eticheta scurta, fara a pierde textul lung.prezentare_din_payload(payload_view.py) — extins cucod_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 -q → 814 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_rulesUNIQUE(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_prezentareparam 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_raruppercase/strip „.0" sau EMPTY; nemapat -> EMPTY (fara fallback pe cod_op_service). - US-006 PASS —
eticheta_scurtafunctie separata + dict propriu + KeyError; tupleEtichetaramane 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 viahtmx:beforeRequestpreventDefault; Enter/Space; Inchide goleste+focus; #trimitere-detaliu global golit. - US-009 PASS —
/mapari/reguli-text/previewscoped+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_hitsdin 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_overlappur (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_sendinspecta DOARoperations_mapping, nu si regula text care a rezolvat itemul → un rand rezolvat prin regula cu auto_send=0 trecea pequeued(trimis automat), incalcand AC-ul central de siguranta US-001/US-003/US-004 (blast radius substring +FINALIZATAireversibil). Repro confirmat:classify_prezentare(..., auto_send=0)→queued. Reparat (app/mapping.py, TDD,tests/test_text_rule_autosend.py):_rezolva_din_reguli_textintoarce siauto_send;resolve_prestatiimarcheaza itemul curegula_fara_autosendcand 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_sendprinde 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 -q814 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 langamapping.py:276(inclassify_prestatie),mapping.py:441,import_router.py:204,import_router.py:1079, mai existaroutes.py:984(corectie webneeds_data) siroutes.py:2278(import web, intr-un loop pe randuri). Daca nu se threadeazatext_rulessi acolo, regulile text NU se aplica pe caile web. → D-eng-1. - A2 —
valid_codes=Nonepe 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 sivalid_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 faravalid_codes. - A3 — face explicit seam-ul
classify_prezentare.mapping.py:276e inclassify_prezentare(helperul partajat care garanteaza ca/valideazada acelasi verdict ca trimiterea reala — invariant 5.2). Threadingtext_rulescere param nou peclassify_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-ulevery 15ssa 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). Adaugaeticheta_scurta(status)->strseparat (dict propriu + gardaKeyError). - T1 — adauga stories/teste pentru cele 2 callsite-uri web (corectie + import) care aplica o regula text.
- T2 — incarca
text_rules/valid_codeso data per cerere/batch, NU per rand (routes.py:2278e in loop; la fel disciplina inreresolve_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_prezentareparam + 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_sendDEFAULT 0 (nu 1). O regula pe substring potriveste si operatii viitoare nevazute;FINALIZATAe 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