{"record":{"id":"b787848444d51530","repo":"Hmbown/CodeWhale","slug":"pet-expression-version-does-not-match-its-checkpoint-world","errorCode":null,"errorMessage":"Pet expression version does not match its checkpoint.","messagePattern":"Pet expression version does not match its checkpoint\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-world.ts","lineNumber":142,"sourceCode":"  }\n\n  recording(withCheckpoint = true, completed = false): PetRecording {\n    const tapeEnd = completed ? this.bucketIndex + 1 : this.tapeLog.length;\n    const inputEnd = completed ? this.interactionIndex : this.interactionLog.length;\n    const history = this.historyDigest(tapeEnd, inputEnd);\n    return { petReplayVersion: this.segmented ? 2 : 1, expressionVersion: this.sim.expressionVersion,\n      tape: this.tapeLog.slice(0, tapeEnd), interactions: this.interactionLog.slice(0, inputEnd).map(e => ({ ...e })),\n      ...(this.origin ? { start: { ...structuredClone(this.origin), hasTelemetry: this.hasTelemetry, history } } : {}),\n      ...(withCheckpoint ? { checkpoint: { ...this.checkpoint(), history } } : {}) };\n  }\n\n  static fromRecording(points: [number, number][], value: unknown): PetWorld {\n    const r = value as PetRecording;\n    if (!r || ![1, 2].includes(r.petReplayVersion) || !Array.isArray(r.tape) || !Array.isArray(r.interactions)) throw new Error('Invalid pet recording.');\n    const version = r.expressionVersion === undefined ? 1 : r.expressionVersion;\n    if (![1, 2].includes(version)) throw new Error('Unsupported pet expression version.');\n    if (r.checkpoint !== undefined && !r.checkpoint || r.start !== undefined && !r.start) throw new Error('Invalid pet checkpoint.');\n    for (const c of [r.start, r.checkpoint]) if (c && (c.sim?.expressionVersion ?? 1) !== version) throw new Error('Pet expression version does not match its checkpoint.');\n    if (r.petReplayVersion === 1 && (r.start || r.checkpoint && r.checkpoint.petCheckpointVersion !== 1)) throw new Error('Invalid legacy pet recording.');\n    if (r.petReplayVersion === 2 && (!r.start || r.start.petCheckpointVersion !== 2 || r.start.historyStart !== r.start.tick)) throw new Error('The recording segment is missing its starting checkpoint.');\n    const first = r.start && PetWorld.restore(points, r.tape, r.interactions, r.start);\n    const world = r.checkpoint ? PetWorld.restore(points, r.tape, r.interactions, r.checkpoint) : first ?? new PetWorld(points, r.tape, r.interactions, version);\n    if (first) {\n      if (!world.segmented || world.tick < first.tick || r.checkpoint && r.checkpoint.historyStart !== first.tick) throw new Error('Pet segment checkpoints do not agree.');\n      world.origin = first.checkpoint();\n    }\n    return world;\n  }\n\n  /** Retire only consumed input. The exact origin makes each archived segment\n   * independently replayable; no particle, random stream or score is reset. */\n  prepareSegment(): PetSegment {\n    const dropTape = Math.max(0, this.bucketIndex), dropInputs = this.interactionIndex, previous = this.origin;\n    const tape = this.tapeLog.slice(dropTape), interactions = this.interactionLog.slice(dropInputs);\n    const points = this.sim.p.map(p => [p.hx, p.hy] as [number, number]);\n    const c = this.checkpoint();","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-world.ts#L124-L160","documentation":"PetWorld.fromRecording validates that any start/checkpoint embedded in a recording carries the same expressionVersion as the recording's own declared expression version. If a checkpoint's sim.expressionVersion (defaulting to 1) differs from the resolved top-level version, the recording's segments would replay under divergent behavior models, so the library refuses to build a world. This guards deterministic replay across expression engine upgrades.","triggerScenarios":"Calling PetWorld.fromRecording(points, value) with a recording whose expressionVersion is 1 or 2 while r.start.sim.expressionVersion or r.checkpoint.sim.expressionVersion is a different value (e.g. a checkpoint saved by an older pet build embedded into a newer-versioned recording).","commonSituations":"Mixing recordings and checkpoints from different app versions; hand-editing a recording's top-level expressionVersion without updating the nested checkpoint sim blob; migrating archived segments by changing only one of the two version fields.","solutions":["Re-capture the recording with a single consistent build so the top-level expressionVersion and each checkpoint's sim.expressionVersion agree.","Align the recording's top-level expressionVersion with the nested checkpoint's sim.expressionVersion (or vice versa) before calling fromRecording.","If intentionally upgrading, run the checkpoint through the proper migration path that rewrites both version markers together."],"exampleFix":"// before (mismatched versions)\nconst rec = { petReplayVersion: 2, expressionVersion: 2, tape: [...], interactions: [...], checkpoint: { petCheckpointVersion: 2, sim: { expressionVersion: 1 } } };\nPetWorld.fromRecording(points, rec); // throws\n// after\nrec.checkpoint.sim.expressionVersion = 2;\nPetWorld.fromRecording(points, rec); // ok","handlingStrategy":"validation","validationCode":"const version = r.expressionVersion ?? 1;\nfor (const c of [r.start, r.checkpoint]) {\n  if (c && (c.sim?.expressionVersion ?? 1) !== version)\n    throw new Error('checkpoint expressionVersion mismatch before fromRecording');\n}","typeGuard":"function hasMatchingExpressionVersion(r: unknown): r is PetRecording & { expressionVersion: 1 | 2 } {\n  const rec = r as PetRecording;\n  const v = rec?.expressionVersion ?? 1;\n  return [1, 2].includes(v) && [rec.start, rec.checkpoint].every(c => !c || (c.sim?.expressionVersion ?? 1) === v);\n}","tryCatchPattern":"try {\n  const world = PetWorld.fromRecording(points, rec);\n} catch (e) {\n  if (e.message.includes('does not match its checkpoint')) {\n    // re-capture or migrate the recording to a single expression version\n  } else throw e;\n}","preventionTips":["Never edit a recording's top-level expressionVersion without updating nested checkpoints.","Store recordings and their checkpoints as one atomic blob.","Version the whole recording format, not fields individually."],"tags":["replay","version-mismatch","validation"],"backgroundTag":"incompatible-source-type","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"}