From bb5de0e2fa72204675fba32dc34419b3061fbc95 Mon Sep 17 00:00:00 2001 From: Anderson Leal Date: Tue, 12 May 2026 15:08:06 -0300 Subject: [PATCH 1/2] docs(skills): markdown bodies for worker and sandbox skill surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-only review branch. Pairs with feature/worker-manager-trigger, which carries the code that registers these bodies with the `skills` worker on boot (via include_str! from the same paths). This branch is for prose review in isolation; it does not build standalone — the include_str! call sites live on the code branch. Content map: crates/iii-worker/src/cli/worker_manager_daemon/skills/ router.md iii://worker — router for the 8 worker::* ops add.md iii://worker/add remove.md iii://worker/remove update.md iii://worker/update start.md iii://worker/start stop.md iii://worker/stop list.md iii://worker/list clear.md iii://worker/clear schema.md iii://worker/schema crates/iii-worker/src/sandbox_daemon/skills/ router.md iii://sandbox — router for the 4 sandbox::* ops create.md iii://sandbox/create exec.md iii://sandbox/exec stop.md iii://sandbox/stop list.md iii://sandbox/list Each leaf carries a uniform agent-facing shape: H1 with one-line action 1-2 sentence summary (non-obvious contract surfaced) - id / timeout / idempotent / request / response (bulleted metadata) ## Example (complete request + response payloads) ## Errors (every code with cause + fix hint) Total ~16 KiB across 14 files (worker 9.7 KiB, sandbox 6.3 KiB). Sandbox bodies cross-reference against the actual on-disk daemon (crates/iii-worker/src/sandbox_daemon/{create,exec,stop,list,errors}.rs) — field names, codes, and envelope shapes are pinned to the real sdk_contract. Each leaf validates against the planned crate::skills::validate_leaf (50–4096 bytes, H1 required, [a-z0-9_-] segments). --- .../cli/worker_manager_daemon/skills/add.md | 34 +++++++++++++ .../cli/worker_manager_daemon/skills/clear.md | 28 +++++++++++ .../cli/worker_manager_daemon/skills/list.md | 37 ++++++++++++++ .../worker_manager_daemon/skills/remove.md | 25 ++++++++++ .../worker_manager_daemon/skills/router.md | 29 +++++++++++ .../worker_manager_daemon/skills/schema.md | 30 ++++++++++++ .../cli/worker_manager_daemon/skills/start.md | 33 +++++++++++++ .../cli/worker_manager_daemon/skills/stop.md | 31 ++++++++++++ .../worker_manager_daemon/skills/update.md | 33 +++++++++++++ .../src/sandbox_daemon/skills/create.md | 45 +++++++++++++++++ .../src/sandbox_daemon/skills/exec.md | 49 +++++++++++++++++++ .../src/sandbox_daemon/skills/list.md | 45 +++++++++++++++++ .../src/sandbox_daemon/skills/router.md | 20 ++++++++ .../src/sandbox_daemon/skills/stop.md | 29 +++++++++++ 14 files changed, 468 insertions(+) create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/clear.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/list.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/router.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/schema.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md create mode 100644 crates/iii-worker/src/cli/worker_manager_daemon/skills/update.md create mode 100644 crates/iii-worker/src/sandbox_daemon/skills/create.md create mode 100644 crates/iii-worker/src/sandbox_daemon/skills/exec.md create mode 100644 crates/iii-worker/src/sandbox_daemon/skills/list.md create mode 100644 crates/iii-worker/src/sandbox_daemon/skills/router.md create mode 100644 crates/iii-worker/src/sandbox_daemon/skills/stop.md diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md new file mode 100644 index 000000000..804fd0ac0 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md @@ -0,0 +1,34 @@ +# worker/add — install a worker + +Install from the iii registry or an OCI image. Writes the entry to `iii.config.yaml`, caches the artifact under `~/.iii/managed/{name}/`, and pins the resolved version in `iii.lock`. Calling twice yields the same outcome. + +- id: `worker::add` +- timeout: 600s (registry pull + binary fetch + ready wait) +- idempotent: yes +- request: `AddOptions { source, force?, reset_config?, wait? }` +- response: `AddOutcome { name, version?, status, awaited_ready, config_path }` + +`source` variants: +- `{ "kind": "registry", "name": "image-resize", "version": "0.1.2" }` — registry slug, optional pinned semver. +- `{ "kind": "oci", "reference": "ghcr.io/iii-hq/node:latest" }` — full OCI ref. +- `{ "kind": "local", "path": "./..." }` — **CLI only**; over the trigger this returns **W102**. + +`status` values: `installed` (new), `already_current` (lockfile match), `repaired` (cache was corrupt), `replaced` (different version was installed before). + +## Example + +```json +{ + "source": { "kind": "registry", "name": "image-resize", "version": "0.1.2" }, + "force": false, + "reset_config": false, + "wait": true +} +``` + +## Errors + +- **W101** missing/malformed `source`. +- **W102** local path via trigger. +- **W110** worker name not in registry. +- **W900** OCI pull, network, or filesystem failure (see `details.message`). diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/clear.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/clear.md new file mode 100644 index 000000000..1a4c23bcb --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/clear.md @@ -0,0 +1,28 @@ +# worker/clear — wipe cached worker artifacts + +Delete cached artifacts under `~/.iii/managed/{name}/`. The worker's entry in `iii.config.yaml` and `iii.lock` is **not** touched — call [`worker/remove`](iii://worker/remove) for that. Useful after `worker/remove` to reclaim disk, or before `worker/add --force` to force a clean re-download. + +- id: `worker::clear` +- timeout: 30s +- idempotent: yes (second call clears 0 bytes) +- request: `ClearOptions { names: [], all?, yes }` +- response: `ClearOutcome { cleared_bytes }` + +Same target rules as [`worker/remove`](iii://worker/remove): either `names` *or* `all: true`, never both, never neither, always `yes: true`. + +## Example + +```json +{ "all": true, "yes": true } +``` + +Response: +```json +{ "cleared_bytes": 812727797 } +``` + +## Errors + +- **W103** target missing or ambiguous. +- **W104** consent missing. +- **W900** filesystem failure (rare). diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/list.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/list.md new file mode 100644 index 000000000..7a43f5ec6 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/list.md @@ -0,0 +1,37 @@ +# worker/list — list workers and run state + +Read the union of every worker in `iii.config.yaml`, every artifact on disk under `~/.iii/managed/`, and every running process. + +- id: `worker::list` +- timeout: 10s +- idempotent: yes (pure read) +- request: `ListOptions { running_only? }` +- response: `ListOutcome { workers: [WorkerEntry] }` + +`WorkerEntry`: +- `name` — config name. +- `version` — string for registry-tracked workers; **omitted from JSON** for engine builtins that aren't lock-tracked. +- `running` — bool. +- `pid` — u32 when discoverable via ps; `null` for engine builtins. + +## Example + +Request: +```json +{ "running_only": false } +``` + +Response: +```json +{ + "workers": [ + { "name": "iii-stream", "running": true, "pid": null }, + { "name": "image-resize", "running": true, "pid": 37037, "version": "0.1.2" }, + { "name": "skills", "running": true, "pid": 37036, "version": "0.2.4" } + ] +} +``` + +## Errors + +- **W900** filesystem read failure (rare — config/lock both unreadable). diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md new file mode 100644 index 000000000..26c0d5de9 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md @@ -0,0 +1,25 @@ +# worker/remove — uninstall workers + +Remove a worker's entry from `iii.config.yaml`. The engine's file watcher tears down any running sandbox. Cached artifacts under `~/.iii/managed/{name}/` are **not** deleted — call [`worker/clear`](iii://worker/clear) for that. + +- id: `worker::remove` +- timeout: 30s +- idempotent: yes +- request: `RemoveOptions { names: [], all?, yes }` +- response: `RemoveOutcome { removed: [string] }` + +Either explicit `names` *or* `all: true`, never both, never neither. `yes: true` is always required. + +## Example + +```json +{ "names": ["image-resize"], "yes": true } +``` + +Also valid: `{ "names": ["image-resize", "skills"], "yes": true }`, `{ "all": true, "yes": true }`. + +## Errors + +- **W103** `names` empty and `all` unset, or both set. +- **W104** `yes` is not `true`. +- **W900** filesystem failure writing `iii.config.yaml`. diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/router.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/router.md new file mode 100644 index 000000000..f47892937 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/router.md @@ -0,0 +1,29 @@ +# worker — install and manage workers + +Owned by the `iii-worker-ops` daemon (auto-spawned as an engine sidecar). Every op below is also callable as `iii worker ` on the CLI; the trigger surface is the SDK path. + +## Operations + +- [`worker/add`](iii://worker/add) — install from registry or OCI reference +- [`worker/remove`](iii://worker/remove) — uninstall (consent required) +- [`worker/update`](iii://worker/update) — re-resolve registry versions +- [`worker/start`](iii://worker/start) — start a configured worker +- [`worker/stop`](iii://worker/stop) — stop a running worker (consent required) +- [`worker/list`](iii://worker/list) — installed + run state + versions +- [`worker/clear`](iii://worker/clear) — wipe cached artifacts (consent required) +- [`worker/schema`](iii://worker/schema) — discover request/response shapes + +Live data: [`iii://fn/worker/schema`](iii://fn/worker/schema) returns the full JSON Schema for all 8 ops, plus per-op `default_timeout_ms` and `idempotent` hints. + +## Error envelope + +All ops return errors as `{ "type": "WorkerOpError", "code": "Wxxx", "message": "...", "details": {...} }`. + +Codes: +- **W100** InvalidName — name doesn't match `[a-z0-9_-]{1,64}`. +- **W101** InvalidSource — required field missing or malformed. +- **W102** LocalPathNotAllowedViaTrigger — `kind: "local"` is CLI-only. +- **W103** MissingTarget — empty `names` + `all` unset, or both set. +- **W104** ConsentRequired — destructive op needs `yes: true`. +- **W110** NotFound — worker not in registry / not installed. +- **W900** Internal — unexpected failure. diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/schema.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/schema.md new file mode 100644 index 000000000..4f7bfb533 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/schema.md @@ -0,0 +1,30 @@ +# worker/schema — discover trigger schemas + +Introspect the JSON Schemas for every `worker::*` trigger. Each entry carries field descriptions, defaults, types, plus per-op `default_timeout_ms` and `idempotent` hints. Construct payloads from this without source-diving. + +- id: `worker::schema` +- timeout: 10s +- idempotent: yes (pure read) +- request: `SchemaRequest { function_id? }` +- response: `SchemaResponse { schemas: [SchemaEntry] }` + +## Example + +Omit `function_id` to list all 8 ops: +```json +{} +``` + +Each `SchemaEntry`: +```json +{ + "function_id": "worker::add", + "description": "Install a worker from registry name or OCI ref", + "request": { "...": "JSON Schema for AddOptions" }, + "response": { "...": "JSON Schema for AddOutcome" }, + "default_timeout_ms": 600000, + "idempotent": true +} +``` + +Also reachable inline as a section URI: [`iii://fn/worker/schema`](iii://fn/worker/schema). diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md new file mode 100644 index 000000000..a1a480f5b --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md @@ -0,0 +1,33 @@ +# worker/start — start a configured worker + +Spawn a worker that's already in `iii.config.yaml`. The engine connects to the process over its WebSocket port and waits for the ready signal. + +- id: `worker::start` +- timeout: 60s +- idempotent: no (stateful — starting an already-running worker is a no-op or error depending on health) +- request: `StartOptions { name, port?, config?, wait? }` +- response: `StartOutcome { name, pid?, port? }` + +- `name` — installed worker name. +- `port` — override the engine WS port (default = engine's `iii-worker-manager` port). +- `config` — YAML config file forwarded as `--config `. Binary workers only; OCI ignores it. +- `wait` — block until ready. Default `true`. + +`pid` and `port` may be `null` for engine builtins that don't surface a process (e.g. `iii-stream`, `iii-http`). + +## Example + +```json +{ "name": "image-resize", "wait": true } +``` + +Response: +```json +{ "name": "image-resize", "pid": 12345, "port": 49134 } +``` + +## Errors + +- **W100** invalid name. +- **W110** worker not installed. +- **W900** spawn failure, ready-wait timeout, or port-bind failure. diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md new file mode 100644 index 000000000..198990cb1 --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md @@ -0,0 +1,31 @@ +# worker/stop — stop a running worker + +Send a graceful shutdown signal. Destructive — requires explicit `yes: true` consent on the trigger surface. The CLI prompts interactively or accepts `-y`. + +- id: `worker::stop` +- timeout: 30s +- idempotent: no (stateful) +- request: `StopOptions { name, yes }` +- response: `StopOutcome { name, stopped }` + +`yes` must be exactly `true` — not `false`, not omitted, not the string `"true"`, not the number `1`. A slip in caller code should not silently kill a worker. + +`stopped: false` means the stop didn't take effect within the daemon's grace window. Retry, or verify with [`worker/list`](iii://worker/list). + +## Example + +```json +{ "name": "image-resize", "yes": true } +``` + +Response: +```json +{ "name": "image-resize", "stopped": true } +``` + +## Errors + +- **W100** invalid worker name (shell metacharacters, empty, > 64 chars). +- **W104** `yes` not `true`. +- **W110** worker name unknown. +- **W900** signal failure or grace-window timeout. diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/update.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/update.md new file mode 100644 index 000000000..c087316cf --- /dev/null +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/update.md @@ -0,0 +1,33 @@ +# worker/update — re-resolve registry versions + +Re-resolve each named worker against the registry, download newer artifacts if available, and rewrite `iii.lock`. Configs in `iii.config.yaml` are preserved. + +- id: `worker::update` +- timeout: 600s +- idempotent: yes +- request: `UpdateOptions { names: [] }` +- response: `UpdateOutcome { updated: [{ name, from_version, to_version }] }` + +`names: []` updates every installed registry-backed worker. `updated` contains one entry per worker that actually changed version; workers already at latest are omitted. + +## Example + +Request: +```json +{ "names": [] } +``` + +After a no-op: +```json +{ "updated": [] } +``` + +After a real change: +```json +{ "updated": [{ "name": "image-resize", "from_version": "0.1.2", "to_version": "0.1.3" }] } +``` + +## Errors + +- **W110** name not installed. +- **W900** registry / network / filesystem failure. diff --git a/crates/iii-worker/src/sandbox_daemon/skills/create.md b/crates/iii-worker/src/sandbox_daemon/skills/create.md new file mode 100644 index 000000000..3b4e9706a --- /dev/null +++ b/crates/iii-worker/src/sandbox_daemon/skills/create.md @@ -0,0 +1,45 @@ +# sandbox/create — spin up a sandbox + +Pull an image from the catalog and start a sandbox VM. Returns a UUID `sandbox_id` you reuse for [`sandbox/exec`](iii://sandbox/exec) and [`sandbox/stop`](iii://sandbox/stop). + +- id: `sandbox::create` +- timeout: 600s (image pull dominates cold starts) +- idempotent: no (each call spawns a new sandbox) +- request: `CreateRequest { image, cpus?, memory_mb?, name?, network?, idle_timeout_secs?, env: [] }` +- response: `CreateResponse { sandbox_id, image }` + +- `image` — preset (`python`, `node`) or full OCI ref in the catalog. Empty catalog denies every call (fail-closed). +- `env` — `Vec` of `"K=V"` entries, NOT a map. +- `name` — optional human label, surfaced in [`sandbox/list`](iii://sandbox/list). + +There is no `cwd` field; the workdir is fixed by the rootfs. + +## Example + +```json +{ + "image": "ghcr.io/iii-hq/node:latest", + "env": ["NODE_ENV=production"], + "cpus": 2, + "memory_mb": 1024 +} +``` + +Response: +```json +{ + "sandbox_id": "550e8400-e29b-41d4-a716-446655440000", + "image": "ghcr.io/iii-hq/node:latest" +} +``` + +`sandbox_id` is a UUID, not the `sbx_*` opaque-id shape some other engines use. + +## Errors + +- **S001** invalid request (malformed `image`, missing required field). +- **S100** image not in catalog. Add it to the catalog or use a preset. +- **S101** rootfs missing on disk — run `iii worker add ` first. +- **S102** auto-install failed (transient — retry). +- **S300** VM boot failed. +- **S400** resource limit hit (too many concurrent sandboxes). diff --git a/crates/iii-worker/src/sandbox_daemon/skills/exec.md b/crates/iii-worker/src/sandbox_daemon/skills/exec.md new file mode 100644 index 000000000..720b1b296 --- /dev/null +++ b/crates/iii-worker/src/sandbox_daemon/skills/exec.md @@ -0,0 +1,49 @@ +# sandbox/exec — run a command in a sandbox + +Execute a command in a running sandbox and stream stdout/stderr back. Per-sandbox serialization: only one exec runs at a time — a concurrent call against the same `sandbox_id` returns **S003**. The call returns when the child exits, the timeout fires, or the VM becomes unreachable. + +- id: `sandbox::exec` +- timeout: caller-set (pass `timeout_ms`) +- idempotent: no +- request: `ExecRequest { sandbox_id, cmd, args?, stdin?, env?, timeout_ms?, workdir? }` +- response: `ExecResponse { stdout, stderr, exit_code?, timed_out, duration_ms, success }` + +- `cmd` is the binary as a single string, NOT an argv array. +- `args` is the argv tail (`Vec`). +- `stdin` is base64-encoded bytes. +- `env` is `Vec` of `"K=V"` entries. + +## Example + +```json +{ + "sandbox_id": "550e8400-e29b-41d4-a716-446655440000", + "cmd": "bash", + "args": ["-lc", "echo hello && date"], + "env": ["FOO=bar"], + "workdir": "/workspace", + "timeout_ms": 30000 +} +``` + +Response: +```json +{ + "stdout": "hello\nMon Oct 13 19:42:11 UTC 2025\n", + "stderr": "", + "exit_code": 0, + "timed_out": false, + "duration_ms": 42, + "success": true +} +``` + +`success` is `true` iff `exit_code == 0` and `timed_out == false`. `exit_code: 127` means "command not found"; `126` means "not executable". Per POSIX, spawn failures surface in `exit_code`, NOT as an error envelope. + +## Errors + +- **S001** invalid request (bad `sandbox_id` UUID, missing `cmd`). +- **S002** sandbox not found. +- **S003** concurrent exec — await the previous one first. +- **S200** exec timed out (stdout/stderr captured pre-timeout are still returned). +- **S300** VM unreachable (boot failed earlier, or shell socket dropped). diff --git a/crates/iii-worker/src/sandbox_daemon/skills/list.md b/crates/iii-worker/src/sandbox_daemon/skills/list.md new file mode 100644 index 000000000..1163817e7 --- /dev/null +++ b/crates/iii-worker/src/sandbox_daemon/skills/list.md @@ -0,0 +1,45 @@ +# sandbox/list — list running sandboxes + +Read the current sandbox roster. Pure read, safe to poll. + +- id: `sandbox::list` +- timeout: 10s +- idempotent: yes +- request: `ListRequest {}` +- response: `ListResponse { sandboxes: [SandboxSummary] }` + +`SandboxSummary`: +- `sandbox_id` — UUID; reuse as the target for [`sandbox/exec`](iii://sandbox/exec) and [`sandbox/stop`](iii://sandbox/stop). +- `name` — optional human label set at create time; `null` when unset. +- `image` — image reference the sandbox booted from. +- `age_secs` — seconds since the sandbox was created. +- `exec_in_progress` — another `sandbox::exec` against this id while this is `true` returns **S003**. +- `stopped` — running sandboxes have `stopped: false`. There is no `running` field; derive it from `!stopped`. + +## Example + +```json +{} +``` + +Response: +```json +{ + "sandboxes": [ + { + "sandbox_id": "550e8400-e29b-41d4-a716-446655440000", + "name": null, + "image": "ghcr.io/iii-hq/node:latest", + "age_secs": 142, + "exec_in_progress": false, + "stopped": false + } + ] +} +``` + +Also reachable inline as a section URI: [`iii://fn/sandbox/list`](iii://fn/sandbox/list). + +## Errors + +`sandbox::list` doesn't surface errors in practice — it always returns the current roster (possibly empty). An unexpected registry read failure surfaces as **S300** (platform). diff --git a/crates/iii-worker/src/sandbox_daemon/skills/router.md b/crates/iii-worker/src/sandbox_daemon/skills/router.md new file mode 100644 index 000000000..f8400b381 --- /dev/null +++ b/crates/iii-worker/src/sandbox_daemon/skills/router.md @@ -0,0 +1,20 @@ +# sandbox — ephemeral VMs in this engine + +The `iii-sandbox` daemon owns the `sandbox::*` triggers. Each sandbox is scoped to the engine it runs inside. Use them for running an LLM-generated patch against an untrusted codebase, ephemeral build workers, or integration test fixtures. + +## Operations + +- [`sandbox/create`](iii://sandbox/create) — spin up a sandbox from a catalog image +- [`sandbox/exec`](iii://sandbox/exec) — run a command inside a sandbox +- [`sandbox/stop`](iii://sandbox/stop) — tear a sandbox down +- [`sandbox/list`](iii://sandbox/list) — list running sandboxes + +Live data: [`iii://fn/sandbox/list`](iii://fn/sandbox/list) returns the current roster inline. + +## Image catalog + +`sandbox/create` rejects any image not in the daemon's catalog (presets `python` / `node`, plus operator-added custom images in `iii.config.yaml`). An empty catalog with no presets denies every call — a deliberate fail-closed default. + +## Error envelope + +Every op returns errors as `{ "type": "", "code": "Sxxx", "message": "...", "docs_url": "...", "retryable": bool }`. `type` is the category (`validation`, `config`, `internal`, `transient`, `execution`, `filesystem`, `platform`) — not the literal string `"SandboxError"`. The `S` codes are independent of the `W` codes used by `worker::*`. diff --git a/crates/iii-worker/src/sandbox_daemon/skills/stop.md b/crates/iii-worker/src/sandbox_daemon/skills/stop.md new file mode 100644 index 000000000..10796bb37 --- /dev/null +++ b/crates/iii-worker/src/sandbox_daemon/skills/stop.md @@ -0,0 +1,29 @@ +# sandbox/stop — tear down a sandbox + +Signal a sandbox to shut down and reap its resources. Already-stopped sandboxes return success with no work done. **Unknown ids return S002** — stop is idempotent against already-stopped state, but not against never-existed state. + +- id: `sandbox::stop` +- timeout: 30s +- idempotent: against already-stopped only +- request: `StopRequest { sandbox_id, wait? }` +- response: `StopResponse { sandbox_id, stopped }` + +`wait: true` blocks until the VM has fully exited. Default `false` is fire-and-forget within the daemon's grace window. + +`stopped: false` means the stop didn't complete in time — the sandbox may still be tearing down. Check [`sandbox/list`](iii://sandbox/list) after a few seconds. + +## Example + +```json +{ "sandbox_id": "550e8400-e29b-41d4-a716-446655440000", "wait": false } +``` + +Response: +```json +{ "sandbox_id": "550e8400-e29b-41d4-a716-446655440000", "stopped": true } +``` + +## Errors + +- **S001** `sandbox_id` is not a valid UUID. +- **S002** sandbox not found (unknown id, or already stopped and reaped — reaping removes the registry entry). From 1c3256100bb2f6f067f2982bb2a2cf9e4c719964 Mon Sep 17 00:00:00 2001 From: Anderson Leal Date: Wed, 13 May 2026 10:07:03 -0300 Subject: [PATCH 2/2] docs(skills): clarify idempotency behavior for worker skill actions --- .../src/cli/worker_manager_daemon/skills/add.md | 11 +++++++++++ .../src/cli/worker_manager_daemon/skills/remove.md | 2 +- .../src/cli/worker_manager_daemon/skills/start.md | 2 +- .../src/cli/worker_manager_daemon/skills/stop.md | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md index 804fd0ac0..79db1d334 100644 --- a/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/add.md @@ -26,6 +26,17 @@ Install from the iii registry or an OCI image. Writes the entry to `iii.config.y } ``` +Response: +```json +{ + "name": "image-resize", + "version": "0.1.2", + "status": "installed", + "awaited_ready": true, + "config_path": "/Users/you/.iii/managed/image-resize/iii.config.yaml" +} +``` + ## Errors - **W101** missing/malformed `source`. diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md index 26c0d5de9..6b85b524f 100644 --- a/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/remove.md @@ -4,7 +4,7 @@ Remove a worker's entry from `iii.config.yaml`. The engine's file watcher tears - id: `worker::remove` - timeout: 30s -- idempotent: yes +- idempotent: yes (second call returns empty `removed` list) - request: `RemoveOptions { names: [], all?, yes }` - response: `RemoveOutcome { removed: [string] }` diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md index a1a480f5b..b8c5e1885 100644 --- a/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/start.md @@ -4,7 +4,7 @@ Spawn a worker that's already in `iii.config.yaml`. The engine connects to the p - id: `worker::start` - timeout: 60s -- idempotent: no (stateful — starting an already-running worker is a no-op or error depending on health) +- idempotent: no (stateful — starting an already-running worker is a no-op; starting an unknown worker returns **W110**) - request: `StartOptions { name, port?, config?, wait? }` - response: `StartOutcome { name, pid?, port? }` diff --git a/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md b/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md index 198990cb1..c5f4b2dfa 100644 --- a/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md +++ b/crates/iii-worker/src/cli/worker_manager_daemon/skills/stop.md @@ -4,7 +4,7 @@ Send a graceful shutdown signal. Destructive — requires explicit `yes: true` c - id: `worker::stop` - timeout: 30s -- idempotent: no (stateful) +- idempotent: no (stateful — already-stopped workers return success; unknown names return **W110**) - request: `StopOptions { name, yes }` - response: `StopOutcome { name, stopped }`