docs(5.15): criterii design din mockup-uri + revizie eng-review
Criterii noi (din docs/mockups/prd-5.15-mockups.html), facute AC testabile: - antet: referinta vizuala obligatorie la mockup-uri - US-002: doar tokeni CSS, fara hex hardcodat (AA pe teme luminoase) - US-003: layout exact strip D6 (glife/copy/last-auth) + stari goale + ierarhie all-time - US-004: 'Eroare VIN' ilustrativ; pills din labels.py - US-007: referinta picker op<->cod (2 stari) + reveal odometru Include si revizia /plan-eng-review care era in working tree necomisa (E1-E8, US-011 authz, US-012 analytics, Val 0, raport eng CLEARED). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,10 @@
|
||||
|
||||
> Proces complet: `docs/ROADMAP.md` §5. Contract RAR (sursa de adevar): `docs/api-rar-contract.md`.
|
||||
> Sistemul de design al landing-ului: `app/web/templates/landing.html` (commit 41aa385), `DESIGN.md`.
|
||||
> Mockup-uri piese fara design (REFERINTA VIZUALA OBLIGATORIE): `docs/mockups/prd-5.15-mockups.html`
|
||||
> — strip sanatate D6 (stari rosu/verde), picker prestatii E4 (op<->cod), reveal odometru initial.
|
||||
> Acopera exact piesele pe care mockup-urile landing nu le aratau si corecteaza contradictiile
|
||||
> mockup<->PRD (VIN unic, contor all-time, culori prin tokeni).
|
||||
> Starea trece: `draft -> aprobat -> in-executie -> verify-pass -> inchis`.
|
||||
|
||||
## 1. Obiectiv
|
||||
@@ -49,6 +53,43 @@ Decizii din /plan-ceo-review (2026-06-28, mod SELECTIVE EXPANSION):
|
||||
din chip (US-009), (b) bulk-fix din lista (US-010), (c) require dinamic odometruInitial la chip
|
||||
R-ODO/I-ODO (US-007), (d) editare keyboard-first in form slim (US-007).
|
||||
|
||||
Decizii din /plan-eng-review (2026-06-28, model claude/opus; outside-voice = Claude subagent,
|
||||
Codex a atins usage-limit). Fiecare confirmata cu userul:
|
||||
- **E1** (ARCH, /repune nu mai sterge operatia): `/repune` face azi `p0.pop("cod_op_service")`
|
||||
la `routes.py:1326` — sterge operatia cand se seteaza un cod direct, rupand D7 si US-009.
|
||||
US-006 ELIMINA acel `pop` si pastreaza `cod_op_service`; test de regresie obligatoriu
|
||||
(op_service supravietuieste unui /repune cu cod). (Rafineaza US-006.)
|
||||
- **E2** (DRY teme, fisier fierbinte): config-ul de teme e duplicat in ~7 locuri in base.html
|
||||
(anti-FOUC `VALID` la :22 + cinci literali paraleli `CYCLE`/`VALID`/`ICONS`/`LABELS`/`NEXT`
|
||||
la :758-765). US-001 CONSOLIDEAZA intr-o singura structura sursa-de-adevar (`THEMES`
|
||||
ordonata) din care se DERIVA ciclul/etichetele/iconitele + setul anti-FOUC. Adaugarea unei
|
||||
teme = o intrare. (Rafineaza US-001.)
|
||||
- **E3** (obs concat idempotent): la import, copierea denumirii operatiei in `obs` se face
|
||||
DERIVE-ON-EMPTY (doar cand `obs` e gol) ca sa fie idempotenta la re-import/re-editare. Test
|
||||
dedicat anti-dublu-concat ("Schimb ulei; Schimb ulei"). (Rafineaza US-005.)
|
||||
- **E4** (binding operatie<->cod in chips — HIGH): chip-urile NU sunt o lista plata de coduri.
|
||||
Cand exista operatii (`cod_op_service`), UI-ul randeaza UN picker PE operatie (eticheta op +
|
||||
chip-ul ei de cod), pastrand perechea per-item pe care modelul o are deja; lista plata de
|
||||
coduri libere DOAR pentru cazul fara operatie (corectie pura). Astfel US-009 citeste perechea
|
||||
direct, iar deduparea e PER-ITEM (nu "dupa cod" — doua operatii distincte pot mapa legitim la
|
||||
acelasi cod RAR). (Rafineaza US-006 AC2 + US-007 AC3 + US-009.)
|
||||
- **E5** (serializare Val 3 pe routes.py): US-005 si US-006 rescriu ACEEASI functie
|
||||
`post_corecteaza` (`routes.py:1120-1262`). Regula "un singur autor pe fisier fierbinte" se
|
||||
EXTINDE la routes.py in Val 3: US-005 INAINTE de US-006 (secvential, nu paralel). (Rafineaza §6.)
|
||||
- **E6** (US-007 HTMX server-driven PRIMARY): inversam abordarea — chips add/remove via `hx-post`
|
||||
care re-randeaza partial-ul chips+form; reveal-ul conditional `odometruInitial` rezulta GRATIS
|
||||
din re-randarea server; navigare tastatura = `<select>`/`<datalist>` nativ. JS custom DOAR ca
|
||||
progressive enhancement (snappiness), nu calea principala. Elimina path-ul dublu JS/no-JS.
|
||||
(Rafineaza US-007.)
|
||||
- **E7** (contoare in timp local RO): `azi`/`luna asta` se bucketeaza in timp local RO (UTC+2/+3),
|
||||
nu UTC — `updated_at` e `datetime('now')` UTC, deci `date(updated_at)` pur ar numara gresit
|
||||
trimiterile dintre miezul noptii local si ~03:00. Folosim offset RO (ex. `date(updated_at,'+3 hours')`
|
||||
cu aceeasi baza `now`) + test la granita de miez de noapte local. (Rafineaza US-003.)
|
||||
- **E8** (interleave fix authz GET-listari — securitate): CLAUDE.md noteaza scurgere cross-cont
|
||||
deschisa ("GET-urile de listare sunt globale + neprotejate"). Userul a ales sa INTERLEAVE
|
||||
remedierea in 5.15 -> story noua **US-011** (account-scope pe GET-urile de listare + teste),
|
||||
nu queue dupa polish-ul de teme.
|
||||
|
||||
Fapte verificate care fundamenteaza scope-ul (nu presupuneri):
|
||||
- `vin` la RAR e **un singur camp** (17 car., MAJUSCULE, fara O/I/Q) — cerinta "fara 2 campuri
|
||||
VIN" e deja respectata azi (`_form_editare.html` are un singur `vin`); ramane sa NU regresam.
|
||||
@@ -90,6 +131,11 @@ acelasi produs, coerent vizual.
|
||||
noi: `--text->--ink`, `--sub->--muted`, `--okt->--ok`, `--errt->--err`, `--infot->--accent`.
|
||||
- [ ] Selectorul ciclic parcurge TOATE: light -> dark -> petrol -> grafit -> cobalt -> cupru ->
|
||||
hartie -> Auto, afiseaza eticheta temei curente, persistenta `localStorage` (D2).
|
||||
- [ ] **DRY (E2)**: config-ul de teme traieste intr-o SINGURA structura sursa-de-adevar
|
||||
(`THEMES` ordonata, cu `{id,label,icon}`) din care se DERIVA `CYCLE`/`NEXT`/`ICONS`/`LABELS`
|
||||
(azi 5 literali paraleli la base.html:758-765) SI setul anti-FOUC `VALID` (azi separat la
|
||||
base.html:22). Adaugarea unei teme noi = o singura intrare; test ca derivatele acopera
|
||||
toate temele (prinde o intrare ICONS/LABELS lipsa, nu doar token CSS lipsa).
|
||||
- [ ] "Auto" pastrat: urmeaza `prefers-color-scheme`, rezolva la dark/grafit sau light/hartie
|
||||
(decizie minora: Auto -> dark + hartie pentru light, sau dark/grafit — aliniaza cu I2).
|
||||
- [ ] Script anti-FOUC in `<head>` seteaza `data-theme` sincron pre-paint pentru toate starile;
|
||||
@@ -118,6 +164,12 @@ chips **pentru ca** dashboard-ul si formularul sa le consume DRY, identic cu moc
|
||||
printr-un flag (`slim=True`), fara a rupe randarea actuala (default neschimbat).
|
||||
- [ ] `.chips` + `.chip` (cu buton `×` de stergere) pentru prestatii multi-select; accesibil
|
||||
(buton real cu `aria-label`), stilat ca in mockup (accent 18%, font 10-11px).
|
||||
- [ ] **Doar tokeni, fara hex hardcodat (criteriu din mockup)**: toate culorile componentelor noi
|
||||
(contor, lista slim, chips, strip, picker) folosesc EXCLUSIV variabile CSS
|
||||
(`var(--errt)`/`var(--okt)`/`var(--accent)`/`var(--card2)`/`var(--line2)` etc.), NU hex literal
|
||||
si NU inline-styles copiate ca-atare din `landing.html`. Cifra "De corectat" rosie = token
|
||||
(`var(--errt)`), nu `#E05D5D` hardcodat, ca sa ramana AA pe temele luminoase (hartie/light).
|
||||
Referinta: `docs/mockups/prd-5.15-mockups.html`.
|
||||
- [ ] Zero regresie vizuala pe componentele existente (`.card/.pill/.act/.tabel-trimiteri`).
|
||||
- **Verificare E2E**: pagina de proba/sandbox sau direct in US-003/004/007; vizual pe un esantion de teme + 390/1280.
|
||||
|
||||
@@ -136,8 +188,17 @@ chips **pentru ca** dashboard-ul si formularul sa le consume DRY, identic cu moc
|
||||
oprit SAU RAR inaccesibil ("Blocat: worker oprit" / "Blocat: RAR inaccesibil"), cu ultima
|
||||
autentificare RAR. Glife accesibile ✓/✗ (nu doar culoare). Invariant zero-silent-failures:
|
||||
semnalul "declaratiile NU pleaca" e imposibil de ratat, NU ingropat sub volum.
|
||||
**Layout exact (din mockup)**: strip full-width sus; glifa (✗ rosu / ✓ verde) + text bold la
|
||||
stanga, "Ultima autentificare RAR: ..." mono muted la dreapta. Copy: rosu "Blocat: worker oprit
|
||||
— declaratiile NU pleaca" (sau "... RAR inaccesibil"), verde "Declaratiile curg normal".
|
||||
Referinta: `docs/mockups/prd-5.15-mockups.html`.
|
||||
- [ ] Sub strip: card "Trimiteri RAR AUTOPASS" cu 3 contoare slim: **In coada** (queued, accent),
|
||||
**Trimise** (sent, verde), **De corectat** (blocate = needs_data + needs_mapping + error, rosu).
|
||||
- [ ] **Stari goale + ierarhie contor (criteriu din mockup)**: cifra principala a contorului "Trimise"
|
||||
e **all-time** (cifra mare bold), iar "luna asta"/"azi" sunt o sub-linie mono secundara
|
||||
(`luna {n} · azi {n}`) — NU "luna asta" ca cifra principala (corecteaza framing-ul din mockup-ul
|
||||
landing). Contorul "De corectat" la 0 se afiseaza **muted, nu rosu** (rosu doar cand exista
|
||||
blocate — pastreaza pattern-ul `_status.html:47`). Referinta: `docs/mockups/prd-5.15-mockups.html`.
|
||||
- [ ] Cardul **Trimise** afiseaza trei valori temporale (D4): all-time (cifra principala) + "luna asta"
|
||||
+ "azi" (sub-linie secundara). `_status_counts` extins cu `sent_today`/`sent_month`.
|
||||
**Sursa de timp**: NU exista coloana `sent_at`; folosim `status='sent' AND date(updated_at)=...`.
|
||||
@@ -145,6 +206,13 @@ chips **pentru ca** dashboard-ul si formularul sa le consume DRY, identic cu moc
|
||||
la +90z (`purge_after` se seteaza in ACEEASI scriere care marcheaza `sent`), deci `updated_at`
|
||||
== momentul trimiterii pentru randurile `sent` -> fara migrare de coloana (respecta Non-Goal).
|
||||
Daca pe viitor apar scrieri post-`sent`, reevalueaza o coloana `sent_at` dedicata.
|
||||
**Timezone (E7)**: `updated_at` e `datetime('now')` = UTC; bucketarea `azi`/`luna asta`
|
||||
se face in TIMP LOCAL RO (ex. `date(updated_at,'+3 hours')`, aceeasi baza `now`), altfel
|
||||
trimiterile dintre miezul noptii local si ~03:00 cad pe ziua precedenta si "luna asta" e
|
||||
gresita in primele ore ale zilei de 1. Test la granita de miez de noapte local.
|
||||
**Caveat reconcile (E6 outside-voice)**: pe reconciliere (raspuns pierdut) worker-ul
|
||||
marcheaza `sent` cu `updated_at` = momentul reconcilierii, nu al inserarii RAR — pentru
|
||||
randurile reconciliate (rare) `updated_at` poate diferi de momentul real al trimiterii.
|
||||
- [ ] Navigarea existenta (Trimiteri/Mapari + badge needs_mapping) se pastreaza. Click pe contorul
|
||||
**De corectat** deep-link-eaza in lista filtrata pe blocate (`?status=` existent din 5.x),
|
||||
nu intr-o pagina noua.
|
||||
@@ -169,7 +237,8 @@ si mai usor de scanat, pastrand filtrele si actiunile.
|
||||
blocate (checkbox doar pe `gestionabil`) raman FUNCTIONALE.
|
||||
- [ ] Click pe rand deschide `/_fragments/trimitere/{id}` in modal (neschimbat).
|
||||
- [ ] Slim layout consistent desktop si <=1024px (cardurile responsive existente nu regreseaza).
|
||||
- [ ] Pill-urile de stare folosesc maparea din `labels.py` (zero etichete noi).
|
||||
- [ ] Pill-urile de stare folosesc maparea din `labels.py` (zero etichete noi). Eticheta "Eroare VIN"
|
||||
din mockup-ul landing e DOAR ilustrativa — se foloseste `stare_scurt` existent (ex. "De corectat").
|
||||
- **Verificare E2E**: browser — filtrare + paginare + click detaliu + bulk pe blocate, pe 4 teme,
|
||||
pe 390/820/1280.
|
||||
|
||||
@@ -194,6 +263,11 @@ sa corectez/completez ce s-a facut, separat de codurile RAR.
|
||||
daca fisierul NU are coloana Observatii, denumirea operatiei se **COPIAZA** (nu se muta) si in
|
||||
`obs`; daca are coloana Observatii, se pastreaza textul ei. Format de concatenare definit
|
||||
(denumiri separate prin "; "). Fluxul needs_mapping ramane neatins.
|
||||
- [ ] **Idempotent (E3)**: copierea operatiei in `obs` e DERIVE-ON-EMPTY (doar cand `obs` e gol)
|
||||
ca re-importul/re-editarea sa NU dubleze textul ("Schimb ulei; Schimb ulei"). Test dedicat
|
||||
anti-dublu-concat.
|
||||
- [ ] **Cuplaj preview-import**: `obs` se adauga in `EDIT_FIELDS` (`import_router.py:261`); `_merge_override`
|
||||
il propaga (obs e free-text, cade pe ramura ne-canonicalizata — fara strip "0", doar trim).
|
||||
- **Verificare E2E**: `POST /trimitere/{id}/corecteaza` cu `obs` -> persistat -> vizibil in detaliu;
|
||||
optional proba live RAR ca `obs` apare in FINALIZATA.
|
||||
|
||||
@@ -216,6 +290,10 @@ comanda poate avea mai multe prestatii, asa cum accepta RAR.
|
||||
(`routes.py:1146-1164`, `1288-1324`) — multi-select le rescrie ca: pastreaza itemii existenti
|
||||
cu `cod_op_service`/`denumire` (invariant D7) si seteaza/adauga `cod_prestatie` pe ei.
|
||||
`idPrezentare:null` se adauga in `payload.py` la construirea payload-ului, NU in itemul intern.
|
||||
**E1 (CRITIC)**: `/repune` face azi `p0.pop("cod_op_service", None)` la `routes.py:1326` —
|
||||
ELIMINA acel `pop`: cand se seteaza un cod direct, `cod_op_service`/`denumire` RAMAN pe item
|
||||
(altfel rupe D7 si US-009). **Test de regresie obligatoriu** (IRON RULE): op_service
|
||||
supravietuieste unui /repune cu cod.
|
||||
- [ ] **Pereche operatie<->cod definita**: cand exista operatii (`cod_op_service`), fiecare cod-chip
|
||||
se ataseaza unei operatii (1 operatie -> 1 cod, ca azi, dar acum N operatii -> N coduri);
|
||||
cand NU exista operatie (cod direct, ex. corectie pura), chip-urile sunt coduri libere intr-o
|
||||
@@ -223,8 +301,9 @@ comanda poate avea mai multe prestatii, asa cum accepta RAR.
|
||||
- [ ] Fiecare cod e validat fata de nomenclator (`valid_codes`); cod necunoscut -> respins cu
|
||||
mesaj (NU se trimite raw — invariant ORA-12899 din CLAUDE.md/contract).
|
||||
- [ ] Lista goala de coduri -> ramane `needs_mapping` (nu se trimite fara cod).
|
||||
- [ ] **Coduri duplicate** (acelasi cod adaugat de 2x) -> deduplicate inainte de persistare
|
||||
(cheia sorteaza deja dupa identitate, dar lista persistata nu trebuie sa contina duplicate).
|
||||
- [ ] **Coduri duplicate** -> dedupare **PER-ITEM, nu "dupa cod"** (E4): doua operatii distincte
|
||||
pot mapa legitim la acelasi cod RAR; deduparea naiva dupa cod ar sterge o operatie reala si
|
||||
ar distruge contextul op->cod cerut de US-009. Dedup = acelasi (op, cod) de 2x, nu acelasi cod.
|
||||
- [ ] Recalcul idempotenta dupa editare (mecanism existent), cu prinderea coliziunii ca azi.
|
||||
- [ ] Se pastreaza regula `odometruInitial` obligatoriu cand lista contine `R-ODO`/`I-ODO`
|
||||
(contract §payload) — validare existenta, doar verificata pe lista.
|
||||
@@ -244,8 +323,10 @@ e compact si imi arata clar codurile RAR si observatiile, ca in mockup.
|
||||
- [ ] Formularul foloseste varianta slim de camp (US-002): VIN, Data prestatiei, Nr. inmatriculare,
|
||||
Observatii (textarea), prestatii (chips), Odometru — un SINGUR camp VIN (fara "Confirma VIN").
|
||||
- [ ] Observatii = textarea liber, legat de `obs` (US-005).
|
||||
- [ ] Prestatii = chips multi-select: fiecare cod ca chip cu `×`; un picker (dropdown din
|
||||
nomenclator) adauga un cod nou; lista se trimite ca `cod_prestatie` multiplu (US-006).
|
||||
- [ ] Prestatii = chips multi-select. **Binding op<->cod (E4)**: cand exista operatii
|
||||
(`cod_op_service`), UN picker PE operatie (eticheta op + chip-ul ei de cod), pastrand
|
||||
perechea per-item; lista plata de coduri libere DOAR pentru cazul fara operatie (corectie
|
||||
pura). Fiecare cod ca chip cu `×`; lista se trimite ca `cod_prestatie` multiplu (US-006).
|
||||
- [ ] Acelasi `_form_editare.html` slujeste ambele modale (detaliu `/corecteaza` si preview
|
||||
`/editeaza`), fara duplicare; degradare fara JS rezonabila (chips ca lista, picker = select).
|
||||
- [ ] **Require dinamic odometruInitial** (D10c): cand lista de chips contine `R-ODO` sau `I-ODO`,
|
||||
@@ -255,10 +336,18 @@ e compact si imi arata clar codurile RAR si observatiile, ca in mockup.
|
||||
navigheaza optiunile; Esc inchide modalul; focus-ul revine logic dupa adaugare/stergere.
|
||||
- [ ] Stilizare fidela mockup-ului pe toate temele; tinte 44px pe mobil; a11y (label-uri, aria,
|
||||
anunt de chip adaugat/sters pentru screen-reader).
|
||||
- [ ] **Suprafata JS reala** (nota de efort): chips add/remove client-side + picker navigabil cu
|
||||
tastatura + management focus + reveal conditional odometruInitial = JS ne-trivial intr-un app
|
||||
HTMX/minimal-JS. Fallback fara JS: picker = `<select>` simplu (server valideaza R-ODO->odo,
|
||||
deci no-JS da `needs_data` corect, nu date gresite) — multi-select se degradeaza la un cod/submit.
|
||||
- [ ] **HTMX server-driven PRIMARY (E6)**: chips add/remove via `hx-post` care re-randeaza
|
||||
partial-ul chips+form; reveal-ul conditional `odometruInitial` rezulta GRATIS din re-randarea
|
||||
server (server computeaza din lista de chips, fara ramura JS); navigare tastatura =
|
||||
`<select>`/`<datalist>` nativ. JS custom DOAR ca progressive enhancement (snappiness), nu
|
||||
calea principala. Elimina path-ul dublu JS/no-JS pe care formularea anterioara il cerea.
|
||||
- [ ] **Referinta vizuala (criteriu din mockup)**: `docs/mockups/prd-5.15-mockups.html` defineste
|
||||
aspectul-tinta — VIN unic (FARA al doilea camp "Confirma VIN" din mockup-ul landing); Observatii
|
||||
ca textarea slim; picker PE operatie cu DOUA stari vizuale: (a) operatie mapata = chip cod cu `×`
|
||||
+ "+ alt cod" + link "salveaza regula op->cod" (US-009); (b) operatie ne-mapata = picker galben
|
||||
"alege cod RAR" cu eticheta "lipsa cod". OdometruInitial: ascuns implicit (doar hint discret
|
||||
"se cere doar pentru R-ODO/I-ODO") si DEZVALUIT cu bordura-stanga galbena + label "necesar pentru
|
||||
R-ODO" cand lista de chips contine R-ODO/I-ODO.
|
||||
- **Verificare E2E**: browser — editare trimitere needs_data: schimb VIN + scriu Observatii + adaug
|
||||
2 coduri RAR (chips, cu tastatura) + adaug R-ODO (apare odometruInitial) + sterg un chip -> salvare
|
||||
-> persistat; identic in preview import.
|
||||
@@ -316,6 +405,43 @@ de corectat/zi nu vreau sa intru in fiecare individual.
|
||||
- **Verificare E2E**: selectez 3 randuri needs_mapping + aplic un cod -> toate 3 -> `queued`.
|
||||
- **Verificare E2E**: rulare completa documentata in Raportul VERIFY.
|
||||
|
||||
### US-011: Securitate — account-scope pe GET-urile de listare (interleave, E8)
|
||||
**Ca** operator **vreau** ca listarile sa-mi arate DOAR trimiterile contului meu **pentru ca**
|
||||
azi GET-urile de listare sunt globale + neprotejate (scurgere VIN/PII cross-cont, notata in CLAUDE.md).
|
||||
|
||||
- **Depinde de**: — (backend pur, independent de UI; ruleaza in paralel cu valurile de design)
|
||||
- **Fisiere**: `app/web/routes.py` (GET-urile de listare trimiteri), `app/api/v1/router.py`
|
||||
(GET-urile API de listare daca sunt globale), `app/auth.py` (refolosire scope existent),
|
||||
`tests/test_web_scope.py`, `tests/test_api_scope.py` (~5 fisiere)
|
||||
- **Test intai (RED)**: `test_get_listare_scoped_cont` — un cont NU vede randuri ale altui cont;
|
||||
`test_get_listare_neautentificat_401`; `test_get_detaliu_scoped` (404-before-leak pe id strain).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] GET-urile de listare (trimiteri + orice listare globala) devin account-scoped, refolosind
|
||||
mecanismul de scope existent (ca POST-urile + bulk-delete: 404-before-409 pe id strain).
|
||||
- [ ] Un cont nu poate enumera/citi VIN/PII al altui cont prin listare sau detaliu.
|
||||
- [ ] Enforcement aliniat cu `AUTOPASS_REQUIRE_API_KEY` (dev vs prod), fara a rupe contul id=1
|
||||
implicit in dev.
|
||||
- [ ] Actualizeaza nota din CLAUDE.md ("GET-urile de listare ... de remediat") cand e inchis.
|
||||
- **Verificare E2E**: doua conturi cu trimiteri; contul A nu vede niciun rand al contului B in
|
||||
listare, filtre, paginare sau detaliu.
|
||||
|
||||
### US-012: Analytics device-mix (validare premisa mobil, in-PR)
|
||||
**Ca** owner **vreau** sa stiu raportul desktop/mobil al operatorilor **pentru ca** sa decid daca
|
||||
rafinarile mobil (390px) viitoare merita efortul (premisa nevalidata din TODOS 5.13/CEO-F1).
|
||||
|
||||
- **Depinde de**: — (instrumentare backend, independenta de UI)
|
||||
- **Fisiere**: `app/web/routes.py` (sau middleware existent), `app/schema.sql` SAU `app_events`
|
||||
(reuse tabela de evenimente existenta — fara coloana noua daca `app_events` poarta semnalul),
|
||||
`tests/test_device_mix.py` (~3 fisiere)
|
||||
- **Test intai (RED)**: `test_device_mix_inregistrat`, `test_device_mix_fara_pii`.
|
||||
- **Acceptance criteria**:
|
||||
- [ ] La acces dashboard, clasifica grosier viewport/UA in desktop/mobil si inregistreaza in
|
||||
`app_events` (semnal agregat, FARA PII suplimentar). Reuse tabela existenta — fara migrare
|
||||
daca `app_events` poarta semnalul.
|
||||
- [ ] Un mod simplu de citire a raportului (query/admin), suficient pentru a decide investitia mobil.
|
||||
- [ ] Zero PII nou; aliniat retentiei `app_events` existente.
|
||||
- **Verificare E2E**: acces dashboard de pe doua viewport-uri -> doua evenimente clasificate corect.
|
||||
|
||||
## 4. Riscuri
|
||||
|
||||
- **base.html fisier fierbinte**: US-001/US-002 il ating amandoua + US-003/004/007 il citesc.
|
||||
@@ -355,15 +481,22 @@ de corectat/zi nu vreau sa intru in fiecare individual.
|
||||
## 6. Valuri de executie (graful de dependente)
|
||||
|
||||
```
|
||||
Val 0: [US-011] authz GET-listari (backend pur; ruleaza in paralel cu orice val) ||
|
||||
Val 1: [US-001] base.html teme + tokeni (autor unic pe base.html)
|
||||
Val 2: [US-002] base.html componente (dupa US-001, autor unic pe base.html)
|
||||
Val 3: [US-003] [US-004] dashboard + strip sanatate + lista (consuma US-002; disjuncte) ||
|
||||
[US-005] [US-006] backend obs + prestatii (fisiere backend; in paralel cu UI)
|
||||
[US-005] -> [US-006] backend obs APOI prestatii — SECVENTIAL (E5): ambele rescriu
|
||||
ACEEASI functie post_corecteaza (routes.py:1120-1262), autor unic
|
||||
Val 4: [US-007] formular slim cu chips (dupa US-002+US-005+US-006)
|
||||
Val 5: [US-009] [US-010] salvare mapare din chip || bulk-fix (dupa US-006/007 resp. US-004)
|
||||
— disjuncte la nivel de template (_form_editare vs _submissions)
|
||||
Val 6: [US-008] regresie + E2E final (dupa toate)
|
||||
```
|
||||
|
||||
> **Regula autor-unic extinsa (E5)**: pe langa base.html, `routes.py` are autor unic in Val 3:
|
||||
> US-005 INAINTE de US-006 (ambele in `post_corecteaza`). US-009/US-010 in Val 5 sunt disjuncte
|
||||
> la nivel de template; adauga rute noi separate in routes.py (regiuni diferite, mergeabile).
|
||||
|
||||
> Secventiere fata de alte PRD-uri (D9): **5.15 INAINTE de 5.14** (mapare LLM) — 5.15 fixeaza forma
|
||||
> listei `prestatii` si UX-ul de confirmare; 5.14 umple codurile peste aceeasi forma.
|
||||
|
||||
@@ -416,7 +549,35 @@ extinderi acceptate (D10) -> US-007 imbogatit + US-009 (salvare mapare din chip)
|
||||
obiectivul pur de propagare design. **Acceptat constient** (alegerea userului); ramane optiunea de
|
||||
a le scoate intr-un PRD separat daca propagarea design e ce e urgent.
|
||||
|
||||
**VERDICT:** APROBAT cu modificari incorporate (+remedieri spec-review). Scope: 10 stories / 6 valuri.
|
||||
Fara CODEX/cross-model in aceasta rulare (review uni-model). Gata de executie dupa confirmarea userului.
|
||||
### /plan-eng-review — 2026-06-28 (model claude/opus; outside-voice = Claude subagent, Codex usage-limit)
|
||||
|
||||
| Review | Trigger | Why | Runs | Status | Findings |
|
||||
|--------|---------|-----|------|--------|----------|
|
||||
| CEO Review | `/plan-ceo-review` | Scope & strategy | 1 | issues_open->remediat | 10 stories, 4 ext acceptate, spec-review remediat |
|
||||
| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 1 | issues_open->remediat | 7 issues (2 HIGH), 1 regresie IRON-RULE, +2 stories noi |
|
||||
| Outside Voice | Claude subagent | Independent 2nd opinion | 1 | issues_found | 10 findings; 2 HIGH absorbite, restul foldate |
|
||||
|
||||
**Step 0 scope:** acceptat ca-atare (10 stories). Gate de complexitate = breadth, nu depth (zero clase/servicii noi). User a confirmat pastrarea US-009/US-010.
|
||||
|
||||
**Constatari eng-review (toate confirmate cu userul si foldate in AC):**
|
||||
- **E1 (ARCH, HIGH, conf 9/10)** `routes.py:1326` `/repune` face `p0.pop("cod_op_service")` — sterge operatia, rupe D7+US-009. US-006: elimina pop + test regresie (IRON RULE).
|
||||
- **E2 (Code-quality, conf 9/10)** config teme duplicat ~7 locuri pe base.html (anti-FOUC + 5 literali). US-001: o singura structura `THEMES`, restul derivat.
|
||||
- **E3 (Test, conf 7/10)** obs concat la import poate dubla textul la re-import. US-005: derive-on-empty + test anti-dublu.
|
||||
- **E7 (Perf/Correctness, conf 8/10 — outside-voice)** `date(updated_at)` UTC numara gresit `azi`/`luna` peste granita local RO. US-003: bucketare timp local + test granita.
|
||||
|
||||
**Outside-voice (Claude subagent) — material absorbit:**
|
||||
- **OV/E4 (HIGH, conf 9/10)** chip-uri lista plata fara binding op<->cod -> rupe US-009; dedup-dupa-cod sterge operatie legala. US-006/007: picker PE operatie cand exista op; flat doar fara op; dedup per-item.
|
||||
- **OV/E5 (HIGH, conf 9/10)** Val 3 conflict same-function: US-005+US-006 rescriu `post_corecteaza`. §6: serializare US-005 -> US-006 pe routes.py.
|
||||
- **OV/E6 (MED, conf 8/10)** US-007 supraestimeaza JS custom intr-un app HTMX. US-007: hx-post server-driven primary; reveal odo gratis; select/datalist nativ.
|
||||
- **OV/E8 (MED securitate, conf 7/10)** GET-uri de listare globale neprotejate (scurgere VIN/PII cross-cont, CLAUDE.md). User a ales INTERLEAVE -> **US-011** (account-scope + teste).
|
||||
- **OV minore foldate:** reconcile drift pe `updated_at` (caveat US-003); cost poll non-sargabil (notat, non-blocant); cuplaj `EDIT_FIELDS` pentru obs preview (US-005 AC).
|
||||
|
||||
**TODO (decizie user):** premisa mobil nevalidata -> user a ales BUILD-IN-PR -> **US-012** (analytics device-mix, fara PII, reuse `app_events`).
|
||||
|
||||
**Scope actualizat:** 10 -> **12 stories** (+US-011 authz, +US-012 analytics). Fara migrare de schema. Outside-voice a confirmat "no migration" TRUE.
|
||||
|
||||
**Failure modes — gap critic:** niciun gap critic ramas silent. Cel mai aproape: E1 (regresie tacuta op_service pe /repune) — acum acoperit de test obligatoriu. E7 (off-by-a-day tacut) — acum cu test de granita.
|
||||
|
||||
**VERDICT:** CEO + ENG CLEARED — gata de executie. 12 stories. Outside-voice absorbit (2 HIGH foldate). Fara migrare de schema.
|
||||
|
||||
NO UNRESOLVED DECISIONS
|
||||
|
||||
Reference in New Issue
Block a user