Files
rar-autopass/docs/prd/prd-5.23-editor-prestatii-unificat.md
Claude Agent 56ebe1f7cb feat(editor): editor prestatii unificat in modal - un select, un rand de chips (PRD 5.23)
Inlocuieste cele 3 controale de asociere cod RAR cu un singur rand de chips
+ un singur select care adauga instant la change. Stare = un hidden chips_state
JSON versionat; post_form_chips redus la 2 actiuni (add/remove). Optgroup
Sugestii (fuzzy/k-NN) + optiune "Nu se declara la RAR" in select, cu tinta
implicita evidentiata si placeholder care o numeste. exclus persistat prin
payload_json (treapta noua de precedenta in resolve_prestatii, round-trip complet).

Siguranta: itemii exclusi sunt scosi din payload la momentul trimiterii
(worker split_prestatii_excluse inainte de build_rar_payload + filtru defensiv),
ca sa nu ajunga NICIODATA la RAR ca codPrestatie:null.

Suita: 1680 passed, 1 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:45:34 +00:00

38 KiB
Raw Permalink Blame History

PRD 5.23 — Editor prestatii unificat in modalul "Editare rand" (un select, un rand de chips)

Stare: inchis (2026-07-08; executie echipa agenti Sonnet TDD, VERIFY context curat PASS, code-review CLOSE = 1 blocker de siguranta prins + reparat. Vezi Anexa /autoplan, GSTACK REVIEW REPORT, Raport VERIFY; mockup final aprobat: select cu placeholder-tinta)

Proces: docs/ROADMAP.md §5. Contract RAR: docs/api-rar-contract.md. Context vizual: screenshot 2026-07-07-003.jpg (modal "Editare rand 4", badge "Cod RAR lipsa").

1. Obiectiv

Modalul "Editare rand" (import preview + corectie Trimiteri) are azi TREI controale pentru aceeasi sarcina — asocierea codurilor RAR la prestatii: (1) rand per operatie cu select "— alege cod RAR —"

  • buton "Adauga", (2) container separat de chips pentru coduri libere (ex. OE-1 ×), (3) select "+ Adauga alta operatie / cod RAR" cu buton "+". Utilizatorul nu stie care control face ce, iar chips-urile apar in containere diferite, pe randuri diferite.

Livrabila: un singur control — un rand unic de chips (toate codurile, legate de operatie sau libere) + un singur select care adauga chip instant la selectie (fara butoane "Adauga"/"+"). Selectul primeste sugestii fuzzy/k-NN pentru operatia nemapata si optiunea "Nu se declara la RAR".

2. Non-Goals (anti scope-creep)

  • NU se schimba panoul de mapare (_mapari.html, _preview_import.html). EXCEPTIE confirmata in review (eng, 2026-07-07): resolve_prestatii primeste O SINGURA treapta noua de precedenta — exclus=1 explicit pe item (din override/corectie) supravietuieste re-rezolvarii (azi it.pop("exclus", None) la mapping.py:298 il sterge, ceea ce face US-004 imposibil altfel). Restul precedentei ramane neatins.
  • NU se schimba contractul API POST /v1/prezentari. override_json["prestatii"] castiga campul exclus pe item; itemii exclusi RAMAN in payload_json (adnotati), nu mai sunt eliminati la salvare — split_prestatii_excluse ii scoate doar din payload-ul trimis la RAR.
  • NU se schimba validarea codului fata de nomenclator la salvare (invariant ORA-12899) — ramane.
  • NU se adauga cautare/autocomplete text in select (ramane <select> nativ; imbunatatire separata).
  • NU se atinge butonul "salveaza ca regula" de la Trimiteri — ramane, doar mutat pe chip-ul nou.

3. Comportamentul controlului unificat (spec)

Prestatii — coduri RAR
┌───────────────────────────────────────────────────────────────┐
│ [⚠ OP-ITP · fara cod]  [OE-1 ×]        [— alege cod RAR — ▾] │
└───────────────────────────────────────────────────────────────┘
  • Un singur container .chips cu toate elementele: chips-uri cod (× sterge), chips-uri warning pentru operatii nemapate, si selectul unic la capat. Wrap natural doar la lipsa de spatiu — niciodata containere/chenare separate pe zone.
  • Chip cod legat de operatie: eticheta COD, titlu/tooltip cu denumirea operatiei sursa (ex. OE-8 — Inlocuire anvelope). × scoate codul; daca operatia ramane fara cod, reapare chip-ul warning al operatiei.
  • Chip cod liber (fara operatie sursa): identic vizual, tooltip "cod adaugat manual".
  • Chip warning (⚠ OP-ITP · fara cod): reprezinta operatia din import nemapata. Click pe el il face tinta activa (evidentiat); implicit tinta = prima operatie nemapata.
  • Selectul unic, hx-trigger="change"POST /form-chips cu chips_action=add:
    • daca exista tinta activa (operatie nemapata) → codul se leaga de acea operatie (chip warning → chip cod); tinta trece automat la urmatoarea operatie nemapata;
    • placeholder-ul selectului numeste tinta (decizie gate D2, 2026-07-07): cu tinta activa → Cod pentru OP-X — alege; fara tinta → — adauga cod RAR —. Legatura cod↔operatie e explicita la momentul alegerii, fara text suplimentar sub control;
    • fara operatii nemapate → codul se adauga ca chip liber;
    • dupa adaugare selectul revine la placeholder; chip-ul nou primeste evidentierea .chip-nou.
  • Continutul selectului, cand tinta e o operatie nemapata:
    • optgroup "Sugestii" sus: sugestia principala + fuzzy/k-NN top-3 pentru denumirea operatiei (aceleasi surse ca in _preview_import.html:89-121);
    • optiunea "Nu se declara la RAR (exclude operatia)" = sentinel __NEDECLARAT__ (EXCLUDE_SENTINEL, app/mapping.py:44) → operatia primeste chip distinct Nedeclarat (stil mut, ×-ul readuce chip-ul warning), item anotat exclus in starea chips;
    • restul nomenclatorului, ca azi. Fara tinta (doar cod liber): fara optgroup Sugestii si fara __NEDECLARAT__.
  • Dedup: acelasi (operatie, cod) nu se adauga de doua ori (comportament existent pastrat).
  • Fara text explicativ permanent sub control (decizie utilizator la aprobarea mockup-ului, 2026-07-07): starea tintei se comunica DOAR vizual (stilul chip-ului warning evidentiat); mesajele chips_ok/chips_error raman exclusiv ca feedback tranzitoriu la actiuni.
  • Mockup aprobat: artifact "Mockup — Editor prestatii unificat (PRD 5.23)".
  • Modul plat (corectii fara operatii din import) foloseste ACELASI control: zero chips warning, selectul adauga doar coduri libere. Logica has_r_odo/data-has-r-odo (reveal odometru initial pe R-ODO/I-ODO) si stilul chip-warn pe aceste coduri se pastreaza neschimbate.

3.1 Specificatie vizuala si de stari (din review-ul de design, 2026-07-07)

Scara de dominanta vizuala — UN SINGUR tratament puternic simultan:

Element Fundal / chenar / text Nota
Chip tinta activa fundal warn-soft, chenar SOLID 1px --warn + inel 2px warn-transparent singurul element "tare" din rand
Chip warning (ne-tinta) fundal warn-soft, chenar DASHED 1px --warn click → devine tinta
Chip cod fundal --card2-albastru (--accent 15%), text --accent, chenar subtil eticheta COD mono
Chip Nedeclarat fundal gri mut, text taiat (line-through), chenar dashed gri NU discret pana la invizibil
.chip-nou animatie de emfaza >=1.5s (fade), tranzitorie nu concureaza cu tinta
Chip R-ODO/I-ODO chip-warn existent, neschimbat reveal odometru pastrat

Invariante de interactiune:

  • Ordinea chips e STABILA: operatiile in ordinea din import, codurile libere la coada; o rezolvare nu re-ordoneaza randul (warning → cod in acelasi slot).
  • Cat exista operatii nemapate, EXISTA tinta (implicit prima nemapata); click pe tinta activa = no-op (nu se poate "deselecta" — adaugarile libere sunt posibile doar fara warnings).
  • Click pe chip warning = comutare de tinta CLIENT-SIDE (toggle clasa + hidden chips_target_index), fara round-trip; indexul tintei pleaca la server la urmatorul add.
  • Selectul are id stabil (chips-picker); dupa swap outerHTML, focusul revine pe select.
  • Optiunile selectului: COD — denumire nomenclator; in optgroup "Sugestii" acelasi format (optgroup-ul e singura marca de sugestie). Eticheta chip warning: max ~18ch + ellipsis, text complet in title.

Tabel de stari (ce VEDE utilizatorul):

Stare Ce se vede
Loading (add in zbor) selectul disabled (hx-disabled-elt="this") + stil .htmx-request pe sectiune; dedup face inofensiv un dublu-fire
Eroare add (400/500/retea) mesaj chips_error SUB control (pattern 5.22, persista pana la urmatoarea actiune); selectul re-activat; tinta neschimbata; hx-on::response-error acopera esecul de retea
Gol (mod plat, zero chips) doar selectul cu placeholder "— adauga cod RAR —"; container cu min-height (modalul nu salta la primul chip)
Succes partial chip-ul nou cu emfaza >=1.5s; tinta avanseaza (vezi decizia de la gate)
Complet (zero nemapate) mesaj tranzitoriu "Toate operatiile au cod" (chips_ok); optgroup Sugestii si __NEDECLARAT__ dispar din select
Nedeclarat mesajul numeste operatia: "OP-X nu se va declara la RAR"
Nomenclator gol empty state existent (pastrat)

4. Stories atomice

US-001: Stare chips extinsa cu exclus + actiuni add/remove unificate in backend

