Add use-case recipes and smoke packs
Turn the stable workspace surface into five documented, runnable stories with a shared guest-backed smoke runner, new docs/use-cases recipes, and Make targets for cold-start validation, repro/fix loops, parallel workspaces, untrusted inspection, and review/eval workflows. Bump the package and catalog surface to 3.6.0, update the main docs to point users from the stable workspace walkthrough into the recipe index and smoke packs, and mark the 3.6.0 roadmap milestone done. Fix a regression uncovered by the real parallel-workspaces smoke: workspace_file_read must not bump last_activity_at. Verified with uv lock, UV_CACHE_DIR=.uv-cache make check, UV_CACHE_DIR=.uv-cache make dist-check, and USE_CASE_ENVIRONMENT=debian:12 UV_CACHE_DIR=.uv-cache make smoke-use-cases.
This commit is contained in:
parent
21a88312b6
commit
894706af50
22 changed files with 1310 additions and 16 deletions
|
|
@ -22,7 +22,7 @@ Networking: tun=yes ip_forward=yes
|
|||
|
||||
```bash
|
||||
$ uvx --from pyro-mcp pyro env list
|
||||
Catalog version: 3.5.0
|
||||
Catalog version: 3.6.0
|
||||
debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows.
|
||||
debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling.
|
||||
debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled.
|
||||
|
|
@ -121,6 +121,10 @@ $ uvx --from pyro-mcp pyro mcp serve --profile workspace-core
|
|||
|
||||
`pyro demo` proves the one-shot create/start/exec/delete VM lifecycle works end to end.
|
||||
|
||||
Once that stable workspace flow works, continue with the five recipe docs in
|
||||
[use-cases/README.md](use-cases/README.md) or run the real guest-backed smoke packs directly with
|
||||
`make smoke-use-cases`.
|
||||
|
||||
When you need repeated commands in one sandbox, switch to `pyro workspace ...`:
|
||||
|
||||
```bash
|
||||
|
|
@ -252,7 +256,7 @@ State: started
|
|||
Use `--seed-path` when the workspace should start from a host directory or a local
|
||||
`.tar` / `.tar.gz` / `.tgz` archive instead of an empty `/workspace`. Use
|
||||
`pyro workspace sync push` when you need to import later host-side changes into a started
|
||||
workspace. Sync is non-atomic in `3.5.0`; if it fails partway through, prefer `pyro workspace reset`
|
||||
workspace. Sync is non-atomic in `3.6.0`; if it fails partway through, prefer `pyro workspace reset`
|
||||
to recover from `baseline` or one named snapshot. Use `pyro workspace diff` to compare the current
|
||||
`/workspace` tree to its immutable create-time baseline, `pyro workspace snapshot *` to create
|
||||
named checkpoints, and `pyro workspace export` to copy one changed file or directory back to the
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ uvx --from pyro-mcp pyro env list
|
|||
Expected output:
|
||||
|
||||
```bash
|
||||
Catalog version: 3.5.0
|
||||
Catalog version: 3.6.0
|
||||
debian:12 [installed|not installed] Debian 12 environment with Git preinstalled for common agent workflows.
|
||||
debian:12-base [installed|not installed] Minimal Debian 12 environment for shell and core Unix tooling.
|
||||
debian:12-build [installed|not installed] Debian 12 environment with Git and common build tools preinstalled.
|
||||
|
|
@ -167,6 +167,11 @@ This is the stable persistent-workspace contract:
|
|||
- `workspace export` copies results back to the host
|
||||
- `workspace stop|start` and `workspace disk *` add secondary stopped-workspace inspection and raw ext4 export
|
||||
|
||||
When that stable workspace path is working, continue with the recipe index at
|
||||
[use-cases/README.md](use-cases/README.md). It groups the five core workspace stories and the
|
||||
real smoke targets behind them, starting with `make smoke-use-cases` or one of the per-scenario
|
||||
targets such as `make smoke-repro-fix-loop`.
|
||||
|
||||
## 6. Optional demo proof point
|
||||
|
||||
```bash
|
||||
|
|
@ -274,7 +279,7 @@ Workspace commands default to the persistent `/workspace` directory inside the g
|
|||
the identifier programmatically, use `--json` and read the `workspace_id` field. Use `--seed-path`
|
||||
when the workspace should start from a host directory or a local `.tar` / `.tar.gz` / `.tgz`
|
||||
archive. Use `pyro workspace sync push` for later host-side changes to a started workspace. Sync
|
||||
is non-atomic in `3.5.0`; if it fails partway through, prefer `pyro workspace reset` to recover
|
||||
is non-atomic in `3.6.0`; if it fails partway through, prefer `pyro workspace reset` to recover
|
||||
from `baseline` or one named snapshot. Use `pyro workspace diff` to compare the current workspace
|
||||
tree to its immutable create-time baseline, `pyro workspace snapshot *` to capture named
|
||||
checkpoints, and `pyro workspace export` to copy one changed file or directory back to the host. Use
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ Canonical example:
|
|||
|
||||
- [examples/openai_responses_vm_run.py](../examples/openai_responses_vm_run.py)
|
||||
- [examples/openai_responses_workspace_core.py](../examples/openai_responses_workspace_core.py)
|
||||
- [docs/use-cases/repro-fix-loop.md](use-cases/repro-fix-loop.md)
|
||||
|
||||
## MCP Clients
|
||||
|
||||
|
|
@ -67,6 +68,7 @@ Starter config:
|
|||
- [examples/mcp_client_config.md](../examples/mcp_client_config.md)
|
||||
- [examples/claude_desktop_mcp_config.json](../examples/claude_desktop_mcp_config.json)
|
||||
- [examples/cursor_mcp_config.json](../examples/cursor_mcp_config.json)
|
||||
- [docs/use-cases/README.md](use-cases/README.md)
|
||||
|
||||
## Direct Python SDK
|
||||
|
||||
|
|
@ -123,6 +125,7 @@ Examples:
|
|||
- [examples/python_lifecycle.py](../examples/python_lifecycle.py)
|
||||
- [examples/python_workspace.py](../examples/python_workspace.py)
|
||||
- [examples/python_shell.py](../examples/python_shell.py)
|
||||
- [docs/use-cases/README.md](use-cases/README.md)
|
||||
|
||||
## Agent Framework Wrappers
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ goal:
|
|||
make the core agent-workspace use cases feel trivial from a chat-driven LLM
|
||||
interface.
|
||||
|
||||
Current baseline is `3.5.0`:
|
||||
Current baseline is `3.6.0`:
|
||||
|
||||
- the stable workspace contract exists across CLI, SDK, and MCP
|
||||
- one-shot `pyro run` still exists as the narrow entrypoint
|
||||
|
|
@ -49,7 +49,7 @@ More concretely, the model should not need to:
|
|||
2. [`3.3.0` Workspace Naming And Discovery](llm-chat-ergonomics/3.3.0-workspace-naming-and-discovery.md) - Done
|
||||
3. [`3.4.0` Tool Profiles And Canonical Chat Flows](llm-chat-ergonomics/3.4.0-tool-profiles-and-canonical-chat-flows.md) - Done
|
||||
4. [`3.5.0` Chat-Friendly Shell Output](llm-chat-ergonomics/3.5.0-chat-friendly-shell-output.md) - Done
|
||||
5. [`3.6.0` Use-Case Recipes And Smoke Packs](llm-chat-ergonomics/3.6.0-use-case-recipes-and-smoke-packs.md)
|
||||
5. [`3.6.0` Use-Case Recipes And Smoke Packs](llm-chat-ergonomics/3.6.0-use-case-recipes-and-smoke-packs.md) - Done
|
||||
|
||||
Completed so far:
|
||||
|
||||
|
|
@ -63,6 +63,9 @@ Completed so far:
|
|||
narrow and widen only when needed.
|
||||
- `3.5.0` added chat-friendly shell reads with plain-text rendering and idle batching so PTY
|
||||
sessions are readable enough to feed directly back into a chat model.
|
||||
- `3.6.0` added recipe docs and real guest-backed smoke packs for the five core workspace use
|
||||
cases so the stable product is now demonstrated as repeatable end-to-end stories instead of
|
||||
only isolated feature surfaces.
|
||||
|
||||
## Expected Outcome
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# `3.6.0` Use-Case Recipes And Smoke Packs
|
||||
|
||||
Status: Planned
|
||||
Status: Done
|
||||
|
||||
## Goal
|
||||
|
||||
|
|
|
|||
31
docs/use-cases/README.md
Normal file
31
docs/use-cases/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Workspace Use-Case Recipes
|
||||
|
||||
These recipes turn the stable workspace surface into five concrete agent flows.
|
||||
They are the canonical next step after the quickstart in [install.md](../install.md)
|
||||
or [first-run.md](../first-run.md).
|
||||
|
||||
Run all real guest-backed scenarios locally with:
|
||||
|
||||
```bash
|
||||
make smoke-use-cases
|
||||
```
|
||||
|
||||
Recipe matrix:
|
||||
|
||||
| Use case | Recommended profile | Smoke target | Recipe |
|
||||
| --- | --- | --- | --- |
|
||||
| Cold-start repo validation | `workspace-full` | `make smoke-cold-start-validation` | [cold-start-repo-validation.md](cold-start-repo-validation.md) |
|
||||
| Repro plus fix loop | `workspace-core` | `make smoke-repro-fix-loop` | [repro-fix-loop.md](repro-fix-loop.md) |
|
||||
| Parallel isolated workspaces | `workspace-core` | `make smoke-parallel-workspaces` | [parallel-workspaces.md](parallel-workspaces.md) |
|
||||
| Unsafe or untrusted code inspection | `workspace-core` | `make smoke-untrusted-inspection` | [untrusted-inspection.md](untrusted-inspection.md) |
|
||||
| Review and evaluation workflows | `workspace-full` | `make smoke-review-eval` | [review-eval-workflows.md](review-eval-workflows.md) |
|
||||
|
||||
All five recipes use the same real Firecracker-backed smoke runner:
|
||||
|
||||
```bash
|
||||
uv run python scripts/workspace_use_case_smoke.py --scenario all --environment debian:12
|
||||
```
|
||||
|
||||
That runner generates its own host fixtures, creates real guest-backed workspaces,
|
||||
verifies the intended flow, exports one concrete result when relevant, and cleans
|
||||
up on both success and failure.
|
||||
38
docs/use-cases/cold-start-repo-validation.md
Normal file
38
docs/use-cases/cold-start-repo-validation.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Cold-Start Repo Validation
|
||||
|
||||
Recommended profile: `workspace-full`
|
||||
|
||||
Smoke target:
|
||||
|
||||
```bash
|
||||
make smoke-cold-start-validation
|
||||
```
|
||||
|
||||
Use this flow when an agent needs to treat a fresh repo like a new user would:
|
||||
seed it into a workspace, run the validation script, keep one long-running
|
||||
process alive, probe it from another command, and export a validation report.
|
||||
|
||||
Canonical SDK flow:
|
||||
|
||||
```python
|
||||
from pyro_mcp import Pyro
|
||||
|
||||
pyro = Pyro()
|
||||
created = pyro.create_workspace(environment="debian:12", seed_path="./repo")
|
||||
workspace_id = str(created["workspace_id"])
|
||||
|
||||
pyro.exec_workspace(workspace_id, command="sh validate.sh")
|
||||
pyro.start_service(
|
||||
workspace_id,
|
||||
"app",
|
||||
command="sh serve.sh",
|
||||
readiness={"type": "file", "path": ".app-ready"},
|
||||
)
|
||||
pyro.exec_workspace(workspace_id, command="sh -lc 'test -f .app-ready && cat service-state.txt'")
|
||||
pyro.export_workspace(workspace_id, "validation-report.txt", output_path="./validation-report.txt")
|
||||
pyro.delete_workspace(workspace_id)
|
||||
```
|
||||
|
||||
This recipe is intentionally guest-local and deterministic. It proves startup,
|
||||
service readiness, validation, and host-out report capture without depending on
|
||||
external networks or private registries.
|
||||
43
docs/use-cases/parallel-workspaces.md
Normal file
43
docs/use-cases/parallel-workspaces.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Parallel Isolated Workspaces
|
||||
|
||||
Recommended profile: `workspace-core`
|
||||
|
||||
Smoke target:
|
||||
|
||||
```bash
|
||||
make smoke-parallel-workspaces
|
||||
```
|
||||
|
||||
Use this flow when the agent needs one isolated workspace per issue, branch, or
|
||||
review thread and must rediscover the right one later.
|
||||
|
||||
Canonical SDK flow:
|
||||
|
||||
```python
|
||||
from pyro_mcp import Pyro
|
||||
|
||||
pyro = Pyro()
|
||||
alpha = pyro.create_workspace(
|
||||
environment="debian:12",
|
||||
seed_path="./repo",
|
||||
name="parallel-alpha",
|
||||
labels={"branch": "alpha", "issue": "123"},
|
||||
)
|
||||
beta = pyro.create_workspace(
|
||||
environment="debian:12",
|
||||
seed_path="./repo",
|
||||
name="parallel-beta",
|
||||
labels={"branch": "beta", "issue": "456"},
|
||||
)
|
||||
|
||||
pyro.write_workspace_file(alpha["workspace_id"], "branch.txt", text="alpha\n")
|
||||
pyro.write_workspace_file(beta["workspace_id"], "branch.txt", text="beta\n")
|
||||
pyro.update_workspace(alpha["workspace_id"], labels={"branch": "alpha", "owner": "alice"})
|
||||
pyro.list_workspaces()
|
||||
pyro.delete_workspace(alpha["workspace_id"])
|
||||
pyro.delete_workspace(beta["workspace_id"])
|
||||
```
|
||||
|
||||
The important proof here is operational, not syntactic: names, labels, list
|
||||
ordering, and file contents stay isolated even when multiple workspaces are
|
||||
active at the same time.
|
||||
42
docs/use-cases/repro-fix-loop.md
Normal file
42
docs/use-cases/repro-fix-loop.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Repro Plus Fix Loop
|
||||
|
||||
Recommended profile: `workspace-core`
|
||||
|
||||
Smoke target:
|
||||
|
||||
```bash
|
||||
make smoke-repro-fix-loop
|
||||
```
|
||||
|
||||
Use this flow when the agent has to reproduce a bug, patch files without shell
|
||||
quoting tricks, rerun the failing command, diff the result, export the fix, and
|
||||
reset back to baseline.
|
||||
|
||||
Canonical SDK flow:
|
||||
|
||||
```python
|
||||
from pyro_mcp import Pyro
|
||||
|
||||
pyro = Pyro()
|
||||
created = pyro.create_workspace(environment="debian:12", seed_path="./broken-repro")
|
||||
workspace_id = str(created["workspace_id"])
|
||||
|
||||
pyro.exec_workspace(workspace_id, command="sh check.sh")
|
||||
pyro.read_workspace_file(workspace_id, "message.txt")
|
||||
pyro.apply_workspace_patch(
|
||||
workspace_id,
|
||||
patch="--- a/message.txt\n+++ b/message.txt\n@@ -1 +1 @@\n-broken\n+fixed\n",
|
||||
)
|
||||
pyro.exec_workspace(workspace_id, command="sh check.sh")
|
||||
pyro.diff_workspace(workspace_id)
|
||||
pyro.export_workspace(workspace_id, "message.txt", output_path="./message.txt")
|
||||
pyro.reset_workspace(workspace_id)
|
||||
pyro.delete_workspace(workspace_id)
|
||||
```
|
||||
|
||||
Canonical MCP/chat example:
|
||||
|
||||
- [examples/openai_responses_workspace_core.py](../../examples/openai_responses_workspace_core.py)
|
||||
|
||||
This is the main `workspace-core` story: model-native file ops, repeatable exec,
|
||||
structured diff, explicit export, and reset-over-repair.
|
||||
41
docs/use-cases/review-eval-workflows.md
Normal file
41
docs/use-cases/review-eval-workflows.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Review And Evaluation Workflows
|
||||
|
||||
Recommended profile: `workspace-full`
|
||||
|
||||
Smoke target:
|
||||
|
||||
```bash
|
||||
make smoke-review-eval
|
||||
```
|
||||
|
||||
Use this flow when an agent needs to read a checklist interactively, run an
|
||||
evaluation script, checkpoint or reset its changes, and export the final report.
|
||||
|
||||
Canonical SDK flow:
|
||||
|
||||
```python
|
||||
from pyro_mcp import Pyro
|
||||
|
||||
pyro = Pyro()
|
||||
created = pyro.create_workspace(environment="debian:12", seed_path="./review-fixture")
|
||||
workspace_id = str(created["workspace_id"])
|
||||
|
||||
pyro.create_snapshot(workspace_id, "pre-review")
|
||||
shell = pyro.open_shell(workspace_id)
|
||||
pyro.write_shell(workspace_id, shell["shell_id"], input="cat CHECKLIST.md")
|
||||
pyro.read_shell(
|
||||
workspace_id,
|
||||
shell["shell_id"],
|
||||
plain=True,
|
||||
wait_for_idle_ms=300,
|
||||
)
|
||||
pyro.close_shell(workspace_id, shell["shell_id"])
|
||||
pyro.exec_workspace(workspace_id, command="sh review.sh")
|
||||
pyro.export_workspace(workspace_id, "review-report.txt", output_path="./review-report.txt")
|
||||
pyro.reset_workspace(workspace_id, snapshot="pre-review")
|
||||
pyro.delete_workspace(workspace_id)
|
||||
```
|
||||
|
||||
This is the stable shell-facing story: readable PTY output for chat loops,
|
||||
checkpointed evaluation, explicit export, and reset when a review branch goes
|
||||
sideways.
|
||||
34
docs/use-cases/untrusted-inspection.md
Normal file
34
docs/use-cases/untrusted-inspection.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Unsafe Or Untrusted Code Inspection
|
||||
|
||||
Recommended profile: `workspace-core`
|
||||
|
||||
Smoke target:
|
||||
|
||||
```bash
|
||||
make smoke-untrusted-inspection
|
||||
```
|
||||
|
||||
Use this flow when the agent needs to inspect suspicious code or an unfamiliar
|
||||
repo without granting more capabilities than necessary.
|
||||
|
||||
Canonical SDK flow:
|
||||
|
||||
```python
|
||||
from pyro_mcp import Pyro
|
||||
|
||||
pyro = Pyro()
|
||||
created = pyro.create_workspace(environment="debian:12", seed_path="./suspicious-repo")
|
||||
workspace_id = str(created["workspace_id"])
|
||||
|
||||
pyro.list_workspace_files(workspace_id, path="/workspace", recursive=True)
|
||||
pyro.read_workspace_file(workspace_id, "suspicious.sh")
|
||||
pyro.exec_workspace(
|
||||
workspace_id,
|
||||
command="sh -lc \"grep -n 'curl' suspicious.sh > inspection-report.txt\"",
|
||||
)
|
||||
pyro.export_workspace(workspace_id, "inspection-report.txt", output_path="./inspection-report.txt")
|
||||
pyro.delete_workspace(workspace_id)
|
||||
```
|
||||
|
||||
This recipe stays offline-by-default, uses only explicit file reads and execs,
|
||||
and exports only the inspection report the agent chose to materialize.
|
||||
Loading…
Add table
Add a link
Reference in a new issue