docs(prd): PRD-uri Etapa 3 (3.1/3.2/3.3) aprobate dupa autoplan
Faza PLAN pentru multi-cont / self-onboarding. Trei PRD-uri scrise, ancorate in cod, trecute prin autoplan (voci Claude independente; Codex degradat pe usage-limit) si aprobate la poarta umana. - 3.1 creare cont: CLI tools/account.py + accounts.active; CUI unic prin index partial - 3.2 filtrare GET pe cont: scope pe cheie, allowlist campuri, nomenclator global - 3.3 self-onboarding web: sesiuni + cont 'in asteptare' + CSRF + interfata admin web + email; US-007 promovat in MVP (7->12 stories) Dashboard ROADMAP actualizat (stare 'PRD aprobat', linkuri PRD). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
152
docs/prd/prd-3.1-creare-cont.md
Normal file
152
docs/prd/prd-3.1-creare-cont.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# PRD 3.1 — Creare cont nou
|
||||
|
||||
**Stare**: aprobat
|
||||
|
||||
> Proces complet: `docs/ROADMAP.md` §5. Contract RAR (sursa de adevar): `docs/api-rar-contract.md`.
|
||||
> Starea trece: `draft → aprobat → in-executie → verify-pass → inchis` (actualizata de lead).
|
||||
|
||||
## 1. Obiectiv
|
||||
|
||||
Inlocuieste crearea conturilor prin `INSERT` SQL manual cu un tool admin dedicat
|
||||
(`tools/account.py`), simetric cu `tools/apikey.py`. Este **fundatia Etapei 3**: 3.2
|
||||
(filtrare GET pe cont) si 3.3 (self-onboarding web) au nevoie de o cale curata si testata
|
||||
de a materializa conturi. Pastram filozofia din `app/auth.py`: lifecycle-ul de admin
|
||||
traieste in CLI pe masina gateway, **fara suprafata HTTP de admin**.
|
||||
|
||||
## 2. Non-Goals (anti scope-creep)
|
||||
|
||||
- **Fara endpoint HTTP** de creare cont in 3.1. (Nota: la poarta de aprobare utilizatorul a cerut o
|
||||
**interfata web de admin** pentru activarea conturilor — vezi 3.3; aceasta inverseaza stanta
|
||||
initiala "fara suprafata HTTP de admin". Admin web traieste in 3.3, nu aici. CLI-ul 3.1 ramane
|
||||
pentru bootstrap/suport.)
|
||||
- **Fara autentificare de utilizator** (email/parola, sesiuni) — apartine 3.3. Aici contul are
|
||||
doar `name` + `cui`, ca azi.
|
||||
- **Fara setare creds RAR** in acest tool — exista deja `POST /v1/conturi/rar-creds` (T1).
|
||||
- **Fara coloane de user/parola** — identitatea de login (email/parola) vine in 3.3. Aici adaugam
|
||||
doar `accounts.active` (lifecycle de cont), de care depinde gate-ul „cont in asteptare" din 3.3.
|
||||
- **Fara stergere cont** — momentan nu e nevoie; conturile sunt putine si durabile. (Dezactivarea
|
||||
exista, via `active`.)
|
||||
|
||||
## 3. Stories atomice
|
||||
|
||||
### US-001: Coloana `accounts.active` + helper-e cont in `app/accounts.py`
|
||||
**Ca** dezvoltator **vreau** functii pure de creare/listare/(de)activare cont **pentru ca** atat
|
||||
CLI-ul (3.1) cat si fluxul web (3.3) sa materializeze si sa activeze conturi prin aceeasi cale testata.
|
||||
|
||||
- **Depinde de**: —
|
||||
- **Fisiere**: `app/schema.sql` (`accounts.active`), `app/db.py` (migrare `_migrate`),
|
||||
`app/accounts.py` (nou), `tests/test_accounts.py` (nou) (~4 fisiere)
|
||||
- **Test intai (RED)**: `tests/test_accounts.py` — `test_create_account_returneaza_id`,
|
||||
`test_create_account_activ_implicit`, `test_create_account_name_gol_ridica_eroare`,
|
||||
`test_create_account_cui_duplicat_respins`, `test_set_active_comuta`, `test_list_accounts_ordonat`
|
||||
- **Acceptance criteria**:
|
||||
- [ ] `accounts.active INTEGER NOT NULL DEFAULT 1` adaugata in `schema.sql` + migrata idempotent in
|
||||
`_migrate` (conturi existente raman active; default id=1 activ).
|
||||
- [ ] `create_account(conn, name, cui=None, active=True) -> int` insereaza si intoarce `id`-ul nou.
|
||||
- [ ] `name` gol/whitespace → `ValueError` (nu insereaza).
|
||||
- [ ] `cui` ne-nul duplicat → `ValueError` (un CUI = un cont — decizie confirmata §5).
|
||||
`cui=None` se accepta multiplu (conturi fara CUI, ex. default).
|
||||
- [ ] `set_active(conn, account_id, active: bool)` comuta starea; cont inexistent → `ValueError`.
|
||||
- [ ] `list_accounts(conn) -> list[dict]` intoarce `id, name, cui, active, created_at`, ordonat dupa
|
||||
`id`, **fara** `rar_creds_enc`.
|
||||
- **Verificare E2E**: n/a (helper pur) — acoperit de teste unitare.
|
||||
|
||||
### US-002: CLI `tools/account.py` (create/list/activate/deactivate)
|
||||
**Ca** admin gateway **vreau** `python -m tools.account create|list|activate|deactivate` **pentru ca**
|
||||
sa onboardez si sa **activez** un client fara SQL manual, optional emitand prima cheie API intr-un pas.
|
||||
|
||||
- **Depinde de**: US-001
|
||||
- **Fisiere**: `tools/account.py` (nou), `tests/test_tools_account.py` (nou) (~2 fisiere)
|
||||
- **Test intai (RED)**: `tests/test_tools_account.py` — `test_create_afiseaza_id`,
|
||||
`test_create_with_key_emite_cheie`, `test_create_cui_duplicat_exit_2`,
|
||||
`test_activate_comuta_starea`, `test_list_afiseaza_activ`
|
||||
- **Acceptance criteria**:
|
||||
- [ ] `create --name "Service X" [--cui RO123] [--inactive]` creeaza contul (implicit activ) si
|
||||
tipareste `id`-ul; `--inactive` creeaza cont in asteptare.
|
||||
- [ ] `--with-key` emite si o cheie API (`app.auth.create_api_key`), afisata **o singura data**
|
||||
(reuseaza pattern-ul din `tools/apikey.py`).
|
||||
- [ ] `activate --id N` / `deactivate --id N` comuta `active` (mesaj de confirmare).
|
||||
- [ ] `name` gol, `cui` duplicat sau `id` inexistent → mesaj la stderr + exit code 2.
|
||||
- [ ] `list` tipareste tabelul `id | name | cui | activ | created_at`.
|
||||
- [ ] `init_db()` apelat la start (asigura schema + migrare `active`), ca in `tools/apikey.py`.
|
||||
- **Verificare E2E**: `python -m tools.account create --name "Test SRL" --cui RO99 --inactive --with-key`
|
||||
→ cont inactiv + cheie; `python -m tools.account activate --id <id>` → `list` arata `activ=da`.
|
||||
|
||||
## 4. Riscuri
|
||||
|
||||
- **Unicitate CUI** — daca un client are mai multe puncte de lucru sub acelasi CUI, constrangerea
|
||||
blocheaza al doilea cont. Mitigare: o aplicam la nivel de helper (nu schema UNIQUE) ca sa fie usor
|
||||
de relaxat; decizia finala in §5.
|
||||
- **Coliziune cu cont default (id=1)** — `INSERT OR IGNORE ... id=1 'default'` exista in schema.
|
||||
`create_account` foloseste AUTOINCREMENT, deci nu atinge id=1. Test: primul cont creat are id≥2.
|
||||
|
||||
## 5. Intrebari deschise (REZOLVATE la poarta de aprobare)
|
||||
|
||||
- **CUI unic** — REZOLVAT: unic cand e prezent (un CUI = un cont), `NULL` permis multiplu.
|
||||
- **`--with-key`** — REZOLVAT: optional (flag), emiterea cheii e o decizie constienta.
|
||||
- **Coloana `active`** — adaugata aici (lifecycle de cont) pentru ca 3.3 sa creeze conturi „in
|
||||
asteptare" (`active=0`) si gate-ul de trimitere sa le opreasca pana la activarea de catre admin.
|
||||
|
||||
## 6. Valuri de executie (graful de dependente)
|
||||
|
||||
```
|
||||
Val 1: [US-001] ← schema active + helper, fisiere (cvasi-)noi → fara dependente
|
||||
Val 2: [US-002] ← deblocat de US-001 (CLI peste helper)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Addendum review (autoplan, `[subagent-only]` — Codex indisponibil: usage limit)
|
||||
|
||||
> Doua voci Claude independente (Eng + Produs/DX). Schimbarile de mai jos sunt **obligatorii la
|
||||
> executie** (auto-decise prin principiile autoplan: completitudine, explicit, DRY). Convergenta
|
||||
> mare intre voci pe primele doua.
|
||||
|
||||
**A1 [HIGH, ambele voci] — Unicitatea CUI prin index partial, NU check in helper.** Check-ul
|
||||
`SELECT cui → ValueError → INSERT` are o fereastra de coliziune (doi `create_account` concurenti,
|
||||
relevant cand 3.3 reuseaza helperul din web). Fix: in `schema.sql` + `_migrate`
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS ux_accounts_cui ON accounts(cui) WHERE cui IS NOT NULL` (SQLite
|
||||
trateaza NULL ca distincte nativ → `cui=NULL` multiplu merge gratis). Helperul **normalizeaza**
|
||||
(`trim` + `upper`) si prinde `IntegrityError → ValueError` pentru mesaj prietenos. Inlocuieste AC
|
||||
"cui duplicat" + adauga `test_create_cui_null_multiplu_permis`, `test_create_cui_normalizat`.
|
||||
|
||||
**A2 [HIGH, ambele voci] — `accounts.active` este INERT pana la 3.3.** Nimic nu citeste `active`
|
||||
azi (`resolve_account_id` verifica doar `api_keys.active`; worker-ul nu se uita la cont). Pana la
|
||||
3.3/US-008 (gate worker), `deactivate` NU opreste trimiterile. Documenteaza-l explicit ca lifecycle
|
||||
flag consumat de 3.3; nu lasa asteptarea falsa ca dezactiveaza trimiterile. (Decizia keep-in-3.1 vs
|
||||
move-to-3.3 = taste, vezi poarta.)
|
||||
|
||||
**A3 [MEDIUM, Produs] — Flag consistent cu `tools/apikey.py`:** `activate`/`deactivate` folosesc
|
||||
`--account N` (nu `--id N`), aliniat cu sora. `create` pastreaza `--name`/`--cui`.
|
||||
|
||||
**A4 [MEDIUM, ambele] — Mesaje de eroare cu cauza+fix.** CUI duplicat → numeste contul existent:
|
||||
`eroare: CUI RO123 e deja folosit de contul 4 (foloseste 'activate --account 4' sau alt CUI)`.
|
||||
Specifica textul ca tinta de test.
|
||||
|
||||
**A5 [LOW→MEDIUM] — `--with-key` atomic:** `create_account` + `create_api_key` in aceeasi
|
||||
tranzactie (`BEGIN IMMEDIATE` → ambele → COMMIT; pe esec ROLLBACK). DB ruleaza autocommit
|
||||
(`isolation_level=None`) — tranzactia trebuie explicita. Pe esec emitere cheie: mesaj clar ca
|
||||
contul A fost creat.
|
||||
|
||||
**A6 [MEDIUM, Produs — pentru 3.3] — adauga comenzi CLI ceruta de 3.3:**
|
||||
- `list --pending` (filtreaza `active=0`) — adminul descopera conturile de activat (3.3 nu are
|
||||
notificare; vezi addendum 3.3).
|
||||
- `set-password --account N` — scapare de reset parola pentru 3.3 (Non-Goal SMTP), altfel "reset
|
||||
prin admin" e o promisiune fara implementare. (Implementarea hash-ului = `app/users.py` din 3.3;
|
||||
comanda poate astepta US din 3.3, dar planific-o aici ca sa nu se piarda.)
|
||||
|
||||
**A7 [MEDIUM] — Teste RED lipsa de adaugat:** `cui=None` multiplu (A1), `list` fara
|
||||
`rar_creds_enc`, `set_active` idempotent (set activ pe activ nu arunca), `create_account(active=False)`
|
||||
la nivel de helper, primul cont creat are `id>=2` (nu atinge default id=1).
|
||||
|
||||
**A8 [LOW] — Obiectiv corectat:** 3.1 e fundatie pentru **3.3** (care reuseaza `create_account` +
|
||||
`active`); pentru 3.2 e doar suport de VERIFY (al doilea cont). 3.2 NU materializeaza conturi.
|
||||
|
||||
**Deferat (P3, in afara scope-ului imediat — noteaza in ROADMAP/TODO):** `rename`/`set-cui`
|
||||
(corectie typo fara SQL manual), `--if-not-exists` (provisioning idempotent). Regret probabil, dar
|
||||
nu blocheaza livrabila.
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user