feat(5.19): auto-send toggle per cont + tinere manuala randuri (held)

Comutator accounts.auto_send_enabled per cont: Auto OFF (default) tine randurile
la ingestie (submissions.held=1), worker-ul (claim_one AND held=0) le sare pana la
eliberare umana (per rand/bulk/auto-release OFF->ON). Snapshot held prin chokepoint
unic held_for_account pe toate caile de ingestie (API, import, reresolve, reactivare).

- schema/migrare: coloana held + index partial idx_submissions_held; auto_send_enabled
- API: echo onest held+motiv (US-010), ruta /prezentari/{id}/trimite-acum
- web: toggle header, modal confirmare tipata, buton Trimite per rand + Trimite toate,
  banner coada tinuta imbatranita (L.142), contor "In asteptare (manual)"
- worker: expire_held (US-008, inchide gaura retentie PII), metrics held gauges
- ops: tools/carantina_held + runbook rollback (R4)

Nota review (/code-review high): re-snapshot held lipseste pe caile repune/corectie
(requeue_submission, post_corectie, bulk-fix) — de aliniat separat cu create_prezentari.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-07-06 08:34:40 +00:00
parent 2ec3292382
commit 3b5cf7a7d9
30 changed files with 2344 additions and 32 deletions

View File

@@ -71,13 +71,40 @@ ETICHETE_SCURTE: dict[str, str] = {
}
def eticheta_scurta(status: str) -> str:
# ---------------------------------------------------------------------------
# Randuri tinute manual (held) — US-006, PRD 5.19
#
# `held` NU e o stare noua in masina de stari: un rand cu `status='queued' AND
# held=1` este in coada, dar tinut pana la eliberarea umana. Il afisam distinct
# ("In asteptare (manual)") cu culoare de AVERTIZARE (--warn / amber) — asteptare
# benigna, NU eroare. Reutilizam clasa `s-needs_review` (definita in base.html ca
# `color:var(--warn)`); NU folosim clasele needs_* rosii (s-needs_data /
# s-needs_mapping = --err). CHECK-ul din schema.sql ramane neatins.
# ---------------------------------------------------------------------------
ETICHETA_HELD: Eticheta = (
"In asteptare (manual)",
"Tinuta pentru confirmarea ta; nu pleaca spre RAR pana nu o eliberezi.",
"s-needs_review",
)
ETICHETA_HELD_SCURTA = "Manual"
def eticheta_scurta(status: str, held: bool = False) -> str:
"""
Returneaza eticheta compacta (pill) pentru o stare de submission.
Cand `status='queued'` si `held` este adevarat, randul e tinut manual si
intoarce "Manual" (distinct de "In coada") — vezi PRD 5.19 D5. Pentru orice
alta stare `held` este ignorat, iar apelul fara `held` ramane identic cu cel
dinainte de US-006.
Arunca KeyError daca starea nu este mapata — intentionat, ca sa prinda
stari noi adaugate in schema fara mapare corespunzatoare.
"""
if held and status == "queued":
return ETICHETA_HELD_SCURTA
try:
return ETICHETE_SCURTE[status]
except KeyError:
@@ -87,13 +114,20 @@ def eticheta_scurta(status: str) -> str:
)
def eticheta_stare(status: str) -> Eticheta:
def eticheta_stare(status: str, held: bool = False) -> Eticheta:
"""
Returneaza (text, subtext, css_class) pentru o stare de submission.
Cand `status='queued'` si `held` este adevarat, randul e tinut manual si
intoarce eticheta "In asteptare (manual)" cu clasa de avertizare (amber /
--warn) — vezi PRD 5.19 US-006/D5. Pentru orice alta stare `held` este
ignorat, iar apelul fara `held` ramane identic cu cel dinainte de US-006.
Arunca KeyError daca starea nu este mapata — intentionat, ca sa prinda
stari noi adaugate in schema fara mapare corespunzatoare.
"""
if held and status == "queued":
return ETICHETA_HELD
try:
return STARI_SUBMISSION[status]
except KeyError: