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

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 or 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:


# 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 and 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →