feat(ux): import compact + preview format Trimiteri + navigatie + scoatere auto_send (5.11)

8 stories TDD (echipa Sonnet, lead orchestreaza). US-001 scoate hold-ul auto_send din mapare
(has_no_auto_send->False, simbol pastrat; cod rezolvat->queued). US-002 scoate bifa auto_send
din UI. US-003 preview pas 3 in format .tabel-trimiteri (STARI_PREVIEW + nota_umana_preview,
fara repr Python; view-model prez). US-004 filtre layout/stil ca referinta + buton Custom.
US-005 navigatie Trimiteri/Mapari sub contoare pe toate paginile. US-006 import <details> nativ
colapsabil. US-007 post-commit reveal (OOB _coada/_status + HX-Trigger). US-008 auto-refresh
dupa actiuni (nudge eliminat).

VERIFY context curat PASS (8/8). /code-review high: 3 buguri reparate (tab nav la self-refresh,
pill Custom valori stale, nota_umana_preview precedenta needs_mapping). 934 passed, 1 skipped.
Backend trimitere + schema NEATINSE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-26 15:16:28 +00:00
parent 412102b9b1
commit 283299ff20
34 changed files with 3079 additions and 389 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,567 @@
<!-- /autoplan restore point: /home/claude/.gstack/projects/romfast-rar-autopass/main-autoplan-restore-20260626-095534.md -->
# 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 `<div id="status-bar">` — 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 `<details>` 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 — `<details id="import-details" {open if not are_trimiteri}>` 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.
---
<!-- ============================================================= -->
<!-- /autoplan REVIEW REPORT — generat 2026-06-26, commit 412102b -->
<!-- Codex indisponibil (usage limit) -> dual voices = subagent-only -->
<!-- ============================================================= -->
# 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 `<details>` 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 <details> (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
(`<details>`) — 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.
<!-- AUTONOMOUS DECISION LOG -->
## 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: `<details>` 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.