How career-ops Ensures User Personalization Is Not Overwritten by System Updates
career-ops uses a strict two-layer architecture—User Layer and System Layer—with immutable rules enforced by DATA_CONTRACT.md, AGENTS.md, and the update-system.mjs script to guarantee that personalized files like cv.md, config/profile.yml, and 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, 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, these files are never auto-updated:
cv.md— Your personalized CV contentconfig/profile.yml— Core profile configurationmodes/_profile.md— Custom archetypes, narratives, and negotiation scriptsmodes/_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— Shared mode infrastructure- All
*.mjsscripts — System automation and tooling templates/*— Default templates shipped with the repository
Immutable User Layer: The Core Rule in AGENTS.md
The 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
-
The script reads
DATA_CONTRACT.mdto populate two lists:userFiles— Files that must be excluded from any update operationsystemFiles— Files that may be safely overwritten
-
When
applyUpdate()runs, onlysystemFilesare touched:
// 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:
# 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 (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— User's archetypes and negotiation scriptsconfig/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, modes/_custom.template.md) that serve only as fallbacks for new users.
When a system update ships new templates, the following occurs:
- Template files in
templates/*may be updated - The user's existing
_profile.mdandprofile.ymlremain untouched - 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 (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 |
Declares User vs. System layers | Provides the authoritative list that the updater consults for every operation |
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 |
Primary personalization storage | Loaded with precedence over any shipped template; immutable to updates |
modes/_profile.template.md |
Default template | Used only when user has no personalization file; never overwrites _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.mddefines exactly which files the system may touch - Immutable User Layer policy in
AGENTS.mdprohibits any automatic modification of personalized content - Manifest-driven updates via
update-system.mjsphysically 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.mjsprevent 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, 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 (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. 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →