feat(thressgame-coverage): Wave 3 (binding scope + param walker + validator extensions)

- T11: PrimitiveApplyContext.bindings (immutable Map<string,BindingValue>); withBinding helper; threaded through 22 test files + triggers.ts/apply.ts
- T12: param-resolver.ts walker resolves { $var }, { ctx-attr: { entity, attr } }, { ctx-build: { col, row } } shapes; wired before primitive.apply in triggers.ts + custom/apply.ts; BindingError class
- T13: validator binding-out-of-scope check (descriptor.primitives.binding-out-of-scope); BINDING_INTRODUCING_KINDS map (8 future kinds); cycle-guarded $var walker
- T14: validator imperative-in-passive check (descriptor.primitives.imperative-in-passive, 10 IMPERATIVE_KINDS); LastModifierChooser tracking on PRESET_STATE_ENTITY (chooser-entity stub)

Tests: 2014 -> 2048 (+34). bun run check exit 0.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-26 09:10:21 -06:00
commit defe56feb9
No known key found for this signature in database
36 changed files with 1865 additions and 11 deletions

View file

@ -775,7 +775,7 @@ Max Concurrent: 8 (Waves 5+6+7+9 overlap)
- Message: `feat(chess): marker entity factory + priority resolver`
- Files: `packages/chess/src/engine.ts`, `packages/chess/src/engine.test.ts`
- [ ] 11. Binding scope stack on PrimitiveApplyContext
- [x] 11. Binding scope stack on PrimitiveApplyContext
**What to do**:
- Edit `packages/chess/src/modifiers/primitives/context.ts`: extend `PrimitiveApplyContext` with `bindings: ReadonlyMap<string, EntityId | readonly EntityId[] | Square | number | string>`
@ -817,7 +817,7 @@ Max Concurrent: 8 (Waves 5+6+7+9 overlap)
- Message: `feat(chess): binding scope on PrimitiveApplyContext`
- Files: `packages/chess/src/modifiers/primitives/context.ts`, `context.test.ts`
- [ ] 12. Param walker resolves {$var}, {ctx-attr}, {ctx-build} shapes
- [x] 12. Param walker resolves {$var}, {ctx-attr}, {ctx-build} shapes
**What to do**:
- Create `packages/chess/src/modifiers/primitives/param-resolver.ts` exporting `resolveParams(params: unknown, ctx: PrimitiveApplyContext): unknown` — recursively walks params, substituting:
@ -861,7 +861,7 @@ Max Concurrent: 8 (Waves 5+6+7+9 overlap)
- Message: `feat(chess): param walker for binding/ctx-attr/ctx-build resolution`
- Files: `packages/chess/src/modifiers/primitives/param-resolver.{ts,test.ts}`, `packages/chess/src/modifiers/triggers.ts`
- [ ] 13. Validator: binding-ref-out-of-scope error
- [x] 13. Validator: binding-ref-out-of-scope error
**What to do**:
- Edit `packages/chess/src/modifiers/custom/validate.ts`: add a binding-scope walker that builds a binding-name set per primitive subtree and rejects any `{ $var: "X" }` reference where `X` not in scope
@ -893,7 +893,7 @@ Max Concurrent: 8 (Waves 5+6+7+9 overlap)
- Message: `feat(validator): binding-scope check`
- Files: `validate.ts`, `validate.test.ts`
- [ ] 14. Validator: imperative-in-passive + chooser-entity activation context
- [x] 14. Validator: imperative-in-passive + chooser-entity activation context
**What to do**:
- Edit validate.ts: walk descriptor tree; if a primitive whose `kind` is in IMPERATIVE_KINDS set (place-piece, destroy-piece, move-piece, swap-pieces, convert-piece-type, set-piece-attr, cancel-capture, spawn-marker, spawn-marker-pair, destroy-marker) appears OUTSIDE a trigger's `primitives` or `then`/`else` array → reject with error code `descriptor.primitives.imperative-in-passive`