{"record":{"id":"80c663ef71d995e3","repo":"ruvnet/ruflo","slug":"anchor-task-task-id-has-no-query","errorCode":null,"errorMessage":"anchor task ${task.id} has no query","messagePattern":"anchor task (.+?) has no query","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/harness-project-anchor.ts","lineNumber":94,"sourceCode":"    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\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 {","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/harness-project-anchor.ts#L76-L112","documentation":"Thrown per-task when task.q is missing, not a string, or trims to an empty string. The 'q' field is the retrieval query that the flywheel harness evaluates against; an empty query makes the task unmeasurable. The trim check rejects whitespace-only queries, not just literal empty strings.","triggerScenarios":"A task object whose q field is undefined, null, a number, an empty string \"\", or a string of only spaces/tabs/newlines. Also fires when the key is mistyped (e.g. \"query\" instead of \"q\").","commonSituations":"Authoring a task skeleton with a placeholder query like \"\" or \"TODO\"-and-then-deleting it; renaming the field to a more descriptive 'query'/'prompt' key; a templating step that left q blank for tasks whose data hadn't been filled in.","solutions":["Find the task by the id in the message and populate its q field with a non-empty query string.","Confirm the key is the single letter \"q\" (not \"query\", \"prompt\", \"question\").","Add a generation-time check that every task has a non-blank q before emitting the anchor."],"exampleFix":"// before\n{\"id\":\"search-1\",\"q\":\"   \",\"labels\":[\"retrieval\"]}\n// after\n{\"id\":\"search-1\",\"q\":\"How does the auth middleware refresh expired tokens?\",\"labels\":[\"retrieval\"]}","handlingStrategy":"validation","validationCode":"function assertQueries(tasks: { id: string; q?: unknown }[]): void {\n  for (const t of tasks) {\n    if (typeof t.q !== 'string' || t.q.trim().length === 0) {\n      throw new Error(`task ${t.id} has no/blank query`);\n    }\n  }\n}","typeGuard":"function hasQuery(task: unknown): task is { q: string } {\n  return typeof task === 'object' && task !== null\n    && typeof (task as { q?: unknown }).q === 'string'\n    && (task as { q: string }).q.trim().length > 0;\n}","tryCatchPattern":"try {\n  loadEffectiveFlywheelAnchor(root, opts);\n} catch (e) {\n  if (e instanceof Error && /has no query/.test(e.message)) {\n    const id = e.message.match(/task (.+?) has no query/)?.[1];\n    // populate the q field for that task, then retry\n  }\n  throw e;\n}","preventionTips":["Use the exact key 'q' (single letter).","Reject empty/whitespace queries at authoring time.","Add a generator test that every task.q.trim() is non-empty."],"tags":["flywheel-anchor","validation","configuration","json"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}