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 stringRELEASES_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.mjsand provides four commands:check,apply,rollback, anddismiss. - Version detection fetches
RAW_VERSION_URLandRELEASES_APIin parallel, resolving conflicts by selecting the higher semantic version. - Updates apply only to
SYSTEM_PATHSafter creating atomic backup branches namedbackup-pre-update-<version>-<timestamp>. - A safety scanner aborts the update if any
USER_PATHSfile is modified, preventing user data corruption. - The
rollbackcommand 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →