{"record":{"id":"81eb2f1ea40ded72","repo":"santifer/career-ops","slug":"outcome-types-mjs-key-is-accepted-by-outcome","errorCode":null,"errorMessage":"outcome-types.mjs: \"${key}\" is accepted by outcome.mjs but has no canonical meaning; add it to ALIASES or CANONICAL_OUTCOMES","messagePattern":"outcome-types\\.mjs: \"(.+?)\" is accepted by outcome\\.mjs but has no canonical meaning; add it to ALIASES or CANONICAL_OUTCOMES","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/outcome-types.mjs","lineNumber":78,"sourceCode":" * result. What stops an omission here is the coverage check below and the drift\n * test, not cleverness.\n */\nconst ALIASES = {\n  stage_reached: 'interview_progress',\n  interview: 'interview_progress',\n  offer: 'offer_received',\n  accepted: 'hired',\n  declined: 'offer_declined',\n  rejection: 'rejected',\n  ghosted: 'no_response',\n};\n\n// A synonym added to OUTCOME_MAP without a meaning here is a real decision, not\n// a spelling. Fail loudly at load rather than let it read as unknown wherever\n// the journal is consumed — which is the failure this module exists to end.\nfor (const key of Object.keys(OUTCOME_MAP)) {\n  if (!CANONICAL_OUTCOMES.includes(key) && !(key in ALIASES)) {\n    throw new Error(`outcome-types.mjs: \"${key}\" is accepted by outcome.mjs but has no canonical meaning; add it to ALIASES or CANONICAL_OUTCOMES`);\n  }\n}\n\n/**\n * Resolve any accepted spelling to its canonical type.\n *\n * Applies the same normalization `outcome.mjs` applies before writing\n * (lowercase, `-` → `_`), so a hand-edited journal saying `Offer-Declined`\n * still resolves.\n *\n * @param {unknown} raw - A type as written in a journal or typed on the CLI.\n * @returns {string|null} The canonical type, or null when unrecognized.\n */\nexport function canonicalOutcome(raw) {\n  const key = String(raw ?? '').trim().toLowerCase().replace(/-/g, '_');\n  if (CANONICAL_OUTCOMES.includes(key)) return key;\n  return ALIASES[key] ?? null;\n}","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/santifer/career-ops/blob/1696bec4d021768e7359f9aad6b329cba883da20/lib/outcome-types.mjs#L60-L96","documentation":"outcome-types.mjs self-validates its own tables at module load: every key in OUTCOME_MAP must either be a canonical outcome or an alias pointing at one. If a synonym was added to OUTCOME_MAP without declaring what it canonically means, the module throws at import time rather than letting the journal consumers silently read it as 'unknown'. This is an internal-consistency invariant for the developer/maintainer of the module, not end users.","triggerScenarios":"Editing OUTCOME_MAP in lib/outcome-types.mjs to add a new synonym string without adding that key to ALIASES or to CANONICAL_OUTCOMES, then importing outcome.mjs.","commonSituations":"Adding a user-requested synonym ('rejected' → 'rejected') to the map but forgetting the ALIASES entry; a merge that brought in a partial key addition; renaming a canonical outcome without updating the map.","solutions":["Add the new key to ALIASES, mapping it to an existing canonical outcome","Or add the key to CANONICAL_OUTCOMES if it is genuinely a new outcome type","Run the module's test suite after any OUTCOME_MAP change to catch this at CI time"],"exampleFix":"// before (in OUTCOME_MAP)\n'declined': null, // added with no meaning declared\n// after (in ALIASES)\n'declined': 'rejected',","handlingStrategy":"validation","validationCode":"// before import, in tests: assert every OUTCOME_MAP key is canonical or an alias\nfor (const k of Object.keys(OUTCOME_MAP)) assert(CANONICAL_OUTCOMES.includes(k) || k in ALIASES, k);","typeGuard":null,"tryCatchPattern":"try { const types = await import('./lib/outcome-types.mjs'); } catch (e) { console.error('outcome-types table inconsistent:', e.message); process.exit(1); }","preventionTips":["Add a unit test that imports outcome-types.mjs so load-time validation runs in CI","When adding a synonym, update OUTCOME_MAP and ALIASES in the same commit","Review OUTCOME_MAP diffs in code review for unmapped keys"],"tags":["module-init","configuration","data-consistency"],"backgroundTag":"schema-validation-failed","analyzedSha":"1696bec4d021768e7359f9aad6b329cba883da20","analyzedAt":"2026-09-01T19:19:23.111Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}