Remote Training Workflow

The five remote commands prepare one instance, launch text training, pull coherent results, and verify termination readiness. Run them on the Mac from the checkout that owns the canonical inputs. The user launches and terminates Lambda instances. These commands use SSH and GNU rsync; they do not launch instances or perform campaign decisions.

The implementation has local fixture tests, which do not certify an instance. Real Lambda image behavior, tmux, Mac caffeinate, and two-instance continuation need the post-merge qualification below. Plan 10's Phase 2 ran it before its campaign, on the recorded private source, and Stage 7 is complete; a public release or later campaign repeats it on its own source. The result is recorded in specs/findings/reference-configuration.md, not in this guide.

Prerequisites and Configuration

Use GNU rsync 3.2 or newer on the Mac (brew install rsync); the bundled macOS openrsync is refused. SSH must work noninteractively with your existing key. A changed host key stops the command with a manual ssh-keygen -R instruction; the workflow never changes known hosts. The remote prerequisite operation qualifies or installs GNU rsync before uploading code, since rsync must exist to receive the bootstrap script. Full bootstrap then runs from the pushed tree.

conf/remote.yaml holds transport settings, separately from model settings: remote repo, sync interval (600 seconds), in-flight window (120 seconds), untracked cap (10 MB), fixed pulled roots and generated-file ignore patterns. To select an installed GNU executable explicitly, put rsync_path: /opt/homebrew/bin/rsync in a transport YAML and select it before the subcommand:

uv run --locked naics-embedder remote --remote-config conf/remote-local.yaml up --host ubuntu@INSTANCE_IP

That example requires the effective training config to name the canonical manifest. The committed conf/config.yaml keeps its manifest null; the Mac names it in the gitignored conf/local.yaml under the checkout root (config.supervision.manifest_path, a repo-relative path under data/; conf/local.example.yaml shows the form). Both up and train accept --config PATH (default conf/config.yaml) and trailing key=value tokens. Training YAML paths must be repo-relative and present in the pushed checkout. YAML is applied over model defaults, then conf/local.yaml, then the overrides in order, with the last repeated key winning. Missing files and unknown or malformed keys refuse, and so does any conf/local.yaml key other than the two manifests. The CLI forwards argument values literally; overrides are never arbitrary shell commands.

conf/local.yaml is ignored by Git, so it is never pushed and an instance never reads it. The launch argv names the verified manifest explicitly instead, so a run records the same repo-relative manifest string on both hosts.

Build canonical inputs once on the Mac through the data workflow, then put the exact manifest path it prints in conf/local.yaml. Do not rebuild them on an instance. The descriptions parquet and the full validated bundle travel at identical repo-relative data/ paths. Their fingerprints must match on both hosts. A changed parquet or bundle requires a fresh run.

Offline Model Cache

Before a CUDA session becomes ready, up copies the Mac's cached model and tokenizer snapshots into .remote/model-cache/hub on both hosts. MiniLM must resolve to revision 1110a243fdf4706b3f48f1d95db1a4f5529b4d41, matching the reference comparator. Cache files are materialized as regular files and their complete inventories and SHA-256 values are verified. Missing snapshots, changed refs, incomplete weights/tokenizers and altered copies refuse; the workflow downloads nothing and does not repair an existing cache. An existing instance cache is checked before any cache upload, including when it is empty. Staging requires complete single-file weights; snapshots with weight indexes are unsupported.

Fresh and exact-resume wrappers explicitly set HF_HUB_OFFLINE=1 and HF_HUB_CACHE to the verified instance cache. Legacy Transformers cache overrides are unset. Launch checks the cache on both hosts before continuation upload and again before tmux, and records revisions/hashes in the segment provenance. CUDA visibility, the 21-setting identity, constructor guards, budget, checkpoint paths and complete-restore contract are unchanged. Real-instance qualification must verify these cache and process gates; local tests do not certify an instance.

Five Commands

These are operator examples, not commands executed by implementation tests:

uv run --locked naics-embedder remote up --host ubuntu@INSTANCE_IP
uv run --locked naics-embedder remote train seed=1 experiment_name=stage7-reference-s1
uv run --locked naics-embedder remote sync --once
uv run --locked naics-embedder remote status
uv run --locked naics-embedder remote finish

