RCH — Remote Compilation Helper

SkillDev tools

Offload cargo/gcc/bun builds to remote workers. Use when compilation slow, "[RCH] local" in stderr, workers unhealthy, hook silent, sync fails, disk pressure, or SSH/daemon/telemetry recovery.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the RCH — Remote Compilation Helper skill

What this skill tells your AI

The instructions your AI receives, as published by dicklesworthstone/remote_compilation_helper in .claude/skills/rch/SKILL.md and read by ahel’s review.

rch transparently offloads compilation commands to remote workers via a Claude Code PreToolUse hook (and, for every other agent/shell, via the managed cargo shim). The daemon picks a healthy worker, rsync's the workspace, runs the build, syncs artifacts back, and exits with the worker's exit code.

This skill is the operational layer agents use when that pipeline isn't working — and the much more common case where it thinks it's working but is silently building locally. Single principle: self-resolve before asking the human. Every recovery path here is one the agent can run alone.

Harmonized against remote_compilation_helper main @ b5c0566 (2026-08-25), workspace v1.0.58. The released 1.0.58 binary predates the post-release shim toolchain-wrapper commits; where that matters it is called out.


Read This First

When a build feels slow, run one thing:

RCH_VISIBILITY=verbose <your-command> 2>&1 | grep -E '^\[RCH\]'

The summary line is a contract:

PatternWhat to do
[RCH] remote <worker> (...)Healthy. Done.
[RCH] remote <worker> failed (exit N) / failed [RCH-Exxx] ...Real build/env failure. rch error explain RCH-Exxx; references/ERROR_CODES.md.
[RCH] local (<reason>)Fail-open. references/FAIL_OPEN.md; look the reason up verbatim.
[RCH] remote required; refusing local fallback (<reason>)Fail-closed (strict remote / shim / dispatcher role). Exit 103 = retry after fixing the fleet; exit 1 = permanent.
no [RCH] line at allHook didn't fire, or the shim isn't ahead on PATH. scripts/protocol_test.sh "<cmd>" then rch shim status.

If you can't see why offload isn't happening, prove the path in isolation, loudly:

RCH_REQUIRE_REMOTE=1 RCH_VISIBILITY=verbose rch exec -- cargo check --workspace --all-targets

[RCH] remote <worker> (...) ⇒ the offload pipeline is healthy and the problem is upstream (hook classifier, PATH/shim order, the invocation form). A refusal line names exactly what's missing. Then references/RECOVERY_PLAYBOOKS.md.


Quick Start

rch check                                   # exit 0 ready / 1 degraded / 2 not ready
rch status --workers --jobs                 # full status
rch status --remediation                    # bands: operator-action vs self-healing vs normal fail-open
rch status --fleet                          # desired vs live workers: bypassed / disabled / unreachable / missing
rch workers probe --all                     # connectivity + component matrix + probe_warnings
rch hook status && rch agents status        # which agents are wrapped
rch shim status                             # cargo shim: PATH order, local builds running, toolchains wrapped
rch admit "cargo test -p foo"               # read-only admission preflight
rch diagnose --dry-run "cargo check --workspace --all-targets"
RCH_REQUIRE_REMOTE=1 rch exec -- cargo check --workspace --all-targets
rch self-test --all                         # end-to-end verify
rch doctor --reliability --scope topology,pressure   # (`ownership` scope is post-1.0.58)
rch doctor --fix --dry-run                  # what doctor would auto-fix
rch --robot-triage --json                   # agent mega-command

If rch status shows storage pressure, check both / and /tmp on the worker before deciding what to fix:

ssh <user>@<host> 'df -h / /tmp /data 2>/dev/null; free -h; cat /proc/pressure/memory /proc/pressure/io'

Fast Triage Order

Run in order; stop at the first failing stage.

  1. Availabilityrch check, rch status --workers --jobs, rch status --fleet, rch workers probe --all, rch queue
  2. Config + socket consistencyrch config show --sources, rch --json config get general.socket_path, rch --json daemon status (version is in rch --json status.data.daemon.daemon.version)
  3. Interceptionrch hook status, rch agents status, rch shim status, rch hook install (idempotent)
  4. Classification + admission + closurerch diagnose --dry-run "<cmd>", rch admit "<cmd>"
  5. Remote compile proofRCH_REQUIRE_REMOTE=1 rch exec -- cargo check --workspace --all-targets
  6. If sync fails or storage looks badrch cache status --workers <id>, rch gc --dry-run --workers <id>, then references/DISK_AND_PRESSURE.md

