# memory — JANKENBOTS Durable memory for this project. Read at session start, update before session end. Date format: `YYYY-MM-DD`. Deep design detail lives in `DESIGN.md` (the full record carried over from the idea's `plan.md`); this file is the distilled decisions + live work. > **Location:** this project lives at **`/mnt/f/jankenbots/jankenbots`** (Windows `F:\jankenbots\jankenbots`) so the Unity Editor (Windows-only) and the git repo/docs share one folder. `~/claude/projects/jankenbots` is a **symlink** to it. Work from either path. **Name:** the game is **JANKENBOTS** (working title was JANKENSTEIN; the archived idea folder keeps the old name at `~/claude/archive/ideas/jankenstein/`). ## Decisions & rationale - **Concept (locked).** ~25-player friendslop build-brawl: teams build a janky physics robot from themed scrap; **each teammate pilots one functional part** (pillar #6 — "you build your control scheme, not a robot"); death → free-roaming ragdoll **gremlin**. 8 design pillars in `DESIGN.md`. - **Engine: Unity** (2026-07-10). Decided after a 3-angle netcode/prior-art study. The physics-party genre is overwhelmingly Unity (Fall Guys, Gang Beasts, Human Fall Flat, PEAK, Lethal Company, Party Animals, Stumble Guys, Trailmakers, Besiege, Robocraft); the two UE physics-heavy titles both swapped UE physics for Bullet. An earlier Unreal-MCP-tooling edge was correctly overridden. **The defining risk is networked physics at ~25-player scale — no shipped game occupies that quadrant.** - **Control feel v0.1 — driving = symmetric** (2026-07-10). Each locomotion part = one pilot applying *literal force* ("you are the left tread") — the only model that generalises to arbitrary scrap contraptions and the one that keeps pillar #6. Tread pilot verbs: **snap-to-cruise throttle** (both slam forward = easy straight; coordination lives in the in-between), **lock-pivot** (called-play turns), **passive lean** (nobody idle). Asymmetric (throttle/steer split) held as the fallback if the prototype groans. - **Weapons = three-body call-and-response** (2026-07-10). A weapon pilot perturbs the drivers' world → turn simultaneous chaos into rhythm: wind-up **telegraphs a brace**, a **braced bonus** makes drive+strike a combo, weapon doubles as engine/counterweight. Generalises to **5 archetypes** — Punch / Grinder / Grip (later tier) / Guard / Passive. The **weapon mix is a build-time chaos dial** (over-building self-punishes = pillar #6 made mechanical). - **Team size & lobby math** (2026-07-10). Team **3–5** (3 = smallest full driver+driver+weapon triangle; 5 = coordination/budget ceiling); lobby **≥2 teams**; sweet spot 5 teams of 5. Unequal sizes balanced by a **flat build budget** (small team = concentrated & nimble; big team = distributed & tippy → equal power, different character), reinforced by physics (bigness = top-heavy, self-limiting) + coordination tax; matchmaking buckets pre-made friend groups & prefers equal sizes, NPC-fills the rest. **No living "roamer" — everyone is in the bot; off-bot = gremlins (dead) only.** Style vote needs ≥3 teams (2-team lobbies use fight-result + spectator vote). - **Prototype netcode = Unity NGO + Relay, host-authoritative** (2026-07-10). Chosen for the *throwaway* first prototype (free, first-party, friends join by code, no port-forwarding). **This does NOT commit the final architecture** — server-authoritative state sync vs Photon Quantum deterministic lockstep is still deferred until the game is proven fun. ## Open questions / TODOs - [ ] **▶ M1 BUILD COMPLETE — only the 2-human playtest remains** (see `docs/prototype-plan.md`). Done so far (2026-07-10, all compiling clean + pushed): **packages** (NGO 2.13.0 + `com.unity.services.multiplayer` 2.2.4 — the *unified* SDK that supersedes the deprecated standalone relay/lobby packages; pulls transport 6.5.0 + core/auth), **4 scripts** in `Assets/Scripts/{Net,Bot}/`, **scene** `Assets/Scenes/M1_Arena.unity` (NetworkManager+UnityTransport+NetworkBootstrap, ground/cam/light, set as build scene), **replicated Player capsule prefab** (`Assets/Prefabs/Player.prefab`, registered as `NetworkConfig.PlayerPrefab`). - **✅✅✅ SHOVE BALL + FOLLOW CAMERA BUILT & SMOKE-VERIFIED (2026-07-12) — M1 build is DONE.** Wired both into `M1_Arena` via MCP, solo-host smoke test passed (ball spawns + rests on ground + shoves under force; camera moves to bot+offset and frames it). Committed `b21f2d9`. **Shove ball** = in-scene `NetworkObject` sphere `ShoveBall` (1.5m, Rigidbody 10kg, linearDamping/angularDamping 0.5, Interpolate, Continuous) at world `(0,0.75,6)` (6m in front of the bot's +Z spawn facing, resting on the Plane at y=0); `SphereCollider` + `Assets/Physics/ShoveBall.physicsMaterial` (dyn/stat friction 0.4/0.5, bounce 0.3, Average combines); `NetworkRigidbody`+`NetworkTransform` → host-authoritative, exactly mirroring the Bot. **NO custom script** (passive prop; host PhysX + NetworkRigidbody replicate it for free — correct M1-minimal call). **Follow camera** = `Assets/Scripts/Camera/FollowCamera.cs` (plain client-local MonoBehaviour, NOT networked) on `Main Camera`, `target`=Bot chassis, `worldOffset (0,8,-12)`. Deliberately **position-follow-only** (fixed world offset + separately-slerped look-at, ignores the bot's roll/yaw) so the janky bot's spin/tip doesn't nauseate; escalation lever if it feels detached = follow bot YAW only. Auto-finds "Bot" by name if `target` empty. **Control-scheme note confirmed this session:** TreadPart v0.1 is **forward + steer only, NO reverse** (throttle W/↑ is forward-only per its own comment; turning = asymmetric throttle or lock-pivot; lean=anti-tip). **Unity 6 (6000.5.3f1) API gotchas hit:** `PhysicMaterial`→`PhysicsMaterial`, `PhysicMaterialCombine`→`PhysicsMaterialCombine`, Rigidbody `.drag`/`.angularDrag`→`.linearDamping`/`.angularDamping` (old names still readable but set the new ones). `NetworkBootstrap` is in the **global namespace** (no `Jankenbots.Prototype`), `HostGame()` is async `Task`. **▶ ONLY M1 STEP LEFT = the real 2-human friends playtest over Relay** (this session's test was solo host + a scripted impulse as a ball-shove proxy). Then M2 = hammer. - **✅ CAPSULE REPLICATION VERIFIED (2026-07-11) — M1 netcode thesis proven.** Two clients over Relay (host = original editor via MCP, client = ParrelSync clone), both capsules spawned + drove independently + replicated **bidirectionally, smooth**. The `input→ServerRpc→host-physics→NetworkTransform` loop works over real Relay. **ParrelSync 1.5.2** installed for local 2-client testing (clone at `../jankenbots_clone_0`, symlinks Assets+ProjectSettings; open it via `ClonesManager.OpenProject` or the ParrelSync menu; its MCP loses the port fight so only the original is MCP-controllable). - **✅✅ TWO-TREAD BOT DRIVES — M1 CORE THESIS VALIDATED (2026-07-11).** Two clients over Relay, each piloting one tread of the shared bot; coordinating both to drive straight **"feels good"** (user's words). First attempt the bot was too hot + top-heavy and launched off on a nudge → tuned to stable: cruiseForce 450, linearDamping 1.5, angularDamping 6, maxAngularVelocity 3, **centerOfMass (0,-0.6,0)** (serialized override, implicitCom=false → persists). These are baked into the scene Bot. **Gotchas found:** (1) the step-2 `PlayerPrefab` capsule still auto-spawns per client and PlayerDriver ALSO reads WASD → despawn capsules (or clear PlayerPrefab) for a clean bot test — in the real game players are seats, not free capsules; (2) each re-host mints a NEW Relay join code (old ones die) — stale codes were the cause of a failed join; (3) recompiling scripts mid-session forces the ParrelSync clone to recompile → don't Join during compile. Remaining M1: shove ball + follow camera (step 5-6), then the REAL verdict = a live 2-human friends playtest (the auto-drive was a solo proxy). - **✅ SHARED BOT BUILT (2026-07-11), solo-smoke-verified; awaiting 2-pilot drive test.** In-scene `Bot` NetworkObject in M1_Arena: chassis Cube (Rigidbody 60kg + NetworkObject + NetworkTransform + NetworkRigidbody + SeatManager) with two child treads `Tread_L`/`Tread_R` (TreadPart, side Left/Right) at world ±1.2 X. In-scene bot auto-spawns on host; seat claim + gating verified solo. **DESIGN FIX (important):** the two drafts were mis-wired — TreadPart gated on NetworkObject `IsOwner` (needs per-tread ownership → impossible, NGO forbids nested NetworkObjects in a spawned prefab) while SeatManager's `OwnsSeat/PilotOf` API sat unused. Rewrote TreadPart to **seat-based gating**: both treads share the chassis NetworkObject; a client drives the tread whose seat it holds (`PilotOf(side)==LocalClientId`), host re-validates `OwnsSeat` on the RequireOwnership=false RPC. Also swapped TreadPart to `Keyboard.current` (New-only input). Controls: **W/↑ throttle · Shift pivot · A/D lean**. Moved SeatManager OnGUI below the bootstrap panel (was overlapping). - **✅ UGS GATE CLEARED + HOST PATH VERIFIED (2026-07-10).** Cloud project **jankenbots** (`cloudProjectId a0d222ae-c79b-4120-bc0d-947bfd7600cb`, org `megaproxy`) is linked (has been in ProjectSettings since project creation) AND **Relay is now activated in the dashboard** (the missing step — done via cloud.unity.com ▸ Development ▸ Products ▸ Relay; there is NO enable toggle, opening the product page activates it). Verified by an in-editor Play-mode smoke test: `UnityServices.State=Initialized`, anonymous auth signed in, `HostGame()` created a real Relay allocation + join code, host's Player capsule auto-spawned (`IsSpawned`, owner=0). **The host half of M1-step-2 works end-to-end.** Still unverified: a *remote client joining* + peer-to-peer replication (needs a 2nd instance — standalone build via `manage_build` or a ParrelSync clone). - **Next build steps:** (5) shared bot = chassis Rigidbody + 2 `TreadPart` treads + `SeatManager` (⚠ `TreadPart` still reads legacy `Input.GetAxis` — swap to `Keyboard.current` like PlayerDriver was, project is Input-System-**New-only**); (6) shove ball + follow cam; (7) 2-friends feel test over Relay (needs a standalone build via `manage_build` or a ParrelSync clone for the 2nd client). - **Verified Relay API gotcha (baked into NetworkBootstrap):** with the unified SDK, `new RelayServerData(allocation, "dtls")` does NOT exist — use `AllocationUtils.ToRelayServerData(allocation, "dtls")` (`AllocationUtils` is in `Unity.Services.Relay.Models`). Corrected via live editor reflection. - [x] **Project location decided + unified** — `F:\jankenbots\jankenbots` (git + Unity + docs together), symlinked `~/claude/projects/jankenbots`. Unity `.gitignore`/`.gitattributes` in place. - [x] **Unity project created** — fresh URP + Input System template (`Assets/`, `SampleScene`, `InputSystem_Actions`). Still to add: NGO + Relay + Lobby packages (part of M1 above). - [x] **Unity MCP working** — CoplayDev server on `127.0.0.1:8067`, portproxy `8068→8067`, `.mcp.json` → `172.27.208.1:8068/mcp`; handshake verified (v3.4.4). **Tools only load when Claude is started from this project folder** with Unity open + server running. - [ ] **Netcode architecture (deferred, the real risk)** — dedicated server-authoritative state sync (Robocraft 2 blueprint) vs Photon Quantum lockstep (Stumble Guys precedent). Decide *after* the fun is proven + the 25-player physics spike. Netcode target ≈ 25–30 primary rigid bodies + gremlins/debris. - [ ] **The 25-player physics/netcode spike** — two ~30-joint bots colliding at arena scale, ≥100 ms latency, min-spec. The make-or-break gate before scaling up. - [ ] Remaining **design** questions (detail in `DESIGN.md`): build-coupling tightness, hidden per-player agenda, at-scale voting, part taxonomy, scavenge-contest rules, art style, progression (cosmetic-only), final name (working title JANKENSTEIN; alts BODGEBOTS / SCRAPYARD SCRUM / CLANKERS / RUSTBUCKETS / JALOPY WARS). ## Session log ### 2026-07-12 (session 4 — M1 build finished: shove ball + follow camera) - **▶ RESUME HERE next session:** the entire M1 build is done (bot drives, shove ball, follow camera — all in `M1_Arena`, all smoke-verified). **The ONE thing left in M1 is the real 2-human friends playtest over Relay** — flip off any `debugAutoDriveFull`, don't rely on scripted proxies, get two actual people each on one tread pushing the ball around. That playtest is the true fun verdict. After it → **M2 = add a hammer** (first weapon). See the "SHOVE BALL + FOLLOW CAMERA" ✅✅✅ block above for every value/gotcha. - Fanned out two Sonnet subagents (camera-script author + ball-networking designer) while MCP was down, then wired both into the scene via MCP once the editor came back. Ball needs no script; camera is `FollowCamera.cs`. Confirmed the control scheme is **forward + steer, no reverse**. 2 commits pushed (`87f929b` camera script, `b21f2d9` scene+ball+material). Unity-6 physics API renames noted in the block above. ### 2026-07-11 (session 3 — M1 bot drives, thesis validated) - **▶ RESUME HERE next session:** M1 core is proven (two-tread bot drives over Relay, feels good). **Remaining M1:** (1) build the **shove ball** (big physics sphere) + a **follow camera** framing the bot — makes coordination purposeful; (2) run the **real 2-human friends playtest** (this session used a host-side `debugAutoDriveFull` auto-drive as a solo proxy — flip it off / don't rely on it with real players); then **M2 = add a hammer**. To bot-test cleanly: despawn the player capsules or clear `NetworkConfig.PlayerPrefab` (they double-drive on WASD), and remember each re-host = a NEW Relay code. - Built the whole M1 bot this session via MCP + ParrelSync 2-client testing; tuned physics live after it launched off; validated it drives straight with two pilots. All committed + pushed (see the M1-progress TODO block above for the full state, gotchas, and verified API/tuning values). ParrelSync clone editor can be closed between sessions; reopen via ClonesManager when needed. ### 2026-07-10 (session 2 — M1 build start) - **Started Milestone 1 via a research/draft workflow + serial in-editor build.** Ran a 9-agent workflow (verified NGO/Relay API research → adversarial verify → draft 4 scripts → integration plan); the verify agent installed the packages to reflect against the *live* editor, catching two hallucinated Relay APIs before any code hit the project. Then serially, all main-loop MCP work (packages/scene/prefabs are stateful/single-editor → NOT parallelized): dropped scripts (compile clean, 0 errors), built `M1_Arena` scene + NetworkManager, made the replicated Player capsule prefab, fixed PlayerDriver input for New-only Input System. 4 commits pushed. **Stopped at the UGS-link gate** (see TODO) — nothing is runtime-testable until that's linked. - **Runtime host smoke-test (solo, no 2nd client):** enter Play via `manage_editor play`, then `execute_code` to call `nb.HostGame()` and poll `read_console` — saw QosJob region pings + `Hosting with join code XXXXXX`. Note: the `editor/state` resource's background tick FREEZES (goes stale) during playmode transitions, but direct `execute_code`/`read_console` commands still work — probe with those, don't trust the stale state snapshot. - **MCP resource URI correction:** editor state is `mcpforunity://editor/state` (not `/editor_state`); project info `mcpforunity://project/info`. `find_gameobjects` wildcard `*` returns nothing — search by name/component/path. `execute_code` uses CodeDom (C# 6) by default → fully-qualify `UnityEngine.Object`, no C#12 syntax. ### 2026-07-10 (session 1 — promotion + setup) - **Promoted from `ideas/jankenstein` to a real project.** Scaffolded the WSL-side project home (this repo): filled `CLAUDE.md`, seeded this `memory.md` from the idea's `plan.md`, carried the full design record over as `DESIGN.md`, added `docs/prototype-plan.md`. Idea folder archived to `archive/ideas/jankenstein`. - Decided to build a **basic networked prototype first** (user wants to test the coordination feel with real friends — local split-screen won't capture the social/voice-chat core). Stack: **Unity NGO + Relay**, host-authoritative, throwaway netcode. Milestone 1 = one shared bot, each player one tread. - **Consolidated the project + seeded Unity MCP config** (`.mcp.json` → `unity` HTTP server at `172.27.208.1:8080/mcp`, `.claude/settings.local.json`, `docs/mcp-setup.md` runbook), Unity `.gitignore`/`.gitattributes` (no LFS yet). - **Renamed JANKENSTEIN → JANKENBOTS and unified folders.** User created the Unity project at `F:\jankenbots\jankenbots` (named it jankenbots). Moved the seeded repo (git history + docs + `.mcp.json`) **into** the Unity project root so it's one folder; renamed the Forgejo repo `jankenstein → jankenbots` + updated the remote; re-pointed the symlink to `~/claude/projects/jankenbots`. Unity project is a fresh URP/Input-System template (has `Assets/`, `SampleScene`, `InputSystem_Actions`). **MCP status: ✅ WORKING (verified 2026-07-10, mcp-for-unity-server v3.4.4).** Server binds `127.0.0.1:8067` (HTTP mode), bridged to WSL via `netsh portproxy 0.0.0.0:8068→127.0.0.1:8067`; Claude reaches `http://172.27.208.1:8068/mcp` (`.mcp.json`). **Key gotcha:** 8080 is unusable — it's in a Hyper-V/WinNAT reserved range (WSL2), bind fails with WSAEACCES/Errno 13; use a non-reserved port + a *distinct* proxy port. Full detail in `docs/mcp-setup.md`. **Session wrapped here** — everything committed & pushed; resume by opening a fresh Claude session from this folder (Unity open, MCP server running) and starting Milestone 1. ## External references - Full design record: `DESIGN.md` (this repo) — control feel v0.1, weapon archetypes, lobby math, tech direction, netcode prior art. - Prototype build-plan: `docs/prototype-plan.md`. - Wiki (orientation): http://192.168.1.249:6876/projects/jankenbots - Idea-phase history: `~/claude/archive/ideas/jankenstein/` (original brainstorm `plan.md` + session logs). - Netcode prior art (Rocket League GDC talk, Fall Guys/Robocraft/Stumble Guys case studies, Gaffer On Games) — cited in `DESIGN.md` → References. - **Unity MCP setup runbook:** `docs/mcp-setup.md`. Server = CoplayDev/unity-mcp in **HTTP mode host-side**; Claude (WSL) connects at `http://172.27.208.1:8080/mcp` (`.mcp.json` committed). Bridge pattern mirrors the UE servers (host-side HTTP + host-IP). Not yet stood up — needs the editor + package installed.