Skip to content

Add server-side scrim endpoint (POST /api/v1/scrim) with atomic volume budget #160

Description

@razam-sherwani

Dependency: READY — unblocked 2026-08-18. The former #169 blocker was closed as not planned: #168's default now applies to every new ladder, so a scrim ladder stood up today inherits maxQueuedPerTeam = 10 and no backfill is needed. Closes #112 when it lands. See the turnstile correction in the comments before implementing.

Summary

Add POST /api/v1/scrim { taBotSlug, count } so students can run rate-limited practice matches against TA bots server-side instead of relying entirely on the locally-shipped obfuscated binary. See OBFUSCATION_PITCH.md §6 for full design rationale (Option E / J-centric recommendation).

Requirements:

  • Resolve the TA-bot team server-side (TA-bot UUIDs never appear in the request/response).
  • Enforce an atomic, windowed volume budget — one SQL statement, not read-then-write:
    INSERT INTO scrim_usage (team_id, ladder, window_start, count) VALUES (..., 1)
    ON CONFLICT (team_id, ladder, window_start)
    DO UPDATE SET count = scrim_usage.count + 1 WHERE scrim_usage.count < :cap
    RETURNING count;
  • Concurrency check must include created/scheduling states, not just waiting/in_progress (the current Set.of(waiting, in_progress) in GameMatchService.countTeamQueuedMatchesByLadder is a hole a team can sit in).
  • Return Retry-After on rejection.
  • Write an audit row (this table doubles as attribution evidence and as the only telemetry available today — there's no actuator, and Prometheus only scrapes postgres_exporter).
  • Carries @RequireTurnstile.

Closes #112

This is the same fix needed for the existing check-then-act race described in #112 ("I introduced a check-then-act race in the scrimmage rate limiter... we should fix this in the future"). Implement the atomic upsert pattern for both the new scrim budget and the existing rate limiter, and close #112 when this merges.

Suggested starting budget

~20/day, 60/week, burst 2 — then tune from the scrim_usage table rather than debating the number up front.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    High PriorityenhancementNew feature or requestreadyNo open blockers - can be started now

    Type

    No type

    Fields

    Stage

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions