Feedback: Claude can iterate 8+ times on same layer without considering 'wrong layer' as hypothesis
Context
Working on a Roblox game project. A creature physics issue ("Bear bouncing/flipping when hitting walls") was reported. Spent 8 versions iterating in the physics layer:
- v1582: Massless+nocollide for body parts
- v1583/v1584: Reactive upright watcher with raycast Y correction
- v1585: BodyGyro X/Z lock + SetNetworkOwner(nil)
- v1586: Tuned BodyGyro damping
- v1587: Heartbeat positive-Y velocity clamp
- v1588: Aggressive humanoid state disable (broke ground snap → reverted)
- v1589: Restored v1587 + debug print
- v1590: Fixed
humanoidforward-reference bug exposed by debug print - v1591: Tried BodyVelocity Y lock (caused some bears to walk in midair → reverted)
- v1592: Reverted to v1590 acceptable state
Each iteration introduced new edge cases (sinking into ground, walking in midair, oscillation). I marked the residual bouncing as "acceptable" and moved on.
Three turns later, user asked about a different problem: "creatures embed into structures before attacking, AttackRange becomes meaningless." I implemented a forward raycast in the AI layer (v1593-v1594) so creatures stop at AttackRange instead of colliding into walls.
User then reported: "the bouncing also disappeared because creatures no longer touch structures."
The 8 physics-layer iterations were treating a symptom. The root cause was an AI behavior — creatures were ramming walls and the impulse caused vertical bouncing. Fixing the AI fixed the physics issue as a byproduct.
Pattern 1 — Layer blindness
When iterating on a problem in layer X for 3+ turns without convergence, I should naturally generate the hypothesis: "Maybe this isn''t an X-layer problem." Currently I don''t — I keep refining within the layer because each fix produces some improvement, never zero. This is a textbook local minimum.
Suggestion: Some heuristic for the model to surface "you''ve iterated N times in this domain — would it help to step back and consider whether the problem lives elsewhere?" The user shouldn''t have to be the one to suggest it (they often won''t know either, since the symptom genuinely appears in layer X).
Pattern 2 — Closure / forward-reference bugs in Lua
In the same session I generated code with two identical Lua closure forward-reference bugs:
local humanoid = ...declared after aHeartbeat:Connect(function() ... use humanoid ... end)closure → closure capturedhumanoidas global → resolved to nil at runtime → debug log showedfloor=n/a state=n/aeverywherelocal function _reconcilePhysicalInventory(...)declared afterpcall(_reconcilePhysicalInventory, player)call site → silentattempt to call a nil valueat runtime
Both are textbook Lua/Luau scoping mistakes. When writing closures or pcall references that touch outer locals, I should sweep for "is this name actually declared above this line?" Currently I don''t — I write the closure and the local separately and the upvalue capture happens at parse time, before the local exists.
Suggestion: When generating code containing closures or function references in dynamically-scoped languages (Lua, Python late-binding loops), do an explicit pre-emit check: "every name this closure references — is it visible at the point of closure construction?"
Pattern 3 — Discrete-time reasoning gap
When implementing the forward raycast, I picked a buffer of +4 stud past AttackRange. The user had to flag: "half of them still embed — probably a tick interval thing."
The actual math: NEAR tick = 0.5s, WalkSpeed = 13 stud/sec → creatures move 6.5 stud per tick. A 4-stud buffer is mathematically guaranteed to miss ~62% of cases where the creature was 4-10 stud beyond the buffer at the previous tick.
I should have computed buffer ≥ tickInterval × maxSpeed from the start. Instead I picked a vibe number.
Suggestion: When implementing detection / sampling logic in discrete-time systems (game ticks, polling loops, cron windows), explicitly reason about the relationship Δstate ≤ tickInterval × rateOfChange before picking thresholds. This is a one-line sanity check the model could prompt itself to do.
Cost observed
- 8 iterations × ~5-10 minutes = 60+ minutes wall-clock + cache invalidation between sessions
- Two bug-fix versions (v1570, v1590) burnt extra rounds finding the closure bugs from runtime symptoms instead of catching them at write time
- The buffer mistake was caught after ~1 round of partial-success testing
Environment
- Claude Code (Opus 4.7)
- Roblox / Luau project
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