Live Quiz Platform

Build Plan — Combined

Status Live checklist
Generated 2026-06-05
Source of truth .claude/context/knowledgebase/build-plan/ (5 files: project + per-app)

Implementation work organised across one file per app plus a cross-cutting project file. Each app's content is collapsible below — project starts expanded; per-app sections start collapsed. Within each app, sections are grouped by phase (MVP → Alpha → Beta → Production → Stretch) and ordered by build dependency.

Definition of Done for every item: TDD followed (failing test first, then implementation, then refactor); end-to-end automated test exists for any user-facing feature the item touches; lint and format pass; coding standards followed.


Progress

Overall
70/32621%
Project
15/2177%
Designer
55/6980%
Host
0/210%
Client
0/150%
Remote
0/40%
Project (cross-cutting)15 / 217
Foundation MVP project4 / 6
  • Initialize repo with four Unity projects (Quiz.Host, Quiz.Client, Quiz.Remote, Quiz.Preview) plus the Quiz.Designer/ Tauri 2 + Angular 21 workspace plus shared local UPM packages (com.quiz.core pure C# / .NET Standard 2.1, com.quiz.runtime Unity-aware, com.quiz.shared-assets) plus the schemas/ JSON Schema source-of-truth tree. On-disk layout in Repository Layout.
  • Seed each Unity project from the shared Wildfire game template (com.wildfiregames.core UPM + _Game/ scaffold with Setup/MainMenu/Play scenes, persistent systems, save system, audio).
  • Coplay Unity MCP package pinned in every project's Packages/manifest.json; root .mcp.json registers the UnityMCP server. Routing protocol in AI Tooling — Unity MCP.
  • Shared C# class library consumed by all four Unity projects via UPM — local file: packages wired into every project's Packages/manifest.json. Schema content tracked under Shared schema.
  • DOTween installed via UPM in every Unity project (Host, Client, Remote, Quiz.Preview) and verified with a smoke-test tween
  • Pre-commit hooks for C# format and analyzer
Azure DevOps CI / CD pipelines MVP project0 / 24

Per CI / CD. Pipelines are YAML at .azure-pipelines/, owned in-repo and reviewed in PR. Trunk-based branching; pr-validation.yml is a required check on main.

Agent + service connections

  • Self-hosted Mac mini agent provisioned: latest macOS, current Xcode + iOS / macOS SDKs, Unity Editor + license, Rust toolchain + Tauri prerequisites, signing certificates and provisioning profiles in macOS Keychain. Registered in ADO under agent pool MacMini.
  • Microsoft-hosted Azure Pipelines agent pool authorised for the project (default for any pipeline not pinned to MacMini).
  • Unity service connection holding the activation file; activation file cached on the Mac mini agent.
  • Apple Developer account credentials + App Store Connect API keys stored as ADO secret variables for release.yml.
  • Azure Repos remote configured; repository's default branch set to main; branch policies set: 1 reviewer minimum, pr-validation.yml required, no direct pushes to main.

Pipelines

  • .azure-pipelines/pr-validation.yml — runs on every pull request. Triggers per-area pipelines on the agent pool that can build them; aggregates pass/fail.
  • .azure-pipelines/quiz-core.ymldotnet test for packages/com.quiz.core/Tests/. Microsoft-hosted; no Unity license.
  • .azure-pipelines/quiz-runtime.yml — Unity edit-mode + play-mode tests under packages/com.quiz.runtime/Tests/. Mac mini.
  • .azure-pipelines/quiz-host.yml — Unity Player builds for Windows (Microsoft-hosted) + macOS / iOS (Mac mini); play-mode tests.
  • .azure-pipelines/quiz-client.yml — Unity Player builds for the configured Client platforms; play-mode tests.
  • .azure-pipelines/quiz-remote.yml — Unity Player builds for the configured Remote platforms; play-mode tests.
  • .azure-pipelines/quiz-preview-webgl.yml — Unity WebGL Player build of Quiz.Preview/ on the Mac mini; output published as a pipeline artefact for quiz-designer.yml to consume.
  • .azure-pipelines/quiz-schemas.yml — schema codegen (NJsonSchema → C# into com.quiz.core/Runtime/Generated/; quicktype + ajv → TypeScript into Quiz.Designer/src/app/generated/); publishes both drops as pipeline artefacts.
  • .azure-pipelines/quiz-designer.yml — consumes the TS schema artefact and the Quiz.Preview WebGL artefact; on Microsoft-hosted Windows runs npm ci + ng build + ng test (Jest) + Playwright screenshot pass against ng serve + tauri build to produce the signed Windows installer; on the Mac mini runs the same npm ci + ng build + tauri build to produce the signed + notarised macOS DMG.
  • .azure-pipelines/release.yml — manual or git-tag triggered. Signs, packages, and publishes installers to the configured store / distribution channels. Dry-run nightly on main (no publish).

Caching + reporting

  • NuGet package cache keyed by **/packages.lock.json enabled across pipelines.
  • Unity Library/ cached per-project per-agent so re-imports skip on incremental builds.
  • Test results published as JUnit XML so the ADO test report aggregates across pipelines.
  • Code coverage published as Cobertura XML; coverage ratchet enforced (no decrease).

End-to-end pipeline tests

  • PR-validation green path: open a PR with a one-line readme change, confirm pr-validation.yml runs and passes on every relevant pipeline.
  • PR-validation red path: open a PR with a deliberately failing test, confirm pr-validation.yml fails and blocks merge per the branch policy.
  • Mac mini cold start: clear the MacMini agent's caches, confirm a full pipeline run completes within a target wall-clock budget (TBD at first measure).
  • Quiz.Preview → Quiz.Designer artefact handoff: quiz-preview-webgl.yml publishes the WebGL bundle and quiz-designer.yml consumes it; round-trip works on main.
  • release.yml dry run: produces signed installers for every configured channel without publishing.
Azure DevOps repos + wiki setup MVP project2 / 2

ADO hosts source control + the wiki mirror + CI pipelines. No Boards / work-item usage — this build plan is the task list.

  • ADO project provisioned (or existing project repurposed) with Repos, Pipelines, and Wiki enabled.
  • Wiki provisioned and pointed at the auto-generated knowledgebase mirror produced by the docs skill.
Shared schema MVP project6 / 9

Authored as JSON Schema under schemas/; codegen produces C# types into com.quiz.core for the Unity apps and TypeScript types + ajv validators into the Angular Designer. See Repository Layout — Cross-language contract. The Codegen wiring itself is tracked under Schemas + codegen.

  • Quiz manifest, slide, canvas, and element schemas authored under schemas/package-format/ (per Quiz Package Format)
  • Round (slide-grouping) schema (title, scoring metadata, contiguous slide range)
  • Per-slide host-notes field in the slide schema (used by F-DE-19 and rendered by the Remote — both MVP)
  • Per-element reveal-trigger field in the element schema (on slide entry / on quizmaster trigger / after delay) — F-DE-20
  • Per-slide timing config: lock-on-time-up, late-submission rule, quizmaster-override-allowed (F-DE-10)
  • WebSocket message envelope schemas under schemas/live-play/ (typed, validated) with extension hooks for object-type-specific messages, distinct families for Designer transfer / Client live-play / Remote control
  • Schema version field on quiz packages, validation on load (F-X-2)
  • Object-type registry interface — C# side in com.quiz.core, TypeScript side in the Angular Designer — declaring type id, schema version, and the surface contracts referenced by the apps
  • Round-trip serialisation tests for every schema, asserted from both the NUnit (C#) and Jest (TS) sides against shared fixtures under schemas/fixtures/
Object-type plugin contract MVP projectdesignerhostclient0 / 9
  • IObjectType interface in com.quiz.core (type id, schema version, schema accessor) — see Object-Type Architecture
  • Designer editor-surface contract (Angular standalone component + view-model service in Quiz.Designer/src/app/object-types/; DesignerEditor<TData> descriptor declares palette icon + group + the inputs the properties inspector binds to)
  • Host runtime-surface contract (UGUI prefab + behaviour binding)
  • Client runtime-surface contract (UGUI prefab + behaviour binding)
  • Optional protocol-extension contract for object-type-specific messages
  • Per-element statefulness declaration on IObjectType — types that own live-session state (timer countdown, accumulated answers, reveal index) flag themselves stateful and expose a serialiser the Host's crash-recovery snapshot captures. Stateless types (static text, image) skip the snapshot path. See Object-Type Contract — Statefulness.
  • Built-in registry implementation in each app — types register themselves on app startup
  • First built-in object type — core.text — implementing every contract end-to-end as the reference example
  • End-to-end test: a quiz containing only a core.text element on each canvas renders correctly across Designer preview, Host, and Client
Schemas + codegen MVP projectdesigner1 / 6

Per Repository Layout — Cross-language contract. The schemas/ tree is the single source of truth for every data shape that crosses the Designer ↔ Unity boundary; codegen produces C# types into com.quiz.core and TypeScript types + ajv validators into the Angular workspace.

  • Establish schemas/ directory layout: package-format/, live-play/, transfer/, object-types/, fixtures/; commit existing manifest / slide / canvas / element / envelope schemas as JSON Schema draft 2020-12.
  • schemas/codegen.config.json declares inputs, output paths, and naming conventions for both codegen targets.
  • C# codegen target — NJsonSchema.CodeGeneration.CSharp produces records into packages/com.quiz.core/Runtime/Generated/. Output is git-ignored; build invokes codegen before compile.
  • TypeScript codegen target — quicktype produces interfaces; ajv produces compiled validators; both land in Quiz.Designer/src/app/generated/. Output is git-ignored; npm run schema:gen is invoked by ng build and on watch.
  • Shared semantic-fixture suite under schemas/fixtures/ — paired NUnit / Jest harnesses assert TS and C# validators agree on every input.
  • CI pipeline .azure-pipelines/quiz-schemas.yml publishes both code drops as artefacts.
Per-Unity-project scaffolding — Universal MVP project0 / 3

Each Unity project (Host, Client, Remote, Quiz.Preview) converges on the shape captured in Per-App Scaffolding. Apply changes per-editor by routing through the AI Tooling — Unity MCP set_active_instance protocol; verify with read_console after each compile.

  • Set companyName to QuizCompany in every project's ProjectSettings/ProjectSettings.asset.
  • Verify Active Input Handling is Input System Package; _Global/Configuration/InputActions.inputactions in place.
  • One asmdef per app, named Quiz.{App}.Game.asmdef, root namespace Quiz.{App}.Game.
Load-bearing prototype MVP projecthostclient0 / 5
  • In-process C# WebSocket server runs in the Host using SimpleWebTransport per Tech Stack. Server must support all three message families from MVP: Designer transfer, Client live-play, Remote control. The MVP control-message envelope must reserve room for the Alpha rich-command set so adding it is purely additive.
  • Host advertises itself via Bonjour/mDNS using Makaretu.Dns.Multicast.New per Tech Stack
  • Client discovers the Host via Bonjour/mDNS
  • Typed ping/pong message exchanged between Host and Client
  • End-to-end test: Client discovers Host and round-trips a message
Remote app — minimum viable controls MVP remotehost0 / 9

The fourth Unity app — see Applications — Remote. MVP delivers the minimum viable controls (discovery, pairing, mirror, host-notes, live state, advance/go-back). The richer command set is in the Alpha section below.

  • Remote discovers Hosts via Bonjour (F-RE-1)
  • Pairing UX — Host displays both a short numeric code AND a QR; Remote can either type the code manually or scan (F-RE-2)
  • Remote opens control-message-family WebSocket to paired Host (F-RE-3)
  • Remote renders the scaled-down mirror streamed from the Host (F-RE-4)
  • Remote displays the per-slide host-notes for the current slide (F-RE-5)
  • Remote displays live session state — scores, timer remaining, slide index (F-RE-6)
  • Remote sends core navigation commands to the Host — advance, go-back (F-RE-7)
  • Remote handles disconnection and reconnection (Wi-Fi blip recovery; rejoins same paired Host) (F-RE-8)
  • End-to-end test: Remote pairs with Host, sees the mirror + host-notes + live state, drives a full quiz session through advance/go-back
MVP object-type cohort MVP designerhostclient0 / 6

The remaining MVP object types beyond core.text. Each implements the full plugin contract with end-to-end tests covering Designer authoring, Host rendering, and (where applicable) Client rendering and input.

  • core.multiple-choice-input end-to-end (Client canvas; tap-to-select; submit)
  • core.free-text-input end-to-end (Client canvas; text entry; submit)
  • core.numeric-input end-to-end (Client canvas; numeric validation; closest-wins scoring; tiebreaker support)
  • core.true-false-input end-to-end (Client canvas; binary ✓ / ✗ tap targets; submit)
  • core.timer end-to-end (either canvas; countdown / elapsed; Host-authoritative; configurable lock-on-time-up + late-submission rule + quizmaster override)
  • core.leaderboard end-to-end (either canvas; per-team standings; trigger-driven reveal — on slide entry / on quizmaster trigger / after delay; reveal animation: full table / row-by-row / bottom-up)
Designer→Host transfer MVP designerhost0 / 7
  • Designer can save a quiz to disk as a .quiz file (manifest + slides) — SaveQuizCommand bundles AuthoringSession state into a QuizPackage; PersistenceService.saveAsync atomic-writes the ZIP via the Tauri fs command. Resource bundling lands with the bundle-on-save item. (F-DE-12)
  • Designer can re-open an existing .quiz file from disk — MenuActions.openAsyncPlatformAdapter.pickFilePersistenceService.openAsyncReplaceActiveQuiz command. (F-DE-13)
  • Designer discovers Hosts on the local network via mDNS — Tauri mdns-sd Rust command surfaced through TransferService (F-DE-14)
  • Designer can push a saved .quiz file to a chosen Host over the WebSocket transport — uses the standard browser WebSocket API (via rxjs/webSocket) from the Angular process (F-DE-14)
  • Host accepts incoming .quiz transfers, validates the manifest against its built-in registry, and stores the file locally on operator confirm (F-HO-4, F-HO-7)
  • Host UI manual-confirm prompt for every incoming transfer ("Designer X wants to send 'My Quiz' (50 MB). Accept?")
  • End-to-end test: Designer saves → Designer pushes to Host → operator accepts → Host loads → Host can start a session
Designer Run-from-slide subprocess [MVP — deferred] designerhost0 / 3

Deferred to the end of the MVP feature work. Ctrl+F5's spawnHost Tauri command currently throws notInTauri; the renderer-side shortcut is wired, the backend implementation lands here once the Host build is signable + bundled as externalBin.

  • Wire tauri-plugin-shell + bundle.externalBin in tauri.conf.json so the Designer ships with the Host binary inside its app bundle.
  • TauriPlatformAdapter.spawnHost invokes the bundled binary with {quizPath, startSlide} as CLI args (the Host's bootstrap reads them).
  • End-to-end: Ctrl+F5 on a saved .quiz launches the local Host with the active slide pre-selected.
Cross-platform validation MVP projectdesignerhostclientremote0 / 15

Cross-platform from day one — every platform stood up during MVP rather than expanded into post-MVP. Each item below is a check that the corresponding feature works on the named platform end-to-end (build, network discovery, transfer, live play). Not separate engineering work, but separate validation runs. Designer ships desktop only in MVP (Windows + macOS); iPad and Android tablet authoring are Stretch. Linux is out of scope.

  • Designer (Tauri + Angular) validated on Windows — Quiz.Preview WebGL canvas embedded, JS bridge round-trip, multi-window via Tauri WebviewWindow API, custom titlebar with Win 11 Mica, Run from slide subprocess spawn end-to-end
  • Designer (Tauri + Angular) validated on macOS — same surface as Windows; multi-window; macOS traffic-light titlebar overlay; Run from slide subprocess spawn end-to-end
  • Host validated on macOS
  • Host validated on Windows
  • Host validated on iPad
  • Host validated on Android tablet
  • Client validated on iPhone
  • Client validated on Android phone
  • Client validated on iPad
  • Client validated on Android tablet
  • Remote validated on iPhone
  • Remote validated on Android phone
  • Remote validated on iPad
  • Remote validated on Android tablet
  • Per-platform Bonjour/mDNS validated
End-to-end vertical slice MVP projectdesignerhostclient0 / 2
  • Automated end-to-end test: Designer authors a quiz (with per-slide host-notes) → Designer pushes to Host over LAN → Host loads → Clients join (eager push) → quizmaster pairs a Remote → quizmaster advances slides from the Remote → answers tally → leaderboard reveals on slide-entry → leaderboard finalises
  • Manual playthrough of a multi-round quiz (text + multiple-choice + free-text + numeric + timer + leaderboard) on a real Wi-Fi network on every supported platform combination, with the quizmaster walking the room and driving advance/go-back from a paired Remote

First rich object types Alpha designerhostclient0 / 3
  • core.audio-clip element (Host canvas) — paired with core.free-text-input makes a music round
  • End-to-end test exercising a slide composed of audio + free-text answer
  • Document the object-type plugin contract as a new knowledgebase page once it has stabilized across the Alpha cohort
Additional content object types Alpha designerhostclient0 / 5
  • core.image element (display on either canvas)
  • core.video element (display on Host canvas)
  • core.drawing-input element (Client capture, Host display, live mirror; touch-only in v1)
  • core.buzzer-input element (first-press semantics across multiple Clients)
  • core.ranking-input element (Client canvas; drag-to-reorder; items text / image / both; all-or-nothing or per-position partial credit)
Crash recovery Alpha hostclient0 / 7
  • Host snapshots session state to disk after every scoring event and every slide advance (F-HO-18)
  • Snapshot format: JSON DTOs in the shared class library; same shape as live message envelopes
  • Host on launch detects a saved session matching the loaded .quiz and prompts the operator to resume or start fresh (F-HO-19)
  • Resume restores slide pointer, team list, scores, per-element state; Host re-advertises on Bonjour
  • Client persists its team identity (Host-issued stable token) to local storage (F-CL-10)
  • Client reconnect path attaches as the original team using the stored token, whether the Host disconnect was Wi-Fi or crash
  • End-to-end test: simulated Host crash mid-session → relaunch → resume → all Clients reconnect with scores intact
Remote app — rich control commands Alpha remotehost0 / 6

Layered onto the MVP minimum viable controls — the rich command set that turns the Remote from "advance the deck" into "fully run the room from your pocket". F-RE-9 (Remote side) and F-HO-24 (Host side).

  • Remote rich command: jump to a specific slide (F-RE-9, F-HO-24)
  • Remote rich command: trigger element reveals (e.g. show the leaderboard, show the answer) (F-RE-9, F-HO-24)
  • Remote rich command: lock / unlock Client input (F-RE-9, F-HO-24)
  • Remote rich command: extend / skip the timer (F-RE-9, F-HO-24)
  • Remote rich command: override scoring per team (F-RE-9, F-HO-24)
  • End-to-end test: Remote drives a full quiz session using the rich command set — including a triggered leaderboard reveal mid-slide and a live scoring override
Reliability and performance Alpha projecthostclientremote0 / 5
  • Reliability soak test: client disconnect storms, host backgrounding, Wi-Fi flap, simulated Host crash + recovery
  • Performance: 60 fps animation budget verified on iPhone 12 / equivalent Android (measured in the Unity runtime build)
  • Element answer-submit round-trip latency < 200 ms on local Wi-Fi (verified)
  • Remote control-message round-trip latency < 200 ms on local Wi-Fi (verified)
  • mDNS reliability on real devices. Validate Makaretu.Dns.Multicast.New advertisement + discovery on iOS 16+, Android 12+, Windows 10/11, macOS 12+ over a typical pub-grade Wi-Fi router. If the pure-C# multicast proves unreliable on any platform (advertisement drops, discovery misses Hosts, multicast blocked by the OS), implement the native fallback bridge for that platform — NSNetService (iOS) or NsdManager (Android) — and route the Unity-side discovery API through the bridge. Native bridges held as a contingency per Tech Stack. Conditional work — only landed if validation flags it.

Categories question type Beta designerhostclient0 / 1
  • core.categories-input end-to-end (Client canvas; multi-line entry; per-line scoring; "Name 5 X" format)
Match-pairs question type Beta designerhostclient0 / 1
  • core.match-pairs-input end-to-end (Client canvas; drag/connect pairing UX; left + right columns; items text / image / both; per-pair scoring)
Image-reveal question type Beta designerhostclient0 / 2
  • core.image-reveal-input end-to-end (Host canvas: image obscured by author-picked shader filter — pixelate / blur / mosaic — clearing over the question duration; Client canvas: multiple-choice submit; earlier-correct scoring via configurable speed-bonus curve)
  • Inspector fields: reveal curve (linear / ease-out / stepped), filter type, filter start/end intensity, points-vs-time function
Word-scramble buzzer question type Beta designerhostclient0 / 2
  • core.word-scramble-buzzer end-to-end (Host canvas: animated scrambled-letter tiles with drift / swap motion; Client canvas: buzzer-style first-press + text submit; first correct team scores)
  • Inspector fields: target word, scramble difficulty (animation intensity + hint cadence), wrong-answer penalty, per-team buzzer cooldown after wrong attempt
Multiple-choice elimination + speed-bonus extensions Beta designerhostclient0 / 3
  • Extend core.multiple-choice-input config with optional elimination schedule (incorrect options vanish on an author-configured timeline — e.g. 4 → 2 over the question duration)
  • Extend core.multiple-choice-input config with optional speed-bonus curve (more points for earlier correct submissions)
  • Both fields default off; inspector reveals them under an "advanced" disclosure
Brand-true polish Beta projectdesignerhostclientremote2 / 6
  • Shared design language: typography, palette, motion vocabulary per Design Specification applied across Designer, Host, Client, and Remote (F-X-3)
  • Final font picks locked: Bebas Neue (display + numerals), Inter (body), JetBrains Mono (mono)
  • Final brand colours locked from branding.jpg sampling: magenta #FF009F, electric-blue #16B2EB, deep-purple #961EEF, white #FFFFFF, near-black #0F0B1A
  • Mascot animation rig — rigged once in the shared assets package; pose library callable from any of the four apps
  • Brand-true reveal/transition motion replaces the Alpha first-pass
  • Final product/brand name locked (apps referred to by their engineering project names — Quiz.Designer / Quiz.Host / Quiz.Client / Quiz.Remote — until then)
Theme system Beta designerhostclientremote0 / 5
  • Designer chrome ships dark + light themes; author choice persisted in app settings (F-DE-25)
  • Quiz manifest gains a theme field (dark / light / brand presets) (F-DE-26)
  • Host renders each loaded quiz against the manifest's declared theme (F-X-4)
  • Client renders each loaded quiz against the manifest's declared theme (F-X-4)
  • Remote renders the host-mirror in the manifest's declared theme (F-X-4)
Per-team theming Beta designerclienthost0 / 4
  • Author defines a palette of team colours and an avatar set in the quiz (F-DE-21)
  • Team picks colour + avatar at join (F-CL-12)
  • Team identity (colour + avatar) renders consistently across every Client surface
  • Per-team Host moments (leaderboard rows, team callouts) use the chosen colour and avatar
Mini-game framework Beta hostclient0 / 5
  • Mini-game framework (native Unity) and a core.mini-game element (F-CL-7)
  • Mini-game registry (id → mini-game module) resolved by the core.mini-game element at slide load
  • mini-game.internal-clock shipped as a built-in (Host runs a hidden countdown; teams press to stop their own clock with no visible number; closer to configurable target time scores more; overshoot scores zero by default; inspector fields: target time, points-vs-error curve, overshoot policy, per-team retry rule)
  • mini-game.team-shoot shipped as a built-in (Host flashes a team name; that team races a configured opponent — named team or next-fastest — to tap their Client; wrong-team presses lose configurable points; inspector fields: match-up rule, win/lose points, wrong-team penalty per team, reaction window)
  • mini-game.spin-wheel-modifier shipped as a built-in (post-answer flourish; spinning wheel with ± value segments; lands on a segment that applies the modifier to the configured target teams; inspector fields: segment list, spin duration, target rule, parent-question linkage)
Sound design Beta projecthostclientremote0 / 5

Full audio language per Design Specification — sound design (F-X-5).

  • Source / commission stings: correct, incorrect, lock, time-up, big reveal, end of round, end of quiz
  • Source / commission transition motifs: slide-advance whoosh, leaderboard reveal swell, round-change motif
  • Optional ambient music bed (light, between-rounds), author-controllable per quiz
  • Audio asset licensing decided (royalty-free pack, commissioned, or hybrid)
  • Audio mix tuned for venue — stings cut through pub ambient noise without being intrusive
Quiz UK pilot Beta project0 / 4
  • At least one real Quiz UK pub night runs successfully on the Beta build
  • Real-device testing across all platforms — modern + older device on each side of iOS / Android
  • Performance and latency targets re-verified on older test devices
  • Hardware/OS minimum versions confirmed against real devices and Unity's Editor and Player support matrix.

Pre-release Production project0 / 9

Confirmed distribution channels: iOS App Store, Google Play Store, Microsoft Store, macOS App Store, macOS direct DMG.

  • iOS App Store: signing, packaging, submission flow, update mechanism
  • Google Play Store: signing, packaging, submission flow, update mechanism
  • Microsoft Store: signing, packaging, submission flow, update mechanism
  • macOS App Store: signing, packaging, submission flow, update mechanism
  • macOS direct DMG: notarization, distribution, update mechanism
  • Sign in with Apple wired up (only relevant if cloud auth Stretch ships first)
  • Privacy policy drafted and published
  • Store listings drafted (descriptions, screenshots, age rating, accessibility statements)
  • Crash reporting / telemetry decisions made

Stretch goals Stretch projectdesignerhostclientremote0 / 38

These items deliver the cloud-backed authoring path sketched in Backend Schema and Authentication, plus other Stretch work. None are in v1 scope.

Cloud-backed authoring

  • Pick cloud vendor (managed Postgres + object storage) and provision the project
  • profiles, quizzes, quiz_versions tables (F-DE-15)
  • RLS policies on tables (owner_id = auth.uid())
  • Storage RLS scoped to quizzes/{owner_id}/...
  • Version pruning job (latest 20 + pinned)
  • Email magic link sign-in on Designer (F-DE-1)
  • Email magic link sign-in on Host (F-HO-1)
  • Designer: save quiz to cloud, version history, restore (F-DE-15)
  • Designer: soft-delete + trash view (F-DE-16)
  • Designer: pin a version to protect from pruning
  • Host: list quizzes from the cloud library, mark which are downloaded (F-HO-2)
  • Host: download and cache a quiz package from the cloud (F-HO-2)

Additional question types

  • core.hotspot-input (tap on image coordinates; map / anatomy / geography rounds)

AI-aided quiz authoring (F-DE-22)

  • LLM integration in Designer with verification UX (every generated artefact requires explicit author confirmation)
  • Generation modes: question on a topic, multiple-choice distractors, host-notes draft from a question + answer
  • Privacy posture decision: third-party API vs local model

Broadcast / streaming-friendly Host view (F-HO-22)

  • Alternative Host display mode: subdued backgrounds, boosted overlay contrast, in-room-only chrome suppressed
  • Toggleable from the Host (and via the Remote) without restarting the session

Recurring teams across sessions (F-HO-23, F-CL-13)

  • Team-identity store keyed by quizmaster + venue (depends on cloud auth)
  • Team-claim UX on Client join ("Are you returning team X?")
  • Privacy and data-protection posture documented alongside

Tournaments / leagues

  • Season schema in the cloud library (depends on recurring teams + cloud-backed authoring)
  • Cross-session scoring rules
  • League standings UI on Designer/Host

Quiz templates / starter packs (F-DE-23)

  • Cloud library exposes templates as a distinct kind of quiz
  • Designer "clone from template" flow makes a private, editable copy

Question bank / reusable questions (F-DE-24)

  • Question schema decoupled from a specific quiz (cloud library)
  • Designer "pull from question bank" flow into any quiz

Speed-round mode

  • Round-level config: very short timer, no inter-slide pause, no countdown UI
  • Could be promoted to Alpha as a small extension to round timing schema

Other stretch

  • Bundle-supplied object types loader (signing, sandboxing, install UX — see OQ#1)
  • Apple Pencil sketching/annotation in Designer (F-DE-17)
  • Equivalent stylus support on non-Apple tablets (F-DE-17)
  • Per-individual identity within a team — would change the eager-push model and join screen
  • Multi-Remote support (co-quizmasters, conflict resolution, override semantics)
  • Faster-cadence Host crash recovery (per-message snapshots; multi-instance Host failover)
  • Cross-quiz analytics — see OQ#3
  • Internet-based live play relay (Cloudflare Durable Objects or similar) — see OQ#2
Designer55 / 69
Tauri 2 + Angular Designer setup MVP designer8 / 8

Per Designer Shell. Single Tauri 2 workspace hosting an Angular 21 application; desktop only (Windows + macOS).

  • Create Quiz.Designer/ as a Tauri 2 + Angular 21 workspace (pnpm create tauri-app then ng new into the same root, or scaffold by hand per Repository Layout — Designer project graph). Angular CLI workspace targets ES2022; Tauri Rust shell targets Rust 1.78+.
  • Pin Angular to the latest stable major (21.x at time of MVP; check npm view @angular/core dist-tags and use the latest); pin Tauri to 2.x stable.
  • Configure src-tauri/tauri.conf.json with decorations: false, titleBarStyle: "Overlay" on macOS, windowEffects: { effects: ["mica"] } on Windows 11, an initial window size matching the mockups, and a Quiz.Designer app identifier.
  • Wire tauri-plugin-dialog, tauri-plugin-fs, tauri-plugin-process, tauri-plugin-menu, and the mdns-sd Rust crate via Tauri commands under src-tauri/src/commands/.
  • Bootstrap the Angular workspace under Quiz.Designer/src/ with Angular Material + Angular CDK + RxJS; configure standalone-components architecture, signal-based reactivity, and zoneless change detection.
  • Override Angular Material primary / secondary / surface tokens in a custom CSS-variable theme that mirrors the Studio chrome surface tokens defined in Design Specification — Studio. Showtime brand colours stay inside the embedded Quiz.Preview canvas only.
  • Splash + boot-sequence window. Tauri opens a borderless 720 × 480 splash WebviewWindow first, loading the Angular SplashComponent. Splash pre-warms async services (schema codegen check, library index hydration, Quiz.Preview ready event, mDNS listener init) and reports stage events to the splash UI. On all-ready, Tauri opens the main shell window at the saved size, focuses it, and fade-closes the splash (~120 ms). Splash is brand-locked Showtime and ignores the Studio chrome theme — identical visual in light and dark. Per Designer Shell — Boot sequence.
  • Custom Angular titlebar component (<app-titlebar>) implements the Rider-style strip: logomark + wordmark + File / Edit / View / Help dropdowns + doc title + dirty dot + Undo / Redo + autosave badge + Run-from-slide CTA. -webkit-app-region: drag for the drag region; setTrafficLightPosition for macOS traffic-light placement; right-edge padding on Windows to clear the OS caption buttons. Per Designer Shell — Custom titlebar.
Designer composition + service layer MVP designer6 / 6

The Angular DI graph and the platform-adapter layer that lets the same code run under Tauri today and the browser later.

  • PlatformAdapter interface in src/app/platform/ declares file-dialog, filesystem, mDNS, subprocess-spawn, and OS-menu capabilities.
  • TauriPlatformAdapter implementation wires each capability to the matching Tauri command.
  • AuthoringSession Angular service — activeQuiz, selection, isDirty exposed as signals + observables; QuizChanged / SelectionChanged / DirtyChanged events.
  • CommandDispatcher Angular service — execute() + undo() pipeline; per-quiz undo / redo stacks. First two concrete commands — CreateNewQuiz and ReplaceActiveQuiz — validate the contract end-to-end.
  • PropertyDispatcher helper that batches keystroke edits in inspector components and emits commands at commit boundaries — per Designer Shell — Undo / redo granularity.
  • DI bootstrap registers AuthoringSession, CommandDispatcher, ObjectTypeRegistry (seeded with core.text initially), and the platform adapter.
Designer persistence + autosave MVP designer11 / 11
  • PersistenceService Angular service — .quiz save / load via TauriPlatformAdapter's fs commands + the zip Rust crate; recent-files index in the platform app-data folder.
  • Atomic write protocol for every disk write — <target>.tmp → fsync → rename, implemented on the Rust side and exposed as a single Tauri command. Stranded .tmp GC on launch.
  • Autosave timer — debounced (8 s after last edit, 30 s ceiling); RxJS-driven scheduler with injectable clock for headless tests.
  • Tiered slot retention — keep snapshots at anchor ages (now / 5 min / 1 hour / 6 hours / 1 day / 7 days); evict the rest.
  • File → Restore from backup… Angular dialog listing surviving slot backups for the active quiz. Picking a backup loads it as activeQuiz with isDirty = true.
  • Corruption detection on Open — when a .quiz fails to load, surface a banner "Couldn't open . The most recent backup is from
  • Recovery flow on launch with no quiz arg — empty-state banner listing orphaned drafts (" drafts from an earlier session").
  • Crash detection — clean-exit marker written on shutdown; absence on next launch indicates an unclean exit.
  • Snapshot GC pass on launch — cleans stranded .tmp files, evicts past-cap backups, removes AutoSaves/<quizId>/ folders whose source has not been opened in 60 days, removes AutoSaves/<sessionId>/ draft folders older than 30 days.
  • Persistent autosave-failure banner ("Autosave failed 3× — your work is at risk") after 3 consecutive backup-or-write failures within 2 minutes. Backup failure aborts the autosave write — never write the new state without a backup.
  • Bundle-on-save / unbundle-on-load logic spanning Persistence + Library: Save copies referenced library bytes into the .quiz archive's resources/; Open content-hash-de-dupes archive resources against the local library. Round-trip test verifies a saved-then-opened quiz preserves every resource id and bytes match.
Designer library + file pickers MVP designer4 / 4
  • LibraryService Angular service — device-wide media catalog at platform app-data folder; SHA-256 content-hash de-dup (Web Crypto API in the renderer); LibraryAsset record.
  • File-picker capability on PlatformAdapter — Tauri impl wraps tauri-plugin-dialog.
  • macOS entitlements / Info.plist for file access — declare app-sandbox, network.client, network.server, files.user-selected.read-write.
  • Windows installer manifest opts the app out of Program Files placement constraints that would block library writes.
Designer object types (Designer side) MVP designer2 / 2
  • ObjectTypeRegistry Angular service — editor-surface registry on the Designer side of the plugin contract. First registered type is core.text (paired with the cross-cutting Object-type plugin contract section in project.md).
  • First Angular properties-inspector component for core.text, bound to the type's view-model service via the PropertyDispatcher helper.
Designer transfer skeleton MVP designer1 / 1
  • TransferService Angular service — wraps the LAN-discovery + WebSocket push path. Skeleton lands as StubTransferService that throws on pushAsync / discoverHostsAsync; the real impl lands with the Designer→Host transfer chunk in project.md.
Designer input + shortcuts MVP designer3 / 3
  • Global keyboard-shortcut router — Angular service installs a keydown listener that routes Ctrl/Cmd-Z / Shift-Z / S / O / Delete / arrows to CommandDispatcher or component event outputs. Ignores keypresses inside inputs / contentEditable so typing into the inspector still works.
  • PowerPoint-style preview shortcuts: F5 enters Extended Preview mode from slide 1, Shift+F5 enters from the current slide, Ctrl+F5 triggers Run-on-Host subprocess spawn.
  • Drag-drop into the design surface — Angular CDK drag-drop wires palette → canvas and slide-list reorder. Touch fallback via long-press gesture.
Designer UI / component skeletons MVP designer1 / 1
  • Stub Angular standalone components: ShellComponent (3-column layout via CDK), SlideListComponent, PaletteComponent, PropertiesInspectorComponent, LibraryPanelComponent, PushToHostDialogComponent, LibraryPickerDialogComponent, RestoreFromBackupDialogComponent. Each subscribes to the matching service event (AuthoringSession.QuizChanged / SelectionChanged, LibraryService.Changed) and cleans up subscriptions via DestroyRef.
Designer panel layout — resize, move, pop-out MVP designer5 / 5

Per the Designer Shell, the v1 shell needs operator-customisable layout — panels that resize, drag-reorder, and detach into separate windows.

  • Column splitters — draggable splitter handles between the slide-list / canvas / right-rail columns. Drag clamps each column to a min / max range; double-click resets to the mockup default. Remaining: widths persist to a PreferencesService slice keyed quiz.designer.layout.v1 (the service lands alongside the autosave-retention chunk).
  • Pane registryLayoutState enumerates the panes (slide-list, canvas, right-rail) and the dock zones (left / center / right / popped); a PaneHostComponent resolves a pane id to its component. The shell renders each zone from LayoutService.state so panels are re-homed by moving ids, not by rewriting layout markup.
  • Drag-reorder between dock zones — each pane carries a drag grip; dropping it on another dock zone calls LayoutService.movePane. Layout changes are workspace state, deliberately not routed through the quiz CommandDispatcher (a panel move should not sit on the document undo stack alongside slide edits — consistent with Designer Shell — Undo / redo granularity treating workspace operations as session, not authoring, actions). Persistence via PreferencesService lands alongside the autosave-retention chunk.
  • Pop-out to a separate window — pane header has a "↗ pop out" button. Tauri shell binds this to WebviewWindow.new("pane-<id>", { url: "/pane/<id>" }) via the pop_out_pane command; closing the pop-out re-docks the pane to its previous slot via the pane:redock event the backend emits on WindowEvent::Destroyed. Web-shell binding to window.open lands with the BrowserPlatformAdapter (Web Designer Stretch).
  • Layout reset — the titlebar View → Reset Layout menu item calls LayoutService.reset, restoring the default slide-list / canvas / right-rail shape; the reset is persisted so it survives a restart. Preview pop-out as the first concrete use is deferred alongside the rest of the Quiz.Preview chunks below.
Designer authoring features MVP designer14 / 14
  • Create new quiz — CreateNewQuizCommand is wired to the slide-rail + New quiz button + the File menu. (F-DE-2)
  • New-quiz title / description / tags entry dialog so the user picks an identity instead of CreateNewQuizCommand hard-coding "Untitled". (F-DE-2)
  • Add, edit, reorder, delete slides — AddSlideCommand + DeleteSlideCommand + click-to-select + CDK drag-reorder via ReorderSlidesCommand are wired into SlideList. (F-DE-3)
  • Per-slide rename — inline-edit affordance on each slide-list row, dispatching SetSlideTitleCommand directly through CommandDispatcher (blur / Enter commits; Escape cancels). (F-DE-3)
  • Group slides into rounds; reorder, ungroup (F-DE-4)
  • Place / move / configure / delete elements on the Host canvas (F-DE-5)
  • Place / configure / delete elements on the Client canvas (F-DE-6)
  • Object-type palette listing built-in types — initially core.text, expanded as the MVP cohort lands (F-DE-7)
  • Configure scoring per slide and per round, including late-submission rules (F-DE-9)
  • Configure timing per slide: duration, lock-on-time-up, late-submission rule, quizmaster-override-allowed (F-DE-10)
  • Per-element reveal trigger UI: choose on-slide-entry / on-quizmaster-trigger / after-delay (F-DE-20)
  • Per-slide host-notes editor (free-form text, rendered only on the Remote during play) (F-DE-19)
  • Package as canonical .quiz format — Quiz.Core.Packaging.QuizPackage{Reader,Writer} write + read the ZIP (manifest.json + slides/*.json), data-only, no runtime code. (F-DE-18)
  • Auto-derive the manifest's objectTypes[] registry on save from the element type-ids actually used across the slide tree, so the Host can refuse / upgrade packages whose declared cohort doesn't match its built-in registry. (F-DE-18)
Designer Quiz.Preview JS bridge [MVP — deferred] designer0 / 7

Deferred to the end of the MVP feature work. The static DOM Host / Client canvas inside the Designer's main canvas panel is enough to author a .quiz package against — the WebGL preview only shows how the slide will look once it lands on the Host + Client runtimes, so it can land last without blocking the rest of the authoring surface.

  • Bundle the WebGL output of Quiz.Preview/ under Quiz.Designer/src/assets/preview/ (consumed from the CI artefact at build time) and load it inline as a <canvas> via the standard Unity WebGL loader.
  • PreviewBridge Angular service — pushes slide JSON to the embedded Quiz.Preview canvas via unityInstance.SendMessage; subscribes to ready / rendered / error JS callbacks invoked by the WebGL build.
  • First end-to-end push: load a placeholder core.text slide, push state, observe re-render in the embedded canvas, log the rendered callback.
  • Reconnect strategy: if the canvas reports error or fails to load, retry with backoff and surface a banner.
  • Embedded Quiz.Preview WebGL canvas reflects the slide live as it is edited — every state mutation pushes JSON to the canvas via PreviewBridge (F-DE-11)
  • Preview pop-out is the first concrete use of the multi-window pane framework — preview canvas lives in its own pane so authors can run it on a second monitor while they edit. Default keyboard shortcut: Ctrl/Cmd + Shift + P.
  • Smoke-test launch via tauri dev on Windows and macOS — splash → main shell with embedded preview canvas visible and a Quiz.Preview ready event observed.
Quiz.Preview Unity setup [MVP — deferred] designer0 / 6

Deferred alongside the Quiz.Preview JS bridge. Sibling Unity project, WebGL-only build target. No editing UI; pure render target driven by JS messages from the Angular shell.

  • Create Quiz.Preview/ as a Unity project with WebGL set as the only build target.
  • Add Scenes/Preview/Preview.unity — single render scene with the slide canvas + camera; entry point for WebGL boot.
  • Add Scripts/PreviewBoot.cs — listens for JS messages (LoadSlide, UpdateProperty, SetSelection); calls back via [DllImport("__Internal")] for ready / rendered / error events.
  • Reference com.quiz.shared-assets, com.quiz.runtime, com.quiz.core via Packages/manifest.json.
  • Player Settings: defaultScreenWidth/Height flexible (canvas size driven by host page); tune WebGLMemorySize for the largest expected scene.
  • Smoke-test: WebGL build loads in a plain HTML page, accepts a LoadSlide JS call, renders a placeholder slide, emits a rendered callback.

Designer additions for Alpha Alpha designer0 / 1
  • Object-type palette includes the Alpha cohort (audio, image, video, drawing, buzzer)
Host0 / 21
Per-Unity-project scaffolding — Host MVP host0 / 4
  • Scenes/MainMenu/MainMenu.unity — pre-quiz lobby (load .quiz, accept Designer transfers, list connected teams, start session).
  • Scenes/Play/Play.unity — slide-driven runner that advances through the quiz, dispatches element behaviours, and runs the live session.
  • Player Settings: defaultScreenOrientation: 3 (LandscapeLeft); disable portrait autorotates; defaultScreenWidth/Height: 1920/1080 (TV / projector target).
  • Smoke-test in Editor: Host launches, MainMenu and Play scenes load without errors, orientation is landscape.
Host live-session features MVP host0 / 15
  • Open WebSocket server, advertise on local network via Bonjour/mDNS (F-HO-3)
  • Load .quiz from local FS via file picker (in addition to Designer push) (F-HO-5)
  • Loaded packages remain available offline (F-HO-6)
  • Resolve every object type the loaded package declares; refuse on missing or version-incompatible types (F-HO-7)
  • Start a session from a loaded package (F-HO-8)
  • Join screen lists connected teams (one Client device per team) with team names (F-HO-9)
  • On Client connect, eagerly push Client-canvas content + Client-side resources for the whole quiz (F-HO-10)
  • Advance through slides; render each slide's Host canvas at the connected display's resolution (F-HO-11)
  • Receive answers (via element protocol extensions), apply scoring (including late-submission rules), render leaderboard on triggered reveal (F-HO-14)
  • Host is authoritative on timer state; emits "time-remaining" tick and "lock" message (F-HO-16)
  • Host operator can override timer live for the current slide (extend / skip / lock / unlock) (F-HO-17)
  • Handle Client disconnect/reconnect without ending the session (F-HO-15)
  • Host accepts a paired Remote on the control-message-family WebSocket; pairing UX (manual confirm or QR-code — pick during the prototype) (F-HO-20)
  • Host streams a periodic mirror of its current display, the current slide's host-notes, and live state (scores, timer remaining, slide index) to the paired Remote (F-HO-21)
  • Host accepts core navigation commands from the paired Remote: advance, go-back (F-HO-21)

First-pass animation Alpha host0 / 2
  • Animated reveals and transitions on Host slides — first pass, not yet brand-true (F-HO-13)
  • Leaderboard reveal animations — first pass
Client0 / 15
Per-Unity-project scaffolding — Client MVP client0 / 4
  • Scenes/MainMenu/MainMenu.unity — discovery + join (list visible Hosts, enter team name, join).
  • Scenes/Play/Play.unity — render the current slide's Client canvas, dispatch input elements, show score.
  • Player Settings: defaultScreenOrientation: 4 (AutoRotation) — Client supports portrait and landscape on phones and tablets.
  • Smoke-test in Editor: Client launches, MainMenu and Play scenes load without errors.
Client team-play features MVP client0 / 11
  • Discover hosts via Bonjour (F-CL-1)
  • Enter team name and join — one shared device per team; no per-individual identity (F-CL-2)
  • Receive eager push from Host on join; cache slide content + resources for the session (F-CL-3)
  • Late-join progress UI + jump-to-current-slide on completion (F-CL-3)
  • Resolve every object type the package declares; report a fatal mismatch to the host on missing or version-incompatible types (F-CL-4)
  • Render the current slide's Client canvas with its responsive layout (F-CL-5)
  • core.multiple-choice-input element: tap input, submit answer (F-CL-6)
  • core.free-text-input element: text entry, submit answer (F-CL-6)
  • Render Host's authoritative timer state; lock input on Host "lock" message (F-CL-11)
  • Show team score and standings (F-CL-8)
  • Reconnect after a disconnect (F-CL-9)
Remote0 / 4
Per-Unity-project scaffolding — Remote MVP remote0 / 4
  • Scenes/MainMenu/MainMenu.unity — discovery + pairing (list visible Hosts, pair via short numeric code OR QR scan per F-RE-2, connect).
  • Scenes/Play/Play.unity — render the mirrored Host canvas + host-notes + live state, send control commands.
  • Player Settings: defaultScreenOrientation: 1 (Portrait); disable landscape autorotates.
  • Smoke-test in Editor: Remote launches in portrait orientation, MainMenu and Play scenes load without errors.