# How career-ops Ensures User Personalization Is Not Overwritten by System Updates

> Discover how career-ops protects user personalization from system updates using its two-layer architecture and immutable data contracts. Keep your customizations safe and intact.

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

---

**career-ops uses a strict two-layer architecture—User Layer and System Layer—with immutable rules enforced by [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md), [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md), and the `update-system.mjs` script to guarantee that personalized files like [`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) are never modified, deleted, or replaced during updates.**

The **career-ops** repository is a personal career management system built around AI-assisted workflows. Because users invest significant effort customizing their CV narratives, target archetypes, and negotiation scripts, the project implements comprehensive safeguards to ensure that **user personalization survives every system update intact**. These protections are implemented across multiple layers: a declarative data contract, policy enforcement for agents, a carefully designed update mechanism, and proactive health checks.

---

## The Two-Layer Architecture: User Layer vs. System Layer

At the heart of career-ops' protection strategy is a strict separation between what the system owns and what the user owns. This separation is formalized in **[`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md)**, which serves as the single source of truth for layer classification.

### Files That Belong to the User Layer

The User Layer contains all personalization content. According to [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md), these files are **never auto-updated**:

- [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) — Your personalized CV content
- [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) — Core profile configuration
- [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) — Custom archetypes, narratives, and negotiation scripts
- [`modes/_custom.md`](https://github.com/santifer/career-ops/blob/main/modes/_custom.md) — Additional user-defined mode customizations

### Files That Belong to the System Layer

The System Layer contains code, templates, and infrastructure that can be safely replaced:

- [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) — Shared mode infrastructure
- All `*.mjs` scripts — System automation and tooling
- `templates/*` — Default templates shipped with the repository

---

## Immutable User Layer: The Core Rule in AGENTS.md

The **[`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md)** file codifies the non-negotiable rule that protects user personalization. Line 16 explicitly declares:

> "User Layer (NEVER auto-updated; personalization goes HERE)"

This policy is not merely documentation—it is enforced by the update mechanism. **No update process may read, modify, or delete files in the User Layer.** This rule applies equally to automated scripts and human agents operating within the system.

By establishing this policy at the architectural level, career-ops ensures that even future maintainers or unfamiliar contributors cannot accidentally architect an update that touches user files.

---

## The Update Mechanism: How `update-system.mjs` Respects Boundaries

The **`update-system.mjs`** script implements the layer separation at runtime. When executing an update, it consults the Data Contract to determine precisely which files are eligible for replacement.

### How the Update Process Works

1. The script reads [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) to populate two lists:
   - `userFiles` — Files that must be excluded from any update operation
   - `systemFiles` — Files that may be safely overwritten

2. When `applyUpdate()` runs, only `systemFiles` are touched:

```javascript
// Pseudo-logic inside update-system.mjs (simplified)
const userFiles = readDataContract().userLayer;   // list from DATA_CONTRACT.md
const systemFiles = readDataContract().systemLayer;

function applyUpdate(newVersion) {
  systemFiles.forEach(f => overwrite(f, newVersion[f])); // ONLY system files
  // userFiles are deliberately excluded
}

```

### Safe Update Commands

Users can verify and apply updates with confidence:

```bash

# Example: Running the update check (safe – never touches user files)

node update-system.mjs check

# Example: Applying an update (only System Layer files are replaced)

node update-system.mjs apply --confirm

```

Because the update logic is **manifest-driven** from the Data Contract, there is no path for accidental user file inclusion—even if a developer mistakenly believes a file should be updated.

---

## Path-Resolution Isolation: Keeping User Data Physically Separate

Beyond logical layer separation, career-ops implements **physical isolation** through path resolution. As documented in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) (lines 81-89), the system resolves `DATA_ROOT` independently from the repository root.

This means:

- Users can relocate their personal files to a completely different directory using environment variables or a marker file
- The update logic always operates on the repository root
- Personalized content never resides in the directory receiving system updates

This architectural decision provides defense-in-depth: even if the update mechanism were compromised, it would still lack access to user data stored outside the repository tree.

---

## Template Precedence: User Files Always Win

Personalization in career-ops is stored in two specific locations that receive **loading priority**:

- [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) — User's archetypes and negotiation scripts
- [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) — User's core configuration

### How Precedence Protects Personalization

All mode implementations load these files **before** any default templates. The system ships with template files ([`modes/_profile.template.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.template.md), [`modes/_custom.template.md`](https://github.com/santifer/career-ops/blob/main/modes/_custom.template.md)) that serve only as **fallbacks for new users**.

When a system update ships new templates, the following occurs:

1. Template files in `templates/*` may be updated
2. The user's existing [`_profile.md`](https://github.com/santifer/career-ops/blob/main/_profile.md) and [`profile.yml`](https://github.com/santifer/career-ops/blob/main/profile.yml) remain untouched
3. At runtime, the user's personalized files are loaded with precedence over any template

This ensures that **custom archetypes, narrative structures, and house rules persist indefinitely**, regardless of how many template improvements are released upstream.

---

## Onboarding Checks: `doctor.mjs` Prevents Accidental Defaults

The **`doctor.mjs`** health-check runs at the start of each session and provides an early warning system for personalization gaps. As implemented in [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md) (lines 66-71), it can detect when a personalization file contains only the shipped template—flagging this as an *unpersonalized* warning.

This safeguard prevents the scenario where a user unintentionally relies on default content, which could create confusion about what constitutes "personalized" data. By prompting users to edit their profile before proceeding, `doctor.mjs` ensures that:

- Users are explicitly aware of which files they own
- The "personalized vs. template" distinction is always clear
- No user accidentally builds career-critical workflows on content that might change

---

## Key Files and Their Roles

| File | Role | Why It Protects Personalization |
|------|------|--------------------------------|
| [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) | Declares User vs. System layers | Provides the authoritative list that the updater consults for every operation |
| [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md) | High-level policy documentation | Communicates and reinforces the "never auto-update user files" rule to all agents |
| `update-system.mjs` | Update execution engine | Guarantees only System Layer files are replaced through manifest-driven logic |
| [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) | Primary personalization storage | Loaded with precedence over any shipped template; immutable to updates |
| [`modes/_profile.template.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.template.md) | Default template | Used only when user has no personalization file; never overwrites [`_profile.md`](https://github.com/santifer/career-ops/blob/main/_profile.md) |
| `doctor.mjs` | Health-check and onboarding | Flags unpersonalized templates to prevent accidental reliance on defaults |
| `path-resolver.mjs` | `DATA_ROOT` resolution | Enables physical separation of user data from repository update operations |

---

## Summary

career-ops ensures **user personalization is not overwritten by system updates** through six integrated safeguards:

- **Declarative layer separation** in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) defines exactly which files the system may touch
- **Immutable User Layer policy** in [`AGENTS.md`](https://github.com/santifer/career-ops/blob/main/AGENTS.md) prohibits any automatic modification of personalized content
- **Manifest-driven updates** via `update-system.mjs` physically exclude user files from replacement operations
- **Path-resolution isolation** allows user data to reside outside the repository entirely
- **Template precedence rules** guarantee user files are loaded before any system defaults
- **Onboarding health checks** in `doctor.mjs` prevent accidental reliance on template content

Together, these mechanisms guarantee that any future update—whether a bug fix, feature addition, or dependency upgrade—will leave your personalized CV, target archetypes, and custom workflow definitions completely intact.

---

## Frequently Asked Questions

### What happens if I accidentally delete my personalization files?

The system will fall back to template files ([`modes/_profile.template.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.template.md), [`config/profile.template.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.template.yml)) on next launch. Run `node doctor.mjs` to detect this condition and receive guidance on restoring your personalization from backup or version control.

### Can I store my personalization files in a completely different directory?

Yes. Configure `DATA_ROOT` via environment variable or marker file as documented in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) (lines 81-89). The `path-resolver.mjs` module will locate your files while `update-system.mjs` continues to operate only on the repository root.

### How does career-ops handle new configuration options in updates?

New options ship as updates to `templates/*` and documentation. When you run `doctor.mjs`, it will notify you of new available options without modifying your existing [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml). You may manually adopt new options by referencing the updated templates.

### Is the User Layer protection enforced for manual edits I make to system files?

No. The protection applies only to **automatic updates** executed by `update-system.mjs`. If you manually edit a System Layer file such as a script or shared mode, those changes may be overwritten when you next run `node update-system.mjs apply`. Keep all personalization in User Layer files only.