{"record":{"id":"a6628fa9029f9e72","repo":"affaan-m/ECC","slug":"workers-must-have-unique-slugs-duplicate-work","errorCode":null,"errorMessage":"Workers must have unique slugs — duplicate: ${workerSlug}","messagePattern":"Workers must have unique slugs — duplicate: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/tmux-worktree-orchestrator.js","lineNumber":203,"sourceCode":"  const coordinationDir = path.join(coordinationRoot, sessionName);\n  const baseRef = config.baseRef || 'HEAD';\n  const defaultLauncher = config.launcherCommand || '';\n\n  if (workers.length === 0) {\n    throw new Error('buildOrchestrationPlan requires at least one worker');\n  }\n\n  const seenSlugs = new Set();\n  const workerPlans = workers.map((worker, index) => {\n    if (!worker || typeof worker.task !== 'string' || worker.task.trim().length === 0) {\n      throw new Error(`Worker ${index + 1} is missing a task`);\n    }\n\n    const workerName = worker.name || `worker-${index + 1}`;\n    const workerSlug = slugify(workerName, `worker-${index + 1}`);\n\n    if (seenSlugs.has(workerSlug)) {\n      throw new Error(`Workers must have unique slugs — duplicate: ${workerSlug}`);\n    }\n    seenSlugs.add(workerSlug);\n\n    const branchName = `orchestrator-${sessionName}-${workerSlug}`;\n    const worktreePath = path.join(worktreeRoot, `${repoName}-${sessionName}-${workerSlug}`);\n    const workerCoordinationDir = path.join(coordinationDir, workerSlug);\n    const taskFilePath = path.join(workerCoordinationDir, 'task.md');\n    const handoffFilePath = path.join(workerCoordinationDir, 'handoff.md');\n    const statusFilePath = path.join(workerCoordinationDir, 'status.md');\n    const launcherCommand = worker.launcherCommand || defaultLauncher;\n    const workerSeedPaths = normalizeSeedPaths(worker.seedPaths, repoRoot);\n    const seedPaths = normalizeSeedPaths([...globalSeedPaths, ...workerSeedPaths], repoRoot);\n    const templateVariables = buildTemplateVariables({\n      branch_name: branchName,\n      handoff_file: handoffFilePath,\n      repo_root: repoRoot,\n      session_name: sessionName,\n      status_file: statusFilePath,","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/tmux-worktree-orchestrator.js#L185-L221","documentation":"Thrown inside the workers.map loop when slugify(worker.name) (or the fallback worker-N) produces a slug that has already been seen in this plan. Slugs become branch names (orchestrator-<session>-<slug>), worktree paths, and coordination directory names — duplicates would collide on the filesystem and in git. The check uses a Set of seen slugs, so the second occurrence triggers the error.","triggerScenarios":"Two workers named 'Worker' both slugify to 'worker'; workers named 'auth-fix' and 'auth fix' (the space becomes a hyphen, colliding with the explicitly-named 'auth-fix'); workers with no name that both fall back to 'worker-1' (this can happen if name is omitted on multiple entries — though normally the index differs, explicit duplicate names are the usual cause); names that differ only in punctuation/case.","commonSituations":"A config generator stamps every worker with a generic name; copy-paste of a worker block without renaming; case-insensitive thinking ('Alpha' and 'alpha' both slugify to 'alpha'); names that differ only in characters slugify strips (e.g. 'auth.fix' and 'auth fix' and 'auth-fix').","solutions":["Give every worker a unique name that produces a unique slug.","Compute slugs upstream and de-duplicate before calling buildOrchestrationPlan: add a numeric suffix on collision.","Avoid relying on the worker-N fallback when name is omitted — give explicit names.","Remember slugify lowercases, replaces non-[a-z0-9] runs with '-', and trims leading/trailing dashes; design names accordingly."],"exampleFix":"// before\nworkers: [\n  { name: 'Auth Fix', task: '...' },\n  { name: 'auth-fix', task: '...' },   // same slug\n]\n// -> Workers must have unique slugs — duplicate: auth-fix\n\n// after\nworkers: [\n  { name: 'auth-fix-tests', task: '...' },\n  { name: 'auth-fix-impl',  task: '...' },\n]","handlingStrategy":"validation","validationCode":"function slugify(value, fallback = 'worker') {\n  const n = String(value || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');\n  return n || fallback;\n}\n\nfunction dedupeWorkerNames(workers) {\n  const seen = new Set();\n  return workers.map((w, i) => {\n    let slug = slugify(w.name || `worker-${i + 1}`);\n    while (seen.has(slug)) slug += `-${i + 1}`;\n    seen.add(slug);\n    return { ...w, name: w.name || slug };\n  });\n}\n\nconfig.workers = dedupeWorkerNames(config.workers);","typeGuard":"function hasUniqueSlugs(workers) {\n  const slugs = workers.map((w, i) => slugify(w.name || `worker-${i + 1}`));\n  return new Set(slugs).size === slugs.length;\n}","tryCatchPattern":"try {\n  buildOrchestrationPlan(config);\n} catch (error) {\n  if (/Workers must have unique slugs/.test(error.message)) {\n    config.workers = dedupeWorkerNames(config.workers);\n    buildOrchestrationPlan(config);\n    return;\n  }\n  throw error;\n}","preventionTips":["Slugify names yourself and de-duplicate by appending a numeric suffix on collision.","Remember slugify is case-insensitive and strips punctuation — 'Auth Fix' and 'auth-fix' collide.","Give every worker an explicit, distinctive name rather than relying on the worker-N fallback.","Add a uniqueness unit test for any worker-list builder."],"tags":["orchestration","validation","naming","slug"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}