Files
rar-autopass/docs/prd/prd-5.11-ux-import-compact-preview-navigatie.md
Claude Agent 283299ff20 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>
2026-06-26 15:16:28 +00:00

568 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- /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.