Ca dezvoltator vreau o singura pereche de actiuni add/remove pe starea chips (cu tinta optionala si suport sentinel __NEDECLARAT__) pentru ca azi exista 4 actiuni suprapuse (add, add_flat, add_extra, remove_flat) care dubleaza logica.

  • Depinde de: —
  • Fisiere: app/web/routes.py (post_form_chips :2402-2521, _chips_state_from_form :3862-3918), tests/test_web_chips.py (~2 fisiere)
  • Test intai (RED): tests/test_web_chips.pytest_add_cu_tinta_leaga_de_operatie, test_add_fara_tinta_adauga_cod_liber, test_add_nedeclarat_marcheaza_exclus, test_remove_cod_readuce_operatia_nemapata, test_dedup_operatie_cod
  • Acceptance criteria:
    • Starea chips serializata intr-UN SINGUR hidden chips_state = JSON versionat ({"v":1,"items":[{"cod":"","op":"","den":"","exclus":0},...]}) — decizie gate D4: elimina structural clasa de bug-uri de aliniere pe index (2 regresii in 2 release-uri). _chips_state_from_form parseaza JSON-ul; JSON invalid → eroare vizibila, nu stare goala.
    • Compat tranzitorie la deploy: daca chips_state lipseste dar exista listele vechi (cod_prestatie/chip_op_service/chip_denumire), parserul cade inapoi pe ele cu exclus=0 (form-uri deschise peste deploy); fallback-ul se sterge in release-ul urmator.
    • chips_state prezent mereu (lista goala = {"v":1,"items":[]}): golirea intentionata a chips se persista la salvare (inlocuieste santinela chips_present).
    • chips_action=add + chips_target_index=i leaga codul de operatia i; fara target → chip liber; cod __NEDECLARAT__ + target → item cu exclus=1 si fara cod_prestatie.
    • chips_action=remove (index unic) acopera si fostul remove_flat; actiunile add_flat/add_extra/chips_add_cod_{i}/chips_add_cod_flat sunt eliminate.
    • Tinta implicita dupa un add reusit = urmatoarea operatie nemapata (sau nici una).
    • Validare tinta SEMANTICA, nu doar in-range: chips_target_index trebuie sa arate spre o operatie nemapata si ne-exclusa; altfel chips_error (acopera POST fabricat si index stale dupa un remove).
    • chip_exclus parsat strict ("1"→1, orice altceva→0); __NEDECLARAT__ fara tinta valida → chips_error explicit.
    • Testele existente pe actiunile vechi (tests/test_web_corectie_prestatii.py, tests/test_web_form_editare_slim.py) sunt ACTUALIZATE in acelasi val (suita nu ramane rosie intre valuri).
    • Emfaza chip nou pe INDEX (chips_added_index), nu pe valoarea codului (doua operatii pot avea legitim acelasi cod).
    • python3 -m pytest -q trece.
  • Verificare E2E: acoperit de US-005.

US-002: Template unificat — un container, un select, fara butoane Adauga/+

Ca operator vreau un singur rand de chips si un singur select care adauga instant pentru ca azi nu stiu care din cele 3 controale trebuie folosit.

  • Depinde de: US-001
  • Fisiere: app/web/templates/_chips_prestatii.html (rescriere), app/web/templates/base.html (CSS .chips/.chip* :815-843, eliminare .op-row), tests/test_web_chips.py (~3 fisiere)
  • Test intai (RED): tests/test_web_chips.pytest_render_un_singur_container_chips, test_render_fara_op_row_si_fara_butoane_adauga, test_chip_warning_pentru_operatie_nemapata
  • Acceptance criteria:
    • Un singur container .chips contine: chips cod (cu tooltip operatia sursa), chips warning operatii nemapate, chip Nedeclarat pentru itemi exclusi, selectul unic la capat.
    • Selectul (nume camp: chips_pick, id stabil chips-picker) are hx-trigger="change", hx-post="/form-chips", hx-target="#chips-section", hx-swap="outerHTML", hx-include="closest form" (transporta CSRF + starea hidden — omiterea = 403) si hx-disabled-elt="this" (capcana htmx din 5.22 — NU mosteni de la form).
    • Selectul revine la placeholder dupa ORICE raspuns (succes SI eroare); valoarea ramasa in chips_pick NU e aplicata la Salveaza pe niciuna din cai (regresia c6e29aa nu reapare sub alt nume — codul se adauga DOAR prin actiunea add).
    • Pe durata unui request /form-chips, INTREAGA sectiune e inerta (pointer-events:none pe .htmx-request sau echivalent) — click pe × / chip warning mid-flight nu pierde actiuni.
    • Click pe chip warning comuta tinta CLIENT-SIDE (clasa + hidden chips_target_index), fara round-trip; click pe tinta activa = no-op.
    • Focus restaurat pe #chips-picker dupa swap.
    • Zero aparitii op-row, Adauga, add_flat, chips_add_cod_ in template.
    • chips_submission_id e emis ca hidden in sectiune si ecouat de /form-chips, astfel incat "salveaza ca regula" ramane disponibil si DUPA re-randari (azi dispare la prima interactiune — bug perpetuat altfel).
  • Verificare E2E: acoperit de US-005.

US-003: Sugestii fuzzy/k-NN + optiunea "Nu se declara la RAR" in selectul unic

