RoDebate← Dezbateri

Document tehnic

RoDebate — Specificație funcțională și tehnică

MVP, versiunea 1.2 • 12 noiembrie 2026

Sursă: RoDebate_Specificatie_Functionala_Tehnica_MVP_v1.2.docx

Pe scurt

  • Mod de joc inițial: 1 vs. 1, față în față, pe un singur telefon cu browser, microfon și difuzor.
  • Catalog: cinci moțiuni publicate de administrator; ulterior, propuneri și vot comunitar pentru moțiuni noi.
  • Joc: research inițial, discursuri PRO/CONTRA, replici, întrebări și concluzii; panouri cu cronometru mare, tip ceas de șah, și buton „Predă tura”.
  • Moderator: mesaje vocale neutre, predefinite; opțional, sfaturi la începutul fiecărei runde pentru începători.
  • Înregistrare: fiecare tură este înregistrată și trimisă imediat spre procesare, în timp ce continuă tura următoare.
  • Analiză: transcriere incrementală, apoi arbitraj final unitar cu OpenAI API în MVP; ulterior model local prin JudgeProvider.
  • Rezultate: transcript, punctaje și explicații în conturile ambilor jucători; votul spectatorilor este independent și poate fi inactiv în MVP.

Ordinea priorităților: prevederile din secțiunile 16–25 sunt amendamentele cele mai recente și prevalează asupra descrierilor mai vechi (de exemplu, WebRTC între doi utilizatori sau trimiterea audio abia la final).

Scenariul prioritar: două persoane în aceeași încăpere folosesc UN SINGUR telefon cu browser, microfon și difuzor. Telefonul conduce jocul vocal și înregistrează. Pentru MVP sunt disponibile cinci moțiuni aprobate de admin. Votul spectatorilor poate rămâne inactiv până la lansarea modului public.

1. Goals / criterii de succes

  • G1. Două persoane joacă față în față cu un singur telefon, roluri PRO/CONTRA și cronometru autoritativ, cu suport de pauză/reconectare.
  • G2. Un singur microfon înregistrează o pistă ambientală; turn-taking-ul/etapele ajută la atribuirea vocilor, iar ambiguitățile sunt verificate înainte de scor. Audio ajunge securizat la procesorul laptopului.
  • G3. Se creează transcript sincronizat, corectabil și auditabil.
  • G4. Arbitrajul AI produce scoruri pe 5 criterii, explicații și citări la segmente, fără acces la votul publicului.
  • G5. Votul publicului rămâne separat de scorul AI, dar este opțional în MVP-ul single-phone, fără spectatori.
  • G6. Schimbarea OpenAI API → model local necesită modificarea configurației/adaptorului, nu rescrierea aplicației.
  • G7. Erorile de audio, transcriere și arbitraj nu produc victorii inventate sau rating greșit.
  • G8. Datele audio și personale au consimțământ/informare, acces controlat, retenție și ștergere definite.

2. Scope MVP și non-goals

Inclus în MVPAmânat pentru ulterior
1 vs. 1, live, microfon, timerDebate 2v2 / 3v3, turnee extinse
Lobby, confirmare, PRO/CONTRAStreaming video, chat public avansat
Recorder și upload către laptopArbitraj în timp real în timpul discursurilor
STT, transcript, arbitraj OpenAI APIFine-tuning / distilare model local
Scor AI, vot public, raportFact-checking automat complet al internetului
Contestații, audit, eroriEconomie complexă de XP și recompense

3. Arhitectura recomandată

Frontend mobil Next.js/React/TypeScript pe UN telefon ↔ backend Node/NestJS pentru conturi, catalog de moțiuni, faze și upload ↔ storage temporar protejat + coadă de joburi ↔ worker de pe laptop → transcriere ASR + diarizare asistată de ture → JudgeProvider → raport JSON → API → profilurile ambilor jucători. Pentru joc în aceeași încăpere NU este necesar WebRTC/SFU între doi utilizatori.

Nu se recomandă expunerea laptopului ca server public cu port deschis. Workerul local inițiază o conexiune de ieșire autentificată (poll/long-poll/WebSocket către coadă) sau se folosește un tunel securizat. Backendul public nu stochează chei OpenAI în browser.

ComponentăResponsabilitateAlegere propusă
Web appLobby, audio, timeline, rezultateNext.js + TypeScript
Game APIAuth, meciuri, voturi, scoring statusNestJS/Node
Real-timeTimer și event-uri cu timp autoritativWebSocket / Socket.IO
AudioTransport live și captură pe pisteWebRTC + MediaRecorder/recorder server
QueueJoburi idempotente și retryRedis/BullMQ sau echivalent
Laptop workerDescărcare, STT, arbitraj, upload raportPython sau Node, Docker opțional
Judge adapterOpenAI sau local cu același contractJudgeProvider interface
PersistenceMeciuri, audit, scoruri, metadatePostgreSQL + object storage

4. Flux cap-coadă

  • S0 CREATED: creatorul alege moțiunea, acceptă regulile, invită adversar.
  • S1 LOBBY: test microfoane, confirmare consimțământ/informare, verificare moderator și poziții.
  • S2 LIVE: un singur telefon prezintă instrucțiuni audio și vizuale, numără timpul și înregistrează pista comună cu marcaje de fază. Nu există microfoane separate de dezactivat.
  • S3 PER_TURN: la finalul fiecărei ture se sigilează segmentul cu checksum, se înregistrează metadatele și se inițiază upload securizat, în paralel cu tura următoare; la sfârșitul meciului se închide manifestul complet.
  • S4 TRANSCRIBING (incremental per segment): audio-ul turelor încheiate este transcris în paralel cu jocul; speaker mapping este asistat de ture/timestamps și, opțional, diarizare. Fragmentele suprapuse sau incerte sunt marcate pentru verificare.
  • S5 REVIEWABLE: transcriptul este segmentat pe runde și se verifică automat segmentele lipsă / speaker confidence.
  • S6 EVALUATING: același pachet de date intră în OpenAIJudgeProvider sau LocalJudgeProvider.
  • S7 VALIDATING: rezultatul JSON este verificat, citările sunt rezolvate la timestamp, se marchează erorile.
  • S8 PROVISIONAL: votul public și arbitrajul AI se afișează separat; se permite contestație.
  • S9 FINAL: după 24 h fără contestații sau după rezolvare, rezultatul devine definitiv și actualizează ratingul.

5. State machine și evenimente

StareEvenimente acceptateObservație
CREATED / LOBBYjoin, mic_ready, consent, assign_sideStart doar cu 2 jucători
LIVE_OPENINGphase_started, timer_tick, segment_saved3 min per participant
LIVE_REBUTTALphase_started, timer_tick3 min per participant
LIVE_CROSSturn_started, mic_granted3 min întrebări fiecare
LIVE_CLOSINGphase_started, timer_tick2 min fiecare
VOTINGvote_cast, vote_closedVot eligibil din backend
PROCESSINGupload_complete, transcript_ready, judging_readyFără rating până la validare
PROVISIONAL / FINALappeal_opened, appeal_resolvedAudit versionat

6. Schema minimă de date

  • User(id, handle, role, eligibility, createdAt)
  • Debate(id, motion, proUserId, contraUserId, state, ruleVersion, rubricVersion, startedAt, endedAt)
  • Round(id, debateId, phase, speaker, startedAt, endedAt)
  • Recording(id, debateId, singleAudioTrack, location, sha256, durationMs, uploadStatus, encryptionStatus, retentionUntil)
  • TranscriptSegment(id, debateId, speakerId, startMs, endMs, text, confidence, revision)
  • Evidence(id, debateId, submittedBy, url, sourceTitle, verificationStatus)
  • JudgeJob(id, debateId, provider, model, promptVersion, status, retryCount, errorCode)
  • JudgeScore(id, debateId, side, criteriaJSON, total, rationale, citationsJSON, status)
  • AudienceVote(id, debateId, voterId, side, eligible, createdAt) cu unicitate (debateId, voterId)
  • Appeal(id, debateId, applicantId, reason, evidence, status, resolution)
  • AuditEvent(id, debateId, eventType, actor, timestamp, payload)

7. Contract arbitru AI

Interfață logică: JudgeProvider.evaluate(DebateJudgeInput) -> DebateJudgeResult. Adapterul primește doar informațiile indispensabile și returnează obiecte validate strict la runtime.

Input: motion, sides, rules_version, rubric_version, phases, transcript_segments[{speaker,start_ms,end_ms,text}], evidence[], procedural_events[]. Excluse: voturi public, nume personale, rating, avatar, popularitate.

Output: provider, model, prompt_version, rubric_version, scores[{side,arguments:0..30,evidence:0..25,rebuttal:0..25,clarity:0..10,conduct:0..10,total:0..100,reasoning_summary,segment_refs[]}], winner:PRO|CONTRA|DRAW|INSUFFICIENT_DATA, factual_claims[{claim,verification_status,segment_ref}], warnings[], quality_flags[].

Reguli: total=sum(criteria); winner calculat și verificat în backend, nu acceptat orbește de la model; citările trebuie să indice segmente existente; o ieșire invalidă este respinsă; modelul nu decide singur sancțiuni grave.

8. Strategia de evaluare și reducerea costurilor

  • Separă speech-to-text de evaluare. Transcrierea poate rula local (de ex. model ASR optimizat), în timp ce arbitrajul folosește OpenAI API în MVP.
  • Nu trimite 30 de minute audio direct către modelul de arbitraj dacă transcriptul segmentat este suficient.
  • Folosește câte un job idempotent per segment și un singur job de finalizare per meci; retry limitat și cache după hash transcript + model + prompt + rubrică.
  • Un pas deterministic extrage fazele, speakerii, timpul și numărul de intervenții; AI primește doar conținutul de evaluat.
  • Ține config provider=OPENAI|LOCAL, model, version, timeout, max_tokens, retry_count; fără schimbări în UI.
  • Măsoară cost pe meci, latența, stabilitatea scorurilor între rulări și concordanța cu evaluări de referință.

9. Migrarea la LLM local „specializat pentru debate”

  • Faza A: rubrică/prompt în română, output structurat, teste manuale pe 20-50 de transcrieri variate; nu antrena un model de la zero.
  • Faza B: benchmark mai mare, cu arbitri umani care etichetează setul de referință și motivează scorurile; anonimizare și acord explicit pentru utilizarea datelor la antrenare.
  • Faza C: evaluare zero-shot/few-shot a unui model instruct mic, cuantizat, pe hardware-ul disponibil; verifică suportul pentru limba română.
  • Faza D: numai dacă e nevoie, LoRA/QLoRA cu exemple de arbitraj sau distilare pe setul licențiat. Rulează evaluări de bias, consistență și drift.
  • Faza E: shadow mode: OpenAI și LOCAL evaluează aceleași meciuri, dar numai un provider atribuie scorul oficial; schimbarea oficială se face după criterii de acceptare publicate.
  • Important: performanța unui LLM local depinde de CPU/GPU, RAM/VRAM și de numărul de tokenuri; alegerea modelului nu poate fi finalizată fără specificațiile laptopului.

10. API și fluxuri tehnice (propunere)

Endpoint / eventMetodăFuncție
POST /debatesHTTPCreează meci
POST /debates/:id/joinHTTPJoin + rol
POST /debates/:id/startHTTPStart autorizat
debate:phase_changedWebSocketCronometru și permisiuni microfon
POST /debates/:id/recordingsHTTP signed uploadÎncarcă înregistrare / metadate
GET /worker/jobs/nextWorker authenticatedLaptopul preia job
POST /worker/jobs/:id/resultWorker authenticatedRaport transcriere/arbitraj
POST /debates/:id/votesHTTPVot eligibil
GET /debates/:id/resultsHTTPRezultate separate
POST /debates/:id/appealsHTTPContestație

