{"record":{"id":"19e5c1737fd669cc","repo":"santifer/career-ops","slug":"non-canonical-status-status-defaulting","errorCode":null,"errorMessage":"⚠️  Non-canonical status \"${status}\" → defaulting to \"Evaluated\"","messagePattern":"⚠️  Non-canonical status \"(.+?)\" → defaulting to \"Evaluated\"","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"merge-tracker.mjs","lineNumber":206,"sourceCode":"    // Spanish → English\n    'evaluada': 'Evaluated', 'condicional': 'Evaluated', 'hold': 'Evaluated', 'evaluar': 'Evaluated', 'verificar': 'Evaluated',\n    'aplicado': 'Applied', 'enviada': 'Applied', 'aplicada': 'Applied', 'applied': 'Applied', 'sent': 'Applied',\n    'respondido': 'Responded',\n    'entrevista': 'Interview',\n    'oferta': 'Offer',\n    'rechazado': 'Rejected', 'rechazada': 'Rejected',\n    'contratado': 'Hired', 'contratada': 'Hired', 'accepted': 'Hired', 'accept': 'Hired',\n    'descartado': 'Discarded', 'descartada': 'Discarded', 'cerrada': 'Discarded', 'cancelada': 'Discarded',\n    'no aplicar': 'SKIP', 'no_aplicar': 'SKIP', 'skip': 'SKIP', 'monitor': 'SKIP',\n    'geo blocker': 'SKIP',\n  };\n\n  if (aliases[lower]) return aliases[lower];\n\n  // DUPLICADO/Repost → Discarded\n  if (/^(duplicado|dup|repost)/i.test(lower)) return 'Discarded';\n\n  console.warn(`⚠️  Non-canonical status \"${status}\" → defaulting to \"Evaluated\"`);\n  return 'Evaluated';\n}\n\n// normalizeVia (Unicode-aware Via/agency key, #1596/#1603) lives in\n// tracker-parse.mjs so merge-tracker and analyze-patterns share ONE normalizer\n// and agency identity can't drift between scripts. (normalizeCompany lives in\n// tracker-utils.mjs since #1460 so every tracker writer shares one company key.)\n\n/**\n * Extract the bracketed report number from a Markdown report link.\n *\n * Report-number equality is an exact duplicate signal, but only after company\n * equality is confirmed by the caller. This helper reads links such as\n * `[123](../reports/123-company-role-date.md)` and returns the numeric id.\n *\n * Layouts whose header has no dedicated Report column (e.g. a customized\n * `… | Materials | Apply Link | Follow-up | Notes` shape) embed the link in\n * Notes prose instead (see buildRow), so the Notes cell is scanned as a","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/merge-tracker.mjs#L188-L224","documentation":"merge-tracker.mjs's validateStatus() normalizes TSV status values to canonical states (see templates/states.yml): it checks an alias table, special-cases DUPLICADO/repost → Discarded, and for anything unrecognized warns and defaults to 'Evaluated'. An unrecognized status silently reshapes the row's lifecycle state in the tracker.","triggerScenarios":"A tracker-addition TSV in batch/tracker-additions/ (or legacy row) whose status column holds a value that matches neither a canonical state nor an alias — e.g. 'sent last week', 'in progress', a localized non-aliased word, or a status with markdown bold.","commonSituations":"Hand-written TSV rows, imports from another tracker with different state names, typos like 'Evaluted' or lowercase variants missing from the alias table.","solutions":["Replace the status value in the TSV with a canonical state from templates/states.yml (Evaluated, Applied, Responded, Interview, Offer, Hired, Rejected, Discarded, SKIP)","Use set-status.mjs afterwards to move the row to the correct state via the canonical write path","If the value is a recurring localized synonym, it may merit an alias entry rather than per-row fixes","Run node normalize-statuses.mjs to sweep existing non-canonical statuses in the tracker"],"exampleFix":"// before\ncat\t2026-09-16\tAcme\tEngineer\tEvaluated?\t4.2/5\t✅\t[42](reports/042-...)\n// after\ncat\t2026-09-16\tAcme\tEngineer\tEvaluated\t4.2/5\t✅\t[42](reports/042-...)","handlingStrategy":"validation","validationCode":"const CANONICAL = ['Evaluated','Applied','Responded','Interview','Offer','Hired','Rejected','Discarded','SKIP'];\nif (!CANONICAL.includes(status)) throw new Error('Non-canonical status: ' + status);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Copy status values only from templates/states.yml when writing TSV rows","Never put markdown bold or dates in the status field","Use set-status.mjs for status changes instead of hand-editing","Run normalize-statuses.mjs periodically to catch drift"],"tags":["tracker","status-normalization","tsv","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}