# PRD 5.11 — Import compact + preview in format Trimiteri + navigatie + simplificare auto_send **Stare**: inchis (2026-06-26 — verify-pass + code-review; vezi RAPORT VERIFY si RAPORT AUTOPLAN) > Proces complet: `docs/ROADMAP.md` §5. Contractul RAR (sursa de adevar de contract): > `docs/api-rar-contract.md`. Starea trece: `draft → aprobat → in-executie → verify-pass → inchis` > (actualizata de lead). Acest PRD nu repeta strategia/contractul — le linkeaza. ## 1. Obiectiv Reparam un set de bug-uri si frictiuni UX descoperite la dogfooding pe o baza goala (prima utilizare), confirmate in browser (E2E Playwright pe `prezentari_test.csv`): 1. **Dupa primul import ramai blocat** — wizard-ul se reseteaza la pasul 1, mesajul „vezi mai jos in Trimiterile tale" trimite la o sectiune care **nu exista in DOM** la first-run (`trimiteri-section` absent), iar lista nu apare fara reload manual. 2. **Navigatie infundata** — din `?tab=mapari` / `?tab=jurnal` (si Cont/Integrare/Nomenclator) nu exista niciun link inapoi la Trimiteri; logo-ul nu e link; meniul hamburger nu are „Trimiteri". 3. **Tabelul de preview (pasul 3) e neingrijit** — overflow orizontal taie coloanele Verificat?/Actiuni (butonul Editeaza), formularul de editare inline se taie, coloana „Note" scapa un **repr Python brut** (`[{'cod_op_service': ...}]`) iar „Stare" arata coduri brute (`needs_mapping`/`needs_data`). 4. **`auto_send=0` produce o stare falsa** — un rand mapat (ex. OE-2) apare ca `needs_mapping` cu mesaj tehnic („cod mapat cu auto_send=0…") si fara actiune; bifa „In coada automat" adauga complexitate si confuzie. **Decizie utilizator (2026-06-26): scoatem complet conceptul auto_send din UI.** 5. **Randul de filtre arata neuniform** — pill-urile au stil outline diferit de butonul solid Filtreaza si stau in dreapta lui; la hover deveneau rosu plin si textul devenea ilizibil. 6. **Wizard-ul de import ocupa tot ecranul** — stepper + upload sunt doua carduri mari stivuite. Toate sunt **UI/UX + o simplificare de mapare** (auto_send). Backendul de trimitere (worker, masina de stari de trimitere, idempotenta, contract RAR) ramane neatins, cu o singura exceptie controlata: logica de mapare nu mai „tine" randuri pe `auto_send` (US-001). ## 2. Non-Goals (anti scope-creep) - **Nu** atingem worker-ul, reconcilierea, idempotenta sau contractul RAR. - **Nu** schimbam canalul API (`POST /v1/prezentari` / `/valideaza`) — doar UI web + `mapping.py`. (Campul `auto_send` din payload-ul API, daca exista, ramane acceptat dar ignorat — vezi US-001.) - **Nu** stergem coloanele DB `operations_mapping.auto_send` / `operation_text_rules.auto_send` (migrare distructiva inutila) — le lasam cu default `1` si nu le mai citim pentru a tine randuri. - **Nu** rescriem categoriile de filtrare cu etichetele din referinta (Facturate/Anulate/Diferente etc.) — pastram categoriile reale autopass (Toate / needs_mapping / needs_data / error). Adoptam doar **layout-ul si stilul** vizual din referinta (`image.png`). - **Nu** schimbam fluxul de erori pe 3 niveluri (5.4) — doar consumam helperele existente in preview. ## 3. Stories atomice > Fiecare story: cea mai mica unitate care lasa sistemul functional. Backend + UI pentru acelasi > comportament = 2 stories. `Fisiere` + `Depinde de` complete (decid paralelizarea). ### US-001: Scoate „hold pe auto_send" din logica de mapare (backend) **Ca** operator **vreau** ca o operatie mapata la un cod RAR sa intre direct in coada **pentru ca** bifa auto_send introducea o stare `needs_mapping` falsa si confuza pe randuri deja corecte. - **Depinde de**: — - **Fisiere**: `app/mapping.py`, `app/web/routes.py` (DOUA puncte: `post_mapeaza_inline` ~1018-1069 parsare form `auto_send` + delegare `reresolve_account`; ramura `has_no_auto_send` din `post_corectie_trimitere` ~1166 — referinta „~1160-1185" din draft arata gresit catre mapeaza), **`app/api/v1/import_router.py`** (importa `has_no_auto_send` ~linia 48, il foloseste in `_resolve_row_for_preview` ~233 — OMIS in draft; vezi AC „nu sterge simbolul"), `tests/test_mapping.py`, `tests/test_web_mapeaza.py`, **`tests/test_t6_auto_send.py` + `tests/test_text_rule_autosend.py` (de sters/rescris — encodeaza invariantul VECHI; vor deveni RED)** (~6 fisiere) - **Test intai (RED)**: `tests/test_mapping.py` — `test_operatie_mapata_intra_in_queued_indiferent_de_autosend`, `test_regula_text_rezolvata_nu_mai_tine_randul`, `test_fara_stare_needs_mapping_pe_auto_send_oprit`, `test_niciun_rand_existent_nu_se_dezgheata` (AC dezghet), `test_canal_api_auto_send_ignorat_intra_queued` (`classify_prezentare`, `mapping_meta` auto_send=0 → `queued`) - **Acceptance criteria**: - [ ] `resolve_prestatii` NU mai marcheaza `regula_fara_autosend` / nu mai produce ramura `AUTO_SEND_OPRIT`; o operatie cu cod rezolvat (mapare exacta SAU regula text) → `queued`. - [ ] **`has_no_auto_send` NU se sterge — se neutralizeaza (`return False`) si ramane DEFINIT** (importat in `routes.py:70` SI `import_router.py:48`; stergerea simbolului → `ImportError` la load → app nu porneste). „Eliminat" si „intoarce mereu False" NU sunt echivalente: alege a doua. - [ ] Toate cele 4 callsite-uri tratate: clasificare (`mapping.py` ~413), `reresolve_account` (~664 + cheia stat `review_manual`), corectie (`routes.py` ~1166), preview (`import_router.py` ~233). - [ ] `/trimitere/{id}/mapeaza`: dupa mapare, randul cu cod valid trece `queued` (nu `needs_mapping` cu mesaj `auto_send`); raspuns fara cardul „auto-send oprit". - [ ] `needs_mapping` ramane DOAR pentru operatii fara cod RAR rezolvat (semantica reala). - [ ] Coloanele DB raman; `save_mapping` accepta inca `auto_send` (default 1) dar valoarea nu mai tine randuri (compat migrare). Niciun rand existent nu se „dezgheata" automat fara actiune. - [ ] **Randuri legacy `needs_mapping`-din-auto_send (cod deja prezent)** raman blocate pe test/prod fara afordanta de mapare (`_nemapate_pentru_submission` ~881 intoarce `[]`). DECIZIE explicita: requeue one-time la deploy (`reresolve_account` per cont, acum le trece `queued`) SAU documentam corectia inline ca singura iesire. (De notat in raportul VERIFY.) - **Verificare E2E**: `POST /trimitere/{id}/mapeaza` din panoul de detaliu (browser) → rand `queued`. ### US-002: Scoate bifa „In coada automat" din UI (Mapari + preview + detaliu) **Ca** operator **vreau** sa nu mai vad bifa auto_send nicaieri **pentru ca** nu mai are efect (US-001) si crea confuzie. - **Depinde de**: US-001 - **Fisiere**: `app/web/templates/_macros.html` (macro `autosend_toggle`), `app/web/templates/_mapari.html`, `app/web/templates/_preview_import.html`, `app/web/templates/_trimitere_detaliu.html`, `tests/test_web_mapari.py` (~5 fisiere) - **Test intai (RED)**: `tests/test_web_mapari.py` — `test_mapari_fara_toggle_autosend`, `test_preview_panou_mapare_fara_autosend`, `test_detaliu_mapare_inline_fara_autosend` - **Acceptance criteria**: - [ ] Macro-ul `autosend_toggle` eliminat (sau golit) si scos din cele 3 sabloane. - [ ] Panoul „Operatii de mapat la cod RAR" (preview) si tab-ul Mapari salveaza maparea fara nicio referinta la „In coada automat" / „auto_send". - [ ] Coloana „In coada" din tabelul Mapari operatii salvate + reguli text dispare. - [ ] Niciun text rezidual „auto_send" / „Tine pentru verificare" in sabloane. - **Verificare E2E**: browser pe `/?tab=mapari` + panoul de mapare din preview — fara bifa. ### US-003: Preview pas 3 — format identic cu tabelul Trimiteri (UI) **Ca** operator **vreau** ca tabelul de preview sa arate exact ca lista Trimiteri **pentru ca** acum scapa text intern brut, taie coloane si formularul de editare. - **Depinde de**: — - **Fisiere**: `app/web/templates/_preview_import.html`, `app/web/templates/_preview_rand.html`, `app/web/templates/base.html` (refolosire `.tabel-trimiteri` + latimi `col-*` pt coloanele extra), **`app/web/payload_view.py`** (`prezentare_din_payload` accepta dict → `row.resolved` direct; OMIS), **stratul de adaptare in builderele de preview** (`app/web/routes.py` `_web_compute_preview` ~1851 SI `app/api/v1/import_router.py` `_resolve_row_for_preview` ~122) — construieste un view-model tip `prez` + traduce `resolved_status`→eticheta, OMIS in draft, `app/web/labels.py` (NU reutilizare directa — vezi AC; necesita un map de stari PREVIEW), `tests/test_preview_import.py` (~6 fisiere) - **Test intai (RED)**: `tests/test_preview_import.py` — `test_preview_nu_contine_repr_python` (fixture cu rand `needs_mapping`/unmapped REAL — altfel trece in gol), `test_preview_stare_eticheta_umana` (acopera `ok`/`needs_review`/`already_sent`/`duplicate_in_file`), `test_preview_foloseste_clasa_tabel_trimiteri` - **Acceptance criteria**: - [ ] **NU reutiliza `eticheta_stare`/`eticheta_scurta` direct: ridica `KeyError` pe starile de preview** (`ok`/`needs_review`/`already_sent`/`duplicate_in_file` — absente in `STARI_SUBMISSION`). Adauga un map de stari preview→eticheta umana (extinde `labels.py` sau map nou), inclusiv text pentru `already_sent`/`duplicate_in_file`. - [ ] **NU pasa `row.errors` (lista Python) in `motiv_uman`/`parse_erori`** (asteapta string JSON → `str()` + `json.loads` esueaza → fallback `raw[:160]` = ACELASI repr pe care US-003 il repara). Stratul de adaptare serializeaza erorile (`json.dumps`) sau formateaza uman inainte de render. - [ ] Tabelul de preview foloseste `.tabel-trimiteri`, fara overflow la 1280px (`scrollWidth <= clientWidth`). Necesita: `data-eticheta` pe TOATE celulele (carduri <768px le citesc via `td::before`) + latimi `col-*` pt cele 4 coloane extra (Verificat?/Actiuni/Note/etc.) sub `table-layout:fixed`. - [ ] Coloana „Note" → mesaj uman, **niciodata** repr Python (`[{'cod_op_service': ...}]`). - [ ] „Stare" → pill uman (ca la Trimiteri), nu cod brut `needs_mapping`/`needs_data`. - [ ] Vehicul (nr + VIN sub), operatie (+ cod RAR sub) randate via view-model `prez` (din `payload_view`). - [ ] Formularul inline `colspan` e **full-width sub rand** dar IESE din grila `table-layout:fixed` (rand `display:block` sau editor in afara tabelului fix), cu Salveaza/Anuleaza mereu vizibile. - [ ] Stare „filtrat la zero" (filter JS din `_preview_import.html` ascunde toate randurile) afiseaza un mesaj, nu tabel gol mut; filtrul coexista cu cardurile `td{display:block}`. - **Verificare E2E**: browser pasul 3 — coloane intregi, fara repr brut, editare necliata, mobil + desktop. ### US-004: Randul de filtre Trimiteri — layout + stil ca referinta (UI) **Ca** operator **vreau** un rand de filtre uniform si lizibil **pentru ca** pill-urile aveau alt stil decat butonul Filtreaza, stateau in dreapta si la hover deveneau rosu ilizibil. - **Depinde de**: — - **Fisiere**: `app/web/templates/_coada.html`, `app/web/templates/_pills.html`, `app/web/templates/base.html` (CSS `.pill-cat` + hover + layout), `tests/test_web_filtre.py` (~4 fisiere) - **Test intai (RED)**: `tests/test_web_filtre.py` — `test_pill_uri_in_stanga_controalelor`, `test_pill_categorie_stil_uniform`, `test_quick_pills_data_seteaza_interval` - **Acceptance criteria**: - [ ] Layout ca `image.png`: pill-uri rapide de data (Azi / 7 zile / 30 zile / Custom) in STANGA, camp de cautare la mijloc, pill-uri de stare cu contoare la dreapta — un singur stil de pill. - [ ] Pill-urile NU mai stau `margin-left:auto` izolate la dreapta butonului. - [ ] **Stare activa, dezambiguizata**: pill-ul „Toate" (reset) = `--accent` plin; pill-urile de CATEGORIE = culoarea lor de categorie cand sunt active (NU toate accent — „accent plin" din draft contrazice schema per-categorie din `_pills.html`). Enumera token-ul de prim-plan per categorie ca sa garantezi AA, SAU foloseste o pereche fixa (alb pe saturat) cu valori verificate. - [ ] **Hover lizibil, regula explicita**: hover = `background:color-mix(in srgb, currentColor 12%, transparent)` (nu `filter:brightness` actual, nu rosu plin); pill-ul ACTIV suprima hover-ul. Acelasi mecanism pe reset si pe categorie. - [ ] **Focus pastrat**: inelul `:focus-visible` (outline accent) ramane pe toate cele 3 variante de pill dupa rescrierea CSS (US-004 atinge exact acest bloc — usor de pierdut). - [ ] Quick-pills de data seteaza `data_de`/`data_pana` (preset) si reincarca lista (HTMX), pastrand pill-ul de stare activ. - [ ] Functioneaza in Light/Dark/Petrol cu token-uri concrete enumerate (nu „verifica AA" abstract). - **Verificare E2E**: browser — comutare quick-pill data + stare, hover citibil pe toate temele. ### US-005: Link-uri de navigatie sub contoare pe toate paginile (UI) **Ca** operator **vreau** sa ajung oricand inapoi la Trimiteri / Mapari **pentru ca** din Mapari/Jurnal nu mai aveam cale de intoarcere. - **Depinde de**: — - **Fisiere**: `app/web/templates/base.html` (header + `#cont-menu`), **AMBELE** `dashboard.html` SI `_status.html` (ambele randeaza `
` — pe cai diferite: full-page vs OOB/partial; „sau" din draft risca nav prezenta la load dar pierduta la refresh + duplicat de id), `tests/test_web_nav.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_nav.py` — `test_nav_trimiteri_mapari_pe_mapari`, `test_nav_trimiteri_pe_jurnal`, `test_logo_linkeaza_acasa` - **Acceptance criteria**: - [ ] Sub contoarele din `#status-bar`, pe FIECARE pagina (acasa/mapari/jurnal/cont/integrare/ nomenclator): rand de link-uri `Trimiteri` + `Mapari` (cu badge needs_mapping daca exista), cu marcaj activ pe pagina curenta. **Sursa marcajului activ = o variabila de context numita explicit (ex. `tab_activ`, comparata cu `?tab=`), nu ghicita de implementator.** - [ ] Badge-ul `needs_mapping` reutilizeaza O SINGURA sursa (acelasi camp ca `_mapari_badge` din hamburger / `blocate_total`), nu un al doilea contor calculat diferit. - [ ] Logo-ul ROMFAST + titlul linkeaza la `/` (Trimiteri). - [ ] Meniul hamburger capata si „Trimiteri" (Acasa) ca prima intrare. - [ ] Niciun deadlock: din orice tab se ajunge la Trimiteri intr-un click. - **Verificare E2E**: browser — din Mapari si Jurnal, un click → Trimiteri. ### US-006: Import = container compact colapsabil (UI) **Ca** operator **vreau** ca importul sa nu ocupe tot ecranul **pentru ca** stepper-ul + upload-ul mare stau in cale dupa ce am deja trimiteri. - **Depinde de**: — - **Fisiere**: `app/web/templates/_acasa.html`, `app/web/templates/_upload.html`, `app/web/templates/_stepper.html`, `app/web/templates/base.html` (CSS/JS accordion), `tests/test_web_acasa.py` (~4 fisiere) - **Test intai (RED)**: `tests/test_web_acasa.py` — `test_import_colapsat_cand_are_trimiteri`, `test_import_deschis_la_first_run` - **Acceptance criteria**: - [ ] Stepper + upload intr-UN singur container („Importa un fisier"), nu doua carduri. - [ ] Colapsat implicit cand contul are deja trimiteri (`are_trimiteri=True`); auto-deschis la first-run (`are_trimiteri=False`). - [ ] In timpul fluxului (mapcoloane/preview) containerul ramane deschis (nu se inchide intre pasi). - [ ] **Implementare cu `
` nativ** (disclosure CSS-only): serverul seteaza atributul `open` din `are_trimiteri`, deci „fara JS → degradare la deschis" si „colapsat la returning" sunt ambele corecte fara toggle JS (un toggle JS pur ar lasa returning-user-ul fara-JS cu ecranul permanent deschis = exact bug-ul reparat). `aria-expanded`/focus pastrate. - [ ] Ordinea de stivuire definita pe cele 4 combinatii (`are_creds` × `are_trimiteri`) — accordion, „Primii pasi", `trimiteri-section` nu se suprapun la first-run-cu-creds. - **Verificare E2E**: browser — first-run deschis; dupa ce exista trimiteri, colapsat; click → extins. ### US-007: Dupa commit, lista Trimiteri apare + se reimprospateaza automat (UI + glue) **Ca** operator **vreau** sa-mi vad trimiterile imediat dupa import **pentru ca** acum mesajul trimite la o sectiune inexistenta si nimic nu se actualizeaza fara reload. - **Depinde de**: US-006 - **Fisiere**: `app/web/routes.py` (`web_confirma_import` ~2757 intoarce AZI doar `_upload.html` via `hx-target=#import-section, outerHTML` — un swap `outerHTML` pe `#import-section` NU poate materializa un frate `#trimiteri-section`; alege mecanismul de dezvaluire — vezi AC), `app/web/templates/_upload.html`, `app/web/templates/_acasa.html`, `app/web/templates/_coada.html`, **`app/web/templates/_status.html` + `dashboard.html`** (OOB pe `#status-bar` — e in afara lui `acasa-section`, nu se actualizeaza la re-randarea `_acasa`/`_upload`), `tests/test_import_commit.py` (~6 fisiere) - **Test intai (RED)**: `tests/test_import_commit.py` — `test_commit_raspuns_contine_trimiteri_section`, `test_commit_raspuns_seteaza_hx_trigger_trimiteriChanged` (asertie pe header literal), `test_first_run_dupa_commit_arata_lista`, `test_commit_actualizeaza_status_bar` (OOB contoare) - **Acceptance criteria**: - [ ] **Mecanism de dezvaluire ales explicit**: fie (a) re-randam `_acasa.html` complet si retargetam pe `#acasa-section`, fie (b) emitem un OOB swap care injecteaza `_coada.html`. Minimal fata de codul actual (return `_upload.html`-only) = **(b)**. La first-run `#submissions-wrap` proaspat injectat isi declanseaza singur `hx-trigger="load"`. - [ ] **Raspunsul seteaza `HX-Trigger: trimiteriChanged`** (lipseste azi pe confirma; cf. `post_mapeaza_inline:1066`) — altfel la returning-user (sectiune deja in DOM) randurile noi `queued` nu apar. Atentie la dublu-load daca re-randam si `_acasa` (inofensiv, de notat). - [ ] Containerul de import se colapseaza, iar lista Trimiteri se reincarca automat si arata randurile. - [ ] **Contoarele `#status-bar` se actualizeaza via OOB swap** (sunt in `_status.html`/`dashboard.html`, in afara `acasa-section` — re-randarea `_acasa` nu le atinge), imediat, nu la poll-ul de 15s. - [ ] Mesajul de succes ramane onest („S-au pus in coada N prezentari") si pointeaza la o sectiune care chiar exista. - **Verificare E2E**: browser, baza goala — import → commit → lista apare + contoare actualizate, fara reload. ### US-008: Auto-refresh dupa actiuni, fara „Reincarca" manual (UI) **Ca** operator **vreau** ca lista sa se actualizeze singura dupa ce actionez **pentru ca** butonul manual „Reincarca" (nudge „Date noi") trecea neobservat (ex. dupa ce am mapat un cod si s-a trimis la RAR). - **Depinde de**: US-007 - **Fisiere**: `app/web/templates/base.html` (JS poller/nudge), `app/web/templates/_coada.html`, `app/web/templates/_submissions.html`, `tests/test_web_refresh.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_refresh.py` — `test_actiune_proprie_reincarca_automat`, `test_nudge_nu_mai_blocheaza_actualizarea` - **Acceptance criteria**: - [ ] Dupa o actiune proprie (mapare inline, corectie, repune, commit import) lista se reincarca automat (fara click pe „Reincarca"). - [ ] Poll-ul de fundal: cand detecteaza date noi declansate de actiuni proprii, aplica refresh automat; nudge-ul manual ramane doar pentru schimbari externe (alt proces/worker) — sau se elimina daca devine redundant. (Decizie de implementare documentata in raport.) - [ ] Nu se reseteaza filtrul/pagina curenta la auto-refresh (pastreaza `#filtre-trimiteri`). - **Verificare E2E**: browser — mapare inline a unui cod → randul devine `queued`/`sent` fara click manual. ## 4. Riscuri - **R1 — Scoaterea auto_send rastoarna „default-ul de siguranta CEO"** (reguli text / mapari tineau randul pentru verificare). Acceptat constient de utilizator (2026-06-26): mapped → queued direct. Mitigare: randurile genuin nemapate raman `needs_mapping` (nu pleaca), iar preview-ul are gate per-rand („Verificat?") inainte de commit. Documentam in CLAUDE.md (invariantul auto_send) ca a fost retras. - **R2 — Preview in format Trimiteri**: tabelul Trimiteri presupune un view-model (vehicul/op/cod RAR); randurile de preview vin din alt drum (`_resolve_row_for_preview`). Risc de divergenta de campuri. Mitigare: refolosim `payload_view` / `labels` ca pe canalul Trimiteri; test anti-repr. - **R3 — Accordion + HTMX**: colapsarea nu trebuie sa ascunda pasii in timpul fluxului mapcoloane/preview. Mitigare: container deschis cat timp `import-section` randeaza un pas != upload slim; test dedicat. - **R4 — Regresie tabel**: `.tabel-trimiteri` are reguli responsive (carduri <768px) — preview-ul are coloane diferite (Verificat?/Actiuni). Mitigare: verificare E2E mobil + desktop. ## 5. Intrebari deschise > Rezolvate cu utilizatorul la planificare (2026-06-26) — vezi raspunsurile incorporate mai sus: > Q1 filtre = **layout complet ca referinta** + fix hover lizibil; Q2 preview = **format ca Trimiteri**; > Q3 = **scoatem complet bifa auto_send** (Mapari + Trimiteri); Q4 = **acordeon compact + lista apare/ > refresh automat dupa commit** + link-uri Trimiteri/Mapari sub contoare. - (rezolvat) Pastram coloanele DB `auto_send`? → DA, default 1, ne-citite pentru hold (non-distructiv). - (deschis, minor) Nudge-ul „Date noi" pentru schimbari externe: il pastram redus sau il eliminam? → decizie la implementare (US-008), documentata in raport. ## 6. Valuri de executie (graful de dependente) ``` Val 1 (paralel, fisiere disjuncte): [US-001] mapping.py + routes mapeaza (backend) [US-003] preview format Trimiteri (_preview_*.html, base.css) [US-004] filtre layout+stil+hover (_coada/_pills/base.css) [US-005] navigatie sub contoare (base.html/_status/dashboard) [US-006] import accordion compact (_acasa/_upload/_stepper) Val 2 (deblocate de Val 1): [US-002] scoate toggle auto_send din UI (dep US-001; _macros/_mapari/_preview/_detaliu) [US-007] post-commit reveal+refresh (dep US-006; routes/_acasa/_upload/_coada) Val 3: [US-008] auto-refresh dupa actiuni (dep US-007; base.js/_coada/_submissions) ``` Atentie la fisiere fierbinti partajate intre stories (serializare de catre lead): `base.html` (US-004 CSS, US-005 header/menu, US-006 accordion JS) si `_coada.html` / `_preview_import.html` apar in mai multe stories — NU paralel pe acelasi fisier (vezi ROADMAP §5.5). --- ## Raport VERIFY > Completat de subagentul verificator (context curat, 2026-06-26) in faza VERIFY — ROADMAP §5.6. > Executie: echipa de teammates Sonnet (lead orchestreaza, NU scrie cod), 6 runde TDD pe valuri cu > fisiere disjuncte; `base.html`/`routes.py`/`_coada.html`/`_status.html` serializate ca fisiere fierbinti. **VERDICT GLOBAL: PASS → verify-pass / CLOSE.** 8/8 stories trec criteriile de acceptare, cu dovezi cod + teste + randare runtime (verificatorul a pornit aplicatia reala si a inspectat HTML-ul randat). - **Suita**: `python3 -m pytest -q` → **929 passed, 1 skipped** (skip = `test_live_rar`, gated corect pe marker `live` + `AUTOPASS_LIVE_RAR`). Smoke boot `python3 -c "import app.main"` → OK (critic US-001: `has_no_auto_send` pastrat → fara ImportError). (Dupa adaugarea testelor cu nomenclatura PRD pe US-008: 931 passed.) - **US-001** PASS — `has_no_auto_send`→`return False` (definit + importat in routes.py:72 si import_router.py:48); `resolve_prestatii` nu mai marcheaza `regula_fara_autosend`; ramura `AUTO_SEND_OPRIT` scoasa din classify; cele 4 callsite tratate; canal API auto_send=0 → `queued`; `needs_mapping` doar pt unmapped real; coloane DB raman; teste vechi rescrise (nu sterse). Decizie randuri legacy: `reresolve_account` le re-trece `queued` la deploy (documentat). - **US-002** PASS — macro `autosend_toggle` golit, simbol pastrat; **0** reziduuri auto_send / „In coada automat" in template-uri (grep) + 0 in dashboard-ul randat. - **US-003** PASS — `STARI_PREVIEW` map nou in `labels.py` (fara KeyError pe stari preview); `nota_umana_preview` (errors ca lista → fara repr brut); view-model `prez` din `payload_view` (accepta dict); `.tabel-trimiteri` + `col-*` + `data-eticheta` pe toate celulele + editor `tr.preview-edit` (scapa grila fixa) + mesaj „filtrat la zero". - **US-004** PASS — quick-pills data STANGA / cautare MIJLOC / pills stare DREAPTA; hover `color-mix(currentColor 12%)`, `:focus-visible` accent, categorie activa=`currentColor`, reset=`--accent`, activ suprima hover. (Nota minora: butonul „Custom" — vezi mai jos.) - **US-005** PASS — nav Trimiteri+Mapari in `_status.html` randat pe fiecare pagina (dashboard.html incarca fragmentul, fara duplicat de id), activ din `tab_activ`, badge din sursa unica `counts.needs_mapping`; logo+titlu link `/`; hamburger „Trimiteri" prima. Confirmat runtime (`aria-current="page"` pe Mapari la `?tab=mapari`). - **US-006** PASS — `
` nativ (fara toggle JS); runtime: first-run `open` + placeholder `#trimiteri-section`; returning colapsat + lista plina. - **US-007** PASS — `web_confirma_import` → `_upload` slim + OOB `_coada` (#trimiteri-section) + OOB `_status` (#status-bar) + header `HX-Trigger: trimiteriChanged`; `#submissions-wrap` se auto-incarca; mesaj de succes onest catre o sectiune care chiar exista. - **US-008** PASS — actiuni proprii emit `trimiteriChanged` → reincarcare; poller compara `data-v` si cheama `reincarcaTrimiteri()` cu `hx-include="#filtre-trimiteri"` (pastreaza filtru+pagina); nudge „Date noi" ELIMINAT (decizie documentata: distinctia propriu-vs-extern nu e posibila pe client). - **E2E**: PROBAT la nivel de aplicatie reala (uvicorn :8011, login sesiune, HTML randat real pentru accordion/nav/logo/filtre/zero-reziduu auto_send). NEPROBAT: click-through Playwright complet pe upload→preview→commit si testul live RAR `FINALIZATA` (gated, fara creds web in mediu) — acelasi status ca livrabilele anterioare; backend trimitere neatins, risc minim. - **Regresia de aur**: PASS la nivel de teste — `test_api` (POST /v1/prezentari) + `test_import_e2e` (import→commit→queued) + worker reconcile/rar_errors verzi; worker/idempotenta/`build_key`/contract RAR NEATINSE (non-goals respectate). **Note minore (non-blocante, follow-up):** 1. US-004 „Custom": AC enumera 4 quick-pills (Azi/7zile/30zile/**Custom**); doar 3 randate ca butoane (JS-ul suporta ramura `'custom'`, dar fara buton). De adaugat butonul SAU scos „Custom" din AC. 2. Cod/comentarii vestigiale in `import_router.py` (`_motiv_clasificare` ramura `needs_mapping`/auto_send acum inaccesibila; comentarii care inca spun „auto_send gate"). Pur cosmetic. --- # RAPORT AUTOPLAN ## Faza 1 — CEO (strategie & scope) ### 0A. Provocarea premiselor - **Premisa centrala (US-001): "auto_send hold = stare falsa".** Citind `mapping.py:252-254` si `434-447`, hold-ul era un **gate de siguranta intentionat**, nu un bug. Reformulare onesta: scoatem un gate de siguranta pentru ca frictiunea > riscul de ireversibilitate perceput. **GATE PREMISA: utilizatorul a confirmat (2026-06-26) scoaterea completa** — premisa acceptata constient, documentata in R1. - Premisa "preview-ul are gate Verificat? care compenseaza" e PARTIALA: gate-ul exista doar pe calea import web; canalul API + remaparea inline (`routes.py:1166`) trec direct in `queued`. Acceptat de utilizator la gate. - Restul premiselor (overflow tabel, repr Python in Note, hover rosu ilizibil, nav infundata, wizard mare, reveal post-commit rupt) = **confirmate in cod** (vezi Faza 3). ### 0B. Ce exista deja (mapare sub-probleme -> cod) | Sub-problema | Cod existent de refolosit | |---|---| | Etichete umane stare/motiv | `labels.py: eticheta_stare, motiv_uman, parse_erori` | | Viewmodel rand trimitere | `payload_view.py` + `r.prez.*` in `_submissions.html` | | Tabel responsive | `.tabel-trimiteri` + carduri <768px in `base.html` | | Pills stare + contoare | `_pills.html` + `filtreazaStare()` in `base.html` | | Refresh lista | HTMX `trimiteriChanged from:body` (deja cablat in `_coada.html`) | | Sectiune trimiteri | `#trimiteri-section` exista in `_coada.html` (doar conditionat de `are_trimiteri`) | ### 0C. Dream-state delta - ACUM: 8 frictiuni de first-run, una mascand o decizie de siguranta ireversibila. - ACEST PLAN: first-run curat (import -> preview ingrijit -> commit -> lista apare/refresh), nav fara fundaturi, mapare fara concept auto_send. - IDEAL 12 luni: un **E2E smoke de first-run** ca poarta de release (codifica scriptul de dogfooding) ca aceste 8 simptome sa nu reapara. **Recomandare deferata (vezi TODOS).** ### 0C-bis. Alternative (US-001) | Abordare | Efort | Risc | Verdict | |---|---|---|---| | Scoatere completa (ales) | mic | gate pierdut pe API/inline | **ALES de utilizator** | | Hold doar pe text-rules unattended | mic | minim (pastreaza net unde conteaza) | respins de utilizator la gate | | Default per-cont (ca `on_unmapped_error_default`) | mediu | reversibil, dar pastreaza complexitate | respins de utilizator | ### 0D-0F. Mod = SELECTIVE EXPANSION. Premisa confirmata la gate. Procedam. ### Voci duale CEO (subagent-only; Codex indisponibil) **CLAUDE SUBAGENT (CEO):** 11 constatari. Critice: (F1) US-001 e o decizie clasa-siguranta impachetata cu UX cosmetic — risc de aprobare-prin-asociere; (F6) regret 6 luni = o regula text gresita auto-trimite FINALIZATA terminal nerecuperabil. Hi: F3 (premisa laundering), F4 (mitigare R1 supraevaluata — nu acopera canalul API), F5 (cazul periculos real = text-rules unattended), F8 (alternativa default-per-cont neanalizata), F10 (over-bundling). Recomandare subagent: split US-001/002 din restul. **Utilizatorul a ales full removal la gate — F4/F5/F6 raman ca follow-up optional, nu blocant.** ``` CEO DUAL VOICES — CONSENSUS: Dimensiune Claude Codex Consensus 1. Premise valide? Partial N/A flag (gate trecut de user) 2. Problema corecta? Da N/A confirmat de 1 voce 3. Scope calibrat? Nu(F10) N/A flag (over-bundle, dar waves ok) 4. Alternative explorate? Nu(F8) N/A flag (rezolvat la gate) 5. Riscuri piata/legal acoperite? Partial N/A flag (R1 acceptat) 6. Traiectorie 6 luni sanatoasa? Partial N/A flag (F6 -> TODOS) Codex = N/A (usage limit). Single-critical din 1 voce = flagat oricum. ``` ### NU in scope (confirmat) Worker / reconciliere / idempotenta / contract RAR / canal API (payload) / coloane DB auto_send (raman, default 1) / categorii filtrare (raman cele reale autopass) / fluxul de erori 3-niveluri. ### Registru Erori & Salvare (relevant acestui plan) | Suprafata | Esec | Salvare existenta | |---|---|---| | Import upload | fisier invalid / multi-foaie | `eroare_upload` / select foaie (`_upload.html`) | | Preview rand | cod necunoscut nomenclator | promovat la `cod_op_service` -> `needs_mapping` (nu se trimite raw) | | Commit | rand `needs_mapping` | exclus din commit (ramane in lista) | ### Sumar CEO Plan sanatos ca directie. Singurul flag strategic (US-001 fara control compensator pe calea unattended) **acceptat constient la gate**. US-003-008 = castiguri de first-run cu risc redus; US-007/008 cele mai sigure. Recomandare: pastram PRD-ul unit (waves deja serializeaza fisierele fierbinti), adaugam 1 item TODOS (E2E first-run + control optional text-rule). ## Faza 2 — Design (UI scope: DA) ### Voci duale Design (subagent-only; Codex indisponibil) **CLAUDE SUBAGENT (design):** constatari grounded in template-uri reale. - **F2.4 (CRITIC):** `eticheta_stare`/`eticheta_scurta` ridica `KeyError` pe starile de preview (`ok`/`needs_review`/`already_sent`/`duplicate_in_file`) — reutilizarea „ca la Trimiteri" crapa preview-ul. → US-003 AC. - **F3.1 (CRITIC):** „activa = accent plin" contrazice pill-urile colorate per-categorie (`_pills.html:10`). Reset = accent; categorie = culoarea ei. → US-004 AC. - **F2.1/F2.2/F2.3 (high):** celulele de preview n-au `data-eticheta` (carduri <768px ilizibile); coloanele extra n-au latimi `col-*` sub `table-layout:fixed`; editorul `colspan` mosteneste latimea care depaseste. → US-003 AC. - **F3.2/F3.3 (high):** token-uri de contrast/hover neenumerate per tema/categorie — cel mai mare risc de „haunt". → US-004 AC. - **F2.6 (high):** preview n-are view-model `prez`; adaptorul lipseste din Fisiere. → US-003 Fisiere. - **F4.1/F4.2 (high):** `#status-bar` definit in DOUA locuri; variabila de tab activ nenumita. → US-005 Fisiere+AC. - **F1.3 (medium):** degradarea fara-JS contrazice „colapsat la returning" — necesita `
` nativ. → US-006 AC. ``` DESIGN LITMUS — CONSENSUS: Dimensiune Claude Codex Consensus 1. Ierarhie info (accordion) ok* N/A confirmat (1 voce) *cu state-table 2. Stari lipsa (preview) NU N/A flag CRITIC (F2.4) -> US-003 3. Matrice pill hover/active/focus NU N/A flag CRITIC (F3.1) -> US-004 4. Nav activ + badge Partial N/A flag (F4) -> US-005 5. Specificitate AC Partial N/A flag (token-uri, view-model) Codex = N/A (usage limit). ``` ## Faza 3 — Eng (arhitectura, teste, risc) ### Diagrama de dependente (componente noi vs existente) ``` [import upload] -> _upload.html (#import-section, outerHTML swap) | US-006: invelit in
(open=are_trimiteri) v [web_confirma_import ~2757] --returneaza--> _upload.html (AZI: doar atat -> bug US-007) US-007 fix: + OOB inject _coada.html (#trimiteri-section) + HX-Trigger trimiteriChanged + OOB #status-bar (_status.html / dashboard.html) | v [#submissions-wrap] --hx-trigger: load, trimiteriChanged from:body--> /_fragments/submissions | | v v _submissions.html (r.prez view-model) _pills.html (OOB) <- US-004 CSS [mapping.py resolve_prestatii] --auto_send hold SCOS (US-001)--> status: queued | needs_mapping(real) has_no_auto_send: NEUTRALIZAT (return False), simbol PASTRAT (importat in routes.py + import_router.py) callsite-uri: clasificare ~413 | reresolve ~664 | corectie routes ~1166 | preview import_router ~233 [preview builders] _web_compute_preview ~1851 / _resolve_row_for_preview ~122 US-003: + adaptor resolved_status -> eticheta umana + view-model prez (payload_view) + json.dumps(errors) ``` ### Voci duale Eng (subagent-only; Codex indisponibil) **CLAUDE SUBAGENT (eng):** 13 constatari verificate in cod. - **F1 (HIGH/boot-crash):** `import_router.py:48` importa `has_no_auto_send`, `:233` il foloseste — al 4-lea callsite, OMIS din US-001 Fisiere. Stergerea simbolului → `ImportError` → app nu porneste. „Eliminat" ≠ „return False". → US-001. - **F4 (HIGH):** `test_t6_auto_send.py` + `test_text_rule_autosend.py` encodeaza holdul vechi → devin RED, neprogramate. → US-001. - **F5/F6 (HIGH):** `motiv_uman`/`parse_erori` asteapta string JSON; `row.errors` (lista) → fallback `raw[:160]` = ACELASI repr. Necesita strat de adaptare in buildere. → US-003. - **F2 (medium):** referinta de linie „~1160-1185" arata gresit; sunt 2 puncte (mapeaza ~1032 form, corectie ~1166). → US-001. - **F3 (medium):** randuri legacy needs_mapping-din-auto_send raman fara afordanta (`_nemapate_pentru_submission` ~881 → `[]`). → US-001 AC dezghet. - **F7 (medium):** `payload_view.py` necesar dar omis din US-003 Fisiere. → US-003. - **F8/F9/F10 (HIGH):** US-007 — `outerHTML` pe `#import-section` nu materializeaza frate `#trimiteri-section`; lipseste `HX-Trigger`; `#status-bar` (alt fisier) nu se actualizeaza. → US-007. - **F11/F13 (low):** waves §6 supravand paralelismul pe `base.html` (3 stories, regiuni disjuncte → serializate de lead); `save_mapping` scrie auto_send=0 dupa US-002 (inofensiv cat e ignorat). ``` ENG DUAL VOICES — CONSENSUS: Dimensiune Claude Codex Consensus 1. Arhitectura sanatoasa? Da N/A confirmat (1 voce) 2. Acoperire teste suficienta? NU N/A flag (F4 + gap-uri) -> test plan 3. Riscuri performanta? ok N/A nimic flagat (batch lookup already_sent fara N+1) 4. Securitate? ok N/A nimic nou (CSRF/auth neatinse de UI) 5. Cai de eroare tratate? Partial N/A flag (boot-crash F1, KeyError F5/F6) 6. Risc de deploy gestionabil? Partial N/A flag (randuri legacy F3) Codex = N/A (usage limit). ``` ### Test plan (artefact pe disc) `~/.gstack/projects/romfast-rar-autopass/main-5.11-test-plan-20260626.md` — 18 codepath-uri mapate la acoperire, suite de rulat (inclusiv smoke de boot `import app.main` dupa US-001), 3 gap-uri critice. ### Registru moduri de esec (cu flag-uri critice) | Mod de esec | Trigger | Gravitate | Mitigare in plan | |---|---|---|---| | App nu porneste | stergerea `has_no_auto_send` | CRITIC | US-001 AC: neutralizare, nu stergere + smoke boot | | Preview crapa la render | reutilizare `eticheta_stare` pe stari preview | CRITIC | US-003 AC: map de stari preview | | Repr Python reapare in Note | `row.errors` in `motiv_uman` | HIGH | US-003 AC: adaptor json.dumps | | Returning-user nu vede randuri noi | lipsa `HX-Trigger` pe confirma | HIGH | US-007 AC: header trimiteriChanged | | Randuri legacy blocate fara iesire | needs_mapping-din-auto_send pe prod | MEDIUM | US-001 AC: requeue one-time SAU doc corectie | ## Cross-phase themes - **Theme: vocabularul de stari preview ≠ submission** — flagat INDEPENDENT in Faza 2 (Design F2.4) si Faza 3 (Eng F5/F6). Semnal de incredere mare: US-003 nu poate reutiliza direct `labels.py`; necesita un strat de traducere + view-model. Cea mai importanta corectie a planului. - **Theme: Fisiere sub-dimensionate fata de AC** — US-001 (import_router), US-003 (payload_view + buildere), US-007 (_status.html) — toate omiteau fisiere pe care propriile AC le cer. Corectat in stories. ## Corectie graf de executie (§6) Wave 1 NU e complet paralel: `base.html` e atins de US-004 (CSS pill), US-005 (header/nav), US-006 (`
`) — regiuni disjuncte, dar lead-ul le SERIALIZEAZA (3 edituri pe acelasi fisier). Paralelismul real al Wave 1 e mai mic decat anunta graful. `_coada.html` (US-004→007→008) e serializat oricum de dependente. ## Deferate la TODOS.md - E2E smoke de first-run ca poarta de release (CEO F2). - Control compensator optional pe auto-trimitere unattended (CEO F5/F6) — risc rezidual acceptat de user. ## Decision Audit Trail | # | Faza | Decizie | Clasificare | Principiu | Rationament | Respins | |---|---|---|---|---|---|---| | 1 | CEO | Premisa auto_send: full removal | GATE (user) | — | Utilizatorul a confirmat la gate | hold text-rule-only; default per-cont | | 2 | CEO | Pastram PRD unit (nu split US-001/002) | Taste | P3/P6 | Waves serializeaza deja; pre-launch, risc real ~0 | split in 2 PRD-uri (subagent F1/F10) | | 3 | CEO | E2E first-run -> TODOS, nu in scope acum | Mechanical | P3 | Boil-lake separat, nu blocheaza 5.11 | inclus in 5.11 | | 4 | Eng | US-001: neutralizare has_no_auto_send, NU stergere | Mechanical | P5 | Stergerea = ImportError (import_router) = boot crash | stergere simbol | | 5 | Eng | US-001 Fisiere += import_router.py + teste vechi | Mechanical | P1/P2 | Callsite real + teste care devin RED | lasa Fisiere ca-n draft | | 6 | Eng | US-001: randuri legacy -> decizie requeue/doc | Taste | P1 | In blast radius; impact pe prod DB | ignora (lasa blocate tacit) | | 7 | Design+Eng | US-003: strat adaptare + map stari preview | Mechanical | P1 | Reutilizare directa crapa/repr — cross-phase theme | reutilizare directa labels | | 8 | Design+Eng | US-003 Fisiere += payload_view + buildere | Mechanical | P1/P2 | AC cere view-model prez inexistent pe calea preview | doar template+labels | | 9 | Design | US-003: data-eticheta + col-* + colspan escape | Mechanical | P1 | Altfel overflow/mobil rup (AC propriu) | doar clasa .tabel-trimiteri | | 10 | Design | US-004: reset=accent, categorie=culoare proprie | Mechanical | P5 | „accent plin" contrazice schema per-categorie | toate accent | | 11 | Design | US-004: token-uri hover/active/focus enumerate | Mechanical | P1/P5 | „verifica AA" abstract = haunt | lasa la implementator | | 12 | Design | US-005: AMBELE status-bar + var tab_activ | Mechanical | P5 | „sau" risca nav pierduta la refresh + dup id | un singur fisier | | 13 | Design | US-006: `
` nativ | Mechanical | P5 | Toggle JS pur rupe degradarea fara-JS | toggle JS | | 14 | Eng | US-007: OOB inject _coada + HX-Trigger + OOB status-bar | Mechanical | P1/P5 | outerHTML nu materializeaza frate; lipsa trigger | „include/dezvaluie" vag | ## Stare review - Faza 1 CEO: rulat (subagent-only). Premisa confirmata la gate. - Faza 2 Design: rulat (subagent-only). 2 critice -> AC corectate. - Faza 3 Eng: rulat (subagent-only). 1 boot-crash + cross-phase theme -> stories corectate. - Faza 3.5 DX: SARIT — fara suprafata developer-facing (planul exclude canalul API; UI = operator). - Codex: indisponibil toata sesiunea (usage limit pana Jul 18) -> toate vocile = subagent-only.