ruvnet/ruflo · error · Error

project flywheel anchor requires at least 4 labelled tasks

Error message

project flywheel anchor requires at least 4 labelled tasks

What it means

Thrown by parseTasks() when parsed.tasks is either not an array or contains fewer than 4 entries. The flywheel harness requires a minimum of 4 labelled retrieval tasks to make the benchmark statistically meaningful; fewer tasks flatten the objective and make the result indistinguishable from 'already optimal'. This is a hard floor on anchor quality, not a recommendation.

Source

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

  const actual = realpathSync(absolute);
  const physical = relative(root, actual);
  if (physical === '..' || physical.startsWith(`..${sep}`) || isAbsolute(physical)) {
    throw new Error('flywheel anchor symlink escapes project root');
  }
  return actual;
}

function parseTasks(path: string): { version: string; tasks: HumanEvalTask[] } {
  const parsed = JSON.parse(readFileSync(path, 'utf8')) as {
    schemaVersion?: string;
    version?: string;
    tasks?: HumanEvalTask[];
  };
  if (parsed.schemaVersion && parsed.schemaVersion !== PROJECT_ANCHOR_SCHEMA) {
    throw new Error(`unsupported flywheel anchor schema: ${parsed.schemaVersion}`);
  }
  if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {
    throw new Error('project flywheel anchor requires at least 4 labelled tasks');
  }
  const ids = new Set<string>();
  for (const [index, task] of parsed.tasks.entries()) {
    if (!task || typeof task.id !== 'string' || !/^[A-Za-z0-9._-]{1,128}$/.test(task.id)) {
      throw new Error(`invalid anchor task id at index ${index}`);
    }
    if (ids.has(task.id)) throw new Error(`duplicate anchor task id: ${task.id}`);
    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 };
}

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Add labelled tasks until the array has at least 4 entries, each with a valid id, q, and non-empty labels.
  2. Verify the key is spelled exactly "tasks" (plural) and is a JSON array, not an object or wrapper.
  3. Run JSON.parse on the file and assert Array.isArray(tasks) && tasks.length >= 4 before pointing the harness at it.

Example fix

// before
{ "schemaVersion": "ruflo.flywheel-anchor/v1", "tasks": [{"id":"t1","q":"...","labels":["x"]}] }
// after — add at least 3 more labelled tasks
{ "schemaVersion": "ruflo.flywheel-anchor/v1", "tasks": [
  {"id":"t1","q":"...","labels":["x"]},
  {"id":"t2","q":"...","labels":["y"]},
  {"id":"t3","q":"...","labels":["z"]},
  {"id":"t4","q":"...","labels":["w"]}
]}
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'fs';
function assertEnoughTasks(path: string): void {
  const parsed = JSON.parse(readFileSync(path, 'utf8'));
  if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {
    throw new Error(`anchor needs >=4 tasks, has ${Array.isArray(parsed.tasks) ? parsed.tasks.length : 0}`);
  }
}

Type guard

function hasMinTasks(parsed: unknown): boolean {
  if (typeof parsed !== 'object' || parsed === null) return false;
  const tasks = (parsed as { tasks?: unknown }).tasks;
  return Array.isArray(tasks) && tasks.length >= 4;
}

Try / catch

try {
  loadEffectiveFlywheelAnchor(root, opts);
} catch (e) {
  if (e instanceof Error && /requires at least 4 labelled tasks/.test(e.message)) {
    // add more labelled tasks to the anchor, then retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Loading an anchor file whose tasks field is missing, is a non-array (e.g. an object), is an empty array, or has 1-3 entries. Also fires when the JSON key is mistyped (e.g. "task" instead of "tasks") so parsed.tasks is undefined.

Common situations: Starting with a stub anchor file containing one sample task during scaffolding and forgetting to expand it; renaming the "tasks" key; truncating a generated anchor during a copy-paste; a CI-generated anchor that filtered out invalid tasks down to <4.

Related errors


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