{"record":{"id":"a9d7755a5264bdf4","repo":"tinyhumansai/openhuman","slug":"title-must-be-a-non-empty-string","errorCode":null,"errorMessage":"title must be a non-empty string","messagePattern":"title must be a non-empty string","errorType":"validation","errorClass":"SpecError","httpStatus":null,"severity":"error","filePath":"scripts/agent-batch/lib.mjs","lineNumber":114,"sourceCode":"    }\n    if (typeof agent.id !== \"string\" || !/^a\\d{2,3}$/.test(agent.id)) {\n      throw new SpecError(\n        `id must match /^a\\\\d{2,3}$/ (got \"${agent.id}\")`,\n        `${at}.id`,\n      );\n    }\n    if (seenId.has(agent.id))\n      throw new SpecError(`duplicate id \"${agent.id}\"`, `${at}.id`);\n    seenId.add(agent.id);\n    if (!Number.isInteger(agent.issue) || agent.issue <= 0) {\n      throw new SpecError(\"issue must be a positive integer\", `${at}.issue`);\n    }\n    if (seenIssue.has(agent.issue)) {\n      throw new SpecError(`duplicate issue #${agent.issue}`, `${at}.issue`);\n    }\n    seenIssue.add(agent.issue);\n    if (typeof agent.title !== \"string\" || agent.title.trim().length === 0) {\n      throw new SpecError(\"title must be a non-empty string\", `${at}.title`);\n    }\n    const m = BRANCH_RE.exec(agent.branch);\n    if (!m) {\n      throw new SpecError(\n        `branch must match cursor/<id>-<issue>-<slug> (got \"${agent.branch}\")`,\n        `${at}.branch`,\n      );\n    }\n    if (m[1] !== agent.id) {\n      throw new SpecError(\n        `branch id segment \"${m[1]}\" does not match agent id \"${agent.id}\"`,\n        `${at}.branch`,\n      );\n    }\n    if (Number(m[2]) !== agent.issue) {\n      throw new SpecError(\n        `branch issue segment \"${m[2]}\" does not match agent issue ${agent.issue}`,\n        `${at}.branch`,","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/agent-batch/lib.mjs#L96-L132","documentation":"agent.title must be a string that is non-empty after trim() — it becomes the PR/issue human-readable summary, so blank or whitespace-only titles are rejected. Path is \"agents[i].title\". Note the check is trim-based, so a title of \" \" fails but a title with internal spaces is fine.","triggerScenarios":"\"title\": \"\", \"title\": \"   \", title omitted as undefined (caught earlier as missing field), or a non-string title like 42.","commonSituations":"Placeholder titles left blank when the spec was drafted; copy-paste that kept only whitespace; generators writing null or empty strings for missing summaries.","solutions":["Write a short imperative title, e.g. \"Move channels providers behind gate\"","Ensure the JSON value is a non-empty string (not null/number)","If generating specs, default titles from the issue title before validating"],"exampleFix":"// before\n{ \"id\": \"a01\", \"title\": \"\", ... }\n\n// after\n{ \"id\": \"a01\", \"title\": \"Fix CI timeout under load\", ... }","handlingStrategy":"validation","validationCode":"for (const [i, a] of spec.agents.entries()) {\n  if (typeof a.title !== \"string\" || a.title.trim().length === 0) {\n    console.error(`agents[${i}].title must be non-empty`);\n    process.exit(1);\n  }\n}","typeGuard":"/** @param {unknown} v */\nfunction isNonEmptyTitle(v) {\n  return typeof v === \"string\" && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  validateSpec(spec);\n} catch (e) {\n  if (e instanceof SpecError && e.path?.endsWith(\".title\")) {\n    console.error(`write a title for ${e.path}: ${e.message}`);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Default titles from the GitHub issue title when generating specs","Fill titles in as you add agents rather than backfilling at the end"],"tags":["validation","json","spec","agent-batch"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}