{"record":{"id":"60f501af545e1fe9","repo":"santifer/career-ops","slug":"possible-duplicates-group-map-e-e-num","errorCode":null,"errorMessage":"Possible duplicates: ${group.map(e => `#${e.num}`).join(', ')} (${group[0].company} — ${group[0].role})","messagePattern":"Possible duplicates: (.+?)`\\)\\.join\\(', '\\)\\} \\((.+?) — (.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"verify-pipeline.mjs","lineNumber":162,"sourceCode":"    badStatuses++;\n  }\n}\nif (badStatuses === 0) ok('All statuses are canonical');\n\n// --- Check 2: Duplicates ---\nconst companyRoleMap = new Map();\nlet dupes = 0;\nfor (const e of entries) {\n  // Unicode-aware (#2393): an [a-z0-9] strip erases non-Latin scripts outright,\n  // so every Japanese company and every Japanese role keyed to '' and unrelated\n  // rows were reported as \"possible duplicates\".\n  const key = normalizeTextKey(e.company) + '::' + normalizeTextKey(e.role);\n  if (!companyRoleMap.has(key)) companyRoleMap.set(key, []);\n  companyRoleMap.get(key).push(e);\n}\nfor (const [key, group] of companyRoleMap) {\n  if (group.length > 1) {\n    warn(`Possible duplicates: ${group.map(e => `#${e.num}`).join(', ')} (${group[0].company} — ${group[0].role})`);\n    dupes++;\n  }\n}\nif (dupes === 0) ok('No exact duplicates found');\n\n// --- Check 3: Report links ---\n// Markdown links resolve relative to the file that contains them, so report\n// links must resolve against the tracker's own directory (see #760). For the\n// transition we also accept legacy root-relative links: try the tracker dir\n// first, then fall back to the repo root before flagging a link broken.\nconst TRACKER_DIR = dirname(APPS_FILE);\nlet brokenReports = 0;\nfor (const e of entries) {\n  const match = e.report.match(/\\]\\(([^)]+)\\)/);\n  if (!match) continue;\n  const link = match[1];\n  if (!existsSync(join(TRACKER_DIR, link)) && !existsSync(join(CAREER_OPS, link))) {\n    error(`#${e.num}: Report not found: ${link}`);","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/verify-pipeline.mjs#L144-L180","documentation":"verify-pipeline.mjs check 2 groups tracker rows by a normalized company::role key (Unicode-aware since #2393 -- an [a-z0-9] strip previously keyed every Japanese company/role to '' and flagged unrelated rows) and warns when a key has more than one row. 'Possible' because the project rule forbids duplicate company+role entries, yet genuinely distinct requisitions can legitimately share a title; the warning lists the row numbers so a human can judge.","triggerScenarios":"Two TSVs merged for the same company+role (violating the never-duplicate rule); a reposted role re-applied and tracked twice; two distinct requisitions with identical titles but no distinguishing req/job ID in the notes column.","commonSituations":"Parallel workers independently evaluating the same posting; re-applying to a reposted job months later; leveled variants ('Engineer' vs 'Engineer II') that normalize to the same key.","solutions":["If both rows are the same opening, collapse them: node dedup-tracker.mjs.","If they are distinct requisitions, add the req/job/posting ID to both rows' notes (e.g. 'req JR-10423') so merge-tracker treats them as distinct (#1524) and the pairing is explainable.","Keep each row's report link pointing at its own report file."],"exampleFix":"# before: same title, indistinguishable rows\n| 64 | ... | Acme | ML Engineer | ... | req JR-10423 |\n| 71 | ... | Acme | ML Engineer | ... |              |\n# after: both carry their requisition IDs (distinct openings, warning explainable)\n| 64 | ... | Acme | ML Engineer | ... | req JR-10423 |\n| 71 | ... | Acme | ML Engineer | ... | req JR-10501 |","handlingStrategy":"validation","validationCode":"// Before writing a TSV, check the tracker for an existing company+role row\nfunction normalizeKey(s) { return s.trim().toLowerCase().replace(/[\\s–—-]+/g, ' '); }\nconst existing = entries.some(e =>\n  normalizeKey(e.company) === normalizeKey(company) && normalizeKey(e.role) === normalizeKey(role));\nif (existing && !reqId) throw new Error('company+role already tracked — update the row or add a req ID');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Respect the repo rule: NEVER create a tracker entry if company+role exists — update the existing row instead.","Put the req/job/posting ID in the notes column when a company runs genuinely distinct requisitions under one title (#1524).","Run node dedup-tracker.mjs after merges to collapse accidental duplicates."],"tags":["verify-pipeline","duplicates","tracker","unicode-normalization","requisitions"],"backgroundTag":"duplicate-records","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}