Doua medii RAR configurabile per cont, fiecare cu bifa de activare si set propriu de credentiale. medii_disponibile=enabled AND creds deriva tot UX-ul. 13 stories / 6 valuri. Premisa verificata live: test/prod = sisteme separate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
41 KiB
PRD 5.20 — Medii RAR per cont (Testare / Productie): activare, credentiale, selectie per trimitere
Stare: aprobat
Proces complet:
docs/ROADMAP.md§5. Contract RAR (sursa de adevar):docs/api-rar-contract.md. Stare:draft -> aprobat -> in-executie -> verify-pass -> inchis.
1. Obiectiv
Trateaza Testare si Productie ca doua medii RAR configurabile per cont. Fiecare mediu are, independent: o bifa de activare si un set propriu de credentiale. Un mediu e disponibil pentru trimitere doar daca e activat SI are credentiale. Din disponibilitate decurge tot UX-ul: cand un singur mediu e disponibil totul merge acolo (fara selector); cand ambele sunt disponibile, apare selector la import + toggle in statusbar + alegere in API. Trimiterile arata mereu un badge cu mediul tinta. Scop: clientul declara real pe Productie, iar cine are si cont de test RAR isi poate testa integrarea pe Testare — fara redeploy si fara variabila globala de mediu.
Premisa verificata (2026-06-29, doua seturi reale): test si prod sunt sisteme RAR complet separate; un set de credentiale se autentifica pe exact unul (creds dev: test 200 / prod 401; creds client real: test 401 / prod 200). Deci 2 seturi de creds per cont; un cont prod-only NU poate trimite la test fara cont de test emis de RAR. Detaliu memorat: vezi memoria de proiect "rar-test-prod-creds-separate".
2. Non-Goals (anti scope-creep)
- NU eliminam
AUTOPASS_RAR_ENVglobal: ramane ancora de migrare + fallback pentru actiuni de sistem fara cont (ex. keepalive login). Per-submission are precedenta cand exista. - NU configuram base_url-uri din UI (raman in
config.py); NU adaugam un al treilea mediu. - NU gating pe plan/tier pentru Productie (decizie user: liber). „Guard-ul" e: Productie e tinta doar daca e activata + are creds, plus o confirmare unica la activarea Productie (constientizare L.142), NU per trimitere.
- NU schimbam masina de stari, backoff-ul, sau payload-ul
postPrezentare. - NU migram automat credentiale de prod ale clientilor — ei le introduc; migrarea doar muta creds-ul existent in slotul mediului sub care contul opera efectiv.
3. Cerinte transversale (reguli de derivare)
- REQ-DISP:
medii_disponibile(cont)= mediile din {test, prod} cuenabled=1SI creds prezente. Sursa unica de adevar pentru vizibilitatea selector/toggle si pentru validarea unei tinte cerute. - REQ-VIZ: selector la import + toggle in statusbar apar DOAR cand
len(medii_disponibile) >= 2. La 1 mediu, tinta e implicita (acel mediu), fara selector. La 0, trimiterea e blocata cu mesaj „configureaza credentiale RAR". - REQ-BADGE: orice trimitere afiseaza badge Test/Productie (chiar si la 1 mediu — claritate ca declari real).
- REQ-DEFAULT:
rar_env_default(cont)e mereu unul din mediile disponibile; cont client nou =prod. Daca default-ul nu mai e disponibil (mediu dezactivat), cade pe singurul disponibil; daca 0 disponibile -> nicio tinta. - REQ-CONF: trimiterea pe Productie nu cere confirmare per-rand; constientizarea vine din badge + o confirmare UNICA la activarea mediului Productie in configurare.
4. Stories atomice
Backend + UI pentru acelasi comportament = stories separate.
Fisiere+Depinde decomplete.
US-001: Schema — medii per cont (activare + creds) + env pe submission
Ca sistem vreau sa stochez per cont activarea si credentialele fiecarui mediu, default-ul, si env-ul tinta pe fiecare submission pentru ca test si prod sunt sisteme separate cu credentiale separate.
- Depinde de: —
- Fisiere:
app/schema.sql,app/db.py(migrare idempotenta),tests/test_schema_migrate.py - Test intai (RED):
tests/test_schema_migrate.py—test_coloane_medii_pe_cont,test_default_client_prod_on_test_off,test_migrare_creds_in_slotul_env_global,test_submissions_rar_env - Acceptance criteria:
accounts:rar_test_enabled INTEGER NOT NULL DEFAULT 0,rar_prod_enabled INTEGER NOT NULL DEFAULT 1(ambele CHECK IN (0,1));rar_creds_test_enc TEXT,rar_creds_prod_enc TEXT;rar_env_default TEXT NOT NULL DEFAULT 'prod' CHECK (rar_env_default IN ('test','prod'))submissions.rar_env TEXT NOT NULL DEFAULT 'test' CHECK (rar_env IN ('test','prod'))- Migrare existenti (NU presupune env-ul):
rar_creds_enc-> slotulAUTOPASS_RAR_ENVglobal de la migrare; seteazaenabled=1DOAR pe mediul cu creds;rar_env_default= acel mediu. Conturi fara creds: raman pe default-urile coloanei (prod on / test off). Coloana veche RAMANE acum (dropul e in US-013, dupa ce toate citirile trec pe per-env) - (AUTO-FIX G — CRITIC, amendament AC) Backfill
submissions.rar_envEXISTENT dinAUTOPASS_RAR_ENVglobal, NU lasa peDEFAULT 'test'. Un rand prod pre-migrare etichetat 'test' -> US-006 reconciliaza contra endpoint TEST -> no-match -> re-send prod = DUPLICAT REAL IREVERSIBIL.DEFAULT 'test'ramane doar plasa pentru randuri net-noi (fiecare INSERT din US-004/005/009 seteazarar_envexplicit) - (AUTO-FIX E4/3) Recompute
idempotency_keypentru randurile existente la forma env-aware (build_key(account_id, canon, rar_env)curar_env-ul backfill-at), ca lookup-urile de dedup (API + import) sa nu rateze randuri legacy -> altfel re-POST = duplicat test_submissions_rar_envasserteaza ca un rand PRE-migrare ajunge cu env-ul global (NU 'test') si reconciliaza contra endpointului corect- migrare idempotenta pe DB existent, fara pierdere de date
python3 -m pytest tests/test_schema_migrate.py -qPASS
- Verificare E2E: DB pre-migrare cu
AUTOPASS_RAR_ENV=test-> creds aterizeaza inrar_creds_test_enc,rar_test_enabled=1,rar_env_default='test'.
US-002: Logica de disponibilitate si default efectiv
Ca sistem vreau un helper unic care intoarce mediile disponibile si default-ul efectiv al unui cont pentru ca vizibilitatea UI, API-ul si worker-ul sa decida identic (REQ-DISP/REQ-DEFAULT).
- Depinde de: US-001
- Fisiere:
app/rar_env.py(nou) sauapp/mapping.py,tests/test_rar_env_disponibil.py - Test intai (RED):
tests/test_rar_env_disponibil.py—test_doar_prod_cu_creds,test_ambele,test_zero_cand_lipsesc_creds,test_default_cade_pe_singurul_disponibil,test_enabled_fara_creds_nu_e_disponibil - Acceptance criteria:
medii_disponibile(account) -> list[str](subset din ['test','prod']) = enabled AND creds prezenterar_env_efectiv(account) -> 'test'|'prod'|Noneaplica REQ-DEFAULTpython3 -m pytest tests/test_rar_env_disponibil.py -qPASS
- Verificare E2E: —
US-003: Idempotenta include rar_env
Ca sistem vreau ca build_key sa incorporeze rar_env pentru ca aceeasi prezentare la test si apoi
la prod sunt doua trimiteri reale distincte, nu un duplicat.
- Depinde de: —
- Fisiere:
app/idempotency.py,tests/test_idempotency.py - Test intai (RED):
tests/test_idempotency.py—test_key_difera_intre_test_si_prod,test_key_stabil_pe_env - Acceptance criteria:
build_key(account_id, canon, rar_env)-> chei diferite test vs prod pe acelasi continut; stabil pe re-apel- toate apelurile (
router.py,import_router.py) trec env-ul rezolvat python3 -m pytest tests/test_idempotency.py -qPASS
- Verificare E2E: —
US-004: Rezolvare tinta la ingestie (cerere > default cont) + respinge tinta indisponibila
Ca sistem vreau sa decid env-ul unui submission si sa resping tintele indisponibile pentru ca o tinta fara mediu activ/creds nu trebuie sa intre in coada.
- Depinde de: US-002
- Fisiere:
app/validation.py,app/mapping.py,tests/test_rar_env_resolve.py - Test intai (RED):
tests/test_rar_env_resolve.py—test_cerere_castiga,test_fallback_default_cont,test_tinta_indisponibila_respinsa,test_valoare_invalida - Acceptance criteria:
- precedenta: valoare ceruta (daca e in
medii_disponibile) >rar_env_efectiv(cont) - tinta ceruta dar indisponibila -> eroare clara („mediul X nu e activat / fara credentiale"), fara enqueue
- valoare invalida (≠ test/prod) -> eroare de validare, fara fallback silentios
python3 -m pytest tests/test_rar_env_resolve.py -qPASS
- precedenta: valoare ceruta (daca e in
- Verificare E2E: —
US-005: API — camp rar_target pe POST /v1/prezentari si /valideaza
Ca integrator ROAAUTO vreau sa pot preciza rar_target, cu default = default-ul contului meu pentru ca
sa aleg unde declar fara sa stiu env-ul global.
- Depinde de: US-003, US-004
- Fisiere:
app/api/v1/router.py,app/models.py,tests/test_api_rar_target.py - Test intai (RED):
tests/test_api_rar_target.py—test_default_din_cont_cand_lipseste,test_target_explicit,test_target_indisponibil_respins,test_get_ecou_rar_env,test_valoare_invalida_422 - Acceptance criteria:
- camp optional
rar_target: "test"|"prod"pePOST /v1/prezentarisi/valideaza - absent ->
rar_env_efectiv(cont)(pt client prod-only =prod) - tinta indisponibila -> raspuns clar, fara enqueue;
SubmissionResult+ GET ecou-iescrar_env - valoare invalida -> 422 fara echo de input (handler global pastrat)
python3 -m pytest tests/test_api_rar_target.py -qPASS
- camp optional
- Verificare E2E:
POST /v1/prezentarifararar_targetpe un cont prod-only -> submission env=prod.
US-006: Worker — sesiuni si trimitere per (cont, env)
Ca worker vreau login/JWT separat per (account_id, rar_env), cu base_url + creds corecte per submission
pentru ca test si prod sunt sisteme RAR diferite.
- Depinde de: US-001
- Fisiere:
app/worker/__main__.py(AccountSessions),app/rar_client.py(base_url per env),app/reconcile.py,tests/test_worker_rar_env.py - Test intai (RED):
tests/test_worker_rar_env.py—test_sesiune_separata_per_env,test_base_url_dupa_submission,test_creds_din_slotul_env,test_reconcile_pe_env_corect - Acceptance criteria:
- cheia cache sesiune =
(account_id, rar_env); JWT/keepalive/last_rar_login_ok per env RarClientprimeste env/base_url explicit (nu doarsettings.rar_base_url)- creds alese: submission efemere ->
accounts.rar_creds_{env}_enc; lipsa -> blocaj clar (nu trimite) - reconcilierea cauta in
finalizatepe endpoint-ulsubmission.rar_env - purjarea atinge DOAR
submissions.rar_creds_enc, NUaccounts.rar_creds_{env}_enc python3 -m pytest tests/test_worker_rar_env.py -qPASS
- cheia cache sesiune =
- Verificare E2E: doua submission-uri (test + prod, creds prezente) -> doua login-uri distincte in jurnal.
US-007: Validare login pe env-ul ales (signup / preview / test integrare)
Ca sistem vreau ca validarea credentialelor sa loveasca mediul caruia ii apartin pentru ca o parola prod nu se valideaza contra RAR test si invers (confirmat: 401 incrucisat).
- Depinde de: US-002
- Fisiere:
app/web/routes.py,app/rar_client.py,app/web/templates/_integrare.html,tests/test_validare_env.py - Test intai (RED):
tests/test_validare_env.py—test_valideaza_pe_env_creds,test_mesaj_distinge_env - Acceptance criteria:
- validarea (signup, „testeaza integrarea", preview) foloseste env-ul setului de creds verificat
- mesaj distinct „creds invalide pe TESTARE" vs „pe PRODUCTIE"
python3 -m pytest tests/test_validare_env.py -qPASS
- Verificare E2E: in UI „testeaza integrarea" cu creds prod -> login pe endpoint prod.
US-008: Configurare cont — doua medii (bifa activare + creds), default, confirmare prod
Ca titular de cont vreau sa activez fiecare mediu, sa-i introduc credentialele si sa aleg default-ul pentru ca vreau sa controlez unde se poate trimite si unde merge implicit.
- Depinde de: US-001, US-007
- Fisiere:
app/web/routes.py,app/web/templates/_cont.html,app/crypto.py(refolosit),tests/test_cont_medii.py - Test intai (RED):
tests/test_cont_medii.py—test_activeaza_si_salveaza_creds_per_env,test_default_doar_dintre_disponibile,test_activare_prod_cere_confirmare,test_creds_criptate_fara_echo - Acceptance criteria:
- doua sectiuni „Testare" si „Productie": fiecare cu bifa Activeaza + campuri email/parola; default client = Productie bifat, Testare nebifat
- la salvare, creds-ul fiecarui mediu activat e validat prin login pe acel env (US-007); invalid -> nu se marcheaza disponibil
- selectorul de default ofera DOAR mediile disponibile; nu poti seta default un mediu indisponibil
- activarea mediului Productie cere o confirmare unica „Inteleg ca trimiterile pe Productie sunt declaratii reale (L.142)"
- creds criptate Fernet in
rar_creds_{env}_enc, niciodata reflectate inapoi in pagina python3 -m pytest tests/test_cont_medii.py -qPASS
- Verificare E2E: activez Testare + creds valide si Productie + creds invalide -> doar Testare devine disponibil.
US-009: Import web — selector mediu conditionat de disponibilitate
Ca operator vreau sa aleg mediul la import doar cand am ≥2 disponibile, pre-bifat pe default pentru ca la un singur mediu alegerea e inutila.
- Depinde de: US-002, US-004
- Fisiere:
app/import_router.py,app/import_parse.py,app/web/templates/_upload.html,_preview_import.html,tests/test_import_rar_env.py - Test intai (RED):
tests/test_import_rar_env.py—test_selector_ascuns_la_un_mediu,test_selector_prezent_si_prebifat_la_doua,test_commit_seteaza_env_pe_submissions - Acceptance criteria:
- selector Test/Prod apare DOAR daca
len(medii_disponibile) >= 2; initial =rar_env_efectiv - la 1 mediu: fara selector, toate randurile primesc acel mediu
- la commit, toate submission-urile lotului primesc
rar_envales python3 -m pytest tests/test_import_rar_env.py -qPASS
- selector Test/Prod apare DOAR daca
- Verificare E2E: cont prod-only -> import fara selector, submissions env=prod; cont cu ambele -> selector pre-bifat.
US-010: Badge mediu in liste, preview, jurnal, audit + ecou API
Ca utilizator vreau sa vad pe fiecare trimitere mediul tinta pentru ca sa nu confund testul cu realul.
- Depinde de: US-001
- Fisiere:
app/web/templates/_submissions.html,_coada.html,_trimitere_detaliu.html,_preview_rand.html,_jurnal.html,app/web/routes.py(audit export),app/api/v1/router.py(GET),tests/test_badge_rar_env.py - Test intai (RED):
tests/test_badge_rar_env.py—test_badge_in_lista,test_audit_contine_rar_env,test_get_ecou_rar_env - Acceptance criteria:
- badge vizibil (Test vs Productie, culori distincte) in lista, preview rand, detaliu, jurnal
rar_envin audit export si inGET /v1/prezentari(/{id})python3 -m pytest tests/test_badge_rar_env.py -qPASS
- Verificare E2E: rand prod -> badge „Productie"; export audit contine coloana.
US-011: Statusbar — indicator mediu + toggle conditionat
Ca operator vreau sa vad in statusbar mediul default si sa-l pot schimba cand am ≥2 medii pentru ca sa stiu mereu unde trimit si sa comut rapid.
- Depinde de: US-002, US-008
- Fisiere:
app/web/templates/_status.html,base.html,app/web/routes.py(ruta toggle account-scoped),tests/test_statusbar_env.py - Test intai (RED):
tests/test_statusbar_env.py—test_afiseaza_env_default,test_toggle_doar_la_doua_medii,test_toggle_schimba_default - Acceptance criteria:
- statusbar afiseaza mediul default al contului logat (Test/Productie), distinct vizual
- toggle apare DOAR la
len(medii_disponibile) >= 2; comutarea schimbarar_env_default(HTMX, fara reload) - la 1 mediu: doar eticheta statica
python3 -m pytest tests/test_statusbar_env.py -qPASS
- Verificare E2E: cont cu ambele -> click statusbar schimba default; cont prod-only -> eticheta fixa „Productie".
US-012: Audit + e2e pe medii
Ca lead vreau evenimente de audit la activare mediu / schimbare default / blocaj tinta, plus teste e2e pentru ca orice atingere a mediului Productie trebuie trasabila.
- Depinde de: US-005, US-006, US-009, US-011
- Fisiere:
app/audit.py/log_event,tests/test_e2e_rar_env.py - Test intai (RED):
tests/test_e2e_rar_env.py—test_lant_import_pana_la_queued,test_activare_prod_logata,test_tinta_indisponibila_blocata_si_logata - Acceptance criteria:
- audit la: activare/dezactivare mediu, schimbare
rar_env_default, blocaj tinta indisponibila - e2e (TestClient + SQLite temporar) acopera import->queued cu env corect, ambele cai
python3 -m pytest tests/test_e2e_rar_env.py -qPASS
- audit la: activare/dezactivare mediu, schimbare
- Verificare E2E: jurnal arata „mediu Productie activat" + „default schimbat" cu cont + timestamp.
US-013: Retragerea accounts.rar_creds_enc (toate citirile -> per-env, apoi DROP)
Ca sistem vreau ca toate cele ~40 de locuri care citesc accounts.rar_creds_enc sa treaca pe coloanele
per-mediu si apoi sa sterg coloana veche pentru ca modelul per-env sa fie sursa unica, fara schema dubla.
- Depinde de: US-005, US-006, US-008 (consumatorii principali deja pe per-env)
- Fisiere:
app/worker/__main__.py(fallback + bucla keepalive „toate conturile cu creds"),app/web/routes.py(indicatoriiare_creds),app/api/v1/integrare_router.py(are_creds_rar),app/api/v1/router.py(POST /v1/conturi/rar-credsdevine env-aware),app/accounts.py(purge la stergere cont),app/db.py(DROP cu garda),app/models.py,tests/test_retragere_creds_enc.py - Test intai (RED):
tests/test_retragere_creds_enc.py—test_niciun_read_pe_coloana_veche,test_conturi_rar_creds_env_aware,test_are_creds_pe_per_env,test_drop_cu_garda_blocat_daca_lipsa_copiere - Acceptance criteria:
- worker fallback + keepalive citesc
rar_creds_{env}_enc(per env), nu coloana veche are_creds(web) +are_creds_rar(integrare) devin per-mediu („are creds pe Testare/Productie")POST /v1/conturi/rar-credsprimeste mediul (rar_target/env) si scrie in slotul corect — schimbare de contract API, documentata indocs/api-rar-contract.md- purjarea la stergere cont (
accounts.py) sterge ambele sloturi per-env - DROP cu garda: migrarea verifica intai ca fiecare
rar_creds_encnon-null a aterizat intr-un slot per-env (assert), apoiALTER TABLE accounts DROP COLUMN rar_creds_enc(SQLite 3.45 OK); verificare esuata -> NU dropa, ridica eroare (fail-safe) - (AUTO-FIX 6a — CRITIC) Elimina ATOMIC blocul
ADD COLUMN rar_creds_encdindb.py:77-78in aceeasi migrare cu DROP-ul. Altfel urmatorul boot vede coloana absenta si o re-ADD goala -> ping-pong perpetuu, garda se rupe. Garda e one-way: dropeaza doar cand sloturile per-env sunt populate SI coloana inca exista - (AUTO-FIX 6b — HIGH) DROP-ul nu crapa boot-ul:
init_db/_migrateruleaza la fiecare pornire a ambelor procese; unDROP COLUMNcare arunca (SQLite < 3.35 / assert garda esuat) propaga -> API + worker crash-loop. Prinde + degradeaza (log + lasa coloana pe loc), NU arunca. Asserteazasqlite_version() >= 3.35(verifica SQLite din imaginea Docker, nu doar dev box) si sare drop-ul gracios sub acel prag - (AUTO-FIX 6c — HIGH) Re-ruleaza backfill old->new IMEDIAT inainte de assert: creds setate via
POST /v1/conturi/rar-credsintre deploy-ul US-001 si US-013 aterizeaza doar in coloana veche; copiaza-le in slotul per-env (ancora globala) inainte de garda, altfel garda blocheaza drop-ul la nesfarsit - (AUTO-FIX 6d) Verificare prin
PRAGMA table_info(accounts)carar_creds_enclipseste, NU doar prin grep (ambele coloane —accountssisubmissions— au acelasi nume; purjarea worker-ului ramane pesubmissions.rar_creds_enc) grep -rn "rar_creds_enc" app/nu mai gaseste citiri peaccounts(doarsubmissions.rar_creds_encramane)python3 -m pytest tests/test_retragere_creds_enc.py -qPASS
- worker fallback + keepalive citesc
- Verificare E2E: dupa migrare,
PRAGMA table_info(accounts)nu mai continerar_creds_enc; fluxul de cont (salvare creds, worker trimite) functioneaza pe per-env.
5. Riscuri
- Trimitere reala accidentala (FINALIZATA terminal, L.142): atenuat prin badge omniprezent + Productie disponibil doar dupa activare explicita + creds + confirmare unica la activare. NU exista anulare la RAR.
- Default invalid dupa dezactivare mediu: REQ-DEFAULT recalculeaza; teste US-002 acopera caderea pe disponibil.
- Migrare ambigua (CONFIRMAT):
rar_creds_encpoate fi test SAU prod; migrarea aterizeaza in slotulAUTOPASS_RAR_ENVglobal + activeaza doar acel mediu. De validat pe DB-ul real inainte de deploy. - Client prod-only nu poate testa: corect by design; UI explica explicit (nu „creds invalide"), nu ofera Testare fara creds test.
- Idempotenta: schimbarea cheii (US-003) cere ca TOATE apelurile sa treaca env-ul; grep dupa
build_key+ teste. - Retragere
rar_creds_enc(US-013): ~40 citiri + endpoint APIPOST /v1/conturi/rar-creds(contract). Blast radius mare, dar single-release e mai curat decat schema dubla. DROP cu garda (assert copiere) = fara pierdere de date; produsul e in TESTE (putine conturi reale). Recuperarea via coloana veche dispare dupa DROP — acceptat.
6. Intrebari deschise — REZOLVATE (user 2026-06-29)
- Default API = default-ul contului (NU „test" hardcodat), fiindca clientii sunt prod-only. CONFIRMAT.
- Activare implicita cont nou = Productie on / Testare off; contul operator setat manual pe Testare. CONFIRMAT.
- Confirmare Productie = o data, la activarea mediului in configurare (nu per trimitere). CONFIRMAT.
rar_creds_encvechi = se STERGE in acest PRD (US-013), nu in 5.2x. DROP cu garda (assert copiere), toate citirile mutate pe per-env, endpointPOST /v1/conturi/rar-credsdevine env-aware. CONFIRMAT.
7. Valuri de executie (graful de dependente)
Val 1: [US-001] [US-003] ← schema + idempotenta (fisiere distincte) → paralel
Val 2: [US-002] ← deblocat de US-001
Val 3: [US-004] [US-006] [US-007] ← rezolvare ingestie / worker / validare → paralel
Val 4: [US-005] [US-008] [US-009] [US-010] ← API / config cont / import / badge → paralel
Val 5: [US-011] ← statusbar (depinde de US-008)
Val 6: [US-012] [US-013] ← audit + e2e; retragere rar_creds_enc + DROP (depind de tot)
Raport VERIFY
Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6. PASS/FAIL per criteriu, cu dovezi. Lipseste pana la VERIFY.
/autoplan Review (2026-06-29, commit 7371c37)
Voci: Claude (primar) + Claude subagent (independent). Codex indisponibil (usage limit, revine 18 iul) -> mod [subagent-only]. Poarta premisa: user a ales "Build full per-account multi-env (as planned)" — premisa de baza (sisteme separate) verificata live; nevoia de dashboard unic justifica per-cont peste 2 deployment-uri pinned.
Auto-fixuri (corectitudine/siguranta — incorporate in stories)
| # | Story | Gap (gasit de) | Fix incorporat | Principiu |
|---|---|---|---|---|
| G | US-001 | CRITIC (subagent): migrarea backfill-eaza creds dar NU submissions.rar_env existent; randuri prod pre-migrare cad pe DEFAULT 'test' -> US-006 reconciliaza contra endpoint TEST -> no-match -> re-send prod = duplicat real ireversibil |
Migrarea backfill-eaza submissions.rar_env din AUTOPASS_RAR_ENV global (DEFAULT 'test' doar pentru randuri net-noi). Test: rand prod pre-migrare reconciliaza contra endpoint prod |
P1 completeness + siguranta |
| L | US-005/US-013 (NU US-006 — eng finding 5: write-back e in router.py, pe care US-006 nu-l atinge) |
HIGH (ambele voci, router.py:250): write-back creds efemere API -> accounts.rar_creds_enc durabil nu e rutat pe slotul submission.rar_env |
Write-back tinteste accounts.rar_creds_{submission.rar_env}_enc + test. Plus: nu auto-propaga creds API NEVALIDATE in slotul durabil per-env (ar putea clobber-i un slot login-validat); propaga doar dupa login reusit |
P1 |
| K | US-013 | HIGH (subagent): POST /v1/conturi/rar-creds e contract extern; env-aware in-place = breaking |
Endpoint aditiv: param env optional, default = default cont; apelanti vechi neatinsi. (Independent de decizia DROP) |
P5 explicit + back-compat |
| M2 | US-013 | MEDIUM (Claude): _keepalive_target alege un cont fara notiune de env dupa per-env |
Keepalive foloseste ancora globala AUTOPASS_RAR_ENV + un cont cu creds in slotul acelui env |
P5 |
| M3 | US-003 | MEDIUM (Claude): _already_sent_lookup (import_router.py:369) are dual-lookup legacy; adaugarea env in cheie cere extinderea lui, nu doar a parametrului |
US-003 extinde dual-lookup (cheie noua env-aware + fallback legacy) | P1 |
| D | US-001 | HIGH (subagent): corectitudinea migrarii e "de validat manual"; trebuie poarta testata | Script de audit pre-migrare (raporteaza slot-ul atribuit) + assert DROP-cu-garda existent ca poarta, nu nota manuala | P1 |
| M | US-012 | MEDIUM (subagent): niciun test live dual-env; riscul dominant (rutare gresita env) e exact ce SQLite nu prinde | Test live opt-in dual-env (extinde test_live_rar): 1 rand test + 1 prod -> 2 login-uri, 2 endpoint-uri, badge corecte, reconciliere pe env corect |
P1 |
| backup | US-013 | MEDIUM (Claude): "recovery via coloana veche dispare dupa DROP — acceptat" | Inainte de DROP, dump coloana veche criptata intr-un backup timestamped (recuperare supravietuieste DROP) | P2 boil-lake |
Decizii user la poarta finala (REZOLVATE 2026-06-29) — APROBAT
- A (DROP US-013) -> PASTREAZA single-release. User: "aplicatia e doar in teste, nu folosita de clienti" -> blast radius mic, rollback-ul conteaza mai putin. Decizia §6 ramane. Garzile 6a/6b/6c sunt obligatorii in AC US-013 (eliminare atomica bloc ADD, catch+degrade fara boot-crash, re-backfill interim) + backup criptat inainte de DROP. NU se amana.
- J/H1 (interlock prod) -> doar butonul de commit colorat (F8), FARA modal. REQ-CONF ramane. Lantul: bifa activare (o data) + badge "fierbinte" + buton "Declară la PRODUCȚIE (real)". Fara confirmare per-commit (evita oboseala de click; clientii prod-only oricum n-au selector).
- H (fallback default) -> doar toast zgomotos (F5), FARA re-confirmare. REQ-DEFAULT auto-fallback ramane; toast-ul "Mediul implicit a trecut pe X" face flip-ul vizibil. Fara gate suplimentar.
Taste (recomandari acceptate — fara override)
- T1: token dedicat
--prod(brick) pentru badge-ul Productie. T2:rar_envca nume unic input+output (scoaterar_target/env).
Taste decisions (auto-decise cu recomandare — override la poarta)
- T1 — token culoare Productie: rosu (
--err) se ciocneste cu erorile, amber (--warn) cu badge-ul legacy. Recomandat: token dedicat--prod(brick inchis) SAU--accentplin. (design F2) - T2 — nume camp request: recomandat
rar_envpeste tot (un singur nume input+output), scoaterar_target/env. (DX F1)
Teme cross-fază (semnal de incredere ridicat — aparut independent in 2+ faze)
- Siguranta declaratiei reale ireversibile — TOATE 4 fazele (CEO G/H1/J, Design F1/F8/F10, Eng 1b/3/G, DX F2/F3/F4). Semnalul dominant: badge + interlock + discoverability + rutare env corecta converg pe "nu declara real din greseala".
- Flip silentios al mediului default — CEO-H, Design-F5, DX-F3 (3 faze). Fa flip-ul zgomotos + nu auto-promova prod silentios.
- Risc DROP US-013 — CEO-A, Eng 6a/6b/6c (2 faze). Intareste amanarea DROP-ului.
- Ambiguitate spec/nume care musca implementer-ul — Design-F14, Eng-4a, DX-F1/F7. Auto-fixurile TREBUIE sa intre in AC + contract inainte de implementare.
NOT in scope (confirmat)
Eliminarea ancorei globale AUTOPASS_RAR_ENV; base_url din UI; al treilea mediu; gating plan/tier pe prod; schimbari masina-stari/backoff/payload; auto-migrare creds prod client. (PRD §2)
Ce exista deja (leverage)
crypto.py Fernet (creds per-env), AccountSessions (re-key (cont,env)), RarClient (primeste settings; +param env), config.rar_base_url_test/prod (deja prezent), build_key (+param), account_scope_clause. Fara infra noua.
Auto-fixuri DESIGN (structurale — incorporate in stories)
Voci: Claude (primar) + Claude subagent. Scorecard: 1 CRITIC, 7 HIGH, 5 MEDIUM, toate CONFIRMED.
| # | Story | Gap | Fix incorporat | Sev |
|---|---|---|---|---|
| F1 | US-010 | CRITIC: "culori distincte" e singura spec a singurului guard vizual contra riscului dominant | Badge normativ: Productie = fill plin, saturat, text alb, iconita + cuvant complet UPPERCASE cu diacritice ("PRODUCȚIE"); Testare = outline/tint linistit (muted/accent), receding. Asimetria de greutate ESTE designul | CRITIC |
| F2 | US-010 | HIGH: rosu (--err) rezervat erorilor, amber (--warn) ocupat de .badge-env legacy + needs_* |
Token dedicat --prod (ex. brick #B4452F) SAU --accent plin pentru Productie; hex/token scris in AC, nu improvizat per template. (taste: hexul exact -> poarta) |
HIGH |
| F3/F12 | US-010 | HIGH: "Test/Testare/prod/PRODUCTIE" folosite interschimbabil; bypass labels.py |
labels.py adaugat in Fisiere: ETICHETE_ENV + eticheta_env(env)->(text,css) (oglindeste eticheta_scurta). Productie UPPERCASE+diacritice, Testare title-case; clase .badge-prod/.badge-test definite o data in base.html langa .sugg-sursa |
HIGH |
| F11 | US-011 | HIGH: .badge-env EXISTENT in header arata AUTOPASS_RAR_ENV global -> dupa 5.20 e semantic gresit; doua indicatoare env cu surse diferite in acelasi viewport |
US-011 retrage/repurpune header .badge-env (preferat: scos pentru user logat, inlocuit de indicatorul account-scoped din statusbar). NU coexista doua surse de adevar |
HIGH |
| F4 | US-009 | HIGH: starea 0-medii e numita dar nedesignata; blocaj la commit (dupa munca) = calea minima | Blocaj la UPLOAD (nu commit): banner --warn (refoloseste pattern "Cont in asteptare", _status.html:8) + CTA link ?tab=cont, inainte de drop-zone |
HIGH |
| F5 | US-011 | HIGH: schimbarea silentioasa a default-ului (mediu dezactivat) nu are UI -> target real/test comuta fara ca userul sa stie | Toast explicit (componenta #toast exista) la schimbarea rar_env_default ca efect al disponibilitatii: "Mediul implicit a trecut pe X". Leaga de CEO-H |
HIGH |
| F8 | US-009 | HIGH: o bifa la activare apoi nimic = sub-avertizare; modalul per-trimitere a fost respins (REQ-CONF) | Butonul de commit POARTA greutatea cand target=Productie: "Declară la PRODUCȚIE (real)" + culoarea Productie (FARA modal, FARA click extra -> nu incalca REQ-CONF). Copy bifa activare: adauga ireversibilitatea ("declarații oficiale, finale și fără anulare") | HIGH |
| F6/F7 | US-008/US-011 | MEDIUM: stari loading/error pt toggle HTMX + validare creds la RAR nespecificate; stare per-sectiune (activat-fara-creds-valide) | toggle: hx-indicator + disabled in zbor, pe esec NU schimba default + eroare; US-008 validare creds arata htmx-indicator ("se verifica la RAR…") + esec in .banner cu copy per-env (US-007); fiecare sectiune arata 3 stari: dezactivat / activat-fara-creds / disponibil |
MEDIUM |
| F9/F10 | US-009 | MEDIUM/HIGH: selectorul absent la 1 mediu = env invizibil la import; default pre-bifat prod la prima trimitere | Mereu randeaza un indicator env la import (eticheta statica la 1 mediu, toggle la >=2, ACEEASI pozitie). Prod pre-bifat e sigur DOAR daca F8+F9 livreaza impreuna — legate explicit in AC | HIGH |
| F13 | US-010 | MEDIUM: sa nu forkeze un badge structural nou | Refoloseste idiomul .sugg-sursa (10px, weight 700, tint+border) pt Testare; Productie = aceeasi geometrie dar fill plin+alb+icon (spargerea e semnalul) |
MEDIUM |
Auto-fixuri ENG (corectitudine/deploy — incorporate in stories)
Voci: Claude (primar) + Claude subagent (verificat contra codului real). Meta (eng 4a): toate auto-fixurile de mai jos sunt NORMATIVE si trebuie sa intre in AC-ul story-urilor inainte de implementare — un implementer care urmeaza AC-ul literal, fara ele, livreaza bug-urile critice. G + 6a deja imbinate in AC US-001/US-013.
| # | Story | Gap (vs cod real) | Fix | Sev |
|---|---|---|---|---|
| E1/1a | US-006 | get_token purjeaza submissions.rar_creds_enc WHERE account_id=? -> dupa re-key, login TEST sterge creds efemere ale submission-urilor PROD ale contului -> prod blocat |
WHERE account_id=? AND rar_env=? + test test_purge_creds_doar_pe_env |
HIGH |
| 1b/E6 | US-006 | recover_orphans filtreaza doar pe account_id; iterat per sesiune (cont,env) reconciliaza orfanii prod contra endpoint TEST -> no-match -> re-POST prod = DUPLICAT real |
+rar_env in WHERE; apelat per (cont,env) din active(); test orfan env A nereconciliat contra env B |
HIGH/CRITIC |
| 3/E4 | US-003 | API channel (router.py:223) NU are dual-lookup; re-POST al unui rand pre-5.20 cu cheie env-aware rateaza randul legacy -> duplicat. Import dual-lookup ignora env-ul randului matchuit |
Recompute-keys la migrare (US-001, vezi acolo) acopera ambele canale uniform; daca pastrezi dual-lookup, exista si in router.py SI gate pe matched_row.rar_env==target_env |
HIGH |
| 1c/E8 | US-006 | claim_one nu selecteaza s.rar_env -> worker nu poate alege cheia sesiune/base_url/slot |
AC explicit: claim selecteaza + propaga rar_env in dict-ul claimed |
MEDIUM |
| 1d | US-006/US-001 | worker_heartbeat e un singur rand global (WHERE id=1); US-006 cere last_rar_login_ok PER env dar US-001 nu adauga schema per-env -> neimplementabil ca scris |
Decizie: pastreaza heartbeat global (JWT/sesiune per env e suficient), scoate "per env" din AC US-006; SAU adauga coloana in US-001. Recomandat: global | MEDIUM |
| 1e | US-006 (doc) | _refresh_nomenclator upsert intr-un nomenclator_rar env-less la fiecare login; login test suprascrie cu coduri test, prod cu prod -> un cod valid pe prod poate fi respins la ingestie daca ultimul refresh a fost test |
Documenteaza presupunerea (nomenclator identic intre medii — aceleasi 18 coduri) SAU scope per-env (out of scope acum). Minim: nota explicita | MEDIUM |
| 5 | US-005/US-013 | write-back creds API nevalidate -> slot durabil (vezi L de mai sus) | re-asignat la US-005/US-013; propaga doar dupa login reusit | MEDIUM/HIGH |
| 6a..6d | US-013 | ping-pong re-ADD / boot-crash / interim-creds / grep ambiguu | imbinate in AC US-013 (vezi acolo) | CRITIC/HIGH |
ENG DUAL VOICES — CONSENSUS TABLE
Dimension Claude Subagent Consensus
────────────────────────────── ──────── ───────── ────────────────────
1. Architecture sound? da/cond da/cond CONFIRMED (cond. fixuri)
2. Test coverage sufficient? lacune +API b/c CONFIRMED lacune
3. Performance risks? low low CONFIRMED low
4. Security (creds routing)? L/5 5+unvalid CONFIRMED
5. Error paths (boot)? E1/E9 6a/6b CRIT CONFIRMED (boot-crash)
6. Deployment risk (DROP)? migrare CRIT/HIGH CONFIRMED ELEVAT -> intareste challenge A
Codex: indisponibil (N/A). Mesaj-cheie: caile de duplicat ireversibil (1b, 3) si boot-crash/ping-pong (6a, 6b) musca in productie; intaresc recomandarea de a amana DROP-ul (challenge A).
Diagrama teste (codepath -> acoperire)
| Codepath nou | Story test | Stare |
|---|---|---|
medii_disponibile/rar_env_efectiv |
US-002 | acoperit |
| resolve target (cerere>default), respinge indisponibil | US-004 | acoperit |
| idempotency env-aware + recompute legacy | US-003/US-001 | GAP recompute -> adaugat |
migrare backfill submissions.rar_env |
US-001 | GAP (G) -> adaugat in AC |
| worker sesiune (cont,env) + base_url per env | US-006 | acoperit |
| purge creds scoped pe env | US-006 | GAP (E1) -> adaugat |
| recover_orphans per env | US-006 | GAP (1b) -> adaugat |
| write-back slot routing | US-005/013 | GAP (L/5) -> adaugat |
| reconcile endpoint per env (inline + orfani) | US-006 | inline acoperit; orfani GAP -> adaugat |
| keepalive env (ancora globala) | US-013 | GAP (M2) -> adaugat |
| DROP garda: assert + idempotent re-run + fail-loud/no-crash | US-013 | partial -> intarit (6a/6b/6c) |
| API-channel idempotency back-compat | US-003 | GAP (3) -> adaugat |
| badge/labels env | US-010 | acoperit |
API rar_target default/explicit/invalid/indisponibil |
US-005 | acoperit |
| config 2 sectiuni + confirmare prod | US-008 | acoperit |
statusbar toggle viz + retragere header .badge-env |
US-011 | toggle acoperit; header GAP (F11) -> adaugat |
| live dual-env smoke | US-012 | GAP (M) -> adaugat opt-in |
Auto-fixuri DX (contract API extern — incorporate in stories)
Voci: Claude (primar) + Claude subagent (perspectiva integrator VFP/ROAAUTO). Riscul ireversibilitatii ridica stacheta pe claritate nume / eroare / discoverability pre-trimitere.
| # | Story | Gap | Fix | Sev |
|---|---|---|---|---|
| F1 | US-005/US-013 | Trei nume pt un concept: input rar_target, echo/DB rar_env, rar-creds env (US-013 AC scrie literal "rar_target/env") |
Un singur cheie: rar_env pe input + output + rar-creds (englez snake, consistent cu coloana si on_unmapped_error). Scoate rar_target/env. (taste usor -> poarta) |
HIGH |
| F2 | US-004 | Eroarea "mediu indisponibil" e proza, fara cod/envelope 6-chei/status; errors.py nu e in Fisiere |
RAR_MEDIU_INDISPONIBIL in errors.CATALOG (problema/cauza cu lista disponibile/fix "activeaza in Cont"); adauga errors.py la Fisiere US-004; distinge literal-invalid (422 pydantic) de valid-dar-indisponibil (cod dedicat); acopera si cazul 0-medii |
HIGH |
| F3 | US-004/contract | Flip runtime test->prod prin canal web: operator comuta disponibilitatea -> apelant API fara rar_env trece silentios pe prod (real). Migrarea previne flip la DEPLOY, nu la RUNTIME |
Mitigat de F4+F5 (probe pre-trimitere); documenteaza reasignarea ca comportament cunoscut; leaga de CEO-H | HIGH |
| F4 | US-010 (sau story noua) | Niciun GET nu expune medii_disponibile/rar_env_default -> integratorul afla env-ul doar din eroare sau dupa o trimitere reala |
GET /v1/conturi/medii account-scoped: {medii_disponibile, rar_env_default, test:{enabled,has_creds}, prod:{...}} (refoloseste helper US-002, <1 fisier) |
HIGH |
| F5 | US-005 | ValidareResult (dry-run) NU ecou-ieste rar_env; dry-run e canalul sigur de a confirma unde ar ateriza o trimitere reala |
adauga rar_env: str la ValidareResult + /valideaza; models.py |
MEDIUM |
| F6 | US-004/US-005 | Respingere whole-request vs per-rand inconsistenta cu on_unmapped_error (per-rand, 200) |
Decide + documenteaza; recomandat: corp parsabil imbogatit cu cod (prietenos VFP), noteaza asimetria intentionat |
MEDIUM |
| F7 | US-005/US-010/US-004/US-013 | Contractul (sursa adevar) actualizat doar pt rar-creds; lipsesc field-ul nou, echo-ul, cod-ul nou. /v1/conturi/rar-creds NU e documentat deloc azi -> US-013 e documentare de la zero, nu amendament |
AC explicit "update api-rar-contract.md" pe fiecare; US-013 documenteaza endpoint-ul intreg (req/resp, param env, slot default) |
HIGH |
| F8 | US-013 (doc) | env optional default = slot default cont: integrator cu creds TEST pe cont nou (default prod) le scrie silentios in slot prod -> US-007 le respinge "invalide pe PRODUCTIE" desi sunt valide (test) |
pastreaza aditiv; documenteaza ca omiterea env tinteste slotul default; mesaj validare sugereaza nepotrivire env ("creds valide pentru alt mediu?") |
MEDIUM |
DX DUAL VOICES — CONSENSUS TABLE
Dimension Claude Subagent Consensus
─────────────────────────────── ─────── ───────── ──────────────
1. Getting started (aditiv)? low fr low fr CONFIRMED low
2. Naming guessable? D1 incon F1 3-nume CONFIRMED -> rar_env
3. Error messages actionable? D2 gap F2 gap CONFIRMED gap
4. Docs findable & complete? D4 gap F7 gap+ CONFIRMED gap
5. Back-compat safe? D3 resid F3 runtime CONFIRMED (1 rezidual)
6. Discoverability pre-send? D5 gap F4 gap CONFIRMED gap
Codex: indisponibil (N/A). DX scor initial: ~6/10 (model API solid + aditiv, dar nume inconsistent + eroare neimbogatita + zero discoverability + contract neactualizat). Tinta dupa fixuri: ~9/10.
Jurnal integrator (condensat)
| Etapa | Azi (plan brut) | Dupa fixuri DX |
|---|---|---|
| Afla env-urile contului | doar din eroare / dupa trimitere reala | GET /v1/conturi/medii |
| Trimite | rar_target (nume #1) |
rar_env (un nume) |
| Confirma tinta fara trimitere reala | imposibil (valideaza nu ecou-ieste) | /valideaza ecou-ieste rar_env |
| Eroare tinta indisponibila | proza, fara cod | cod: RAR_MEDIU_INDISPONIBIL + fix |
| Citeste rezultatul | rar_env (nume #2) |
rar_env (acelasi) |
| Doc | contract fara field/endpoint | contract complet |