{"record":{"id":"e52c3f1e9cecb001","repo":"JuliusBrussee/caveman","slug":"cave-harness-artifact-digest-invalid","errorCode":"cave_harness_artifact_digest_invalid","errorMessage":"cave_harness_artifact_digest_invalid","messagePattern":"cave_harness_artifact_digest_invalid","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/adapters.ts","lineNumber":97,"sourceCode":"  harness: HarnessID;\n  wireContract: Readonly<Record<string, unknown>>;\n}\n\nexport function createHarnessAdapter(\n  id: HarnessID,\n  identity: HarnessAdapterIdentity,\n  wireContract: Readonly<Record<string, unknown>>,\n  invoke: HarnessInvoke,\n): HarnessAdapter {\n  if (!/^[0-9A-Za-z][0-9A-Za-z._-]{0,63}$/.test(identity.adapterVersion)) {\n    throw new Error(\"cave_harness_adapter_version_invalid\");\n  }\n  if (!/^[0-9A-Za-z][0-9A-Za-z._+-]{0,127}$/.test(identity.upstreamVersion)) {\n    throw new Error(\"cave_harness_upstream_version_invalid\");\n  }\n  if (!/^[0-9a-f]{64}$/.test(identity.bundleSHA256) ||\n      !/^[0-9a-f]{64}$/.test(identity.dependencyLockSHA256)) {\n    throw new Error(\"cave_harness_artifact_digest_invalid\");\n  }\n  const manifest = deepFreeze({\n    schemaVersion: 1 as const,\n    harness: id,\n    adapterVersion: identity.adapterVersion,\n    upstreamVersion: identity.upstreamVersion,\n    bundleSHA256: identity.bundleSHA256,\n    dependencyLockSHA256: identity.dependencyLockSHA256,\n    wireContract: canonicalRecord(wireContract),\n  });\n  const contractSHA256 = sha256(stableStringify(manifest));\n  return Object.freeze({\n    id,\n    version: identity.adapterVersion,\n    manifest,\n    contractSHA256,\n    async run(request: HarnessRequest): Promise<HarnessResult> {\n      const frozenRequest = snapshotRequest(request);","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/adapters.ts#L79-L115","documentation":"`createHarnessAdapter` requires both `identity.bundleSHA256` and `identity.dependencyLockSHA256` to match `^[0-9a-f]{64}$` — exactly 64 lowercase hexadecimal characters. Uppercase hex, a `sha256:` prefix, short git-style SHAs, or an empty value throw before the manifest is frozen. These digests anchor the adapter's artifacts into the manifest and its contractSHA256, so format violations are treated as a broken evidence chain.","triggerScenarios":"Passing `git rev-parse --short HEAD` output (7-12 hex chars); a digest from a CI step that uppercases checksums; container-style `\"sha256:abcd…\"` digests; passing `\"\"` because the dependency lock was never hashed.","commonSituations":"Wiring digests from release pipelines that prefix or uppercase; substituting a git commit SHA for a content digest; hashing the wrong bytes (format passes but later identity checks fail) or forgetting one digest entirely.","solutions":["Compute digests with `crypto.createHash('sha256').update(bytes).digest('hex')` — always 64 lowercase hex","Normalize inputs: `.replace(/^sha256:/, '').toLowerCase()` before constructing the identity","Never substitute a git short SHA or a truncated hash for the full file digest"],"exampleFix":"// before\nbundleSHA256: ciDigest /* \"sha256:9F2AC3…\" uppercase with prefix */\n// after\nbundleSHA256: ciDigest.replace(/^sha256:/, '').toLowerCase()","handlingStrategy":"type-guard","validationCode":"import { createHash } from 'node:crypto';\nconst bundleSHA256 = createHash('sha256').update(bundleBytes).digest('hex');\nconst dependencyLockSHA256 = createHash('sha256').update(lockBytes).digest('hex');\n// digest('hex') is always exactly 64 lowercase hex chars — the contract shape","typeGuard":"const SHA256_HEX_RE = /^[0-9a-f]{64}$/;\nfunction isSha256Hex(v: unknown): v is string {\n  return typeof v === 'string' && SHA256_HEX_RE.test(v);\n}\nfunction validIdentity(i: HarnessAdapterIdentity): boolean {\n  return isSha256Hex(i.bundleSHA256) && isSha256Hex(i.dependencyLockSHA256);\n}","tryCatchPattern":null,"preventionTips":["Always produce digests with createHash('sha256').digest('hex') — never uppercase tool output","Strip 'sha256:' prefixes and lowercase before constructing the identity","Never substitute git short SHAs or truncated hashes for full content digests"],"tags":["typescript","sha256","checksum","validation","build-artifacts"],"backgroundTag":"invalid-checksum-format","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}