feat(thressgame-coverage): Wave 8 (WS protocol v2 + suspended execution + request-choice)

- T43: WS protocol v2 schema; protocolVersion field; RequestChoice/SubmitChoice/ProtocolVersionMismatch messages; v1 backward-compat
- T44: server-side request-choice broadcast on push; submit-choice validation (kind/forPlayer/value-type); ordered LIFO matching
- T45: PendingChoices stack on GAME_ENTITY; pushPendingChoice/popPendingChoice/peekPendingChoice helpers; serializePendingChoice (Map<->Array roundtrip); MAX_CHOICE_DEPTH=8 enforced
- T46: submitChoiceAndResume(engine, choiceId, value); descriptor-by-id lookup; bindings restored; remaining primitives executed via runPrimitives from primitiveIndex+1
- T47: request-choice primitive; SuspendedExecution exception mechanism; dispatcher catches and stops sibling iteration; deterministic choiceId via session counter
- T48: AutoChoiceResolver test transport (answersByKind / answersById); drainPendingChoices LIFO walk
- T49: server-side choice timeout enforcement; auto-resolve to first-option-per-kind; disconnect handler (forfeit / pause)
- T50: ChoiceTimeoutPolicy on GAME_ENTITY (timeout-with-default | no-timeout); CreateGameRequest extended; default 60s

Tests: 2533 -> 2658 (+125). bun run check exit 0.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-26 11:54:24 -06:00
commit d4931a50ee
No known key found for this signature in database
37 changed files with 6841 additions and 295 deletions

View file

@ -671,3 +671,46 @@ Added BlockList wrapping BlockCard with dnd-kit for sorting. Added tests verifyi
2. Depth-4 invalid (conditional -> on-move -> conditional -> add-to-attribute) triggering `descriptor.primitives.depth.exceeded`
3. Mixed old/new kinds at depth 3 (on-captured -> conditional -> add-aura) valid
- `bun test packages/chess/src/modifiers/custom/validate.test.ts` passes with 16 test cases.
---
## Path-based selection refactor + nested-editing + add-child button — 2026-04-21
### Problem
Nested BlockCards couldn't be selected/edited. BlockList recursion hardcoded `selectedIndex={null}` + `onSelect={() => {}}` (no-ops) because selection state was flat `number | null`. Also no visible affordance existed to add primitives inside an expanded trigger (user had to know "click parent → palette banner appears → click palette item").
### Solution
- Replaced `selectedIndex: number | null` with `SelectionPath = readonly number[]` throughout VisualBuilderPane / BlockList / BlockCard.
- `[]` = no selection; `[0]` = top-level 0; `[0, 2]` = child 2 of top-level 0 (via `params.primitives`). Arbitrary depth supported.
- Replaced `expandedIndices: Set<number>` with `expandedPaths: Set<string>` keyed by `path.join('.')` (avoids deep-set-equality ceremony).
- 5 new pure path walkers in VisualBuilderPane.tsx: `getNodeAtPath`, `updateAtPath`, `removeAtPath`, `appendChildAtPath`, `reorderAtPath` + `pathStartsWith` helper.
- Deleted redundant `handleNestedReorder` / `handleNestedRemove` — consolidated into path-based versions.
- New BlockCard prop `onAddChildClick?: () => void` renders a dashed-violet "+ Add primitive inside" button at the bottom of the nested container. BlockList wires it to `() => onSelect(thisPath)` for container primitives only (checks `primitive?.childPrimitives !== undefined`).
- Nested-container now renders even when children-list is empty — so empty triggers STILL show the add button.
- Nested DnD id collision guard: `nodeIds` include `basePath.join('.')` so nested SortableContexts don't share IDs.
- `handleSelect` auto-expands container primitives so the add-child button appears immediately.
### Conditional `then`/`else` — documented as out of scope
Path walker only traverses `params.primitives`. `conditional`'s separate `then`/`else` arrays are NOT selectable/editable in visual mode — same status as before this refactor. Comment in VisualBuilderPane.tsx:85-94.
### Edge cases handled
- Remove subtree containing selection → `pathStartsWith` clears selection
- Remove cleans expandedPaths via key-prefix match
- Reorder adjusts selection index if it pointed into the reordered list
- "Add at top level instead" button → `setSelectedPath([])`
### Verification
- 27/27 visual-builder tests pass (up from 22, +5 new tests covering nested selection + add-child button)
- `bun run check` → 166 files / 1957 tests pass
- 0 lsp_diagnostics errors in modified files
- No `as any`, no `@ts-ignore` introduced
### Files touched
- packages/chess/src/ui/visual-builder/VisualBuilderPane.tsx (+245 / -126 lines)
- packages/chess/src/ui/visual-builder/BlockList.tsx (+86 / -49)
- packages/chess/src/ui/visual-builder/BlockCard.tsx (+25 / -4)
- packages/chess/src/ui/visual-builder/{BlockCard,BlockList,VisualBuilderPane}.test.tsx (updated for new prop shapes + new tests)
### Pre-existing noise confirmed NOT caused by this work
- `ParamField.snapshot.test.tsx` has 15 obsolete snapshots (T14 legacy) — untouched ParamField.tsx per user request
- `CustomModifierEditor.mode-roundtrip.test.tsx` fails under `bun test` direct but passes under `bun run check` (vitest environment) — pre-existing localStorage mocking limitation documented at learnings.md:658