# Native referees and competitions (SDK 1.11) Native sessions provide reusable durable commands, identities, revisions, readiness, server clocks, outcomes, result-derived Elo, invitations and recovery. Competitions add registration, locked rules, pairings, standings and organizer actions. Chess, tic-tac-toe, private-answer quizzes and role-based workflows use the same service and SDK. No external engine account or uploaded server program is required. Existing casual multiplayer and reviewed external HTTP/WSS backends remain supported. ## Declare reviewed rules Package this alongside index.html as hackemist-services.json: ```json { "schema_version": 1, "modules": ["sessions", "competitions"], "sessions": { "standard": {"engine": "chess", "version": 1}, "noughts": {"engine": "tic_tac_toe", "version": 1}, "assessment": {"engine": "quiz", "version": 1}, "approval": { "engine": "workflow", "version": 1, "config": { "states": ["draft", "review", "approved"], "initial": "draft", "final": ["approved"], "transitions": { "submit": {"from": "draft", "to": "review", "role": "participant"}, "approve": {"from": "review", "to": "approved", "role": "organizer"} } } } } } ``` Publish through the ordinary release flow. Competitions requires sessions. Rule names and evaluator versions are retained with existing sessions; a retained rule name cannot be switched to a different engine/version. Introduce a new name for a different ruleset. Evaluators are reviewed platform code; declarations cannot execute arbitrary JavaScript, Python or user-authored expressions. ## Open and play ```js await Hackemist.ready; await Hackemist.sessions.enroll('Ada'); const created = await Hackemist.sessions.create({ ruleset: 'standard', clock: {initialSeconds: 300, incrementSeconds: 2}, rated: true, pool: 'blitz' }); // Share created.inviteCode. The other signed-in player joins it: // const joined = await Hackemist.sessions.join(code); const game = await Hackemist.sessions.open(created.id, { onChange: render, onError: showConnectionError }); await game.command({type: 'ready'}); // both players explicitly confirm // On the current player's turn: await game.command({type: 'move', move: 'e2e4'}); // On uncertain response, retain the command and expose a Retry action: // await game.retry(); // When leaving the screen: game.close(); // releasing a UI handle does not resign a game ``` The session controller re-reads trusted SQL snapshots after room hints and polls only while visible/online. Connected hints use the configured session reconciliation cadence (30 seconds by default), with server-deadline checks scheduled sooner; polling fallback uses the native watch cadence. Hints contain resource IDs/revisions, never trusted moves/results. Without room capacity the same durable session continues through polling. Timers use server clock snapshots; clients cannot select timestamps, winners, scores or rating changes. A timeout command is validated against server time. Disconnecting does not pause a running clock. `onTransport` reports connecting, connected or polling independently of durable service errors. One native hint room is used per launch; additional watched sessions continue through polling. Explicit handle closure leaves its hint room and does not resign the durable game. Bursts of room hints coalesce at the operator's `session_hint_refresh_ms` interval (1 second by default), retaining a final refresh rather than dropping the last update. Authoritative command acknowledgements update the local board immediately. Direct sessions.command requires expectedRevision. Mutations accept operationId; use Hackemist.operationId() once and retain it with the exact input for retries. Stale game-action revisions fail with SERVICE_CONFLICT. Readiness while waiting and first-seat hint-channel attachment commute with concurrent updates; hint attachment does not advance the game revision. Replays reauthorize access and return the current authorized projection plus committedRevision, rather than private historical snapshots. Do not create a new operation ID to retry an uncertain move. Chess uses legal UCI moves, including castling, en passant and promotion, with checkmate, stalemate, dead material, automatic fivefold/75-move draws and on-turn threefold/50-move claims. Commands include ready, move, claim_draw, resign, offer_draw, accept_draw, decline_draw and timeout. There is no server-side AI. Rated chess/tic-tac-toe requires a clock and at least two accepted moves. Elo uses an independent logistic calculation (difference capped at 400), K=40 for a new player's first 30 games, then 20 or 10 after a 2400 peak. It is not an official federation rating. Walkovers, readiness forfeits, cancellation and account deletion do not award Elo. Rating pools are isolated by app, live/preview and ruleset. For tic-tac-toe, send {type:'move',cell:0} (squares 0–8). For workflows send {type:'transition',action:'submit'} or the appropriate organizer action. Workflows have no competitive scores. For quizzes an organizer supplies private.questions at session/event creation, each with prompt, options and answer index. Keep real answer keys out of static app source. Send {type:'answer',question:0,answer:1}; each player answers once. Other answers and pending-round points are withheld until both answers commit. The server computes points and outcomes. ## Competition lifecycle competitions.create(config) creates a draft; competitions.action(id,'open', {expectedRevision}) opens registration. Players use competitions.register(id, {acceptRules:true,code}) for private invitations, or omit code for public events. Opening locks rules. The organizer uses action 'advance' to start/advance rounds, 'walkover' with session and winner for an administrative decision, or 'cancel'. Settings can change only on a draft. An unfinished round cannot advance. Configuration includes name, format, ruleset, clock, capacity, closesAt in server milliseconds, and optional rounds, cut, rated, pool, visibility ('public'/'invite'), readyMinutes, minRating, maxRating, allowProvisional, description and rules. Formats are swiss, round-robin, knockout and swiss-knockout. Swiss pairings prefer opponents not previously met and rotate byes; this scheduler does not claim FIDE Dutch pairing certification. Ties use Buchholz, Sonneborn–Berger, wins and seed. Drawn knockout games require an organizer advance to create a color-swapped rematch; there is no automatic unbounded replay loop. Byes/walkovers count for standings, without Elo. Known schedule size is checked against visible operator round/session quotas before starting; rematches require additional quota. sessions.profile/list/history/leaderboard and competitions.get/list recover resources after reopening. List pages use explicit cursors and operator budgets. Competition organizers/participants can inspect their assigned sessions; ordinary project owners and creator file tools receive no blanket runtime-data access. Private quiz keys are removed from all participant/public projections. Account export includes participating sessions; deletion anonymizes shared identities, retains others' completed history and closes affected games without rated wins. All durable writes reuse guarded native SQL storage and idempotency receipts. Active sessions reserve their configured state budget plus a commit allowance in the same project byte quota, and rated-result records are admitted before clocks start. Unrelated record writes cannot consume that reserved result capacity. Terminal results release unused reservations. Indexed due deadlines are resolved on new session admission as well as by active clients, freeing abandoned games without a permanent game tick loop. The reserve and cleanup budgets are visible operator settings and included in existing native-table backups. There is no schema migration, process per match, continuous server tick loop, arbitrary backend execution, or cloud-file hosting. Backups include these native tables through the existing backup/restore paths. See resource-limits.md for operator controls, persistence, plan overrides and infrastructure requirements.