{"record":{"id":"fbbbff2d37e21019","repo":"santifer/career-ops","slug":"cross-channel-duplicate-key-replace","errorCode":null,"errorMessage":"Cross-channel duplicate — ${key.replace('::', ' / ')} reached via ${list.map(v => v.raw).join(' AND ')} (rows ${list.map(v => `#${v.num}`).join(', ')}) — double-submission risk, resolve by hand","messagePattern":"Cross-channel duplicate — (.+?) reached via (.+?) \\(rows (.+?)`\\)\\.join\\(', '\\)\\}\\) — double-submission risk, resolve by hand","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"verify-pipeline.mjs","lineNumber":407,"sourceCode":"// and dedup-tracker key agencies with (#2397), so \"Hays\" and \"HAYS \" read as one\n// channel while リクルート and パーソル stay two; the raw spelling is kept for\n// the message. Before this, both non-Latin agencies normalized to '' and fell\n// back to 'direct', hiding exactly the double-submission this check exists for.\nconst normalizeChannel = (v) => normalizeVia(v ?? '') || 'direct';\nconst channelsByRole = new Map();\nfor (const e of entries) {\n  const company = String(e.company || '').trim();\n  if (!company || company === '?') continue;\n  const key = `${company.toLowerCase()}::${String(e.role || '').trim().toLowerCase()}`;\n  if (!channelsByRole.has(key)) channelsByRole.set(key, new Map());\n  const channels = channelsByRole.get(key);\n  const norm = normalizeChannel(e.via);\n  if (!channels.has(norm)) channels.set(norm, { raw: String(e.via || '').trim() || '—', num: e.num });\n}\nfor (const [key, vias] of channelsByRole) {\n  if (vias.size > 1) {\n    const list = [...vias.values()];\n    warn(`Cross-channel duplicate — ${key.replace('::', ' / ')} reached via ${list.map(v => v.raw).join(' AND ')} (rows ${list.map(v => `#${v.num}`).join(', ')}) — double-submission risk, resolve by hand`);\n    viaIssues++;\n  }\n}\nif (viaIssues === 0) ok('Via channels consistent');\n// --- Check 12: Duplicate tracker numbers (#1704) ---\n// The # column is a row id and must be unique. Unlike Check 2 (company+role\n// dedup, which can false-positive on a legitimate re-application), the SAME\n// number appearing on 2+ rows is never legitimate: it means set-status.mjs\n// can't tell the rows apart, and any external reference to \"application #N\"\n// (interview-prep notes, memory, cross-links) becomes ambiguous. Pure\n// addition, no existing check covers this — see #1704 for the 124-row sweep\n// that found this in the wild (merge-tracker.mjs trusted a stale TSV number\n// as-is whenever it exceeded that run's max, without checking it wasn't\n// already used by an unrelated row merged in a separate, earlier invocation).\nconst numGroups = new Map();\nfor (const e of entries) {\n  if (!numGroups.has(e.num)) numGroups.set(e.num, []);\n  numGroups.get(e.num).push(e);","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/verify-pipeline.mjs#L389-L425","documentation":"Error \"Cross-channel duplicate — ${key.replace('::', ' / ')} reached via ${list.map(v => v.raw).join(' AND ')} (rows ${list.map(v => `#${v.num}`).join(', ')}) — double-submission risk, resolve by hand\" thrown in santifer/career-ops.","triggerScenarios":"Thrown at verify-pipeline.mjs:407 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":[],"exampleFix":null,"handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}