Loading…
Loading…
Loading…
…
No submissions yet.
Gate-approved questions waiting for launch votes. Review the terms and vote to turn a question into a live market.
Loading…
Who adjudicates, and what is true about them: mutability and its mechanism, the named-contributor dependency, permission records, and the exclusion reason where one applies. A market's matched rows are frozen onto it at convert - editing a row here never rewrites the terms of a market already trading.
Loading…
Who plays whom, which round, when - never results. The question
gate reads this as its <schedule> block, so the closed-world
existence check stops blocking real events. Every row is HUMAN-entered - the
feed below only proposes. Names get a tick when they match the player lookup
(aliases included, case-insensitive).
Loading…
The intake pipeline: the harvester detects fixture-relevant notices and lists them here with links. Follow the link, screenshot the schedule graphic, Propose from image (or paste a screenshot straight into the box), review, Add all.
event | round | A vs B | 2026-08-17T10:00 (times UTC; append KST to auto-convert)Renders SOOP's notice board (a JS SPA) every few hours, archives notices before their ~2-month deletion, and DETECTS fixture-relevant posts - each becomes a linked row above awaiting the human graphic pass. It never writes fixture data. A failed render is a regression - the state line below says which state you're in.
Parsed schedule lines land on the Fixtures leaf (with manual entry for what only eyes can read).
Cast publication as a fact about a STAGE (held-questions release anchor). Stamp a stage when its English cast publishes; matching held questions show "ready to release" in Triage - the release itself stays a human decision.
Loading…
Nightly sync of tracked players' career pages from eloboard.com (used with the operator's permission - non-commercial, a few reads per day in the KST night/morning window, sequential with a delay, attributed by site name). Downstream code reads OUR stored snapshots, never the live site. The run happens in the main loop; each KST day runs at most once, stalest players first.
The scraper fetches ONLY the ids listed here - never discovers players by walking
id ranges (that's the scraping behaviour the operator is fighting). The
eloboard id is the wr_id in the player's page URL:
board.php?bo_table=bj_list&wr_id=12.
Loading…
Upload images and reference them from Content text as
{img:tag} (currently rendered on the About page). Tags are
lowercase letters/digits/dashes; re-uploading a tag replaces the image.
{br} in Content text adds an extra vertical gap;
{img:tag:indent} renders the image indented + narrower (so site
screenshots read as illustrations); [label](#route) and
[label](mailto:...) render as links. About/T&C sections run
h1/p1 … h20/p20 (about.* /
terms.*); the footer halves are footer.left /
footer.right (multi-line). An EMPTY block hides that key's
built-in default.
Loading…
What the sweep model judges (conduct only) and what it must never flag (spoilers, truth, market talk, bare profanity). Version-stamped on every verdict.
Loading…
Open reports, oldest first. Spoiler reports may have already auto-hidden the comment (the fast path) - resolving here is the human decision.
Loading…
The sweep runs behind the site (comments are visible on posting; it can retract). This is NOT an approval queue - clearing a false positive is the common case and takes one click. A cleared comment is never re-flagged.
Loading…
Loading…
Loading…
Banned accounts cannot submit questions (they can still trade). Manual add/remove only.
Loading\…
Every bid on the selected market, matched portions split out. Partially matched bids show two rows: the matched slice and the still-open remainder.
Pick a market.
Hide old test markets from the front page. Nothing is deleted - evidence, scores and history survive, and a hidden market's page still opens by direct link. Resolvable as normal from Admin → Resolve.
Gate jobs waiting for (or receiving) an LLM answer, then recent completions with how long they took. Auto-refreshes every 5s while open.
Loading…
Loading…
Pick a market from the dropdown above (or click a market on the Markets tab).
Registered accounts (email-confirmed signups - the seeded "Acting as" identities aren't accounts). Approving creates the account's starting balance and emails them. All accounts are admin role for now (agreed 2026-07-12); role demotion is a later milestone.
Approvals live in Housework → Triage. Click a username to open its account view.
Loading…
Loading…
Loading…
Loading…
Runs the eval set against each ticked model. Repeat >1 runs the ticked set that
many times over - model output varies run to run, and repeats show by how much.
Bare ids go direct to Anthropic; vendor/model ids route via OpenRouter
(both lists: Admin → LLM → Models). Results land on the Scoring tab.
One entry per line: vendor/model, optionally
| provider/quant to pin the serving provider (becomes its own picker entry -
model@provider/quant - with its own run history; fallbacks off,
require_parameters on), optionally | Display name.
A comma-separated list in field two (| deepinfra/fp8, parasail/fp8) makes a
pool: OpenRouter routes within that allowlist by its own algorithm
(fallbacks on inside the pool). The pool id sorts the providers, so reordering the
line keeps its run history. Works for the gate too - pick the pool entry as the gate model.
One entry per line: model_id | Display | input | 5m cache write | 1h cache write | cache hit | output
($/MTok; the 1h write is informational - runs and the gate use the 5m tier). Legacy 6-cell
lines without the 1h column still parse. Prices come from the
Anthropic pricing page
- Check catalog flags new/retired ids but the API exposes no prices, so keeping these current is a human job.
One [i18n.key] header per block; the text under it replaces that
key's default wording sitewide (reload a tab to see it). Delete a block to fall back to the
built-in default. The full key list lives under System → Keys.
Text in ANY key supports {br}, {img:tag}, [label](#route) and blank-line
paragraph breaks (the About/T&C commands), plus site variables:
{gate_eta} renders the typical gate review time; {max_close_date} the latest market close the gate allows (today + its timing window).
Tagged open markets take the featured-card slots ahead of the by-volume ranking (family legs are never featured - they'd misread as a market probability without their siblings).
Loading…
Download the user-entered LLM sections as files - handy for backup or working on
the prompt material outside the browser. Text sections download as .txt
(version in the filename); eval questions and run history as .json;
All is one JSON bundle of everything.
Import - same formats back in: a .txt replaces one text
section (target auto-detected from the exported filename, or pick below); an
eval-questions .json merges by question number (existing questions keep
their run history) or replaces the whole set; the bundle .json imports
its text sections + eval questions.
The user-facing copy from Admin → System → Content, as
.txt. Per-page downloads are the [about.*] /
[terms.*] / [privacy.*] blocks of that doc;
All is the whole Content doc, footer and page copy
included. Legal text worth keeping a dated copy of before editing.
The moderation policy as .txt (version in the filename); its
eval questions and run history as .json. All
is one JSON bundle, and includes the auto-hide dimension list and the
configured sweep model - the settings that decide what a verdict DOES.
When ON, structured submissions run through the LLM gate (queue consumer in the main loop,
concurrency-capped = bounded spend). Verdicts land in gate_verdicts; passes either
auto-accept (gate changed nothing) or await the submitter's confirmation. When OFF, submissions
auto-approve as before. The deterministic layer runs regardless.
Comment moderation: the retrospective sweep (never a gate - comments publish first, judged after), scored by its own eval set under Comments → Evals. Vision calls: every image read - frame classification, sweeps, and Propose-from-image on Fixtures. Blank model = inherit the gate model. Blank fallback = inherit the gate panel's fallback.
Close votes: bidders on an OPEN market can vote to stop trading ("this is decided in reality"); it closes at this % of distinct bidders, rounded up. 0 = off. Votes to resolve: a CLOSED market resolves once one outcome collects this many distinct accounts' votes (strict plurality - ties wait for the admin). 0 = off (admin-only resolution - the recommended setting: any single account settling markets is dangerous).
Submission rate limits per role (N free per day, then at least M seconds apart), the daily LLM spend cap (gate pauses + eval runs refuse when reached; queued gate jobs wait, nothing is lost), and email alerts for warning/error log events.
Submissions the gate couldn't settle: self-consistency disagreements (the samples' verdicts shown side by side) and jobs that exhausted their retries. Accept puts the question back on the normal conveyor (converts next cycle, using the latest PASS contract if one exists); Reject closes it with feedback to the submitter.
Loading…
Loading\u2026
Email-confirmed signups awaiting your approval. Approving creates the starting balance and emails them; account management stays in Back Office → Accounts.
Loading…
Admin-curated facts newer than the glossary (captured at resolve time; the gate carries active entries for 90 days). Shown here: drafts awaiting review, and entries expiring within 7 days or already expired - renew, edit, promote to glossary, or delete.
Loading…
The override flywheel: every admin decision on the review queue is captured here with its reason and dimension. Harvest turns one into an eval question (expected verdict = your verdict, editable in the Evals editor) - human/gate disagreements are the highest-value eval cases there are. Retain defers 90 days; a candidate nobody harvests shows up here asking to be dismissed.
Loading…
Machine-detectable staleness: glossary not saved in 60+ days, model lists in 30+, and (when a resolution is overturned) recent-events entries whose evidential basis is gone.
Loading…
Rejected submissions: when, who, what, and how many attempts they made. Click a row for the verdict history; Promote sends it to the Review Queue for a human second look.
Loading…
Loading…
Modules from the flowchart. Stripe colors: green = backend, red = frontend, violet = infrastructure (added since the flowchart). Status reflects the code as built, not the flowchart's (stale) annotations. Only major features noted. Test counts are from pytest; 1253 backend tests total, covering every meaningful backend module, plus a real-browser FE smoke harness (scripts/fe_smoke.py - seeds via QA, drives the player-facing pages headless, 11 checks incl. a live bid and comment through the actual click paths). Both run by CI on every push.
gate_verdicts, FAIL → feedback in the submitter's list, PASS auto-accepts when the gate changed nothing or awaits the submitter's confirmation on the proposal card; edits always re-gate. Accepted contracts (criteria, completed source/invalid, earlier trading close) applied to the market at convert. Full eval harness (question editor under LLM → Evals; LLM → Run for single + sequential batch runs across the whole catalog with per-model progress/estimates; Scoring tab; harness_version boundary - h6 = nonce'd submission envelope, h7 (2026-08-14) = group markets: the envelope carries market type / k / legs for family submissions, the date line is labelled "Market close:", and the schedule block says human-entered; borderline questions may accept a secondary failure dimension). Eval questions can describe family submissions (type/legs/k in the editor detail row, export/import, eval-set hash). Eval-iteration round 4 (2026-08-15): OpenRouter provider POOLS (comma list in the model-list line → sorted composite id; one provider = measurement pin, several = allowlist OpenRouter routes within; require_parameters on every call), runs with some-but-under-10% call errors re-run just those questions once (paced 30s for 429 recovery; 10%+ = lost cause, no retry), Run Batch appends to a running batch, and Scoring flags runs served by more than one provider.evidence_entry_ids), URL-existence recovery path for verifiability rejections (Liquipedia allowlist, title-only, pre-live hold + admin confirm, false-negative fee refund), Housework → Triage leaf, eval fixtures for both (the harness never reads the live table). Source rework Phase 1 (2026-07-30, harness v5): exact-duplicate deterministic guard, notes/backstop/close-criteria/duplicate tool fields, Sources doc ({INJECT_SOURCES}, versioned src<N>), candidate-markets + tracked-player prompt sections (live in the gate, fixtures in evals), minute-granular trading close. Prompt-side additions + eval-set revision (1f) are Andrew's, pending.close_vote_threshold_percent % of distinct non-cancelled bidders) - skin-in-the-game holds at sweep time (cancel all your bids, forfeit your vote). Admin "Close trading now" overrule on Admin → Resolve. Group markets (Phase 3, 2026-08-15): close votes aggregate family-wide - bidders on ANY leg are the family's bidder set, one vote per account, the sweep closes every leg together; the vote panel lives on the family page. Resolution votes (2026-07-14, knob at 0 = OFF, the recommended setting): CLOSED markets resolve when one outcome collects N distinct accounts' votes as the strict plurality; ties wait for the admin; provisional/community_vote through the normal settle path.remaining_contracts is the source of truth, no stake↔contract drift). Price-time priority, partial fills, self-match prevention. Price-improvement residual released back to user at bid close via release_funds; non-BALANCE_004 release failures surface at WARNING (no silent stake retention). Sub-MIN_CONTRACT remainders close as closed_dust with residual refunded. Displayed market price is the order-book midpoint with a ¤1 volume floor; decoupled from the trade-tape fields. Money-conservation invariant (sum(user_totals) + sum(unresolved_match_pool)) covered by dedicated tests that bypass the per-test drift-repair fixture. Interface kept narrow (single process_bids entry point) so a future batch-auction engine can swap in. Price priority symmetric on both sides (NO-side inversion fixed 2026-07-06). Cancellation race hardened: consumes execute at match time and a failed consume VOIDS the match (counterparty compensated + re-reserved); status updates carry a WHERE guard so a concurrent cancel is never clobbered.drain() (take-and-clear) is the only safe committer pattern for the shared endpoint queue - closed a lost-write race between concurrent requests.resolution_source_id / backstop_source_id / cast_channel_id + a defaulted read_mode, and the matched rows are FROZEN onto the market as source_registry_snapshot - resolve-time reads use the snapshot, so a registry edit never rewrites terms someone is already trading on. Sequential-outright lineage (predecessor_market_id) rides through. Group markets (2026-08-14): a partition/slate submission converts into a market_families row + one ordinary leg market per entry (composed question "«stem» - «label»", so every write-time title snapshot stays self-contained), with an automatic catch-all leg for open player sets (never for the closed race set); 18 group tests in test_group_markets.py.on_conflict/returning knobs; update ops accept an optional where guard (deferred updates can refuse to clobber a state that changed between queueing and commit).backend/app/modules/log_queue.py. event_log() enqueues; send_log drains once per main_loop cycle. Symmetric to Write to DB → Commit to DB but for log events. Local DB write stays synchronous inline in event_log() so the in-page panel sees events promptly.backend/app/modules/send_log.py drains log_queue and POSTs each item to AWKanban with a 3-second timeout. Silent no-op if AWKANBAN_URL/KEY unset. Failures aggregated to a single WARNING.pam.py with 5 read functions (account summary, open/closed positions, transactions, per-market leaderboard). Five endpoints under /api/users/{id}/... + /api/markets/{id}/leaderboard wired into the "My Account" top-tab and the specific-market detail page. ResolveEngine writes user_market_results on settlement, now including stake-weighted Brier loss + delta. Lifetime Brier averaged across resolved markets; per-market Brier shown in Closed Positions and on the resolved-market leaderboard./api/users. No dedicated backend file yet.seed_family, 2026-08-15: a one-click [QA] partition with synthetic legs + crossing bid pairs), two scenarios, runtime loop-interval control, tag-by-prefix wipe (family rows included). QA mode is runtime-toggleable from the UI (overrides the QA_MODE_ENABLED env default). 14 tests focused on the dangerous + foundational paths (wipe safety, seed_users, seed_family, SeededRng determinism).w rather than m to disambiguate from Send Notification.)#family page. Clear button (top right) empties every field and leaves edit mode - the escape hatch for an edit whose target became uneditable. Site-wide gate-off banner (admin viewers) flags the ungated submit path.#family/<id> page (one chart, one line per traded leg via the --series-1..6 theme tokens; Options table with inline per-leg bidding; family-wide close-vote panel). Families compete for featured-card slots as one unit; partitions carry the sum÷k badge (Phase 3, 2026-08-15 - suppressed unless every leg has a real price, never for slates). Admin front-page tag (2026-08-14): tagged open markets take the featured-card slots ahead of the by-volume ranking. Rows/cards are real anchors (browser history + ctrl-click) and carry "you have a bid here" dots (● matched / ○ open) from the viewer's positions.#markets/<id> (shareable/bookmarkable) - price-over-time chart, order book, Market terms block (resolution source / criteria / invalid conditions / both close dates), bid placement, resolution-state badge, post-resolution leaderboard, QA-only bid/match lists. Auto-refreshes. Non-open markets wear their state as a chip on the title, and resolved markets' charts render in the grey settled tokens. Family legs carry a "part of a group market" backlink and hide the community close/resolution vote panels (those sweeps skip families - close and resolve are family-wide acts). Bid-entry rework (2026-08-14): price typed in hundredths with the "0." in the label ("77" = 0.77), narrower boxes, amount/price reset when the viewed market changes; order-book rows carry ⇄ match (fill the form to take the whole level, opposite side at the complementary price) and ⧉ copy (join the level) - both only FILL the form, the side button stays the confirm./api/admin/resolve_family, which asserts cardinality BEFORE anything moves. Front-page toggle (2026-08-14) on open non-leg markets: tagged markets take the featured-card slots ahead of the by-volume ranking.git pull → run any new deploy/migrations/*.sql → docker compose up -d --build. Boot-time schema guard emits a red schema_behind_code event naming any missing columns if a migration was skipped.sources table records mutability and its mechanism, the named-contributor dependency, permission records, known instability, the per-source settling period, the default read mode, and the exclusion reason where one applies - Liquipedia and Namu Wiki are seeded excluded WITH their reasons so that fact never depends on data entry, and eloboard is seeded rate_limited because evidence capture reads that flag. Editable at LLM → Sources, beneath the prose doc the LLM reads (one leaf, two layers). Matching is against slug / name / alias lines; the matched rows are frozen per-market at convert. Player identity is folded into tracked_players (Hangul, romanisations, date-ranged aliases, race, RepMastered name, disambiguation flag) rather than grown as a parallel table. Resolution evidence: resolving takes an optional resolution URL / evidence URL / note; we fetch and archive OUR OWN copy (the archive is the artifact, the URL is a convenience pointer - eloboard's URL churn is confirmed) into an append-only resolution_evidence row (RESTRICT, like gate_verdicts). Capture is best-effort and never blocks settlement; failures surface in Triage. Captures against a rate_limited source are gated by evidence_capture_min_gap_seconds and skipped, not delayed, sharing the scraper's fetch clock - so resolving twenty markets can't spend twenty eloboard reads. Public Resolution Evidence panel on resolved markets: outcome, the sources as FROZEN at listing, resolver, hash, links, and the archived copy (served as text/plain - third-party HTML must not run on our origin).source_snapshots and reused rather than re-fetched), the overlay frame (what was shown - the identity read the transcript can't give), and a timestamped VOD URL. Transcript search is a TIME-INDEX and a candidate generator only; the admin looks at a frame and decides. Three modes, none an error: candidates found, captions-present-but-nothing-matched, and no-captions - the last two are manual mode, which is the expected path until the phrase list widens. Heavy work runs in an out-of-band browser_jobs queue (mainloop process, never the resolve request), so a multi-minute VOD download can't block settlement.resolution_flags capture "something is off" (conflict / unparsed / other) - they carry the admin's note and never a result, so the queue can't spoil its reader; an open conflict pauses auto-finalize; resolving CLEARS rather than deletes, because a resolution that had a conflict flag is what a dispute wants to find. Also closes a collision the preview flow created: capture reuses a copy of the same URL fetched within the hour, so preview-then-resolve no longer records a rate-limit error despite a fresh archive sitting right there.scripts/frame_eval.py, run on gate-model change). Commit 0's answer forced Option D: YouTube bot-checks datacenter IPs, so yt-dlp work (frames, sweeps, transcripts, the evidence frame) runs on an ADMIN MACHINE via scripts/media_worker.py - polls the media-job queue, executes locally, posts back; the server classifies on ingest. Circumvention tooling was rejected on audit-surface grounds (the conduct note in CLAUDE.md). A fully manual path exists regardless: attach a screenshot of the decisive moment at resolve and it becomes the evidence frame with no yt-dlp anywhere. Candidate moments carry transcript context (click-expand); Check Now names WHY nothing was found.evidence_kind='supplementary' with NO outcome (it corroborates, it never asserts) and never flipping a market - notices explicitly say 일정 변동이 있을 수 있습니다. A notice that CONTRADICTS the cast raises a conflict flag for a human; ambiguous matches escalate rather than guess. Deletion is a hard-fail: soop_capture_at_risk fires on both routes to permanent loss - a missing expected notice AND an exhausted render job (the likelier one, and the one that would otherwise fail silently at the layer nobody watches). Distinct urgency from source_unhealthy on purpose: "down" is recoverable, "will be deleted before we capture it" has a closing window.<schedule> block (never results; empty feed omits the block). Machine reads: eloboard-routed stat markets get a proposed outcome from stored snapshots via narrow claim readers; every uncertainty (ambiguous threshold, straddle, stale, two players) resolves to NO proposal with its reason - a wrong number invites rubber-stamping. Cast poll: uploads-feed polling detects a match's VOD (handle/hangul/alias matching), auto-runs the transcript analysis, honours cast precedence with a preferred-wait window, supersedes re-uploads, and offers per-market Check Now. Spoiler property pinned throughout: proposals and candidates are admin-panel data only.claim_classes table and machine parsed_value reads stay deferred.tracked_players ids (no enumeration); raw HTML + parsed stats stored per fetch in source_snapshots (the evidence-capture artifact); label-anchored Korean parser (total/ELO/races/tournaments) verified against a live page; runs in the main loop, stalest-first rotation, once per KST day; control panel at LLM → Players (leaf renamed from Scraper 2026-08-06 - we scrape in several places now; what matters is what this one is for); parse/staleness failures surface in Triage. Player identity (2026-08-04): a successful scrape records the player's real name from the 플레이어 cell into tracked_players.hangul, filling only what is empty so hand-reconciled values always win. Names are stored verbatim and never transliterated - Korean personal-name romanisation is conventional, not phonetic, so a generated spelling would be plausible and wrong. A Latin page name is surfaced in the roster (page_name) rather than stored, because tag stripping means the parser captures a single token.romanisations stays a human field (the reliable machine path is a source that renders 김민철 SoulKey side by side, not transliteration), unified rate config (evidence capture gets its own per-host gap knob in source rework Phase 2; merge with the scraper's window/delay limits later - both spend the same read budget).theme-broodwar.css (per-niche - fonts + ink variables; copy this one file to re-skin a niche) + design-system.css (shared - scale, 5+2 type roles, element defaults). Cache-busted serving (?v= mtime) with a dev-only ?theme= preview override. Tabular numerals on money/number tables. Phase 2: full color vocabulary (inks, surfaces, borders, yes/no market sides, ok/warn/danger/info semantics, admin/QA accents); charts read the theme via themeVar(). Phase 3: the whole inline-style long tail swept onto var() - ZERO hex anywhere outside the theme file (census-enforced) + element-class kit. Phase 4: the dark Broodwar theme is live - sketch ink palette, authored dark surfaces, self-hosted Chakra Petch (OFL, latin+latin-ext woff2), themed form controls, Chart.js theme defaults. Light palette kept as theme-default.css (/?theme=default). Responsive pass 1 (2026-08-16): player-facing surfaces at phone widths (two-row nav with scrollable tab strip, stacked cards/bid buttons, two-block family Options + Open Positions rows, clickable transaction history) - admin stays desktop-only on purpose. Settled-chart tokens (--settled/--settled-soft): resolved markets' lines go grey. Playwright screenshot rig (scripts/screenshot_pages.py): every player page at every target device size in one run, local or prod (--api-key, --user).theme-broodwar.css). 563 inline style= attrs still convert to system classes opportunistically. Niche identity - name/favicon/banner copy/chart series palette (Phase 5). Plan: .claude/plans/plan_frontend_design_system.md.t(key) for JS strings + data-i18n/-title/-placeholder bindings applied at boot. Missing keys render as the raw key (self-reporting). Browsable table under System → Keys. Covered: nav, auth overlay, user bar, all four user tabs, admin headers/nav, Refresh buttons.Loading...