Keeler Mind-Swap Simulator: UI Requirements
Table of Contents
1. Purpose
Requirements for rebuilding the body swap simulator from scratch in any language (ClojureScript, TypeScript, Rust/WASM, Lean 4 game, etc.). Same role as the transform tool spec – an implementing agent reads this and produces a working tool.
The current ClojureScript implementation at
src/wal_sh/tools/body_swap/ is the reference. This spec extracts the
requirements so another implementation can be verified against it.
2. Confirmation gate
Before writing code, the implementing agent MUST state:
- How many swappable bodies are in the cast (answer: 12)
- How many helpers (answer: 2, Sweet Clyde and Bubblegum Tate)
- What the affine constraint is (each pair swaps at most once)
- What the episode swap sequence produces (a 7-cycle and a 2-cycle)
- How many repair swaps the episode needs (answer: 13)
3. Domain model
| Term | Definition |
|---|---|
| body | keyword identifier; holds exactly one mind |
| mind | same identifier space; mind m is home iff held by body m |
| state | map body -> mind; a bijection (permutation) |
| edge | unordered pair of distinct bodies |
| trace | ordered vector of edges |
| legal | all trace edges pairwise distinct |
| helper | :sweet-clyde or :bubblegum-tate; introduced only during repair |
4. Invariants
- U01 Affine edges: no edge appears twice in the trace
- U02 Bijection: state is always a valid permutation
- U03 Edge cap: trace length <= C(n,2) where n = body count
- U04 Repair provenance: every repair swap touches a helper
- U05 Helper freshness: no history swap touches a helper
5. Cast
| Row | Characters | Swappable |
|---|---|---|
| 1 | Amy, Professor Farnsworth, Bender, Leela | yes |
| 2 | Fry, Dr. Zoidberg, Hermes, Kif | yes |
| 3 | Wash Bucket, Emperor Nikolai, Scruffy, Nixon's Head | yes |
| 4 | Sweet Clyde, Bubblegum Tate | no (helpers) |
6. Episode swap sequence (Prisoner of Benda, 6ACV10)
| # | Swap | Reason |
|---|---|---|
| 1 | Professor <-> Amy | Prof wants youth, Amy wants to eat |
| 2 | Amy(Prof) <-> Bender | Bender needs human body for heist |
| 3 | Professor(Amy) <-> Leela | Leela wants movie discount |
| 4 | Amy(Bender) <-> Wash Bucket | Bender needs to reach Nikolai |
| 5 | Fry <-> Zoidberg | Fry tests if Leela likes his looks |
| 6 | Wash Bucket(Bender) <-> Nikolai | Bender gets emperor's body |
| 7 | Leela(Amy) <-> Hermes | Hermes thinks he can lose Amy's weight |
Result: (professor leela hermes amy bender nikolai wash-bucket)(fry zoidberg)
Repair: 13 swaps via Sweet Clyde + Bubblegum Tate. Everyone restored.
7. Functional requirements
Buttons are grouped into three toolbar zones separated by visual dividers:
| Group | Buttons | Purpose |
|---|---|---|
| Scenarios | Scramble, Worst Case, Episode | Generate swap histories |
| Proof | Keeler Repair | Demonstrate the theorem |
| Meta | Reset, Share | Admin: clear state, export URL |
7.1. Scenarios
7.1.1. FR1 Manual swapping
- Click two body cards to swap
- Consumed edge: refused with message, state unchanged
- Click selected card to deselect
- Swapping disabled during repair animation
7.1.2. FR2 Scramble
- Random legal history from fresh reset
- URL updates with seed on completion
7.1.3. FR3 Worst case
- Exhaust all C(12,2) = 66 pairs
- Demonstrates maximum-entropy permutation
7.1.4. FR4 Load episode
- Replay the 6ACV10 swap sequence (7 swaps)
- Deterministic (no randomness)
7.2. Proof
7.2.1. FR5 Keeler repair
- Introduces helpers, computes repair plan, animates step by step
- Plan per k-cycle
[c0 ... c_{k-1}]:x<->c0, x<->c1, ..., x<->c_{k-2}, y<->c_{k-1}, x<->c_{k-1}, y<->c0 - Final
x<->yiff helpers end transposed (odd nontrivial cycle count) - Cards update live during animation
7.2.2. FR6 Verification
- After repair: all bodies hold own mind, trace is legal
- Failure is loud: names the residual permutation
7.3. Meta
7.3.1. FR7 Reset
- Returns to identity state, empty trace, helpers waiting
- Clears
?s=from URL
7.3.2. FR8 Share
- Copies current URL (with
?s=seed) to clipboard - Flash confirmation on button
7.3.3. FR9 Seed loading
- On page load, decode
?s=parameter and replay trace - Invalid seeds are silently ignored (falls through to idle)
7.4. Displays
7.4.1. FR10 Cards and indicators
- Body cards: name, whose mind is inside, where own mind went
- Cycle notation
- Consumed edge list (struck through)
- Repair trace with step highlighting
8. Test contract (property-based)
| Property | Conjecture | Trials |
|---|---|---|
| Keeler restores identity for any legal trace | F1 | 100+ |
| Every repair swap touches a helper | F2 | 100+ |
| Parity: final x<->y iff odd cycle count | F5 | 100+ |
| All repair edges distinct (affine) | U01 | 100+ |
| Swap is involution | -- | 200+ |
| Trace is always legal | U01 | 100+ |
Current test suite: 13 tests, 21 assertions, 600 PBT trials, 0 failures.
9. URL seed encoding
Shareable state via ?s=<base64url> query parameter. The address bar
updates live on every action.
9.1. Encoding scheme
Each swap is packed into one byte: high nibble = cast index of first body, low nibble = cast index of second body. The byte sequence (preserving swap order) is base64url-encoded.
cast indices: 0=amy 1=professor 2=bender 3=leela
4=fry 5=zoidberg 6=hermes 7=kif
8=wash-bucket 9=nikolai 10=scruffy 11=nixon
byte = (index-a << 4) | index-b
seed = base64url(byte-sequence)
Example: the episode sequence (7 swaps) encodes as ~12 characters. Worst case (66 swaps, all pairs) encodes as ~88 characters.
9.2. Round-trip property
decode(encode(trace)) = trace – the edge sequence and resulting
permutation must be identical. This is testable as a PBT property.
9.3. URL lifecycle
- Page load: if
?s=present, decode and replay trace. - Every action:
replaceStatewith current seed (scramble, worst case, episode, card swap). - Reset: clears
?s=from URL. - Share button: copies current URL to clipboard.
10. UI state machine
Figure 1: UI interaction states (derived from browser.cljs atom discriminants)
Six states, two interaction modalities (buttons and card clicks):
| State | Atom discriminant | Entry |
|---|---|---|
idle |
empty trace, no selection | Reset, page load |
seed_loaded |
transient | page load with ?s= |
has_trace |
edges > 0, no selection | swap, scramble, worst, episode |
char_selected |
selected non-nil | card click |
repairing |
repair non-nil, timer active | Keeler Repair button |
repaired |
repair done, timer cleared | timer completes all steps |
The affine_violation node is transient (console warning + deselect),
not a durable state.
11. Non-functional
- No network. Static bundle.
- Deterministic given seed.
- Home/displaced encoded by color AND glyph (accessibility).
- Under 500KB gzipped.
12. Related
- Keeler's Theorem research note
- Transform tool spec (same pattern)
spec/keeler/Keeler.lean(Lean 4 proofs)spec/dafny/Keeler.dfy(Dafny proofs)test/wal_sh/tools/body_swap/core_test.clj(PBT suite)