Ca operator vreau sugestiile de mapare direct in selectul din modal pentru ca azi ele exista doar in panoul de mapare, iar in modal caut manual prin tot nomenclatorul.

  • Depinde de: US-001, US-002
  • Fisiere: app/web/routes.py (post_form_chips — construire sugestii pentru operatia tinta, refolosind sursele din preview), app/web/templates/_chips_prestatii.html, tests/test_web_chips.py (~3 fisiere)
  • Test intai (RED): tests/test_web_chips.pytest_select_are_optgroup_sugestii_pt_tinta, test_select_are_nedeclarat_doar_cu_tinta, test_select_fara_tinta_fara_sugestii
  • Acceptance criteria:
    • Cu tinta activa: optgroup "Sugestii" cu sugestia principala + top-3 fuzzy/k-NN pentru denumire-a operatiei; sub el optiunea __NEDECLARAT__; apoi nomenclatorul complet.
    • Fara tinta: selectul listeaza doar nomenclatorul (fara Sugestii, fara __NEDECLARAT__).
    • Sugestiile folosesc aceleasi functii ca _preview_import.html (fara drum nou de cod).
    • Degradare gratioasa: motor de sugestii rece/dezactivat → select DOAR cu nomenclatorul (fara optgroup), fara eroare si fara blocare pe warmup (lock non-blocking, PRD 5.21).
    • python3 -m pytest -q trece.
  • Verificare E2E: acoperit de US-005.

US-004: Persistarea exclus prin salvare (import + Trimiteri) — cu round-trip complet

Ca operator vreau ca excluderea aleasa in modal sa se pastreze la salvare, sa produca starea corecta si sa fie REVERSIBILA la redeschidere pentru ca altfel optiunea din select ar fi cosmetica sau, mai rau, operatia ar fi declarata contra intentiei mele (RAR e ireversibil).

Corectie din review (eng, CONFIRMATA in cod): pipeline-ul actual sterge exclus per-item (resolve_prestatii face it.pop("exclus", None), mapping.py:298) si elimina itemii exclusi din payload_json la corectie (routes.py:1744). Fara fix-urile de mai jos, un item exclus din modal ar reveni needs_mapping sau — daca operatia are mapare pe cont — ar fi DECLARAT la RAR.

  • Depinde de: US-001
  • Fisiere: app/mapping.py (resolve_prestatii — treapta noua: item-exclus explicit supravietuieste), app/web/routes.py (web_editeaza_rand, post_corectie_trimitere :1619, _prestatii_chips_from_payload :1336 + toti constructorii de context chips), app/api/v1/import_router.py (apply_row_override :319-366), tests/test_web_editare_rand.py (~4 fisiere)
  • Test intai (RED): tests/test_web_editare_rand.pytest_salvare_pastreaza_exclus_in_override, test_exclus_supravietuieste_resolve_chiar_cu_mapare_pe_cont, test_redeschidere_modal_arata_chip_nedeclarat_si_x_readuce_warning, test_rand_toate_operatiile_excluse_devine_excluded, test_salvare_trimitere_cu_exclus
  • Acceptance criteria:
    • resolve_prestatii pastreaza exclus=1 venit explicit pe item (precedenta: item-exclus > cod explicit > regula exclus cont > mapare > reguli text); restul treptelor neschimbate.
    • Itemii exclusi RAMAN in payload_json/override_json adnotati cu exclus=1; split_prestatii_excluse ii scoate DOAR din payload-ul trimis la RAR si din cheia idempotenta.
    • Toti constructorii de chips (_prestatii_chips_from_payload, preview, re-render eroare) propaga exclus → chip Nedeclarat reapare la redeschidere; × il readuce la warning.
    • Rand cu toate operatiile excluse → stare preview excluded ("Nedeclarat"); la Trimiteri → needs_data cu motiv explicit (invariantele existente).
    • Validarea cod-vs-nomenclator la salvare ramane; sentinelul __NEDECLARAT__ e RESPINS explicit daca apare ca cod_prestatie in starea hidden (nu doar in select), pe ambele cai.
    • python3 -m pytest -q trece.
  • Verificare E2E: acoperit de US-005.

US-005: Verificare E2E browser pe ambele fluxuri

Ca lead vreau dovada in browser ca editarea e acum un singur control coerent pentru ca bug-ul raportat e de UX vizual, nu doar de logica.

  • Depinde de: US-002, US-003, US-004
  • Fisiere: — (doar verificare; eventuale fix-uri in fisierele de mai sus)
  • Test intai (RED): —
  • Acceptance criteria:
    • Import preview: rand cu operatie nemapata + cod liber → modalul arata UN rand de chips (warning + cod + select), fara chenare/zone separate; screenshot atasat la raport.
    • Selectarea unui cod din Sugestii transforma chip-ul warning in chip cod, instant, fara "+".
    • Alegerea "Nu se declara la RAR" produce chip Nedeclarat; salvare → rand "Nedeclarat".
    • Trimiteri (/trimitere/{id} corectie): acelasi control, "salveaza ca regula" functional.
    • Salvarea persista chips-urile (regresie c6e29aa) si nu reapar controale duble la re-deschidere.
  • Verificare E2E: browser HTMX pe http://localhost:8010/ (import xlsx test + o trimitere needs_mapping), conform pasilor de mai sus.

