Skip to content

Push, pull & sync

Every imported project records a local base in .hlix/state.json and a cloud head with an immutable revision ID and monotonically increasing generation. Synchronization compares both sides to that common base.

  • Run commands from the imported project root
  • .hlix/config.json contains a project ID
  • .hlix/state.json contains the last synchronized revision
  • Finish and test valuable local work before any forced pull

Both sides are compared against the base revision they last agreed on: if only one moved, the direction is unambiguous; if both moved, hlix refuses rather than merging.

Local and cloud are each compared against the base revision they last agreed on. Only one side changed means push or pull; both changed is refused as a conflict rather than merged.Local.hlix/state.jsonCloud headrevision · generationBase revisionwhat both last agreed onlocal moved?cloud moved?verdictunchangedunchangednonechangedunchangedpushunchangedchangedpullchangedchangedconflicta push is a compare-and-swap on revision id AND generationa lost race is a conflict, never a silent overwrite
Command Use when Behavior
hlix push Local files changed; cloud did not uploads a new immutable revision using compare-and-swap
hlix pull Cloud changed; local did not verifies and applies the cloud head locally
hlix sync You want hlix to choose only when one side changed pushes, pulls, or reports already synchronized

hlix sync is intentionally not a merge engine. If local and cloud both changed from the same base, it stops with a conflict.

All three commands accept --dry-run. It prints the divergence, takes no action, and reports the verdict the real command would reach.

Terminal window
hlix push --dry-run

Expected result:

push --dry-run: Would push the local changes as a new cloud revision.
local generation 3 (rev_01J8Z4) — modified
cloud generation 3 (rev_01J8Z4)
files 412 local, 410 cloud — 3 local-only, 1 cloud-only, 2 differing

The first line is the verdict; the block below is the evidence for it. The verdict is one of four per command:

Command Verdict line
push Would push the local changes as a new cloud revision. · Nothing to push — local matches the last synced revision. · Would refuse: the cloud changed since this folder's base revision.
pull Would replace local files with the cloud revision. · Nothing to pull — the cloud head is the local base revision. · Would refuse: local files changed. Re-run with --force to replace them.
sync Would push … · Would pull the cloud revision into this folder. · Already in sync. · Would refuse: local and cloud both changed since the last sync.

A dry run that would end in a conflict exits 1, the same way hlix import --dry-run exits 1 for a blocker. The report is the deliverable, and the exit code still carries the verdict — so a script can gate on it without parsing the text. conflict is the only verdict that exits 1; every other dry run exits 0.

A forced pull over local changes is the one case where the verdict alone is not the whole answer. The dry run adds a second line:

Terminal window
hlix pull --force --dry-run
pull --dry-run: Would replace local files with the cloud revision.
Requires --yes (or interactive approval) to execute.
local generation 3 (rev_01J8Z4) — modified
cloud generation 4 (rev_01J9A2) — ahead
files 412 local, 415 cloud — 2 local-only, 5 cloud-only, 3 differing

That run exits 0: needing an answer is not a refusal, and a caller who has one — a terminal, or --yes — executes exactly this plan.

In --json, the same fact is a field rather than a line:

{"dryRun":true,"action":"pull","approvalRequired":true,"localChanged":true,"cloudChanged":true}
Terminal window
hlix push

Expected result:

push complete.

The new revision becomes head only when the cloud still matches the revision and generation in local state. Protected files are uploaded separately from the Git bundle.

Terminal window
hlix pull

Expected result: pull complete. The CLI verifies bundle size, SHA-256, Git bundle validity, commit SHA, manifest metadata, and every protected file before applying data.

If local managed files changed, pull refuses. After reviewing and backing up those changes, replace them explicitly:

Terminal window
hlix pull --force

pull --force is the one command that deletes local work, so it asks before replacing anything. The two flags say different things:

  • --force states “replace my changes.” It selects the destructive behavior.
  • --yes states “do not ask.” It answers the prompt in advance.

Interactively, --force alone prints what it is about to destroy and waits:

hlix pull --force will replace this folder with cloud revision 4.
Local-only files to delete: 3
Files to replace with the cloud copy: 2
Local changes that were never pushed cannot be recovered by Hlix.

Declining exits cancelled. A --json or non-interactive caller cannot answer a prompt, so it is refused rather than defaulted to yes:

`pull --force` replaces local files. Review with `hlix pull --dry-run`, then repeat with `--yes`.

That refusal is approval_required. --force with no local changes replaces nothing and does not prompt.

For a Git project, pull updates the managed working tree to the cloud commit. For a non-Git import, it replaces files described by the revision manifest. Ignored local-only files are outside that managed set.

Terminal window
hlix sync

The decision table is exact:

Local since base Cloud since base Result
no change no change Already in sync.
changed no change push
no change changed pull
changed changed conflict; no side is overwritten
Terminal window
hlix sync --json

Expected result: a versioned JSON envelope containing action, revisionId, and generation. Run the command again; it should report an action of none.

  • not_initialized: run hlix import . from the project root.
  • workspace_mismatch: a --workspace, --base-url, or HLIX_* value contradicts this folder’s binding. Drop the override, or point --cwd at a folder bound to the workspace you meant. Run hlix status to see which source decided what.
  • approval_required: a destructive step needs an approval this caller cannot give. Review with --dry-run, then repeat with --yes.
  • cancelled: you declined the prompt. Nothing was changed.
  • Cloud changed since the local base. Run `hlix pull` or resolve with `hlix sync`. — review before choosing a side.
  • Local files changed since the last sync. Push them, or use `hlix pull --force` to replace them. — push first if the local work matters.
  • Both local and cloud changed since the last sync. Pull or push explicitly after reviewing the conflict.: preserve local work in a branch or backup, inspect the cloud state, then choose a side explicitly.
  • Project has active tasks: wait for them to reach a terminal state, pull their resulting head when needed, then retry.
  • Downloaded revision … failed integrity verification: stop. Do not bypass the check; retry over a trusted network and contact support if it repeats.
  • SECRETS_ENCRYPTION_KEY is not set: the deployment cannot decrypt protected state. An operator must repair backend configuration.
  • Refusing to replace non-regular protected file: replace the symlink/device with the intended regular file only after confirming the path.