# What Is the Core Architecture of career-ops? A Deep Dive into the System/User Boundary Design

> Explore the core architecture of career-ops, a flat-root, two-layer system. Understand the system/user boundary design and its data contract for AI-agnostic job automation.

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

---

**Career-ops uses a flat-root, two-layer architecture that enforces a strict Data Contract between system code and user data, enabling local-first, AI-agnostic job search automation.**

This architecture powers santifer/career-ops, an open-source job search toolkit that runs entirely on your machine and works with any AI coding CLI. The design ensures your sensitive career data never mixes with the tool's source code, making updates safe and reproducible.

---

## The Two-Layer Data Contract

At the heart of career-ops architecture lies the **Data Contract** documented in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) and [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md). This contract creates an immutable boundary between two layers:

- **System layer** — Contains all tool code: prompts, scripts, templates, dashboards, and `update-system.mjs`. Defined in `SYSTEM_PATHS`.
- **User layer** — Contains your private data: CV, profile, tracker, reports, and job descriptions. Defined in `USER_PATHS`.

Only `update-system.mjs` can modify system files. User data is Never touched during updates. This separation prevents accidental data loss and enables safe self-upgrades.

---

## Core Design Principles

The three principles in [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md)【/cache/repos/github.com/santifer/career-ops/main/ARCHITECTURE.md#L9-L12】 shape every architectural decision:

- **Local-first** — All processing runs against local files. No external service stores your data.
- **AI-agnostic** — Prompt files in `modes/` drive AI logic. Claude Code, Codex, OpenCode, or any AI CLI can execute them.
- **Human-in-the-loop** — The tool prepares content; you review and submit applications.

---

## Architecture Components

The component map in [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md)【/cache/repos/github.com/santifer/career-ops/main/ARCHITECTURE.md#L36-L50】 breaks into these interconnected parts:

