Catalog central pur app/errors.py ca sursa unica cod->{problema,fix},
consumat de API+UI+worker. Aditiv (field/message pastrate la octet) +
rar_error stocat superset. Scope: fluxul de declarare; login/signup/CSRF
neatinse. labels.parse_erori degradeaza gratios; UI progresiv AA light+dark.
631 teste.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
30 KiB
PRD 5.4 — Erori pe 3 niveluri (problema + cauza + fix) pe API si UI
Stare: inchis
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).
1. Obiectiv
Fiecare eroare pe care o vede un integrator (canal API) sau un service-auto (UI web) sa raspunda la trei intrebari, in loc de una singura:
- Problema — ce s-a intamplat (categorie umana, scurta). "VIN invalid."
- Cauza — de ce, specific. "VIN-ul are 16 caractere; RAR cere exact 17."
- Fix — ce sa faci acum. "Verifica VIN-ul pe talon (pozitia E); 17 caractere majuscule, fara O/I/Q."
Motivul (lentila DX, Etapa 5): erorile plate ("Fisier nerecunoscut", "cheie API invalida", "VIN invalid") transfera incertitudinea catre utilizator — care fie ghiceste, fie deschide un tichet de suport. Cele trei niveluri inchid bucla la sursa: mai putine tichete, integrare self-service.
Invariant de corectitudine (motivul cheie de design): cele trei niveluri pentru un anumit cod de
eroare se definesc o singura data, intr-un catalog central pur (app/errors.py), consumat de
toate suprafetele (API + UI + worker). Daca textul s-ar duplica pe canale, API si UI ar putea
diverge — un cod ar spune un lucru in JSON si altul in dashboard. Catalogul unic face imposibila
divergenta (acelasi pattern care a facut 5.2 corect: o singura sursa partajata, nu doua copii).
2. Non-Goals (anti scope-creep)
- NU acoperim login / signup / CSRF / auth 401 (decizie utilizator 2026-06-22: focus pe fluxul de
declarare). Aceste suprafete sunt edge / dev si raman mesaje plate.
auth_routes.py,csrf.py, handler-eleLoginRequired/AdminRequired/CsrfErrordinmain.py— NEATINSE. - NU breaking change pe API (decizie utilizator 2026-06-22: aditiv). Pastram campurile existente
(
field,message,error,type/loc/msg) si ADAUGAMcod,problema,cauza,fix. Clientii vechi (ROAAUTO / soft propriu integrat la 5.1/5.2) nu se strica; cei noi pot afisa 3 niveluri. - NU schema noua —
submissions.rar_errore deja TEXT si stocheaza JSON; doar imbogatim continutul. Zero migrare. - NU apel live nou la RAR — pentru erorile RAR 400 imbracam mesajul RAR existent (passthrough ca
cauza) intr-un invelis 3-niveluri; nu schimbam clasificarea transient/terminal a worker-ului. - NU schimbam regulile de validare —
validate_prezentarevalideaza exact aceleasi conditii; doar ataseazacod+ nivelele la fiecare eroare existenta. - NU schimbam masina de stari / idempotenta / mapping-rezolvarea / nomenclatorul.
- NU traducere i18n — romana, ca tot proiectul.
3. Stories atomice
US-001: Catalog central de erori (app/errors.py) — DONE (588 teste)
Ca dezvoltator al gateway-ului vreau o singura sursa de adevar care mapeaza fiecare cod de eroare la (problema, fix), cu un helper care construieste obiectul de eroare 3-niveluri, pentru ca API-ul, UI-ul si worker-ul sa nu poata diverge in ce explica utilizatorului.
- Depinde de: —
- Fisiere:
app/errors.py(modul pur nou),tests/test_errors.py(~2 fisiere) - Test intai (RED):
tests/test_errors.py—test_catalog_complet— fiecare intrare areproblema+fixne-goale (string).test_eroare_construieste_3niveluri—eroare("VIN_FORMAT", field="vin", cauza="...")intoarce dict cu cheile{field, cod, problema, cauza, fix, message},cod=="VIN_FORMAT",problema/fixluate din catalog,cauzacea data.test_message_back_compat— candcauzae dat,message == cauza(alias pentru clientii vechi).test_cod_necunoscut_ridica—eroare("INEXISTENT")ridicaKeyError/ValueError(nu inventeaza text gol — drift prins la dezvoltare).
- Acceptance criteria:
app/errors.pypur (fara import DB/HTTP), expuneCATALOG: dict[str, dict](cod → {problema, fix}) sieroare(cod, *, field=None, cauza=None) -> dict.- Obiectul de eroare are exact cheile
{field, cod, problema, cauza, fix, message};message(back-compat)== cauzacandcauzae dat, altfel== problema. - CATALOG contine codurile pentru toate suprafetele in scop (validare continut, mapare op→cod,
RAR, import) — vezi lista din §4. Fiecare intrare are
problema+fixne-goale. fixe specific si actionabil (finding CEO): numeste un loc/o actiune concreta (ex. "talon, pozitia E", "tab-ul Mapari", "salveaza ca .csv UTF-8"), NU boilerplate generic ("verifica datele"). Un fix generic = eroare plata mai lunga; testul de calitate al livrabilei. (Verificat la review uman.)eroarecu cod absent din CATALOG ridica eroare (nu intoarce text gol).python3 -m pytest -qverde.
- Verificare E2E: unitar (modul pur) — fara canal.
US-002: Validarea de continut emite 3 niveluri (validation.py) — DONE
Ca integrator API vreau ca fiecare eroare de validare (VIN/nr/data/odometru/prestatii/b64) sa spuna problema + cauza + fix, pentru ca sa corectez payload-ul fara sa ghicesc formatul cerut.
- Depinde de: US-001
- Fisiere:
app/validation.py,tests/test_validation.py(~2 fisiere) - Test intai (RED):
tests/test_validation.py—test_vin_invalid_are_3niveluri— VIN cu O/I/Q → eroare cucod=="VIN_FORMAT",problema,fixne-goale,field=="vin",messagepastrat (back-compat).test_data_prea_veche_cod/test_data_viitor_cod/test_data_format_cod— coduri distincte.test_odometru_initial_lipsa_cod/test_odometru_ordine_cod— coduri distincte.test_prestatii_goale_cod,test_b64_invalid_cod.test_back_compat_field_message— fiecare eroare are incafield+message(forma veche pastrata pentru clientii existenti).test_toate_codurile_in_catalog— fiecarecodemis devalidate_prezentareexista inCATALOG.
- Acceptance criteria:
validate_prezentareintoarce erori cu{field, message, cod, problema, cauza, fix}(aditiv —field+messageneschimbate la octet fata de azi).- Fiecare regula are un
codstabil (vezi lista §4); textul (problema/fix) vine dinerrors.eroare. - Byte-compat (finding Eng):
cauza= mesajul existent verbatim (eventual + context specific precum lungimea VIN gasita);messageramane EXACT string-ul de azi → testele existente care comparamessageraman verzi fara modificari. - Toate testele existente (
test_validation.py,test_api.py,test_validare_dryrun.py) raman verzi (forma vechefield/messageintacta). python3 -m pytest -qverde.
- Verificare E2E: canal API —
POST /v1/prezentari/valideazacu VIN invalid →erori[0]are cele 3 niveluri +field/messagevechi.
US-003: Propagare 3 niveluri prin mapare + raspuns API (mapping.py, router.py) — DONE
Ca integrator API vreau ca raspunsul /valideaza si motivul stocat (rar_error) sa transporte
cele 3 niveluri pentru validare SI pentru coduri nemapate / auto-send oprit, pentru ca verdictul sa
fie la fel de explicit indiferent de ramura (needs_data / needs_mapping).
- Depinde de: US-001, US-002
- Fisiere:
app/mapping.py,app/api/v1/router.py,app/models.py,tests/test_mapping.py,tests/test_validare_dryrun.py(~5 fisiere) - Test intai (RED):
test_mapping.py::test_unmapped_are_3niveluri— cod_op_service necunoscut →classify_prezentareproduceneeds_mappingcucod=="COD_NEMAPAT"+ problema/cauza (codul concret)/fix in structura.test_mapping.py::test_auto_send_oprit_3niveluri— mapare cuauto_send=0→cod=="AUTO_SEND_OPRIT"- 3 niveluri.
test_mapping.py::test_needs_data_pass_through— erorile de validare imbogatite trec neatinse princlassify_prezentareinrar_error.test_validare_dryrun.py::test_erori_au_3niveluri—/valideazacu VIN invalid →erori[i]arecod/problema/cauza/fix; cu cod_op nemapat →nemapatecarry 3 niveluri.
- Acceptance criteria:
classify_prezentarepastreaza erorile de validare imbogatite (pass-through) inrar_error.- Ramura
needs_mapping(cod nemapat) si notaauto_send=0se construiesc prinerrors.eroare(3 niveluri), nu string-uri ad-hoc. rar_errorstocat = SUPERSET al formei de azi (finding Eng critic): pastreaza structura veche (needs_data→ array[{field,message,...}];needs_mapping→{unmapped:[...], ...}), ADAUGA cheile 3-niveluri. Asalabels.motiv_umanactual ramane functional intre Val 3 si Val 4 (nu se strica pana e actualizat in US-006) si nu e nevoie de migrare. Aplica principiul aditiv si la datele stocate, nu doar la API.- Raspunsul
/valideaza(erori,nemapate) includecod/problema/cauza/fixaditiv; modelele dinmodels.pyaccepta cheile noi fara a respinge (verifica: tipulerori/nemapatee permisiv sau extins; nu schema stricta care respinge chei in plus). - Teste subset, nu egalitate exacta (finding Eng): testele existente care comparau
==un dict de eroare se actualizeaza la asertii de subset (comportament identic, doar chei aditive). POST /v1/prezentari(calea reala) ramane cu comportament identic —test_api.pyverde;rar_errorstocat e JSON 3-niveluri pentru needs_data/needs_mapping.python3 -m pytest -qverde.
- Verificare E2E: canal API —
/valideazacu (a) VIN invalid → needs_data + 3 niveluri, (b) cod_op nemapat → needs_mapping + 3 niveluri. Regresia de aur:POST /v1/prezentarienqueue neschimbat.
US-004: Erorile RAR (400/401) imbracate pe 3 niveluri in worker (worker, rar_client.py) — DONE
Ca service-auto vreau ca o respingere de la RAR sa fie tradusa in problema + cauza (mesajul RAR exact) + fix, in loc de un JSON brut, pentru ca sa inteleg ce a respins RAR fara sa citesc JSON.
- Depinde de: US-001
- Fisiere:
app/worker/__main__.py,app/rar_client.py(eventual),tests/test_worker_*.py(~3 fisiere) - Test intai (RED):
tests/test_worker_rar_errors.py(nou) —test_rar_400_stocheaza_3niveluri—RarError(status=400, field_errors=[{field,message}])→ worker stocheazarar_errorJSON cucod=="RAR_VALIDARE",problema,cauzacontinand mesajul RAR exact (passthrough),fixcu indrumare; pastreaza sifield_errorsoriginale.test_rar_401_creds_3niveluri—RarAuthError→cod=="RAR_CREDS_INVALIDE"+ 3 niveluri, stareerror(fara retry, neschimbat).test_clasificare_transient_neschimbata— 5xx/timeout raman transient (retry), comportament identic.
- Acceptance criteria:
- La RAR 400,
rar_errorstocat = SUPERSET (finding Eng): pastreaza array-ulfield_errorsoriginal[{field,message}](calabels.pyactual sa-l randeze per-camp) + ADAUGA invelisul 3-niveluri (cod=RAR_VALIDARE,cauza=mesajul RAR exact passthrough,fix=indrumare). - La RAR 401,
rar_error= 3-niveluri (cod=RAR_CREDS_INVALIDE), stareerror(fara retry). - Clasificarea transient vs terminal NESCHIMBATA (5xx/408/429 retry; 4xx terminal); reconcilierea anti-duplicat neatinsa.
- Fara echo de creds in
rar_error(mesajul RAR nu contine parola; verificat in test). python3 -m pytest -qverde.
- La RAR 400,
- Verificare E2E: worker pe RAR test (daca exista creds) — prezentare cu VIN invalid → RAR 400 →
needs_datacurar_error3-niveluri vizibil in dashboard. (Live optional — vezi riscuri.)
US-005: Erorile de import imbracate pe 3 niveluri (import_router.py) — DONE
Ca service-auto care incarca un fisier vreau ca erorile de upload / mapare coloane / commit sa spuna ce e gresit, de ce si cum repar, pentru ca sa pot incarca singur fara suport.
- Depinde de: US-001
- Fisiere:
app/api/v1/import_router.py,tests/test_import_*.py(~2 fisiere) - Test intai (RED):
tests/test_import_errors.py(nou) —test_fisier_prea_mare_3niveluri— 413 → detail continecod=="IMPORT_FISIER_PREA_MARE"+ problema/cauza (nr randuri vs max)/fix; pastreazaerror/messagevechi.test_antet_neclar_3niveluri— HeaderError →cod=="IMPORT_ANTET_NECLAR"+ 3 niveluri +found.test_encoding_3niveluri,test_fisier_nerecunoscut_3niveluri,test_multiple_sheets_3niveluri.test_fara_mapare_coloane_3niveluri— preview fara mapare →IMPORT_FARA_MAPARE_COLOANE.test_confirmare_gresita_3niveluri— commit cu n gresit →IMPORT_CONFIRMARE_GRESITA+n_ok.test_override_ilizibil_3niveluri— editare cu override corupt →IMPORT_OVERRIDE_ILIZIBIL.
- Acceptance criteria:
- Fiecare
HTTPExceptionde import in scop aredetailSUPERSET: pastreazaerror/message/ campurile contextuale existente (sheets/found/n_ok) si ADAUGAcod/problema/cauza/fix. - Codurile vin din
errors.eroare(catalog), nu string-uri ad-hoc. - Toate testele de import existente raman verzi (forma veche
error/messageintacta; asertii de subset unde comparau exact — finding Eng). python3 -m pytest -qverde.
- Fiecare
- Verificare E2E: canal API —
POST /v1/importcu fisier prea mare / antet neclar → detail 3-niveluri.
US-006: Componenta UI de eroare pe 3 niveluri + stari submission (labels.py, templates core) — DONE
Ca service-auto vreau ca dashboard-ul sa afiseze problema (bold) + cauza + fix (linie de actiune) pentru randurile needs_data / needs_mapping / error si per-camp in preview, pentru ca sa stiu ce sa fac fara sa deschid un tichet.
- Depinde de: US-001, US-002, US-003, US-004 (codurile/structura trebuie sa existe)
- Fisiere:
app/web/labels.py,app/web/templates/_eroare.html(macro nou),app/web/templates/base.html(CSS),app/web/templates/_trimitere_detaliu.html,app/web/templates/_status.html,app/web/templates/_preview_rand.html,tests/test_web_*.py(~7 fisiere) - Test intai (RED):
tests/test_web_erori.py(nou) —test_motiv_uman_3niveluri—labels.motiv_umanperar_error3-niveluri intoarce problema/cauza/fix (nu doar un string plat); fallback gratios la rar_error vechi/string/corupt.test_detaliu_afiseaza_fix—/_fragments/...pe submission needs_data → HTML contine textulfix.test_preview_rand_per_camp_fix— preview rand needs_data → fiecare camp invalid aratafix-ul.
- Acceptance criteria:
- Progresiv, dashboard compact pastrat (finding Design — nu regresa 3.5/3.6): in lista/rand
se vede problema (eticheta umana existenta) + fix-ul ca o singura linie de actiune; cauza +
mesajul tehnic RAR integral stau in detaliu /
<details>(nu 3 linii per rand → zid de text). Cele 3 niveluri complete apar in panoul de detaliu si in preview-ul de rand, nu in fiecare rand din lista. - Scannabil (finding Design): nivelele au tratament vizual / etichete care le fac parcurgibile fara citire integrala (ex. "Problema" / "De ce" / "Cum repari", sau ierarhie vizuala clara).
- Macro Jinja
_eroare.htmlrandeaza consistent; mesajul tehnic RAR integral ramane in<details>(pattern existent in_trimitere_detaliu.html). labels.pyciteste catalogul / parseazarar_error3-niveluri; degradeaza gratios pe forma veche (string /[{field,message}]/ JSON corupt) — fara 500 (lectia 3.6 cu decriptarea).- Reutilizeaza, NU inlocuieste, pattern-ul bun din
_status.html(problema + subtext-hint deja ~ problema + fix);_trimitere_detaliu.html,_preview_rand.htmlfolosesc macro-ul. - CSS in paleta light+dark din 5.3 — fara culori hardcodate; accentul de "fix/actiune" trece AA
in AMBELE teme (lectia 5.3:
--okpica AA ca text); distinct de rosul de eroare. python3 -m pytest -qverde.
- Progresiv, dashboard compact pastrat (finding Design — nu regresa 3.5/3.6): in lista/rand
se vede problema (eticheta umana existenta) + fix-ul ca o singura linie de actiune; cauza +
mesajul tehnic RAR integral stau in detaliu /
- Verificare E2E: browser HTMX pe
/— rand needs_data arata problema+cauza+fix; preview rand invalid arata fix per-camp; dark + light (5.3) ambele lizibile.
US-007: 3 niveluri in import / upload / preview UI + rute web (routes.py, templates import) — DONE
Ca service-auto vreau ca erorile de la upload, mapare coloane si preview import sa apara cu cele 3 niveluri in interfata, pentru ca sa rezolv singur problemele de fisier.
- Depinde de: US-006 (macro
_eroare.html), US-005 (codurile de import) - Fisiere:
app/web/routes.py,app/web/templates/_upload.html,app/web/templates/_mapcoloane.html,app/web/templates/_preview_import.html,tests/test_web_*.py(~5 fisiere) - Test intai (RED):
tests/test_web_import_erori.py(nou) —test_upload_eroare_3niveluri— upload fisier invalid prin ruta web → fragment contine problema+fix.test_mapcoloane_format_json_3niveluri— format coloane JSON invalid →COLOANE_FORMAT_JSON3 niveluri.test_cod_rar_necunoscut_3niveluri— mapare operatie cu cod RAR inexistent → 3 niveluri + sugestie.
- Acceptance criteria:
- Caile web de eroare din
routes.py(upload, mapare coloane, format JSON, cod RAR necunoscut, corectie) trec context 3-niveluri catre template (din catalog), nu string plat. _upload.html,_mapcoloane.html,_preview_import.htmlfolosesc macro-ul_eroare.html.- Forma veche (mesaj plat) inca functioneaza unde nu exista cod (fara regresie); toate testele web existente verzi.
python3 -m pytest -qverde.
- Caile web de eroare din
- Verificare E2E: browser HTMX — upload fisier prea mare → 3 niveluri; mapare coloane JSON invalid → fix.
US-008: Documentare envelope de eroare imbogatit (api-rar-contract.md) — DONE
Ca integrator nou vreau sa stiu forma exacta a erorilor (campuri vechi + cele 3 niveluri noi) si lista de coduri, pentru ca sa-mi construiesc gestionarea de erori fara reverse-engineering.
- Depinde de: US-001, US-002, US-003, US-005 (codurile finale)
- Fisiere:
docs/api-rar-contract.md(~1 fisier) - Test intai (RED): — (doc; verificare manuala). Optional
tests/test_errors.py::test_doc_acopera_coduriledaca e fezabil ieftin (verifica ca fiecare cod din CATALOG apare in doc). - Acceptance criteria:
- Sectiune noua in
api-rar-contract.md: forma erorii ({field, message, cod, problema, cauza, fix}), nota de back-compat (campurile vechi raman), tabel cod → problema/fix. - Mentioneaza ca
/valideazasirar_errorstocat folosesc aceeasi forma.
- Sectiune noua in
- Verificare E2E: review uman al documentului.
4. Catalog de coduri (referinta — definit in US-001)
| Domeniu | Cod | problema (nivel 1) | unde |
|---|---|---|---|
| Validare | VIN_FORMAT |
VIN invalid | US-002 |
| Validare | NR_INMATRICULARE_FORMAT |
Numar de inmatriculare invalid | US-002 |
| Validare | DATA_FORMAT |
Data prestatiei in format gresit | US-002 |
| Validare | DATA_PREA_VECHE |
Data prestatiei prea veche | US-002 |
| Validare | DATA_VIITOR |
Data prestatiei in viitor | US-002 |
| Validare | ODOMETRU_FINAL_FORMAT |
Odometru final invalid | US-002 |
| Validare | ODOMETRU_INITIAL_LIPSA |
Lipseste odometrul initial | US-002 |
| Validare | ODOMETRU_INITIAL_FORMAT |
Odometru initial invalid | US-002 |
| Validare | ODOMETRU_INITIAL_ORDINE |
Odometru initial > final | US-002 |
| Validare | PRESTATII_GOALE |
Nicio prestatie | US-002 |
| Validare | B64_INVALID |
Imaginea nu e base64 valid | US-002 |
| Mapare | COD_NEMAPAT |
Lipseste codul RAR al operatiei | US-003 |
| Mapare | AUTO_SEND_OPRIT |
Necesita confirmare manuala | US-003 |
| RAR | RAR_VALIDARE |
RAR a respins prezentarea | US-004 |
| RAR | RAR_CREDS_INVALIDE |
Credentiale RAR invalide | US-004 |
| Import | IMPORT_FISIER_PREA_MARE |
Fisier prea mare | US-005 |
| Import | IMPORT_ANTET_NECLAR |
Antet de coloane neclar | US-005 |
| Import | IMPORT_ENCODING |
Codare de caractere nesuportata | US-005 |
| Import | IMPORT_FISIER_NERECUNOSCUT |
Fisier nerecunoscut | US-005 |
| Import | IMPORT_MULTIPLE_SHEETS |
Mai multe foi in fisier | US-005 |
| Import | IMPORT_FARA_MAPARE_COLOANE |
Coloanele nu sunt mapate | US-005 |
| Import | IMPORT_CONFIRMARE_GRESITA |
Numar confirmat gresit | US-005 |
| Import | IMPORT_OVERRIDE_ILIZIBIL |
Editarea anterioara nu se poate citi | US-005 |
| Coloane | COLOANE_FORMAT_JSON |
Format de coloane (JSON) invalid | US-007 |
Lista finala se fixeaza in US-001 (catalog) + drift-test (
test_toate_codurile_in_catalog). Codurile nu sunt parte din contractul de back-compat (campuri noi); mesajele RAR exacte raman incauza.
4b. Intrebari deschise (rezolvate inainte de executie)
- Latimea scope-ului — REZOLVAT: focus pe fluxul de declarare (validare continut, RAR 400, import, mapare op→cod); login/signup/CSRF/auth raman plate. [user 2026-06-22]
- Compatibilitate API — REZOLVAT: aditiv, fara breaking change (campuri vechi pastrate, adaugam
cod/problema/cauza/fix); documentat inapi-rar-contract.md. [user 2026-06-22]
5. Riscuri
- Drift catalog (cod folosit dar absent din CATALOG, sau text gol) →
errors.eroareridica pe cod necunoscut (US-001) +test_toate_codurile_in_catalog(US-002) — drift prins la dezvoltare, nu in prod. fixgeneric, fara valoare (finding CEO) → criteriu de calitate explicit (US-001): fiecarefixnumeste loc/actiune concreta; verificat la review uman + in design-review-ul UI.rar_errorstocat schimbat rupelabels.pyintre valuri (finding Eng) → stocam SUPERSET (old keys intacte) in US-003/US-004;labels.pyactual ramane functional pana la US-006; zero migrare.- Teste cu egalitate exacta de dict (finding Eng) → se trec la asertii de subset (acelasi comportament, chei aditive); contractul de back-compat e pe campurile vechi, nu pe absenta celor noi.
- Breaking change accidental pe API → aditiv prin constructie + testele existente (
test_api.py,test_validare_dryrun.py,test_import_*.py) sunt contractul de back-compat: raman verzi = forma vechefield/message/errorintacta. AC explicit in fiecare story backend. - 500 la afisare UI pe
rar_errorvechi/corupt (lectia 3.6: decriptare neprotejata) →labels.pydegradeaza gratios pe forma veche (string /[{field,message}]/ JSON corupt), test dedicat (US-006). - Scurgere de creds prin
cauza(mesaj RAR passthrough) → mesajele RAR de validare nu contin parola (field/message pe campuri de prezentare); test no-echo (US-004). Handler-ul 422 dinmain.pydeja dropeazainput/ctx. - Suprafata mare → 8 stories pe valuri cu fisiere disjuncte (vezi §6); backend (US-002/004/005) paralel, UI secvential dupa backend, docs paralel.
- Verbozitate UI (3 niveluri = zgomot) → progresiv: problema + fix vizibile, mesajul tehnic RAR
integral ramane in
<details>(pattern existent in_trimitere_detaliu.html). - Conflict pe fisiere comune (lectia 5.1: clobber la worktree/merge) → mapping.py atins doar de
US-003; routes.py doar de US-007; templates partitionate intre US-006 (detaliu/status/preview_rand) si
US-007 (upload/mapcoloane/preview_import); macro
_eroare.htmlcreat in US-006, consumat in US-007 (dependenta de val, nu paralel).
6. Valuri de executie (graful de dependente)
Val 1: [US-001] backbone catalog (singur — toti depind de el)
Val 2: [US-002] [US-004] [US-005] backend paralel, fisiere disjuncte
validation.py worker/rar import_router.py
Val 3: [US-003] mapping.py + router.py + models.py (depinde US-002)
Val 4: [US-006] [US-008] UI core (labels+templates) || docs (api-rar-contract.md) — disjuncte
Val 5: [US-007] UI import/web (depinde US-006 pt macro)
- Val 2: max 2-3 teammates simultan (ROADMAP §5.5). validation.py / worker+rar_client / import_router.py sunt disjuncte → 3 teammates paraleli OK.
- Val 4: US-006 (templates+labels) si US-008 (doc) ating fisiere disjuncte → paralel.
- Dupa fiecare val: lead-ul ruleaza
python3 -m pytest -q(regresie) si bifeaza stories in PRD.
7. Review-uri de plan (aplicate inainte de cod — ROADMAP §5.3)
Obligatorii:
/plan-ceo-review(valoare/scope) +/plan-eng-review(fezabilitate/teste)./plan-design-review— DA (atinge UI: US-006, US-007). Rezultatele se aplica IN acest PRD inainte de cod.
CEO (valoare/scope) — PASS. Problema corecta (DX: erorile plate transfera incertitudinea la user →
tichete de suport), aliniata cu directia Etapa 5. Scope-ul (declaration flow) e cel mai direct la valoare;
login/signup/CSRF taiate corect (decizie user). Inversiune ("ce-l face sa esueze?"): un fix generic
("verifica datele") face dintr-o eroare 3-niveluri doar o eroare plata mai lunga — valoarea traieste sau
moare in specificitatea fix-ului. → Aplicat ca criteriu de calitate explicit (US-001 AC + risc): fiecare
fix numeste loc/actiune concreta. Deferare constienta: POST /v1/prezentari real intoarce doar
status, nu erori inline — integratorul afla "de ce" printr-un GET sau prin /valideaza; a adauga
erori inline pe ruta reala ar fi aditiv + util, dar e scope creep peste 5.4 → notat ca oportunitate
viitoare, nu in scope acum.
Eng (fezabilitate/teste) — PASS cu 3 conditii (aplicate in PRD). Catalogul pur + helper = backbone
fezabil, drift prins la dev. (1) Critic — rar_error stocat trebuie SUPERSET (old keys intacte): altfel
labels.motiv_uman se strica intre Val 3 (backend schimba forma) si Val 4 (UI o citeste); superset =
zero migrare + degradare gratioasa (lectia 3.6). Aplicat in US-003/US-004 AC + risc. (2) Byte-compat:
validarea pune mesajul existent verbatim ca cauza, message ramane identic → testele pe message
raman verzi (US-002). (3) Teste subset, nu egalitate exacta de dict (chei aditive) — aplicat in
US-002/003/005. models.py trebuie sa accepte chei in plus pe erori/nemapate (verificat in US-003).
Worker testabil cu rar_client mock-uit (fara live RAR).
Design (UI — US-006/US-007) — PASS cu 3 conditii (aplicate in PRD). (1) Progresiv, nu regresa
dashboard-ul compact din 3.5/3.6: in lista/rand → problema + fix pe o linie; cauza + tehnic RAR in
detaliu/<details>; cele 3 niveluri complete doar in panoul de detaliu + preview-ul de rand. (2)
Scannabil: etichete/ierarhie vizuala ("Problema"/"De ce"/"Cum repari") ca user-ul sa parcurga fara
citire integrala. (3) AA in ambele teme (lectia 5.3): accentul de "fix/actiune" trece AA light+dark,
fara culori hardcodate, distinct de rosul de eroare. Reutilizeaza pattern-ul bun din _status.html
(problema + subtext = ~ problema + fix), nu il inlocui.
Raport VERIFY
Verificator independent (context curat, rol qa-only), 2026-06-22. VERDICT GLOBAL: PASS.
1. Suita — PASS. python3 -m pytest -q → 628 passed, 234 warnings.
2. Acceptance criteria US-001..US-008 — toate PASS. Verificate direct pe cod + probe live (TestClient + SQLite temp):
- US-001: catalog pur, 24 coduri (
MISSING:[],EMPTY:[]),eroare('INEXISTENT')→KeyError, chei{field,cod,problema,cauza,fix,message},message==cauza/==problemacorect. - US-002: byte-compat confirmat —
messageVIN identic la octet cugit show HEAD:app/validation.py(textul vechi pus cacauza); erori complete pe 6 chei. - US-003:
rar_errorSUPERSET confirmat — needs_mapping pastreazaunmapped; auto_send pastreazaauto_send; needs_data ramane array cufield+message;nemapatedin/valideazapoarta 3 niveluri;models.py:113-114permisiv (list[dict]). - US-004: RAR 400→
RAR_VALIDARE(field/messagepastrate, cauza=mesaj RAR passthrough), RAR 401→RAR_CREDS_INVALIDEfara retry fara echo creds;_is_transientneschimbat. - US-005: detalii import superset (
error/message/sheets/found/n_ok+ 3 niveluri). - US-006:
parse_eroridegradeaza gratios (string plat /[{field,message}]fara cod / JSON invalid / None — fara exceptie); detaliu randeaza Problema/De ce/Cum repari +<details>tehnic; CSS doar variabile paleta, AA in ambele teme (accent 5.17/5.33, err 4.83/5.06), accent ≠ rosu. - US-007: upload/mapcoloane prin macro; per-camp
camp-fix. - US-008: contract documentat (forma 6 chei, back-compat, tabel cod→problema/fix complet).
- Calitate
fix(finding CEO) — PASS: specifice/actionabile ("talon pozitia E", "tab-ul Mapari", "CSV UTF-8", "maxim 5000 randuri"); niciun fix generic. - Non-Goal — PASS:
auth_routes.py/csrf.py/main.pyNEATINSE (confirmatgit status).
3. E2E canal API — PASS. /valideaza: (a) VIN invalid → erori[0] cu cod/problema/cauza/fix + field/message vechi; (b) cod_op nemapat → nemapate[0] cu cod_op_service/denumire + 3 niveluri. POST /v1/prezentari real → 200 {status:queued}.
4. E2E canal web (UI, TestClient pe fragmente) — PASS. Upload invalid → _upload.html cu eroare-3n + "Cum repari" + fix; submission needs_data → _trimitere_detaliu.html cu 3 niveluri + <details> tehnic. (Browser Playwright neutilizat — fragmentele TestClient acopera criteriile.)
5. Regresia de aur — PASS (live neprobat, conform asteptarii). POST /v1/prezentari→queued + test_api.py verde. Flux LIVE RAR (worker→FINALIZATA pe RAR test) NEPROBAT — lipsesc AUTOPASS_CREDS_KEY+creds test+--send in mediu; NU e FAIL al 5.4 (endpoint-urile/UI noi nu ating trimiterea; worker-ul doar imbogateste rar_error).
Observatie minora (ne-blocanta), REPARATA la CLOSE: exemplul JSON din api-rar-contract.md avea message/cauza cu un text VIN usor diferit de cel real emis de validation.py:72. Corectat (ambele linii) sa coincida verbatim cu codul.