{"record":{"id":"3e386ded2c7e84bc","repo":"Hmbown/CodeWhale","slug":"invalid-engine-pet-clock-native","errorCode":null,"errorMessage":"Invalid Engine pet clock.","messagePattern":"Invalid Engine pet clock\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/pet-native.js","lineNumber":1729,"sourceCode":"    }\n    /** Transactional batch copy; failed validation cannot accept half a packet. */\n    clone() {\n        const next = new PetEngineTelemetry();\n        const copy = (value) => JSON.parse(JSON.stringify(value));\n        next.events = copy(this.events);\n        const spans = new Map(next.events.map(event => [event.id, event]));\n        // Active and waiting spans must still reference their journal entry so a\n        // later heartbeat extends the coverage consumed by bucket().\n        const span = (event) => spans.get(event.id) ?? copy(event);\n        next.active = new Map(Array.from(this.active, ([key, event]) => [key, span(event)]));\n        next.waiting = this.waiting ? span(this.waiting) : undefined;\n        next.sequence = this.sequence;\n        next.lastTime = this.lastTime;\n        return next;\n    }\n    observe(value, at) {\n        if (!Number.isFinite(at) || at < this.lastTime || at > pet_sim_js_1.PET_MAX_SECONDS * 1000)\n            throw new Error('Invalid Engine pet clock.');\n        if (!value || typeof value !== 'object' || Array.isArray(value))\n            throw new Error('Invalid Engine pet metadata.');\n        const e = value;\n        const allowed = ['event', 'index', 'channel', 'tool_call_id', 'tool_name', 'id', 'worker_status', 'failed'];\n        if (Object.keys(e).some(k => !allowed.includes(k)) || typeof e.event !== 'string'\n            || Object.values(e).some(v => typeof v === 'string' && v.length > 4096)\n            || e.channel !== undefined && !['text', 'reasoning'].includes(e.channel)\n            || ['tool_call_id', 'tool_name', 'id', 'worker_status'].some(k => e[k] !== undefined && typeof e[k] !== 'string')\n            || e.failed !== undefined && typeof e.failed !== 'boolean'\n            || e.index !== undefined && (!Number.isSafeInteger(e.index) || e.index < 0))\n            throw new Error('Invalid Engine pet metadata fields.');\n        this.lastTime = at;\n        this.events = this.events.filter(span => span.endTime >= at - 12_800);\n        const id = (field) => { const s = e[field]; if (typeof s !== 'string' || !s)\n            throw new Error(`Missing Engine ${field}.`); return s; };\n        const index = () => { if (!Number.isSafeInteger(e.index))\n            throw new Error('Missing Engine index.'); return String(e.index); };\n        const start = (key, name, category, agentId) => {","sourceCodeStart":1711,"sourceCodeEnd":1747,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L1711-L1747","documentation":"The Engine pet observer's observe(value, at) validates the observation timestamp. 'at' must be a finite number, >= the last observed time (monotonic clock), and <= PET_MAX_SECONDS * 1000; otherwise 'Invalid Engine pet clock.' is thrown to keep the simulated pet timeline consistent.","triggerScenarios":"observe() called with at = NaN/undefined, a timestamp earlier than a previous observation (out-of-order events), or a timestamp beyond PET_MAX_SECONDS*1000.","commonSituations":"Feeding logs whose timestamps are not monotonic (replayed or reordered journal entries), passing Date.now() (ms epoch) instead of session-relative ms, or corrupted/missing time fields in session metadata.","solutions":["Convert timestamps to session-relative ms and sort events before calling observe","Assert Number.isFinite(at) and at >= lastTime at the call site","Check the timestamp is not an epoch time that exceeds PET_MAX_SECONDS*1000","Drop or clamp out-of-order events instead of feeding them to observe"],"exampleFix":"// before\nengine.observe(meta, entry.wallClockMs);\n// after\nconst at = entry.offsetMs;\nif (!Number.isFinite(at) || at < engine.lastTime || at > PET_MAX_SECONDS * 1000) return;\nengine.observe(meta, at);","handlingStrategy":"validation","validationCode":"function validClock(at, engine, PET_MAX_SECONDS) {\n  return Number.isFinite(at) && at >= engine.lastTime && at <= PET_MAX_SECONDS * 1000;\n}","typeGuard":"const isSessionMs = (t) => typeof t === 'number' && Number.isFinite(t) && t >= 0;","tryCatchPattern":"try {\n  engine.observe(meta, at);\n} catch (err) {\n  if (err.message === 'Invalid Engine pet clock.') {\n    console.warn('non-monotonic or out-of-range timestamp, clamping', at, '>=', engine.lastTime);\n    engine.observe(meta, Math.max(engine.lastTime, Math.min(at, PET_MAX_SECONDS * 1000)));\n  } else throw err;\n}","preventionTips":["Sort all events by timestamp before observing","Convert epoch times to session-relative ms once, at ingestion","Keep a single monotonic clock source for the session","Validate timestamps when parsing log entries, not at observe time"],"tags":["time","validation","monotonic-clock"],"backgroundTag":"value-out-of-range","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"}