{"record":{"id":"a0ecc47d29ae412a","repo":"JuliusBrussee/caveman","slug":"cave-host-sandbox-lock-ineligible","errorCode":"cave_host_sandbox_lock_ineligible","errorMessage":"cave_host_sandbox_lock_ineligible","messagePattern":"cave_host_sandbox_lock_ineligible","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/build.ts","lineNumber":227,"sourceCode":"  evidence: Array<{ fixture: string; seed: number; value: RunEvidence }>;\n  costPerTask: number;\n  errorRate: number;\n  p95Latency: number;\n  fixturePassRate: number;\n  retentionLCB95: number;\n  passing: boolean;\n};\n\nexport async function compile(input: CompileInput): Promise<CompileResult> {\n  // An unsandboxed run cannot produce a lock. Host mode runs tool\n  // closures in the host process, so its evidence shows no containment and is\n  // refused here rather than downgraded to a soft status. The whole definition\n  // graph is checked, not just the root: a host subagent under a fixture root\n  // runs its closures in this process exactly as a host root would, and the CLI\n  // saves that lock all the same. Coding agents lock by compiling against\n  // fixture corpora with a contained sandbox mode.\n  if (graphUsesHostSandbox(input.agent)) {\n    throw new Error(\"cave_host_sandbox_lock_ineligible\");\n  }\n  const approved = input.evals.filter((fixture) => fixture.approved && fixture.required);\n  if (approved.length === 0) {\n    return emptyResult(\"needs_eval\", \"no approved required eval fixture\");\n  }\n  const seeds = input.seeds ?? [1, 2, 3, 4, 5];\n  if (seeds.length === 0 || new Set(seeds).size !== seeds.length) {\n    throw new Error(\"caveman build: seeds must be a non-empty unique set\");\n  }\n  const candidates = input.candidates ?? generateCandidatePlans(\n    input.agent,\n    input.contextIR,\n    input.baselinePlan,\n    input.modelCandidates ?? [input.baselinePlan.model],\n    true,\n    new Map(),\n    ENGINE_TRANSFORM_CAPABILITIES,\n    new Set(input.evals.some((fixture) =>","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/build.ts#L209-L245","documentation":"compile refuses to produce a lock when the agent definition graph uses the host sandbox mode, because host sandboxes execute closures in the calling process and a saved lock would overstate containment. The entire definition graph is checked, not just the root, so a host subagent nested under a fixture root also triggers this. This is a hard error, never downgraded to a soft status.","triggerScenarios":"Calling compile with input.agent whose graph (root or any subagent/closure) resolves to a host sandbox mode — e.g. coding-agent closures running in-process — then attempting to save the compile result as a lock.","commonSituations":"Compiling an agent that embeds host-executing tool closures; a fixture-root agent that delegates to a host-mode subagent; switching a subagent's sandbox to host after a previously lockable config.","solutions":["Reconfigure the offending agent/subagents to a contained sandbox mode before compiling for a lock","Inspect the whole agent graph (not just the root) for host sandbox usage and eliminate it","If in-process execution is required, skip lock compilation for this agent and run it unlocked","Split the lockable contained portion from host-executing portions into separate agents"],"exampleFix":"// before\ncompile({ agent: graphWithHostSubagent, evals, config }); // throws\n// after\nconst contained = withSandboxMode(graphWithHostSubagent, \"contained\");\ncompile({ agent: contained, evals, config });","handlingStrategy":"try-catch","validationCode":"// pre-check the graph for host sandbox usage if the library exposes inspection\n// otherwise treat compile as the check itself and avoid saving locks on throw\ntry { const result = compile(input); } catch (e) { /* lock ineligible */ }","typeGuard":null,"tryCatchPattern":"try {\n  const result = compile(input);\n  saveLock(result);\n} catch (e) {\n  if (e instanceof Error && e.message === \"cave_host_sandbox_lock_ineligible\") {\n    // run unlocked; audit the agent graph for host sandbox subagents\n  } else throw e;\n}","preventionTips":["Audit every subagent's sandbox mode, not just the root's","Keep lock-target agents fully on contained sandbox modes","Never assume a fixture root makes nested host subagents lockable"],"tags":["sandbox","lock","security-policy"],"backgroundTag":"unsupported-operation","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"}