Files
rar-autopass/docs/prd/prd-3.6-editare-preview-acasa-unificata.md
Claude Agent 35f35d03cc fix(web): protejeaza decriptarea override_json in preview + inchide 3.6 (CLOSE)
decrypt_creds(override_json) era in afara try/except-ului care protejeaza
raw_json in preview_import (import_router) si _web_compute_preview (routes).
La rotatie cheie Fernet (risc acceptat R4) sau token corupt, raw_json degrada
gratios la {} dar override_json arunca 500 pe tot batch-ul. Acum ambalat
identic (fallback None -> {}).

Prins de /code-review high la CLOSE. Writeback: ROADMAP 3.6 -> DONE,
PRD -> inchis + Raport CLOSE. Duplicare _override_of/canonicalize notata
ca cleanup viitor (disciplina backend-neatins). 523 teste pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:09:03 +00:00

48 KiB
Raw Blame History

PRD 3.6 — Editare celule in preview + Acasa unificata (Trimiteri inline, upload slim, Mapari tabelar)

Stare: inchis (2026-06-22 — CLOSE: /code-review high a prins 1 bug real, reparat; dashboard ROADMAP → DONE. Toate US-001..007 implementate, 523 teste pass, VERIFY E2E browser + LIVE RAR test PASS; vezi ## Raport VERIFY si ## Raport CLOSE)

Proces complet: docs/ROADMAP.md §5. Contract RAR (sursa de adevar): docs/api-rar-contract.md. Starea trece: draft → aprobat → in-executie → verify-pass → inchis (actualizata de lead). Continua 3.5 (prd-3.5). Backend trimitere (worker, masina stari, idempotenta-logica, mapping-rezolvare) NU se atinge — doar canalul de import si stratul web. Exceptie schema decisa la poarta autoplan: UNA coloana nullable import_rows.override_json (Approach B), cu migrare defensiva _migrate ca la 3.3b/3.5. Worker/idempotenta/mapare raman neatinse.

1. Obiectiv

Dupa upload, utilizatorul putea citi randurile cu probleme dar nu le putea corecta inainte de trimitere — singura editare de continut era post-confirmare, in alt tab. Livram: (a) editare de celule direct in preview (buton "Editeaza" pe rand), si (b) reunificarea fluxului pe pagina Acasa — Trimiterile devin o sectiune permanenta sub upload (tab-ul "Trimiteri" dispare), zona de upload se comprima la o bara slim, iar "Mapari" trece pe format tabelar compact cu eticheta auto-send reformulata ca un comutator Automat/Manual.

Motivatie: microcopia spune "vezi mai jos trimiterile", dar trimiterile erau intr-un alt tab; cazul real de utilizare (fisier cu o data/VIN lipsa sau gresit) cerea re-upload in loc de o corectie pe loc.

2. Non-Goals (anti scope-creep)

  • Fara editare a operatiei/codului RAR in celula — codul de operatie ramane rezolvat prin panoul de mapare existent (needs_mapping). Editarea de celule acopera campurile de continut: VIN, nr. inmatriculare, data prestatie, odometru initial/final. (Operatia se schimba din panoul de mapare, nu din tabel.)
  • Fara editare in bloc / multi-rand — un rand pe rand, mod editare explicit.
  • Schema: o singura coloana nouaimport_rows.override_json (nullable, criptat Fernet), patch CANONIC peste randul mapat (Approach B). NU se modifica raw_json (ramane cheiat pe anteturile fisierului). Restul schemei neatins.
  • Fara atingerea logicii de idempotenta, validare, mapare sau a worker-ului — editarea = mutatie pura de stocare; recalculul de stare merge OBLIGATORIU prin _resolve_row_for_preview (un singur clasificator, fara drift).
  • Fara reorganizarea tab-urilor ramase (Mapari/Cont/Nomenclator) dincolo de scoaterea tab-ului Trimiteri.
  • Fara paginare noua pe Trimiteri — sectiunea de pe Acasa refoloseste filtrarea existenta (US-009 din 3.5).

3. Stories atomice

Backend + UI pentru acelasi comportament = stories separate. Toate rutele web noi sunt sub require_login si scoped pe contul din sesiune (gard cross-account 404, identic cu rutele existente). CSRF pe toate POST-urile.

US-001: Backend — persista editarea unui rand de preview

Ca utilizator care vede un rand cu date gresite/lipsa in preview vreau sa salvez valori corectate pentru acel rand pentru ca sa-l trimit fara re-upload de fisier.

  • Depinde de: —
  • Fisiere: app/schema.sql (coloana override_json + _migrate), app/api/v1/import_router.py (_resolve_row_for_preview + commit_import aplica override; ruta noua), tests/test_import_edit_row.py, tests/fixtures/import_antet_necanonic.csv (NOU), tests/fixtures/import_lipsa_coloana.csv (NOU) (~5 fisiere)
  • Stocare (Approach B, decis la poarta autoplan): editarea scrie un dict CANONIC in import_rows.override_json (nullable, criptat Fernet). _resolve_row_for_preview si commit_import aplica mapped.update(override_json) ULTIMUL, dupa maparea json_mapare. Astfel se poate completa si un camp a carui coloana LIPSESTE din fisier (cazul "completez informatii lipsa"), iar raw_json/idempotency raman neatinse.
  • Test intai (RED): tests/test_import_edit_row.pytest_editeaza_rand_antet_necanonic_devine_ok (fixture cu antet Serie sasiu/Data — prinde bug-ul de stocare), test_editeaza_completeaza_coloana_absenta (fisier fara coloana data → editarea adauga data → ok), test_editeaza_status_identic_cu_GET_preview (ruta editare NU re-deriva status; egal cu GET /preview), test_editeaza_rand_scoped_alt_cont_404, test_editeaza_batch_inexistent_404, test_editeaza_row_index_invalid_pe_batch_valid_404, test_editeaza_pastreaza_campuri_neatinse (operatie/prestatii raman), test_editeaza_batch_committed_409 (guard post-commit), test_editeaza_raw_corupt_no_op (decrypt fail → 422/no-op, fara crash), test_editeaza_empty_input_sterge_campul (semantica empty = CLEAR, pentru cazul "corectez o valoare gresita").
  • Acceptance criteria:
    • Migrare: import_rows.override_json TEXT (nullable), _migrate defensiv (idempotent, ca is_admin in 3.3b).
    • Ruta POST /v1/import/{import_id}/rand/{row_index}/editeaza (resolve_account_id) + alias web POST /_import/{import_id}/rand/{row_index}/editeaza (require_login). Campuri: vin, nr_inmatriculare, data_prestatie, odometru_initial, odometru_final.
    • Mutatie pura: decripteaza override_json curent (sau {}), aplica campurile (vezi semantica empty), re-cripteaza, UPDATE. NU recalculeaza statusul in ruta — preview-ul il rederiva via _resolve_row_for_preview.
    • Semantica empty: input gol = STERGE cheia din override (revine la valoarea din fisier daca exista). Documentat + testat.
    • Scoping intr-o singura interogare: import_rows r JOIN import_batches b ON b.id=r.batch_id WHERE b.id=? AND b.account_id=? AND r.row_index=? → 404 pe gol (acopera alt cont, batch inexistent, row_index invalid).
    • Guard committed: batch cu status='committed' → 409 (editarea n-ar avea efect downstream).
    • decrypt_creds → None/exceptie → 422/no-op defensiv (ca import_router.py:602-606), niciodata scriere goala.
    • Coercion: nu se afirma canonicalize_row pe odometru_initial (normeaza doar _final); validarea (_parse_int) tolereaza ".0" — testul verifica prin validare, nu prin canonicalize.
    • NU atinge submissions.
  • Verificare E2E: TestClient — (a) fixture cu antet ne-canonic, rand needs_data → editeaza → preview = ok; (b) fixture fara coloana data → editeaza data → ok.

US-002: UI — buton "Editeaza" pe rand in tabelul de preview

Ca utilizator vreau sa pun un rand in mod editare si sa-i corectez celulele pe loc pentru ca sa nu reincarc tot fisierul pentru o singura valoare.

  • Depinde de: US-001
  • Fisiere: app/web/templates/_preview_import.html, app/web/templates/_preview_rand.html (NOU — fragment rand), app/web/routes.py (handler fragment rand), tests/test_preview_edit_ui.py
  • Test intai (RED): tests/test_preview_edit_ui.pytest_preview_are_buton_editeaza_pe_rand, test_editeaza_intra_in_mod_editare_form_propriu (randul devine FORM separat, NU in #confirm-form), test_salveaza_reda_doar_randul (raspuns = fragment rand + OOB contoare, NU tot #import-section), test_enter_in_camp_editare_nu_declanseaza_confirm, test_eroare_validare_pastreaza_valorile_introduse (data invalida → ramane in editare, mesaj pe camp).
  • Acceptance criteria:
    • Fiecare rand are un buton "Editeaza" (coloana de actiuni la final).
    • Swap pe rand, NU pe sectiune (D-3.1): Editeaza/Salveaza tintesc randul <tr> (hx-target pe rand, hx-swap="outerHTML"), iar rezumatul + bara de confirmare se actualizeaza prin OOB swap. NU se re-randeaza #import-section (altfel se pierd bifele reviewed_rows, n_confirmat, filtrul activ, alte randuri in editare).
    • Form propriu (D-3.3): input-urile de editare stau intr-un <form> separat (sau form= attribute), NU in #confirm-form. Butonul "Trimite la RAR" e DEZACTIVAT cat un rand e in mod editare. Enter intr-un camp de editare salveaza randul, niciodata nu declanseaza confirmarea (ireversibila).
    • Refolosire _trimitere_detaliu.html (DRY, rezolva mobil+eroare): modul de editare foloseste aceeasi grila responsiva repeat(auto-fit, minmax(200px,1fr)) + error-map + scroll-to-row ca formul de corectie existent. Pe viewport ingust randul devine card stacked (label deasupra input), nu celule in scroll orizontal.
    • Stari (D-2.1/D-2.2): hx-indicator "se salveaza…" pe rand + butoane dezactivate in timpul cererii; hx-on::response-error pastreaza randul + valorile introduse + banner ne-distructiv la 500/CSRF/timeout.
    • Mutual-exclusion (D-3.2/D-3.6): cat un rand e in editare, butoanele Editeaza ale celorlalte randuri sunt dezactivate; "Incarca alt fisier" / schimbarea de tab cu editare dirty cer confirmare (sau auto-cancel cu toast).
    • La eroare de validare a valorii, randul ramane in editare cu mesajul pe campul vinovat (tipar corectie_errors).
    • Dupa Salveaza: scroll la + evidentiaza randul editat (reuse scriptul de outline din _trimitere_detaliu.html).
    • "Anuleaza" inchide editarea fara scriere. Accesibil: butoane min 44px, aria-label per camp cu nr. rand + VIN.
  • Verificare E2E: browser HTMX pe / (Playwright MCP) — upload fixture cu un rand fara data → Editeaza → completeaza data → Salveaza → DOAR randul se schimba pe ok, contorul "gata de trimis" creste, bifele altui rand raman.

US-003: Acasa unificata — Trimiteri ca sectiune permanenta, fara tab "Trimiteri"

Ca utilizator vreau sa vad trimiterile pe aceeasi pagina cu upload-ul pentru ca "vezi mai jos trimiterile" sa fie adevarat si sa nu navighez intre tab-uri.

  • Depinde de: —
  • Fisiere: app/web/templates/dashboard.html, app/web/templates/_acasa.html, app/web/routes.py, tests/test_acasa_trimiteri.py
  • Test intai (RED): tests/test_acasa_trimiteri.pytest_tab_bar_fara_trimiteri, test_acasa_contine_sectiunea_trimiteri (tabel + filtre), test_tab_coada_redirect_la_acasa (?tab=coada nu da 404, serveste Acasa), test_acasa_fara_linkuri_ajutor (randul de linkuri catre tab-uri eliminat), test_badge_trimiteri_scoped_pe_acasa.
  • Test intai (RED) (in plus): test_fragment_coada_serveste_acasa (fragmentul, nu doar pagina), test_sectiune_trimiteri_are_heading ("Trimiterile tale"), test_acasa_pastreaza_wayfinding_mapari_coduri.
  • Acceptance criteria:
    • Tab-ul "Trimiteri" (coada) eliminat din tab-bar; raman Acasa·Mapari·Cont·Nomenclator.
    • _acasa.html randeaza, sub upload + primii pasi, sectiunea Trimiteri completa cu heading "Trimiterile tale" (divizor vizual fata de upload): filtrele (US-009), tabelul (_submissions.html), panoul #trimitere-detaliu.
    • Un singur sticky bar pe ecran (D-1.2): cat un preview de import e activ (#import-section randat), sectiunea Trimiteri e ascunsa/colapsata; dupa commit, se reveleaza si scroll la ea. Nu coexista doua bare sticky.
    • Wayfinding pastrat (D-5.3): se scoate DOAR linkul redundant "Trimiteri"; "Mapari" si "Coduri RAR" raman ca o linie de ajutor discreta (utile pentru operatori non-tehnici).
    • GET /?tab=coada si GET /_fragments/coada nu dau 404 — ambele servesc continutul Acasa (fragmentul nu mai randeaza _coada.html orfan; _coada.html sters/repurposat).
    • Anti dublu-poll (M5): poll-ul de trimiteri din sectiune are hx-trigger gated pe document.visibilityState==='visible' (sau aliniat la 15s ca status-ul) — nu doua timere perpetue pe pagina mereu deschisa.
    • Contorul de atentie (badge) se reflecta in heading-ul sectiunii, nu pe un tab disparut.
    • Microcopia post-confirmare tinteste sectiunea "Trimiterile tale" de pe Acasa.
  • Verificare E2E: browser pe / — dupa confirmarea unui import, trimiterile apar in aceeasi pagina sub upload, cu heading; click pe rand blocat deschide corectia inline; ?tab=coada si /_fragments/coada servesc Acasa.

US-004: UI — zona de upload comprimata la o bara slim

Ca utilizator vreau un upload care ocupa putin spatiu pentru ca trimiterile de sub el sa fie vizibile fara scroll.

  • Depinde de: US-003 (pentru a avea ce sta sub bara)
  • Fisiere: app/web/templates/_upload.html, tests/test_upload_slim.py
  • Test intai (RED): tests/test_upload_slim.pytest_upload_slim_pe_un_rand (markup compact, fara caseta mare drop-zone dominanta), test_upload_pastreaza_drag_drop_si_input (input file + handler drag-drop raman), test_upload_pastreaza_select_foaie (cazul multi-sheet inca functioneaza).
  • Acceptance criteria:
    • Upload-ul devine o bara pe un rand: eticheta "Importa:" + buton "Alege fisier (xlsx/csv)" + zona "sau trage aici", microcopy scurt ("NU se trimite nimic pana confirmi" pastrat, dar discret).
    • Slim DAR accentuat (D-1.1/D-5.2): bara pastreaza un tratament distinct (border/fundal de accent) ca sa ramana punctul de intrare evident chiar cu un tabel lung dedesubt.
    • First-run pastreaza hero (D-5.1): daca not are_trimiteri, bara ramane usor mai inalta cu copy-ul "Primul fisier? Trage-l aici."; se colapseaza la slim doar dupa ce contul are trimiteri. Empty-state-ul redundant al tabelului Trimiteri se suprima cand sunt zero trimiteri (bara de upload acopera deja CTA-ul).
    • Drag-drop pe bara si input[type=file] ascuns raman functionale (JS-ul existent refolosit).
    • Cazul multi-sheet (select foaie) inca apare cand fisierul are mai multe foi.
  • Verificare E2E: browser pe / — bara de upload ocupa ~1 rand cand exista trimiteri; first-run pastreaza hero-ul.

US-005: UI — Mapari "De rezolvat" + "Operatii salvate" ca tabel

Ca utilizator cu multe mapari vreau sa le vad tabelar pentru ca stiva de carduri/forme ocupa prea mult loc.

  • Depinde de: —
  • Fisiere: app/web/templates/_mapari.html, tests/test_mapari_tabel.py
  • Test intai (RED): tests/test_mapari_tabel.pytest_mapari_de_rezolvat_in_tabel, test_mapari_salvate_in_tabel, test_mapari_salvare_si_stergere_inca_functioneaza (POST-urile /mapari, /mapari/salvate, /mapari/salvate/sterge neschimbate).
  • Acceptance criteria:
    • Sectiunea "De rezolvat" (operatii needs_mapping) randata ca tabel: coloane operatie/denumire + nr. blocate, sugestii, select cod RAR, comutator (US-007), actiune Salveaza — un rand de tabel per operatie.
    • Sectiunea "Mapari operatii salvate" randata ca tabel cu aceleasi coloane + Sterge.
    • Starea stocata redata (H4): comutatorul (US-007) din tabelul salvate reflecta valoarea auto_send STOCATA per mapare (din _load_saved_op_mappings, routes.py:738), nu un default hard "Automat".
    • Comportamentul existent neschimbat: re-rezolvare automata a blocatelor la salvare/edit cod; endpoints identice; csrf.
    • Tabelele folosesc .tablewrap (scroll orizontal pe mobil) ca Trimiteri.
  • Verificare E2E: browser pe tab Mapari — operatiile nemapate si cele salvate apar ca tabele; salvare + stergere OK.

US-006: UI — Formate de coloane ca tabel

Ca utilizator vreau formatele de coloane salvate ca tabel pentru ca sa le compar dintr-o privire.

  • Depinde de: —
  • Fisiere: app/web/templates/_mapari.html, tests/test_formate_tabel.py
  • Test intai (RED): tests/test_formate_tabel.pytest_formate_coloane_in_tabel, test_formate_editare_data_si_stergere_inca_functioneaza.
  • Acceptance criteria:
    • Sectiunea "Formate de coloane salvate" randata ca tabel: nr. coloane / maparile col→camp / format data (editabil) / Sterge.
    • POST-urile /formate-coloane/editeaza si /formate-coloane/sterge neschimbate; csrf pastrat.
    • .tablewrap pentru consistenta cu celelalte tabele.
  • Verificare E2E: browser pe tab Mapari — formatele apar ca tabel; editarea formatului de data + stergerea functioneaza.

US-007: UI — comutator pe COADA in loc de bifa "auto-send" (framing sigur, nu "Manual/trimitere")

Ca operator vreau sa stiu clar ce se intampla cu o operatie la fisierele viitoare pentru ca "auto-send" e jargon, iar "Manual/Automat" suna ca si cum sistemul ar trimite singur la RAR (fals — periculos).

Decis la poarta autoplan (UC-A): framing pe punerea in coada, NU pe trimitere. Toti reviewerii (CEO/Eng/Design): "Automat/Manual" citit global peste declaratii ireversibile = risc de send-safety. Etichetele poarta singure sensul.

  • Depinde de: US-005 (acelasi markup de mapari)
  • Fisiere: app/web/templates/_mapari.html, app/web/templates/_preview_import.html, tests/test_autosend_toggle.py
  • Test intai (RED): tests/test_autosend_toggle.pytest_comutator_coada_prezent (textul contine "in coada" / "verificare", NU "trimite"/"Manual" gol), test_eticheta_scoped_pe_operatie (microcopy contine "aceasta operatie"), test_pune_automat_mapeaza_auto_send_true, test_tine_pentru_verificare_mapeaza_auto_send_false, test_default_pune_automat (comportament identic cu checked de azi).
  • Acceptance criteria:
    • Bifa auto_send inlocuita cu un comutator cu doua stari, etichetat pe COADA, in: panoul de mapare din preview (_preview_import.html) si ambele locuri din _mapari.html. Antet: "La fisierele viitoare cu aceasta operatie:" Optiuni: "Pune automat in coada" / "Tine pentru verificare".
    • Microcopy scoped pe operatie (NU global): "...doar pentru aceasta operatie; nimic nu pleaca la RAR pana confirmi." Niciun cuvant "Manual"/"trimite" izolat care sa implice bypass al confirmarii RAR. Caption prezent si in preview (azi checkbox-ul din preview nu are caption).
    • Maparea valoare→backend pastreaza semantica auto_send existenta ("Pune automat"=true, "Tine"=false); default "Pune automat in coada" (mirror la checked de azi).
    • name="auto_send" pastrat cu value-uri ce produc bool corect — zero atingere backend (cale aleasa).
  • Verificare E2E: browser — salvez o operatie pe "Tine pentru verificare" → la urmatorul import randul cu acea operatie ramane blocat (nu intra automat in coada); pe "Pune automat in coada" → intra in coada (tot cu gate-ul de confirmare la trimitere).

4. Riscuri

  • Stocare editare (REZOLVAT prin Approach B): raw_json e cheiat pe anteturile fisierului; o editare pe cheie canonica ar fi ignorata pe fisiere cu antet ne-canonic, iar un camp fara coloana-sursa n-ar putea fi completat. Mitigare: override_json (patch canonic aplicat ultimul). Test obligatoriu cu fixture antet ne-canonic + coloana absenta.
  • Swap pe rand vs sectiune (REZOLVAT): editarea tinteste randul + OOB contoare, NU #import-section; form propriu; confirm dezactivat la editare. Previne pierderea starii si Enter→trimitere ireversibila.
  • Dublu-poll pe Acasa: status 15s + trimiteri 10s pe pagina mereu deschisa. Mitigare: poll-ul de trimiteri gated pe document.visibilityState (sau 15s). Nu doua timere perpetue.
  • ?tab=coada + /_fragments/coada vechi: fallback la continutul Acasa (nu 404, nu fragment orfan).
  • Export "randuri cu probleme": trebuie sa reflecte valorile editate. Recalculul prin _resolve_row_for_preview (cu override aplicat) => export consistent automat.
  • Recalcul preview pe fisier mare: cu swap pe rand, doar randul + contoarele se re-randeaza (nu toate 5000).

5. Intrebari deschise

Rezolvate cu utilizatorul inainte de executie (poarta de aprobare PRD). Toate cele de mai jos sunt inchise prin sesiunea de planificare (AskUserQuestion 2026-06-19):

  • Editare preview: buton "Editeaza" pe rand (mod editare explicit pe rand), nu click-pe-celula. [INCHIS]
  • Trimiteri pe Acasa: mutare completa, tab-ul "Trimiteri" eliminat (linkul redundant "Trimiteri" scos; wayfinding "Mapari"/"Coduri RAR" pastrat). [INCHIS]
  • Upload: bara slim pe un rand, accentuata + hero pastrat la first-run. [INCHIS]
  • auto-send: framing pe coada ("Pune automat in coada" / "Tine pentru verificare"), NU "Automat/Manual" global. [INCHIS — poarta autoplan UC-A]
  • Stocare editare: Approach B (override_json), relaxeaza Non-Goal-ul de schema. [INCHIS — poarta autoplan]
  • Structura: un singur PRD 3.6, valuri secventiate (Acasa intai, editare dupa storage). [INCHIS — poarta autoplan]

6. Valuri de executie (secventiate post-autoplan)

Val 1 (Acasa unificata — livreaza 80% din valoare):   [US-003] [US-004]
Val 2 (Editare preview — dupa storage redesign B):     [US-001] → [US-002]
Val 3 (Cosmetic mapari + toggle sigur):                [US-005] → [US-006] [US-007]

Secventiere ceruta la poarta autoplan: US-003 (+US-004 ca sub-task, ii face loc) intai — fixeaza microcopia care minte azi. US-001/002 dupa ce override_json (Approach B) e in loc. US-005 si US-007 ating _mapari.html/_preview_import.html impreuna — acelasi worker (sectiuni distincte). Regula de aur: la fiecare val, regresia pytest -q verde + bifare in PRD.


Raport VERIFY

Verificare 2026-06-19 (lead-driven: suita automata + E2E browser Playwright + trimitere LIVE pe RAR test).

Rezultat global: PASS

Teste automate: python3 -m pytest -q523 passed, 0 failed (baseline pre-3.6: 483; +33 stories echipe, +7 US-007).

Per story (E2E browser pe /, mediu test, auth dev):

  • US-003 PASS — tab "Trimiteri" absent din tab-bar (Acasa·Mapari·Cont·Nomenclator); sectiunea "Trimiterile tale" randata sub upload cu heading + badge + filtre + tabel; GET /?tab=coada si GET /_fragments/coada servesc continut Acasa (200, contin "Trimiterile tale", fara fragment _coada.html orfan); cat preview-ul de import e activ, sectiunea Trimiteri e ascunsa (un singur sticky bar — D-1.2); wayfinding "Mapari"/"Coduri RAR" pastrat.
  • US-004 PASS — upload comprimat la bara slim ("Importa:" + buton + "sau trage aici" + microcopy discret); drag-drop + select foaie pastrate; multi-sheet ok.
  • US-001 PASSimport_rows.override_json (nullable, Fernet, _migrate defensiv) aplicat ULTIMUL in _resolve_row_for_preview (l.194) si commit_import (l.1070); ruta scoped JOIN→404, guard committed→409, empty=clear; verificat LIVE ca override COMPLETEAZA o coloana ABSENTA din fisier (fixture import_lipsa_coloana.csv fara coloana data → editarea adauga data → ok).
  • US-002 PASS — buton "Editeaza" pe rand (aria-label cu nr.+VIN); swap pe <tr> + OOB pe rezumat/contoare (NU pe #import-section); form propriu (Enter nu declanseaza confirm); mutual-exclusion (Editeaza celorlalte randuri + "Trimite la RAR" dezactivate cat un rand e in editare); hx-indicator "se salveaza…"; reuse grila responsiva _trimitere_detaliu.html.
  • US-005/006 PASS — "De rezolvat", "Operatii salvate" si "Formate de coloane" ca tabele .tablewrap; H4 confirmat (checkbox auto_send reflecta valoarea STOCATA: OP-AUTO bifat, OP-MANUAL nebifat); POST-uri neschimbate.
  • US-007 PASS — comutator etichetat pe COADA ("La fisierele viitoare cu aceasta operatie:" + "Pune automat in coada" / caption "Tine pentru verificare … nimic nu pleaca la RAR pana confirmi"); fara "Manual"/"trimite"/"auto-send"; scoped pe operatie; name="auto_send" pastrat (semantica de prezenta → bool corect cu ambele parsere, zero backend); prezent in preview + ambele locuri din Mapari.

E2E LIVE pe RAR test (trimitere reala): import import_lipsa_coloana.csv (fara coloana data) → preview needs_data → Editeaza → completare data 2026-06-08 (override pe coloana absenta) → ok → commit "Trimite la RAR" → worker (send_enabled=true, creds <test>) login RAR test + nomenclator refresh (18 coduri) → postPrezentare → submission sent, id_prezentare=68696. Confirmat independent: python3 -m tools.rar_finalizate listeaza 68696 WVWZZZ1KZAW000456 2026-06-08 98765. Dovedeste lantul complet override→commit→worker→RAR pe mediul real de test.

Bug-uri prinse la VERIFY (JS, invizibile la TestClient) si reparate (htmx 1.9.12):

  1. htmx.config.useTemplateFragments=true (base.html) — raspunsul de editare (<tr> + OOB #preview-rezumat/#preview-ok-count) era parsat in context de tabel (<table><tbody>), foster-parenta div-urile OOB → htmx:swapError + contoare pierdute.
  2. Re-activarea #confirm-btn dupa salvare deferita pe setTimeout(0) (_preview_rand.html) — altfel updateN rula cat inca exista tr[data-editing="1"] tranzitoriu → butonul "Trimite la RAR" ramanea blocat dupa o editare reusita.
  3. updateN actualizeaza si numarul ok din #n-hint (_preview_import.html) — textul "(N ok)" ramanea stale dupa editare.

Non-Goals respectate: worker / masina stari / idempotenta-logica / mapping-rezolvare NEATINSE; singura atingere de schema = 1 coloana nullable override_json cu migrare defensiva (relaxare decisa la poarta autoplan, Approach B); submissions neatins de ruta de editare.


AUTOPLAN REVIEW — APROBAT (2026-06-19)

Pipeline /autoplan: CEO -> Design -> Eng. Mod: SELECTIVE EXPANSION. Vocile: codex [indisponibil — usage limit]

  • subagent Claude independent per faza. 12 corecturi auto-decise (audit trail mai jos) + 3 decizii la poarta, toate aprobate de user: (UC-A) US-007 framing pe coada; (poarta) Approach B override_json; (poarta) un PRD, valuri secventiate. Stories revizuite cu toate fix-urile (sectiunile 2-6 de mai sus reflecta decizia finala).

FAZA 1 — CEO (strategie & scope)

0A. Premise Challenge

  • Premisa centrala ("operatorii trebuie sa corecteze valori in preview, inainte de trimitere"): VALIDA si confirmata de durerea reala — azi un VIN/data lipsa cere re-upload de fisier intreg.
  • Premisa de implementare ("editarea = suprascriere import_rows.raw_json"): SUSPECTA. raw_json e cheiat pe numele coloanelor din fisier (antet original), nu pe campuri canonice; _resolve_row_for_preview (import_router.py:138-140) si commit-ul (import_router.py:922-945) re-aplica json_mapare peste raw_json. O editare care scrie cheia canonica vin e ignorata pe orice fisier al carui antet difera de numele canonic. Fixture-ul tests/fixtures/test_data_mapping.csv are din intamplare antet = nume canonice -> testele ar trece, realul ar esua tacit. (vezi Eng pentru fix.)
  • Premisa "completez informatiile lipsa": cazul in care campul lipsa e o coloana inexistenta in fisier nu are col_f in care sa scrie -> raw_json nu poate exprima valoarea. Premisa promisa userului depaseste ce poate face stocarea actuala fara un strat de override. (escaladata la poarta — vezi UC-1.)
  • Premisa "muta Trimiteri pe Acasa": VALIDA — microcopia "vezi mai jos trimiterile" deja minte azi.
  • Premisa de impachetare (4 schimbari intr-un PRD): cele 4 NU sunt cuplate tehnic. Singura legatura e "incap pe ecranul Acasa". Risc de scope: editarea celulelor (cu fix-ul de stocare) e de departe cea mai riscanta; restul sunt cosmetice. (vezi 0F + decizie de gust DG-1: split.)

0B. Existing Code Leverage (ce exista deja)

Sub-problema Cod existent care o rezolva partial/total
Editare campuri de continut + re-validare + re-enqueue US-010 corectie inline (routes.py:583 post_corectie_trimitere, _trimitere_detaliu.html:44-84) — exact aceleasi campuri (vin/nr/data/odo), validare, detectie coliziune idempotency. US-001/002 trebuie sa REFOLOSEASCA acest tipar, nu sa-l reconstruiasca.
Tabele compacte .tablewrap + _submissions.html (deja tabel cu scroll orizontal)
Sectiunea Trimiteri pe Acasa _submissions.html + filtre US-009 + #trimitere-detaliu — se includ ca atare
Upload drag-drop JS din _upload.html:74-107 — se pastreaza, doar markup-ul se comprima
Re-randare preview cu stare recalculata web_preview_import + _resolve_row_for_preview — editarea doar schimba sursa

Concluzie: PRD-ul e in mare parte re-cablare a unor fluxuri existente. Singurul cod nou real e ruta de editare (US-001) + modul-editare pe rand (US-002). Bun pentru DRY, dar US-001 NU trebuie sa-si scrie propria logica de validare/idempotency — o are in corectie.

0C. Dream State

  CURRENT                          THIS PLAN                         12-MONTH IDEAL
  Import = ecran read-only;        Editezi randul in preview;        Import = foaie editabila live,
  Trimiteri in alt tab;     -->    Trimiteri sub upload pe Acasa; -->  mapare AI (Etapa 4.1),
  corectie doar post-trimitere;    un singur ecran operational         o singura suprafata, zero re-upload

Planul muta sistemul CATRE idealul "o singura suprafata operationala". Aliniat.

0C-bis. Implementation Alternatives (pentru editarea de celule — nucleul riscant)

APPROACH A: Suprascriere raw_json cu reverse-map canonical->col_fisier
  Summary: ruta editare mapeaza campul canonic inapoi la coloana de fisier via json_mapare, scrie raw_json.
  Effort: M   Risk: High
  Pros: fara schema noua (respecta Non-Goal); o singura sursa de adevar
  Cons: NU poate completa un camp a carui coloana lipseste din fisier (cazul "informatii lipsa" real);
        reverse-map ambiguu daca doua coloane mapeaza pe acelasi camp; fragil

APPROACH B: Coloana noua import_rows.override_json (patch canonic), aplicata ultima in resolver+commit
  Summary: editarea scrie un dict canonic de override; resolver si commit il suprapun peste mapped.
  Effort: M   Risk: Low
  Pros: exprima ORICE camp, inclusiv cele absente din fisier; nu atinge semantica raw_json/idempotency;
        migrare defensiva _migrate ca la PRD-urile 3.3b/3.5
  Cons: incalca Non-Goal "fara modificari schema" (1 ALTER, mic); 2 puncte unde se aplica override-ul

APPROACH C: Fara editare in preview — extinde US-010 corectie sa fie accesibila si pre-commit
  Summary: confirma intai, apoi corecteaza in Trimiteri (fluxul existent), fara editare in preview.
  Effort: S   Risk: Low
  Cons: contrazice decizia explicita a userului (editare IN preview); pune o trimitere "rea" in coada intai

RECOMMENDATION: Approach B — singurul care onoreaza promisiunea "completez informatii lipsa" (campuri absente din fisier), cu risc mic si fara sa atinga idempotency/worker. Costul: relaxarea Non-Goal-ului de schema (1 coloana nullable + migrare defensiva). -> poarta finala UC-1.

0D. Selective Expansion — complexity check

  • 7 stories, ~8 fisiere atinse, 0 clase/servicii noi. Sub pragul de smell (8 fisiere / 2 clase). OK.
  • Minimum care livreaza valoare: US-001+US-002 (editarea) SAU US-003 (Acasa unificata) — sunt independente.
  • Expansiuni candidate (cherry-pick, auto-decise spre DEFER per P3/P6): buton "repara toate needs_review", editare bulk, undo editare. Toate -> TODOS (nu in blast radius imediat).

0E. Temporal Interrogation

  • HOUR 1: userul importa, vede un rand needs_data, da Editeaza, completeaza data, Salveaza -> ok. Functioneaza DOAR daca stocarea editarii e corecta (Approach B). Cu Approach A + fisier cu antet ne-canonic: editarea pare ca se salveaza dar randul ramane needs_data -> incredere distrusa.
  • HOUR 6+: zeci de fisiere cu formate diverse de antet. Approach A esueaza pe majoritatea; Approach B tine.

0F. Mode Selection: SELECTIVE EXPANSION confirmat. Scope-ul de baza tinut; o singura expansiune ceruta

(override_json), restul deferite. Decizie de structura (split 4-in-1) -> DG-1 la poarta.

Step 0.5 — Voci duale CEO

  • Codex (CLI): [codex-unavailable: usage limit] — a citit contextul dar a esuat la analiza pe limita de cont.
  • Subagent Claude (independent): a confirmat din cod premisa centrala (web_confirma_import -> _upload.html cu "urmareste coada de mai jos", dar coada e in alt tab => microcopia minte azi). Findings principale: US-001 dubleaza motorul existent post_corectie_trimitere (routes.py:583-717); US-007 "Automat/Manual" risca interpretare gresita de send-safety (auto_send e per-operatie, nu global, peste declaratii ireversibile); US-004 e consecinta US-003, nu peer; Acasa ajunge sa ruleze DOUA poll-uri (status 15s + trimiteri ~10s); recomanda split (groful de valuri din PRD demonstreaza independenta).
CEO DUAL VOICES — CONSENSUS TABLE  (Codex N/A — subagent-only)
═══════════════════════════════════════════════════════════════
  Dimensiune                          Claude  Codex  Consensus
  ─────────────────────────────────── ─────── ─────── ─────────
  1. Premise valide?                   partial N/A    DG: editare-in-staging vs reuse corectie
  2. Problema corecta?                 da      N/A    CONFIRMAT (US-003 e valoarea reala)
  3. Scope calibrat?                   nu      N/A    FLAG: 4-in-1, recomandare split (DG-1)
  4. Alternative explorate?            nu      N/A    FLAG: reuse post_corectie_trimitere ne-analizat
  5. Riscuri (send-safety)?            da      N/A    FLAG CRITIC: US-007 Automat/Manual (UC-2)
  6. Traiectorie 6 luni sound?         partial N/A    US-003 da; US-001 = tax de mentenanta daca dubleaza
═══════════════════════════════════════════════════════════════
Single critical din subagent (US-007 send-safety, US-001 DRY) = flagged regardless.

CEO Sections 1-10 (rezumat per sectiune — examinat, nu doar numit)

  1. Strategic fit: US-003 aliniat la idealul "o suprafata operationala"; restul cosmetic. OK.
  2. Error & Rescue: editarea poate introduce valori care re-blocheaza randul (data invalida) — trebuie eroare pe camp, nu pierdere de input (acoperit US-002 AC). Nicio cale de eroare tacuta noua daca se reda preview-ul.
  3. DRY (CRITIC): US-001 vs post_corectie_trimitere — aceeasi validare/canonicalizare/idempotency. Reuse obligatoriu.
  4. Send-safety (CRITIC): US-007 — "Manual" suna global peste un sistem cu declaratii ireversibile. Reformulare ceruta.
  5. Observability: editarea ar trebui sa lase un log (cine/cand a editat un rand de staging) — minor, GDPR-friendly.
  6. Scope/impachetare: cele 4 sunt independente (graf valuri). Recomandare split (DG-1).
  7. Reuse UI: .tablewrap, _submissions.html, filtre US-009, upload JS — reutilizate corect.
  8. Page-weight: dublu-poll pe Acasa (status 15s + trimiteri 10s) — de consolidat/confirmat (Eng#4).
  9. Migrare deep-link: ?tab=coada trebuie sa nu dea 404 (acoperit US-003).
  10. 6-month: US-001 fara reuse = doua locuri de intretinut; US-005/006 cosmetice = risc minim.

Mandatory outputs — FAZA 1

NOT in scope (deferite la TODOS): buton "repara toate needs_review", editare bulk multi-rand, undo editare, log de audit pe editare de staging. Ce exista deja (refolosit): post_corectie_trimitere, .tablewrap, _submissions.html+filtre US-009, upload JS, _resolve_row_for_preview. Dream delta: planul muta catre "o singura suprafata"; gap ramas = mapare AI (Etapa 4.1). Failure modes: (a) editare ignorata tacit pe fisier cu antet ne-canonic [CRITIC, Eng]; (b) camp lipsa fara coloana sursa nu poate fi completat [CRITIC]; (c) "Manual" interpretat global [CRITIC]; (d) dublu-poll pe pagina mereu deschisa [mediu].

FAZA 1 COMPLETE. Codex: indisponibil (usage limit). Subagent Claude: 6 findings (2 critice). Consensus: 1/6 confirmat clar, 4 flag-uri -> poarta. Trec la Faza 2 (Design).

FAZA 3 — Eng (arhitectura, teste, securitate)

Codex [codex-unavailable: usage limit]. Subagent Claude independent (context curat).

Step 0.5 — Eng consensus

ENG DUAL VOICES — CONSENSUS TABLE  (Codex N/A — subagent-only)
═══════════════════════════════════════════════════════════════
  Dimensiune                          Claude  Codex  Consensus
  ─────────────────────────────────── ─────── ─────── ─────────
  1. Arhitectura sound?               nu      N/A    FLAG: US-001 stocare gresita (C1)
  2. Acoperire teste suficienta?      nu      N/A    FLAG: fixture mascheaza bug; lipsesc anteturi ne-canonice
  3. Riscuri performanta?             partial N/A    FLAG: dublu-poll Acasa (M5)
  4. Securitate (scoping)?            da      N/A    OK cu nota: query scoped JOIN (M7)
  5. Cai de eroare?                   nu      N/A    FLAG: decrypt fail, empty edit, batch committed (L9-L11)
  6. Risc deploy?                     da      N/A    OK: 1 ALTER defensiv (override_json) ca 3.3b/3.5
═══════════════════════════════════════════════════════════════

Findings Eng (cu dovezi cod)

  • C1 [CRITIC] — US-001 stocare gresita. raw_json cheiat pe anteturi fisier; resolver+commit re-aplica json_mapare (import_router.py:138-140, 928-930). Scrierea cheii canonice e ignorata pe antet ne-canonic; camp fara coloana-sursa nu poate fi exprimat deloc. Fix = Approach B (import_rows.override_json nullable, Fernet, aplicat ULTIMUL in resolver + commit). Cere rescrierea AC US-001 (liniile actuale incoded Approach A)
    • relaxare Non-Goal schema (1 ALTER + _migrate defensiv). -> poarta UC-1.
  • H2 [HIGH] — DRY. US-001 nu trebuie sa-si re-deriveze statusul; recalcul OBLIGATORIU prin _resolve_row_for_preview (altfel drift de clasificare vs preview, iar clasificarea controleaza send-ul). Ruta editare = mutatie pura de stocare.
  • H3 [HIGH] — US-007 eticheta. Scopeaza la operatie: "Aceasta operatie: Automat / Manual" + microcopy "pentru aceasta operatie". name="auto_send" pastrat (zero backend) e corect.
  • H4 [MED] — US-005/007. Tabelul "mapari salvate" trebuie sa randeze starea auto_send STOCATA (routes.py:738), nu sa hard-defaulteze Automat.
  • M5 [MED] — dublu-poll Acasa. status 15s (dashboard.html:14-17) + submissions 10s (_coada.html:43-45) pe pagina mereu deschisa. Fix: aliniaza submissions la 15s SAU hx-trigger gated pe document.visibilityState==='visible'.
  • M6 [MED] — /_fragments/coada. ?tab=coada cade deja pe Acasa (routes.py:266), dar fragmentul /_fragments/coada (routes.py:309-313) inca randeaza _coada.html orfan. Fix: fragmentul redirectioneaza la continutul Acasa; test dedicat.
  • M7 [MED] — scoping editare. O singura interogare scoped JOIN import_rows r JOIN import_batches b ... WHERE b.account_id=? AND r.row_index=? -> 404 pe gol. Web alias sub require_login, /v1/... sub resolve_account_id.
  • M8 [MED] — coercion. canonicalize_row normeaza DOAR odometru_final (idempotency.py:44), nu _initial. Testul de coercion sa nu afirme strip prin canonicalize pe odometru_initial.
  • L9-L12 [LOW/MED]: test decrypt-fail (raw corupt -> 422/no-op); semantica empty-input (ignore vs clear — defineste explicit, cazul "completez" poate cere clear); guard editare pe batch committed (409, ca import_router.py:822-823); setup test panel preview (seed unmapped_op).

Mandatory outputs — FAZA 3

Diagrama arhitectura (override_json, Approach B):

  upload ─▶ import_rows(raw_json[antet fisier], override_json[canonic]=NULL)
                         │
   editare rand ─────────┘  scrie override_json[camp_canonic]=valoare  (mutatie pura)
                         │
   GET preview ─▶ _resolve_row_for_preview:
        mapped = json_mapare(raw_json)        ◀── pas existent
        mapped.update(override_json)          ◀── PAS NOU (aplicat ultimul)
        ─▶ validate ─▶ status (ok/needs_data/...)
                         │
   commit ─▶ acelasi merge override peste mapped ─▶ submissions

Test diagram (coverage): randuri noi de acoperit — (a) fisier antet ne-canonic editat -> ok [LIPSESTE azi]; (b) camp fara coloana-sursa completat -> ok [LIPSESTE]; (c) edit == GET preview status [LIPSESTE]; (d) decrypt fail; (e) empty-input semantics; (f) batch committed -> 409; (g) scoped 404 alt cont; (h) /_fragments/coada fallback. NOT in scope: undo, bulk. Ce exista: _resolve_row_for_preview, post_corectie_trimitere, _migrate. Failure modes critice: C1 (acoperit de Approach B); fixture verde-dar-stricat (acoperit de fixture antet ne-canonic).

FAZA 3 COMPLETE. Subagent: 1 critic, 3 high, 4 medium, 4 low. Trec la sinteza + poarta.

FAZA 2 — Design (ierarhie, stari, interactiune)

Codex [codex-unavailable]. Subagent Claude independent (a citit toate cele 8 template-uri).

Design litmus scorecard (0-10)

Dimensiune Scor Nota
Ierarhie informatie 5 upload demotat la slim + tabel sub el => monitorizarea domina actiunea principala
Stari (loading/error/partial) 4 lipsesc spinner save, eroare server, scroll-to-row, empty unificat
Interactiune edit-mode 3 cea mai slaba zona — swap pe toata sectiunea distruge starea (CRITIC)
Copy Automat/Manual 4 "Manual" citit global => contrazice promisiunea de siguranta
First-run vs returning 5 first-run pierde hero "Primul fisier?", 3 nudge-uri redundante

Findings Design (severitate)

  • D-3.1 [CRITIC] — swap distructiv. hx-target="#import-section" + outerHTML la editare re-randeaza tot; pierde bife reviewed_rows, n_confirmat, filtrul activ, alte randuri in editare. Fix: swap DOAR randul <tr>
    • OOB pe rezumat/contoare.
  • D-3.3 [HIGH] — Enter = trimitere ireversibila. Input-urile de editare in acelasi #confirm-form cu butonul "Trimite la RAR". Fix: randul de editare = FORM PROPRIU; dezactiveaza confirm cat un rand e in editare. Test: Enter in camp editare NU declanseaza confirm.
  • D-2.1 [HIGH] — fara stare "se salveaza". Re-randare completa fara hx-indicator/buton dezactivat. Fix: spinner pe rand.
  • D-2.2 [HIGH] — eroare server = pierdere tacuta de date. outerHTML pe sectiune la 500/CSRF/timeout poate goli #import-section. Fix: hx-on::response-error pastreaza randul + banner; test dedicat.
  • D-3.4 [HIGH] — integritate N la editare. Re-randarea reseteaza n_confirmat. Fix: blocheaza confirm la editare + recalcul autoritar.
  • D-3.5 [HIGH] — mobil inutilizabil. ~10 coloane cu input-uri in container cu scroll orizontal. Fix: pe viewport ingust, randul de editare = card stacked (refoloseste grila din _trimitere_detaliu.html).
  • D-4.1 [HIGH] — "Automat/Manual" contrazice "nu se trimite nimic pana confirmi". Fix: framing pe COADA, nu pe trimitere: "La fisierele viitoare cu aceasta operatie: [Pune automat in coada] / [Tine pentru verificare]". -> poarta UC-2.
  • D-1.1/1.2 [HIGH/MED] — upload demotat + coexistenta preview vs Trimiteri. Fix: upload slim DAR accentuat/distinct; ascunde Trimiteri cat un preview de import e activ (un singur sticky bar pe ecran); heading "Trimiterile tale".
  • D-3.2/3.6/5.1/5.3 [MED/LOW]: mutual-exclusion + debounce pe Editeaza; warn pe navigare cu editare dirty; first-run pastreaza hero "Primul fisier?" cand nu exista trimiteri (suprima empty-state redundant); pastreaza wayfinding Mapari/Coduri.
  • Lauda: refolosirea literala a markup-ului din _trimitere_detaliu.html (grila responsiva + error-map + scroll-to-row) rezolva D-2.2, D-3.5, D-2.5 gratis. -> AC HARD pe US-002.
DESIGN CONSENSUS (Codex N/A — subagent-only): 1 critic, 7 high, 5 med, 3 low.
Gate de design: D-3.1 + D-3.3 (swap distructiv + Enter-trimite) = problema de corectitudine/siguranta, nu rafinament.

FAZA 2 COMPLETE. Trec la sinteza cross-faza + poarta finala.

SINTEZA CROSS-FAZA

Teme care apar in 2+ faze (semnal de incredere mare):

  1. Refolosire post_corectie_trimitere / _trimitere_detaliu.html — CEO (0B), Eng (H2), Design (lauda). Reuse = AC hard pe US-001/US-002.
  2. US-007 "Automat/Manual" periculos — CEO (UC-2), Eng (H3), Design (D-4.1). Reformulare ceruta. -> poarta UC-A.
  3. Stocare editare gresita (C1) — CEO (0A), Eng (C1). Approach B (override_json) + fixture cu antet ne-canonic.

DECISION AUDIT TRAIL (auto-decise pe cele 6 principii)

# Faza Decizie Clasificare Principiu Rationament
1 Eng C1: adopta Approach B (override_json) in loc de raw_json Auto P1,P5 Approach A e stricat (ignora editari pe antet ne-canonic; nu poate completa coloane absente)
2 Eng US-001 = mutatie pura; status recalculat prin _resolve_row_for_preview Auto P4 DRY evita drift de clasificare care controleaza send-ul
3 Design Edit swap pe rand <tr> + OOB contoare, NU pe #import-section Auto P1,P5 swap pe sectiune distruge starea neclickata
4 Design Rand editare = FORM propriu + confirm dezactivat la editare Auto P1 previne Enter->trimitere ireversibila
5 Design Refolosire grila _trimitere_detaliu.html (responsiv + error-map + scroll) Auto P4 DRY rezolva mobil+eroare+scroll gratis
6 Design Spinner save + hx-on::response-error pastreaza randul Auto P1 fara pierdere tacuta de date
7 Eng M5: poll trimiteri gated pe document.visibilityState (sau 15s) Auto P3 reduce load perpetuu pe pagina mereu deschisa
8 Eng M6: /_fragments/coada -> continut Acasa (nu doar ?tab=coada) Auto P1 evita fragment orfan din bookmark vechi
9 Eng M7: query scoped JOIN import_rows×import_batches -> 404 Auto P1 sec fara scope-leak/TOCTOU
10 Eng M8: scoate afirmatia canonicalize pe odometru_initial Auto P5 canonicalize_row normeaza doar _final
11 Eng L11: guard editare pe batch committed -> 409 Auto P1 editare post-commit nu are efect downstream
12 Design First-run pastreaza hero "Primul fisier?"; slim accentuat; pastreaza wayfinding Mapari/Coduri Auto P1 discoverability first-run
UC-A CEO/Design US-007 reformulare labels (user a ales explicit "Automat/Manual") USER CHALLENGE toti reviewerii: risc send-safety. User decide.
DG-1 CEO Split: ship US-003(+004) intai, US-001/002 dupa reuse, US-005/006 batch, US-007 redesign Taste P6 cele 4 sunt independente (graf valuri)

Raport CLOSE (2026-06-22)

CLOSE conform ROADMAP §5.8 pe diff-ul livrabilei (ead6324..178bc87, doar app/).

/code-review high (8 unghiuri finder + verificare, recall-biased):

  • REPARAT (1 bug real, corectitudine). Decriptarea override_json era in afara try/except-ului care protejeaza raw_json in ambele cai de preview:

    • app/api/v1/import_router.py preview_import (linia 699)
    • app/web/routes.py _web_compute_preview (linia 1061) La rotatie cheie Fernet (risc acceptat R4) sau token corupt, raw_json degrada gratios la {} dar override_json arunca exceptie -> 500 pe TOT batch-ul in loc de preview cu override gol. Fix: override_json ambalat in try/except identic cu raw_json (fallback None -> {}). 523 teste pass dupa fix.
  • NOTAT, nereparat (cleanup viitor — disciplina "backend trimitere neatins"). Duplicare _override_of (decriptare override) + blocul "canonicalize dupa override" in 3-4 locuri (preview/commit pe canalul API vs. web). Refactor intr-un resolver partajat = candidat de cleanup, in afara scopului unui pas de CLOSE. Verificat ca cele 4 cai sunt logic identice (preview si commit produc aceeasi cheie de idempotenta), deci nu e bug azi.

  • Restul candidatelor (form-binding HTML, fallback tab coada->acasa, falsy-zero pe override) REFUTED la verificare: comportament intentionat (US-003/004) sau deja corect in cod.

Convenții (CLAUDE.md): fara incalcari (RO peste tot, fara emoji, invariante idempotenta/scoping/ 422-no-echo pastrate).

Toate PASS -> writeback dashboard (DONE, 2026-06-22) + PRD **Stare**: inchis.