feat(T2): reconciliere anti-duplicat + retry/backoff + recuperare orfane

Inchide bucla de trimitere (plan.md sect. 4 worker, failure registry).

- app/reconcile.py: match_finalizata pe vin+dataPrestatie+odometruFinal (int),
  alege id maxim la duplicate (RAR accepta duplicate, confirmat live)
- app/rar_client.get_finalizate: parseaza data.content (descoperit live ca
  ruta = GET /prezentari/getAllPrezentariFinalizate; filtrele nu merg pe test)
- app/worker rescris:
  - recuperare orfane (rand 'sending' peste lease = worker mort mid-POST)
  - pe eroare tranzitorie/timeout: reconciliere INAINTE de re-send (anti-duplicat);
    daca recordul exista la RAR -> sent fara re-POST
  - retry/backoff exponential; peste worker_max_retries -> error + banner
  - re-login la token expirat (JWT 30h)
- schema: coloana next_attempt_at (backoff) + migrare aditiva in init_db
- config: worker_sending_lease_s, worker_retry_base_s/max_s, worker_max_retries
- contract: documentata ruta+forma getAllPrezentariFinalizate (verificat live)

Verify: pytest 54 passed (15 noi T2) + validare live (reconciliere record 68514).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-15 18:20:32 +00:00
parent 36d1b916d5
commit 77088daf29
9 changed files with 495 additions and 67 deletions

View File

@@ -55,6 +55,7 @@ CREATE TABLE IF NOT EXISTS submissions (
rar_error TEXT,
id_prezentare INTEGER, -- data.id intors de RAR la succes
retry_count INTEGER NOT NULL DEFAULT 0,
next_attempt_at TEXT, -- backoff: randul nu se ia inainte de acest moment (T2)
sending_since TEXT, -- pentru lease/timeout pe randuri 'sending' orfane (T2)
purge_after TEXT, -- sent + 90z (P2)
created_at TEXT NOT NULL DEFAULT (datetime('now')),