Skip to main content

remote-debug

Autonomous physics debugging against nudge.exe (app/scene-based) and nudge_tests.exe (unit-test pause points). Scientific method with deterministic repros, mechanical metrics, varied-input verification, and bias guards. Activate when the user asks to debug a physics bug end-to-end and wants the agent to drive from repro to verified fix.

معلومات المصدر

المستودع
RandyGaul/nudge
آخر نشاط في المصدر
١٨ أبريل ٢٠٢٦ في ٢٣:٤٣
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٣٤
التفرعات
٢

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
remote-debug
description
Autonomous physics debugging against nudge.exe (app/scene-based) and nudge_tests.exe (unit-test pause points). Scientific method with deterministic repros, mechanical metrics, varied-input verification, and bias guards. Activate when the user asks to debug a physics bug end-to-end and wants the agent to drive from repro to verified fix.
version
1.0.0
# Remote Debug -- Autonomous Physics Debugging End-to-end loop: find a deterministic repro, isolate one variable at a time, measure against expectations, propose a minimal fix, and re-verify across many varied inputs. The agent drives; the human watches. ## When to activate - User points at a bug and asks Claude to find + fix it. - User says things like "debug this", "why is X happening", "find the repro", or invokes `/remote-debug <bug>`. - A unit test is failing and the user wants pause-point inspection instead of printf spelunking. ## When NOT to activate - The user wants a feature, refactor, or design discussion. - The user is asking how an algorithm works — that is research, not debugging. - You already have a verified repro and root cause and just need to implement the fix. ## Two modes The viewer (`nudge_viewer.exe`) talks to either target over TCP and inspects memory via `ReadProcessMemory`. Type tables are sent by the engine on connect, so all command syntax is identical -- the difference is the simulation host. | Mode | Target | When to use | |------|--------|-------------| | **App mode** | `nudge.exe` | Scene-based bugs: scene loads, interactive push/drag, rendering-tied repros, mouse-recording playback. | | **Test mode** | `nudge_tests.exe --debug --break=<pattern>` | Unit-test bugs: a specific test fails and you want to pause at a known point and inspect state -- an alternative to printf debugging. | Both modes support the same inspection commands (`summary`, `get`, `table`, `contacts`, `warm`, `filter`, `snap`, `diff`, `stable`, `raw`). Test mode additionally supports pause points (`DBG_BREAK(name, world)`) and the control commands `continue` / `where` / `break-filter`. ## Core principles 1. **Find the repro FIRST.** Can't debug what you can't reproduce. Don't investigate until you have a deterministic repro that shows the bug. 2. **Use the user's actual interaction.** If the user triggers it with the mouse, use `playrecording` to replay their mouse recording. Don't substitute with `push` -- it's a different interaction pattern. 3. **Determinism is required for comparison.** Same script + same binary = same result. Fresh engine per run. Non-deterministic comparisons are INVALID. 4. **ONE variable per experiment.** Never change two things between comparisons. 5. **The fix is usually small.** If you're writing 50+ lines, you're probably fixing a symptom. The actual fix for this engine's capsule bug was 2 lines. 6. **Band-aids create new bugs.** Speculative contacts, warm cache thresholds, substep increases -- these mask the real bug and introduce new problems. 7. **Listen to the user.** When they say "narrowphase", look at the narrowphase. When they say "it's not the solver", stop modifying the solver. 8. **Verify with VARIED inputs.** One passing test is not proof. Run 15+ varied scenarios. Failure to find ANY repro after the fix = confirmed. ## Phase 0: Find the repro **This is the MOST IMPORTANT phase. Do NOT skip or rush it.** **Step 1: Try the user's exact interaction.** - If the user has a mouse recording: `playrecording` command - If the user describes an interaction: replicate with `drag`/`push` - If the user says "knock capsules over": knock them over at various angles and forces **Step 2: Vary inputs until you find a failure.** - Try different push forces: gentle (1), moderate (3), hard (5) - Try different angles: sideways, diagonal, upward+sideways - Try different application points: top, middle, offset - Try different scenes: single body, multiple bodies, mixed shapes - The bug may only appear at specific angular velocities or tilt angles **Step 3: Confirm the repro is deterministic.** ```bash # Run the SAME commands 3 times with fresh engines for run in 1 2 3; do ./build/Debug/nudge.exe & sleep 2 echo -e "scene X\npause\nstep N\npush ...\nstep M\nget body_hot/K\nexit" | timeout T ./build/Debug/nudge_viewer.exe localhost 9999 taskkill //F //IM nudge.exe sleep 1 done # All 3 outputs must be IDENTICAL ``` **Step 4: If you can't find a repro with `push`, use `playrecording`.** The user's mouse constraint creates sustained force over many frames -- fundamentally different from an instant impulse. Add commands to replay user recordings rather than trying to approximate the interaction. **A repro that settles after a few seconds is NOT a repro of "persistent erratic bouncing."** Keep varying inputs until you find one that shows the EXACT symptom the user describes. ## Phase 1: Isolate -- one variable at a time **Disable everything, then add back one at a time:** - N^2 broadphase (eliminates BVH) - Disable sleep, warm start, incremental NP - Single body + ground (eliminates multi-body) **For EACH toggle, run the SAME deterministic repro and compare.** If the result changes when toggling X, X is involved. If the result is identical, X is not involved -- move on. ## Phase 2: Measure Record at the frame of anomaly: - pen depth, lambda, velocity, feature_id, contact count - Expected pen = velocity * dt / substeps - Expected lambda for resting ≈ 0.08 - If measured >> expected, the discrepancy IS the bug **Track the full contact cycle:** contact frame -> gap frames -> re-contact. Healthy contacts persist many frames. Single-frame contacts are the red flag. ## Phase 3: Hypothesize -- in the right file **Priority:** 1. Narrowphase early-exit conditions (hard boundaries without slop margin) 2. Broadphase pair filtering (tight AABB check discarding near-contact pairs) 3. Contact filtering between NP and solver 4. Solver convergence (only after 1-3 are ruled out) **Key lesson learned:** The capsule bounce bug was a 2-line fix in the narrowphase early exit -- `r.distance > a.radius` needed `+ LINEAR_SLOP`. Not in the solver, not in warm start, not in the broadphase. **If 3 experiments in the same file don't help, you're in the wrong file.** ## Phase 4: Test -- one atomic experiment - Add ONE printf, rebuild, run repro, read output - Revert the printf after reading - ONE experiment per iteration ## Phase 5: Fix -- minimal - Fix the MEASURED root cause - **DO NOT** tweak solver parameters, thresholds, substep counts, or margins - **DO NOT** add speculative contacts or change eviction thresholds - The fix should be in the narrowphase or broadphase code path, not the solver ## Phase 6: Verify -- varied inputs (critical) **Running the original repro is NOT ENOUGH. You must search for a new repro with varied inputs and FAIL to find one.** Run at minimum 15 tests covering: ``` # Push variations (6+ tests) push (1,0,0), (2,0,0), (3,0,0), (0,0,2), (1,0,1), (2,1,1) # Push with torque (3+ tests) push with offset application points # Drag interactions (3+ tests) gentle drag, moderate drag, diagonal drag # Multi-body (2+ tests) bowling pins, capsule-capsule collision # Regression (2+ tests) sphere, box -- must still work # Test suite ./build/Debug/nudge_tests.exe -- ALL tests pass ``` **For each test: check |vy| < 0.01 after 10 seconds (600 frames).** **Only declare FIXED when:** Zero failures across all varied tests + all unit tests pass. ## Progress logging Every 3 iterations: ``` === Debug Progress (iteration N) === Hypotheses: X tested (Y confirmed, Z disproven) Repro status: [found / searching / confirmed deterministic] Variables ruled out: [list] Current file under investigation: [path] ``` ## Key lessons (from capsule bounce debugging) | Lesson | Detail | |--------|--------| | The fix was 2 lines | `r.distance > a.radius` -> `r.distance > a.radius + LINEAR_SLOP` in two functions | | Band-aids created new bugs | Speculative contacts caused negative penetrations and energy injection | | The user's recording was the real repro | `push` couldn't reproduce the exact mouse-drag dynamics | | 10+ iterations were wasted in the solver | The bug was in the narrowphase all along | | N^2 vs BVH comparison was not deterministic | Different frame timing invalidated the comparison | | "Settled after 10s" is not "no bug" | The user sees erratic bouncing in the FIRST few seconds | | Contact toggling at tangent boundary | GJK distance oscillating around `radius` caused 1-frame contacts | ## Anti-patterns | Don't | Why | |-------|-----| | Substitute `push` for mouse interaction | Different dynamics -- use `playrecording` | | Change multiple variables per experiment | Can't attribute results | | Declare victory on one test | Must fail to find repro across 15+ varied inputs | | Spend >3 iterations in wrong file | Switch to the file the user pointed at | | Fix symptoms (big lambda) | Find WHY the contact is intermittent | | Add speculative contacts | Creates ghost contacts with negative penetration | | Increase substeps as a fix | Masks temporal aliasing, doubles perf cost | | Compare non-deterministic runs | Use fresh engine per run, same script | ## Commands Run `help` in the viewer for all viewer commands (RPM inspection). Run `help` as a driver command for all engine commands (TCP control). Key commands for debugging: - `summary` -- scene overview - `contacts <body>` -- solver contacts for a body - `stable [threshold] [frames]` -- mechanical pass/fail stability check - `push <idx> <f> [r]` -- impulse at body (app mode only) - `playrecording` -- replay user's mouse recording (app mode only) - `npviz` + `npvset <A|B> <kind> <pos> <rot> <params>` -- load shapes into NP Viz for visual narrowphase debugging (app mode only) - `continue` / `c` -- resume from a pause point (test mode) - `where` -- show current pause point name + file:line (test mode) - `break-filter [pattern]` -- view or change the break filter at runtime (test mode) - `np-debug <0|1> [body_a] [body_b]` -- toggle narrowphase snapshot capture + filter (either body index, `-1` = any). After enabling, step one frame (`step 1` in app mode, or `continue` past a DBG_BREAK in test mode), then `get np_debug` dumps the SAT intermediates and emitted contacts for the first matching hull-hull pair. Force `--threads 1` when capturing -- the slot is single-entry and races under parallel NP. ## Test mode -- pause points instead of printf Use this when a unit test is misbehaving and you need to inspect live engine state mid-test without recompiling with printfs. ### Adding a pause point Place `DBG_BREAK("name", world)` anywhere in a test, inside the loop or right before an assertion. The macro is defined by `src/debug_server.c` (included into the test binary) and is a zero-cost predicate check unless the binary is started with `--debug`. Example: ```c for (int f = 0; f < 60; f++) { world_step(w, 1.0f / 60.0f); if (f == 30) DBG_BREAK("my_test:frame30", w); // pauses here when armed } ``` Name pattern: pick something greppable and scope-prefixed. `my_test:frame30` beats `break1`. The viewer glob-matches against this. ### Running the test under the debugger ```bash # Start the test binary with --debug. --break arms one or more named points. # Without --break, DBG_BREAK is a no-op. --break=* catches every pause point. powershell Start-Process -FilePath 'build/Debug/nudge_tests.exe' \ -ArgumentList '--break=my_test:*', '--unit' -PassThru # Or run it directly in foreground and attach a viewer from another shell. ./build/Debug/nudge_tests.exe --debug --break=my_test:* --unit ``` When a matching `DBG_BREAK` is reached the test thread blocks. The binary prints `[dbg] break "<name>" at <file:line> (world=0x...)` to stderr and keeps servicing TCP until a viewer sends `continue`. ### Driving the viewer ```bash # Interactive ./build/Debug/nudge_viewer.exe localhost 9999 # Scripted (pipe commands via stdin) (echo "where"; echo "summary"; echo "get body_state"; echo "continue"; echo "quit") \ | ./build/Debug/nudge_viewer.exe localhost 9999 ``` Typical inspection loop at a break: ``` > where OK break="my_test:frame30" at src/tests_weld_bridge_unit.c:83 world=0x... > summary # frame, body count, contact count, islands > get body_hot 1 # velocities / angular velocities of body 1 > get body_state 2 # position + rotation of body 2 > table body_hot # spreadsheet view of all bodies > contacts 1 # solver contacts touching body 1 > continue # resume; fires again at the next matching break ``` ### Flow between breaks After `continue` the test resumes and runs until the next matching pause point (or the test ends). The viewer prompt returns immediately; send `info` or `where` to check whether you're at a new break, still running, or the binary has exited. Tighten the filter at runtime with `break-filter <pattern>` if you only care about later breaks in the same run. ### Disabling without editing Leaving `DBG_BREAK(...)` calls in checked-in tests is fine -- they cost one predicate check when `--debug` isn't passed. You don't need to `#ifdef` them out. Skipping past a compiled break without editing the source: set the filter so the name doesn't match (`--break=somethingElse`) or omit `--break` entirely. ## Companion modules - `physics-debug` -- deeper methodology reference (5-phase breakdown, break-one-frame-before rule, cdb step-through recipes, impulse-ledger-vs-velocity check, bias guards). Reach for it when the bug is inside a function body and reflection alone isn't enough. - `win-debugger` MCP -- tool-level cdb access for function locals and callstacks. ## Now begin Find the repro first. Use `playrecording` if the user has a recording. Vary inputs. One variable at a time. For unit-test bugs, prefer `DBG_BREAK` + viewer over adding printfs.
عرض على GitHub