PRD prin /prd + /autoplan (CEO/Design/Eng/DX, voce unica - Codex la plafon). Per-cont accounts.auto_send_enabled (default OFF time-boxed) + per-rand submissions.held; snapshot la TOATE ~8 situri queued via held_for_account() (Eng a prins bug reactivare router:237 ce ocolea Auto OFF); claim_one AND held=0. Crescut 6->10 stories: US-007 banner/metrics coada imbatranita, US-008 retentie held (GDPR/L.142), US-009 fixturi teste + audit, US-010 onestitate API (invariant 5.7). 26 taskuri. Eticheta redenumita; testare sigura (rar_env/valideaza) -> TODOS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 KiB
PRD 5.19 — Bifa "Auto": transmitere automata sau manuala din coada
Status: DRAFT (asteapta aprobare). Sursa de contract:
docs/api-rar-contract.md. Limba: romana, fara emoji. Stil: aditiv, nedistructiv pe backend-ul de trimitere.
1. Introducere
Astazi transmiterea catre RAR e controlata de un singur comutator global
(AUTOPASS_WORKER_SEND_ENABLED, env): cand e pornit, worker-ul ia ORICE rand queued
al unui cont active si il trimite imediat. Nu exista un control per-cont care sa
permita unui service sa-si tina prezentarile in coada pentru verificare umana inainte
de a pleca la RAR.
Cazul concret care motiveaza feature-ul: utilizatorul testeaza canalul API din ROAAUTO
(Visual FoxPro) direct in productie (autopass.romfast.ro), pe contul lui de test.
Vrea ca prezentarile sa apara in coada si sa astepte, nu sa plece automat la RAR,
pana cand le verifica si apasa explicit "Trimite". Reper vizual: bifa "Auto" din
dashboard-ul gomag-vending (image.png).
2. Obiective
Obiectiv principal
- Un comutator "Auto" per-cont, persistat pe contul service-ului: cand e bifat, prezentarile pleaca automat la RAR (comportament actual); cand e debifat, randurile asteapta vizibil in coada pana cand un operator le trimite manual.
Obiective secundare
- Trimitere manuala per rand ("Trimite") si in bloc ("Trimite toate (N)", analogul "Start Sync" din gomag).
- La activarea Auto (OFF -> ON), randurile deja tinute sunt eliberate automat spre transmitere.
- Vizibilitate: randurile tinute apar in coada cu o stare umana clara ("In asteptare (manual)"), separate de cele in curs de trimitere.
Metrici de succes
- Cu Auto OFF, un
POST /v1/prezentarivalid creeaza un rand care NU e trimis de worker (ramane vizibil in coada) pana la actiune umana. - Cu Auto ON, acelasi rand pleaca la RAR fara interventie (zero regresie fata de azi).
- Bifa supravietuieste restartului (persistata in
accounts), per-cont (un cont OFF nu afecteaza alt cont).
3. Design (decizii luate cu utilizatorul)
| # | Decizie | Alegere |
|---|---|---|
| D1 | Default bifa "Auto" pe conturi (inclusiv noi) | OFF (manual) — sigur, nimic nu pleaca fara confirmare |
| D2 | La OFF -> ON, randurile deja tinute | Eliberate automat spre transmitere |
| D3 | Plasare in UI | Bara de status (langa contoare, ca in mockup gomag) |
| D4 | Trimitere manuala | Per rand + buton "Trimite toate (N)" |
| D5 | Persistenta | Bifa salvata pe contul service-ului (accounts) |
Mecanica aleasa: flag held pe submission (NU stare noua)
Randurile tinute raman in starea queued (sunt logic in coada, doar puse pe pauza),
marcate cu o coloana booleana noua submissions.held. Motiv: evitam atingerea
CHECK-ului de stari si a masinii de stari (queued/sending/sent/needs_mapping/ needs_data/error), a pill-urilor, filtrelor si contoarelor — schimbare strict
aditiva. Eticheta umana "In asteptare (manual)" se deriva din status='queued' AND held=1 in stratul de afisaj (labels.py).
- Comutatorul de cont (
accounts.auto_send_enabled) guverneaza DOAR: (a) valoarea implicita a luiheldla ingestie; (b) eliberarea in bloc la OFF -> ON. - Worker-ul (
claim_one) ia doarstatus='queued' AND held=0. Nu mai stie de comutatorul de cont — ramane simplu si robust. - Trimiterea manuala (per rand sau bulk) =
held: 1 -> 0; worker-ul preia randul la urmatorul poll. Functioneaza chiar daca contul e pe Auto OFF (override uman per rand).
Comutatorul global AUTOPASS_WORKER_SEND_ENABLED ramane kill-switch master (productia
il porneste). Feature-ul nou se aseaza DEASUPRA lui: held tine randul indiferent de env.
4. User Stories
US-001: Schema — comutator cont + flag held
Ca dezvoltator Vreau coloanele de persistenta pentru bifa Auto si pentru randurile tinute Pentru ca starea sa supravietuiasca restartului si sa fie per-cont.
Acceptance Criteria:
accounts.auto_send_enabled INTEGER NOT NULL DEFAULT 0 CHECK (auto_send_enabled IN (0,1))adaugat inapp/schema.sql+ migrare defensiva inapp/db.py::_migrate(ALTER idempotent, ca laemail/tier).submissions.held INTEGER NOT NULL DEFAULT 0 CHECK (held IN (0,1))adaugat + migrare defensiva. Index partialidx_submissions_held ON submissions(held) WHERE held=1.- Index in
_migrate, nu doarschema.sql(Eng MEDIUM):CREATE TABLE IF NOT EXISTSnu se declanseaza pe DB existent -> indexul partial trebuie creat si in_migrate(caidx_submissions_batchladb.py:155), altfel un DB prod upgradat capata coloana (ALTER) dar NU si indexul. - Contul implicit id=1 (dev) ramane pe default (0) — fara tratament special.
- Helperi in
app/accounts.py:get_auto_send(conn, account_id) -> boolsiset_auto_send(conn, account_id, enabled: bool)(idempotent, scoped pe cont). python3 -m pytest -qramane verde (migrare aditiva, fara regresie pe schema).
US-002: Ingestie respecta comutatorul de cont
Ca operator de service cu Auto OFF Vreau ca prezentarile noi sa intre in coada tinute (held=1) Pentru ca sa le verific inainte sa plece la RAR.
Acceptance Criteria:
- La INSERT-ul
status='queued'pe canalul API (app/api/v1/router.py, ~l.282),held=0 daca accounts.auto_send_enabled=1 altfel 1(snapshot la ingestie). - Acelasi snapshot la commit-ul de import (
app/api/v1/import_router.py, ~l.1193). - La reresolve (un
needs_mappingrezolvat trece pequeued,app/mapping.py~l.895),heldse seteaza tot din comutatorul contului. heldNU intra inpayload_json, NU inbuild_key/idempotenta, NU in payload-ul RAR — e pur control de coada (careviewedla import).- DRY + acoperire COMPLETA (review CEO + Eng Finding A — HIGH): calculul
helde UN SINGUR helperheld_for_account(conn, account_id) -> int, chokepoint pt. TOATE situriSET status='queued', nu doar 3. Codebase-ul are ~8 scriitori dequeued:router.py:282(enqueue),import_router.py:1190(commit),mapping.py:895(reresolve),router.py:237(reactivare error->queued la re-POST — BUG real de bypass: randul pastraheldVECHI -> se auto-trimite desi Auto OFF), si rutele web de operatorroutes.pymapeaza-inline / corecteaza / repune / bulk-fix. - Politica rute operator: pentru tranzitiile declansate de operator in panoul de
detaliu (corecteaza/repune/mapeaza/bulk-fix),
held=0(actiunea operatorului = intentie explicita de trimitere) — DAR e o DECIZIE documentata, nu o omisiune, si respecta UX-ul de confirmare cand contul e OFF. Canalele de ingestie (API/import/reresolve/reactivare) =held_for_account. requeue_with_backoff(worker:154) NU atingeheld(tranzitie interna worker).- Echo pe dedup (Eng MEDIUM): ramura de dedup (
router.py:264, re-POST pe rand existent) intoarce si eaheld(azi ar da un "queued" curat fals — vezi US-009). - Test: cont Auto OFF ->
POST /v1/prezentarivalid -> randqueued, held=1; cont Auto ON ->queued, held=0. - Test reresolve: cont Auto OFF, submission
needs_mapping-> mapare salvata -> rand devinequeued, held=1(nu pleaca automat).
US-003: Worker nu trimite randurile tinute
Ca sistem Vreau ca worker-ul sa sara peste randurile held=1 Pentru ca transmiterea sa astepte decizia umana.
Acceptance Criteria:
claim_one(app/worker/__main__.py) adaugaAND s.held = 0laWHERE-ul de claim.- Test: rand
queued, held=1cu contactivesi send pornit ->claim_oneintoarceNone(nu il ia); acelasi rand cuheld=0-> e luat (sending). - Recuperarea orfanilor / reconcilierea NU sunt afectate (held se aplica doar la claim
din
queued; un rand dejasendingramane gestionat normal).
US-004: Bifa "Auto" in bara de status (toggle + persistenta + auto-release)
Ca operator Vreau o bifa "Auto" in bara de status, salvata pe cont Pentru ca sa pornesc/opresc transmiterea automata dintr-un click.
Acceptance Criteria:
- Control checkbox HTMX cu eticheta vizibila "Trimite automat la RAR" (decizie user;
NU "Auto" — eviti coliziunea cu "Trimitere automata" worker din
labels.py) + helptext ("Debifat: prezentarile asteapta confirmarea ta"), in clusterul de header langa.rar-chipSAU pe rand propriu in bara de status (vezi D1). Reflectaaccounts.auto_send_enabledal contului din sesiune. POST /auto-send(ruta web, subrequire_login+ scope cont + CSRF) comuta bifa si o persista pe cont; raspuns OOB care re-randeaza bara de status.- La trecerea OFF -> ON: toate randurile
queued AND held=1ale contului devinheld=0(eliberare in bloc), scoped strict pe contul curent. Eliberarea e o SINGURA instructiune SQL atomica (UPDATE ... WHERE account_id=? AND status='queued' AND held=1), NU un loop (review CEO: atomicitate + evita contention cu worker-ul). - Garda de confirmare (review CEO F4): daca exista N>0 randuri tinute la activarea Auto, comutatorul cere o confirmare explicita cu numarul si destinatia ("Activarea Auto trimite imediat N prezentari catre RAR PRODUCTIE — FINALIZATA e ireversibila"). Fara confirmare, randurile tinute NU pleaca. Motiv: pe contul de test, un OFF->ON necugetat ar arunca toate prezentarile de proba in RAR real.
- La trecerea ON -> OFF: randurile deja
queued held=0NU sunt retrase (doar ingestiile NOI vor fi tinute); randurile insending/sentneatinse. - Verify in browser: comuti bifa, se salveaza, ramane dupa refresh; cu OFF un rand nou apare tinut; comutand pe ON randurile tinute pleaca.
US-005: Trimitere manuala — per rand + "Trimite toate (N)"
Ca operator cu Auto OFF Vreau sa trimit un rand tinut sau toate odata Pentru ca sa eliberez selectiv sau in bloc spre RAR.
Acceptance Criteria:
- Buton "Trimite" pe fiecare rand
queued held=1in lista de trimiteri/coada (_submissions.html/_coada.html), scoped + CSRF. POST /trimitere/{id}/trimite-acum: 404-before-leak pe id strain; seteazaheld=0DOAR daca randul equeued held=1(no-op sigur altfel); OOB refresh.- Buton bulk "Trimite toate (N)" (N = nr. randuri tinute ale contului) ->
POST /trimite-toate: elibereaza toatequeued AND held=1ale contului (held=0), cu confirmare tipata (count + "catre RAR PRODUCTIE", review CEO F5). Update atomic scoped pe cont (NU poate elibera randuri ale altui cont). POST /trimitere/{id}/trimite-acumUPDATE includeAND status='queued'ca un rand dejasending(luat de worker intre afisaj si click) sa fie no-op sigur (edge race).- Eliberarea seteaza doar
held=0; worker-ul preia randul la urmatorul poll (trimitere asincrona, ca azi). Necesita worker pornit + send master ON + cont activ. - Butonul "Trimite toate (0)" e ascuns cand nu exista randuri tinute.
- Test: rand tinut ->
trimite-acum->held=0; apoiclaim_oneil ia.
US-006: Afisaj stare "In asteptare (manual)"
Ca operator Vreau sa disting randurile tinute de cele in curs de trimitere Pentru ca sa stiu ce asteapta decizia mea.
Acceptance Criteria:
app/web/labels.py: pentrustatus='queued' AND held=1-> eticheta umana "In asteptare (manual)" + clasa CSS de avertizare (caneeds_*);held=0ramane "In asteptare" (queued normal).- Bara de status arata un contor separat "In asteptare (manual): N" cand N > 0
(derivat din
queued AND held=1); contorulqueuedtotal ramane corect. - Lista de trimiteri marcheaza randurile tinute (badge/pill), butonul "Trimite" apare doar pe ele.
- Verify in browser: un rand tinut afiseaza eticheta corecta si butonul; dupa trimitere trece la "In curs de trimitere" -> "Trimisa".
US-007: Vizibilitate coada tinuta imbatranita (mitigare OBLIGATORIE pt. default OFF)
Ca operator / admin Vreau un semnal vizibil cand prezentari raman tinute prea mult Pentru ca default OFF (decizie user, pana devine stabil) lasa altfel prezentari nedeclarate tacit — exact esecul silentios pe care L.142/2023 il face risc legal.
Conditie: user a ales DELIBERAT default OFF "pana devine stabil" peste avertismentul de conformitate (review CEO F1/F3). Aceasta US e atenuarea agreata si e BLOCANTA, nu optionala.
Acceptance Criteria:
- Bara de status: cand exista randuri
queued AND held=1mai vechi deNzile (configAUTOPASS_HELD_WARN_DAYS, default 7), afiseaza un banner de avertizare ("M prezentari tinute de >N zile — declarare obligatorie L.142") cu deep-link la lista filtrata pe tinute. /metricsexpune un gaugeautopass_held_submissions(total randuri tinute) siautopass_held_oldest_age_seconds(varsta celui mai vechi rand tinut), scoped global (observabilitate ops, review CEO F3).- Bannerul + gauge sunt derivate (zero stare noua); contorul varstei foloseste
created_atal randului. - Test: rand tinut cu
created_atvechi -> bannerul apare; gauge raporteaza varsta.
US-008: Retentie randuri tinute (inchide gaura GDPR/L.142, review CEO F6)
Ca sistem
Vreau ca randurile tinute la nesfarsit sa aiba o politica de expirare
Pentru ca un queued held=1 nu e nici sent nici blocat -> azi NU primeste
purge_after -> PII criptat (si rar_creds_enc efemer pe canalul API) ar sta vesnic.
Acceptance Criteria:
- Worker-ul expira randurile
queued AND held=1mai vechi deheld_retention_days(config, default 90, aliniat T16): le trece laerrorcu mesajTINUT_EXPIRAT(terminal) + seteazapurge_afterDIRECT la momentul expirarii (NU lasamark()sa apliceblocked_retention_days=30). Eng MEDIUM: altfel viata reala = 90 (held) + 30 (error) = 120 zile, nu 90. Fie purge_after explicit la tranzitie, fie documenteaza 120. - La eliberarea manuala/auto a unui rand tinut, daca
rar_creds_enc(canal API) e prea vechi, worker-ul cade peaccounts.rar_creds_enc(fallback re-login) ca azi — verificat ca creds efemere expirate nu blocheaza trimiterea. - Test: rand tinut vechi -> ciclul de purjare al worker-ului il expira + seteaza
purge_after; PII devine purjabil.
US-009: Fixturi teste + jurnal audit (review CEO F7 + observabilitate)
Ca dezvoltator
Vreau ca suita existenta sa nu se blocheze pe default OFF si ca actiunile sa fie auditate
Pentru ca default OFF + claim_one ... AND held=0 face ca lantul POST -> claim -> sent
din testele existente (+ test_live_rar) sa stagneze tacit daca nu setam Auto ON.
Acceptance Criteria:
conftest/factory de cont seteazaauto_send_enabled=1(sauheld=0) pe conturile folosite de testele care exercita lantul de trimitere;test_live_rarseteaza explicit Auto ON.pytest -qramane verde.- Subtilitate id=1 (Eng HIGH/test): contul implicit id=1 e creat de
schema.sql(INSERT OR IGNORE), NU decreate_account-> un fix care patcheaza doar factory-ul NU acopera contul folosit de majoritatea testelor (test_import_e2e,test_creds_delivery,test_live_rarar stagna). Conftest face explicitUPDATE accounts SET auto_send_enabled=1 WHERE id=1(autouse). E un fix de STARE DB, nu env var (coloana e per-rand inaccounts). - Audit
app_events: comutarea Auto (auto_send_schimbatcu valoarea + cont) si eliberarile manuale/bulk (held_eliberatcu count) sunt jurnalizate (redactat, scoped). - Echo onest pe canalul API (aliniat invariant 5.7): raspunsul
POST /v1/prezentaripentru un rand tinut indica starea reala (held=true/ nota umana "tinut pentru verificare"), nu un fals "queued" curat. Dev-ul ROAAUTO vede ca randul NU a plecat. - Test: eveniment audit scris la toggle + la eliberare; raspuns API reflecta
held.
US-010: Onestitate + observabilitate pe canalul API (review DX Faza 3.5)
Ca dezvoltator ROAAUTO/VFP care integreaza prin POST /v1/prezentari
Vreau sa vad clar ca un rand e tinut si NU a plecat la RAR
Pentru ca azi un rand tinut intoarce byte-identic cu unul gata de auto-send
(status:queued, erori:[]) -> reintroduce exact bug-ul de succes-fals 5.7.
Acceptance Criteria:
- Camp
held: bool = FalsepeSubmissionResult(models.py) + plumbing dinheld_for_accountin_rezultat_enqueue(..., held=...)SI pe ramura de dedup (router.py:264). Candheld and status=='queued',motivdevine NON-null (DX CRITICAL): mesaj uman "In asteptare — tinut pt verificare; NU trimis la RAR (Auto OFF)". heldin proiectiile GET (_PREZENTARE_FIELDSrouter.py:398+ listacolsrouter.py:369): un dev care faceGET /v1/prezentari/{id}vedeheld=true, nu unqueuedetern fara semnal (DX HIGH).- Reutilizeaza vocabularul existent
AUTO_SEND_OPRIT(errors.py:92) pt. mesajul held — NU inventa al treilea vocabular "auto_send" (DX + R6). Mesaj 3-niveluri (problema/cauza/fix) perar_error/motiv. - Documentatie hub
/integrare(integrare_examples.py/_integrare.html): tabel "De ce nu ajunge la RAR?" (held / needs_mapping / needs_data) + nota explicita "conturi noi pornesc cu Auto OFF, randurile asteapta eliberare manuala" + cum verifici/comuti (DX HIGH — altfel primul POST da 200/queued, dev-ul crede ca merge, nimic nu ajunge). - (Optional, paritate API) endpoint de eliberare API simetric cu
/repune(router.py:458):POST /v1/prezentari/{id}/trimite-acum, scoped pe cont, 404-before, no-op daca nuqueued AND held=1— ca integratorul API sa nu fie fortat in browser. - Test: held ->
held=true+motivnon-null pe enqueue, dedup si GET (regresie catest_queued_fara_erori_nemapate).
5. Cerinte functionale
- [REQ-001] Comutatorul "Auto" e per-cont, persistat in
accounts.auto_send_enabled, default 0 (OFF) inclusiv pentru conturi noi. - [REQ-002] Cu Auto OFF, orice ingestie care ar produce
queuedproducequeued held=1. - [REQ-003] Worker-ul nu trimite niciodata un rand
held=1. - [REQ-004] OFF -> ON elibereaza in bloc randurile tinute ale contului (atomic, scoped), DAR cu confirmare tipata cand N>0 (count + "RAR PRODUCTIE"); ON -> OFF nu retrage randuri deja eliberate.
- [REQ-005] Operatorul poate elibera un rand tinut individual sau toate odata (bulk cu confirmare).
- [REQ-006]
heldnu influenteaza payload-ul RAR, idempotenta sau validarea — pur coada. - [REQ-007] Toate rutele noi sunt scoped pe contul din sesiune, sub
require_login, cu CSRF si 404-before-leak pe id strain.account_idse deriva INTOTDEAUNA din sesiune, NICIODATA dintr-un camp de formular (Eng security): altfel un operator pe contul A ar elibera in bloc randurile contului B postandaccount_id=B. Per-rand prin_get_submission_scoped(404 inainte de UPDATE). - [REQ-008] Randurile tinute imbatranite sunt VIZIBILE (banner +
/metrics) si au politica de retentie/expirare (nu raman PII vesnic). Comutarea + eliberarile sunt auditate inapp_events.
6. Non-Goals (ce NU facem)
- Fara interval/programare de sync (dropdown "1 min" + buton "Start Sync" din gomag): worker-ul autopass e continuu, nu pe interval. "Trimite toate" e analogul lui "Start Sync".
- Fara stare noua de submission (
held/tinut): folosim flag boolean pequeued. - Fara comutator per-operatie sau per-canal: granularitatea e per-cont (decizie D5).
(Nota: coloanele
auto_sendramase peoperations_mapping/operation_text_rulessunt neutralizate din 5.11 si NU se reactiveaza aici.) - Fara modificarea kill-switch-ului global
AUTOPASS_WORKER_SEND_ENABLED. - Fara retragerea randurilor deja in
sending/sent(FINALIZATA e terminal la RAR). heldNU e sandbox de testare (avertisment de onestitate — tema cross-faza CEO F2 + DX4): eliberarea unui rand tinut declara REAL la RAR (FINALIZATA ireversibila). "Tinut" doar AMANA o trimitere reala. Ca sa testezi fara consecinte cu functia asta: tii randul si il STERGI (nu-l eliberezi). Decizie user (poarta finala): 5.19 = doar tinut operational; fara documentare/valideazaca unealta de testare si fara rutare per-cont la RAR test (rar_env). Acestea raman posibile follow-up-uri (TODOS), neangajate in 5.19.
7. Consideratii tehnice
Stack / fisiere atinse
- Schema:
app/schema.sql+app/db.py::_migrate(2 coloane aditive + 1 index). - Backend cont:
app/accounts.py(get/set toggle). - Ingestie:
app/api/v1/router.py,app/api/v1/import_router.py,app/mapping.py(reresolve) — sethelddin comutator. - Worker:
app/worker/__main__.py::claim_one(+AND s.held=0). - Web:
app/web/routes.py(rute/auto-send,/trimite-toate,/trimitere/{id}/trimite-acum),app/web/labels.py, template-uri_status.html/_submissions.html/_coada.html.
Patterns de urmat
- Migrare defensiva aditiva (model
accounts.email/accounts.tierdin 5.12/5.17). - Rute web scoped + CSRF + OOB HTMX (model
submissions_admin.py/ butoanele de lifecycle 5.6). - Strat de afisaj pur in
labels.py(model 5.4) — fara logica de stare in template.
Riscuri tehnice
- R1 (default OFF schimba comportamentul): azi nu exista hold; cu default 0, conturile ar tine totul. Acceptabil — productia e pre-lansare, fara conturi legacy active (cf. 5.17), iar utilizatorul vrea explicit OFF pe contul de test. Documentat ca decizie constienta (D1).
- R2 (reresolve scapa snapshot-ul): daca uitam
heldpe calea de reresolve (mapping.py), un rand deblocat dinneeds_mappingar pleca automat desi contul e OFF. Acoperit explicit de US-002 AC. - R3 (idempotenta):
heldNU intra in cheie -> un re-POSTal aceluiasi continut loveste randul existent (dedup), nu creeaza dublura. Confirmat de invariantulbuild_key. - R4 (hazard de rollback — review CEO + Eng, HIGH operational): daca se da revert pe cod
DUPA ce randuri au
held=1, worker-ul pierde filtrulAND held=0-> ar trimite TOATE randurile tinute la RAR (FINALIZATA ireversibila). Atenuare OBLIGATORIE: livreaza ODATA cu feature-ul un helpertools/care carantineaza randurile tinute (UPDATE submissions SET status='error', rar_error='ROLLBACK_QUARANTINE' WHERE held=1) + pas de runbook scris in §9 (copy-paste, nu improvizat sub presiune). - R7 (eroziune creds efemere — Eng low-med): la orice login reusit worker-ul NULL-eaza
TOATE
submissions.rar_creds_encale contului (worker:382), nu doar randul trimis. Un cont hibrid web+API cu keepalive-login poate sterge creds-urile efemere ale unui rand tinut API -> la eliberare se cade peaccounts.rar_creds_enc(fallback). Acoperit de US-008, dar triggerul e login-frate, nu varsta creds — de formulat corect. - R5 (contention SQLite la bulk release):
UPDATEmasiv pe "Trimite toate" concureaza cuBEGIN IMMEDIATEal worker-ului -> posibildatabase is locked. Update-ul atomic (o instructiune) + retry/backoff scurt; sau chunking daca N e mare. - R6 (naming): apare al TREILEA
auto_send(contauto_send_enabledvsoperations_mapping.auto_sendvsoperation_text_rules.auto_send). Comentariu clar inschema.sqlcare le distinge, ca un viitor dezvoltator sa nu le confunde.
Rafinari UI (review design Faza 2 — OBLIGATORII la implementare)
- D1 (container real): RAR dot e in
base.html(header.rar-chip), NU in_status.html. US-004 AC corectat: comutatorul Auto sta in clusterul de header langa.rar-chip(vizibilitate maxima, langa semnalul RAR real) SAU pe un rand propriu etichetat in bara de status — NU "langa dot" in_status.html(dot-ul nu e acolo). - D2 (toggle non-optimist): checkbox HTMX flip-uie vizual indiferent de raspuns. Necesita
hx-indicator+ revert-on-failure (la esec POST/auto-send-> bifa revine + toast eroare). Fara fals-sigur tacit pe un comutator de transmitere guvernamentala. - D3 (poller nu inghite toggle-ul):
#status-bararehx-trigger="every 15s"+hx-swap="outerHTML"-> ar inlocui comutatorul la fiecare 15s (pierdere focus tastatura + flicker). Exclude comutatorul din swap-ul periodic (container separat sauhx-preserve). - D4 (modal de confirmare real): confirmarea tipata (count + "RAR PRODUCTIE") NU se poate
face cu
hx-confirm(doar OK/Cancel nativ). Necesita un component modal (count, destinatie, type-to-confirm) — adaugat in lista de fisiere. Per-rand "Trimite" primeste si el o confirmare (1 linie + microcopy de ireversibilitate), nu doar bulk-ul. - D5 (camp derivat, nu in template):
heldNU e stare noua -> pill-ul existent ar randa "In coada" identic pt held si non-held. Calcul UN camp de afisaj derivat inroutes.py(regula "display layer pur"), nu in template. Culoare--warn(amber), NU clasaneeds_*(rosu/eroare) — held e asteptare benigna, nu eroare. - D6 (mobil 390px): per-rand actiune = afordanta dedicata pe
.trimitere-slimcuevent.stopPropagation()(randul e el insusirole=button), NU buton-copil nestat. Al 6-lea contor "In asteptare (manual)" se pliaza in celula "In coada" pe bara compacta (nu adauga a 6-a celula la 10px). Pill scurt ("Manual"/"Tinut") cu fraza completa intitle. - D7 (ordonare bannere):
_status.htmlpoate avea deja 3 bannere (cont inactiv / trial / RAR jos) + al 4-lea (US-007 held). Regula de prioritate un-singur-banner ca sa nu impinga contoarele sub fold pe mobil.
Dependente
- Trimiterea manuala produce efect doar cu worker pornit + send master ON + cont
active(mediul de productie real). In dev (send OFF) randul eliberat ramanequeued held=0.
8. Open Questions
- Trimiterea manuala se face asincron (flip
held=0, worker preia la poll). Acceptam latenta de un poll (cateva secunde) sau vrem feedback "in curs" imediat in UI? (Propunere: asincron + OOB refresh, fara sincron — consistent cu arhitectura.) - Pe mobil, butonul "Trimite" per rand + "Trimite toate" incap in layout-ul compact (5.13)? (Propunere: "Trimite toate" in bara sticky, "Trimite" iconita pe card.)
9. Plan de verificare
- Regresie
python3 -m pytest -qverde (baseline curent ~1392) + teste noi per story. - E2E browser (Playwright, logat): comutare bifa persistenta dupa refresh; rand nou tinut cu Auto OFF; eliberare per rand si bulk; tranzitie OFF -> ON elibereaza in bloc.
- Optional live RAR (
AUTOPASS_LIVE_RAR=1): cont OFF -> rand tinut -> "Trimite" ->sent idPrezentare=...confirmat in finalizate.
10. Decizii /autoplan — audit trail
Pipeline: CEO -> Design -> Eng -> DX, voce unica (Codex indisponibil pana 2026-07-18, plafon utilizare). Deciziile intermediare auto-decise pe 6 principii; portile umane = premise + taste.
Poarta de premise (decizia ta)
- Scop: AMBELE — control operational permanent + ajutor de testare.
- Default Auto: OFF, pastrat "pana devine stabil" (ales constient peste avertismentul de conformitate L.142). Inverseaza recomandarea CEO F1 (default ON). Acceptat ca decizie de domeniu; declanseaza atenuari OBLIGATORII (US-007/008/009).
Decizii auto (6 principii)
| # | Faza | Decizie | Clasif. | Principiu | Motiv |
|---|---|---|---|---|---|
| 1 | CEO | Approach A (held boolean) ca baza, nu stare noua (B) sau enum mod (C) | Mecanica | P5+P3 | aditiv, reuse pattern reviewed; B atinge masina de stari pazita |
| 2 | CEO | US-007 vizibilitate coada imbatranita OBLIGATORIE | Mecanica | P1+observ | atenuarea agreata pt default OFF; inchide esecul silentios F3 |
| 3 | CEO | US-008 retentie randuri tinute | Mecanica | P1 | F6: held nu primeste purge_after -> PII vesnic (GDPR/L.142) |
| 4 | CEO | US-009 fixturi teste Auto ON + audit + echo API held | Mecanica | P1 | F7: default OFF stagneaza testele; invariant 5.7 raspuns onest |
| 5 | CEO | Garda de confirmare OFF->ON + bulk (count + RAR PRODUCTIE) | Mecanica | P1 | F4/F5: flush ireversibil de randuri test in RAR real |
| 6 | CEO | held_for_account() helper unic (DRY) |
Mecanica | P4 | calcul held inline de 3x = sit uitat trimite automat |
| 7 | CEO | Enum mod cont (live/hold/test) -> TODOS | Mecanica | P3 | scope dincolo de cerere; dream-state, nu blocant |
Decizii de taste / provocari -> poarta finala (Faza 4)
- T-EXP1 (reframe testare, CEO F2 + DX4) -> REZOLVAT: user a ales "doar tinut". Nici
rar_env, nici documentarea/valideazaca unealta de testare in 5.19. Ambele -> TODOS (posibil follow-up). Pastrat doar avertismentul de onestitate ca eliberarea declara real. - T-LABEL (eticheta toggle, Design HIGH) -> REZOLVAT: user a ales REDENUMIREA. Eticheta
vizibila = "Trimite automat la RAR" (nu "Auto"), ca sa nu se ciocneasca cu
"Trimitere automata" (worker viu) din
labels.py. Conceptul/coloana ramanauto_send_enabled.
Faze Design/Eng/DX (audit)
| Faza | Decizie cheie | Clasif. | Motiv |
|---|---|---|---|
| Design | D1-D7 rafinari UI (non-optimist, poller, modal, mobil, camp derivat, --warn, bannere) | Mecanica | structural, P5 explicit |
| Eng | held_for_account la TOATE ~8 situri queued (bug reactivare router:237) | Mecanica | P5; bypass real Auto OFF |
| Eng | conftest UPDATE id=1; index in _migrate; purge_after direct; account_id din sesiune | Mecanica | corectitudine/securitate |
| DX | held pe SubmissionResult+GET; reuse AUTO_SEND_OPRIT; hub docs | Mecanica | P1; invariant 5.7 |
Sumar completare review
+====================================================================+
| /autoplan — MEGA PLAN REVIEW — COMPLETION SUMMARY |
+====================================================================+
| Mod | SELECTIVE EXPANSION |
| Voci | Claude subagent (CEO/Design/Eng/DX); |
| | Codex INDISPONIBIL (plafon -> 2026-07-18) |
| Poarta premise | scop=AMBELE; default OFF (user, time-boxed) |
| CEO | 7 findings, 2 critice -> atenuate |
| Design | 5->8/10; 13 findings, 3 critice -> D1-D7 |
| Eng | 7 issues; 1 BUG real (reactivare bypass) |
| DX | 5->8/10; onestitate API -> US-010 |
| Stories | 6 -> 10 (US-007/008/009/010 adaugate) |
| Taskuri | 26 (14 P1, 9 P2, 3 P3), agregate pe faze |
| Tema cross-faza | hold != sandbox testare (CEO F2 + DX4) |
| Taste rezolvate | T-EXP1=doar tinut; T-LABEL=redenumire |
| Deferate (TODOS) | enum mod cont; rar_env; doc /valideaza |
| Test plan | scris pe disc (~/.gstack/.../test-plan) |
| Artefacte taskuri | 4 JSONL pe faza |
| Decizii nerezolvate | 0 |
+====================================================================+
GSTACK REVIEW REPORT
| Review | Trigger | Why | Runs | Status | Findings |
|---|---|---|---|---|---|
| CEO Review | /plan-ceo-review |
Scope & strategy | 1 | issues_open->resolved | 5 propuneri, 4 acceptate, 2 deferate; 2 gap critice atenuate |
| Eng Review | /plan-eng-review |
Architecture & tests (required) | 1 | issues_open->resolved | 7 issues (1 bug bypass reactivare), 0 gap critice ramase |
| Design Review | /plan-design-review |
UI/UX gaps | 1 | issues_open->resolved | 5->8/10, 13 findings (3 critice) -> D1-D7 |
| DX Review | /plan-devex-review |
Developer experience gaps | 1 | issues_open->resolved | 5->8/10, onestitate API (US-010) |
- CROSS-MODEL: N/A — Codex indisponibil (plafon utilizare pana 2026-07-18); voce unica Claude subagent pe toate fazele.
- VERDICT: CEO + DESIGN + ENG + DX CLEARED (voce unica) — PRD revizuit, gata de implementare. Toate deciziile portilor inchise cu user.
NO UNRESOLVED DECISIONS