{"record":{"id":"693a9984769b04d8","repo":"Hmbown/CodeWhale","slug":"1-the-particle-checkpoint-does-not-match-this-whale","errorCode":"1","errorMessage":"The particle checkpoint does not match this whale.","messagePattern":"The particle checkpoint does not match this whale\\.","errorType":"exception","errorClass":"NSError(CodewhalePet)","httpStatus":null,"severity":"error","filePath":"pet/swift/PetSim.swift","lineNumber":301,"sourceCode":"            q.ang = atan2(q.hy, q.hx)\n            q.rad = (q.hx * q.hx + q.hy * q.hy).squareRoot()\n            q.tail = clamp01(((-q.hx - q.hy) * 0.5 + 0.22) / 0.62)\n            return q\n        }\n        cur = channelIndex(\"reasoning\")!\n        prev = cur\n    }\n\n    func restoreValidated(_ checkpoint: PetParticleCheckpoint) throws {\n        // Array and identity checks also protect this native boundary if its\n        // caller changes. Mutation starts only after the complete shape passes.\n        guard checkpoint.version == 1, [1, 2].contains(checkpoint.expressionVersion ?? 1), checkpoint.body.count == p.count,\n              checkpoint.particles.count == p.count, checkpoint.color.count == 3,\n              CHANNELS.indices.contains(checkpoint.previous), CHANNELS.indices.contains(checkpoint.current),\n              checkpoint.body.enumerated().allSatisfy({ i, v in\n                  v.count == 3 && v[0] == p[i].hx && v[1] == p[i].hy && v[2] == p[i].s\n              }), checkpoint.particles.allSatisfy({ $0.count == 8 && $0.allSatisfy(\\.isFinite) })\n        else { throw NSError(domain: \"CodewhalePet\", code: 1, userInfo: [NSLocalizedDescriptionKey: \"The particle checkpoint does not match this whale.\"]) }\n        expressionVersion = checkpoint.expressionVersion ?? 1\n        phase = checkpoint.phase; clock = checkpoint.clock; tear = checkpoint.tear\n        prev = checkpoint.previous; cur = checkpoint.current\n        col = (checkpoint.color[0], checkpoint.color[1], checkpoint.color[2]); frame = checkpoint.frame\n        for i in p.indices {\n            let v = checkpoint.particles[i]\n            p[i].x = v[0]; p[i].y = v[1]; p[i].vx = v[2]; p[i].vy = v[3]\n            p[i].jx = v[4]; p[i].jy = v[5]; p[i].tx = v[6]; p[i].ty = v[7]\n        }\n    }\n\n    /// Advance the sim by dt seconds under `state`. Identical math to PetSim.ts.\n    public func step(dt: Double, state: PetState, motion: Bool = true, sensitivity: Double = 1, podSlots: [(Int, Double)]? = nil) {\n        let s: (Double) -> Double = { lerp(0.5, $0, sensitivity) }\n        let act = s(state.activity), coh = s(state.coherence), att = s(state.attention)\n        let seen = s(state.observed)\n        let mot = motion ? 1.0 : 0.0\n        phase += dt * (0.18 + act * 0.55) * mot","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/swift/PetSim.swift#L283-L319","documentation":"PetSim throws NSError(domain: \"CodewhalePet\", code: 1) with this message at PetSim.swift:301 when a deserialized particle checkpoint fails structural validation: wrong version, expressionVersion outside {1,2}, body/particle-array length mismatched with the live particle count, color not exactly 3 channels, channel indices out of range, per-particle body triplets not matching the current particle state (hx, hy, s), or non-finite particle values. The store refuses to apply a checkpoint that does not describe this exact whale.","triggerScenarios":"Applying a checkpoint from a different whale/pet instance; loading a save written by an older or newer version with a different particle count or expression version; hand-edited or truncated JSON in the habitat store; corrupted archive segments.","commonSituations":"Restoring a backup from another device whose pet evolved differently; version migration between app releases changed checkpoint layout; file corruption from an interrupted sync.","solutions":["Delete or rename the incompatible `<source>.json` (and stale `-segment-` archives) so the app starts a fresh checkpoint.","Verify the checkpoint belongs to the same whale — do not copy habitat files between accounts/devices.","Upgrade or downgrade the app so its expressionVersion set matches the checkpoint's (accepted: 1 or 2, defaulting to 1).","If you produce checkpoints programmatically, ensure body triplets equal the live particles' (hx, hy, s) and all particle fields are finite before saving."],"exampleFix":"// before: blindly applying a foreign checkpoint\ntry pet.apply(checkpoint)\n\n// after: pre-validate shape and count against the live whale\nguard checkpoint.particles.count == pet.particleCount,\n      checkpoint.version == 1 else {\n    logger.warning(\"checkpoint incompatible; resetting\")\n    try? FileManager.default.removeItem(at: habitatFile)\n    return\n}\ntry pet.apply(checkpoint)","handlingStrategy":"validation","validationCode":"// Validate checkpoint shape before applying\nfunc checkpointMatches(_ c: Checkpoint, p: [Particle]) -> Bool {\n    c.version == 1\n        && [1, 2].contains(c.expressionVersion ?? 1)\n        && c.body.count == p.count && c.particles.count == p.count\n        && c.color.count == 3\n        && c.particles.allSatisfy { $0.count == 8 && $0.allSatisfy(\\.isFinite) }\n}","typeGuard":"func isCheckpointMismatch(_ e: Error) -> Bool {\n    let n = e as NSError\n    return n.domain == \"CodewhalePet\" && n.code == 1\n        && n.localizedDescription.contains(\"checkpoint does not match\")\n}","tryCatchPattern":"do { try pet.apply(checkpoint) }\ncatch let e as NSError where isCheckpointMismatch(e) {\n    logger.warning(\"incompatible checkpoint; resetting habitat\")\n    try? resetHabitat()\n}","preventionTips":["Never copy habitat JSON between different whales or devices.","Version-gate checkpoints when changing particle layout across releases.","Ensure saved particle triplets equal the live (hx, hy, s) and all values are finite."],"tags":["validation","checkpoint","swift","state-restoration"],"backgroundTag":"schema-validation-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}