# How Does Career-Ops Use `cv.md` During Evaluation? Inside the 5-Stage Pipeline

> Discover how Career-Ops uses cv.md as the canonical source for candidate experience across five evaluation pipeline stages for consistent and reproducible job assessments.

- Repository: [Santiago Fernández de Valderrama/career-ops](https://github.com/santifer/career-ops)
- Tags: internals
- Published: 2026-08-22

---

**Career-Ops treats [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) as the immutable canonical source of candidate experience, reading it during five distinct pipeline stages—preparation, requirement mapping, PDF tailoring, skill deduplication, and fact verification—to ensure consistent, reproducible job evaluations.**

The `santifer/career-ops` open-source engine implements a strict **single-source-of-truth architecture** where [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) serves as the foundation for all candidate evaluation workflows. Unlike systems that cache or duplicate profile data, Career-Ops reads [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) directly at multiple critical junctures to maintain absolute consistency between the candidate's official history and every generated output.

## Stage 1: Evaluation Preparation and Prompt Construction

The pipeline begins in `web/src/lib/run-prompts.mjs`, where the system constructs the central prompt that drives the **oferta** evaluation mode. At line 25, the prompt explicitly instructs the LLM agent to load [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) before executing any scoring logic.

This preparation step ensures the agent ingests the candidate's complete experience alongside complementary configuration files. The prompt mandates: *"Read [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md), [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) and [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md)"* as prerequisites for analysis.

```javascript
// Excerpt from web/src/lib/run-prompts.mjs (line 25)
1. Read modes/oferta.md and follow it EXACTLY … read cv.md, config/profile.yml …

```

## Stage 2: Block B JD-to-CV Requirement Mapping

The core evaluation logic resides in [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md), where **Block B** performs the critical alignment between job description (JD) requirements and candidate experience. At line 99, this block instructs the LLM to create a quantitative mapping that drives the final fit score.

The mode explicitly directs: *"Read [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md). Create a table with each JD requirement mapped to exact lines in the CV"*. This operation populates the **"Match with CV"** table that appears in evaluation reports, providing traceable evidence for every competency match.

```markdown
<!-- Excerpt from modes/oferta.md (line 99) -->
Read `cv.md`. Create a table with each JD requirement mapped to exact lines in the CV.

```

## Stage 3: PDF Generation and Dynamic Tailoring

When generating tailored PDF outputs via the `pdf` sub-mode, the engine again references [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) as the base document. In `web/src/lib/run-prompts.mjs` (lines 74-76), the pipeline reads [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) to perform **keyword injection**, section re-ordering, and competency-grid construction.

The LLM returns an updated HTML envelope containing the tailored content, which the backend subsequently converts to PDF. This ensures the generated resume remains synchronized with the canonical source while optimizing presentation for specific job applications.

## Stage 4: Skill Deduplication in Upskill Analysis

The `upskill.mjs` utility prevents redundant skill recommendations by parsing [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) to extract the candidate's existing competencies. At line 101, the script invokes `extractSkills()` against the canonical file to build a deduplication set.

```javascript
// Excerpt from upskill.mjs (line 101)
const knownSkills = extractSkills(readFileSync(CV_FILE, "utf8"));

```

By comparing proposed new skills against this `knownSkills` set, the system ensures that **skill-gap analyses only recommend genuinely new competencies**, never duplicating existing entries from the candidate's official history.

## Stage 5: Fact Verification Against Source Truth

The `verify-cv-facts.mjs` script treats [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) as the primary source of truth for validation workflows. When invoked via CLI command, the tool cross-references generated CV content against the original [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) (and optionally [`article-digest.md`](https://github.com/santifer/career-ops/blob/main/article-digest.md)) to detect **fabricated claims or semantic drift**.

```bash

# Verification command syntax

node verify-cv-facts.mjs <generated-cv> --source cv.md

```

This verification step, implemented at line 11 of the script, ensures that all downstream artifacts remain factually consistent with the immutable base document.

## Summary

- **Single-source architecture**: Every evaluation stage reads directly from [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) rather than cached derivatives, eliminating synchronization errors.
- **Traceable mapping**: Block B in [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) creates explicit JD-to-CV line mappings that justify every fit score with specific evidence.
- **Dynamic customization**: PDF generation uses [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) as a template for keyword injection and restructuring while preserving factual accuracy.
- **Intelligent deduplication**: The `upskill.mjs` parser extracts existing skills from [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) to prevent redundant training recommendations.
- **Automated verification**: `verify-cv-facts.mjs` validates generated content against the canonical [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) to catch hallucinations or drift.

## Frequently Asked Questions

### What happens if [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) is missing during evaluation?

The evaluation pipeline will fail to initialize. Because `web/src/lib/run-prompts.mjs` explicitly mandates reading [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) at line 25 before any scoring logic executes, the system requires this file as a mandatory input. The architecture intentionally prevents evaluation without a canonical candidate profile to ensure reproducible results.

### How does Career-Ops prevent the LLM from hallucinating CV details?

The system implements a **fact-verification layer** via `verify-cv-facts.mjs`, which cross-checks generated outputs against the original [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) at line 11. Additionally, Block B's requirement-mapping technique in [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) (line 99) anchors all claims to specific line references in the source file, creating an auditable trail between JD requirements and CV evidence.

### Can I evaluate multiple job postings against the same [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) file?

Yes. Because Career-Ops reads [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) fresh for each evaluation cycle rather than maintaining state, you can run unlimited **oferta** mode evaluations against different job descriptions using the same canonical [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md). Each execution references the immutable source, ensuring consistency across all comparative analyses.

### Does the PDF generation modify the original [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) file?

No. The PDF sub-mode in `run-prompts.mjs` (lines 74-76) reads [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) as input and generates a new HTML envelope for conversion. The original markdown file remains untouched, preserving the **immutable single-source-of-truth** while allowing dynamic customization for specific job applications.