# Career-Ops Data Contract: User Layer vs System Layer File Separation

> Discover the Career-Ops data contract separating user and system layer files. Learn how this ensures your customizations are safe from updates.

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

---

**Career-Ops enforces a strict data contract that separates personal user data (User Layer) from core application logic (System Layer) to ensure updates never overwrite your customizations.**

The open-source Career-Ops repository (`santifer/career-ops`) implements a defensive architecture that protects your personal information during automated maintenance. This **data contract between user layer and system layer files** is formally defined in [[`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md)](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) and guarantees that system updates can refresh core logic without touching your CV, preferences, or generated reports.

## Understanding the Two-Layer Architecture

Career-Ops classifies every file into exactly one of two mutually exclusive layers. This separation creates a hard boundary: the **User Layer** contains irreplaceable personal data, while the **System Layer** contains disposable code and templates that can be safely replaced from upstream.

- **User Layer**: Personal CVs, configuration profiles, custom negotiation scripts, application tracking data, and generated outputs. These files are protected from any automated modification.
- **System Layer**: Mode definitions, plugin infrastructure, utility scripts, documentation, and templates. These files may be overwritten during updates without data loss.

## User Layer Files (Never Auto-Updated)

Files in the User Layer hold personal identity and workflow customizations. According to the source code, the update process **must never read, modify, or delete** these files.

### Personal Data and Configuration

The following paths store your unique professional identity and targets:

- [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) – Your primary Markdown CV.
- [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) – Personal identity, target roles, and compensation goals.
- [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) – User-specific archetypes and negotiation scripts.
- [`modes/_custom.md`](https://github.com/santifer/career-ops/blob/main/modes/_custom.md) – Procedural workflow preferences.
- [`voice-dna.md`](https://github.com/santifer/career-ops/blob/main/voice-dna.md) – Stylistic guardrails for generated content.
- [`article-digest.md`](https://github.com/santifer/career-ops/blob/main/article-digest.md) – Proof-points from your portfolio.
- `interview-prep/` – STAR stories and company-specific preparation files.
- [`portals.yml`](https://github.com/santifer/career-ops/blob/main/portals.yml) – Your curated list of job portals.

### Generated Output and Plugins

This layer also protects dynamic content and private extensions:

- [`config/plugins.yml`](https://github.com/santifer/career-ops/blob/main/config/plugins.yml) – Plugin activation toggles (seeded initially but user-controlled).
- `plugins.local/` and `plugins.lock` – Private plugins and integrity pins.
- `data/` – Application tracker, pipeline inbox, scan history, and follow-up logs.
- `writing-samples/*` – User-provided writing examples.
- `reports/*` and `output/*` – Generated evaluation reports and PDFs.
- `jds/*` – Saved job descriptions for analysis.

When writing system scripts that interact with these files, you must append or modify without fear of later overwriting:

```javascript
// File: scripts/add-company.mjs (System Layer)
import fs from 'fs';
import path from 'path';

export function addPortal(name, url) {
  const portalsPath = path.resolve('portals.yml');   // User-layer file
  const entry = `- name: ${name}\n  url: ${url}\n`;
  fs.appendFileSync(portalsPath, entry, 'utf8');
}

```

## System Layer Files (Safe to Update)

The System Layer contains the engine that powers Career-Ops. These files are designed to be ephemeral and can be replaced wholesale during upstream updates.

### Core Logic and Templates

- [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) – Global scoring system and tool definitions.
- Mode files like [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) and [`modes/pdf.md`](https://github.com/santifer/career-ops/blob/main/modes/pdf.md) that drive CLI behavior.
- Language-specific directories (`modes/de/*`, `modes/fr/*`).
- Agent instruction files: [`CLAUDE.md`](https://github.com/santifer/career-ops/blob/main/CLAUDE.md), [`OPENCODE.md`](https://github.com/santifer/career-ops/blob/main/OPENCODE.md), and [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md).
- Plugin engine: `plugins.mjs` and the `plugins/` directory.
- Utility scripts (`*.mjs`) and helper modules.

### Documentation and Assets

- `docs/*` – Project documentation that may be regenerated.
- `templates/*` – UI and PDF templates that evolve with releases.
- [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) – The contract definition itself.

Because these reside in the System Layer, they can be updated without migration scripts:

```javascript
// File: utils/notify.mjs (System Layer)
export function notify(msg) {
  console.log(`[Career-Ops] ${msg}`);
}

```

## Enforcing the Contract in Code

The separation is maintained by update scripts that check a file's layer membership before performing any operation.

### Detecting File Layers Programmatically

The repository includes logic to determine whether a path belongs to the User Layer:

```javascript
// File: update/check-layer.mjs (System Layer)
import path from 'path';

export function isUserLayer(filePath) {
  const userFiles = [
    'cv.md',
    'config/profile.yml',
    'modes/_profile.md',
    'modes/_custom.md',
    'portals.yml',
    // ... additional user-layer paths
  ];
  const rel = path.relative(process.cwd(), filePath);
  return userFiles.some(p => rel.startsWith(p));
}

```

Update mechanisms use this classification to **skip** any file where `isUserLayer()` returns `true`, ensuring compliance with the core rule: *If a file is in the User Layer, no update process may read, modify, or delete it*.

## Summary

- Career-Ops divides all files into a **User Layer** (personal data) and **System Layer** (core logic).
- User Layer files—including [`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), [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md), and the `data/` directory—are permanently protected from automated updates.
- System Layer files—including [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md), `plugins.mjs`, `templates/`, and documentation—can be safely replaced during maintenance.
- The contract is enforced by update scripts that check file paths against the User Layer registry before performing any write operations.

## Frequently Asked Questions

### What happens if I modify a system layer file?

Any modifications to System Layer files (such as [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) or `utils/notify.mjs`) will be lost during the next update, as these files are replaced wholesale from the upstream repository. Store customizations in User Layer files like [`modes/_custom.md`](https://github.com/santifer/career-ops/blob/main/modes/_custom.md) instead.

### Can I add custom files to the user layer?

Yes. You can create new files in User Layer directories such as `interview-prep/` or `writing-samples/`, and they will be protected from updates. However, avoid naming conflicts with System Layer files to prevent classification errors.

### How does the update process protect user data?

The update process references the definitions in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) and uses helper functions like `isUserLayer()` to identify protected paths. If a file matches the User Layer criteria, the update script explicitly skips it, ensuring zero-touch modification of personal data.

### Where is the data contract formally defined?

The definitive source is [[`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md)](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) in the repository root. This System Layer document lists every protected file pattern and establishes the golden rule that User Layer files must never be read, modified, or deleted by automated processes.