up's overrides prepare inputs; they do not become implicit model overrides for later commands. train resolves the current YAML, conf/local.yaml and its own overrides. Both commands above read the manifest from conf/local.yaml. A supervision.manifest_path=... override given to up instead must also be supplied to train. A legitimate config/input change requires another up before training. Launch validates that code, config and canonical inputs still match the successful ready snapshot. The final launch argv appends the verified repo-relative manifest, literal absolute checkpoint base, recorded accelerator/effective precision and one device after user overrides. Conflicting reserved path overrides refuse; these authoritative arguments do not change saved run identity.

Command Options Result
remote up --host, --force, --config, overrides Push and verify code/inputs; bootstrap; ready session
remote train --resume, --config, overrides Fresh launch, exact continuation, or named finished skip
remote sync --once One verified pull, or ensure detached loop and return
remote finish --stop-training, --pull-edits, --abandon Verified safe result or explicit abandonment
remote status None Read-only JSON observations and errors

Help only parses/displays the interface: it reads no config/state, bootstraps nothing and installs no dependencies. Workflow refusals exit 1. A finished resume skips with its named reason and exits 0. CLI syntax errors use Typer's usage error. There is no arbitrary command, checkpoint path or checkpoint-load-mode switch on this group.

Fresh and Exact Runs

A fresh run requires an absent or actually empty checkpoints/<experiment>/ on both hosts. Any existing file refuses; choose a new experiment. Exact resume requires Mac last.ckpt, every kept checkpoint and both histories from one verified run. It restores the entire directory, verifies every hash remotely, and continues exclusively from last.ckpt.

Use the same remote user and resolved absolute repository/checkpoint path on replacement instances. The path stored by ModelCheckpoint cannot be migrated or rewritten. Persistent .remote/runs/<experiment>.json records survive session replacement. A different remote root, checkpoint base or run identity refuses. A newer/different instance generation refuses before upload: sync first rather than overwrite it with an older Mac copy.

After finishing instance A and preparing B under the same user/path, pass the same training config, manifest, seed and settings with remote train --resume. No saved epoch budget can be extended. The checkpoint's current req11-v1 objective, bundle/codebook, encoder and summaries, all 21 settings, seed and supplementary LoRA controls must agree. Active MoE also checks expert, routing and balancing controls. Missing active controls refuse; inactive MoE controls are ignored. The retired supervision/mining fields are not required. There is no weights migration. Early-stopped runs and runs that spent their saved budget skip before uploads, segment creation, loop start or launch. The finished plan10_smoke is never a continuation/qualification target.

Clock, GPU and Launch Qualification

Bootstrap runs locked dependency synchronization and requires NTP synchronization. NTP is checked again immediately before each launch; false or unavailable synchronization fails closed because monitor timestamps affect margins-first ordering. The remote lockfile hash is verified.

Bootstrap and immediate launch checks enter logical CUDA device 0 and call torch.cuda.is_bf16_supported(including_emulation=False). This must return native support; missing APIs, initialization/property failures, unavailable CUDA and emulation-only support refuse. There is no GPU model whitelist, VRAM threshold or precision fallback. Device name, compute capability, total VRAM and CUDA_VISIBLE_DEVICES are execution metadata; recorded VRAM does not prove that this workload fits. A compatible replacement GPU may differ without changing the run identity.

Probe and launch use the same explicit CUDA visibility (or preserve its unset state). Logical 0 is the first visible device used by the existing one-device Trainer, even on a multi-GPU host. The wrapper sets/unsets that visibility, uses the recorded absolute uv executable and explicit repo working directory, and starts the fixed text command under naics-train tmux with stdin from /dev/null. Training writes an atomic exit code under its immutable segment record. GPU utilization in status is observation, not authorization to launch.

Coherent Pulls and Session Separation

Instance root Mac destination
checkpoints/ checkpoints/
outputs/ outputs/remote/<session>/
logs/ logs/remote/<session>/
.remote/segments/ outputs/remote/<session>/segments/

Every kept checkpoint, last.ckpt, monitor_reads.jsonl and epoch_summary.jsonl travel together. The loop defers the whole run if a member is recent/in-flight or the run is incomplete. Pulls stage under .remote/pulls/, recheck remote inventories, hashes and checkpoint/history identity, and journal verified promotions. Pending promotions block launch and recover before ordinary integrity checks. Failed/unstable copies preserve the previous good generation. Pulls never delete Mac files or prune older kept checkpoints.