5. Riscuri

  • hx-disabled-elt mostenit de la form a mai rupt butoanele chips o data (5.22) — selectul unic primeste explicit hx-disabled-elt="this"; test de template pentru atribut.
  • change pe select nativ se declanseaza si la navigarea cu tastatura pe unele browsere vechi; acceptat (comportamentul modern e la confirmare), iar chip-ul gresit se sterge cu un click.
  • Compatibilitate stare veche: submission-uri/override-uri existente fara chip_exclus trebuie citite ca exclus=0 (default la deserializare) — altfel salvarea de pe randuri vechi crapa.
  • Doua endpoint-uri de save (import + Trimiteri) cu acelasi contract de campuri — orice schimbare de nume de camp se face simultan in ambele, plus _chips_state_from_form unic partajat.

6. Intrebari deschise

Rezolvate cu utilizatorul la aprobare (2026-07-07):

  • × pe un chip Nedeclaratreaduce chip-ul warning al operatiei (operatia NU se sterge).
  • Selectul unic ramane <select> nativ cu tot nomenclatorul — acceptat; autocomplete ramane non-goal.

7. Valuri de executie (graful de dependente)

Val 1: [US-001]                      ← fara dependente
Val 2: [US-002] [US-004]             ← deblocate de US-001, fisiere distincte → paralel
Val 3: [US-003]                      ← deblocat de US-002
Val 4: [US-005]                      ← verificare finala E2E

Anexa /autoplan — Review CEO (Faza 1, 2026-07-07, mod SELECTIVE EXPANSION, voci: subagent-only)

NOT in scope (decizii din review)

  • Autocomplete/cautare in picker — non-goal confirmat de utilizator.
  • Schimbarea panoului de mapare / precedentei resolve_prestatii — non-goal PRD.
  • Badge-uri sursa sugestie (GOLD/SILVER) in select — respins (P5): optgroup "Sugestii" e suficient.
  • "Bulk: aplica maparea la N randuri identice" in preview — deferat TODOS (in afara blast radius).
  • Metrici de baza (deschideri modal / % randuri mapate manual) — deferat TODOS.

What already exists (refolosit, nu reconstruit)

  • Stare chips in hidden inputs + /form-chips stateless (_chips_prestatii.html, routes.py:2402).
  • Validare cod vs nomenclator la add si la save (c6e29aa); mesaje chips_error/chips_ok (5.22).
  • Sugestii: suggest_codes + sugestie_principala (mapping.py:117,868) — aceleasi surse ca preview.
  • EXCLUDE_SENTINEL/split_prestatii_excluse (0bc6187); apply_row_override (c6e29aa).
  • Empty state nomenclator lipsa; has_r_odo/reveal odometru; buton "salveaza ca regula" (Trimiteri).

Dream state delta

Planul muta editorul de la 3 controale/4 actiuni la 1 control/2 actiuni si aduce sugestiile in modal — pas direct spre idealul 12 luni (o singura componenta de mapare peste tot). Ramase pe traiectorie: autocomplete (daca nomenclatorul creste), componenta partajata cu panoul de mapare.

Arhitectura (Sectiunea 1)

[modal Editare rand]──include──▶[_form_editare]──include──▶[_chips_prestatii (REWRITE)]
        │                                                        │ hx-post change/×
        ▼                                                        ▼
POST editeaza (import) ─┐                              POST /form-chips (add|remove)
POST corecteaza (Trim.) ─┴─▶ _chips_state_from_form ──▶ apply_row_override(prestatii+exclus)
                                                              │ (commit)
                              suggest_codes/mapping.py ◀──────┘ resolve + split_prestatii_excluse

Cuplaj NOU: /form-chips → motorul de sugestii (pana acum doar preview-ul) — justificat (reuse), cu conditia caii NE-blocante pe request (lock embeddings non-blocking, PRD 5.21). Fara migrare DB (exclus traieste in override_json). Rollback: git revert simplu. Fereastra de deploy: un modal deschis peste deploy trimite campuri vechi (chips_add_cod_{i}) pe care handlerul nou le ignora — utilizatorul redeschide editarea; acceptat (sesiune de editare scurta).

Registru erori & rescue (Sectiunea 2)

CODEPATH                  | CE POATE ESUA                      | TRATARE                        | USER VEDE
--------------------------|------------------------------------|--------------------------------|-----------
/form-chips add           | cod gol / necunoscut               | chips_error (exista)           | mesaj rosu
/form-chips add           | index tinta iesit din forma        | chips_error (exista)           | mesaj rosu
/form-chips add NED       | __NEDECLARAT__ fara tinta          | GAP -> chips_error nou         | mesaj rosu
/form-chips render        | motor sugestii rece/dezactivat     | GAP -> degradare la nomenclator| select fara optgroup
/form-chips               | nomenclator gol                    | empty state (exista)           | avertisment
save (ambele)             | cod necunoscut in stare            | re-render cu eroare (exista)   | modal + eroare
save (ambele)             | stare veche fara chip_exclus       | default exclus=0 (PRD risc)    | nimic (compat)
save (ambele)             | CSRF invalid/expirat               | 403 (existent)                 | eroare form
enqueue                   | toate operatiile excluse           | excluded / needs_data (exista) | stare explicita

GAP-uri de inchis in implementare: NED-fara-tinta si degradarea sugestiilor (ambele = criterii noi).

Securitate (Sectiunea 3)

Fara endpoint nou. Input nou chip_exclus: parsare stricta ("1" -> 1, orice altceva -> 0). Sentinelul __NEDECLARAT__ nu ajunge NICIODATA in payload RAR (split inainte de enqueue — invariant existent; test explicit). XSS: denumire in options/title — autoescape Jinja (exista). Sugestii cross-account: politica 5.14 (suggestion-only) neschimbata. CSRF pe /form-chips: exista. Nicio amenintare High nemitigata.

Edge cases date/interactiune (Sectiunea 4)

  • Dublu change in zbor: hx-disabled-elt="this" pe select (criteriu US-002).
  • Focus pierdut dupa swap outerHTML: focusul revine pe select dupa add (criteriu nou US-002).
  • Navigare cu tastatura prin select declanseaza change pe unele browsere: acceptat (PRD risc), chip-ul gresit se scoate cu un click.
  • Iesire din modal mid-edit: stare pierduta (comportament existent, form-local) — neschimbat.
  • Dedup (operatie,cod) si cod liber duplicat: exista, se pastreaza.
  • Mod plat + has_r_odo: pastrate explicit (spec actualizat).

Calitate (Sectiunea 5)

4 actiuni -> 2 reduce ramificarea din post_form_chips (~120 linii azi). _chips_state_from_form ramane UNICUL parser, partajat de ambele endpoint-uri de save (risc PRD; test comun). Alternativa "un singur camp hidden JSON" in loc de 4 liste paralele — decizie de gust (vezi gate).

Teste (Sectiunea 6) — completari la RED-urile din PRD

  • test_add_nedeclarat_fara_tinta_da_eroare (US-001)
  • test_sugestii_indisponibile_degradare_fara_optgroup (US-003)
  • test_mod_plat_fara_operatii_select_doar_coduri_libere (US-002)
  • test_has_r_odo_pastrat_dupa_actiuni (US-002)
  • test_sentinel_nedeclarat_nu_ajunge_in_payload (US-004)
  • Template: test_select_are_hx_disabled_elt_this (capcana 5.22)

Performanta (Sectiunea 7)

Sugestiile se calculeaza la FIECARE re-render /form-chips pentru operatia tinta — bounded (matvec numpy 5.21, corpus 17k, <100ms tipic); calea request NU asteapta warmup (lock non-blocking). 18 optiuni in select — trivial. Fara probleme.

Observabilitate (Sectiunea 8)

Starea randurilor e deja vizibila in dashboard (needs_mapping/excluded). Gap acceptat si deferat TODOS: metrica "deschideri modal / sugestie acceptata" (leaga de decizia de produs, nu de 5.23).

Deploy (Sectiunea 9)

Fara migrare, fara feature flag (UI inlocuit atomic in acelasi template). Worker neatins. Post-deploy: smoke = deschide un rand needs_mapping, adauga un cod, salveaza.

Traiectorie (Sectiunea 10)

Reversibilitate 4/5 (revert curat; starile cu exclus persistate raman valide — split exista independent). Datorie REDUSA (actiuni duplicate eliminate). Comentariul de header din _chips_prestatii.html se rescrie (diagrama noua a starii — parte din US-002).

Design/UX la nivel CEO (Sectiunea 11)

Ierarhie: chips (starea) inaintea selectului (actiunea) — corect. Stari acoperite: gol (hint), nomenclator lipsa, eroare, succes, exclus. Mobil: rand cu wrap; latimea selectului pe 390px = criteriu in faza Design. Accesibilitate: aria-labels existente se pastreaza; focus dupa swap. Riscul de mis-binding (tinta implicita + fara text permanent) — ridicat de vocea externa, decizie la gate. Detaliu complet in Faza 2 (plan-design-review).

Decision Audit Trail (/autoplan)