Quick Fixes

SymptomCommand
Hook not installedrch hook install && rch hook status (Gemini/Codex: rch agents install-hook gemini-cli|codex-cli)
Codex / scripts / CI build locallyrch shim install on the dispatcher — references/SHIM.md
rch shim status shows local rustc runningfix PATH order (~/.rch/shims first) or re-run rch shim install after a rustup toolchain install
Daemon not runningrch daemon start
Daemon version drift after upgraderch daemon restart -y — after rch --json queue | jq '.data.active_builds|length' is 0: restart interrupts active builds (-y only skips the warning prompt)
Socket mismatch / stale daemon staterch daemon restart -y then rch --json daemon status
No workers configuredrch workers discover --add --yes && rch workers setup --all (or rch workers init)
Workers unreachablerch workers probe --all; keys → references/SSH_KEY_RECOVERY.md
"all workers unreachable" persists though probes passworkers are in temporary bypass: rch status --fleet; they auto-rejoin after N healthy probes + a canary build; rch workers enable <id> clears the record
All workers busyqueueing is default (waits 330 s); raise it — RCH_DAEMON_WAIT_RESPONSE_TIMEOUT_SECS=900 <cmd> — or raise total_slots
no admissible workersrch admit "<cmd>" — per-candidate rejection reasons + next action
Requested worker refused (RCH_WORKER=x)the [RCH-Innn] line carries a next_action; do it — rch never silently swaps workers
Shell-wrapped cargo refused [RCH-E301]never rch exec -- bash -lc "cargo … && cargo …"; run separate rch exec -- cargo …
Job exits 102 with RCH-E309a declared --result-dir was missing/partial on the worker; fix the path, the job itself may have passed
Need a per-command priority bumpRCH_PRIORITY=high <cmd>
Want RUSTFLAGS/RUST_LOG forwardedRCH_ENV_ALLOWLIST=RUSTFLAGS,RUST_LOG <cmd> (CARGO_TARGET_DIR/TMPDIR are always rewritten on the worker)
Transfer churn / payload too large (transfer skipped).rchignore or [transfer] exclude_patterns, then rch daemon reload
Path dependency missing remotelyreferences/PATH_DEPENDENCIES.md ([path_topology]; rch sync --force for stale worker caches)
Sync Permission denied under /data/projects/<repo>ssh <user>@<host> "stat -c '%U:%G %a %n' /data/projects/<repo>" then fix: ssh <user>@<host> 'sudo chown -R <user>:<user> /data/projects/<repo> && sudo chmod 775 /data/projects/<repo>' (a fleet-wide rch doctor --reliability --scope ownership probe exists on main only)
Worker disk pressure (RCH-E210/211…)rch cache status, rch gc --dry-run, then references/DISK_AND_PRESSURE.md / sbh
Telemetry / SpeedScore brokenreferences/TELEMETRY_RECOVERY.md
Hook says installed but isn't interceptingscripts/protocol_test.sh "<cmd>"
Multiple agents racing on fleet opsscripts/multi_agent_safety.sh <cmd> + Agent Mail reservations
--target *-pc-windows-msvc / *-apple-* won't offloadneeds a worker declaring os = "windows" / "darwin"references/WORKERS.md; without one it correctly runs local
Cross-platform worker taking the wrong jobsit has no os = set; add it, and roll rchd out first (older builds ignore the key silently)
Build must run locally on purpose (release binary for this host).rch/config.toml[general] force_local = true, plus RCH_CARGO_WRAPPER_BYPASS=1 if the shim is installed; inline RCH_ENABLED=0 does not bypass the hook
Need a runbook, not a guessrch doctor --runbook-list; rch doctor --runbook RCH-R006
Need full environment diagnosisrch doctor --json, rch config doctor

Worker Priority and Slots Are Measured, Never Guessed

Rule: never hand-set priority or slots in workers.toml from intuition, core count, or price tier. Derive them from a persisted SpeedScore. If no score exists, fixing that comes first — it is a bug, not a reason to guess.

