Understanding the Canonical Status Enum in Career-Ops: templates/states.yml and Normalization

The Career-Ops repository defines eight canonical application statuses in templates/states.yml, which the normalize-statuses.mjs script enforces by resolving aliases, stripping markdown formatting, and migrating non-standard values to the notes column.

The santifer/career-ops project maintains data integrity through a strict canonical status enum defined in templates/states.yml. This configuration serves as the single source of truth for both the CLI writer and the dashboard UI, ensuring that the applications.md tracker remains consistent and machine-readable across all workflows.

The Canonical Status Enum Structure in templates/states.yml

The authoritative list of application statuses lives in templates/states.yml (lines 9-56). Both the writer utility and the dashboard UI consume this file to prevent drift between data entry and visualization.

YAML Schema Fields

Each entry in the enum contains five specific fields:

  • id – Machine-readable lowercase identifier (e.g., applied).
  • label – Human-readable title displayed in reports (e.g., Applied).
  • aliases – Language-specific synonyms accepted during import (e.g., Spanish variants).
  • description – Brief explanation of when to use the status.
  • dashboard_group – UI categorization used by the dashboard for grouping.

The Eight Canonical Status Values

The enum defines exactly eight permissible statuses that may appear in the status column:

  1. evaluated – Label: Evaluated, Aliases: evaluada – Offer evaluated, awaiting decision.
  2. applied – Label: Applied, Aliases: aplicado, enviada, aplicada, sent – Application has been sent.
  3. responded – Label: Responded, Aliases: respondido – Company replied, no interview yet.
  4. interview – Label: Interview, Aliases: entrevista – Interview process is active.
  5. offer – Label: Offer, Aliases: oferta – An offer has been received.
  6. rejected – Label: Rejected, Aliases: rechazado, rechazada – Candidate was turned down.
  7. discarded – Label: Discarded, Aliases: descartado, descartada, cerrada, cancelada – Candidate withdrew or posting closed.
  8. skip – Label: SKIP, Aliases: no_aplicar, no aplicar, monitor – Role marked as not a fit; do not apply.

These values are case-insensitive and must appear exactly in column 6 of the applications.md table without markdown formatting, date suffixes, or additional text.

How the Normalization Process Operates

The normalize-statuses.mjs script implements the enforcement logic to ensure every row conforms to the canonical enum defined in templates/states.yml.

The normalizeStatus Function Implementation

According to the source code in lines 28-87, the normalizeStatus function handles the transformation pipeline:

  • Strips bold markup (**) from the status cell content.
  • Maps known patterns such as "DUPLICADO", "CERRADA", "Rechazado", or "Aplicado 2023" to their canonical labels.
  • Processes multilingual aliases including Spanish variants, date suffixes, and special markers like MONITOR or geo blocker.
  • Migrates extraneous data to the notes column when the original text contains modifiers (e.g., preserving "DUPLICADO" as a note).
  • Returns an object containing { status: <canonical>, moveToNotes? } or flags unknown entries for manual review.

The Five-Step Normalization Workflow

  1. File Discovery – Detects data/applications.md or falls back to applications.md in the project root.
  2. Line Parsing – Splits each Markdown table row into columns; expects the status value in the sixth column (index 5).
  3. Status Transformation – Invokes normalizeStatus to clean formatting and resolve aliases according to templates/states.yml.
  4. Row Update – Rewrites the status column with the canonical label, appends migrated notes if necessary, and removes bold formatting from the score column.
  5. Reporting & Backup – Prints a change summary, lists unknown statuses, and writes a backup before committing modifications unless --dry-run is active.

Practical Usage Examples

Command-Line Interface

Run the normalizer from the project root to clean applications.md:


# Preview changes without modifying the file

node normalize-statuses.mjs --dry-run

# Execute normalization with automatic backup creation

node normalize-statuses.mjs

Sample dry-run output:

#12: "Aplicado 2024" → "Applied"
#23: "Evaluar" → "Evaluated"
⚠️  2 unknown statuses:
  #45 (line 78): "Pending"
📊 4 statuses normalized
(dry-run — no changes written)

Programmatic Integration

Import the script execution into Node.js workflows for automated CI/CD pipelines:

import { execSync } from 'child_process';

// Normalize and capture output for logging
const output = execSync('node normalize-statuses.mjs --dry-run', { encoding: 'utf-8' });
console.log(output);

Summary

  • Single Source of Truth: templates/states.yml defines exactly eight canonical statuses with multilingual aliases for the entire Career-Ops workflow.
  • Strict Enforcement: The normalize-statuses.mjs script (lines 28-87) ensures applications.md conforms to the enum by processing column 6 of each table row.
  • Smart Cleaning: Normalization removes bold formatting, resolves Spanish and English aliases, strips date suffixes, and migrates extraneous text to the notes column.
  • Safe Execution: The script creates backups automatically and supports --dry-run for previewing changes without writing to disk.
  • Integration Ready: Both CLI and programmatic execution are supported for use in automated tracking workflows.

Frequently Asked Questions

What happens if I use a status not defined in the canonical enum?

The normalizer flags unknown statuses in the console output and excludes them from automatic conversion. You must manually update these entries to one of the eight canonical values (evaluated, applied, responded, interview, offer, rejected, discarded, or skip) or extend the templates/states.yml file to include the new status with appropriate aliases.

How does the normalizer handle language-specific aliases and date suffixes?

The normalizeStatus function recognizes aliases defined in the aliases field of templates/states.yml, including Spanish variants like aplicado and rechazada. It automatically strips date suffixes (e.g., "Aplicado 2024" → "Applied") and handles special markers like "MONITOR" or "geo blocker" according to the mapping rules in lines 28-87.

Can I run the normalization without modifying the applications.md file?

Yes. Append the --dry-run flag to preview all proposed changes. The script will parse the file and report what would be normalized without writing any modifications to disk or creating backups, allowing you to validate transformations before committing.

Where does the normalization script look for the applications file?

By default, normalize-statuses.mjs searches for data/applications.md first, then falls back to applications.md in the repository root. Ensure your tracker file exists at one of these paths relative to the script execution directory for the normalization to execute successfully.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →