Developer handoff

Everything needed to replace the mocked service layer with a real backend and GPU worker.

Architecture
Video never touches the app server; workers write back results.
Browser (React / TanStack Start)
   |  signed upload URL
   v
Object storage  --->  Queue  --->  Python GPU worker
   |                                  |  YOLO detect -> BoT-SORT track -> team classify
   |                                  |  camera motion -> homography -> metre coords
   |                                  |  event detection -> metrics -> xG / xT
   v                                  v
Postgres (RLS, organization_id)  <---  worker writes results + confidence
Database schema
Postgres with row-level security scoped by organization.
organizations(id, name, type, country)
members(id, organization_id, user_id, role)
teams(id, organization_id, name, age_group)
players(id, organization_id, team_id, jersey_number, primary_position, ...)
matches(id, organization_id, home_team_id, away_team_id, kickoff_at,
        pitch_length_m, pitch_width_m, video_analysis_status, data_quality_score)
match_videos(id, match_id, storage_path, source_type, fps, resolution)
analysis_jobs(id, match_id, video_id, status, progress_percent, model_version,
              confidence_score, quality_flags jsonb, worker_job_reference)
tracking_frames(id, match_id, period, timestamp_seconds, objects jsonb)
match_events(id, match_id, event_type, period, timestamp_seconds,
             x_start_m, y_start_m, primary_player_id, outcome,
             confidence, source, validation_status, xg)
player_match_stats(id, match_id, player_id, minutes_played, impact_score,
                   metric_payload jsonb, confidence_payload jsonb, heatmap jsonb)
team_match_stats(match_id, team_id, possession, ppda, xg, ...)
validation_items, clips, reports, exports, integrations, audit_log

-- every table: organization_id + RLS policy using has_org_access(auth.uid(), organization_id)
-- GRANT SELECT, INSERT, UPDATE, DELETE ... TO authenticated; GRANT ALL ... TO service_role;
API contract
Mirrors src/lib/api/types.ts, implemented today against mock data.
POST /analysis-jobs            queue a job for a match video
GET  /analysis-jobs/:id        status, stage, progress, quality flags
GET  /matches/:id/tracking     ?period&start_second&end_second&sample_rate
GET  /matches/:id/events       ?event_types&player_id&min_confidence
POST /matches/:id/identity-map assign tracking IDs to players
POST /events/:id/verify        analyst verdict + corrections
POST /clips | /reports | /exports
POST /webhooks/worker-callback signed callback from GPU worker
Honest limitations
Ship these caveats with the product

Single-camera video cannot guarantee player identity through occlusions and shirt-number ambiguity; identity carries a status and confidence.

Physical metrics from video are estimates and are not equivalent to GPS/LPS wearable data. Do not use them for medical or load-management decisions.

Event attribution accuracy drops in crowded penalty areas, set pieces, and during camera pans.

xG, xT and impact scores are model outputs whose weightings are configurable and should be documented in every exported report.