First-run onboarding: contextual coach-marks + How-to-Play card + dev replay toggle

Teaches the deep, interlocking loop — especially the inverted win condition
(you win by CLEARING EXPEDITIONS, not by surviving base sieges; DR-042/DR-043).

- OnboardingSystem: client-only observe-only PresentationSystemGroup overlay
  (own UIDocument @ sortingOrder 60), soft-gated 10-beat coach-mark sequence
  with a world-space ▶ pointer; never mutates sim / never destroys a ghost.
- OnboardingStepMath: pure, unit-tested step machine (snapshot + IsSatisfied +
  scheme-aware prompts + pointer kinds + persisted-mask helpers).
- HowToPlayPanel: tabbed reference card (Controls / The Loop / Build / Threats /
  Win-Lose), reachable from the main menu and the pause overlay.
- Per-client client-local state in GameSettings (TutorialHints + OnboardingMask
  bitmask, additive) — a Join client keeps its own; a host save-wipe never
  re-teaches. Settings toggle + menu "Replay Tutorial".
- Dev "Force Each Launch" toggle (GameSettings.ForceOnboardingEachLaunch):
  SettingsService.Boot wipes the mask + forces hints on in-memory every launch
  so the tutorial always replays fresh.
- HudSystem suppresses its own location hint while onboarding is active
  (single prompt voice), via OnboardingState + [UpdateAfter(OnboardingSystem)].

Validated green: 20/20 EditMode; Play smoke confirmed overlay render, clean
U+25B6 pointer glyph, no system sort-cycle, and the force-wipe end-to-end.

Docs: DR-043 + session log; reusable lesson archived in the build-gotchas note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Luis Gonzalez
2026-06-29 14:18:22 -07:00
parent 3bb9999173
commit 29e90a5008
20 changed files with 1058 additions and 9 deletions
@@ -0,0 +1,33 @@
---
title: First-Run Onboarding — design decision-tree + offline build (Unity GPU-crash session)
date: 2026-06-28
tags: [session, onboarding, ux, hud, client-only, presentation, dots-dev]
permalink: gamevault/07-sessions/2026/2026-06-28-first-run-onboarding
---
# First-run onboarding — session
`/dots-dev` session on the operator's brief: *"This game needs an onboarding style type of thing, plan something that makes sense."* Full decision: [[DR-043_First_Run_Onboarding]].
## How it went
1. **Ground** — 5-agent read-only fan-out (`wf_670a0cdf-832`) mapped the onboarding-relevant surfaces (HUD/UITK, controls, the macro loop, economy/build, frontend lifecycle) + an exhaustive search confirming **no onboarding/tutorial/help exists anywhere**. (2 of 5 mappers returned degenerate stubs; the 3 working ones triangulated the rest, so no re-run was needed.)
2. **Forks** — operator asked to drive it **decision-tree style**. Two rounds of `AskUserQuestion` (3 + 4 forks) locked the 7 design decisions (table in DR-043). A genre-precedent research pass (`wf_f41c8423-68b`, NN/g + CHI-2012 + DRG/Riftbreaker/CotL/Hades/Helldivers/Remnant) backed every recommendation; the operator chose the recommended option on all 7.
3. **Build** — mid-session the editor began **crashing randomly**. Diagnosed from `Editor.log`: empty managed stack + faulting `dxgi.dll`, GPU = **RTX 5060 Ti, driver 32.0.16.1062** → a **GPU/driver (TDR) fault, not project code** (recurring `Unity.exe.*.dmp`). Operator chose "peek at the crash, then code." Pivoted to building the whole feature **via the filesystem** (decoupled from the unstable bridge), with a single `refresh_unity force` deferred to when the editor is back.
4. **Static review in lieu of a compiler** — 3-lens adversarial review (`wf_d804925a-f7b`) verified every symbol against source: **Lens 1 compile/API = clean PASS**. Fixed 1 major (the v1→v2 migration dropped returning players' graphics settings) + 4 minors (Esc/pause copy + self-skip, pause-freeze for timed beats, static reset on teardown, same-frame HUD suppression ordering).
## What shipped (code-complete, NOT yet validated)
Contextual coach-marks (`OnboardingSystem` — client-only observe-only, own UIDocument @ sortingOrder 60) running the full first lap soft-gated, a world-space `▶` pointer, a tiny welcome strip naming the inverted win goal, a tabbed **How-to-Play** card (menu + pause), a Settings **Tutorial Hints** toggle + **Replay Tutorial**, all per-client via a client-local `GameSettings.OnboardingMask` (v1→v2 additive). Pure logic in `OnboardingStepMath` with `OnboardingStepTests`. **Zero netcode/replication/bake surface.** Files in DR-043.
## Validation — DONE (2026-06-29, editor stable)
**Green.** `refresh_unity force` → console clean → **20/20 EditMode** (incl. 2 new `ForceOnboardingEachLaunch` cases) → Play smoke proved the new dev toggle (seeded a veteran `int.MaxValue` mask + `force=1` → Boot wiped it → tutorial replayed from Welcome), confirmed **no system sort-cycle** from `HudSystem`'s `[UpdateAfter(OnboardingSystem)]`, and verified the `▶` pointer renders as a clean **U+25B6 triangle** (not tofu) + HUD hint suppression. Also shipped this session: a dev **Force Each Launch** onboarding toggle (`GameSettings.ForceOnboardingEachLaunch` + `SettingsService.Boot` per-launch wipe + a Settings cycle row). The deferred CLAUDE.md gotcha was parked in `_Meta/CLAUDE_Build_Gotchas_Archive.md` (file at cap; one-time pattern doesn't earn inline space). Full detail in [[DR-043_First_Run_Onboarding]].
### Original plan (for the record — now executed)
Not compiled/tested/Play-run yet. When the editor is stable: `refresh_unity scope=all mode=force` (the 5 new `.cs` have no `.meta`) → `read_console` clean → `run_tests` EditMode `ProjectM.Tests.EditMode` → Play smoke (welcome+step1 on a fresh client; dormant when hints off / mask full; per-client; no sort-cycle from the new `[UpdateAfter]`) → L3 screenshots (strip / pointer / card; confirm the `▶` glyph renders). Then add the deferred **CLAUDE.md** gotcha line (first-run flags → client-local `GameSettings`, not host `SaveData`) once green.
## Gotchas worth remembering
- **Unity random/idle crashes on this machine = the RTX 5060 Ti driver (`dxgi.dll`), not code.** Fix path: DDU + NVIDIA Studio driver / disable HAGS / `-force-d3d11`. (Native memory: `gpu-crash-dxgi-driver`.)
- **When the editor is unstable, write Assets `.cs` via the filesystem + one `refresh_unity force` later** instead of per-edit MCP `create_script` — the bridge dies with the editor; a static adversarial review can stand in for the compiler.
- **A version bump on an additive settings/save struct re-activates the migration path for every existing file** — migrate by carrying forward the old value and seeding only new fields, never by rebuilding from `Defaults()` (else you silently reset untouched fields). Caught by the post-impl review.
## Next-session intent
~~Get the editor stable (driver), run the verify ladder for DR-043~~ — **done 2026-06-29** (green). Remaining: the **operator fun-gate** — a real first-run playthrough to feel whether the welcome framing lands, the pointers read, and it stays un-naggy (and to confirm the Welcome→Move→Mine→Build→Fabricator→Gate→Clear→Return→Defend→Done cascade paces well with actual input). The dev **Force Each Launch** toggle (Settings → Onboarding) makes that repeatable.