11. Securitate, confidențialitate și eșecuri

  • Cheile API rămân exclusiv în worker/backend; nu în aplicația client. TLS pentru transfer și tokenuri scurte de upload/worker.
  • Înregistrările și transcrierile sunt date personale; acces limitat, loguri de acces, termene de păstrare explicite și ștergere programată.
  • OpenAI API poate prelucra transcriptul în cloud în MVP; utilizatorii trebuie informați. Setările de retenție ale furnizorului trebuie verificate la configurare.
  • Când laptopul este oprit: job queued, retry după reconectare; niciun rezultat AI inventat.
  • Când transcrierea greșește speakerii: job manual_review, fără scor oficial până la corectare.
  • Când răspunsul LLM e invalid sau inconsecvent: schema-validation fail, retry limitat, apoi status analysis_failed.
  • Dacă apar solicitări concurente: limită de joburi active, backpressure și monitorizarea spațiului de stocare.

12. Plan de implementare pe sprinturi

SprintLivrabil verificabil
1Auth, schema DB, configurare moțiune, lobby, state machine, cronometru
2WebRTC/audio, permisiuni microfon, înregistrare pe piste, upload securizat
3Worker laptop, queue, transcriere, editor transcript cu timestamp
4JudgeProvider OpenAI, schema JSON, raport explicabil, testare rubrică
5Vot public eligibil, pagină rezultate duale, audit, contestații
6Observabilitate, rate limits, retenție, accesibilitate, eșecuri, load test

13. Acceptance criteria MVP

  • Un meci poate fi jucat de două persoane față în față pe un singur telefon, cu ghidare vocală, research, ture, înregistrare și finalizare automată.
  • Cronometrul nu poate fi accelerat/schimbat de client; reconnect păstrează starea.
  • Pentru fiecare vorbitor există transcript și timestamp; lipsurile sunt marcate explicit.
  • Raportul AI are exact cele cinci criterii, total verificabil și minimum câte o referință relevantă per justificare semnificativă.
  • Votul publicului nu intră în inputul arbitrului și nu afectează scorul acestuia.
  • Dacă laptopul este oprit, meciul rămâne în coadă și reia procesarea fără duplicat.
  • Schimbarea providerului OpenAI -> LOCAL se face prin aceeași interfață fără schimbări de DB/UI.
  • Operatorul poate investiga incidentul și participantul poate depune contestație.

14. Decizii deschise

  • Specificații laptop: OS, CPU, GPU, RAM/VRAM și spațiu; determină capacitatea STT/LLM local.
  • Mecanism de audio live: peer-to-peer pentru MVP sau SFU pentru scalare; pentru captură robustă poate fi necesară înregistrarea server-side.
  • Retenția audio, transcrierilor, intervalul de păstrare al backupurilor și condițiile publicării.
  • Dacă rezultatul AI este oficial automat în toate meciurile sau numai după anumite verificări de calitate.
  • Pragurile de calitate pentru trecerea modelului LOCAL în producție.

15. Referințe și observații

OpenAI Help: ChatGPT și API au sisteme de facturare separate: https://help.openai.com/en/articles/9039756-managing-billing-for-chatgpt-and-the-api-platform

OpenAI API data controls și retenție: https://developers.openai.com/api/docs/guides/your-data

Versiunea de proiect este propunere arhitecturală, nu dovadă de existență a infrastructurii. Revizuiți GDPR/termeni de utilizare înainte de lansare.

16. Amendament MVP: experiența single-phone

Scenariu principal: două persoane stau lângă un telefon; deschid site-ul mobil RoDebate prin HTTPS; selectează două identități de jucător, moțiunea și rolurile; acceptă informarea privind înregistrarea; jocul este coordonat de ghidul vocal, cronometru și butoane mari. Un singur microfon captează întreaga încăpere. Nu este nevoie de apel audio între dispozitive.

16.1 Catalog inițial și administrare

Cele cinci moțiuni concrete sunt conținut editorial de completat de admin, nu trebuie hardcodate în client. Rejucarea aceleiași moțiuni este nelimitată, inclusiv cu același adversar; fiecare match este independent și reține motionVersion.

