{"record":{"id":"1725bb559f7e8c2c","repo":"Hmbown/CodeWhale","slug":"world-dt-must-be-in-0-10-seconds","errorCode":null,"errorMessage":"World dt must be in [0, 10] seconds.","messagePattern":"World dt must be in \\[0, 10\\] seconds\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":464,"sourceCode":"            this.hashTape(index);\n            return;\n        }\n        if (sequence >= 216_000)\n            throw new Error('Archive the legacy recording before accepting more telemetry.');\n        const changedFrom = Math.min(sequence, this.tapeLog.length);\n        while (this.tapeLog.length <= sequence) {\n            const at = this.tapeLog.length;\n            this.tapeLog.push({ version: 1, sequence: at, simTimeMs: at * 400, durationMs: 400,\n                activity: .12, coherence: .25, attention: 0, channel: 'other', observed: 0, roamX: 0, roamY: 0, flip: 1, lit: 1,\n                onsets: Array(13).fill(0), activeMs: Array(13).fill(0), errors: 0, agentIds: [], waiting: false });\n        }\n        this.tapeLog[sequence] = { ...structuredClone(input), sequence, simTimeMs: sequence * 400 };\n        this.hashTape(changedFrom);\n    }\n    /** dt is bounded so suspending a surface cannot cause an unbounded catch-up. */\n    step(dt, opts = { motion: true, sensitivity: 1 }) {\n        if (!Number.isFinite(dt) || dt < 0 || dt > 10)\n            throw new Error('World dt must be in [0, 10] seconds.');\n        this.accumulator += dt;\n        this.voices = [];\n        while (this.accumulator + 1e-10 >= 1 / HZ) {\n            this.accumulator -= 1 / HZ;\n            const time = ++this.tick / HZ;\n            this.frame = this.makeFrame(time);\n            this.voices.push(...this.score.voices(this.frame));\n            if (!opts.motion) {\n                this.frame.state = { ...this.frame.state, roamX: 0, roamY: 0, flip: 1, lit: this.behaviour === 'doze' ? .18 : 1 };\n                this.frame.surface = -.86;\n                this.frame.caustic = .5;\n                if (this.frame.food)\n                    this.frame.food.y = this.food.y;\n            }\n            const signature = JSON.stringify(this.frame.state);\n            const peers = this.frame.pod.filter(p => p.present);\n            const podSlots = peers.length >= 3 ? peers.map(p => [[0, 2, 4, 1, 3, 5][p.slot], p.phase]) : undefined;\n            if (opts.motion || signature !== this.lastStill || podSlots)","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L446-L482","documentation":"step(dt) advances the world by a bounded delta time so a suspended or backgrounded surface cannot trigger an unbounded catch-up simulation when it resumes. dt must be a finite number of seconds in [0, 10]. NaN, Infinity, negatives, or anything above 10 seconds is rejected before the accumulator grows.","triggerScenarios":"Calling world.step(dt) with NaN or Infinity (e.g. from a broken clock delta), a negative dt from a clock that jumped backwards, or a huge dt such as the raw elapsed seconds after the tab was suspended overnight.","commonSituations":"Computing dt as (now - last) after a system sleep or tab suspension; performance.now() units confusion (ms passed where s expected, giving dt=5000); a paused animation frame returning undefined dt; monotonic-clock rollback in VMs.","solutions":["Clamp dt before calling: `dt = Math.min(Math.max(dt, 0), 10)`.","Compute dt from a monotonic clock in seconds and reset the last-timestamp after suspension so the first frame after resume uses a small dt.","Treat NaN/undefined clock readings as 0 (or skip the frame) instead of forwarding them."],"exampleFix":"// before\nworld.step((now - last) / 1000); // ~5400 after overnight suspend\n// after\nlet dt = (now - last) / 1000;\nlast = now;\ndt = Number.isFinite(dt) ? Math.min(Math.max(dt, 0), 10) : 0;\nworld.step(dt);","handlingStrategy":"validation","validationCode":"function safeStep(world, dt) {\n  const d = Number(dt);\n  if (!Number.isFinite(d) || d < 0 || d > 10) return false;\n  world.step(d);\n  return true;\n}","typeGuard":"const isFiniteDt = (dt) => typeof dt === 'number' && Number.isFinite(dt) && dt >= 0 && dt <= 10;","tryCatchPattern":"try {\n  world.step(dt);\n} catch (e) {\n  if (e.message.includes('World dt must be')) {\n    world.step(0); // skip the bad frame rather than crashing the loop\n  } else throw e;\n}","preventionTips":["Clamp clock deltas to [0, 10] before every step call","Use a monotonic clock and reset the last timestamp after suspension","Convert ms to s carefully — performance.now() deltas need /1000","Treat NaN/undefined dt as 0 or skip the frame"],"tags":["validation","time","frame-loop"],"backgroundTag":"argument-out-of-range","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}