Skip to content

ctx repair

ctx repair checks every page ctx has synced before, and for each one whose base snapshot is missing or no longer matches, fetches the right version from the server and records it again. It also corrects page shas that older servers or ctx builds recorded wrongly. Your working files are never changed.

Rarely by hand. Online ctx status, ctx pull and ctx push run the same repair automatically, over the server listing they fetch anyway. ctx repair is for doing it on demand, all at once, and seeing the result:

  • ctx status lists pages under UNVERIFIED and suggests ctx repair.
  • ctx show --at base fails with base_unavailable and the advice ctx repair.
  • You just upgraded from a ctx version that kept no base snapshots, or an older ctx or ctx mcp process (an agent session started before the upgrade) has been rewriting .contexo/.sync/state.json behind the new one.

Status gives its automatic repair at most 20 seconds and skips it when another ctx process holds the lock, so a large project can stay partly unverified after a status. ctx repair waits for the lock instead.

ctx repair is a ctx-only command. The VS Code extension never calls it: repair is an optional capability in ctx version --json, and the extension relies on the automatic repair instead.

Terminal window
ctx repair
ctx repair --dry-run
ctx repair --json
FlagTypeDefaultDescription
--dry-runboolfalsereport what would be repaired; write nothing
--jsonboolfalseemit the ctx.repair.v1 JSON document

--dry-run makes exactly the same server reads and reports the same result, but writes nothing.

--json prints a ctx.repair.v1 document (see Output below) and turns failures into the one-line error envelope described in JSON output and exit codes.

FlagTypeDefaultDescription
--rootstringproject root directory (default: current directory)

A page needs repair when .contexo/.sync/state.json records a sha for it, but the base snapshot for that sha is missing, belongs to another sha, or has been edited since it was written. For each such page ctx fetches the server’s version at the recorded sha and decides:

  1. Your file already equals the server version. ctx adopts it: that version becomes the base, at the commit the server reports for it.
  2. An older base for the page is still intact. This is the case of an older binary that moved the page’s recorded sha to a teammate’s version after a push conflict, without merging anything. ctx keeps the older base and points the recorded sha back at it (reason kept_previous_base). If your file differs from that base and the server has moved past it, it also records a conflict, so your next push cannot silently overwrite the teammate’s version: you settle it with ctx resolve instead.
  3. Otherwise the server version becomes the base.

Whenever the commit the server reports differs from the one recorded, the recorded sha is corrected. The usual cause is the old server bug that recorded the repo’s HEAD instead of the page’s own last commit (reason path_sha_fixed).

Working files are never written. Only .contexo/.sync/ changes, in this order: base files, conflict files, bases.json, conflicts.json, then state.json. Just before writing, ctx re-reads state.json; a page whose recorded sha changed meanwhile (an older binary takes no lock) is skipped with state_changed.

Repair is careful with the server’s rate limit, which teammates and agents behind the same network share:

  • Pages whose recorded sha is still the one in the server listing are read from the current version: a single bulk request when there are more than 8 of them, otherwise one read for each.
  • Every other page needs the server’s history (the pages.at server capability). Repair makes at most 50 of these history reads per run, at most 4 at a time.
  • The first failure that is not about one page (a 429 rate limit, a network or server error, a rejected token) stops all requests not yet started.

Pages left over are reported as skipped, not as an error: budget_exhausted past the 50-read budget, the failure’s code (for example rate_limited) after a stop, or server_unsupported when the server cannot read history at all. Run ctx repair again to continue: each run picks up the pages still unverified. The human summary prints a “run again” hint when the budget ran out.

The human summary is one line of counts, then one line per repaired page with its old and new sha and the reason, then the skips grouped by reason. When nothing needs repair it says so.

With --json, the ctx.repair.v1 document has these keys, in this order:

Key Holds
schema "ctx.repair.v1"
dry_run whether --dry-run was passed
server_capabilities the capabilities the server advertised in its response headers
backfilled pages whose base was written at their recorded sha: path, sha
repaired pages whose recorded sha changed: path, old_sha, new_sha, reason (path_sha_fixed or kept_previous_base)
skipped pages left alone: path, reason
unverified the paths of the skipped pages
warnings a base_repair_skipped warning when anything was skipped

Every array is present and never null. Skipped pages still exit 0.

Upgrading an older project left it with no base snapshots at all, and one page whose recorded sha was the repo’s HEAD rather than its own commit. A dry run first:

Terminal window
ctx repair --dry-run
ctx repair --dry-run
✔ Dry run: would backfill 3, repair 1, skip 0 (nothing written)
    wiki/concepts/webhook-retries.md   a4b2c1d3 → 7d25c6bf   path_sha_fixed

The same repair for real, as JSON. Three bases were backfilled at the shas already recorded; the fourth page’s sha was corrected to its own commit:

Terminal window
ctx repair --json
ctx repair --json
{
  "schema": "ctx.repair.v1",
  "dry_run": false,
  "server_capabilities": [
    "pages.at",
    "push.expect_absent",
    "write.path_sha"
  ],
  "backfilled": [
    {
      "path": "wiki/concepts/stripe-subscription.md",
      "sha": "a4b2c1d3e5f60718293a4b5c6d7e8f9012345678"
    },
    {
      "path": "wiki/entities/billing-worker.md",
      "sha": "3b8e1a0947c2f6d85e90a1b2c3d4e5f60718293a"
    },
    {
      "path": "wiki/entities/invoice-service.md",
      "sha": "e5a3b9c807162534435261708f9e0d1c2b3a4958"
    }
  ],
  "repaired": [
    {
      "path": "wiki/concepts/webhook-retries.md",
      "old_sha": "a4b2c1d3e5f60718293a4b5c6d7e8f9012345678",
      "new_sha": "7d25c6bf018a3e4d5c6b7a8990f1e2d3c4b5a697",
      "reason": "path_sha_fixed"
    }
  ],
  "skipped": [],
  "unverified": [],
  "warnings": []
}

The command’s own help text:

Terminal window
ctx repair --help
ctx repair --help
Repair finds every synced page whose base snapshot under .contexo/.sync is missing or
no longer matches, fetches the base from the server and records it. It also fixes page
shas that older servers or ctx builds recorded wrongly (the repo HEAD instead of the
page's own commit). Working files are never changed. Pages it cannot verify are
reported as skipped; with --dry-run it makes the same reads and writes nothing.

Usage:
  ctx repair [flags]

Flags:
      --dry-run   report what would be repaired; write nothing
  -h, --help      help for repair
      --json      emit the ctx.repair.v1 JSON document

Global Flags:
      --root string   project root directory (default: current directory)
  1. Checks that .contexo/ exists, then resolves the server, token and repo id. No credentials is not_authenticated (exit 4 with --json); no repo_id is repo_not_configured (exit 5).
  2. Takes .contexo/.sync/lock, waiting up to CONTEXO_LOCK_TIMEOUT (default 10 seconds), then fails with busy (exit 12).
  3. Lists the repo’s pages once and reads the server’s capabilities from that response.
  4. Finds the pages that need repair, fetches what they need within the budget above, and decides each one.
  5. Unless --dry-run, writes the result, then prints the summary or the document.

With --json the exit codes are 0, 4, 5, 7, 8 and 12 (plus 1 and 3). Without --json, any failure is a message and exit 1.

busy (exit 12): another ctx process, often an agent’s push or pull, held the lock past CONTEXO_LOCK_TIMEOUT. Run it again when that finishes.

network, server_error or rate_limited (exit 7) on the listing: the repair never started. These are retryable. A failure after the listing does not fail the command; it shows up as skipped pages.

repo_not_found (exit 8): the server has no repo with that id. Check repo_id with ctx remote.

not_authenticated (exit 4), server_unresolved or repo_not_configured (exit 5): see ctx login and ctx remote.