{"record":{"id":"572c2b88f5dcf94d","repo":"Hmbown/CodeWhale","slug":"invalid-pet-score-checkpoint","errorCode":null,"errorMessage":"Invalid pet score checkpoint.","messagePattern":"Invalid pet score checkpoint\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":1567,"sourceCode":"factories[\"pet-audio\"]=function(exports,require){\n\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.PetScore = void 0;\nexports.renderPetPCM = renderPetPCM;\nconst pet_sim_js_1 = require(\"./pet-sim.js\");\nconst model_js_1 = require(\"./model.js\");\n/** Core emits score events; WebAudio / AVAudioEngine only present their PCM.\n * Calling at any display cadence produces the same score when all world ticks\n * are supplied. Calling twice for a world tick cannot retrigger a voice. */\nclass PetScore {\n    lastWindow = -1;\n    lastSequence = -1;\n    lastAddress = false;\n    checkpoint() { return [this.lastWindow, this.lastSequence, this.lastAddress]; }\n    restore(value) {\n        if (!Array.isArray(value) || value.length !== 3\n            || !value.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= pet_sim_js_1.PET_MAX_SECONDS * 2.5) || typeof value[2] !== 'boolean')\n            throw new Error('Invalid pet score checkpoint.');\n        [this.lastWindow, this.lastSequence, this.lastAddress] = value;\n    }\n    voices(frame) {\n        const time = frame.timeMs / 1000, window = Math.floor((frame.timeMs + 1e-7) / 400);\n        const out = [];\n        const add = (id, frequency, duration, gain, pan = 0, delay = 0, kind = 'tone') => out.push({ id, start: time + delay, duration, frequency, gain, pan, kind });\n        const t = frame.telemetry, fresh = t !== undefined && t.sequence !== this.lastSequence;\n        if (fresh) {\n            this.lastSequence = t.sequence;\n            for (let c = 0; c < pet_sim_js_1.CHANNELS.length; c++) {\n                const channel = pet_sim_js_1.CHANNELS[c], n = t.onsets[c];\n                if (!n || channel.sustained || ['human', 'error'].includes(channel.key))\n                    continue;\n                add(`onset:${t.sequence}:${c}`, channel.freq, .24, .035 * Math.min(2, Math.sqrt(n)), (c / 12 - .5) * .7);\n            }\n            if (t.errors)\n                add(`tear:${t.sequence}`, pet_sim_js_1.CHANNELS.find(c => c.key === 'error').freq, .22, .05, 0, 0, 'noise');\n        }","sourceCodeStart":1549,"sourceCodeEnd":1585,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L1549-L1585","documentation":"This score-voice scheduler keeps a checkpoint of its playback position as a 3-element array [lastWindow, lastSequence, lastAddress] and validates it on restore(). The stored window and sequence must be safe integers in [-1, PET_MAX_SECONDS * 2.5] and the address flag a boolean; otherwise it throws 'Invalid pet score checkpoint.' to avoid resuming playback from a corrupt position.","triggerScenarios":"Calling restore() with null/undefined (no checkpoint saved yet), an array of the wrong length, a JSON-round-tripped value where numbers became strings, NaN from a corrupted store, or an out-of-range sequence from an older build with a different PET_MAX_SECONDS.","commonSituations":"Persisting the checkpoint to disk/config and loading it after an upgrade that changed PET_MAX_SECONDS; a JSON serializer turning Infinity/NaN into null; restoring before the first checkpoint() call; handing restore() the wrong object (e.g. the whole state blob instead of the 3-tuple).","solutions":["Guard the restore call: only call restore() with a value previously returned by checkpoint(), else skip and start from the beginning.","Validate before restoring — same predicate as the library (Array length 3, safe integers in range, boolean) — and discard invalid checkpoints.","Clear the stale persisted checkpoint after a version upgrade that changes PET_MAX_SECONDS or the checkpoint shape.","Check the storage layer for JSON serialization of NaN/Infinity becoming null, and store sanitized values."],"exampleFix":"// before\npetScore.restore(saved.checkpoint); // may be null/corrupt\n// after\nconst cp = saved.checkpoint;\nconst ok = Array.isArray(cp) && cp.length === 3\n  && cp.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= PET_MAX_SECONDS * 2.5)\n  && typeof cp[2] === 'boolean';\nif (ok) petScore.restore(cp);","handlingStrategy":"try-catch","validationCode":"const isValidCheckpoint = (v, maxSeconds) =>\n  Array.isArray(v) && v.length === 3 &&\n  v.slice(0, 2).every(n => Number.isSafeInteger(n) && n >= -1 && n <= maxSeconds * 2.5) &&\n  typeof v[2] === 'boolean';","typeGuard":"const isScoreCheckpoint = (v) =>\n  Array.isArray(v) && v.length === 3 &&\n  Number.isSafeInteger(v[0]) && Number.isSafeInteger(v[1]) &&\n  v[0] >= -1 && v[1] >= -1 && typeof v[2] === 'boolean';","tryCatchPattern":"try {\n  score.restore(saved);\n} catch (e) {\n  if (e.message === 'Invalid pet score checkpoint.') {\n    // fall back to a fresh scheduler instead of a corrupt position\n    score = createPetScore();\n  } else {\n    throw e;\n  }\n}","preventionTips":["Only restore values obtained from checkpoint() — check for null before calling","Sanitize persisted checkpoints: JSON turns NaN/Infinity into null, which fails restore","Invalidate stored checkpoints after upgrading PET_MAX_SECONDS or the checkpoint shape","Validate with the same predicate the library uses before calling restore"],"tags":["state","checkpoint","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}