UX Spec: Build Mode — Picker, Ghost, Placement & Demolish
Status: Reviewed — APPROVED (
/ux-reviewre-pass 2026-08-31: 0 blocking, 3 advisory — applied) · v0.3
Author: Jeroen + ux-designer
Last Updated: 2026-08-31
Journey Phase(s): unknown — no player journey map yet (see Open Questions)
Template: UX Spec
Variants: Build Mode (client-local mode: picker + ghost + reach grid; Player map stays live) · Demolish (an instant targeted action from Idle/Move — never a mode)
Requirements input: base-building Rules 3–9, 11a/11b, 12, 13; the BB States & Transitions table; BB UI Requirements (picker, ghost + per-gate reason strings with precedence + hysteresis, grid within reach only, demolish prompt + refund preview, no build log); BB Edge Cases 11 (race no-op) and 21 (displaced-player one-cell correction); the two/ux-design-owned deliverables — repeat-placement cadence (Rule 11b) and snap-nudge tolerance (OQ6 residual); ADR-0007 §1 Build map + rules (a)/(f)/(g) + §3BuildModeRouting; art bible §7; ui-hud quick-spec Rule 4 (BB row) + queue item ③
Patterns used: #2 List Picker · #6 Reason String · #7 Honest No-Op Cue · #8 Ghost Outline & Reach Grid · #9 Demolish Target Select · #19 Feedback Split · #24 Interact Prompt (suppressed in mode) · #31 Repeat Placement (lifted from this spec)
Related specs:design/ux/hud.md(APPROVED — HUD fully visible in build mode; E11 suppressed),design/ux/inventory.md(storage placement instantiates a chest),design/ux/crafting.md(stationRangedraw radius shared)
Purpose & Player Need
Build mode is where the group's home stops being a campsite. The player enters it wanting to put a wall here — and, most sessions, put fifty walls along this line — with placement feeling deliberate (snap-with-nudge, a thunk per wall) but never fiddly (persistent mode, hold-to-repeat, no confirmation dialogs, no timers). It must be honest about refusals (one world-fact reason per ghost, first failing gate, held stable), and it must resist becoming a build tool: no rotation, no move verb, no blueprints, no build log, no per-player credit (Rules 7, 9, 11a). Demolish is the same design in reverse — instant, total refund, "empty the chest first" — a repair verb for collective authorship, never a punishment.
Player Context on Arrival
- First encounter: home after the first haul, timber in the bag, curiosity — "can I build?" Usually the first friend to try teaches the others by doing.
- Typical: (a) a calm perimeter session at home — the multi-wall case the GDD names as the common one (Rule 5); (b) placing a workbench or chest once; (c) fixing a friend's misplaced wall (demolish + re-place — the only relocation path, Rule 9). Never on a docked land: structures are home-only (Rule 3).
- Emotional assumption: calm — home is hostile-free at MVP; the tension is material ("do we have the timber?"), read live from the picker's affordability.
- Voluntary: always.
BuildToggleis the only entry.
Navigation Position
Gameplay → Build Mode — a client-local mode, not a panel: no UI capture; the Player map stays live for movement and camera, the Build map is enabled alongside (ADR-0007 §3 BuildModeRouting). Top-level from gameplay via BuildToggle (B / D-pad Up), anywhere; entering on a docked land is legal — no GDD rule gates mode entry by location; opening the mode off-island is this spec's ruling (the mode is client-local and costs nothing) — and every cell fails withinBounds (BB AC-4), so the ghost is dashed everywhere with the bounds gate's off-island string "not home ground". Demolish is not in the navigation tree — an instant verb from Idle/Move (Demolish: RMB outside build mode / D-pad Down). Build mode and any panel are mutually exclusive: opening a panel exits the mode (the Inventory edge exits first, then opens the bag); a panel cannot open inside it.
Entry & Exit Points
| Entry Source | Trigger | Player carries this context |
|---|---|---|
| Gameplay (Idle/Move, alive, not captured) | BuildToggle press (B / D-pad Up) | last-selected structure (session-sticky; default: wall); CARRY + in-range STORAGE for affordability; aim / pointer for the ghost |
| Gameplay (Idle/Move) | Demolish press (RMB outside build mode / D-pad Down) — not a mode | nearest structure per #9's selector; refund preview |
| Exit Destination | Trigger | Notes |
|---|---|---|
| Idle/Move | BuildToggle again, or Build.Cancel (RMB / Esc on KB/M; East on gamepad) | ghost vanishes; nothing committed |
| Idle/Move + the action applied | any non-Build Player action — Strike (RT on gamepad; LMB is Confirm on KB/M), Dash, Interact, Revive, Demolish (D-pad Down; RMB is Cancel on KB/M), Inventory | exits first, then the action applies in the same frame (ADR-0007 BuildModeRouting) |
| Pause | Menu (Esc / Start) | exits build mode then opens pause (ADR-0007 rule (g)) — the player never resumes into a live ghost |
| Idle/Move | becomes Downed / Dead | mode drops; ghost vanishes; nothing committed |
| Idle/Move | tide deposit / host quit | same |
| — | Move, camera, aim | never exit (Rule 5) |
Persistence: the mode survives every commit (multi-wall sessions), every refused commit (ghost stays, reason shows), and every race loss (EC11). No one-way exits; every commit is its own host transaction that has already resolved by the time anything changes on screen.
Layout Specification
Information Hierarchy
- The ghost — where the structure will land and whether it is valid (solid vs dashed outline, zero fill — #8)
- The placement reason when invalid — the first failing F1 gate, held stable (#6)
- The picker — which structure is selected + its cost with live ghost-centred affordability
- The reach grid — the placeable radius (
placementReach6 u), build mode only - (Demolish) the target highlight + refund preview (#9)
Deliberately absent: rotation control (Rule 7), move verb (Rule 9), blueprints / queues (Rule 11a), build log or per-player credit (Rule 7 / UI ban), any placement timer (Rule 6).
Layout Zones
- W1 — Ghost (world): pattern #8 — outline-only at the snapped cell (1 u grid, ADR-0003) with nudge 0.35 u to meet an adjacent footprint edge (OQ6 residual, ruled 2026-08-31); solid = valid, dashed = invalid; footprint-sized; follows
PointerWorld(KB/M) or cell-steps along the aim vector (gamepad, #8). Aim semantics (ruled 2026-08-31): the gamepad ghost consumer readsInputFrame.Aim— the same deadzoned right-stick vector camera and combat read (ADR-0007AimStick) — and quantises it to a dominant-axis cell step with key-repeat;Aimkeeps updating normally, so camera aim-bias leans toward where you are placing. Intended; noAimResolverbranch for build mode. - W2 — Placement reason (world): #6, anchored 16 px above the ghost while invalid — the first failing F1 gate in order (reach → bounds → overlap → occupancy → exit → terrain → socket → materials); 250 ms minimum / 2-frame switch hysteresis.
- W3 — Reach grid (world): #8 — cell lines within
placementReach(6 u) fading at the edge; build mode only, never full-canvas. - W4 — Demolish target (world; Idle/Move only): #9 — highlight outline on the targeted structure + refund preview (icon + count per material at current
StructureDefcost) above it, or "empty the chest first" (#6) for a non-empty chest. - S1 — Picker strip (screen): bottom-centre, above the HUD's bottom band (Z1 / Z2 / Z4 untouched),
n × 180 px(n = loaded structure count; 3 at MVP → 540 px) @100 %, centred: the entries side by side — icon (tier-shaped, §7.4) · name · cost with ✓ / — per material, affordability ghost-centred (CARRY ∪ STORAGE withinstationRangeof the ghost, the F1 predicate). Selected entry outlined and raised 2 px. No scrolling at MVP; Tier 1 wraps to a second row before it ever scrolls. - S2 — Strip footer (screen): the selected entry's affordability reason when unaffordable ("not enough fiber in reach") — distinct from W2, which is the placement reason for the ghost's current cell. Ownership rule (ruled 2026-08-31): S2 is suppressed whenever W2 is showing any string — the ghost's blocker is the live question; the picker's affordability is meaningful only when the cell is otherwise placeable. The two never show at once and never duplicate.
n = 0is impossible (ADR-0006 rejects an emptyStructurestable at load), so no empty picker state is designed. - HUD: fully visible (
hud.md); E11 interact prompt suppressed; the centre 60 % box is untouched by S1 / S2.
All px at 100 % UI scale.
Component Inventory
| Zone | Component | Type | Content | Interactive | Pattern |
|---|---|---|---|---|---|
| W1 | Ghost | world outline | footprint; solid / dashed | follows input | #8 |
| W2 | Placement reason | world text | first failing gate ("someone is standing there") | No | #6 |
| W3 | Reach grid | world overlay | cells within 6 u | No | #8 |
| W4 | Demolish highlight + refund preview | world outline + text | "+4 timber" / "empty the chest first" | No | #9, #6 |
| S1 | Picker entries ×3 | horizontal picker | icon · name · cost ✓ / — | Yes (PickPrev / PickNext, click) | #2 variant (horizontal, in-mode — not a captured panel) |
| S2 | Affordability reason | text | "not enough fiber in reach" | No | #6 |
| — | Hold-to-repeat | gesture | Confirm held → one atomic commit per cell along the last ghost-move direction | Yes | #31 Repeat Placement (Rule 11b's deliverable; lifted 2026-08-31) |
ASCII Wireframe
BUILD MODE (home, wall selected, ghost invalid)
┌──────────────────────────────────────────────────────────────┐
│ ◉ Jo ████░ │
│ │
│ someone is standing there ← W2 (16 px above)│
│ ┆‾‾‾‾┆ ← W1 dashed ghost (1×1 wall) │
│ ▪▪▪▪▪▪▪▪ └┄┄┄┄┘ ▪▪▪▪ │
│ · · · · · · · · · · · · ← W3 grid, 6 u radius │
│ · · · · ☺ · · · · · · (fades at the edge) │
│ · · · · · · · · · · · · │
│ │
│ ┏━━━━━━━━━━━━━━┓┌──────────────┐┌──────────────┐ ← S1 │
│ ┃▣ Wall 4 timb✓┃│▤ Bench 8t 4f—││▢ Chest 6t ✓ │ │
│ ┗━━━━━━━━━━━━━━┛└──────────────┘└──────────────┘ │
│ ♥♥♥♥♡ ◔ [⚔][🛡] (S2 empty — wall affordable) 🎒 7/10 │
└──────────────────────────────────────────────────────────────┘
Q / D-pad ← : previous · scroll / D-pad → : next · LMB / South: place (hold to repeat) · RMB / Esc / East: exit
DEMOLISH (Idle/Move, RMB / D-pad Down targeting a wall)
┌ +4 timber ┐ ← W4 refund preview
┃ ▣ wall ┃ ← solid highlight outline
└───────────┘
(a non-empty chest instead shows: "empty the chest first")
Costs shown are illustrative; live values come from StructureDef (wallCost 4 timber at MVP).
States & Variants
| State / Variant | Trigger | What Changes |
|---|---|---|
| Build mode, valid | ghost passes all 8 F1 gates | solid ghost; W2 empty; S2 empty; selected entry ✓ |
| Build mode, invalid — one row per F1 gate, in order | first failing gate | dashed ghost + W2: reach "too far away" · bounds "past the island's edge" (off-island: "not home ground") · overlap "blocked" · occupancy "someone is standing there" · exit "no way out" · terrain "not buildable here" · socket "too close to the dock" · materials "not enough timber in reach". Six are BB's UI Requirements strings verbatim; the bounds and terrain strings are new instances authored here under the GDD's /ux-design retitling right (BB EC23 / CS-17) — owed back to BB's list (OQ5). Precedence = F1 order |
| Unaffordable entry selected | — on the selected entry | S2 shows "not enough fiber in reach" only while W2 is empty; if the ghost's cell fails any gate (incl. materials), W2 shows and S2 is suppressed |
| Off home ground | mode entered on a docked land / at sea | every cell dashed; W2 "not home ground" (the bounds gate — BB AC-4 withinBounds = false); picker live; nothing commits |
| Commit succeeds | host confirm | structure appears with thunk + squash / settle; cost debited (bag flash); ghost stays; mode persists |
| Commit refused at host (EC11) | a gate failed host-side although the ghost was solid (stale positions / someone took the timber) | honest no-op: nothing appears, nothing debited; the ghost re-evaluates next frame and shows why; no dialog, no sound |
| Race lost (two ghosts, one footprint) | the other client's commit landed first | their wall appears; your ghost turns dashed "blocked"; nothing debited |
| Hold-to-repeat running | Confirm held after a successful commit | after each confirm the ghost steps one cell in the last ghost-move direction (KB/M: the direction the pointer last crossed a cell boundary; gamepad: the right stick's dominant axis) and commits again; ≤ 1 commit in flight (Rule 11a) — the next fires only after the previous confirms |
| Hold-to-repeat refused | a cell in the row fails any gate | stops on that cell (ruled 2026-08-31); ghost stays dashed with its reason; release and re-aim to continue — never skips a cell. Self-sealing (BB EC23): a row toward yourself stops on the sealing cell with "no way out" — no lookahead affordance at MVP, stated as a non-design |
| Hold-to-repeat, out of materials | materials gate fails | same stop rule; S2 shows the shortfall |
| Storage placed | chest commit confirmed | chest appears; SharedStorage instantiated (BB Rule 12) — nothing else changes in-mode |
| Demolish targeting | Demolish held with a candidate in range | W4 highlight + refund preview while the button is down; the action resolves on release (#9). "Instant" (BB Rule 9) describes the commit — no timer, no dialog — not the gesture; the gesture is press → preview → release, resolving in one frame on the release edge |
| Demolish refused | non-empty chest / refund won't fit CARRY | "empty the chest first" / "no room for the refund" (#6) — no-op |
| Demolish race | structure already gone | nothing happens (#7); no cue |
| Panel / pause / Downed / Dead | any | mode exits (Entry & Exit table) |
| Loading | — | none: registry, defs and knobs are already-loaded / replicated state |
Interaction Map
Mapping for KB/M (primary) and gamepad (partial); Build map per ADR-0007 §1, routing per §3 BuildModeRouting. This is the one screen where the two schemes deliberately differ (ADR-0007 rule (a)): on KB/M LMB / RMB are Confirm / Cancel inside the mode; on gamepad RT still strikes (and exits) while South places.
| Component | Action | KB/M | Gamepad | Feedback | Outcome |
|---|---|---|---|---|---|
| Mode | Enter / exit | B (BuildToggle) | D-pad Up | brief non-musical cue; grid + ghost appear / vanish; picker strip slides 100–150 ms | client-local |
| Ghost | Move | mouse (PointerWorld) | right stick cell-steps the ghost along its dominant axis (key-repeat 300 / 100 ms, #8); the left stick keeps moving the player | snap + 0.35 u nudge; solid / dashed per frame | — |
| Picker | Previous / next | Q / scroll wheel (PickPrev / PickNext) | D-pad Left / Right | selected outline moves; ghost footprint swaps; no tick sound | client-local; session-sticky (a UX ruling with no GDD basis: the selection survives mode toggles, death and tide deposits; resets to wall on host quit / new session; never saved or replicated) |
| Picker entry | Select by click | click on the entry | — | same | — |
| Ghost | Place | LMB (Build.Confirm) | South | predicted: nothing; host-confirmed: thunk + squash / settle + structure + bag flash | PlaceStructure command |
| Ghost | Hold-to-repeat (Repeat Placement) | hold LMB | hold South | one thunk per cell; the ghost advances after each confirm | one PlaceStructure per cell, serialised (≤ 1 in flight) |
| Mode | Cancel | RMB / Esc (Build.Cancel) | East | ghost vanishes; exit cue | exit |
| Mode | Exit via action | any non-Build Player action | RT (Strike), etc. | exits first; the action applies the same frame | ADR-0007 routing |
| Demolish (Idle / Move) | Target + demolish | RMB down = preview on the cursor-raycast target; release = demolish | D-pad Down held = preview on the nearest-in-cone target; release = demolish | highlight + refund preview while down; host-confirmed removal + sound + refund flash | Demolish command |
| Demolish pressed inside build mode | — | RMB is Build.Cancel (ADR-0007 rule (a)) — exits, no demolish | D-pad Down Pressed exits build mode (§3 exit set); the subsequent Released demolishes only if a candidate is under the reticle at release — ordinarily nothing (ADR-0007 amendment: Demolish is consumed on Released, Pressed opens the preview) | exit cue | — |
Repeat Placement — the gesture defined: Confirm.Held after a confirmed commit → when the confirm lands, the ghost advances one cell in the last ghost-move direction and, if the new cell is valid, commits again; direction resets when the player moves the ghost manually; a refused cell stops the row on that cell; release ends the row. Read as ButtonState.Held — not an asset Hold interaction (ADR-0007 rule (d)); its accessibility alternative is single presses (the same action with no hold).
Focus: the mode has no UI focus — nothing captures; the UI map is disabled in-mode; the picker is driven by PickPrev / PickNext and pointer clicks, never UI.Navigate. Exactly one selected picker entry at all times.
Events Fired
| Player Action | Event | Payload | Persistent? |
|---|---|---|---|
| Place (each cell, incl. every repeat step) | PlaceStructure command (ADR-0002 CommandKind.PlaceStructure) | structureId, origin Cell — intent-time data only (ADR-0003 §4 worked command) | Yes — registry row + cost debit + structure instanceId counter (ADR-0004) |
| Demolish | Demolish command (CommandKind.Demolish) | instanceId | Yes — registry removal + refund to CARRY |
| Enter / exit mode, ghost move, pick | none — client-local (Rule 5: zero replicated state until commit) | — | no |
| Refused commit / race | none from this client | — | the host's refusal is the event |
No analytics events exist in this project.
Transitions & Animations
- Enter: grid fades in 100 ms; picker strip slides up 100–150 ms ease-out (reduced-motion: appear in place). Exit: reverse; input released on the exit edge.
- Ghost: no animation — it moves per frame with input; solid ↔ dashed swaps instantly (the #6 hysteresis governs only the string, never the outline — the outline must be truthful every frame).
- Commit: the placed structure lands 1 px high and eases down — one impact-only squash-and-stretch beat (BB Visual Requirements) + the host-confirmed thunk. Kept under reduced motion — it is on
accessibility-requirements.md's deliberately-kept list (placement squash / settle), and it carries the "placement is an event" tone (corrected 2026-08-31). - Repeat: identical per cell; no cumulative flourish (11b's tone re-validation: one thunk per wall keeps the "event").
- Demolish: removal instant + understated sound; refund = bag flash (#17).
- Displaced player (EC21): one settle beat on the moved sprite, no sound.
- Nothing gates input (art bible §7.5).
Data Requirements
| Data | Source System | Read / Write | Notes |
|---|---|---|---|
StructureDef table (3 at MVP: wall, workbench, chest — id, category, footprint, cost, stationTier?, storageSlots?) | GameData Structures (ADR-0006 stage 6) | Read | Static after boot; picker order = data order |
| Ghost validity + reason | Rules.Build.PlacementLegality — client-provisional evaluation over an ITerrainQuery snapshot, the replicated IStructureRegistry copy, replicated player positions, ISocketLayout | Read | Provisional; the host re-samples all 8 gates at commit (Rule 6); a disagreement = EC11 honest no-op |
| Snap + nudge | Rules.Build.ghostSnapResult (pure; 1 u cells; nudge 0.35 u) | Read | Returns a Cell — byte-identical on both rails (ADR-0003 §2) |
| Affordability per entry (ghost-centred) | Inventory — own CARRY ∪ ISharedStorage within stationRange of the ghost | Read | Storage side one RTT stale (stated) |
placementReach, stationRange, socketNoBuildRadius | GameData Configs.Build / Configs.Craft | Read | Grid radius, draw radius, socket exclusion — never hardcoded |
| Place / demolish | host-channel commands | Write via command only | The UI owns nothing; zero replicated ghost state (Rule 5) |
| Demolish candidates + refund preview | replicated IStructureRegistry + StructureDef.cost (current — no cost snapshot, EC14) | Read | Chest emptiness from ISharedStorage.ContentCount (host-confirmed copy) |
| Last-selected structure | client-local session memory | local | Not saved, not replicated |
| EC21 correction | Rules.Build.FindCorrectionCell on the displaced client (owner rail) | — | Not a build-mode UI concern; listed for completeness |
Architectural flag: the ghost is pure prediction — this spec depends on provisional and authoritative F1 disagreeing gracefully (EC11), never on them agreeing.
Accessibility
Tier: Standard.
- No UI focus in-mode (nothing captures). Operability = the Build map: KB/M fully via B / Q / scroll / LMB / RMB / Esc; gamepad fully via D-pad Up / Left / Right / South / East + right stick — nothing hover-only; the picker is click-or-cycle, never click-only.
- Gamepad parity: outcome parity via
ghostSnapResult(BB AC-8); effort differs by design (cell-step vs pointer) — stated. The right-stick ghost never requires simultaneous left-stick input. - Holds: hold-to-repeat is the project's third held input (after Revive and the title-screen reset) — read as
ButtonState.Held, not an assetHold; the built-in alternative is single presses per cell (identical outcome, no timing). Demolish's press → preview → release is an ordinary click, not a hold (no duration requirement).accessibility-requirements.mdhold inventory updated accordingly. - Colour-independence: valid / invalid = solid vs dashed (never hue); affordability = ✓ / — glyph + the reason string; selected entry = outline weight + 2 px raise; refund preview = text.
- Text: reasons Small 13–14 px with the ink outline (world) / on slate (S2), ≥ 4.5:1; picker names Medium 16–18 px.
- Reduced motion: strip slide → appear; grid fade → appear; the ghost never animates; the placement squash / settle is kept (accessibility keep-list).
- Flash: none introduced. Screen reader: out of scope (documented). Scale: the 540 px strip at 150 % = 810 px, centred → x 555–1365 on 1920; clears Z1 + Z2 (right edge ≈ 468 px at 150 %) and Z4 (left edge ≈ 1682 px), and sits below the centre box's lower edge (y 864), so it never intersects it; world text scales with the UI-scale option.
Localization Considerations
| Element | English max | Layout-critical | Priority |
|---|---|---|---|
| Placement reasons (8 strings; longest "someone is standing there") | ≤ 28 chars | one line above the ghost (world) | HIGH |
| Affordability reason ("not enough fiber in reach") | ≤ 32 chars | one line, S2 | HIGH |
| Picker entry — name line ("Wall") + cost line ("4 timber" / abbreviated "8t 4f" when two materials) | name ≤ 12 chars English; cost ≤ 10 chars | two lines beside a 40 px icon in a 180 px entry (≈ 17 chars/line at Medium) | HIGH |
| Refund preview ("+4 timber") | ≤ 14 chars | one line | Medium |
| "empty the chest first" / "no room for the refund" / "not home ground" | ≤ 24 chars | one line | HIGH |
There is no on-screen input-hint bar at MVP (the wireframe's footer line is documentation). HIGH rows must survive 40 % expansion on one line.
Acceptance Criteria
- [ ] B / D-pad Up enters build mode within 1 frame: grid (6 u radius), ghost at the snapped cell, and the 3-entry strip appear; pressing again exits with nothing committed.
- [ ] Moving the pointer across cell boundaries moves the ghost in 1 u steps; within 0.35 u of an existing wall's edge the ghost aligns flush to it; two walls placed this way share an edge with no gap (screenshot).
- [ ] With a friend standing on the target cell the ghost is dashed and reads "someone is standing there"; when both occupancy and materials fail, the occupancy string shows (precedence); the string holds ≥ 250 ms and never flickers at a cell boundary while the outline updates every frame.
- [ ] LMB / South on a valid ghost: nothing changes locally until the host confirms (~1 RTT), then the wall lands with one squash / settle and thunk, 4 timber leaves the bag (flash), the ghost remains and the mode persists.
- [ ] Holding LMB along a clear line places one wall per confirm in the direction the ghost last moved, ≥ 1 RTT apart, one thunk each; a friend stepping into the line stops the row on that cell (dashed + reason) with no wall skipped; releasing stops immediately; pressing once per cell produces the identical row.
- [ ] Two clients confirm the same cell within one tick: exactly one wall exists; the loser's ghost turns dashed "blocked" and their bag is unchanged.
- [ ] Selecting Bench with insufficient fiber shows "—" on its entry and "not enough fiber in reach" in S2 while the ghost's cell is otherwise valid; when the ghost's cell also fails a gate, W2 shows and S2 is empty (never both); adding fiber to a chest within 3 u of the ghost flips the entry to ✓ without leaving the mode.
- [ ] Pressing Strike (RT) on gamepad exits build mode and swings in the same frame; LMB on KB/M places instead (no swing); RMB inside build mode exits without demolishing anything, even with a wall under the cursor; Esc exits then opens pause, never leaving a live ghost.
- [ ] RMB down on a wall (KB/M) / D-pad Down held facing it (gamepad) shows a highlight and "+4 timber"; release removes it with a sound and 4 timber lands in the bag; a non-empty chest shows "empty the chest first" and nothing happens on release.
- [ ] Entering build mode on a docked land shows every cell dashed with "not home ground"; no commit is possible.
- [ ] Gamepad: moving the right stick in build mode steps the ghost one cell per repeat and the camera's aim bias follows the same direction; D-pad Down inside build mode exits the mode and demolishes nothing unless a structure is under the reticle at release.
- [ ] Reduced-motion on: strip and grid appear without motion; walls still land with the settle beat (deliberately kept); outcome identical. At 150 % UI scale the strip does not overlap the HUD's bottom clusters.
- [ ] No rotation control, move verb, build log or per-player credit exists (reflection over the Build map + screenshot; BB AC-33's no-move-verb asset test).
Open Questions
- Player journey map not yet created (
.claude/docs/templates/player-journey.md) — contexts assumed; validate at the playtest. - Rule 11b tone re-validation — hold-to-repeat keeps one thunk per wall; playtest whether a 50-wall run still reads as "placement is an event" or as a paint tool. Fallback: a 250 ms floor between repeat commits.
- ~~Pattern lifts~~ Done 2026-08-31: #31 Repeat Placement in
interaction-patterns.md; #9 amended to "preview while held, action on release"; a11y hold inventory → three. - Repeat direction on KB/M ("last cell-boundary crossing") — if the playtest shows surprising rows on diagonal pointer paths, switch to "toward the pointer" per step.
- Three new reason-string instances — bounds "past the island's edge" (retitled from a system-fact phrasing), terrain "not buildable here", off-island bounds "not home ground" — authored under BB's
/ux-designretitling right; owed back to BB's UI Requirements list (note appended to BB's OQ table 2026-08-31). - Tier 1: strip wrapping at > 6 entries; a Forge category;
socketNoBuildRadiusre-check (BB OQ2 — LDU-owned). - EC21 settle beat when the displaced player is in build mode — their ghost re-evaluates; confirm no double beat with the wall's own thunk (BB: the wall's thunk marks the event).
- BB Rule 11b prose (its "AC-48c's playtest must include a drag-row segment" clause and the drag-row reasoning around EC23) still assumes a drag-row; drag-a-row was rejected here in favour of hold-to-repeat (#31, a legal 11b choice) — BB should retarget those clauses to "a held-repeat row" (AC-48c's own current text carries no row segment). Flagged via BB OQ 12(b).
- ADR-0007 amendment:
Demolishconsumed onReleased(Pressed= preview); rule (d) reads "exactly one asset-level hold" — threeHeldreads exist at the design layer (Revive, title reset, #31). Appended to ADR-0007's Amendments 2026-08-31.