ruvnet/ruflo · error · Error

project flywheel anchor hash mismatch (got ${actualHash}, pi

Error message

project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})

What it means

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.

Source

Thrown at v3/@claude-flow/cli/src/services/harness-project-anchor.ts:110

    ids.add(task.id);
    if (typeof task.q !== 'string' || task.q.trim().length === 0) {
      throw new Error(`anchor task ${task.id} has no query`);
    }
    if (!Array.isArray(task.labels) || task.labels.length === 0 || task.labels.some((label) => typeof label !== 'string' || !label.trim())) {
      throw new Error(`anchor task ${task.id} requires non-empty string labels`);
    }
  }
  return { version: parsed.version ?? 'project-anchor-v1', tasks: parsed.tasks };
}

function toSelection(
  path: string,
  expectedHash: string,
): FlywheelAnchorSelection {
  const parsed = parseTasks(path);
  const actualHash = humanEvalHash(parsed.tasks);
  if (actualHash !== normalizeHash(expectedHash)) {
    throw new Error(`project flywheel anchor hash mismatch (got ${actualHash}, pinned ${normalizeHash(expectedHash)})`);
  }
  return {
    version: parsed.version,
    anchorRef: actualHash,
    source: 'project',
    path,
    tasks: parsed.tasks.map((task) => ({
      id: task.id,
      input: { id: task.id, q: task.q },
      expected: task.labels,
    })),
  };
}

function isRufloRepository(projectRoot: string): boolean {
  try {
    const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8')) as {
      name?: string;

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Recompute the hash over the current tasks using the same humanEvalHash() function and update the pinned sha256 in the manifest or the anchorHash argument.
  2. If the edit was unintended, revert the anchor file to the version whose hash matches the pin.
  3. 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.

Example fix

// before — manifest pins a sha256 for the old tasks
{"path":"anchor.json","sha256":"sha256:aaaa..."}
// after editing anchor.json, recompute and re-pin
import { humanEvalHash } from './harness-frozen-eval.js';
const tasks = JSON.parse(fs.readFileSync('anchor.json','utf8')).tasks;
const newHash = humanEvalHash(tasks); // e.g. 'sha256:bbbb...'
// write newHash into the manifest's sha256 field
Defensive patterns

Strategy: validation

Validate before calling

import { humanEvalHash } from './harness-frozen-eval.js';
import { readFileSync } from 'fs';
function assertAnchorHash(tasksPath: string, pinned: string): void {
  const tasks = JSON.parse(readFileSync(tasksPath, 'utf8')).tasks;
  const actual = humanEvalHash(tasks);
  const want = pinned.trim().toLowerCase().startsWith('sha256:') ? pinned.trim().toLowerCase() : `sha256:${pinned.trim().toLowerCase()}`;
  if (actual !== want) {
    throw new Error(`anchor hash drift: ${actual} != ${want}; re-pin or revert tasks`);
  }
}

Try / catch

try {
  loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e instanceof Error && /hash mismatch/.test(e.message)) {
    // either revert tasks to the pinned version, or recompute humanEvalHash(tasks)
    // and update the manifest's sha256 / the anchorHash argument
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/5c3e3781cfb0b5ca. Report an issue: GitHub.