Hand-tuned priorities silently invert. A real example from this fleet: the two highest-priority workers (110) had the fewest slots (4 and 5) while ten-slot workers sat at 100, so the scheduler preferentially routed to the smallest, most disk-constrained boxes — which is exactly why those two filled to 96–98% first.

The correct sequence

rch workers benchmark --all --force     # measure
rch workers list --speedscore           # confirm scores persisted and DIFFER
rch workers compare <id> <id> <id>      # side-by-side
rch speedscore <worker>                 # per-worker detail + components
# only now: set priority ∝ measured score, slots ∝ cores

Verify the score is real before you trust it

CheckBad signMeaning
Spread across workersevery worker scores the same (esp. exactly 100.0)scoring is saturated/clamped — worthless for ranking
Correlation with hardwarea 6-core box outscores a 16-core boxbenchmark is single-threaded or measuring current load
Persistencerch speedscore <w> says "not benchmarked yet" right after a successful benchmarkscore isn't being stored; the scheduler will re-benchmark forever

That third one has a loud tell: daemon.log fills with alternating Benchmark completed successfully ... score: 100.0 and Enqueued benchmark request ... reason: new_worker for the same worker. That is an infinite re-benchmark loop and will grow the log to hundreds of MB. (A persisted score is now treated as "already benchmarked" — if you still see the loop, the store is broken.)

rch workers benchmark (the CLI probe) times an SSH round-trip around a hello-world compile. It is not a capability measurement and must never be used to rank workers. The authoritative number is the daemon's SpeedScore (CPU 30 / disk 20 / compilation 20 / memory 15 / network 15 via rch-telemetry).

Before recommending more hardware

rch queue        # → "● N available  ● M busy  ● 0 offline / X / Y slots free"

If slots are free and nothing is queued, more machines will not help — say so. Look for the real constraint in this order: disk pressure (workers >90% degrade and fail builds long before slots run out; rch status --remediation shows the tightest free-disk ratio), then stale/inverted priorities, then slot counts that don't match core counts. Only recommend buying when rch queue shows sustained zero free slots under real load.


Anti-Asking Rules

These are the questions agents historically ask the human that they should just answer themselves. Do not ask. Do.

  • "Can I restart the daemon?" — Yes, once rch --json queue | jq '.data.active_builds|length' is 0. rch daemon restart -y does not drain: -y skips the "N active build(s) will be interrupted" prompt, and the daemon may refuse (shutdown_blocked) while builds run. Wait or rch cancel first.
  • "Can I install the hook / the shim?" — Yes on a dispatcher box (rch hook install, rch shim install). The shim refuses on a role = "worker" box by itself.
  • "The autostart cooldown is blocking me — can I delete the cooldown file?" — Don't; wait auto_start_cooldown_secs (30 s) or run rch daemon start, which isn't gated by the hook cooldown.
  • "Can I drop the corrupt telemetry db?" — Yes. references/TELEMETRY_RECOVERY.md. Telemetry is derived data.
  • "Should I recover SSH keys from a sibling host?" — If the keys are missing here but reachable on another host, yes. references/SSH_KEY_RECOVERY.md Step 3.
  • "Should I run the benchmarks before setting priorities?" — Yes, always. Priorities without a persisted SpeedScore are guesses.
  • "Every worker scored the same — can I rank them by core count?" — No. An identical score across the fleet means scoring is broken; fix the score.
  • "Should we buy more workers?" — Answer from rch queue and rch status --remediation, not intuition. Free slots + empty queue = no. Check disk pressure first.
  • "The worker is at 97% disk — should I delete the rch build pool?" — Run rch cache status --workers <id> then rch gc --dry-run --workers <id>; let the reaper's verdicts (REAPABLE vs pooled (kept) vs active/recent (kept)) decide. Never rm -rf a .rch-target-* dir by hand while rch queue shows that project active.
  • "A shimmed build exited 103 — should I uninstall the shim?" — No. 103 means the fleet had nothing admissible; rch status --remediation, fix that, retry.
  • "It's waiting on a source-authority lock — is it hung?" — No. Another invocation holds a shared path-dependency root on that worker; it releases when that Cargo exits.

When in genuine doubt, capture the escalation packet (end of references/RECOVERY_PLAYBOOKS.md) and surface that — not a wall of text — to the human.


rch exec Modes You Should Know (details: references/EXEC_MODES.md)

NeedForm
Proof it ran remotelyRCH_REQUIRE_REMOTE=1 rch exec -- cargo test --workspace (refuses fallback; exit 103 retryable / 1 permanent)
Machine-readable outcomerch --json exec -- cargo test -p foo → one NDJSON envelope: outcome (delivery) + location + remote_exit_code
Non-compilation remote work (shards, fuzz, bench)rch exec --job --result-dir crashes -- ./fuzz.sh (result dirs come back even on failure; missing dir ⇒ exit 102 RCH-E309)
Build the committed tree, ignore others' editsrch exec --base HEAD --clean-overlay --overlay-path src/lib.rs -- cargo test (remote-only by construction)
Byte-exact source receiptrch exec --source-content-receipt -- cargo test --lockedrch.source_content_receipt.v1
Several cargo commandsseparate invocations — a shell-wrapped chain is refused (RCH-E301)

Knobs Worth Knowing (Env Vars)

Every name below is read by 1.0.58 code (references/CONFIGURATION.md has the full list and the ones that are not read).

VariableUse
RCH_VISIBILITY=summary|verbose|none[RCH] ... summary line per offloaded build. Code default is none — set summary in your harness env. Refusals print regardless.
RCH_REQUIRE_REMOTE=1Fail closed instead of building locally (proof mode). Wins over RCH_FORCE_REMOTE.
RCH_FORCE_REMOTE=1Skip local-time/speedup gating, still fail open.
RCH_QUEUE_WHEN_BUSYWait for a slot instead of local fallback. Default 1. 0 to opt out (benchmarking).
RCH_DAEMON_WAIT_RESPONSE_TIMEOUT_SECS (alias RCH_DAEMON_RESPONSE_TIMEOUT_SECS)Max seconds to wait for a queued worker. Default 330 — set it higher, not lower, to wait longer.
RCH_WORKER=id[,id] (alias RCH_WORKERS, merged)Request specific worker(s); inadmissible ⇒ refused with RCH-Innn + next action.
RCH_PRIORITY=low|normal|highScheduler hint.
RCH_ENV_ALLOWLIST=K1,K2Forward env vars to the remote build (RUSTFLAGS, RUST_LOG, …).
RCH_MAX_REMOTE_ATTEMPTSRemote retry budget (default 3).
RCH_DISABLE_TARGET_REUSE=1Legacy per-job remote target dir instead of the pooled .rch-target-*-pool-*.
RCH_SYNC_TIMEOUT_MSPer-attempt source-upload timeout (1000..=3600000); default is payload-aware.
RCH_COMPRESSION_LEVELTransfer compression (default 3). (RCH_TRANSFER_ZSTD_LEVEL is not read.)
RCH_SSH_SERVER_ALIVE_INTERVAL_SECS / RCH_SSH_CONTROL_PERSIST_SECSKeepalive (recommend 15) / opt-in ControlMaster (default OFF).
RCH_LOG_LEVEL=debugDiagnostics on stderr; surfaces which fail-open path was taken.
RCH_SOCKET_PATHDaemon socket override. (RCH_DAEMON_SOCKET is not read.)
RCH_NO_SELF_HEALING=1 / --no-self-healing / --no-hook-auto-startDisable autostart/hook re-install for one invocation.
RCH_ENABLED=0Disable rch — only effective in the process env of the hook/rch exec, not as an inline prefix inside an agent's command.
RCH_CARGO_WRAPPER_BYPASS=1Shim/toolchain-wrapper bypass → real cargo.
RCH_OUTPUT_FORMAT=json|toon, RCH_JSON=1Machine output.
RCH_CANONICAL_PROJECT_ROOT / RCH_ALIAS_PROJECT_ROOTOverride [path_topology] roots.

RABS (Asupersync-native Accelerated Build Sidecar) — What To Tell Agents

RABS is a new build-caching sidecar inside the rch workspace (11 crates — 10 rabs-* plus rabsd — all publish = false; binaries rabsd, rabs-wrap as a RUSTC_WRAPPER, rabs-wkr). As of 2026-08-25 it is opt-in and shadow-only: rabs-wrap computes a canonical action key, consults the daemon, records a would_have_hit receipt, then execs the real rustc unconditionally — it does not serve cache hits yet (bead bd-k52xe open). Nothing wires RUSTC_WRAPPER automatically; install.sh builds the spine best-effort and scripts/rabs_fleet_deploy.sh deploys it only to bwrap-capable hosts. It is fail-open by design (rabsd --doctor treats a dead daemon as WARN).

