Skip to content

Issuance Explorer

The page at /admin/issuance-explorer is a read-only inspect tool. Pick a match, filter by status (published / blocked / all) and period / phase, then click a published issuance to see why that definition issued, the At play settings and pinned definition snapshot, and how it later resolved — or a blocked attempt to see which gate stopped a trigger that did not publish.

It does not show live league / group / definition settings. Use Question Inheritance for the current inheritance model (live-only). At play and the pinned snapshot are the historical settings path.

This page does not write.

Data source

The admin page is a thin HTML shell. Alpine.js loads:

  • Matches via GET /api/matches (any status for the selected local day)
  • Inspect payload via GET /api/matches/{id}/question-inheritance
  • In-game operational fields via GET /api/matches/{id}/question-issuances (joined by issuance id)
  • Blocked in-game attempts via GET /api/matches/{id}/question-issuance-denials (joined by denial id)
  • Match questions via GET /api/matches/{id} (match_question_issuances, joined by issuance id)

Optional query on the inspect API: kind=in_game|match. The inspect API still accepts definition_id (filters traces); the admin UI does not send it.

Deep-link: ?league=&day=&match_id=&kind=&status=&period=&issuance_id=&denial_id=. period= is a stamped label. Omit it for All. Match detail links here with match only (league, day, match_id — no issuance_id). /admin/question-inheritance?issuance_id= redirects here.

How to read it

Panel Meaning
Event timeline Optional. Collapsed until Show timeline. Scatter of stream events, correctives, blocked attempts, and issue / resolve markers (named Y lanes + legend). Subtle vertical lines mark period / phase changes (from stream events when ingested, otherwise from issuances / blocked rows). Hover a marker for event type, period / phase, situation, and clock (extracted stamps; not the full payload). Drag a time range to zoom; slider / wheel to pan; reset icon for the full match. Times use the browser timezone. Stream / corrective dots need ingest. Hide timeline collapses without refetching. Changing match resets the panel.
Match issuances Starting point. Left list is play order after Published / Blocked / All (in-game) and period / phase filters. # is the match’s published issuance sequence (blocked rows have no number). Published rows show the winning definition and its question group. Blocked titles are event + closed gate + how many definitions triggered (for example atbat · Group · 14 triggered), or the picked definition when probability / pending stopped a selection. A pivotal badge marks in-game issuances whose pinned snapshot is a pivotal moment. Click a published row for identity, At play, snapshot, selection, other definitions, and resolution. Click a blocked row for the stored short-circuit path (no At play / resolution).
Identity Short issuance id (copy full UUID), question sponsor pill when the issuance claimed a slot (Match questions use the match sponsor), issued/resolved times, P/R period labels, situation / clock when stamped, per-issuance answer metrics (in-game only). Resolved opens the resolution modal. Eye icon opens the current definition editor.
At play In-game question_settings_snapshot frozen at issue time (effective / override / league default). Historical — not live Question inheritance.
Definition snapshot Pinned definition_snapshot at issue time as a Setting / Value table (copy JSON for the Question Definitions editor). Legacy rows without a snapshot do not fall back to the live definition body.
Selection path + table Same listener check order, compact on desktop. passed kept this definition in; blocked would have stopped it; skipped did not apply. Caps show as used / cap. Each Check has a ? for what that gate does; Detail is this issuance’s replay.

Match issuances

Published traces are DB-only. They do not replay S3 listener archives. Blocked attempts are compact listener rows (question_issuance_denials) written going forward when a definition trigger matched and nothing published. Matches whose listener ran before this shipped have none.

  • Winner (accurate): trigger match on the stored issuance event; listener gate order using the pinned definition_snapshot and play-time question_settings_snapshot; resolution reason and correct choices from the stored issuance (snapshot conditions, not the live definition if they later diverged).
  • Resolution is later: the question already published; resolution is how it closed. A blocked replay check does not retract it. Resolved opens condition evaluations and event fields (same chrome as History).
  • Published vs replay: the list is published questions. If Selection shows blocked on that winner, replay of stored caps/history would have stopped it (for example a second pivotal in the same period after one already used the slot). That is not “it never issued.”
  • Other definitions (approximate): same event and prior-issuance history. Prefer that definition’s latest earlier snapshot on this match; otherwise the live row (labeled as such). First failing step only.
  • Not reconstructed on published rows: probability roll, false-streak / dynamic-weight in-memory counters, exact weighted winner among a pool. After eligibility the copy is “in the selection pool; this issuance is the one that published.” Probability skips are stored on blocked attempts.
  • Blocked attempts: one row per event where at least one trigger matched and nothing published (caps, group, match-wide subject uniqueness, probability skip, pending slot occupied). Silent “no trigger matched” events are not stored. The list name is event + gate + trigger count unless the listener had already picked a definition (probability / pending_occupied) — then it shows that definition. Do not read a group-blocked atbat row as “this one question definition kept retrying”; many defs can trigger on the same event. The detail pane lists Triggered definitions for those no-pick rows (full triggered list). The Selection path stays the same gates; Group / Pivotal / cap / Subject Detail and Used name the sub-check and numbers when details is present (for example waited 97s of 300s, or subject id + period). Older rows keep a generic reason. The listener also writes one compact Blocked attempt INFO line with that same reason.
  • Legacy rows without a snapshot fall back to the live definition for selection replay only; the identity pane does not reconstruct name/text from the live row.

Match questions use a thinner trace (Beat/API issued + lock/resolve). No listener selection pool, At play table, or in-game answer metrics.

The eye icon next to the winning definition name opens the current QD / MQD editor row. The pinned snapshot below is historical (Setting / Value table, same chrome as At play) and is not that live editor. This page does not deeplink a selected issuance into Question inheritance (that tool is live config, not this issuance’s play-time settings).

Workflow

  1. Open Manage → Issuance Explorer, or Issuance Explorer on match detail (metrics column).
  2. Pick a league, local day (defaults to yesterday), and match. The match option uses AdminMatches.optionLabel (aliases, local scheduled time, TEST when is_test, then shortcode — not sr_id).
  3. Optionally open Event timeline (Show timeline) for stream / issuance markers on the selected match (on demand; not loaded with the page).
  4. On Match issuances, toggle In-game vs Match Questions. For in-game, filter Published / Blocked / All. Filter period / phase with All (default) or a stamped label.
  5. Click a published issuance (the list shows which definition won) to read identity, At play / snapshot, the selection path, other definitions, and resolution. Click a blocked row for the gate that stopped the pass. A group / cap miss lists who triggered; a probability / pending miss names the picked definition. Group / Pivotal / cap / Subject Detail names the sub-check and numbers when stored; older rows stay generic.

Code map

Concern Location
REST inspect GET /api/matches/{id}/question-inheritance
Blocked attempts GET /api/matches/{id}/question-issuance-denials
In-game issuances GET /api/matches/{id}/question-issuances
Match questions GET /api/matches/{id} (match_question_issuances)
Ops + schemas src/lib/question_inheritance/
Admin HTML shell src/api/admin/routes/issuance_explorer.py
Template src/api/admin/templates/issuance_explorer/index.jinja2
Alpine app src/api/admin/static/js/issuance_explorer_app.js
Timeline chart src/api/admin/static/js/issuance_explorer_timeline.js (ECharts on demand)
Timeline JSON GET /api/matches/{id}/event-timeline
CSS src/api/admin/static/css/question_inheritance.css
Tests src/api/tests/admin/test_issuance_explorer.py, src/api/tests/test_match_event_timeline.py