santifer/career-ops · warning

⚠️ Non-canonical status

Error message

⚠️  Non-canonical status "${status}" → defaulting to "Evaluated"

What it means

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.

Solutions

  1. Replace the status value in the TSV with a canonical state from templates/states.yml (Evaluated, Applied, Responded, Interview, Offer, Hired, Rejected, Discarded, SKIP)
  2. Use set-status.mjs afterwards to move the row to the correct state via the canonical write path
  3. If the value is a recurring localized synonym, it may merit an alias entry rather than per-row fixes
  4. Run node normalize-statuses.mjs to sweep existing non-canonical statuses in the tracker

Example fix

// before
cat	2026-09-16	Acme	Engineer	Evaluated?	4.2/5	✅	[42](reports/042-...)
// after
cat	2026-09-16	Acme	Engineer	Evaluated	4.2/5	✅	[42](reports/042-...)
Defensive patterns

Strategy: validation

Validate before calling

const CANONICAL = ['Evaluated','Applied','Responded','Interview','Offer','Hired','Rejected','Discarded','SKIP'];
if (!CANONICAL.includes(status)) throw new Error('Non-canonical status: ' + status);

Prevention

When it happens

Trigger: 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.

Common situations: Hand-written TSV rows, imports from another tracker with different state names, typos like 'Evaluted' or lowercase variants missing from the alias table.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/19e5c1737fd669cc. Report an issue: GitHub.

Appendix: source

Thrown at merge-tracker.mjs:206

    // Spanish → English
    'evaluada': 'Evaluated', 'condicional': 'Evaluated', 'hold': 'Evaluated', 'evaluar': 'Evaluated', 'verificar': 'Evaluated',
    'aplicado': 'Applied', 'enviada': 'Applied', 'aplicada': 'Applied', 'applied': 'Applied', 'sent': 'Applied',
    'respondido': 'Responded',
    'entrevista': 'Interview',
    'oferta': 'Offer',
    'rechazado': 'Rejected', 'rechazada': 'Rejected',
    'contratado': 'Hired', 'contratada': 'Hired', 'accepted': 'Hired', 'accept': 'Hired',
    'descartado': 'Discarded', 'descartada': 'Discarded', 'cerrada': 'Discarded', 'cancelada': 'Discarded',
    'no aplicar': 'SKIP', 'no_aplicar': 'SKIP', 'skip': 'SKIP', 'monitor': 'SKIP',
    'geo blocker': 'SKIP',
  };

  if (aliases[lower]) return aliases[lower];

  // DUPLICADO/Repost → Discarded
  if (/^(duplicado|dup|repost)/i.test(lower)) return 'Discarded';

  console.warn(`⚠️  Non-canonical status "${status}" → defaulting to "Evaluated"`);
  return 'Evaluated';
}

// normalizeVia (Unicode-aware Via/agency key, #1596/#1603) lives in
// tracker-parse.mjs so merge-tracker and analyze-patterns share ONE normalizer
// and agency identity can't drift between scripts. (normalizeCompany lives in
// tracker-utils.mjs since #1460 so every tracker writer shares one company key.)

/**
 * Extract the bracketed report number from a Markdown report link.
 *
 * Report-number equality is an exact duplicate signal, but only after company
 * equality is confirmed by the caller. This helper reads links such as
 * `[123](../reports/123-company-role-date.md)` and returns the numeric id.
 *
 * Layouts whose header has no dedicated Report column (e.g. a customized
 * `… | Materials | Apply Link | Follow-up | Notes` shape) embed the link in
 * Notes prose instead (see buildRow), so the Notes cell is scanned as a

View on GitHub (pinned to aac998c7ed)