Files
rar-autopass/CLAUDE.md
Claude Agent 44f261e269 refactor: comentarii strict functionale, fara referinte PRD/stories
Curatare globala a comentariilor si docstring-urilor (app, tools, teste,
scripturi): eliminate referintele la PRD-uri, US-xxx, task-uri istorice si
review-uri; pastrata doar informatia functionala, formulata scurt. Regula
adaugata in CLAUDE.md (sectiunea Stil). Fara modificari de cod sau comportament.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:48:38 +00:00

7.5 KiB

CLAUDE.md

Ghid Claude Code pentru acest repo.

Ce este

Gateway web (Python/FastAPI) care preia prezentari service-auto si le declara la RAR AUTOPASS (L.142/2023, OM 210/2024). Inlocuieste integrarea VFP RarAutoPass (ROAAUTO), arhivata in legacy-vfp/ (doar referinta).

Stil

  • Limba proiectului: romana (cod, comentarii, commit-uri, docs). Fara emoji.
  • Comentarii scurte, strict functionale. Doar ce nu reiese din cod. FARA referinte la PRD-uri, stories (US-xxx), review-uri sau istoricul deciziei.

Surse de adevar

  • docs/api-rar-contract.md — contractul RAR; la divergenta, contractul are dreptate.
  • docs/ROADMAP.md — progres + proces de lucru; sesiunile noi pornesc de aici. Se modifica doar "Stadiu Implementare"; detaliile stau in docs/prd/.

Comenzi

pip3 install -r requirements.txt          # Python 3.12+

# Dev: API + worker = procese SEPARATE
uvicorn app.main:app --reload --port 8010 # dashboard /, /docs, /healthz, /metrics
python3 -m app.worker                     # proceseaza coada

# start.sh = mediu (test/prod) + rol (api/worker/both/finalizate); loguri in .run/
./start.sh test both --send     # trimite efectiv la RAR test
./start.sh test finalizate      # listeaza prezentarile inregistrate la RAR
./start.sh status | stop
./start-test.sh / ./start-prod.sh   # fixeaza mediul, forwardeaza rolul

# Teste (TestClient + SQLite temporar); testele live RAR sunt skip implicit (marker `live`)
python3 -m pytest -q
python3 -m pytest tests/test_worker_reconcile.py::test_x -q
AUTOPASS_LIVE_RAR=1 python3 -m pytest tests/test_live_rar.py -q  # LIVE opt-in: creeaza FINALIZATA; cere settings.xml cu creds <test>

# Chei API (admin, doar CLI); cheia rfak_... se afiseaza O SINGURA DATA
python3 -m tools.apikey create --account 2 | list | rotate | revoke

docker compose up --build   # deploy: api + worker + autoheal, acelasi image + volum SQLite

Arhitectura

Doua procese peste acelasi SQLite (WAL), comunica EXCLUSIV prin tabela submissions:

  • API (app.main:app) — API v1 (app/api/v1/), dashboard HTMX (app/web/ + templates/), /healthz, /metrics. Worker-ul NU ruleaza aici: un worker mort nu trebuie sa lase containerul "sanatos".
  • Worker (app/worker/__main__.py) — bucla: heartbeat → recuperare orfane → claim atomic (BEGIN IMMEDIATE) → login RAR per cont → postPrezentare → update. Retry/backoff exponential, reconciliere anti-duplicat, lease pe sending orfane, re-login la JWT expirat (TTL 30h). Send OFF implicit (AUTOPASS_WORKER_SEND_ENABLED=false).

Doua canale de intrare in coada: POST /v1/prezentari (ROAAUTO/soft propriu) si import web xlsx/csv → mapare coloane → preview → commit (app/import_parse.py, import_router.py).

Flux: validare (validation.py) → mapare operatie→cod (mapping.py) → enqueue cu PII criptat → worker trimite → dashboard live.

Invariante critice

  • AUTOPASS_CREDS_KEY identica intre API si worker (Fernet, crypto.py): chei diferite → worker nu decripteaza creds → trimiterile esueaza. start.sh both genereaza cheie efemera partajata; in prod, cheie persistenta in .env.
  • Idempotenta = hash de continut canonic server-side (idempotency.py) — RAR accepta duplicate, fara nr. comanda. build_key normalizeaza account_id la account_or_default (None == 1) INAINTE de hash, altfel acelasi rand primeste chei diferite pe API vs import. canonicalize_row normeaza VIN/nr/odometru (strip ".0" din Excel) inainte de validare si cheie.
  • FINALIZATA e terminal la RAR (fara anulare/corectie). Pe eroare ambigua (timeout/TransportError/502/503/504/429/408) sau sending orfan, worker-ul cauta in finalizate (vin+dataPrestatie+odometruFinal) si marcheaza sent fara re-trimitere (reconcile.py). EXCEPTIE: RAR 500 cu mesaj (RarError.rar_message, ex. ORA-12899) = esec DEFINITIV → fara reconciliere/retry, error cu mesajul RAR (RAR_EROARE_SERVER); altfel ar marca fals sent un record PARTIAL lasat de RAR (ne-tranzactional).
  • Creds RAR per cont: durabile in accounts (canal web, fallback re-login) SAU efemere in submissions.rar_creds_enc (canal API, sterse dupa primul login reusit). Worker incearca submission-ul intai, apoi contul. Purjarea sterge DOAR creds efemere din submissions.
  • Auth API-key (auth.py): identifica contul ROAAUTO (separat de creds RAR); stocam doar SHA-256. AUTOPASS_REQUIRE_API_KEY: false (dev) → fara cheie = cont id=1, cheie invalida → 401; true (prod) → obligatorie pe /v1/* protejat. POST-urile, importul si GET-urile de listare (API + fragmente dashboard) sunt account-scoped, 404-before-leak pe id strain. GET /v1/nomenclator public intentionat (coduri RAR publice, fara PII).
  • Admin bootstrap: primul user din TOATA baza la /signup (count_admins()==0, in tranzactie) devine is_admin=1; restul nu. is_admin da acces la /admin (panou global) si e independent de contul id=1 (acela e doar fallback dev pentru API neautentificat). Fix manual: python3 -m tools.account set-admin --account N.
  • Mapare coloane memorata per (account_id, signature_coloane) (column_mappings): reaplicata automat la fisiere cu aceleasi coloane; un cont poate avea mai multe formate.
  • Mapare operatie→cod: prestatia vine cu cod_prestatie (cod RAR) sau cod_op_service + denumire. Nerezolvat → needs_mapping (nu se trimite), editor web cu sugestie fuzzy; salvarea maparii re-rezolva automat submission-urile blocate.
  • cod_prestatie VALIDAT fata de nomenclator la ingestie (resolve_prestatii(..., valid_codes)): cod necunoscut NU se trimite raw — e promovat la cod_op_service (denumire=cod) si intra la mapat. Motiv (confirmat live): RAR accepta doar coduri din nomenclator (max 5 car.); cod necunoscut → HTTP 500 ORA-12899, iar RAR ne-tranzactional lasa record PARTIAL FINALIZATA pe care reconcilierea l-ar marca fals sent. La cod nemapat: on_unmapped_error (boolean top-level pe POST /v1/prezentari + /valideaza) = false → editor/needs_mapping; true → respins fara enqueue (submission_id=null + erori). Precedenta: cerere > accounts.on_unmapped_error_default > false.
  • WAF RAR da 403 fara User-Agent de browser — httpx trimite mereu User-Agent: Mozilla/5.0 (config.py).
  • 422 fara echo de credentiale: handler-ul din main.py pastreaza type/loc/msg dar DROP-a input/ctx (altfel reflecta rar_credentials.password).
  • Retentie: submissions sent + import_batchespurge_after = now + 90 zile; worker purjeaza orar (GDPR/L.142).

Masina de stari submissions

queued → sending → sent (cu id_prezentare de la RAR). Ramuri: needs_mapping, needs_data (RAR 400), error (max retries / 4xx nerecuperabil / RAR 500 cu mesaj / creds invalide sau login 401 — fara retry pe creds gresite). Backoff: next_attempt_at = now + base*2^retry, plafonat. Schema: app/schema.sql.

Mod non-interactiv

Vezi /workspace/CLAUDE.md: cu claude -p, fisierele noi merg DOAR in /workspace/.claude-work/<task>/; modificarile la fisiere existente raman in locatia originala.

Skill routing

Cand cererea se potriveste unui skill, invoca-l prin Skill tool; la indoiala, invoca-l.

  • Idei produs → /office-hours · Strategie → /plan-ceo-review · Arhitectura → /plan-eng-review
  • Design system/plan → /design-consultation, /plan-design-review · Pipeline complet → /autoplan
  • Bug-uri → /investigate · QA site → /qa, /qa-only · Code review → /review · Polish vizual → /design-review
  • Ship/deploy/PR → /ship, /land-and-deploy · Salvare/reluare context → /context-save, /context-restore · Spec → /spec