FazăComportamentMVP?
Admin creeazăExact 5 moțiuni inițiale, fiecare cu ID, text, descriere neutră, categorie, status, versiuneDa
Listă jucabilăDoar moțiunile publicate și marcate playableDa
Propunere utilizatorDraft de moțiune cu audit și verificare adminUlterior
Vot comunitarO persoană / un vot; cvorum și prag configurate și publicateUlterior
Aprobare finalăAdmin verifică eligibilitatea și publică moțiunea votatăUlterior

16.2 Identități pe un singur dispozitiv

  • Utilizatorul A se autentifică pe telefon și invită jucătorul B prin selectare/invitație sau cod QR.
  • Alternativ, B joacă sub un token temporar verificabil; ulterior revendică participarea în cont. Un token nu permite revendicări multiple.
  • Ambele conturi sunt legate la matchId; raportul apare în istoricul ambilor. Niciun cont nu are dreptul să modifice scorul sau transcriptul final.
  • UX: ecranul afișează clar „Rândul lui PRO” / „Rândul lui CONTRA”, plus iconurile și timpul, fără a presupune că telefonul poate diferenția sigur vocile.

16.3 Timp și interacțiuni

Total competitiv: 22 min + research implicit 3 min + briefing/tranziții. Timpul nefolosit nu se transferă. Butonul de terminare cere o confirmare scurtă; comenzile vocale sunt suplimentare și nu trebuie să încheie automat tura fără confirmare. Pause/resume implică evenimente server-side cu timestamp și motiv.

FazăDurată implicităAcțiune
Pregătire / research3 min (admin: 1–5)Căutare, citit, notițe, pregătire simultană
Discurs PRO / CONTRA3 min / persoanăStart vocal; „Am terminat” închide devreme
Replică PRO / CONTRA3 min / persoanăCronometru; tranziție vocală
Întrebări încrucișate6 min total3 min întrebări PRO, 3 min întrebări CONTRA
Concluzii CONTRA / PRO2 min / persoanăÎnchiderea voluntară sau la expirare
Vot publicopțional 3 minSe omite în MVP fără spectatori

16.4 Moderator vocal: fără LLM continuu

Pentru cost minim și predictibilitate, folosește o mașină de stări deterministă și mesaje românești predefinite redate cu speechSynthesis din browser sau fișiere audio locale. LLM este apelat DUPĂ meci pentru evaluare. Instrucțiunile vocale se pot declanșa doar după gest explicit al utilizatorului, pentru a respecta restricțiile autoplay din browser; oferă întotdeauna o copie text vizibilă.

  • Exemple: „Moțiunea este…”, „Aveți 3 minute pentru documentare”, „PRO, este rândul tău”, „Mai ai 30 de secunde”, „Vă mulțumesc, încep analiza”.
  • Moderatorul rămâne neutru: poate reda sfaturi pedagogice generale, identice ca reguli pentru ambele poziții, doar înaintea rundelor și fără cronometru activ; nu generează contraargumente pentru un jucător, nu oferă aprecierea pozițiilor și nu divulgă verdicte în timpul jocului.
  • Buton clar pentru pauză, volum, repetarea instrucțiunilor și oprirea înregistrării; redarea TTS ar trebui evitată pe durata discursului sau identificată ca audio al sistemului.

16.5 Înregistrare pe un singur microfon și robustețe

Captură MediaRecorder/getUserMedia sub HTTPS; track unic cu timestamp-uri pentru phases și user-action events. Test inițial al microfonului, permisiunilor și bateriei; recomandare să se țină telefonul în centrul mesei. Salvare pe bucăți într-un buffer IndexedDB când este disponibil, upload resumable cu checksum și stare de recuperare.

Limitare de produs: un microfon NU oferă identificare perfectă a vorbitorului, mai ales când participanții se suprapun sau când vocea sintetizată a moderatorului intră în înregistrare. În fazele cu un singur vorbitor, etichetele sunt bazate pe tură; în cross-examination folosește „cine vorbește acum”/buton de predare, apoi diarizare locală și semnalizare a incertitudinii. Înregistrarea audio incompletă nu primește arbitraj definitiv automat.

