{"record":{"id":"d08112608fde8b99","repo":"Hmbown/CodeWhale","slug":"invalid-version-1-pet-bucket-telemetry","errorCode":null,"errorMessage":"Invalid version 1 pet bucket.","messagePattern":"Invalid version 1 pet bucket\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-telemetry.ts","lineNumber":30,"sourceCode":"  durationMs: number;\n  onsets: number[];\n  activeMs: number[];\n  errors: number;\n  agentIds: string[];\n  waiting: boolean;\n}\n\nexport function validatePetBucket(value: unknown): asserts value is PetBucket {\n  validatePetState(value);\n  const b = value as PetBucket;\n  if (!b || typeof b !== 'object' || b.version !== 1 || !Number.isSafeInteger(b.sequence) || b.sequence < 0 || b.sequence > PET_MAX_SECONDS * 2.5\n    || b.simTimeMs !== b.sequence * PET_BIN_MS || b.durationMs !== PET_BIN_MS\n    || !CATEGORIES.includes(b.channel as Category) || typeof b.waiting !== 'boolean'\n    || !Array.isArray(b.agentIds) || b.agentIds.length > 250_000 || b.agentIds.some(id => typeof id !== 'string' || !id || id.length > 4096)\n    || !Number.isSafeInteger(b.errors) || b.errors < 0 || b.errors > 250_000\n    || !Array.isArray(b.onsets) || b.onsets.length !== 13 || b.onsets.some(n => !Number.isSafeInteger(n) || n < 0 || n > 250_000)\n    || !Array.isArray(b.activeMs) || b.activeMs.length !== 13 || b.activeMs.some(n => !Number.isFinite(n) || n < 0 || n > 100_000_000))\n    throw new Error('Invalid version 1 pet bucket.');\n}\n\nexport function decodePetJSONL(text: string): PetBucket[] {\n  if (text.length > 64 * 1024 * 1024) throw new Error('Pet tape exceeds 64 MiB.');\n  const rows = text.split(/\\r?\\n/).filter(line => line.trim()).map(line => JSON.parse(line) as unknown);\n  if (rows.length > 216_000) throw new Error('Pet tape exceeds 24 hours.');\n  return rows.map((row, i) => { validatePetBucket(row); if (row.sequence !== i) throw new Error('Non-contiguous pet tape.'); return row; });\n}\n\n/** A live file must advance before its contents count as a new observation.\n * Existing bytes, duplicate samples and a restarted sequence establish a\n * baseline; they never replay an old onset or human request. Drivers supply a\n * bounded tail and reset this cursor after suspension or a new attachment. */\nexport class PetLiveTape {\n  private sequence: number | undefined;\n  reset(): void { this.sequence = undefined; }\n  readTail(text: string): PetBucket | undefined {\n    if (!text) { this.reset(); return; }","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-telemetry.ts#L12-L48","documentation":"validatePetBucket enforces the version-1 PetBucket schema: exact simTimeMs/durationMs alignment to PET_BIN_MS, a known channel Category, boolean waiting, bounded agentIds (strings ≤4096 chars, ≤250k), safe-integer errors, and 13-element onsets/activeMs arrays within bounds. Any violation throws this error, so corrupt or foreign rows never enter telemetry.","triggerScenarios":"decodePetJSONL, readTail, the telemetry store constructor, or acceptTelemetry receiving a parsed JSON row that fails any of the field checks — wrong bin alignment, unknown channel string, onsets.length !== 13, negative errors, oversized id strings, etc.","commonSituations":"A telemetry writer upgraded to a version-2 schema while the reader still expects v1; manually generated or synthetic JSONL rows with wrong field shapes; truncated lines that parse as JSON but lack fields; clock skew producing simTimeMs !== sequence*PET_BIN_MS.","solutions":["Regenerate the offending row so every field matches the v1 schema (bin-aligned simTimeMs/durationMs, 13-length onsets and activeMs)","Check the writer's schema version — upgrade the reader if the data is intentionally v2","Sanitize ids to ≤4096 chars and counts to within the documented bounds at ingestion","Skip/quarantine the invalid line instead of feeding it to decodePetJSONL/readTail"],"exampleFix":"// before\nbuckets.push({ simTimeMs: Date.now() % PET_BIN_MS, durationMs: 1234, ... });\n// after\nbuckets.push({ simTimeMs: sequence * PET_BIN_MS, durationMs: PET_BIN_MS, onsets: new Array(13).fill(0), activeMs: new Array(13).fill(0), ... });","handlingStrategy":"validation","validationCode":"function isLikelyValidBucket(b: any): boolean {\n  return b && b.simTimeMs === b.sequence * PET_BIN_MS && b.durationMs === PET_BIN_MS\n    && typeof b.waiting === 'boolean'\n    && Array.isArray(b.onsets) && b.onsets.length === 13\n    && Array.isArray(b.activeMs) && b.activeMs.length === 13\n    && Number.isSafeInteger(b.errors) && b.errors >= 0;\n}","typeGuard":"function isPetBucket(v: unknown): v is PetBucket {\n  const b = v as PetBucket;\n  return !!b && typeof b.sequence === 'number' && typeof b.simTimeMs === 'number'\n    && Array.isArray(b.onsets) && b.onsets.length === 13\n    && Array.isArray(b.activeMs) && b.activeMs.length === 13\n    && typeof b.waiting === 'boolean' && Array.isArray(b.agentIds);\n}","tryCatchPattern":"try {\n  validatePetBucket(row);\n} catch (e) {\n  if (e.message === 'Invalid version 1 pet bucket.') {\n    quarantine.push({ line: rawLine, reason: e.message }); // skip bad row, keep going\n  } else throw e;\n}","preventionTips":["Keep writer and reader schema versions in lockstep","Bin-align simTimeMs and set durationMs = PET_BIN_MS at the writer","Always emit 13-element onsets/activeMs arrays","Bound agent id length (≤4096) and counts at ingestion"],"tags":["validation","schema","telemetry"],"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"}