{"record":{"id":"5c3e3781cfb0b5ca","repo":"ruvnet/ruflo","slug":"project-flywheel-anchor-hash-mismatch-got-actua","errorCode":null,"errorMessage":"project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})","messagePattern":"project flywheel anchor hash mismatch \\(got (.+?), pinned (.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/harness-project-anchor.ts","lineNumber":110,"sourceCode":"    ids.add(task.id);\n    if (typeof task.q !== 'string' || task.q.trim().length === 0) {\n      throw new Error(`anchor task ${task.id} has no query`);\n    }\n    if (!Array.isArray(task.labels) || task.labels.length === 0 || task.labels.some((label) => typeof label !== 'string' || !label.trim())) {\n      throw new Error(`anchor task ${task.id} requires non-empty string labels`);\n    }\n  }\n  return { version: parsed.version ?? 'project-anchor-v1', tasks: parsed.tasks };\n}\n\nfunction toSelection(\n  path: string,\n  expectedHash: string,\n): FlywheelAnchorSelection {\n  const parsed = parseTasks(path);\n  const actualHash = humanEvalHash(parsed.tasks);\n  if (actualHash !== normalizeHash(expectedHash)) {\n    throw new Error(`project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})`);\n  }\n  return {\n    version: parsed.version,\n    anchorRef: actualHash,\n    source: 'project',\n    path,\n    tasks: parsed.tasks.map((task) => ({\n      id: task.id,\n      input: { id: task.id, q: task.q },\n      expected: task.labels,\n    })),\n  };\n}\n\nfunction isRufloRepository(projectRoot: string): boolean {\n  try {\n    const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8')) as {\n      name?: string;","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/harness-project-anchor.ts#L92-L128","documentation":"Thrown by toSelection() after parseTasks() succeeds: the sha256 computed by humanEvalHash() over the parsed tasks does not equal the pinned expected hash (after normalizeHash lowercases and adds the 'sha256:' prefix). This is the core tamper/integrity check of project-local anchors — it guarantees the tasks being benchmarked are byte-for-byte the ones that were pinned, so a silent edit (added/removed/changed task, reordered labels, reworded query) cannot pass as the canonical anchor.","triggerScenarios":"Editing any field of an already-pinned anchor task (id, q, labels) or changing the number/order of tasks without recomputing and updating the pinned sha256. The hash comes from the manifest's sha256 field, or from the anchorHash option passed to loadEffectiveFlywheelAnchor.","commonSituations":"Fixing a typo in a task query after the anchor was pinned; adding a 5th task to a 4-task anchor; reordering labels within a task (humanEvalHash is order-sensitive); updating ruvllm/claude-flow which changed the hash algorithm; copying an anchor + manifest pair from another repo whose tasks differ but reusing a stale sha256.","solutions":["Recompute the hash over the current tasks using the same humanEvalHash() function and update the pinned sha256 in the manifest or the anchorHash argument.","If the edit was unintended, revert the anchor file to the version whose hash matches the pin.","Confirm you are hashing the exact parsed-task array shape (id+q+labels) that humanEvalHash expects, not a re-serialized string with different key ordering."],"exampleFix":"// before — manifest pins a sha256 for the old tasks\n{\"path\":\"anchor.json\",\"sha256\":\"sha256:aaaa...\"}\n// after editing anchor.json, recompute and re-pin\nimport { humanEvalHash } from './harness-frozen-eval.js';\nconst tasks = JSON.parse(fs.readFileSync('anchor.json','utf8')).tasks;\nconst newHash = humanEvalHash(tasks); // e.g. 'sha256:bbbb...'\n// write newHash into the manifest's sha256 field","handlingStrategy":"validation","validationCode":"import { humanEvalHash } from './harness-frozen-eval.js';\nimport { readFileSync } from 'fs';\nfunction assertAnchorHash(tasksPath: string, pinned: string): void {\n  const tasks = JSON.parse(readFileSync(tasksPath, 'utf8')).tasks;\n  const actual = humanEvalHash(tasks);\n  const want = pinned.trim().toLowerCase().startsWith('sha256:') ? pinned.trim().toLowerCase() : `sha256:${pinned.trim().toLowerCase()}`;\n  if (actual !== want) {\n    throw new Error(`anchor hash drift: ${actual} != ${want}; re-pin or revert tasks`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  loadEffectiveFlywheelAnchor(root, opts);\n} catch (e) {\n  if (e instanceof Error && /hash mismatch/.test(e.message)) {\n    // either revert tasks to the pinned version, or recompute humanEvalHash(tasks)\n    // and update the manifest's sha256 / the anchorHash argument\n  }\n  throw e;\n}","preventionTips":["Treat the pinned sha256 as load-bearing: any task edit requires re-hashing.","Store both tasks and manifest in the same commit so they never drift.","Add a CI check that recomputes humanEvalHash and compares to the manifest's sha256."],"tags":["flywheel-anchor","integrity","hashing","sha256","configuration"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}