Result verification: the resultSource model
Every resolved match on Braket records how its result was established, in matches.resultSource. This one field is the backbone of Braket's verification story: it lets a tournament mix hand-entered results, player reports, game-verified results, and AI-video-verified results, and it lets a future integration replace manual reporting without any schema change. This chapter documents the five sources and the report to confirm to dispute flow that connects them.
The five result sources
matches.resultSource is one of:
resultSource |
Set when | Trust basis |
|---|---|---|
player_report |
A player report is confirmed, or an uncontested report auto-confirms after grace | Two players agree, or one reports and the other does not contest in time |
admin |
An admin sets or overrides the result | Human authority |
game_client |
Both game clients submit agreeing commitments (dual attestation) | Two independent machine attestations match |
ai_video |
Both sides' videos are reviewed and the AI referee agrees above the confidence floor | Two independent recordings agree |
bye |
A bracket slot advances automatically because one side is absent | Structural (no opponent) |
Because the source is a field on the match and not a separate code path, a game integration that lands later simply starts producing game_client results in place of player_report ones. Nothing else about the bracket, standings, ladder, or trophies changes.
The related report table result_reports records each individual report with a channel of player or game_client. Game-client rows additionally store the SHA-256 resultHash and the raw payload (kept for admin forensics on a hash mismatch).
Player report to confirm to dispute
For matches without game integration, results are established by the report/confirm/dispute flow in tournamentService. It only operates on a match that has opened (opensAt reached) and is not already confirmed or forfeited.
- Report. A participant reports the winner (must be one of the two sides) and optionally the score. This inserts a
result_reportsrow (channel: 'player').- If the opponent has already reported the same winner, the match confirms immediately with
resultSource: 'player_report'. - If the opponent has already reported a different winner, the match goes to
disputed. - Otherwise the match moves to
awaiting_result, waiting for the opponent to confirm or dispute.
- If the opponent has already reported the same winner, the match confirms immediately with
- Confirm. The opponent confirms the standing report. The match confirms with
resultSource: 'player_report', taking the reported winner and scores. Confirm is only valid fromawaiting_resultand requires an opposing report to exist. - Dispute. The opponent disputes instead. A dispute report is recorded and the match goes to
disputed, where an admin decides. Dispute is only valid fromawaiting_result.
In team battles any roster member may report, confirm, or dispute for their side; the acting user is resolved to their side's captain id so agreement stays a two-row check.
Auto-confirm grace
An uncontested single report does not hang forever. A lazy sweep, sweepAutoConfirmations, runs on page loads (there is no background scheduler in the PoC) and finalizes any match that:
- is in
awaiting_result, - actually went live (
startedAtis set: this is the adversarial guard, a report against an absent opponent never auto-confirms), and - whose window
closesAtis older than the grace cutoff,now - config.resultGraceHours(default 24 hours).
Such a match is finalized to the last report's claimed winner and scores, with resultSource: 'player_report'. So the pattern is: report, then if the opponent neither confirms nor disputes within 24 hours after the window closes, the report stands automatically. A match that never went live is deliberately excluded and left for an admin.
Admin override and disputes
Admins are the final authority and can resolve any match regardless of reports:
- Set result / forfeit. An admin sets the winner (must be one of the two sides) and optional scores. The match resolves with
resultSource: 'admin', eitherconfirmedor, if a forfeit,forfeited. If the downstream bracket match has already resolved with a different winner, the override is refused to protect bracket integrity (error.downstreamResolved). - Disputes. Any match in
disputed(from conflicting player reports, a game-client hash mismatch, or an inconclusive AI-video review) surfaces to the admin dispute queue with all reports and payloads attached. The admin picks the winner, which resolves it asadmin.
Game-client and AI-video results in this model
The two automated sources plug into the same states:
game_client(dual attestation). Each side's game client POSTs its own game-defined result JSON plus a SHA-256 over the exact bytes. A lone submission parks the match inawaiting_result(so the manual grace machinery can still take over); two submissions whose hashes agree confirm withresultSource: 'game_client'; two whose hashes disagree escalate todisputed. This is the protocol in The game-client result protocol (dual attestation).ai_video. Each side attaches a recording. When both are present the AI referee reviews; agreement above the confidence floor (REFEREE_MIN_CONFIDENCE = 80) confirms withresultSource: 'ai_video'; anything less routes todisputedwith the clips attached. This is AI referee.
In both cases the honest fallback is the same human dispute flow, so an automated verifier can never record a wrong result; at worst it forces the match into the dispute queue.
Audit log
Sensitive admin and secret actions are recorded in a per-tenant audit log (audit_log: tenantId, actorSteamId64, action, and metadata) so that result overrides, key changes, and API-key mints are traceable. The audit log is included in the signed data export (see Trust and data).