CareerOps Data Contract Architecture: User Layer vs System Layer Explained
The CareerOps Data Contract Architecture separates every file in the santifer/career-ops repository into two logical layers—the User Layer for personal data that must never be auto-updated, and the System Layer for core engine files that can be safely replaced during upgrades.
The CareerOps Data Contract Architecture is the single source of truth that governs how the project maintains, updates, and customizes your career management system. This architectural pattern ensures that your personal CV, job tracker, and negotiation scripts remain intact while the underlying engine evolves with new features and bug fixes.
Understanding the Two-Layer Architecture
The architecture bifurcates the repository into distinct zones with opposing update policies. This separation is documented in [DATA_CONTRACT.md](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) and enforced programmatically by scripts like update-system.mjs.
User Layer: Protected Personal Data
The User Layer contains all files that store personal data, customizations, and user-generated output. According to the contract, the updater must never modify, edit, or delete these files during any release cycle.
Core characteristics:
- Update policy: Immutable during auto-updates (read-only for the updater)
- Contents: CV content, profile configurations, custom archetypes, negotiation scripts, application tracker data
- Critical files:
cv.md— your CV in markdown formatconfig/profile.yml— personal identity, target roles, and compensation targetsmodes/_profile.md— custom archetypes and negotiation narrativesdata/applications.md— the canonical application tracker (source of truth)portals.yml— maintained list of companies and search filters
System Layer: Replaceable Core Engine
The System Layer implements the engine of CareerOps: scripts, templates, mode definitions, and supporting infrastructure. These files are designed to be ephemeral and disposable across releases.
Core characteristics:
- Update policy: Safe to replace on each release
- Contents: Core logic, scoring algorithms, UI components, PDF templates, documentation
- Critical files:
modes/_shared.md— core scoring rules and shared utilitiesmodes/oferta.md— evaluation mode instructions*.mjsscripts — all utility scripts includingscan.mjsandset-status.mjstemplates/*— HTML/CSS templates for CV PDFs and cover lettersdocs/*— release documentation
How the Data Contract Enforces File Protection
The contract operates on a simple binary rule enforced by the update mechanism. If a file appears in the User Layer, the updater may read it but must not modify or delete it. Conversely, System Layer files may be safely replaced with the newest upstream version.
The enforcement script update-system.mjs implements this logic by checking file paths against the contract definitions. The system also maintains refused declarations to prevent accidental freezing of system files—blocking absolute paths, paths already belonging to the system layer, and config/local-paths.txt itself from being incorrectly categorized as user data.
Why CareerOps Uses a Two-Layer Design
Separating the repository into User and System layers serves three critical architectural purposes:
- Safety for personal data — Your CV, job-tracker history, and negotiation narratives persist indefinitely without risk of corruption during library upgrades.
- Predictable upgrades — The core engine can evolve with new modes, bug-fixes, and UI improvements without breaking your customizations or losing your application history.
- Clear boundaries for contributors — New contributors can immediately identify which files are off-limits for automated changes versus which files they may modify for a release.
Implementing the Contract in Practice
When extending CareerOps with custom modes or scripts, you must copy templates from the System Layer into the User Layer before modifying them. This pattern ensures the original template can receive upstream updates while your customized version remains protected.
Copy a system template to the user layer (safe operation):
import { execSync } from 'child_process';
// Copy template from system layer to user layer
execSync('cp modes/_profile.template.md modes/_profile.md');
Append custom content to a protected user file:
import fs from 'fs';
// Append to user-layer file - updater will never overwrite this
fs.appendFileSync('modes/_profile.md', '\n## New Archetype\n- Senior Data Engineer\n');
Update script skipping user-layer files using fast-glob:
import glob from 'fast-glob';
// Select only system files, explicitly excluding user data directories
const systemFiles = await glob([
'**/*.mjs',
'templates/**',
'!data/**',
'!modes/_profile.md'
]);
systemFiles.forEach(path => {
// Safe to replace these files with upstream versions
console.log(`Updating system file: ${path}`);
});
These examples demonstrate the golden rule: copy system templates once into the user layer, then treat them as immutable user-owned files that the updater cannot touch.
Key Files by Layer
The contract categorizes specific paths to eliminate ambiguity during updates:
User Layer (Never Auto-Updated):
cv.md— Personal CV contentconfig/profile.yml— Identity and targeting configurationmodes/_profile.md— Custom archetypes and scriptsdata/applications.md— Application tracking dataportals.yml— Company watchlists and filters
System Layer (Auto-Updatable):
modes/_shared.md— Shared evaluation logicmodes/oferta.md— System evaluation modesscan.mjs— Zero-token portal scanner implementationtemplates/cv-template.html— ATS-optimized PDF templateDATA_CONTRACT.md— The contract definition itself
Summary
- The CareerOps Data Contract Architecture bifurcates the repository into User and System layers to protect personal data during automated updates.
- User Layer files (
cv.md,config/profile.yml,data/applications.md, etc.) are read-only for the updater and preserve your customizations indefinitely. - System Layer files (
*.mjs,templates/*,modes/_shared.md, etc.) can be safely overwritten with each release to deliver new features. - The contract is defined in
DATA_CONTRACT.mdand enforced byupdate-system.mjsusing path-based rules and refused declarations. - Always copy system templates to the user layer before customizing them to ensure you benefit from upstream template improvements while keeping your modifications safe.
Frequently Asked Questions
What happens if I accidentally modify a System Layer file?
If you edit a System Layer file directly, your changes will be lost during the next update when update-system.mjs replaces the file with the upstream version. To preserve customizations, copy the file to the appropriate User Layer location (such as modes/_profile.md for custom modes) and modify the copy instead.
How does the update script distinguish between User and System Layers?
The update-system.mjs script references the canonical definitions in DATA_CONTRACT.md and uses glob patterns (via fast-glob) to classify files. It explicitly excludes User Layer paths like data/** and modes/_profile.md from the replacement list, ensuring these files are never overwritten even if they share filenames with system templates.
Can I move a file from the System Layer to the User Layer?
Yes, you can copy any System Layer template into the User Layer to customize it. However, you cannot "move" the original system file—the System Layer must remain intact for the engine to function. Create your custom version in a User Layer location (e.g., copy _profile.template.md to _profile.md) and the updater will automatically protect your new file while continuing to update the original template.
Where is the Data Contract formally documented?
The complete specification lives in [DATA_CONTRACT.md](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) at the repository root. This document serves as the single source of truth for the update policy, enumerating which paths belong to each layer and defining refused declarations that prevent accidental misclassification of system files as user data.
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 →