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

@@ -32,6 +32,7 @@ from pydantic import BaseModel, Field
from datetime import datetime, timezone
from ... import errors
from ...accounts import held_for_account
from ...auth import require_api_access, resolve_account_id
from ...crypto import decrypt_creds, encrypt_creds
from ...db import get_connection
@@ -1125,6 +1126,9 @@ def commit_import(
conn.execute("BEGIN IMMEDIATE")
try:
purge_after_sql = "datetime('now', '+90 days')"
# PRD 5.19 US-002: snapshot `held` din comutatorul contului (chokepoint unic
# held_for_account) — 0 daca Auto ON, 1 daca Auto OFF. Per-cont -> o data.
held_val = held_for_account(conn, acct)
for ok_row in ok_rows:
row_dict = ok_row["data"]
@@ -1208,9 +1212,9 @@ def commit_import(
# INSERT ON CONFLICT DO NOTHING (TOCTOU)
cur = conn.execute(
"INSERT OR IGNORE INTO submissions "
"(idempotency_key, account_id, status, payload_json, batch_id, row_index, purge_after, rar_env) "
"VALUES (?, ?, 'queued', ?, ?, ?, " + purge_after_sql + ", ?)",
(key, acct, payload_json, import_id, row_index, env),
"(idempotency_key, account_id, status, payload_json, batch_id, row_index, purge_after, rar_env, held) "
"VALUES (?, ?, 'queued', ?, ?, ?, " + purge_after_sql + ", ?, ?)",
(key, acct, payload_json, import_id, row_index, env, held_val),
)
if cur.rowcount == 0: