{"record":{"id":"beecf5dbdd954063","repo":"Hmbown/CodeWhale","slug":"invalid-native-whale-body","errorCode":null,"errorMessage":"Invalid native whale body.","messagePattern":"Invalid native whale body\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":27,"sourceCode":"exports.PetNative = void 0;\nconst pet_world_js_1 = require(\"./pet-world.js\");\nconst pet_telemetry_js_1 = require(\"./pet-telemetry.js\");\nconst pet_sim_js_1 = require(\"./pet-sim.js\");\nconst pet_audio_js_1 = require(\"./pet-audio.js\");\nconst pet_engine_js_1 = require(\"./pet-engine.js\");\n/** Synchronous native boundary: JSON state and directly transferable PCM.\n * Native hosts share the actual world / score implementation, not a rewrite. */\nclass PetNative {\n    world;\n    engine = new pet_engine_js_1.PetEngineTelemetry();\n    engineTick = 0;\n    segment;\n    liveTape = new pet_telemetry_js_1.PetLiveTape();\n    stillProjection;\n    constructor(pointsJSON, tapeJSONL = '', interactionsJSON = '[]', live = false, expressionVersion = 2) {\n        const points = JSON.parse(pointsJSON);\n        if (!Array.isArray(points) || points.length !== 980 || points.some(p => !Array.isArray(p) || p.length !== 2 || !p.every(n => Number.isFinite(n) && Math.abs(n) <= 1)))\n            throw new Error('Invalid native whale body.');\n        this.world = new pet_world_js_1.PetWorld(points, live ? (0, pet_telemetry_js_1.compilePetTelemetry)([]) : (0, pet_telemetry_js_1.decodePetJSONL)(tapeJSONL), JSON.parse(interactionsJSON), expressionVersion, true);\n    }\n    step(dt, motion) { this.world.step(dt, { motion, sensitivity: 1 }); return this.snapshot(); }\n    snapshot() { return JSON.stringify({ ...this.world.frame, voices: this.world.voices, digest: (0, pet_sim_js_1.digest)(this.world.sim) }); }\n    /** View-only projection. Display cadence and accessibility preferences never\n     * advance the owner, consume randomness, or change its score/checkpoint. */\n    presentation() {\n        const { sim, frame } = this.world;\n        const state = { ...frame.state, roamX: 0, roamY: 0, flip: 1, lit: frame.behaviour === 'doze' ? .18 : 1 };\n        const key = JSON.stringify([state, frame.pod]);\n        if (this.stillProjection?.key !== key) {\n            const still = new pet_sim_js_1.PetSim(sim.p.map(p => [p.hx, p.hy]), 0xC0FFEE, sim.expressionVersion);\n            const peers = frame.pod.filter(p => p.present);\n            still.step(1 / 30, state, { motion: false, sensitivity: 1,\n                podSlots: peers.length >= 3 ? peers.map(p => [[0, 2, 4, 1, 3, 5][p.slot], p.phase]) : undefined });\n            this.stillProjection = { key, points: still.p.map(p => [p.x, p.y]), style: still.frame };\n        }\n        return JSON.stringify({ ...frame, digest: (0, pet_sim_js_1.digest)(sim), style: sim.frame, activity: this.engine.activity(frame.timeMs),","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L9-L45","documentation":"When the `key` tool resolves to `hold_key`, the duration is validated to be a finite number of seconds between 0.05 and 30. Values that are non-numeric, NaN/Infinity, below the floor, or above the ceiling throw this error before any input is synthesized.","triggerScenarios":"resolveTool(\"key\", {key, duration}) where duration is not a finite number, duration < 0.05, or duration > 30 — e.g. duration: \"2\" (string), duration: 0, duration: 60.","commonSituations":"Passing a duration as a string from JSON config; computing a duration with a formula that yields NaN; trying to hold a key for a minute to simulate a stuck key; zero duration intended as 'momentary' instead of omitting duration.","solutions":["Clamp or choose duration into the 0.05..30 range; for longer holds, chain multiple hold_key calls or use a loop.","Ensure duration is a number, not a string — coerce with Number(duration) and check Number.isFinite before calling.","For a momentary press, omit duration entirely so it resolves as the plain key tool."],"exampleFix":"// before\nresolveTool(\"key\", { key: \"a\", duration: \"1.5\" });\n// after\nconst d = Number(\"1.5\");\nresolveTool(\"key\", { key: \"a\", duration: Math.min(30, Math.max(0.05, d)) });","handlingStrategy":"validation","validationCode":"const d = Number(args.duration);\nif (!Number.isFinite(d) || d < 0.05 || d > 30) {\n  throw new Error(\"duration must be a finite number of seconds in 0.05..30\");\n}","typeGuard":"const isValidDuration = (d) => typeof d === \"number\" && Number.isFinite(d) && d >= 0.05 && d <= 30;","tryCatchPattern":null,"preventionTips":["Coerce string durations with Number() and check Number.isFinite before calling.","Clamp computed durations into 0.05..30; omit duration entirely for momentary presses.","Never pass 0 as 'momentary' — omit duration instead."],"tags":["argument-validation","range-check","computer-use"],"backgroundTag":"value-out-of-range","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}