Dacă telefonul intră în lock/sleep, browserul poate suspenda înregistrarea sau TTS. Menține ecranul activ când este disponibil, afișează avertisment și oferă reluare sau anulare; testează pe iOS Safari și Android Chrome. Nu promite captură continuă în background.

17. Model de date și endpointuri suplimentare

  • Motion(id, slug, title, neutralDescription, category, version, status: DRAFT|PUBLISHED|ARCHIVED, playable, createdByAdminId, publishedAt)
  • MotionProposal(id, authorId, title, description, moderationStatus, votingStatus, createdAt) [FUTURE]
  • MotionProposalVote(id, proposalId, userId, value, createdAt) cu unique(proposalId,userId) [FUTURE]
  • MatchPlayer(matchId, userId|claimToken, side, joinedAt, claimStatus)
  • MatchConfig(matchId, researchSeconds, ruleVersion, motionVersion, publicVotingEnabled, judgeProvider)
  • MatchEvent(matchId, sequence, phase, actor, eventType, timestamp, audioOffsetMs, metadata)
  • TranscriptRevision(matchId, segmentId, oldText, newText, requestedBy, approvedBy, reason)
  • GET /motions?playable=true; POST /admin/motions; PATCH /admin/motions/:id; POST /matches; POST /matches/:id/join
  • POST /matches/:id/events (start_phase, finish_turn, pause, resume); POST /matches/:id/recordings; GET /me/matches; GET /matches/:id/report
  • POST /matches/:id/claim; POST /matches/:id/appeals; POST /admin/motion-proposals/:id/approve [FUTURE]

18. State machine ajustată

CREATED → PLAYERS_LINKED → CONSENTED → DEVICE_CHECK → RESEARCH → BRIEFING → LIVE_PHASES → RECORDING_FINALIZING → UPLOAD_PENDING → TRANSCRIBING → SPEAKER_REVIEW (dacă e necesar) → EVALUATING → PROVISIONAL → FINAL. Stări alternative: PAUSED, CANCELLED, UPLOAD_FAILED, ANALYSIS_FAILED, APPEALED.

Fiecare tranziție este server-authoritative și idempotentă; evenimentele audioOffsetMs aliniază transcriptul cu cine avea tura. Moderatorul vocal este prezentare, nu sursa de adevăr pentru starea jocului.

19. Arbitraj, cost și migrare locală

MVP: laptop worker consumă incremental segmentele audio încheiate, transcrie local dacă hardware-ul permite și poate preanaliza turele în paralel. După încheiere și completarea tuturor segmentelor, trimite transcriptul structurat, moțiunea, fazele și sursele către OpenAI API pentru verdictul final. Cheia API există doar în backend/worker; abonamentul ChatGPT nu include automat credit API. Utilizează hash+versiune rubrică+model pentru cache, rate-limit, buget maxim per match și retry controlat.

Ulterior: LocalJudgeProvider execută un model instruct cuantizat, cu prompt/rubrică, evaluare benchmark față de arbitri umani, apoi opțional fine-tuning LoRA pe date autorizate. Nu se antrenează un LLM de la zero. Testele trebuie să compare consistență, bias PRO/CONTRA, robustețe în limba română și cost total pe meci. Nu se recomandă descrierea drept «antrenat exclusiv pe dezbateri» dacă modelul de bază a fost preantrenat general.

Regula de aur: verdictul nu este obținut din chat-ul ChatGPT personal; aplicația folosește OpenAI API cu cheie și facturare distinctă. În migrarea complet locală, nu mai trimite transcrieri sau audio către cloud pentru arbitraj; alte servicii cloud ale site-ului pot rămâne active.

