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-refresh-rules-after-contact-destruction / design.md
design.md 3.1K source

Context

The engine scans words after controlled and automated movement, transforms objects, then resolves one contact pass. resolveContacts() can remove word entities but currently rebuilds indexes against the old state.rules. That leaves state.rules, state.sentences, object properties, automata carriers, and rule deltas describing a board that no longer exists. Tidewater's sunk STOP word demonstrates the resulting blocked next move.

Goals / Non-Goals

Goals:

  • Make every settled state and event describe rules physically present after destruction.
  • Keep the existing one-pass contact order and deterministic turn boundary.
  • Preserve exact Undo, reset, and restore behavior for the new settled state.

Non-Goals:

  • A second contact cascade or another noun transformation after a rule word is destroyed.
  • Changes to rule precedence, AND parsing, FLOAT, gates, or switch semantics.
  • A new serialized field or a change to authored level data.

Decisions

Refresh derivations at the end of contact removal

Contact effects use the rule set captured immediately before the contact pass. After removing entities, call the existing rule refresh path to parse surviving words and rebuild indexes and automata carriers. Reusing this path keeps reset, step, and wait consistent. Rebuilding only the cell index would retain the phantom rule; repeating the entire turn resolver would introduce contact cascades and transformation chains outside this fix.

Evaluate final victory from surviving entities and final properties

Destruction is determined under pre-contact properties. The final victory check considers surviving entities and the refreshed rule set. Thus a destroyed WIN word cannot grant a phantom win; a destroyed YOU word updates control loss. This follows the existing promise that the settled event describes the surviving board. Contact destruction itself is not repeated if a refreshed rule changes SINK or DEFEAT.

Keep rule deltas relative to the pre-turn snapshot

The event builder already compares the pre-turn and settled rule maps. Refreshing before that comparison reports a rule removed by destruction as rulesRemoved, including its original source IDs. No intermediate rule table becomes a public state.

Risks / Trade-offs

  • [Risk] A rule removed by SINK changes properties after contact effects have run. → Treat contact effects as a single pass; specify this boundary and add a fixture.
  • [Risk] A final WIN check changes outcomes in levels that relied on a word destroyed during the same turn. → Require survivor-based WIN and run bundled proof and Tidewater solver checks.
  • [Risk] Undo restores rules but indexes reference discarded objects. → Keep the existing index rebuild on Undo and add a destruction/Undo regression.

Migration Plan

  1. Add a failing W8 fixture and the final-state rule contract.
  2. Refresh rule derivations after contact removals; verify step, wait, reset, Undo, and restore.
  3. Run the Node and browser suites, then ask the world team to re-solve Tidewater's seven proofs.

Open Questions

None for this bounded fix. Broader contact cascades belong to a separate rule-order change.