{"record":{"id":"dbee56bcedf62be1","repo":"ruvnet/ruflo","slug":"project-flywheel-anchor-requires-at-least-4-labell","errorCode":null,"errorMessage":"project flywheel anchor requires at least 4 labelled tasks","messagePattern":"project flywheel anchor requires at least 4 labelled tasks","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/harness-project-anchor.ts","lineNumber":95,"sourceCode":"  const actual = realpathSync(absolute);\n  const physical = relative(rootPhysical, actual);\n  if (physical === '..' || physical.startsWith(`..${sep}`) || isAbsolute(physical)) {\n    throw new Error('flywheel anchor symlink escapes project root');\n  }\n  return actual;\n}\n\nfunction parseTasks(path: string): { version: string; tasks: HumanEvalTask[] } {\n  const parsed = JSON.parse(readFileSync(path, 'utf8')) as {\n    schemaVersion?: string;\n    version?: string;\n    tasks?: HumanEvalTask[];\n  };\n  if (parsed.schemaVersion && parsed.schemaVersion !== PROJECT_ANCHOR_SCHEMA) {\n    throw new Error(`unsupported flywheel anchor schema: ${parsed.schemaVersion}`);\n  }\n  if (!Array.isArray(parsed.tasks) || parsed.tasks.length < 4) {\n    throw new Error('project flywheel anchor requires at least 4 labelled tasks');\n  }\n  const ids = new Set<string>();\n  for (const [index, task] of parsed.tasks.entries()) {\n    if (!task || typeof task.id !== 'string' || !/^[A-Za-z0-9._-]{1,128}$/.test(task.id)) {\n      throw new Error(`invalid anchor task id at index ${index}`);\n    }\n    if (ids.has(task.id)) throw new Error(`duplicate anchor task id: ${task.id}`);\n    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","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/ruvnet/ruflo/blob/2602b642d92234c710ffbe96bfb33007d481ceab/v3/@claude-flow/cli/src/services/harness-project-anchor.ts#L77-L113","documentation":"A project flywheel anchor must contain at least 4 labelled tasks: `parsed.tasks` must be an array with length >= 4. The floor exists because flywheel promotion decisions are statistical — with fewer than four red/blue tasks the anchor cannot meaningfully distinguish improvement from noise, so the harness refuses to anchor on it.","triggerScenarios":"An anchor tasks JSON with 0–3 entries in its `tasks` array, or `tasks` missing/not an array — often a trimmed example file or a quickly hand-made one.","commonSituations":"Users copying a 2-task example from docs; deleting 'uninteresting' tasks to speed up evals; converting a small test fixture into an anchor.","solutions":["Author at least 4 distinct labelled tasks (each with id, q, labels) representing real retrieval work in the project","If you cannot produce 4 meaningful tasks, the project is not ready for a custom anchor — reconsider running the flywheel harness on it","Lint the file locally (array, length >= 4) before wiring it into a manifest"],"exampleFix":"// before\n{ \"schemaVersion\": \"ruflo.flywheel-anchor/v1\", \"tasks\": [ {t1}, {t2} ] }\n\n// after: four or more labelled tasks\n{ \"schemaVersion\": \"ruflo.flywheel-anchor/v1\", \"tasks\": [ {t1}, {t2}, {t3}, {t4} ] }","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\n\nfunction anchorTaskCountOk(file: string): boolean {\n  const parsed = JSON.parse(readFileSync(file, 'utf8'));\n  return Array.isArray(parsed.tasks) && parsed.tasks.length >= 4;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return loadEffectiveFlywheelAnchor(root, opts);\n} catch (e) {\n  if (e?.message === 'project flywheel anchor requires at least 4 labelled tasks') {\n    throw new Error(`Anchor '${opts.anchorPath}' has fewer than 4 tasks — add labelled tasks until the corpus is statistically usable.`);\n  }\n  throw e;\n}","preventionTips":["Author at least 4 real retrieval tasks before wiring an anchor","Treat a too-small corpus as a design smell, not just a validation error","Lint the tasks file in CI: array present, length >= 4"],"tags":["schema","validation","anchor","minimum-size"],"backgroundTag":"schema-validation-failed","analyzedSha":"2602b642d92234c710ffbe96bfb33007d481ceab","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}