{"record":{"id":"3ec2f586c5d4bdf6","repo":"paperclipai/paperclip","slug":"maintained-live-campaign-must-contain-unique-enabl","errorCode":null,"errorMessage":"Maintained live campaign must contain unique enabled rosters","messagePattern":"Maintained live campaign must contain unique enabled rosters","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs","lineNumber":106,"sourceCode":"  const campaign = await loadObject(campaignPath);\n  if (\n    campaign.schema !== \"paperclip-runner/live-campaign/v1\" ||\n    !Array.isArray(campaign.lanes)\n  ) {\n    throw new Error(`Unsupported live campaign schema in ${campaignPath}`);\n  }\n  const selected = campaign.lanes\n    .filter((lane) => lane.executionClass !== \"disabled\")\n    .map((lane) => {\n      const rosterPath = inside(\n        programRoot,\n        resolve(dirname(campaignPath), String(lane.roster ?? \"\")),\n        \"Campaign roster\",\n      );\n      return basename(rosterPath);\n    });\n  if (selected.length === 0 || new Set(selected).size !== selected.length) {\n    throw new Error(\n      \"Maintained live campaign must contain unique enabled rosters\",\n    );\n  }\n  return new Set(selected);\n}\n\nexport async function buildProtocolEvalCatalog({\n  evalsRoot,\n  rosterSelection = \"all\",\n  campaignId,\n  source = {},\n  maxParallel = 100,\n}) {\n  safeId(campaignId, \"campaign ID\");\n  if (\n    !Number.isSafeInteger(maxParallel) ||\n    maxParallel < 2 ||\n    maxParallel > 100","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs#L88-L124","documentation":"After mapping the campaign's enabled (non-'disabled' executionClass) lanes to their roster basenames, maintainedRosterSelection requires the resulting selection to be non-empty and contain no duplicate rosters. Two lanes pointing at the same roster file, or zero enabled lanes, trigger this error.","triggerScenarios":"campaigns/live-direct-full.json has multiple enabled lanes whose lane.roster resolve to the same roster file, or every lane has executionClass 'disabled' (or there are no lanes).","commonSituations":"Copy-pasting a lane entry to add a variant but forgetting to point it at a different roster; accidentally duplicating a lane; disabling all lanes while iterating and then running the campaign.","solutions":["Deduplicate lanes so each enabled lane references a distinct roster file.","Re-enable at least one lane (set executionClass to something other than 'disabled').","Use the explicit roster selection flag instead of the maintained campaign if you intentionally want a subset."],"exampleFix":"// before\n{ \"lanes\": [ { \"roster\": \"rosters/a.json\" }, { \"roster\": \"rosters/a.json\" } ] }\n// after\n{ \"lanes\": [ { \"roster\": \"rosters/a.json\" }, { \"roster\": \"rosters/b.json\" } ] }","handlingStrategy":"validation","validationCode":"const enabled = campaign.lanes.filter((l) => l.executionClass !== \"disabled\").map((l) => basename(resolve(dirname(campaignPath), l.roster)));\nif (enabled.length === 0 || new Set(enabled).size !== enabled.length) throw new Error(\"Enabled lanes must reference unique rosters\");","typeGuard":"const hasUniqueEnabledRosters = (campaign) => {\n  const rosters = (campaign.lanes ?? []).filter((l) => l.executionClass !== \"disabled\").map((l) => l.roster);\n  return rosters.length > 0 && new Set(rosters).size === rosters.length;\n};","tryCatchPattern":"try {\n  const selection = await maintainedRosterSelection(programRoot);\n} catch (err) {\n  if (err.message.includes(\"unique enabled rosters\")) console.error(\"Check campaigns/live-direct-full.json for duplicated lanes or all-disabled lanes.\");\n  throw err;\n}","preventionTips":["Keep a one-lane-per-roster rule when authoring campaigns.","Never commit a campaign with zero enabled lanes.","Review lane diffs for accidental copy-paste duplicates."],"tags":["campaign","duplicates","validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}