{"record":{"id":"56a7ee0583fc764f","repo":"tinyhumansai/openhuman","slug":"batch-size-spec-agents-length-exceeds-hard-cap","errorCode":null,"errorMessage":"batch size ${spec.agents.length} exceeds hard cap of 25","messagePattern":"batch size (.+?) exceeds hard cap of 25","errorType":"validation","errorClass":"SpecError","httpStatus":null,"severity":"error","filePath":"scripts/agent-batch/lib.mjs","lineNumber":78,"sourceCode":"    );\n  }\n  if (spec.base_branch !== \"main\") {\n    throw new SpecError(\n      `base_branch must be \"main\" (got \"${spec.base_branch}\")`,\n      \"base_branch\",\n    );\n  }\n  if (!Number.isInteger(spec.tracking_issue) || spec.tracking_issue <= 0) {\n    throw new SpecError(\n      \"tracking_issue must be a positive integer\",\n      \"tracking_issue\",\n    );\n  }\n  if (!Array.isArray(spec.agents) || spec.agents.length === 0) {\n    throw new SpecError(\"agents must be a non-empty array\", \"agents\");\n  }\n  if (spec.agents.length > 25) {\n    throw new SpecError(\n      `batch size ${spec.agents.length} exceeds hard cap of 25`,\n      \"agents\",\n    );\n  }\n\n  const seenId = new Set();\n  const seenIssue = new Set();\n  const seenBranch = new Set();\n  for (let i = 0; i < spec.agents.length; i++) {\n    const agent = spec.agents[i];\n    const at = `agents[${i}]`;\n    if (!agent || typeof agent !== \"object\" || Array.isArray(agent)) {\n      throw new SpecError(\"must be an object\", at);\n    }\n    for (const key of REQUIRED_AGENT) {\n      if (!(key in agent))\n        throw new SpecError(`missing required field \"${key}\"`, at);\n    }","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/agent-batch/lib.mjs#L60-L96","documentation":"A hard ceiling: spec.agents.length must not exceed 25. The cap bounds how many concurrent branches/issues/PRs one tracking issue can fan out to, keeping review load and CI cost sane. The actual offending length is interpolated into the message and the path is \"agents\".","triggerScenarios":"Passing a spec with 26 or more entries in the agents array to validateSpec or the batch launcher.","commonSituations":"A large mechanical refactor sliced too coarsely (e.g. one agent per domain in the ~31-directory tree plus per-file agents); programmatically generating one agent per item from a long list without chunking.","solutions":["Split the work into two batches of ≤25 agents, each with its own batch_id slug and its own tracking_issue","Merge trivially small agents so each owns more paths (owned_paths supports multiple directory prefixes)","Re-run validateSpec on each sub-batch spec separately"],"exampleFix":"// before: one spec, 30 agents\n{ \"batch_id\": \"big-refactor\", \"agents\": [ ...30 entries... ] }\n\n// after: two specs\n{ \"batch_id\": \"big-refactor-1\", \"agents\": [ ...first 15... ] }\n{ \"batch_id\": \"big-refactor-2\", \"agents\": [ ...next 15... ] }","handlingStrategy":"validation","validationCode":"if (spec.agents.length > 25) {\n  console.error(`batch size ${spec.agents.length} exceeds hard cap of 25 — split into sub-batches`);\n  process.exit(1);\n}","typeGuard":null,"tryCatchPattern":"try {\n  validateSpec(spec);\n} catch (e) {\n  if (e instanceof SpecError && e.path === \"agents\" && /hard cap/.test(e.message)) {\n    const mid = Math.ceil(spec.agents.length / 2);\n    console.error(`split into batches: agents[0..${mid - 1}] and agents[${mid}..]`);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Check the count before detailed authoring so you do not waste effort on a 30-agent spec","Chunk generators at ≤25 and emit one spec file per chunk with its own batch_id/tracking_issue","Prefer fewer agents with broader owned_paths over many tiny agents"],"tags":["validation","json","spec","agent-batch","limits"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}