| # Architecture & Judging Coverage — Midnight Mystery with Detective Shawn |
|
|
| This document does two jobs: |
| 1. Explains how the system is built (for technical judges and future contributors). |
| 2. Maps the build to the **Build Small** judging surface so the submission collects as many "ways to win" as possible. |
|
|
| --- |
|
|
| ## 1. System overview |
|
|
| ``` |
| main.py (FastAPI) |
| ┌───────────────────────┼───────────────────────────┐ |
| │ │ │ |
| Gradio: onboarding Gradio: game Gradio: case-closed |
| (custom noir form) (HUD + iframe map) (reveal + debrief) |
| │ │ │ |
| ▼ ▼ ▼ |
| case_generator Phaser 3 (game.html) debrief service |
| (5-pass pipeline) ⇅ postMessage / fetch │ |
| │ │ │ |
| ▼ ▼ ▼ |
| └──────── backend/routers (case · interrogation · forensic) ───────┘ |
| │ |
| inference_client (HF Serverless / Ollama) |
| │ |
| Qwen3.5-9B (text) FLUX.2-klein-4B (portraits) |
| ``` |
|
|
| - **Three Gradio apps, one FastAPI host.** Each page is its own `gr.Blocks` mounted via `gr.mount_gradio_app`, so onboarding, the game, and the reveal are independent and each fully custom-styled. |
| - **Phaser ⇄ backend bridge.** The map is a self-contained Phaser scene in an iframe. It talks to FastAPI with `fetch` and `postMessage` — movement, clue discovery, and interrogation all flow over HTTP. |
| - **State.** A Pydantic `CaseFile` holds the entire game; an in-memory store keyed by `case_id` carries it across requests. A complete hardcoded `SEED_CASE` is the guaranteed fallback. |
|
|
| --- |
|
|
| ## 2. The generation pipeline (the centerpiece) |
|
|
| A single small text model is driven through five narrow passes. Splitting the work keeps a 9B model reliable: each prompt is small, each output is validated, each failure is recoverable. |
|
|
| | Pass | Input | Output | Guarantee | |
| |------|-------|--------|-----------| |
| | **0 · Roster** | victim, scene, count, difficulty | cast (name, occupation, archetype, role) | exactly one culprit; fixed cast for later passes | |
| | **1 · Story Bible** | roster + setting | the hidden ground truth (how/why/when, motive) | the truth is written once and never sent to suspects | |
| | **2 · Souls** | story + one cast member | per-suspect "soul": voice, alibi, reveal rules, crack progression | runs **in parallel** (`asyncio`); each has a validator + fallback | |
| | **3 · Evidence** | story + soul summaries + map rooms | clues anchored to real locations + soul write-backs | **≥2 solvable paths**; clues land on real `clue_anchors` | |
| | **4 · Briefing** | **public fields only** | police dossier lines | structurally cannot leak the culprit | |
|
|
| **Why this wins points:** it's a concrete, defensible answer to "can a small model do something hard?" — yes, if you decompose the task, validate each step, and constrain it to a real world (the map manifest). It's not prompt-and-pray. |
|
|
| ### Live systems layered on top |
| - **Emotional state machine** (`state_mutator`, `interrogation_pipeline`): trust / anxiety / defensiveness per suspect; crack thresholds scale with `lying_skill` (1–10). State is rendered into natural-language instructions so the model *enacts* pressure instead of describing it. |
| - **Knowledge gate** (`knowledge_gate`): suspects can only reveal authorized facts; everything else is forbidden context. |
| - **Prompt-injection defense**: known injection patterns are caught and deflected *in character* by archetype. |
| - **Reactive world** (`patrol`, alertness): suspects patrol their zones; a stealth/alertness layer escalates suspects and can warn the culprit. |
| - **Forensics** (`forensic_lab`): clue analysis that can detect contradictions against interrogation transcripts. |
| - **Debrief** (`debrief`): the model grades the playthrough and narrates the resolution. |
|
|
| --- |
|
|
| ## 3. Small-model compliance (the hard requirement) |
|
|
| | Component | Model | Params | < 32B? | |
| |-----------|-------|--------|--------| |
| | Case generation | `Qwen/Qwen3.5-9B` | ~9B | ✅ | |
| | Interrogation / debrief / forensics | `Qwen/Qwen3.5-9B` | ~9B | ✅ | |
| | Portraits | `black-forest-labs/FLUX.2-klein` | ~4B | ✅ | |
|
|
| Rule check: *"each model's parameter count must stay below 32B."* Largest single model here is **~9B**. Comfortably compliant — and the *narrative* of the project is precisely "look how much one small model can carry." |
|
|
| > ⚠️ **Consistency fix before submitting:** model names are stated inconsistently across the repo. `inference_client.py` (the source of truth) defaults to **Qwen3.5-9B** + **FLUX.2-klein-4B**, but `DEPLOYMENT.md` lists Qwen3-14B + Mistral-Small-24B + FLUX, and the onboarding CTA still says "Qwen-2.5-7B". Pick the real set, then make README, DEPLOYMENT.md, the onboarding CTA, and the demo VO all agree. Judges will notice a mismatch. |
| |
| --- |
| |
| ## 4. Judging-surface coverage |
| |
| The field guide describes ~29 ways to win across track placements, Community Choice, sponsor prizes, the Custom-UI category, and stackable bonus badges. This project is strongest where it leans into being *AI-native* and *visually distinct*. |
| |
| | Target | Why this build qualifies | Action to lock it in | |
| |--------|--------------------------|----------------------| |
| | **Thousand Token Wood** (track placement) | Couldn't exist without AI; the model *is* the game | Lead the README + video with this framing ✅ | |
| | **Custom UI badge** | Zero default-Gradio surfaces — bespoke noir CSS, Phaser map, custom loader | Show the UI prominently in the video; tag `custom-ui` | |
| | **OpenBMB / small-model sponsor** | Whole pitch is "one 9B model runs a game" | State param counts everywhere; confirm sponsor tag slug | |
| | **Community Choice** | Shareable, novel hook ("name a victim, AI writes the murder") | Strong social clip; post early for votes | |
| | **"Most badges stacked"** | Genuinely spans creativity + UI + small-model + agentic | Add every *honestly applicable* badge tag | |
| | **Full-package** | App + demo video + social post all exist and align | Ship all three; keep them consistent | |
| |
| ### Submission checklist (hard requirements) |
| - [ ] Deployed as a **Gradio app in the official `build-small-hackathon` HF org** |
| - [ ] **Demo video** showing the app working (see `DEMO_VIDEO_SCRIPT.md`) |
| - [ ] **Social post** created and **linked from the README** (see `SOCIAL_POST.md`) |
| - [ ] README **YAML tags** for the track + every badge being claimed |
| - [ ] All models verified **< 32B** and named consistently everywhere |
| - [ ] Submitted before **June 15, 23:59 UTC** |
|
|
| --- |
|
|
| ## 5. Pre-submission risk list |
|
|
| 1. **Theme consistency.** The game is now *Detective Shawn* (human noir). Sweep for leftover pet/animal copy in user-facing strings (onboarding hints reference "animals in the neighbourhood"; portrait assets are animal placeholders). Anything a judge sees must match the shipped theme. |
| 2. **Model-name drift.** See §3 warning — unify before recording the video. |
| 3. **Inference reliability during judging.** The seed-case fallback covers a hard outage, but verify the Space's `HF_TOKEN`/provider and that portraits degrade gracefully. The demo video is your insurance — record it on a known-good run. |
| 4. **First-paint impression.** Onboarding is the most-judged screen after the video. It's already strong; make sure fonts load on the Space (Google Fonts CDN reachable) so the noir look survives deployment. |
|
|