| Component | Function | Key Files |
|-----------|----------|-----------|
| **AI Interface** | Reads prompt files and executes commands | [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md), CLI entry files |
| **Prompt Engine** | Scoring, evaluation, application logic | `modes/*.md`, [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) |
| **Job Discovery** | Zero-token job source scanning | `scan.mjs`, `providers/` |
| **Evaluation Pipeline** | A-H block structured scoring | [`oferta.md`](https://github.com/santifer/career-ops/blob/main/oferta.md), [`_shared.md`](https://github.com/santifer/career-ops/blob/main/_shared.md) |
| **Generation Tools** | PDF, LaTeX CV, cover letter creation | `generate-*.mjs`, `templates/` |
| **Tracking System** | Canonical application tracker | [`data/applications.md`](https://github.com/santifer/career-ops/blob/main/data/applications.md), `merge-tracker.mjs` |
| **Updater** | Safe system self-updates | `update-system.mjs` |

---

## Data Flow Through the System

A typical run follows this sequence, visualized in [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md)【/cache/repos/github.com/santifer/career-ops/main/ARCHITECTURE.md#L78-L86】:

1. **Scan** — `node scan.mjs` pulls public listings via ATS APIs or `providers/` modules → writes to [`data/pipeline.md`](https://github.com/santifer/career-ops/blob/main/data/pipeline.md)

2. **Evaluate** — `node oferta.mjs` loads [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md), reads [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md), [`_profile.md`](https://github.com/santifer/career-ops/blob/main/_profile.md), and the JD → produces `reports/NNN-*.md`

3. **Track** — `reserve-report-num.mjs` and `merge-tracker.mjs` atomically update [`data/applications.md`](https://github.com/santifer/career-ops/blob/main/data/applications.md)

4. **Generate** — `generate-pdf.mjs`, `build-cv-html.mjs` create application materials

5. **Human Review** — You inspect reports and decide whether to apply

---

## Key Script Entry Points

These top-level scripts implement the architecture's operations:

```bash

# Discover jobs without API tokens

node scan.mjs

# Evaluate a specific job posting

codex exec "oferta https://company.com/jobs/123"

# Generate PDF from current CV

node generate-pdf.mjs

# Reserve batch report numbers for parallel processing

node reserve-report-num.mjs --count 5   # Output: 042-046

```

All scripts enforce the system/user boundary and are located in the repository root alongside [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md).

---

## Quality Assurance Mechanisms

The architecture includes automated safeguards described in [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md)【/cache/repos/github.com/santifer/career-ops/main/ARCHITECTURE.md#L88-L92】:

- **`test-all.mjs`** — Runs 500+ validation checks
- **`updater-migration-tests.mjs`** — Verifies system/user separation during updates
- **CI pipeline** — CodeQL, CodeRabbit, and Renovate for continuous validation

---

## Multi-CLI Compatibility

Each AI coding CLI reads a dedicated tiny entry file that forwards to [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md)【/cache/repos/github.com/santifer/career-ops/main/ARCHITECTURE.md#L72-L74】. This design lets the same core logic run on Claude Code, OpenCode, Gemini, and future CLIs without modification.

---

## Critical Source Files

Understanding these files unlocks the full architecture:

| File | Purpose |
|------|---------|
| [`ARCHITECTURE.md`](https://github.com/santifer/career-ops/blob/main/ARCHITECTURE.md) | Canonical design documentation and component map |
| [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) | Formal system/user boundary specification |
| [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) | Scoring logic, spend-tier routing, global rules |
| [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) | Evaluation prompt with A-H block structure |
| `scan.mjs` | Zero-token job discovery entry point |
| `providers/` | Per-job-board modules |
| `generate-pdf.mjs` / `build-cv-html.mjs` | Playwright-based document generation |
| `merge-tracker.mjs` | Atomic TSV tracker updates |
| `update-system.mjs` | Safe self-update implementation |
| [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md) | Open-agent-skill definition for all CLIs |

---

## Summary

- Career-ops architecture centers on a **two-layer Data Contract** enforcing strict separation between system code and user data
- **Local-first processing** ensures no external service ever holds your career information
- **AI-agnostic prompt files** in `modes/` enable compatibility across any AI coding CLI
- **Human-in-the-loop design** keeps you in control of final application decisions
- **Safe updates** through `update-system.mjs` modify only system paths listed in `SYSTEM_PATHS`

---

## Frequently Asked Questions

### What makes career-ops "local-first"?

All job discovery, evaluation, and document generation runs on your machine using local files. Your CV, profile, and application history never leave your system or get stored by external services. This is enforced by the Data Contract's `USER_PATHS` definition and implemented in scripts like `scan.mjs` and `generate-pdf.mjs`.

### How does the system/user boundary prevent data loss?

`USER_PATHS` explicitly lists directories and files that `update-system.mjs` will never touch. This means running `node update-system.mjs` to get the latest career-ops features cannot accidentally overwrite your CV, tracker, or reports. The `updater-migration-tests.mjs` suite continuously validates this boundary.

### Can I use career-ops with AI tools other than Claude Code?

Yes. The architecture is **AI-agnostic** by design. Prompt logic lives in `modes/*.md` files that any AI CLI can read. Each supported tool has a thin entry file that routes to [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md), the canonical skill definition. This works with Codex, OpenCode, Gemini, and compatible alternatives.

### What happens if I run scripts in the wrong order?

The tracker system uses atomic operations via `reserve-report-num.mjs` and `merge-tracker.mjs` to prevent corruption. However, best practice follows the documented flow: scan → evaluate → track → generate → review. This sequence ensures [`data/pipeline.md`](https://github.com/santifer/career-ops/blob/main/data/pipeline.md) feeds [`oferta.md`](https://github.com/santifer/career-ops/blob/main/oferta.md) evaluation, which correctly populates [`data/applications.md`](https://github.com/santifer/career-ops/blob/main/data/applications.md) before document generation.