The detached Mac loop uses caffeinate -i, logs to .remote/sync.log, retries failures at the persisted interval, and stops on a closed/changed session or matching cooperative stop marker. One-shot sync owns its pass lock. Starting a loop owns the workflow lock. The complete persisted session configuration controls later operations, including custom GNU rsync/intervals; missing, foreign or incomplete snapshots refuse rather than silently use defaults.

status reports host/session, training/tmux/process observations and exit code, GPU, pending dry-run paths, loop ownership, last successful sync, pending promotion and unreachable history. It never repairs evidence. Stale/reused process ownership is reported; restart/signaling applies only to verified owned wrapper/utility identities. Finish proves those processes exit before final verification; a timeout retains evidence and refuses readiness.

Only training and the existing within-run outcome validation monitor run on Lambda. Exports, QCEW/regressor reads, artifact-store operations, margins and decisions stay on the Mac. The instance's selection log returns under logs/remote/<session>/; never merge it into the Mac's append-only logs/selection_log.jsonl. Never open sealed test or outer splits here.

Finish, Edits and Failure Boundaries

finish quiesces the owned loop, verifies previous successful Mac hashes before pulling, checks training stopped, performs a final coherent pull including recent files, and requires zero content/type checksum differences across every mapping. A previously synced Mac file changed or missing is a refusal before repair; preserve that evidence. Pending files/promotions, unhandled instance edits and a late training/process observation also withhold readiness.

--stop-training sends the fixed interrupt to the owned segment, waits within a bounded timeout, and loses work since the last checkpoint. It never forcibly kills training. --pull-edits rescues changed/new/deleted code evidence into a new immutable .remote/instance-edits/<session>/<rescue-id>/, with verified bytes and deletion tombstones. It never writes instance edits into the Mac working tree. A changed edit snapshot needs another rescue; previous rescue evidence is never overwritten.

Only safe=true prints exactly Safe to terminate, followed by the latest owned segment's checkpoint and matching local/remote SHA-256. A verified session with no checkpoint says no checkpoint explicitly. An unreachable instance cannot produce a safe result. Explicit --abandon stops the owned Mac loop, journals the last good sync and closes the session with a possible-data-loss warning; it never prints the safe phrase. Abandon cannot combine with stop or edit rescue. The user still terminates the instance.

Same-host up refuses while training is active, including with --force. Force can record the risk of leaving an unfinished prior host or a reused host marker and overwrite named instance code edits after recording discard evidence. It cannot bypass canonical/resume/GPU/NTP checks, active training or final verification. There is no force-safe finish option.

Post-merge Qualification

After review, verification and merge, qualify image/bootstrap/native BF16/NTP, tmux, caffeinate and cross-instance continuation before relying on them. Use a new dedicated experiment with an unchanged larger epoch budget, interrupt after a completed epoch while unfinished, finish A, then restore/resume on B under the same user/absolute path. Verify next-epoch continuation, history preservation, all kept files and matching hashes. Never relaunch plan10_smoke, alter a saved budget, or use a campaign seed as the qualification experiment. Fixture checks never mark it passed. Plan 10's Phase 2 ran this procedure before seed 1 on the recorded private source; a public release or later campaign runs it again on its own source, as the open item in specs/deferred_items.md records.

The checkout's gitignored conf/local.yaml names the manifests; no private config commit is replayed or held. The held config SHAs that completed plans and findings cite stay reachable locally under refs/held/* in the primary clone; never push them.

Plan 10's Phase 2 (Task 18 onward) is finished history. It trained ten reference seeds on Lambda and fixed each panel's δ = 3 SD on the Mac before configuration selection (specs/findings/reference-configuration.md). Stage 7 is stamped COMPLETE (2026-10-07) in specs/naics-embedding-roadmap.md, and Plan 10 is under specs/plans/completed/. See text training and remote API.

The uv.lock freeze held through Stage 8's decision and was released when the representation split merged (specs/naics-representation-split.md, Req 19). Decisions pair reads only on bit-identical regressor rows, so the growth margin read and every economic candidate read share one platform and one uv.lock.