# Career-Ops Update Checker Workflow: Version Detection and Conflict Resolution

> Discover the career ops update checker workflow for seamless version detection and conflict resolution. Learn how it fetches, compares, and applies updates safely.

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

---

**The Career-Ops update checker workflow is a self-contained auto-updater implemented in `update-system.mjs` that detects new releases by fetching remote versions in parallel, compares semantic versions locally, and applies updates only to system paths while preserving user data through atomic backup branches and rollback capabilities.**

Career-Ops is an open-source career management toolkit that includes a built-in **update checker workflow** to keep system scripts and documentation current. The updater lives entirely within `update-system.mjs` and operates under a strict boundary: it mutates only system files while never touching user data such as [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) or [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml).

## The Check Workflow

The `check` command (implemented in `async function check()` starting at line 91) determines whether a newer release exists by comparing the local `VERSION` file against remote sources.

### Early-Out and Dismissal Logic

If a `.update-dismissed` marker file exists, the function immediately returns `{"status":"dismissed"}` without network calls. This allows users to silence notifications until they explicitly run `check` again.

### Remote Version Detection

The script fetches two sources in parallel using `curl` (avoiding sandboxed `fetch()` limitations):

- `RAW_VERSION_URL`: A plain-text file containing the latest version string
- `RELEASES_API`: The GitHub Releases JSON payload as fallback

This happens at lines 106-112. The `parseVersionFile()` function extracts version strings using `SEMVER_RE`, and if parsing fails, the version remains empty (lines 117-131).

### Offline Handling and Comparison

If both `curl` calls return null, the status becomes `offline`. If at least one succeeds but produces an empty version, the status is `no-remote-version` (lines 136-144). When sources disagree, the updater selects the higher version using `compareVersions()` (lines 147-154). The function performs numeric comparison of `major.minor.patch` components, returning `up-to-date` if local >= remote, or `update-available` with a truncated 500-character changelog otherwise (lines 156-165).

## How Version Conflicts Are Resolved

The `apply` command (line 71) handles mutations when the remote version is newer. It resolves conflicts through a safety-first pipeline that never overwrites user data.

### Atomic Backup Creation

Before any changes, `apply` creates a Git branch named `backup-pre-update-<local>-<timestamp>` (lines 86-90). This branch captures the complete pre-update state for potential rollback.

### System-Only Checkout

The script fetches the canonical repository (`git fetch https://github.com/santifer/career-ops.git main`) then checks out only paths listed in `SYSTEM_PATHS`, plus `BOOTSTRAP_PATHS` to prevent dangling symlinks (lines 92-108).

### Safety Validation and Abort

After checkout, the script scans `git status` for modifications in `USER_PATHS`. If any user file was altered, the update aborts immediately, logs a "SAFETY VIOLATION", and reverts both system files and unintentionally touched user files (lines 129-149). This guarantees that version conflicts never propagate to user data.

### Dependency Installation and Commitment

If validation passes, the script runs `npm install` and optionally rebuilds the Go dashboard (lines 92-100). Changes are committed to the system files, or silently skipped if no changes occurred (lines 104-114). The `.update-lock` file is removed in a `finally` block, leaving the backup branch intact for potential `rollback`.

## Rollback and Recovery

When an update introduces regressions, the `rollback` command (line 27) restores the previous state. It locates the most recent `backup-pre-update-*` branch via `newestBackupBranch()` (lines 30-34), then restores each `SYSTEM_PATHS` entry from that branch. If a path exists in the current system but not the backup (indicating it was added by the faulty update), the script deletes it to faithfully mirror the backup state (lines 58-78). The restoration is committed without touching user files (lines 88-95).

## Running the Update Checker

Execute the workflow via Node.js or the OpenCode CLI:

```bash

# Check for updates

node update-system.mjs check

# Output: {"status":"update-available","local":"1.6.0","remote":"1.7.0","changelog":"..."}

# Apply the update

node update-system.mjs apply

# Rollback if issues occur

node update-system.mjs rollback

# Silence notifications

node update-system.mjs dismiss

```

In OpenCode environments, use the skill interface:

```

/career-ops update check
/career-ops update apply
/career-ops update rollback

```

## Summary

- The **update checker workflow** lives in `update-system.mjs` and provides four commands: `check`, `apply`, `rollback`, and `dismiss`.
- Version detection fetches `RAW_VERSION_URL` and `RELEASES_API` in parallel, resolving conflicts by selecting the higher semantic version.
- Updates apply only to `SYSTEM_PATHS` after creating atomic backup branches named `backup-pre-update-<version>-<timestamp>`.
- A safety scanner aborts the update if any `USER_PATHS` file is modified, preventing user data corruption.
- The `rollback` command restores the exact pre-update state from the newest backup branch, deleting any files added by the faulty update.

## Frequently Asked Questions

### What happens if the update checker can't reach the internet?

If both `curl` calls to `RAW_VERSION_URL` and `RELEASES_API` return null, the `check` function returns `{"status":"offline"}`. If the calls succeed but return empty version strings, it returns `{"status":"no-remote-version"}`. Both states prevent the `apply` command from running.

### How does Career-Ops ensure my resume and config files stay safe during updates?

The `apply` function maintains a strict separation between system and user layers. It only checks out files listed in `SYSTEM_PATHS` and `BOOTSTRAP_PATHS`. After checkout, it scans `git status` for changes in `USER_PATHS` (including [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) and [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml)). If any user file is modified, the script aborts with a "SAFETY VIOLATION" and reverts all changes.

### Can I skip update notifications temporarily?

Yes. Running `node update-system.mjs dismiss` creates a `.update-dismissed` marker file. The `check` function detects this at lines 93-96 and immediately returns `{"status":"dismissed"}` without querying remote servers. The marker persists until you manually run `check` again.

### What is the rollback strategy if an update breaks the system?

The `rollback` command finds the most recent `backup-pre-update-*` branch and restores the repository to that exact state. It deletes any files added by the update that didn't exist in the backup, ensuring the system matches the pre-update snapshot while leaving user data untouched.