Repository navigation
feat(mocactl): workloads create/get/delete and spawn --workload - #520
Conversation
mocactl workloads create --context FILE creates a workload, streams the context file to Context Service with the one-time grant, and activates the uploaded revision. mocactl spawn --workload ID runs the subagent on that workload's context. Replaces kubectl cp seeding of sandbox fixtures (rossoctl#517). Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Jeremy Cohn <Jeremy.Cohn@ibm.com>
ingpaolodettori-dev
left a comment
There was a problem hiding this comment.
The client matches main's /workloads routes, grant fields, name rule, error codes and media type. spawn --workload goes over sync POST /v1/runs with the session token, the only route that resolves workloads. The create rollback and the single fresh-grant retry look correct, and the tests are good (retry, rollback, rollback failure, size limit, unsafe URL, real loopback stream). CI is green. Two non-blocking suggestions and one nit are inline.
| try { | ||
| created = await client.create(opts.name ? { name: opts.name } : {}, signal); | ||
| } catch (err) { | ||
| if (aborted(signal)) return 130; |
There was a problem hiding this comment.
suggestion: A Ctrl-C or network drop after POST /workloads was sent can leave a workload the server created that the client never learned the id of. There's no list route, so without --name it can't be found, and it holds one of the per-user quota slots (5 by default). Consider printing a hint here, or generating the name client-side (wl-<random>) when --name is omitted so the id is always known before the request.
There was a problem hiding this comment.
Thanks. Addressed in #532 (writable per-run copy, stacked on this PR).
| io.err('--workload needs --kind prompt: the harness runs only prompts on a workload'); | ||
| return 2; | ||
| } | ||
| if (!validWorkloadId(opts.workloadId)) { |
There was a problem hiding this comment.
suggestion: spawn --workload doesn't check the workload before creating a session and forking the worker, so a typo or a not-yet-activated workload only shows up later as a failed row (workload_not_found / workload_not_ready). A cheap GET /workloads/{id} preflight (exit 1 unless status === 'ready') would fail fast. Optional.
There was a problem hiding this comment.
Thanks. Addressed in #532 (writable per-run copy, stacked on this PR).
| method: 'PUT', | ||
| headers: { | ||
| authorization: `Bearer ${grant.token}`, | ||
| 'content-type': CONTEXT_MEDIA_TYPE, |
There was a problem hiding this comment.
nit: asGrant keeps grant.contentType, but the PUT always sends CONTEXT_MEDIA_TYPE. They're equal today (the server enforces it), but sending grant.contentType would make the field meaningful. Otherwise drop it from UploadGrant.
There was a problem hiding this comment.
Thanks. Addressed in #532 (writable per-run copy, stacked on this PR).
evaline-ju
left a comment
There was a problem hiding this comment.
Independently probed uploadUrlProblem on hostile inputs — correctly refuses AWS metadata, confusable 127.0.0.1.evil.com, file:/ftp: schemes. validWorkloadId correctly refuses path traversal, newline injection, 51-char ids. encodeURIComponent on top of the regex is defense in depth.
Author: moonlight16 (MEMBER) · CI green
A workload run may set contextWritable: true (mocactl spawn --workload ID --writable). That run gets its own writable copy of the workload's files, discarded when the run ends; the shared cache stays read-only. The cache now keeps exec bits while removing write permission. Also addresses rossoctl#520 review: workloads create generates the name client-side, spawn --workload checks the workload is ready first, and the upload sends the grant's content type. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Jeremy Cohn <Jeremy.Cohn@ibm.com>
A workload run may set contextWritable: true (mocactl spawn --workload ID --writable). That run gets its own writable copy of the workload's files, discarded when the run ends; the shared cache stays read-only. The cache now keeps exec bits while removing write permission. Also addresses rossoctl#520 review: workloads create generates the name client-side, spawn --workload checks the workload is ready first, and the upload sends the grant's content type. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Jeremy Cohn <Jeremy.Cohn@ibm.com>
Part of the work split out of #455 (the original context-delegation PR). Implements the client side agreed in #495 (one CLI for delegating subagent tasks) and the short-term option in #517 (seed sandbox files before a leaf runs).
What this adds
mocactl workloads create --context FILE [--name N] [--json]: creates a workload, uploads the context file straight to Context Service with the one-time grant, and activates the uploaded revision. Prints the workload ID. If the grant has expired it gets a fresh one and retries once; if anything fails after the workload exists, it deletes it so the quota is not used up.mocactl workloads get IDandmocactl workloads delete ID, both with--json.mocactl spawn "prompt" --kind prompt --workload ID: the subagent runs with that workload's files (feat: run tasks on a workload's context in shared sandboxes #494, run tasks on a workload's context). The workload is recorded in the subagent ledger and shown bysubagents --json.The file is made with
contextctl ctx export;mocactldoes not callcontextctl. In the CLI the uploaded thing is called a context, since "bundle" already means config bundles inmocactl.For demos that seed fixtures with
kubectl cp:contextctl ctx export ./repo -o repo.context, thenmocactl workloads create --context repo.context, thenmocactl spawn --workload <id> .... No cluster access is needed, and MOCA puts the files on whichever sandbox a task lands on. Workload files are read-only today; a writable per-run copy is the next PR.🤖 Generated with Claude Code