20. Criterii de acceptare adăugate

  • Admin poate crea și publica exact 5 moțiuni, afișate în lista PLAYABLE fără deploy de frontend.
  • Aceeași moțiune poate avea două meciuri diferite, cu același adversar, fără suprascriere de scor/transcript.
  • Două profiluri distincte sunt legate de jocul pornit de pe un singur telefon și văd raportul în istoricul lor.
  • Telefonul redă corect instrucțiuni de moderator și înregistrează pe același microfon, cu mesaje vizuale de fallback.
  • Research implicit 3 minute, opțiune administrativă 1–5 minute; „Am terminat” încheie o tură fără transferul timpului.
  • Segmentarea speakerilor încrucișați marchează zonele ambigue, nu produce automat o atribuire certă.
  • Jocul poate rămâne pending dacă laptopul este offline; procesarea la revenire nu dublează rezultatul.
  • În MVP fără spectatori, pagina arată „vot public indisponibil” și nu pretinde un câștigător popular.
  • Transcriptul din profil include runde, speaker, marcaje de timp, scoruri, justificări și proces de contestație.
  • Providerul OPENAI poate fi înlocuit cu LOCAL fără modificarea contractului API sau a componentelor de rezultat.

21. Decizii pentru implementarea imediată

Setări propuse implicit: research 180 s; un telefon / un microfon; două conturi legate; trei intervenții vocale per fază (start, 30 s rămase, final); moțiuni 5 publicate de admin; fără votul publicului în meciurile offline; backend stochează în siguranță audio doar tranzitoriu; un singur job OpenAI per meci după transcriere locală.

De confirmat înainte de publicare: textele exacte ale celor cinci moțiuni, mecanismul de revendicare a unui jucător invitat, perioada de retenție și politica GDPR, acceptarea transcrierilor de către jucători, limita pentru costul AI per meci, platformele mobile suportate și specificațiile laptopului.

22. Procesare incrementală pe ture și pipeline asincron

Definiții: „fază” = tip de activitate (argument inițial, contraargument, întrebări, concluzie); „tură” = intervalul în care un jucător are dreptul la cuvânt; „segment” = fișier audio sigilat pentru acea tură. În faza de întrebări încrucișate, tura poate fi fragmentată în subsegmente atribuite vorbitorului activ. Nu trimitem audio înregistrat din research, în mod implicit.

Pipeline non-blocant: CAPTURE_ACTIVE → SEAL_SEGMENT → UPLOAD_QUEUED → UPLOADING → TRANSCRIBING → TRANSCRIBED → PARTIAL_ANALYSIS_READY. În paralel, motorul poate porni CAPTURE_ACTIVE pentru următoarea tură. La final: MANIFEST_CLOSED → ALL_SEGMENTS_READY → FINAL_EVALUATING → RESULT_PROVISIONAL. Orice eroare produce RETRY_PENDING sau REVIEW_REQUIRED, nu un scor fabricat.

Tehnic: browserul pornește MediaRecorder pe tura activă, închide pista/segmentul la transfer, memorează Blob-ul + metadatele în IndexedDB, apoi îl urcă prin URL semnat sau endpoint autorizat. Workerul de pe laptop preia joburile prin conexiune outbound securizată. Uploadul trebuie să aibă debounce/retry cu exponential backoff și chei de idempotency (matchId, turnId, attempt), checksum SHA-256 și confirmare din backend. Browserul nu poate garanta upload în background după închiderea paginii; afișăm explicit segmentele nesincronizate.

Evaluarea incrementală poate crea fișe interne pentru fiecare tură: claims, evidence_mentions, rebuttals, transcript_offsets, confidence. Acestea sunt date pregătitoare, nu scoruri oficiale. Verdictul este emis numai după ce toate segmentele au fost primite, atribuirea vorbitorilor este suficient de sigură și arbitrul vede ansamblul meciului. Pentru reducerea costurilor MVP, implicit transcriem fiecare tură, dar trimitem către OpenAI pentru arbitraj o singură cerere finală; preanaliza AI pe tură este feature flag opțional.

23. Ecranul de joc: ceas de șah cu două zone

