Skip to content

provider: verify CAPE contract against real CLI and fix run face + endpoint discovery - #157

Merged
Meirtz merged 1 commit into
masterfrom
fix/cape-real-cli
Jul 23, 2026
Merged

provider: verify CAPE contract against real CLI and fix run face + endpoint discovery#157
Meirtz merged 1 commit into
masterfrom
fix/cape-real-cli

Conversation

@Meirtz

@Meirtz Meirtz commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #155. The CAPE provider's assumed CLI contract has now been verified line-by-line against the real CAPE source, and the mismatches are fixed. Every item on the README's verification checklist has a source-level answer.

What matched (no change needed): status/logs/cancel argv faces, single-line bare request-id stdout, terminal-state set, request_id echo cross-check, --token-argv-only auth, --bind/--env syntax, the serial one-request-per-session model.

What didn't — fixed here:

  • cape run requires --task-id (now sent) and has no --workspace flag (removed; the workspace is created by the boot script via mkdir -p && cd, and --cwd is dropped since no adapter creates missing dirs). pool/user/meta_root now fail fast when unset (the real parser exits 2 otherwise).
  • Endpoint discovery redesigned: real CAPE has no streaming logs — cape logs returns empty stdout until the workload exits, so stdout-marker + logs-polling could never converge for a long-lived runtime server. Discovery now uses a per-sandbox rw-bound marker file (meta_root config, mounted at /agentix-meta; the boot script writes host port there before exec'ing the bundle bootstrap; the provider polls the host side, interleaved with cape status terminal checks, and fetches logs only on terminal states for diagnostics).
  • cancel that reports an unknown request (404) is treated as already-gone so bookkeeping can't stick after a journal-less controller restart; request-id regex tightened to req-\d{6,}; podman's lack of host networking (client-assigned-port scheme needs apptainer/bubblewrap) documented.

Testing

  • Fake cape rewritten to model the real argparse table (rejects --workspace, requires --pool/--user/--task-id, req-%06d ids, empty logs while RUNNING, 404 stderr JSON on unknown cancel) and actually executes the boot script under sh -c.
  • uv run pytest tests/ plugins/ -q718 passed; ruff clean; pyright 0 errors; uv lock --check current.
  • Real-CAPE local e2e: self-deployed controller + node agent (local-process adapter, auth enabled) → provider.create() → marker-file discovery → GET /health 200 → delete() → request CANCELLED, meta dir removed, port released — 11/11 assertions PASS.

🤖 Generated with Claude Code

…dpoint discovery

The CAPE provider shipped with an assumed CLI contract. Every verb,
flag, and output shape has now been verified item by item against the
CAPE source, and the mismatches are fixed.

What matched: the run/status/logs/cancel verb set; --controller-url/
--token on every verb; the run flag table (--image, --gpus,
--cpu-cores, --memory-gb, --gpu-mode whole, --isolation-policy,
--runtime-adapter, --max-duration-seconds, --session-key, --bind,
--env, `-- <workload>`); "stdout is exactly one request id" without
--wait (real ids are req-%06d; the regex is tightened to req-\d{6,});
the status JSON on stdout with the terminal-state set {COMPLETED,
FAILED, CANCELLED, EXPIRED, LOST, INFEASIBLE}, a request_id echo, and
no stdout/stderr fields; cancel --reason with idempotent terminal
no-op; token passable only as --token argv (no env/file support in the
CLI); the serial session model.

What did not match (submit-fatal argv bugs): --task-id is required by
the real parser and was never passed (now = sandbox id); --pool/--user
are required but were optional (now checked at first use, fail-fast);
--workspace does not exist (argparse rc=2 "unrecognized arguments");
--cwd pointed at a directory no component creates, so the command
would die at process start.

The architectural finding: CAPE has no streaming logs. `cape logs`
succeeds for RUNNING requests but returns empty output until the
command exits (the node agent reports stdout/stderr once, after
reaping the process), and there is no native endpoint query. The old
stdout-marker + logs-polling discovery could therefore never see the
marker of a never-exiting runtime server: every create() would burn
the full 600s budget and fail. Discovery is redesigned around a
marker file through a rw bind: a new required meta_root config names a
submitter-and-node-visible directory (same shared-FS assumption as
bundle); each sandbox gets <meta_root>/<sandbox_id> bound at
/agentix-meta; the boot script now does `mkdir -p <ws> && cd <ws>`,
writes `AGENTIX_ENDPOINT <host> <port>` to /agentix-meta/endpoint, and
execs the bundle bootstrap; the provider polls the host side of the
marker file interleaved with `cape status` terminal-state checks, and
fetches logs only on terminal states (where they are populated) for
diagnostics. delete() removes the meta dir after a confirmed cancel,
and treats a controller 404 on cancel as already-gone so bookkeeping
cannot stick forever after a journal-less controller restart.

Docs now state the host-network constraint: the client-assigned-port
scheme needs apptainer/bubblewrap (host network stack); CAPE's podman
adapter has no host networking, so podman-only pools cannot work.

The fake cape in the tests now models the real contract (argparse-
mirrored run parser rejecting --workspace and requiring --pool/--user/
--task-id with rc=2; req-%06d ids; logs empty while RUNNING; cancel
404 stderr JSON) and really executes the boot script under sh -c with
a stubbed node environment, so discovery is tested against a marker
file the actual script wrote.

Validated against a real local CAPE deployment (controller + node
join, local-process adapter, auth enabled): create() submitted a
request the real parser accepted, discovery found the marker while
real `cape logs` returned empty, GET /health answered 200 on the
discovered URL, and delete() drove the request to CANCELLED, reaped
the workload process group, and removed the meta dir - 11/11 e2e
assertions passed. The local-process adapter ignores bind specs, so
that run exercises the boot script's documented host-path fallbacks;
container-adapter bind behavior still needs a Linux pool.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@Meirtz
Meirtz merged commit 6e5534c into master Jul 23, 2026
5 checks passed
@Meirtz
Meirtz deleted the fix/cape-real-cli branch July 23, 2026 04:24
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