Nascut din incidentul 500 (client VFP). 14 stories: observabilitate (handler global 500->3 niveluri, request_id, jurnal app_events DB+fisier, audit API + login RAR, redactare PII, retentie), lifecycle trimiteri blocate (sterge/re-pune in coada UI+API, dedup nemaiblocat de un rand error, purjare blocate) si banner "Necesita atentia ta" actionabil. Decizii §5 rezolvate cu user. ROADMAP: rand 5.6 APROBAT + hotfix in "Ultima actualizare". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
27 KiB
PRD 5.6 — Observabilitate, jurnal aplicatie & lifecycle trimiteri blocate
Stare: aprobat (decizii §5 rezolvate 2026-06-23)
Proces complet:
docs/ROADMAP.md§5. Contract RAR (sursa de adevar):docs/api-rar-contract.md. Catalog erori (sursa de adevar coduri):app/errors.py(PRD 5.4). Redactare creds:app/security.py.
0. Context — de ce acum
Un client ROAAUTO (Visual FoxPro, MSXML2.ServerXMLHTTP) a primit "Internal Server
Error" la POST /v1/prezentari si nu a existat niciun mod de a vedea ce s-a intamplat
fara a citi traceback-ul brut din access-log-ul uvicorn (.run/api.log).
Cauza concreta a fost o cheie Fernet invalida in .env (AUTOPASS_CREDS_KEY),
care arunca ValueError abia la primul encrypt_creds — un 500 brut, fara mesaj
util pentru client si fara inregistrare la nivel de aplicatie. Cauza a fost reparata
ca hotfix (cheie valida in .env + validare fail-fast la startup —
crypto.validate_creds_key, apelata in main.lifespan; confirmat live HTTP 200).
Acest incident a expus trei goluri structurale, care sunt obiectul acestui PRD:
- Excepțiile interne devin 500 brut, fara traducere in contractul de erori pe 3 niveluri (PRD 5.4) si fara log cu context (request_id, ruta, cont).
- Nu exista jurnal de aplicatie la nivel de eveniment — doar access-log uvicorn
(linii HTTP) +
print(...)ad-hoc in worker. Nu se poate raspunde la "ce s-a intamplat cu cererea X / contul Y" fara grep prin traceback-uri. - Nu exista monitorizare a fluxului de afaceri dincolo de
/healthz+/metrics: incercari de login RAR, ciclul de viata al trimiterilor, erori din catalog.
0bis. Context — lacune de lifecycle (descoperite la testarea live)
La testul live pe RAR test au iesit la iveala doua probleme de lifecycle, confirmate in cod:
- Trimiterile blocate sunt permanente.
purge_afterse seteaza DOAR lastatus='sent'(mark()), iarpurge_expiredsterge DOAR randurisentexpirate. Randurileerror/needs_data/needs_mappingnu primesc niciodatapurge_after→ nu se purjeaza niciodata. Login 401 (creds RAR gresite) →errordirect, fara retry (by design, ca sa nu blocheze contul) → randul ramane la nesfarsit in dashboard, fara cale de stergere prin UI/API/CLI (corectia inline US-010 e doar pentruneeds_*, nuerror). - Un rand
errorblocheaza retrimiterea aceluiasi payload. Cheia de idempotenta e hash de CONTINUT (vin+nr+data+odometru+prestatii+cont) — parola NU intra in cheie. Daca un client trimite cu parola gresita (→error), apoi corecteaza parola si retrimite acelasi payload, primestededuped: truecustatus: errorsi prezentarea nu se mai trimite niciodata. Randul eronat "fura" cheia.
Reproductibil acum: submission 15 (din clientul VFP cu parola placeholder) e blocat pe
error RAR_CREDS_INVALIDE; testul reusit (submission 16 → idPrezentare 68818) a
necesitat un odometru diferit tocmai ca sa ocoleasca aceasta capcana.
1. Obiectiv
Un sistem de observabilitate coerent: orice eveniment relevant (cerere API, login RAR,
tranzitie de trimitere, eroare) este inregistrat structurat intr-un tabel app_events
(vizibil intr-un tab "Jurnal" din dashboard, filtrabil pe cont/tip/data) si intr-un
log text pentru depanare low-level. Orice excepție neasteptata produce un raspuns pe 3
niveluri (PRD 5.4) in loc de 500 brut. PII si credentialele sunt redactate peste tot.
In plus, inchidem doua lacune de lifecycle descoperite la testarea live (vezi §0bis):
trimiterile blocate (mai ales error din creds RAR gresite) sunt azi permanente, nu se
pot sterge/re-pune in coada din interfata, blocheaza retrimiterea aceluiasi payload prin
dedup si nu se purjeaza niciodata. Le facem gestionabile (sterge / re-pune in coada),
deblocam dedup-ul si le aducem sub retentie.
2. Non-Goals (anti scope-creep)
- Fara dependinte/infrastructura noi de observabilitate (Sentry, ELK, Loki, OpenTelemetry, Prometheus push). Ramanem pe SQLite + fisier + dashboard HTMX existent.
- Fara alerting (email/SMS/webhook la eroare).
notify_signupramane singura notificare; alertarea = follow-up daca apare nevoia din uz real. - Nu schimbam contractul de erori (PRD 5.4). Pe partea de observabilitate doar
observam si traducem 500-urile ramase. Partea de lifecycle (US-009+) adauga DOAR
doua tranzitii noi controlate —
error → queued(re-pune in coada) si stergere de randuri ne-sent — fara a atinge logica de trimitere a worker-ului. - Nu stergem/atingem randuri
sentprin noile actiuni de lifecycle: sunt dovada de trimitere la RAR (audit). Stergerea/re-punerea opereaza DOAR peerror/needs_data/needs_mapping;sending(in zbor) e protejat de lease-ul worker-ului. - Nu anulam nimic la RAR —
FINALIZATAramane terminal acolo (fara API de anulare). Stergem doar randul LOCAL din coada gateway-ului, nu inregistrarea de la RAR. - Nu modificam
/healthzsi/metrics(raman; jurnalul e complementar, nu inlocuitor). - Fara UI de configurare a logarii (nivel/retentie se seteaza din env, nu din web).
- Nu logam corpuri de payload integral (PII vehicul/proprietar) — doar metadate + identificatori (submission_id, cont, cod eroare). VIN/nr se logheaza doar redactat/partial.
3. Stories atomice
US-000 (hotfix 500) e DEJA LIVRAT in afara procesului normal (vezi §0): cheie Fernet valida in
.env+crypto.validate_creds_key()apelata inmain.lifespan, confirmata live (POST VFP → 200,queued). Listata aici doar pentru trasabilitate; nu se re-executa.
US-001: Handler global de excepții → eroare 3 niveluri + log
Ca integrator ROAAUTO vreau ca orice eroare interna sa-mi intoarca un raspuns structurat (nu "Internal Server Error" gol) pentru ca sa stiu daca e problema mea (date) sau a gateway-ului, si sa pot raporta cu un identificator.
- Depinde de: — (US-003 imbogateste logul; handlerul poate loga si simplu intai)
- Fisiere:
app/main.py(@app.exception_handler(Exception)),app/errors.py(cod nouEROARE_INTERNA),tests/test_error_handler.py - Test intai (RED):
tests/test_error_handler.py—test_exceptie_neasteptata_da_500_structurat,test_raspuns_contine_request_id_fara_traceback,test_creds_nu_apar_in_raspuns - Acceptance criteria:
- O excepție neprinsa pe orice ruta → HTTP 500 cu body
{cod: "EROARE_INTERNA", problema, fix, request_id}(3 niveluri, PRD 5.4). - Body-ul NU contine traceback, mesaj de excepție brut, sau credentiale (scrub).
- Traceback-ul complet +
request_id+ ruta +account_idse scriu in log (fisier) prinscrub(app/security.py), niciodata in raspuns. - Handlerele existente (LoginRequired/AdminRequired/CSRF/RequestValidationError)
raman neatinse; doar
Exceptiongeneric e nou.
- O excepție neprinsa pe orice ruta → HTTP 500 cu body
- Verificare E2E: forteaza o excepție (ex. cheie Fernet invalida pe o ruta de test) → raspuns JSON 3 niveluri cu request_id; traceback doar in log.
US-002: request_id per cerere (corelare)
Ca operator vreau un identificator unic pe fiecare cerere pentru ca sa pot lega raspunsul clientului de randul din jurnal si de traceback.
- Depinde de: —
- Fisiere:
app/web/middleware.py(sau middleware inmain.py),tests/test_request_id.py - Test intai (RED):
tests/test_request_id.py—test_raspuns_are_header_x_request_id,test_request_id_propagat_in_log - Acceptance criteria:
- Fiecare raspuns are header
X-Request-ID(generat daca clientul nu trimite unul). request_ide disponibil in handlerul de erori (US-001) si in logger (US-003) pe durata cererii (contextvar, fara a polua semnaturi).- Format opac, fara PII (ex.
secrets.token_hex(8)).
- Fiecare raspuns are header
- Verificare E2E: doua cereri → doua
X-Request-IDdistincte, regasite in jurnal.
US-003: Logger structurat central (app/observ.py)
Ca dezvoltator vreau un singur punct prin care se emit evenimente pentru ca formatul, redactarea si dublul canal (DB + fisier) sa fie consistente si imposibil de ocolit.
- Depinde de: US-002 (request_id), schema
app_events(US-004) - Fisiere:
app/observ.py(modul nou:log_event(...)),app/schema.sql(tabelaapp_events),app/db.py(helper insert + read paginat),tests/test_observ.py - Test intai (RED):
tests/test_observ.py—test_log_event_scrie_in_db_si_fisier,test_log_event_redacteaza_pii_si_creds,test_nivel_filtrat_din_env - Acceptance criteria:
log_event(tip, *, nivel, account_id=None, cod=None, mesaj=None, context=None)scrie un rand inapp_eventsSI o linie in log text (acelasi continut redactat).- Toate valorile trec prin
scrub(app/security.py) inainte de persistare — parole/token-uri/rar_credentials→***REDACTED***; VIN logat doar partial. - Nivelul minim e configurabil din env (
AUTOPASS_LOG_LEVEL, defaultINFO). - Eroarea la scrierea jurnalului NU propaga (best-effort, ca
notify_signup): o cadere a logului nu doboara cererea/worker-ul. app_events:id, ts, request_id, account_id, sursa(api|worker), tip, nivel, cod, mesaj, context_json, purge_after.
- Verificare E2E: apel
log_eventdin shell → rand in DB + linie in fisier, ambele redactate.
US-004: Audit cerere API per cont
Ca operator vreau sa vad fiecare cerere /v1/* (cine, ce, rezultat)
pentru ca sa pot diagnostica integrari (ex. clientul VFP) fara acces la server.
- Depinde de: US-003
- Fisiere: middleware/dependinta in
app/api/v1/(hook pe rutele v1),app/api/v1/router.py(evenimente la enqueue),tests/test_audit_api.py - Test intai (RED):
tests/test_audit_api.py—test_post_prezentari_logheaza_eveniment_cont,test_eveniment_contine_status_si_count_fara_pii,test_401_logat_ca_auth_esuat - Acceptance criteria:
POST /v1/prezentariemite evenimentapi_prezentaricu:account_id, nr. prezentari, distributie status rezultat (queued/needs_data/needs_mapping/deduped).- Esecurile de auth (401 cheie invalida/lipsa in prod) emit
api_auth_esuatcu IP + prefix cheie (nu cheia intreaga). - Niciun camp de payload PII integral (doar count + statusuri + coduri).
- Verificare E2E: POST ca VFP (cheie valida + invalida) → ambele apar in jurnal cu cont/rezultat.
US-005: Audit login RAR + ciclu de viata trimiteri (worker)
Ca operator vreau sa vad incercarile de login RAR si tranzitiile trimiterilor pentru ca "nu exista incercari de login vizibile" a fost o plangere directa.
- Depinde de: US-003
- Fisiere:
app/worker/__main__.py(inlocuiesteprint(...)culog_event),app/rar_client.py(eveniment login ok/esuat),tests/test_worker_observ.py - Test intai (RED):
tests/test_worker_observ.py—test_login_reusit_logat,test_login_401_logat_fara_parola,test_tranzitie_sent_si_error_logate - Acceptance criteria:
- Login RAR (reusit/esuat) → eveniment
rar_logincuaccount_id, rezultat, cod HTTP; fara email/parola in clar (scrub). - Tranzitiile
sending→sent/→needs_data/→error/ reconciliere → evenimente cusubmission_id,account_id, cod eroare din catalog. print(...)existente din worker migrate lalog_event(sursa=worker), fara a pierde mesajele in stdout (logul text ramane).
- Login RAR (reusit/esuat) → eveniment
- Verificare E2E: o trimitere live pe RAR test (
--send) →rar_loginok +sentcuidPrezentarein jurnal.
US-006: Tab "Jurnal" in dashboard (admin, filtrabil)
Ca admin vreau sa vad jurnalul in dashboard pentru ca sa diagnostichez fara SSH.
- Depinde de: US-003 (date), US-004/US-005 (continut util)
- Fisiere:
app/web/routes.py(ruta/_fragments/jurnal+ tab),app/web/admin_routes.py(gating admin daca e global),app/web/templates/_jurnal.html,tests/test_web_jurnal.py - Test intai (RED):
tests/test_web_jurnal.py—test_jurnal_doar_admin,test_filtru_pe_tip_si_data,test_non_admin_vede_doar_evenimentele_contului_sau - Acceptance criteria:
- Tab "Jurnal" cu lista paginata: ts, sursa, tip, nivel, cont, cod, mesaj (redactat).
- Filtre: tip eveniment, nivel, interval data, (admin) cont. Scoped: un cont non-admin vede DOAR evenimentele proprii (regula NULL→cont 1, ca restul UI-ului).
- Stil consistent cu tabelele PRD 5.5 (grila
.tablewrap), AA light+dark. - Fara expunere de creds/PII (rendare din campuri deja redactate la scriere).
- Verificare E2E: browser HTMX pe
/→ tab Jurnal, filtrare perar_login/api_prezentari, scoping verificat cu 2 conturi.
US-007: Redactare PII/parole in jurnal (gard de siguranta)
Ca responsabil GDPR vreau garantia ca jurnalul nu scurge date sensibile pentru ca PII vehicul/proprietar + creds RAR nu au voie in loguri (L.142/GDPR).
- Depinde de: US-003
- Fisiere:
app/security.py(extindescrub/SENSITIVE_KEYSdaca e nevoie),tests/test_jurnal_redactare.py - Test intai (RED):
tests/test_jurnal_redactare.py—test_parola_niciodata_in_app_events,test_vin_logat_partial,test_payload_integral_nu_se_logheaza - Acceptance criteria:
- Niciun rand
app_events(sau linie fisier) nu continepassword/token/ email creds in clar — verificat prin scanare la nivel de test. - VIN/nr inmatriculare se logheaza doar partial (ex. ultimele 4) sau hash scurt.
- Test "fuzz": evenimente cu chei sensibile in
context→ toate mascate.
- Niciun rand
- Verificare E2E: provoaca eroare cu creds reale → cauta parola in
app_events+ fisier → 0 hits.
US-008: Retentie / purjare jurnal (GDPR)
Ca responsabil GDPR vreau ca jurnalul sa se auto-stearga dupa o perioada
pentru ca retentia PII e limitata (acelasi mecanism ca submissions/import_batches).
- Depinde de: US-003
- Fisiere:
app/worker/__main__.py(purge_expiredextins peapp_events),app/schema.sql(purge_after),tests/test_jurnal_retentie.py - Test intai (RED):
tests/test_jurnal_retentie.py—test_app_events_primesc_purge_after,test_purjare_sterge_evenimente_expirate - Acceptance criteria:
- Fiecare rand
app_eventsprimestepurge_after = now + 90 zile(AUTOPASS_LOG_RETENTION_DAYS, default 90 — decizie §5). - Purjarea orara existenta (T16) sterge si
app_eventsexpirate. - Logul text foloseste
RotatingFileHandlerin aplicatie (rotatie pe dimensiune, N fisiere de backup) — decizie §5; nu depindem de deploy pentru rotatie.
- Fiecare rand
- Verificare E2E: insereaza eveniment cu
purge_afterin trecut → rulează purjarea → dispare.
US-009: Backend — sterge + re-pune in coada randuri ne-sent (helper)
Ca operator vreau sa pot sterge sau re-pune in coada o trimitere blocata
pentru ca un rand error (creds gresite) ramane altfel permanent si nereparabil.
- Depinde de: —
- Fisiere:
app/submissions_admin.py(modul nou:delete_submission,requeue_submission),tests/test_submissions_admin.py - Test intai (RED):
tests/test_submissions_admin.py—test_sterge_rand_error_scoped,test_nu_sterge_sent_sau_sending,test_repune_error_devine_queued_reset_retry,test_repune_re_ruleaza_classify,test_scope_cross_account_404 - Acceptance criteria:
delete_submission(conn, account_id, sid)sterge randul DOAR daca eerror/needs_data/needs_mappingSI apartine contului; altfel ridica/Intoarce refuz (sent/sending → interzis, cross-account → inexistent).requeue_submission(conn, account_id, sid)mutaerror → queued, reseteazaretry_count=0,next_attempt_at=NULL,sending_since=NULL, re-ruleazaclassifype payload (poate ajungeneeds_data/needs_mappingdaca continutul cere).- Niciuna nu atinge
sent(audit) sausending(lease worker). - Ambele emit eveniment in jurnal (US-003):
submission_sters/submission_repus.
- Verificare E2E: helper apelat din shell pe submission 15 →
queued; pe unsent→ refuz.
US-010: API v1 — sterge + re-pune in coada
Ca integrator ROAAUTO vreau endpointuri pentru a curata/relua trimiteri blocate pentru ca softul propriu sa gestioneze coada fara interventie manuala in DB.
- Depinde de: US-009
- Fisiere:
app/api/v1/router.py(DELETE /v1/prezentari/{id},POST /v1/prezentari/{id}/repune),tests/test_api_lifecycle.py - Test intai (RED):
tests/test_api_lifecycle.py—test_delete_scoped_pe_cheie,test_delete_sent_403,test_repune_error_queued,test_repune_inexistent_404 - Acceptance criteria:
DELETE /v1/prezentari/{id}→ 200/204 pe randuri ne-sent ale contului cheii; 403 pesent/sending; 404 cross-account/inexistent (acelasi mesaj, ca B3).POST /v1/prezentari/{id}/repune→ randul devinequeued(peste helper US-009).- Scoped strict pe contul cheii API (nu se poate atinge alt cont).
- Verificare E2E: cu cheia contului 2,
POST .../15/repune→ 200; worker il re-trimite (creds corecte).
US-011: Web dashboard — butoane Sterge / Re-pune in coada
Ca operator in dashboard vreau butoane pe randurile blocate pentru ca sa le gestionez vizual, fara API/SQL.
- Depinde de: US-009
- Fisiere:
app/web/routes.py(rutePOST /trimitere/{id}/sterge,POST /trimitere/{id}/repune,POST /trimiteri/sterge-bulkcu CSRF + PRG),app/web/templates/_trimitere_detaliu.html+ lista Trimiteri (selectie),tests/test_web_lifecycle.py - Test intai (RED):
tests/test_web_lifecycle.py—test_buton_sterge_doar_pe_blocate,test_repune_din_ui_scoped_sesiune,test_csrf_enforce,test_bulk_sterge_doar_blocate_scoped - Acceptance criteria:
- Pe detaliul unui rand
error: buton "Re-pune in coada" + "Sterge" cu dialog de confirmare simpla (decizie §5). - Pe lista Trimiteri: selectie multipla + "Sterge selectate" (bulk), pe modelul panoului admin (PRD 5.5); actioneaza DOAR pe randuri blocate ale contului.
- Randuri
sent/sending: fara butoane si neselectabile pentru stergere (read-only). - Scoped pe sesiune (regula NULL→cont 1); CSRF enforce; PRG dupa actiune.
- Stil consistent cu corectia inline existenta + panoul admin bulk (PRD 5.5), AA light+dark.
- Pe detaliul unui rand
- Verificare E2E: browser HTMX → pe submission 15 "Re-pune in coada" → dispare din "error", reapare ca trimis dupa worker; "Sterge" → dispare din lista.
US-012: Dedup nu mai e blocat de un rand error
Ca integrator vreau ca o retrimitere a aceluiasi payload (dupa ce am corectat
parola) sa fie acceptata pentru ca azi un rand error cu aceeasi cheie o blocheaza tacit.
- Depinde de: — (atinge
app/api/v1/router.pyenqueue +import_router) - Fisiere:
app/api/v1/router.py(create_prezentari),app/api/v1/import_router.py(commit, daca aplica),tests/test_dedup_error.py - Test intai (RED):
tests/test_dedup_error.py—test_resubmit_peste_error_reactiveaza,test_resubmit_actualizeaza_creds_pe_reactivare,test_resubmit_peste_sent_ramane_deduped,test_resubmit_peste_queued_ramane_deduped - Acceptance criteria:
- La enqueue, daca randul existent cu aceeasi
idempotency_keyeerror: se RE-ACTIVEAZA acelasi rand (re-ruleazaclassify, actualizeazarar_creds_enccu creds-urile noi din cerere, resetretry_count/next_attempt_at), si raspunsul NU mai ededuped: trueci starea noua (ex.queued). - Pentru
sent/queued/sending: comportament neschimbat →deduped: true(nu cream dubluri, nu deranjam in-flight/trimise). needs_data/needs_mapping: ramandedupedla resubmit (decizie §5) — corectia se face exclusiv prin UI (corectia inline existenta), nu prin re-trimiterea payload-ului.- Invariantul UNIQUE(idempotency_key) ramane (re-folosim randul, nu inseram al doilea).
- La enqueue, daca randul existent cu aceeasi
- Verificare E2E: POST cu parola gresita →
error; re-POST acelasi payload cu parola corecta →queued(nudeduped); worker trimite →sent.
US-013: Retentie / purjare randuri ne-sent blocate
Ca responsabil GDPR vreau ca si trimiterile blocate sa se auto-stearga dupa o
perioada pentru ca altfel PII-ul lor ramane permanent (azi doar sent se purjeaza).
- Depinde de: —
- Fisiere:
app/worker/__main__.py(markseteazapurge_aftersi pe stari blocate;purge_expiredextins peerror/needs_*),tests/test_purge_blocate.py - Test intai (RED):
tests/test_purge_blocate.py—test_error_primeste_purge_after,test_purjare_sterge_error_expirat,test_sent_si_blocate_retentii_separate_daca_difera - Acceptance criteria:
- Randurile care intra in
error/needs_data/needs_mappingprimescpurge_after(AUTOPASS_BLOCKED_RETENTION_DAYS, default 30 zile — decizie §5, mai scurt decat 90zsent). - Purjarea orara (T16) sterge si randurile blocate expirate, nu doar
sent. - O re-activare (US-012) / re-pune in coada (US-009) reseteaza/curata
purge_after(randul redevine activ, nu mai e candidat la purjare imediat).
- Randurile care intra in
- Verificare E2E: rand
errorcupurge_afterin trecut → rulează purjarea → dispare.
US-014: "Necesita atentia ta" devine actionabil (link + identificare rand)
Ca operator vreau ca avertismentul de trimiteri blocate sa-mi spuna CARE prezentare
a esuat si sa ma duca la ea pentru ca azi arata doar un contor ("Eroare la trimitere (1)"),
fara VIN/id/link — nu pot actiona, iar banner-ul nu se stinge niciodata cat timp exista error.
- Depinde de: — (UI peste filtrul existent
/_fragments/submissions?status=); se imbina natural cu US-011 (butoane sterge/re-pune in coada) si US-013 (purjare → banner se stinge) - Fisiere:
app/web/templates/_status.html,app/web/routes.py(_render_status/ fragment status — expune si identificatorii randurilor blocate, nu doar contoare),tests/test_web_status_fragment.py - Test intai (RED):
tests/test_web_status_fragment.py—test_categorie_blocata_linkeaza_la_trimiteri_filtrate,test_status_arata_identificator_rand_blocat,test_scoped_pe_cont - Acceptance criteria:
- Fiecare categorie din "Necesita atentia ta" e link catre lista "Trimiteri"
filtrata pe acea stare (deep-link
?tab=...&status=erroretc.), scoped pe cont. - Sub fiecare categorie se afiseaza identificatorul randurilor blocate (VIN partial
- nr inmatriculare +
#id), cel putin pentru primele N, cu "...si inca M" daca sunt mai multe.
- nr inmatriculare +
- Banner-ul dispare cand nu mai exista randuri blocate (consecinta US-009/011/013: stergere / re-pune in coada / purjare → contor 0 → sectiunea nu se mai randeaza).
- Nimic nou expus fara scope (regula NULL→cont 1); PII doar partial (ca jurnalul, US-007).
- Fiecare categorie din "Necesita atentia ta" e link catre lista "Trimiteri"
filtrata pe acea stare (deep-link
- Verificare E2E: browser HTMX → "Eroare la trimitere (1)" arata
#15 WVW…0001 / B123ABCsi linkeaza in Trimiteri filtrat peerror; dupa re-pune in coada +sent, banner-ul dispare.
4. Riscuri
- Scriere DB pe calea fiecarei cereri (US-004) poate adauga latenta/contentie pe SQLite (WAL). Mitigare: insert minimal, best-effort, nivel filtrat; eveniment per cerere agregat (1 rand), nu per camp. De masurat la VERIFY.
- Scurgere PII e riscul central. Mitigare: redactare la SCRIERE (nu la afisare), testata adversarial (US-007); nimic din payload integral nu intra in jurnal.
- Volum jurnal poate umfla DB-ul. Mitigare: retentie + nivel (US-008),
INFOdefault. - Dublu canal divergent (DB vs fisier). Mitigare: un singur
log_eventca sursa unica (US-003), ca dry-run/erori la PRD 5.2/5.4 — imposibil de divergat. - Migrare schema
app_events. Mitigare: migrare defensiva idempotenta in_migrate(caaccounts.active/override_json). - US-012 schimba semantica
dedupedla enqueue. Risc: re-activare nedorita a unui rand trimis. Mitigare: re-activarea e strict peerror(nusent/queued/sending); teste explicite pe fiecare stare; UNIQUE(idempotency_key) garanteaza un singur rand per continut. - Re-pune in coada cu creds gresite (US-009/010/011): daca creds-urile contului sunt
inca gresite, randul re-intra in
error. Acceptat — actiunea nu garanteaza succesul, doar reda dreptul la o noua incercare; jurnalul (US-005) arata de ce a reesuat.
5. Decizii (rezolvate cu utilizatorul — poarta de aprobare PRD)
Rezolvate 2026-06-23. Sunt obligatorii pentru executie.
- Retentie jurnal: 90 zile (aliniat cu
submissions/import_batches). [US-008] - Tipuri de evenimente: lista extensibila, nu fixata acum —
tipe text liber documentat, adaugam tipuri pe parcurs fara migrare. [US-003] - Log text:
RotatingFileHandlerin aplicatie (rotatie pe dimensiune; nu depindem de deploy). [US-008] - Vizibilitate jurnal: non-admin vede DOAR evenimentele contului sau; adminul vede tot, cu filtru pe cont. [US-006]
- Resubmit peste blocate (US-012): doar
errorse re-activeaza (re-ruleaza classify- actualizeaza creds).
needs_data/needs_mappingramandeduped— corectia exclusiv prin UI (corectia inline existenta).sent/queued/sendingramandeduped(neschimbat).
- actualizeaza creds).
- Retentie randuri blocate (US-013): 30 zile (mai scurt decat cele 90 ale
sent; un blocat n-are valoare de audit ca o trimitere reusita). Configurabil prinAUTOPASS_BLOCKED_RETENTION_DAYS, default 30. - Stergere din UI (US-011): confirmare simpla (dialog) + actiune in bloc pe lista (selectie multipla + "Sterge selectate"), pe modelul panoului admin (PRD 5.5).
6. Valuri de executie (graful de dependente)
Val 1: [US-002 request_id] [US-003 logger+schema] ← fundatii, fisiere disjuncte → paralel
Val 2: [US-001 handler 500] [US-004 audit API] [US-005 audit worker] [US-007 redactare]
← deblocate de US-003 (+US-002)
Val 3: [US-006 tab Jurnal] [US-008 retentie] ← consuma datele/coloanele din Val 1-2
--- Lifecycle trimiteri blocate (independent de observabilitate; poate rula in paralel) ---
Val A: [US-009 helper sterge/repune] [US-012 dedup peste error] [US-013 retentie blocate]
← fisiere disjuncte, fara dependente
Val B: [US-010 API lifecycle] [US-011 UI lifecycle] [US-014 banner actionabil]
← deblocate de US-009 (US-014 indep., dar grupat cu UI)
Nota scope: 5 stories de lifecycle (US-009..US-013) in loc de 3 din schita initiala — regula proiectului separa backend + UI in stories distincte (helper / API / UI). Daca vrei livrare mai mica, US-010 (API) e optional pentru un MVP "doar dashboard".
Raport VERIFY
Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6. PASS/FAIL per criteriu, cu dovezi (output pytest citat, E2E pe RAR test). Lipseste pana la VERIFY.