Operator surface: rch rabs gc plan|run|history, rch rabs worker reconcile <W> (proposals only), rch rabs doctor, rch rabs inventory. These act on the RABS CAS (~/.cache/rch/rabs-state/cas), not on worker target dirs — don't reach for them for disk pressure. If an agent asks "is RABS making my builds faster?" the honest answer today is no; the win is still rch offload itself.


Reference Index

Recognising what's wrong:

  • references/FAIL_OPEN.md — every [RCH] local (...) reason and refusal line mapped to a self-fix
  • references/ERROR_CODES.mdRCH-Ennn catalog, RCH-Innn refusal codes, RCH-Rnnn runbooks, the three exit-code schemes
  • references/TROUBLESHOOTING.md — diagnostic flow + common errors

Solving specific failure classes:

  • references/RECOVERY_PLAYBOOKS.md — symptom → fix in ≤90 s, lettered playbooks A–P
  • references/SHIM.md — cargo shim, toolchain wrapping, local-build alarm, building locally on purpose
  • references/EXEC_MODES.md — strict remote, job mode, clean overlay, receipts, exec envelope, source-authority locks
  • references/SSH_KEY_RECOVERY.md — when workers.toml references keys this host doesn't have
  • references/PATH_DEPENDENCIES.md — multi-repo workspaces, closure planner, [path_topology]
  • references/DISK_AND_PRESSURE.md — pooled target dirs, rch gc / rch cache, reaper config, RCH-E210..217, sbh handoff
  • references/TELEMETRY_RECOVERY.md — corrupt ~/.local/share/rch/telemetry/telemetry.db
  • references/SELF_HEALING.md — autostart cooldown, temporary bypass + auto-rejoin, [self_healing]
  • references/SSH_TUNING.md — ControlMaster, keepalives, retry classification

Operating in fleets and swarms:

  • references/MULTI_AGENT_CONTENTION.md — TOCTOU, source-authority locks, fleet deploy races, autostart cooldown sharing
  • references/OPERATIONS.md — runbook + worker fleet lifecycle
  • references/WORKERS.md — worker config, lifecycle states, os = gate, Windows workers, fleet commands
  • references/CONFIGURATION.md — every config section/key/default, env vars (incl. the ones that don't exist), runtime paths
  • references/HOOKS.md — hook protocol, per-agent install, what is/isn't intercepted
  • references/MACHINE_INTROSPECTION.md--json shapes, envelope, verified jq recipes
  • references/COMMANDS.md — full command/flag reference (quoted from clap)

Automation scripts (in scripts/):

  • auto_recover.sh — heuristic, dry-run-by-default fleet recovery
  • worker_disk_triage.sh — read-only mount-aware disk report per worker
  • protocol_test.sh — probe the hook protocol with synthetic input
  • multi_agent_safety.sh — flock wrapper for fleet/setup operations
  • mine_rch_history.sh — find prior agent sessions that hit a given failure
  • diagnose-rch.sh — end-to-end diagnostic (health, socket, workers, hook, shim, protocol)
  • validate-setup.sh — verify prerequisites/config/daemon/hook before use

Templates and project docs:


Adjacent Skills

  • sbh — disk-pressure defense for AI coding workloads. Use when RCH-E210/211/215/216 fires and rch gc isn't enough.
  • agent-mail — file reservations and messaging between agents. Use before rch fleet deploy or any worker config edit in a swarm.
  • ntm / vibing-with-ntm — multi-agent tmux orchestration; common parent context for agents that hit rch failures.
  • cass — search prior agent sessions; scripts/mine_rch_history.sh is the fallback when the cass index has dead pointers.
  • provision-new-machine — provisioning a cloud VPS as an rch worker (root env, TMPDIR-only, no global CARGO_TARGET_DIR).

Reading Output: TUI vs Hook

rch with no subcommand is the PreToolUse hook (JSON in, JSON out) — don't run bare rch expecting help; use rch --help. rch dashboard (alias rch tui) and rch web are interactive and block your session; rch dashboard --dump-state / --test-mode are the non-interactive escapes.

Signals

GitHub stars
60
Forks
4
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
rch-dicklesworthstone
Source
github.com/dicklesworthstone/remote_compilation_helper