{"record":{"id":"6b0d88d257aef20c","repo":"JuliusBrussee/caveman","slug":"caveman-build-dataresidency-is-not-enforced-yet","errorCode":null,"errorMessage":"caveman build: dataResidency is not enforced yet; refusing to ignore residency policy","messagePattern":"caveman build: dataResidency is not enforced yet; refusing to ignore residency policy","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/build.ts","lineNumber":28,"sourceCode":"export interface BuildConfig {\n  entry: string;\n  evals: string;\n  efficiency: \"max\";\n  requiredFixturePassRate: number;\n  qualityRetention: number;\n  maxSearchCostUsd: number;\n  lock: \"strict\";\n  sandbox: \"required\";\n  allowedModels?: string[];\n  deniedModels?: string[];\n  maxP95LatencyMs?: number;\n  forbiddenSafetyClasses?: string[];\n  dataResidency?: string;\n}\n\nexport function defineBuild(options: Partial<BuildConfig> & Pick<BuildConfig, \"entry\" | \"evals\">): BuildConfig {\n  if (options.dataResidency !== undefined) {\n    throw new Error(\n      \"caveman build: dataResidency is not enforced yet; refusing to ignore residency policy\",\n    );\n  }\n  const config: BuildConfig = {\n    entry: options.entry,\n    evals: options.evals,\n    efficiency: options.efficiency ?? \"max\",\n    requiredFixturePassRate: options.requiredFixturePassRate ?? 1,\n    qualityRetention: options.qualityRetention ?? 0.98,\n    maxSearchCostUsd: options.maxSearchCostUsd ?? 2,\n    lock: options.lock ?? \"strict\",\n    sandbox: options.sandbox ?? \"required\",\n    ...(options.allowedModels === undefined ? {} : { allowedModels: [...options.allowedModels] }),\n    ...(options.deniedModels === undefined ? {} : { deniedModels: [...options.deniedModels] }),\n    ...(options.maxP95LatencyMs === undefined ? {} : { maxP95LatencyMs: options.maxP95LatencyMs }),\n    ...(options.forbiddenSafetyClasses === undefined ? {} : { forbiddenSafetyClasses: [...options.forbiddenSafetyClasses] }),\n  };\n  if (!(config.requiredFixturePassRate > 0 && config.requiredFixturePassRate <= 1)) {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/build.ts#L10-L46","documentation":"Thrown by defineBuild when the build config sets dataResidency. The build system does not yet enforce data-residency policy, and silently accepting a residency constraint it cannot honor would be a false security guarantee — so it refuses the config outright. This is a deliberate fail-closed: the option exists in the type so callers can see it is recognized, but setting it is rejected until enforcement ships.","triggerScenarios":"Passing dataResidency: \"eu\" (or any value) in the defineBuild options object; merging a shared org config that includes dataResidency into build options; upgrading to a version whose BuildConfig type exposes the field and enabling it speculatively.","commonSituations":"Compliance-driven teams adding residency constraints preemptively; config templates that enumerate every typed field with placeholder values; porting a config from a tool that does support residency.","solutions":["Remove dataResidency from the options until the library enforces it; track the library changelog for the enforcing release.","If residency is a hard requirement, enforce it at your own layer: restrict provider/model selection via allowedModels to providers whose regions you have verified.","Guard with a version check or feature detection so the config can enable the field automatically once supported."],"exampleFix":"// before\ndefineBuild({ entry, evals, dataResidency: \"eu\" });\n\n// after\ndefineBuild({ entry, evals, allowedModels: [\"eu-hosted-model-a\"] }); // enforce residency via model allowlist","handlingStrategy":"validation","validationCode":"const supported = (() => { try { defineBuild({ entry: \"x\", evals: [], dataResidency: \"probe\" }); return true; } catch { return false; } })();\n// wait — this always throws today; instead gate on a feature constant or semver check:\nimport { version } from \"@caveman/agent\";\nconst residencySupported = false; // flip when the enforcing release ships","typeGuard":"function buildOptionsWithoutResidency<T extends { dataResidency?: string }>(o: T): Omit<T, \"dataResidency\"> {\n  const { dataResidency: _ignored, ...rest } = o;\n  return rest;\n}","tryCatchPattern":"try {\n  defineBuild(options);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"dataResidency\")) {\n    throw new Error(\"dataResidency is not enforced by this version; restrict allowedModels to compliant providers instead\");\n  }\n  throw e;\n}","preventionTips":["Do not populate dataResidency in config templates until the library enforces it; the field is a reservation, not a working knob.","If residency is mandatory, approximate it with allowedModels restricted to verified-region providers and document the manual control.","Watch release notes: remove your own enforcement shim when official support lands."],"tags":["build","config","compliance","data-residency","unsupported-feature"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}