clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

main
miau-is-true / openspec / changes / archive / 2026-09-24-fix-same-entity-win / design.md
design.md 3.4K source

Context

The engine resolves transformations and contact effects in src/engine.js. Victory currently requires two distinct occupants in one cell, so a noun governed by both YOU and WIN does not complete a level even though the rule grammar permits that combination. Proof 03 exposes this defect through MIAU IS YOU AND WIN. The existing stage already consumes the settled won field, so the fix belongs at engine contact resolution and must preserve event ordering.

Goals / Non-Goals

Goals:

  • Evaluate victory after transformations and after SINK/DEFEAT removals.
  • Mark an entity won when it itself has both YOU and WIN, as well as preserving separate-entity YOU + WIN contact.
  • Keep SINK and DEFEAT destructive precedence deterministic and observable in settled events.
  • Verify engine, undo/reset, proof 03, and browser win-card behavior.

Non-Goals:

  • Adding properties or changing sentence parsing, movement, push order, or pack formats.
  • Making a destroyed entity win in the same turn.
  • Changing the stage's win-card layout or animation timing.

Decisions

1. Check victory on surviving settled occupants

After contact destruction has been applied, inspect each remaining occupant. Set state.won when any surviving object has YOU and WIN, or when a surviving YOU shares a cell with a distinct surviving WIN object. This keeps same-entity and separate-entity victory in one settled phase. Checking only the distinct-object case is the current defect; checking before destruction would let SINK or DEFEAT win incorrectly.

2. Preserve destruction precedence

Keep the existing order: SINK removes all occupants in a contact cell, then DEFEAT removes controlled survivors, then WIN evaluates the remaining occupants. A same-entity YOU + WIN object removed by either destructive property cannot set won. No new event fields are needed; the existing destroyed and won values make the result observable.

3. Test at engine and browser boundaries

Add focused engine fixtures for same-entity victory, separate-entity victory, sink/defeat precedence, and undo/reset preservation. Extend the browser acceptance fixture or proof 03 flow so the rule is visible, the winning move returns a settled won: true state, and the existing Win scene appears. Use the existing stage contract rather than adding a special-case UI path.

Risks / Trade-offs

  • [A broad contact rewrite could change existing SINK/DEFEAT behavior] → Keep the current destruction loops and change only the final surviving-occupant victory predicate; run the full suite.
  • [A transformed object may be checked with stale properties] → Refresh rules after transformation before contact resolution, as the engine already does.
  • [Browser acceptance could pass from a separate WIN object and miss the regression] → Use the authored proof 03 layout where the same MIAU entity receives both properties.

Migration Plan

  1. Add the focused rule-engine delta and regression fixtures.
  2. Update the contact victory predicate and run Node tests.
  3. Run the browser proof 03 and Win-card acceptance flow.
  4. Archive this change only after the implementation and acceptance checks pass; rollback is reverting the predicate and its fixtures.

Open Questions

None. The first-release vocabulary already defines YOU and WIN; this change clarifies their interaction for a single surviving entity.