{"record":{"id":"6f1012cd2d39fcd2","repo":"JuliusBrussee/caveman","slug":"caveman-agent-latency-guardrail-requires-positive","errorCode":null,"errorMessage":"caveman agent: latency guardrail requires positive integer p95_ms","messagePattern":"caveman agent: latency guardrail requires positive integer p95_ms","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/primitives.ts","lineNumber":454,"sourceCode":"  approved?: boolean;\n  required?: boolean;\n  input: unknown;\n  tools?: { mode: \"fixture\" | \"live\"; sandbox?: string };\n  quality: QualityGrader[];\n  guardrails?: EvalGuardrail[];\n}): EvalDefinition {\n  if (options.id.trim() === \"\") throw new Error(\"caveman agent: eval id is required\");\n  if (options.quality.length === 0) throw new Error(\"caveman agent: eval needs at least one quality grader\");\n  const known = new Set([\"contains\", \"tool_called\", \"exact_match\", \"json_schema\"]);\n  for (const grader of options.quality) {\n    if (!known.has(grader.type)) {\n      throw new Error(`caveman agent: unknown grader ${(grader as { type: string }).type}`);\n    }\n  }\n  for (const guardrail of options.guardrails ?? []) {\n    if (guardrail.type === \"latency_threshold\" &&\n        (!Number.isSafeInteger(guardrail.p95_ms) || guardrail.p95_ms <= 0)) {\n      throw new Error(\"caveman agent: latency guardrail requires positive integer p95_ms\");\n    }\n    if (guardrail.type === \"error_rate\" &&\n        (!Number.isFinite(guardrail.max) || guardrail.max < 0 || guardrail.max > 1)) {\n      throw new Error(\"caveman agent: error-rate guardrail max must be in [0,1]\");\n    }\n  }\n  const tools = options.tools ?? { mode: \"fixture\" as const };\n  if (tools.mode === \"live\" && !tools.sandbox) {\n    throw new Error(\"caveman agent: live eval tools require an explicit sandbox\");\n  }\n  return Object.freeze({\n    kind: \"eval\",\n    id: options.id,\n    approved: options.approved ?? false,\n    required: options.required ?? true,\n    input: options.input,\n    tools,\n    quality: Object.freeze([...options.quality]),","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/primitives.ts#L436-L472","documentation":"evalFixture() validates latency_threshold guardrails: p95_ms must be a positive safe integer (Number.isSafeInteger and > 0). It throws when a guardrail of type latency_threshold has a missing, zero, negative, fractional, or non-integer p95_ms. This ensures the p95 latency threshold in milliseconds is a usable value.","triggerScenarios":"evalFixture with guardrails: [{ type: \"latency_threshold\", p95_ms: 0 }], a negative p95_ms, a float like 250.5, NaN/Infinity, or omitting p95_ms entirely on a latency_threshold guardrail.","commonSituations":"Using 0 to mean 'no limit' instead of omitting the guardrail; seconds-vs-milliseconds confusion producing small floats; config values that arrive as strings and fail the integer check after partial coercion.","solutions":["Set p95_ms to a positive integer, e.g. { type: \"latency_threshold\", p95_ms: 2000 }.","Round thresholds: Math.round(seconds * 1000), and enforce >= 1.","Remove the latency_threshold guardrail if you do not intend to enforce a latency bound."],"exampleFix":"// before\n{ type: \"latency_threshold\", p95_ms: 1.5 }\n// after\n{ type: \"latency_threshold\", p95_ms: 1500 }","handlingStrategy":"validation","validationCode":"for (const g of guardrails) {\n  if (g.type === \"latency_threshold\" && !(Number.isSafeInteger(g.p95_ms) && g.p95_ms > 0)) {\n    throw new Error(`latency_threshold p95_ms must be a positive integer, got ${g.p95_ms}`);\n  }\n}","typeGuard":"function isPositiveInt(v: unknown): v is number { return typeof v === \"number\" && Number.isSafeInteger(v) && v > 0; }","tryCatchPattern":"try { ev = evalFixture(opts); }\ncatch (e) { if (String(e.message).includes(\"latency guardrail\")) throw new Error(\"Set p95_ms as a positive integer in milliseconds, e.g. 2000\"); throw e; }","preventionTips":["Always express p95_ms in whole milliseconds (e.g. 2000, not 2 or 1.5).","Convert seconds with Math.round(seconds * 1000).","Omit the guardrail entirely if no latency bound is intended, rather than passing 0."],"tags":["validation","eval","guardrail","argument-validation"],"backgroundTag":"value-out-of-range","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}