Pub Quiz Platform

Build Plan — Project (cross-cutting)

Status Live checklist
Scope Project (cross-cutting)
Generated 2026-05-13
Source of truth .claude/context/knowledgebase/build-plan.md

The implementation work that delivers the spec. Every section carries a phase tag and one or more app-scope chips: project for cross-cutting work, or one or more of designer, host, client, remote.

Mirrored into Azure DevOps as User Stories (one per ## section) and Tasks (one per - [ ] bullet) by /sync-build-plan. ADO IDs are stored inline as HTML comments on each heading / bullet.

Definition of Done for every item:


Foundation MVP project 3 / 6
  • Initialize repo with four Unity projects (Quiz.Host, Quiz.Client, Quiz.Remote, Quiz.Preview) plus the .NET MAUI Blazor Quiz.Designer .csproj plus shared local UPM packages (com.quiz.core pure C# / .NET Standard 2.1, com.quiz.runtime Unity-aware, com.quiz.shared-assets). 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 project 0 / 23

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 / Catalyst SDKs, Unity Editor + license, 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-designer.yml — .NET MAUI build for net8.0-windows* (Microsoft-hosted) + net8.0-maccatalyst + net8.0-ios (Mac mini); bundles the published quiz-preview-webgl artefact under wwwroot/; runs Razor / xUnit unit tests.
  • .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 project management setup MVP project 0 / 11

Per Project Management. The PRD is the locked-in requirements artefact; Epics / Features / Stories / Issues are decomposed from it after sign-off.

  • ADO project provisioned (or existing project repurposed) with Boards, Repos, Pipelines, and Wiki enabled.
  • Sprint board configured with the six columns: To Do, Blocked, In Development, Ready For Test, In Testing, Done.
  • Sprint length agreed with the team (default candidate: two-week sprints) and configured in Iteration Paths.
  • Work-item types in use: Epic, Feature, User Story, Issue. Customisations applied if the default templates need adjusting (e.g. adding "Acceptance criteria" + "Definition of done" fields where missing).
  • Wiki provisioned and pointed at the auto-generated knowledgebase mirror produced by the docs skill.

Decompose the PRD into Epics / Features / Stories / Issues

  • Walk the signed-off PRD; create one Epic per major area (typically corresponding to a ## Section in this build plan tagged [MVP] or [Alpha]).
  • Decompose each Epic into Features (shippable user-facing capability).
  • Decompose each Feature into User Stories (sprint-sized) and Issues (technical task / bug).
  • Each Story / Issue has acceptance criteria + Definition of Done filled in per Testing + the development-standards skill.
  • Backlog prioritised; first sprint's items pulled into the Sprint Backlog.
  • Build-plan items linked to ADO work items so completion in ADO ticks the corresponding - [ ] here.
MAUI Blazor Designer setup MVP designer 0 / 10

Per Designer Shell. Single .NET MAUI Blazor Hybrid .csproj, desktop only (Windows + macOS Catalyst).

  • Create Quiz.Designer/ as a .NET MAUI Blazor Hybrid .csproj targeting net8.0-windows* and net8.0-maccatalyst.
  • Reference Quiz.Core as a project / package reference.
  • Wire a BlazorWebView into the MAUI shell with a root Razor component.
  • Add MudBlazor NuGet reference; register in MauiProgram via builder.Services.AddMudServices(); add <MudThemeProvider />, <MudPopoverProvider />, <MudDialogProvider />, <MudSnackbarProvider /> to the root Razor layout.
  • Override Material primary / secondary / surface tokens in a custom MudTheme to match the Design Specification palette.
  • Mac Catalyst multi-window config: add SceneDelegate.cs to Platforms/MacCatalyst/ (subclassing MauiUISceneDelegate); add UIApplicationSceneManifest (UIApplicationSupportsMultipleScenes = true, UISceneDelegateClassName = SceneDelegate) to Platforms/MacCatalyst/Info.plist.
  • WebView2 user-data-folder workaround on Windows: in Platforms/Windows/App.xaml.cs (or the MAUI app constructor), set WEBVIEW2_USER_DATA_FOLDER env var to a writable path under FileSystem.AppDataDirectory before any WebView control is initialised. Required when the app is installed under Program Files.
  • Bundle the WebGL output of Quiz.Preview/ as static content served by BlazorWebView under wwwroot/ and load it as a <canvas> via the Unity loader.
  • First JS bridge round-trip: Razor → unityInstance.SendMessage → WebGL Unity → JS callback → Razor.
  • Smoke-test launch on Windows and macOS Catalyst — splash → main shell with embedded preview canvas visible.
Quiz.Preview Unity setup MVP designer 0 / 6

Sibling Unity project, WebGL-only build target. No editing UI; pure render target driven by JS messages from the Razor 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.
Per-Unity-project scaffolding MVP projecthostclientremote 0 / 15

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.

Universal projecthostclientremote

  • 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.

Host host

  • 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.

Client client

  • 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.

Remote remote

  • Scenes/MainMenu/MainMenu.unity — discovery + pairing (list visible Hosts, pair via manual confirm or QR, 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.
Load-bearing prototype MVP projecthostclient 0 / 5
  • In-process C# WebSocket server runs in the Host (library pick — see Open Questions). 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 (library pick — see Open Questions)
  • 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
Shared schema MVP project 0 / 9
  • Quiz manifest, slide, canvas, and element schemas in the shared C# class library (per Quiz Package Format)
  • Round (slide-grouping) schema in the shared C# class library (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 schema (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 in the shared C# class library — declares type id, schema version, and the surface contracts referenced by the apps
  • Round-trip serialization tests for every schema
Object-type plugin contract MVP projectdesignerhostclient 0 / 8
  • IObjectType interface in the shared C# class library (type id, schema version, schema accessor) — see Object-Type Architecture
  • Designer editor-surface contract (Razor component + view-model in the MAUI Blazor Designer; declares 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
  • 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
MVP object-type cohort MVP designerhostclient 0 / 5

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.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 (MAUI Blazor) architecture skeletons MVP designer 0 / 31

The architectural skeleton the Designer authoring features sit on top of. Top-level shape in Designer Shell. Each skeleton item lands minimally first (just enough to compile and route events) and grows as feature items below consume it. Subsystem-level pages will be authored once these skeletons settle — they replace the previous Unity-only architecture/designer/* subdirectory pages.

Historical note: an earlier set of skeleton items targeted Unity subsystems (_Game/Authoring/, _Game/State/, _Game/Commands/, etc.) inside the previous Unity Quiz.Designer/ project. Those tasks are obsolete — the Designer is now MAUI Blazor — and have been replaced by the items below. The original list is recoverable from git history if any of its detail (autosave cadence, tiered backup slots, atomic write protocol) needs porting forward.

Project + composition root designer

  • Top-level folders under Quiz.Designer/: Components/ (Razor components), Services/ (DI-registered services like IPersistenceService, ILibraryService, IPreviewBridge), State/ (Quiz Model + undo stack), Commands/ (undoable operations), Wwwroot/ (static assets including the bundled Quiz.Preview/ WebGL output)
  • Composition root: MauiProgram.cs registers every service in DI; App.razor mounts the Razor component tree
  • Razor _Imports.razor references the chosen component library (e.g. @using MudBlazor once the library is picked)
  • Single Quiz.Designer.csproj with net8.0-windows10.0.x, net8.0-maccatalyst, net8.0-ios target frameworks; conditional <ItemGroup> for platform-only chrome

State + commands designer

  • AuthoringSession C# class in State/: ActiveQuiz, Selection, IsDirty, plus explicit C# events (QuizChanged, SelectionChanged, DirtyChanged). Razor components subscribe via injection.
  • ICommand interface + dispatcher in Commands/: Execute() + inverse-Undo() contract; undo/redo stack lives here. First two concrete commands — CreateNewQuizCommand, ReplaceActiveQuizCommand — validate the contract end-to-end.
  • Razor components mutate state only through commands so every change is undoable.

Persistence + autosave designer

  • IPersistenceService (DI singleton) — .quiz save / load via Quiz.Core serialisation; recent-files index in platform-appropriate app-data folder (Environment.SpecialFolder.LocalApplicationData on desktop, MAUI FileSystem.AppDataDirectory on iPad).
  • Atomic write protocol for every disk write — <target>.tmp → fsync → rename. Stranded .tmp GC on launch.
  • Autosave timer + per-fire sequence — debounced (8 s after last edit, 30 s ceiling). For quizzes with a SourcePath: conditional pre-write backup (copy SourcePath to app-data AutoSaves/<quizId>/<timestampISO>.quiz only if the freshest existing backup is ≥ 5 min old) → atomic write the new state to SourcePath → prune. For never-saved quizzes: write a draft snapshot to AutoSaves/<sessionId>/<timestampISO>.quiz.
  • Tiered slot retention — pool capped at 6 backups per quizId pinned to exponentially-spaced anchor ages: now (< 5 min), 5 min (≥ 5 min), 1 hour, 6 hours, 1 day, 7 days. Pruning algorithm walks the pool newest → oldest and assigns each backup to the smallest remaining anchor it fits; everything else is evicted.
  • File → Restore from backup… — Razor dialog showing the surviving slot backups for the active quiz. Each entry shows the slot label, a friendly relative time, and the backup's file size. Picking a backup loads it as ActiveQuiz with IsDirty = true.
  • Corruption detection on Open — when a .quiz fails to load, prompt "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 routes through the recovery banner.
  • 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.

Library + file pickers designer

  • ILibraryService (DI singleton) — device-wide media catalog at platform app-data folder; content-hash SHA-256 de-dup; LibraryAsset record.
  • IFilePickerService (DI singleton) — wraps MAUI's FilePicker.PickAsync and MediaPicker.PickPhotoAsync/PickVideoAsync for cross-platform native pickers (Win + macOS + iPad in one API).
  • iOS / iPad Info.plist: NSPhotoLibraryUsageDescription, NSDocumentsFolderUsageDescription permission strings (English + on-brand copy).
  • macOS Catalyst entitlements / Info.plist for file access (sandbox + user-selected files).

Quiz.Preview JS bridge designer

  • IPreviewBridge (DI singleton) — abstraction over the JS interop that pushes slide state to the embedded Quiz.Preview WebGL canvas via unityInstance.SendMessage and receives ready / rendered / error events back via JS callbacks → Razor event invocation.
  • 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.

Object types (Designer side) designer

  • IObjectTypeRegistry (DI singleton) — editor-surface registry on the Designer side of the plugin contract from Quiz.Core. First registered type is core.text (paired with the cross-cutting Object-type plugin contract section above).
  • First Razor properties-inspector component for core.text, bound to the type's view-model.

Transfer skeleton designer

  • ITransferService (DI singleton) — thin Designer wrapper around the WebSocket + mDNS code in Quiz.Core (or Quiz.Runtime if Unity-aware bits are required; ideally Designer side stays in Quiz.Core-only territory). The full Designer→Host transfer flow is tracked in the section below; this skeleton item lands just the DI wiring + a stubbed PushAsync that fails fast.

Input + shortcuts designer

  • Keyboard shortcut handling in the Razor shell — Cmd/Ctrl-S / O / Z / Shift-Z / A / Delete / arrows wired through JSInterop keydown listeners in the root component, dispatched as commands. Long-press touch substitute for multi-select on iPad.
  • Drag-drop into the design surface — Blazor's drag-and-drop API for asset picker → canvas, plus iPad-friendly long-press-to-drag fallback.

UI / Razor component skeletons designer

  • Stub Razor components: Shell.razor, SlideList.razor, Palette.razor, PropertiesInspector.razor, LibraryPanel.razor, PushToHostDialog.razor, LibraryPickerDialog.razor, RestoreFromBackupDialog.razor. Bodies are first-pass placeholders pending a UI review pass by the user (mockups in docs/ui-mockups/designer/ are the visual targets).
Designer→Host transfer MVP designerhost 0 / 7
  • Designer can save a quiz to disk as a .quiz file (manifest + slides + resources) — Razor command, atomic write via IPersistenceService (F-DE-12)
  • Designer can re-open an existing .quiz file from disk — native file picker via IFilePickerService (F-DE-13)
  • Designer discovers Hosts on the local network via Bonjour/mDNS — Quiz.Core discovery layer surfaced through ITransferService (F-DE-14)
  • Designer can push a saved .quiz file to a chosen Host over the WebSocket transport — uses .NET System.Net.WebSockets from the MAUI 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 authoring features MVP designer 0 / 12
  • Create new quiz with title, description, tags (F-DE-2)
  • Add, edit, reorder, delete slides (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)
  • Embedded Quiz.Preview WebGL canvas reflects the slide live as it is edited — every state mutation pushes JSON to the canvas via IPreviewBridge (F-DE-11)
  • 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 (manifest declares object types + versions; no runtime code) (F-DE-18)
Host live-session features MVP host 0 / 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)
Client team-play features MVP client 0 / 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)
Remote app — minimum viable controls MVP remotehost 0 / 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 (manual confirm on Host or QR-code pairing — pick during the prototype) (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
Cross-platform validation MVP projectdesignerhostclientremote 0 / 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 Catalyst); iPad and Android tablet authoring are Stretch. Linux is out of scope.

  • Designer (MAUI Blazor) validated on Windows — Quiz.Preview WebGL canvas embedded, JS bridge round-trip, multi-window via MAUI Window API
  • Designer (MAUI Blazor) validated on macOS (Catalyst) — same surface as Windows; multi-window
  • 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 projectdesignerhostclient 0 / 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 / 4
  • 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)
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
Designer additions for Alpha Alpha designer0 / 1
  • Object-type palette includes the Alpha cohort (audio, image, video, drawing, buzzer)
Reliability and performance Alpha projecthostclientremote0 / 4
  • 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)
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

Mini-game framework Beta hostclient0 / 3
  • Mini-game framework (native Unity) and a core.mini-game element (F-CL-7)
  • First mini-game shipped as a built-in
  • Second mini-game shipped as a built-in
Categories question type Beta designerhostclient0 / 1
  • core.categories-input end-to-end (Client canvas; multi-line entry; per-line scoring; "Name 5 X" format)
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)
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
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
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 / 41

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.match-pairs-input (drag/connect; multi-pair scoring)
  • core.ranking-input (drag-to-reorder; partial-credit option)
  • core.hotspot-input (tap on image coordinates; map / anatomy / geography rounds)
  • core.true-false-input (visually distinct from multiple-choice)

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#4
  • Internet-based live play relay (Cloudflare Durable Objects or similar) — see OQ#3