# Faza Decizie Clasificare Principiu Rationament Respins
1 CEO Abordare A (rescriere template + unificare actiuni) Mecanica P1,P5 B pastreaza confuzia; C incalca non-goal aprobat B, C
2 CEO A11y tastatura + focus dupa swap → criterii US-002 Mecanica P1 in blast radius, 2 fisiere
3 CEO Badge sursa sugestie in select → respins Mecanica P5 optgroup "Sugestii" spune deja asta badge-uri
4 CEO Bulk-apply mapare pe randuri identice → TODOS Mecanica P2,P3 in afara blast radius (panou preview = non-goal)
5 CEO Metrici deschideri-modal/sugestii → TODOS Mecanica P3 produs, nu 5.23
6 CEO GAP-uri erori (NED-fara-tinta, degradare sugestii) → criterii noi Mecanica P1 zero silent failures
7 CEO Auto-advance tinta + fara eticheta pe select TASTE (gate) voce externa: risc mis-binding pe declaratii ireversibile
8 CEO Paleta click 18 coduri vs select nativ TASTE (gate) premisa "tot nomenclatorul" e falsa (18 coduri fixe)
9 CEO Stare chips: 4 liste hidden vs un camp JSON TASTE (gate) a 3-a regresie in zona; JSON = sursa unica
10 CEO Test cu operator real in US-005 Mecanica P1 verifica direct riscul de mis-binding; cost ~0
11 Design Scara de dominanta vizuala (tabel §3.1) Mecanica P1,P5 3 tratamente concurente fara ierarhie = confuzia initiala reambalata
12 Design Stari loading/eroare/gol/complet specificate (tabel §3.1) Mecanica P1 eroarea de retea lasa selectul disabled permanent altfel
13 Design Tinta invarianta (exista cat sunt nemapate; click pe tinta = no-op) Mecanica P5 interzice starea "cod liber cu warnings inca pe rand" tacuta
14 Design Comutare tinta CLIENT-SIDE (fara round-trip) Mecanica P3 round-trip pe click de highlight = lent + race cu add-ul in zbor server-side
15 Design Ordine chips stabila + focus pe select dupa swap Mecanica P1 reflow-ul + focusul pierdut rup fluxul de tastatura pe 10 randuri
16 Design Optiuni COD — denumire; chip warning ellipsis 18ch Mecanica P5 18 coduri criptice fara denumire = memorare fortata
17 Design Nedeclarat vizibil (line-through, nu mut) + mesaj cu numele operatiei Mecanica P1 cea mai riscanta actiune legala nu poate fi cea mai discreta stil mut
18 Design Mesaje chips_ok/err raman pattern 5.22 (persista pana la actiune) Mecanica P4 timer de auto-dismiss = JS nou fara valoare timer 4s
19 Design Auto-advance tinta: pastrat cu emfaza vs eliminat TASTE (gate) CEO-voice: elimina (mis-binding); Design-voice: pastreaza (momentum) + emfaza
20 Design Touch targets × pe chips sub 44px (UI compact 11px existent) Mecanica P4 consecvent cu design-ul compact existent; nota in TODOS a11y 44px
21 Eng exclus supravietuieste resolve_prestatii (treapta noua, non-goal amendat) Mecanica P1 P1 confirmat: it.pop("exclus") mapping.py:298 face US-004 imposibil; alternativa (regula pe cont) schimba semantica aprobata regula per-cont; taierea US-004
22 Eng Itemii exclusi raman in payload_json + exclus in toti constructorii de chips Mecanica P1 P1 confirmat: routes.py:1744 ii elimina → × pe Nedeclarat imposibil dupa salvare
23 Eng chips_submission_id hidden + ecou in /form-chips ("salveaza ca regula" persistent) Mecanica P1,P3 butonul dispare azi la prima re-randare; fluxul nou trece TOT prin /form-chips
24 Eng chips_pick reset dupa orice raspuns; valoarea NEaplicata la Salveaza Mecanica P5 clasa de bug c6e29aa; sentinel NED pozitional pe calea corectie aplicare implicita
25 Eng Validare semantica tinta + chip_exclus strict + sectiune inerta in request Mecanica P1 POST fabricat / index stale / race mid-flight leaga codul de alt item
26 Eng chips_present=1 — golirea listei se persista (nu no-op) Mecanica P1 _chips_state_from_form intoarce None pe form fara chips → golire imposibila no-op actual
27 Eng Actualizarea testelor existente pe actiunile vechi in acelasi val Mecanica P6 altfel suita e rosie intre Val 1 si Val 2
28 Eng Emfaza chip nou pe index, nu pe cod Mecanica P5 doua operatii cu acelasi cod = dubla evidentiere falsa
29 Eng Fara memoizare sugestii per re-render Mecanica P3 rapidfuzz pe 18 randuri + k-NN gardat = bounded; memoizarea = complexitate fara castig memoizare
D2 Gate Auto-advance pastrat + placeholder-ul selectului numeste tinta USER leaga explicit codul de operatie la momentul alegerii, fara text sub control doar emfaza; fara auto-advance
D3 Gate Select nativ pastrat (mockup v1); paleta v2 vazuta si RESPINSA de utilizator USER directia utilizatorului; paleta ramane alternativa daca selectul se dovedeste lent (US-005 cu operator) paleta 18 coduri
D4 Gate Stare chips = UN hidden chips_state JSON versionat + fallback tranzitoriu liste vechi USER elimina clasa de regresii de aliniere pe index 4 liste paralele
D5 Gate Plan revizuit APROBAT de utilizator USER

Anexa Eng (Faza 3) — diagrama de acoperire teste

CODE PATHS                                              USER FLOWS
[+] post_form_chips (rescris, 2 actiuni)                [+] Corectie rand cu N operatii nemapate
  ├── add cu tinta valida        [RED planificat]         ├── sugestie→chip instant   [RED + E2E US-005]
  ├── add fara tinta (liber)     [RED planificat]         ├── comutare tinta client   [E2E US-005]
  ├── add NED cu/fara tinta      [RED planificat]         ├── × pe cod → warning      [RED planificat]
  ├── tinta semantica invalida   [RED planificat]         ├── NED → Nedeclarat → ×    [RED planificat]
  ├── remove index/liber         [RED planificat]         ├── eroare retea la add     [→E2E] [GAP inchis §3.1]
  └── dedup + cod necunoscut     [exista, se muta]        └── golire totala + salvare [RED chips_present]
[+] resolve_prestatii treapta item-exclus [RED planificat — REGRESIUNE potentiala: IRON RULE,
    test obligatoriu ca exclus NU e sters si ca restul precedentei ramane identic]