Layout mobil: header cu moțiunea + faza; două panouri PRO/CONTRA cu nume, rol, timer mare monospaced (min:sec) și stare ACTIVE/WAITING; zonă centrală pentru instrucțiunea moderatorului; footer cu indicator REC, progres upload/transcriere, butoane Pauză, Repetă instrucțiune, Predă tura. Panoul activ are contrast ridicat; cel inactiv este estompat. Nu se folosesc doar culori pentru distingerea activității.

Interacțiune: apăsarea panoului activ sau „Predă tura” încheie segmentul și cere confirmare/gest anti-atingere accidentală; schimbul de ceasuri se face atomic pe server cu expectedVersion și turnId. Clientul afișează imediat tranziția optimistă doar dacă este sigură, reconciliază cu serverul și nu permite multiple comutări din dublu tap. Expirarea timerului activează aceeași tranziție. Pentru o fază cu durată fixă, ceasul nu poate fi mutat în mod arbitrar înainte și înapoi.

Timerul corect se derivă din serverStartedAt, remainingMs și starea RUNNING/PAUSED, sincronizate prin WebSocket/SSE; requestAnimationFrame sau interval client este strict afișaj. La reconectare se reface starea fără a reseta timpul. Moderația vocală anunță „Este rândul lui ...”, „Mai ai 30 de secunde”, „Timpul a expirat” și folosește texte predefinite TTS/Web Speech, cu fallback text; audio TTS este exclus sau marcat distinct în înregistrare.

24. Mod începător: sfaturi la începutul fiecărei runde

În ecranul pre-game: toggle „Ascult sfaturi la începutul fiecărei runde” (implicit OFF; se poate seta pentru fiecare jucător sau la nivel de meci). Sfaturile sunt redate înainte de pornirea ceasului, ideal cu text și buton „Sari peste”. Se folosesc șabloane aprobate de admin, nu apeluri OpenAI în timp real.

Exemple de șabloane: argument inițial: „Spune teza, două motive și un exemplu”; contraargument: „Identifică afirmația adversarului și explică unde nu funcționează”; întrebări: „Adresează întrebări scurte, legate de afirmațiile auzite”; concluzii: „Rezumați cele mai puternice două idei, fără dovezi complet noi”. Aceleași sfaturi structurale sunt disponibile atât pentru PRO, cât și pentru CONTRA. Opțiunea nu influențează arbitrajul.

25. Contracte și criterii de acceptare pentru noul flux

Date: Turn(id, matchId, phaseId, playerId, ordinal, startedAt, endedAt, remainingMs, state); AudioSegment(id, turnId, sequence, storageKey, checksum, durationMs, status, uploadedAt); ProcessingJob(id, segmentId, type, attempts, status, providerVersion); PartialFinding(segmentId, claimsJson, evidenceJson, rebuttalsJson, sourceOffsets); MatchProcessingStatus(matchId, totalSegments, uploaded, transcribed, pending, failed, finalEligible).

API/evente sugerate: POST /matches/:id/turns/:turnId/complete; POST /matches/:id/segments/init-upload; POST /segments/:id/confirm; GET /matches/:id/processing-status; WS match.turn.changed, audio.segment.accepted, transcript.segment.ready, match.processing.blocked, match.result.ready. Includem actor, server timestamp, expectedVersion și idempotencyKey.

Acceptance: (1) segmentul turei PRO începe upload în timp ce CONTRA vorbește; (2) dacă upload eșuează, CONTRA continuă și segmentul PRO se reîncearcă; (3) dublu tap nu sare două ture; (4) timerul mare evidențiază vorbitorul activ și se reface după reload; (5) opțiunea de sfaturi redă un mesaj scurt înaintea fiecărei faze, fără să consume timpul și fără sugestii părtinitoare; (6) lipsa unui segment blochează verdictul final, nu jocul în curs; (7) ambii jucători văd transcriptul și scorul final în cont, cu starea de procesare transparentă.

Decizie practică MVP: upload/transcriere imediat pe tură; arbitraj final unic OpenAI după terminarea jocului; evaluarea AI per tură se poate activa ulterior dacă latența sau cerințele de produs justifică extra costul. Analiza pe tură este asincronă și nu modifică ordine/timer în timpul rundei.