Skip to content

lab: worker job interface (local and SSH transports) - #21

Merged
AdvayMonga merged 10 commits into
mainfrom
lab/worker
Oct 10, 2026
Merged

AdvayMonga merged 10 commits into
mainfrom
lab/worker

Conversation

@AdvayMonga

Copy link
Copy Markdown
Owner

Stacked on #20, which is stacked on #19. Retarget once those merge.

What

lab/worker.py: the job interface from lab/RUNTIME.md, built for the measured jobs.

Job Inputs Returns Out dir
test tree {lint, tests}
measure tree {results, ready_s} engine log; device/telemetry/client rows when passive
equiv tree, reference, prior the gate's result equiv.json, equiv.outputs.jsonl, passive data
profile tree {returncode, output, …} the bundle or instrument files
  • bench, submit and the base measurement share measure. profile, trace, kernel and hostprof share profile; the instrument definitions moved into a pure proftools.instrument(kind, args).
  • The tools keep the policy, the ledger and the budget, and call self.worker. Ledger records and the agent-facing text are unchanged.
  • LAB_WORKER=local (default) runs jobs in process.
  • LAB_WORKER=ssh runs them on the VM lab.vm names:
    • each input tree goes by content hash, at most once per worker (has, put);
    • the worker re-hashes a fresh copy right before each job and refuses a mismatch, so what is measured is what the controller audited;
    • the out dir comes back as a tar;
    • GpuBusy, Contaminated, NotReady and ValueError are raised again on the controller.

Kept on purpose

Calling equiv again on the same snapshot reuses its earlier answers; it never re-rolls the statistical gate. In process this came from the answers file living in the run dir. With a fresh out dir per job it would have been lost, so equiv now ships those answers as the prior input. A new test marks the stored answers and checks the mark survives a second equiv; it fails without the fix.

Tested

  • Remote end to end through a local shell running the same python -m lab.worker CLI that SSH would run:
    • a job runs on the shipped tree, and the tree ships once;
    • GpuBusy, with its facts, crosses from a fake nvidia-smi;
    • NotReady crosses, and the engine log still comes back;
    • a cached tree that changed after put is refused;
    • a put whose content doesn't match its hash is refused.
  • All existing lab tests pass on the local transport. macOS: 235 passed. Linux (Docker-in-Docker, container jail live): the worker tests pass inside the jail; the remaining failures are the test box's (old pytest, no git SHA).
  • Not tested: LAB_WORKER=ssh against a real VM.

Not in this PR

The agent's side on the worker: the agent container with freeze-before-measure, the API-key proxy, and the agent/exec/snapshot/restore/facts jobs.

🤖 Generated with Claude Code

AdvayMonga and others added 5 commits October 9, 2026 00:11
srt cannot jail GPU code on Linux: no device passthrough, and each jail gets its own
network namespace, so a served engine is unreachable from the clients. On Linux,
agent code (tests, profilers, served engines) now runs in one container per command:
NVIDIA runtime, --network none, read-only root, non-root user, no capabilities,
cpuset leaving cores for the clients. A served port reaches the host through a socat
bridge over a unix socket. srt stays on macOS; LAB_JAIL overrides.

vm-setup.sh installs docker + NVIDIA Container Toolkit, builds lab-jail, and checks the
GPU inside it. lab/RUNTIME.md is the controller/worker design this starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A leftover process on the GPU could slow a bench, equiv or submit or stop the engine
from starting, and the ledger recorded that as the snapshot's result; a contaminated
base measurement was cached and skewed every later submit.

serve.Served now refuses to launch while any process holds the GPU (GpuBusy, nothing
launched) and polls the GPU's processes every second while serving; anything outside
the engine's own process tree (descendants, or the container's processes) makes
exclusive() raise Contaminated. bench/equiv/submit/baseline record verdict refused or
contaminated with gpu_processes as facts; a contaminated run is never evidence, never
cached as the base, and is caught before the holdout guard spends a query.
lab.validate's noise bands get the same check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPU work behind test, profile/trace/kernel/hostprof, bench, equiv, submit and the
base measurement now runs as worker jobs (lab/worker.py: test, measure, equiv, profile).
A job takes input trees and JSON args, writes an out dir and returns JSON; the tools keep
the policy, the ledger and the budget and call self.worker.

LAB_WORKER=local (default) runs jobs in process, as before. LAB_WORKER=ssh runs them on
the lab.vm VM: each tree goes by content hash at most once (has/put), the worker
re-hashes a fresh copy before every job and refuses a mismatch, and the out dir comes back
as a tar. GpuBusy, Contaminated, NotReady and ValueError are raised again on the
controller. equiv ships the snapshot's earlier answers with the job, so a second equiv
never re-rolls the gate (it already did not, in process).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…10-09)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he dependency build step (approved 2026-10-09)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AdvayMonga
AdvayMonga changed the base branch from lab/gpu-exclusive to main October 10, 2026 17:31
@AdvayMonga
AdvayMonga merged commit cb28577 into main Oct 10, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant