{"record":{"id":"8e1d8b7a3146d2f1","repo":"Hmbown/CodeWhale","slug":"invalid-legacy-pet-recording-world","errorCode":null,"errorMessage":"Invalid legacy pet recording.","messagePattern":"Invalid legacy pet recording\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-world.ts","lineNumber":143,"sourceCode":"\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();\n    c.petCheckpointVersion = 2; c.historyStart = this.tick; c.hasTelemetry = this.hasTelemetry;","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-world.ts#L125-L161","documentation":"Version 1 (legacy) recordings must never carry a start checkpoint, and any checkpoint they do carry must have petCheckpointVersion === 1. fromRecording throws when a legacy recording violates either rule, because v1 has no segmented replay support and mixing checkpoint versions would make deterministic restore impossible.","triggerScenarios":"Calling PetWorld.fromRecording(points, value) with petReplayVersion === 1 and either r.start set, or r.checkpoint whose petCheckpointVersion is not 1.","commonSituations":"Upgrading a v1 recording to include a modern v2 checkpoint; a serialization bug that injects a start field into old recordings; importing recordings exported by a newer app into an older runtime that still tags them version 1.","solutions":["Remove the stray start/checkpoint from the legacy recording, or set checkpoint.petCheckpointVersion to 1 if it truly is a legacy checkpoint.","Re-export the recording as petReplayVersion 2 with a valid v2 start checkpoint if segmentation is intended.","Regenerate the recording with the tooling version that matches its declared petReplayVersion."],"exampleFix":"// before\nconst rec = { petReplayVersion: 1, tape, interactions, start: someV2Checkpoint }; // throws\n// after\nconst rec2 = { petReplayVersion: 2, expressionVersion: 1, tape, interactions, start: someV2Checkpoint };","handlingStrategy":"validation","validationCode":"if (rec.petReplayVersion === 1 && (rec.start || (rec.checkpoint && rec.checkpoint.petCheckpointVersion !== 1)))\n  throw new Error('recording is not a valid legacy v1 blob');","typeGuard":"function isLegacyRecording(r: unknown): r is PetRecording {\n  const rec = r as PetRecording;\n  return rec?.petReplayVersion === 1 && !rec.start && (!rec.checkpoint || rec.checkpoint.petCheckpointVersion === 1);\n}","tryCatchPattern":"try {\n  return PetWorld.fromRecording(points, rec);\n} catch (e) {\n  if (e.message === 'Invalid legacy pet recording.') {\n    // upgrade to v2 or strip the stray checkpoint\n  } else throw e;\n}","preventionTips":["Keep legacy v1 recordings untouched — never inject checkpoints into them.","Migrate v1 to v2 once via a one-way migration script.","Tag recordings with the exporting app version."],"tags":["replay","legacy","version-mismatch"],"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-22T16:17:23.217Z"}