[+] _chips_state_from_form (4 liste + compat vechi + liste scurte) [RED planificat]
[+] save x2 (import/corectie): exclus round-trip, NED respins, chips_pick ignorat [RED planificat]
[+] template: hx-atribute, focus, sectiune inerta, mod plat, has_r_odo [RED planificat]
COVERAGE planificata: 100% din ramurile noi au RED numit in stories. Fara eval-uri LLM (nu se
ating prompturi). Regresiune-cheie: precedenta resolve_prestatii → test dedicat blocant.

ENG DUAL VOICES — CONSENS (subagent-only):

  Dimensiune                     Claude   Codex  Consens
  1. Arhitectura solida?         DA*      N/A    OK (directia 4→2 actiuni corecta; * dupa fix P1-uri)
  2. Acoperire teste suficienta? NU→DA    N/A    FLAGGED→INCHIS (6 teste noi adaugate in stories)
  3. Riscuri de performanta?     NU       N/A    OK (18 coduri, k-NN gardat, bounded)
  4. Securitate acoperita?       PARTIAL→DA N/A  INCHIS (validare semantica tinta, chip_exclus strict, NED respins la save)
  5. Drumuri de eroare tratate?  PARTIAL→DA N/A  INCHIS (§3.1 + criterii noi)
  6. Risc de deploy gestionabil? DA       N/A    OK (fara migrare; fereastra modal-peste-deploy acceptata)

Raport VERIFY

Verificator independent (subagent context curat, doar PRD + §5.6), 2026-07-08. VERDICT: PASS pe toate criteriile testabile; regresia de aur RAR live = NEPROBAT (contul QA fara creds RAR).

  • Suita: python3 -m pytest -q → verde. La VERIFY: 1672 passed, 1 skipped. Dupa fix-urile de code-review CLOSE: 1680 passed, 1 skipped (+45 teste noi fata de baseline 1635).
  • US-001..US-004 PASS cu dovezi de cod + teste (chips_state JSON unic, 2 actiuni add/remove, exclus supravietuieste resolve_prestatii cu test de regresie blocant pe precedenta, sentinel respins pe ambele cai).
  • US-005 E2E browser (import preview + Trimiteri corectie, Playwright): PASS pe (a) un singur rand de chips cu tinta evidentiata + placeholder-tinta + optgroup Sugestii + optiune "Nu se declara la RAR" la deschiderea initiala; (b) sugestie→chip cod instant fara "+"; (c) Nedeclarat → salvare → rand "Nedeclarat", redeschidere arata chipul, × readuce warning; (d) acelasi control la Trimiteri, "salveaza ca regula" persistent; (e) fara controale duble, zero erori JS de flux.
  • Gap prins in executie (dupa US-003, reparat inainte de VERIFY): tinta implicita + sugestiile nu se calculau la randarea initiala a modalului — helper partajat _chips_target_si_sugestii in toate caile de randare.

Code-review CLOSE (high, 8 unghiuri + verify) — 1 BLOCKER de siguranta + 2 findings, toate reparate

  • BLOCKER (CONFIRMED, reparat): itemii exclusi (exclus=1, cod null) ramaneau in payload_json pe calea de corectie, iar worker-ul ii trimitea la RAR ca {codPrestatie: null} → risc ORA-12899 / FINALIZATA partial ireversibil. Nimic nu facea split_prestatii_excluse la MOMENTUL trimiterii. Fix la chokepoint unic (worker, inainte de build_rar_payload) — acopera toate canalele (API/import/corectie) — + filtru defensiv in build_rar_payload (sare itemii exclusi / fara cod) + teste care dovedesc ca itemul exclus NU apare in payload-ul RAR.
  • Consistenta round-trip (reparat): commit_import/web_confirma_import pastreaza acum prestatiile complete in payload_json (cheia idempotentei = doar declarabile, identica cu preview); bonus: bug latent de idempotenta preview↔commit in _web_compute_preview reparat.
  • Conventii CLAUDE.md (reparat): referinte PRD/US-xxx/decizii scoase din comentariile noi.
  • Robustete tinta (reparat): un item fara cod_op_service nu mai poate deveni tinta invizibila; sugestiile intorc gol pe denumire goala.

GSTACK REVIEW REPORT

Review Trigger Why Runs Status Findings
CEO Review /plan-ceo-review Scope & strategy 1 CLEAR (PLAN via /autoplan) 5 proposals, 2 accepted, 3 deferred; 8 constatari voce externa
Codex Review /codex review Independent 2nd opinion 0 UNAVAILABLE (usage limit pana 2026-07-18) voci = subagent-only
Eng Review /plan-eng-review Architecture & tests (required) 1 CLEAR (PLAN via /autoplan) 8 issues (4×P1 confirmate in cod), 0 critical gaps ramase
Design Review /plan-design-review UI/UX gaps 1 CLEAR (FULL via /autoplan) score: 6/10 → 9/10, 10 decizii in §3.1
DX Review /plan-devex-review Developer experience gaps 0 SKIPPED fara scope developer-facing
  • CROSS-MODEL: indisponibil (Codex la limita de utilizare); toate vocile externe = subagenti Claude independenti (context curat, fara review-ul anterior).
  • VERDICT: CEO + ENG + DESIGN CLEARED — gata de implementare. Decizii gate aplicate: D2 placeholder-tinta, D3 select (paleta respinsa), D4 stare JSON, D5 aprobat.

NO UNRESOLVED DECISIONS