Files
jesse dc0df9122e
CI / quality (push) Failing after 46s
docs: project CLAUDE.md for native-Windows Claude Code handoff
Pure ASCII (PS 5.1 reads non-ASCII as ANSI and desyncs the parser).
Records the aux-lane suppression invariant, the verified 6e50f97 baseline,
the read/write channel split, and the probe scripts that check each metric.
2026-08-06 08:45:27 -04:00

5.6 KiB

CLAUDE.md -- Tokyo Wangan Racing

Guidance for Claude Code running natively on windows-gpu, the same machine as Roblox Studio. Pure ASCII on purpose (PowerShell 5.1 reads non-ASCII as ANSI and desyncs the parser).


1. Read this before touching MapBuilder

src/server/MapBuilder.luau generates the map procedurally. The user also edits geometry by hand in Studio. Those two facts collide, and that collision is the single most important thing about this repo:

A rebuild regenerates anything the source still describes. If the user deleted geometry by hand and you rebuild without teaching the source about that deletion, you silently put it back.

The user WILL notice, immediately, and will say so ("you just added back the X that I deleted"). When that happens: stop editing, run a read-only probe, report exact numbers. Do not "fix" it with another rebuild.

The live invariant: aux-lane suppression

local AUX_SUPPRESS_FROM = 525   -- samples 525..550 have NO aux lane
local AUX_SUPPRESS_TO   = 550   -- because the user deleted that run by hand
local AUX_BLEND         = 5     -- smoothstep width ramp, do not hard-cut
  • Do not remove or widen this range. Re-enabling it resurrects deleted geometry.
  • AUX_BLEND = 5 is not cosmetic. A hard cut produced a 26-stud lateral fence jog. The smoothstep removes it.
  • The parapet is force-closed across the same range (~line 1031). Normally the parapet OPENS along a speed-change lane because the aux lane re-fences the outer edge -- with no aux lane there is nothing to re-fence with, leaving the deck edge bare. That was the measured barrier gap at samples 527..548.

Any change touching aux lanes, parapets, or barriers must re-run tools/probe_regression.luau and match the baseline below.


2. Verified baseline (commit 6e50f97)

Measured in the live datamodel, not inferred from source:

Metric Value
total parts 16788
PAWestSlip 60 segs, 34.4 deg total bend, worst 1.4, 0 kinks >5
PAEastSlip 60 segs, 30.7 deg total bend, worst 1.3, 0 kinks >5
AuxLane parts 30 (samples 525-550 deliberately absent)
barrier coverage 823 samples, 0 unguarded
fence jogs >10 studs 0

AuxLane = 30 is the user's hand-edited count, not the source's original 52. If a probe reports 52, a rebuild has undone the suppression -- that is a regression, not progress.


3. Two channels, different jobs

[A] MCP bridge (roblox_studio)  -> READ   run Luau in Studio, measure, screenshot
[B] Rojo                        -> WRITE  push .luau source into Studio (~1 s)

Use [A] to verify what [B] did. Never assume a sync landed.

Rojo syncs scripts, not built geometry. Editing MapBuilder replaces the ModuleScript instantly, but workspace.Map still holds parts from a PREVIOUS build() run. The map looks stale until you re-run the build. Do not tell the user "you'll see it live" about map geometry.

Rojo version must match the Studio plugin. The plugin auto-updates from the Marketplace without asking. Server older than plugin fails the handshake with a cryptic attempt to index number with 'protocolVersion'. Both are pinned at 7.7.0 (rokit.toml). If the user reports a connect error, check this first.


4. Workflow

make check      # stylua --check + selene + rojo build
make build      # rojo build --output build.rbxl
make serve      # rojo serve  (Studio: Rojo -> Connect)
make fmt        # stylua src/

Probe scripts in tools/ are read-only -- they measure the live datamodel and never build. Run them through the MCP bridge.

Script Answers
probe_regression.luau full baseline check vs the table above
verify_auxlane.luau is the 525-550 suppression intact?
verify_slips.luau slip-road bend angles / kink count
probe_barriergap.luau any unguarded deck edge?
probe_jog.luau lateral fence jogs

5. Hard-won rules

  • Measure, then fix. Do not infer a bug from symptoms. One inverted convention can produce three unrelated-looking symptoms; fixing each symptom leaves the root cause intact.
  • A metric derived from a constant cannot fail. Ask: would this number differ between the broken and fixed build? If not, it is not a test.
  • An empty result set must FAIL, never pass. Count what you actually exercised; if tested == 0 then return "NOTHING TESTED -- FAILURE" end.
  • Verification in Play mode proves nothing. Play is a disposable sandbox discarded on stop. Verify in Edit mode.
  • require() caches stale bytecode after a Rojo edit. The cache still holds old bytecode even though .Source is new -- no error, no warning. Clone the module and require the clone.
  • Enumerate part names, don't just count. A raw total hides partial removals (deleted the tall barrier, missed the kerb). Tally what should be gone vs what should remain, and report both.
  • AutoRecovery places are silently stale. After a crash Studio reopens *_AutoRecovery_1.rbxl holding pre-crash state. Check the title bar.
  • Creator Store assets: 50+ scripts or 1M+ tris is zero-tolerance. Get exact counts before inserting anything.

6. Shell

Git Bash (C:\Program Files\Git\bin\bash.exe) is present -- prefer it. PowerShell over a non-interactive pipe mangles quoting. & is not a statement separator in PowerShell. When PowerShell is unavoidable non-interactively, use -EncodedCommand with UTF-16LE base64.

StudioMCP.exe lives inside the versioned Studio install and moves on every update. Never hardcode it; ~/claude-tools/studio-mcp.bat resolves it dynamically.