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.