From 5e486ab8b18a9b3550d1bdcb722ff3e3de215535 Mon Sep 17 00:00:00 2001 From: "Christian M. Todie" Date: Thu, 9 Jul 2026 09:52:54 -0400 Subject: [PATCH 1/2] =?UTF-8?q?docs(shepherd):=20operational=20runbook=20f?= =?UTF-8?q?or=20live=20agent=20runs=20=E2=80=94=20budget=20seam,=20orphane?= =?UTF-8?q?d-scope=20recovery,=20select=20semantics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the three v0.2.x operational gaps hit during the 2026-07-09 reverie CER-1190 refactor run (budget default too short for compile-check loops + the transport-seam patch; crashed runs orphaning a scope that 'run repair' can't reclaim + the VcsCoreApp recovery API; select settling into vcs-core custody rather than materializing the working tree), plus the pre-flight checklist and the verified real-refactor run record. --- packages/shepherd/README.md | 105 ++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/packages/shepherd/README.md b/packages/shepherd/README.md index fa933b7..b6e62f6 100644 --- a/packages/shepherd/README.md +++ b/packages/shepherd/README.md @@ -55,6 +55,105 @@ containers, but the jail is self-contained (unprivileged Landlock, kernel `CONFIG_SECURITY_LANDLOCK=y`) `doctor` reports `native-jail: available` and jailed runs execute. Always gate on `shepherd doctor claude`, not assumption. +## Operational runbook (live-agent runs) + +The retained-run lifecycle from the quickstart demo scales to real agent work, +but the v0.2.x surface has three operational gaps the quickstart doesn't +exercise. All three were hit during a real multi-file Rust refactor run +(2026-07-09, reverie CER-1190); the fixes are mechanical and repeatable. + +### 1. Budget: the default 240 s is too short for refactors with compile checks + +`workspace.run(runtime={"provider": "claude"})` constructs the +`ClaudeHeadlessProvider` with `budget_seconds=240` (hardcoded default). That's +enough for the donut demo but not for a multi-file edit + `cargo check` cycle +on a large workspace — the agent gets `BudgetExhausted` mid-flight and the run +fails. The budget is **not exposed** through the `runtime` dict (which only +carries `provider` and `model`); you patch it at the transport seam: + +```python +from shepherd_dialect.workspace_control import runtime_provider as rp +from shepherd_dialect.providers import ClaudeHeadlessProvider + +BUDGET_SECONDS = 900 # 15 min — enough for a multi-file refactor + cargo check +MAX_TURNS = 40 + +def _patched_transport(invocation): + return ClaudeHeadlessProvider( + provider_id=invocation.provider_id, + prompt=invocation.prompt, + model=invocation.model_name, + budget_seconds=BUDGET_SECONDS, + max_turns=MAX_TURNS, + ) + +rp._WORKSPACE_RUNTIME_PROVIDER_TRANSPORTS = rp._WorkspaceRuntimeProviderTransports( + claude=_patched_transport, +) +``` + +Set this **before** `workspace.run(...)`. The transport object is a frozen +dataclass holding one callable; replacing it is the only public-adjacent way +to raise the budget without a source fork. Revisit if upstream exposes +`budget_seconds` in the runtime plan. + +### 2. Crashed runs orphan a scope that blocks readiness — `repair` won't fix it + +A run that dies mid-flight (budget exhausted, host loss, wrong interpreter) +can leave an **orphaned scope ref** in `.vcscore/`. The next `workspace.run` +fails with `readiness blocked by run-XXXXXXXX`. `shepherd run repair` +reclaims orphaned *operation refs* only — it will report "Nothing to repair" +while the scope ref still blocks. The fix is the `VcsCoreApp` recovery API: + +```python +uv run --with "shepherd-ai==0.2.1" python3 -c " +from vcs_core._app import VcsCoreApp, AppOpenMode +with VcsCoreApp.open_existing('.', mode=AppOpenMode.RECOVERY) as app: + print(app.archive_orphaned_scopes()) +" +``` + +This is the failure mode the durable-patterns spec (OPS-482) targets for +automated recovery. Until that lands, `archive_orphaned_scopes()` is the +manual unblock. Gate on it: if `shepherd run repair` says "Nothing to +repair" but `workspace.run` still fails with `readiness blocked`, run the +snippet above. + +### 3. `select` moves into vcs-core custody, not the working tree + +`shepherd run select ` settles the output (state → `selected`) but does +**not** materialize the files into the working tree — it moves the changeset +into its "live parent world" inside `.vcscore/`. To get the files into the +tree for compile-check, read them from the retained changeset and write them +yourself: + +```bash +shepherd run changeset --read > # one file at a time +``` + +or batch-extract with a loop. The retained changeset is the source of truth; +`select`/`release`/`discard` are settlement actions, not filesystem +materialization. This gap is inherent to v0.2.x's world/custody model. + +### Pre-flight checklist for a real agent run + +1. `shepherd doctor claude --probe` — 9/9 including live auth round-trip. +2. Check readiness if the workspace has prior runs: + ```python + uv run --with "shepherd-ai==0.2.1" python3 -c " + from vcs_core._query_readiness import evaluate_readiness, ReadinessRequest + r = evaluate_readiness('.vcscore', ReadinessRequest(command='shepherd.run')) + print(r.state, len(r.blockers or [])) + " + ``` + If `blocked`, run the `archive_orphaned_scopes()` snippet from §2. +3. Patch the budget (§1) if the task involves edits + compile/test cycles. +4. After the run: `shepherd run show ` (enforcement, terminal status), + `shepherd run changeset --latest` (files), `--read ` (content). +5. To apply: extract files from the changeset (§3), `cargo check` / test, + then `git add` + commit. `select` is optional — it's a settlement record, + not the materialization step. + ## Verified CI-gated: install-script pin format + README/pin agreement (`tests/`), @@ -71,6 +170,12 @@ shellcheck, ruff. Exercised live on WSL2 (2026-07-06, kernel 6.6.87.2): `discard`. Caveat observed: a demo run that crashes mid-flight (e.g. wrong interpreter) can leave the workspace `readiness blocked` for later runs — a fresh `shepherd init` directory clears it. +- **Real agent refactor** (2026-07-09, reverie CER-1190): jailed agent ran + 5.7 min with budget patched to 900 s / 40 turns, wrote a 10-file changeset + (multi-crate `tracing-subscriber` consolidation), changeset reviewed + file-by-file via `--read`, rustfmt-clean, all 4 sites replaced, `reveried` + untouched. Three operational gaps documented above were all hit and + resolved during this run. Needs live exercise per-host: jail availability (kernel-dependent) and claude auth — both reported by `shepherd doctor claude`. From 7777fa0f3118b33f46cdb3b0978f5df5a041f4a5 Mon Sep 17 00:00:00 2001 From: "Christian M. Todie" Date: Thu, 9 Jul 2026 09:54:26 -0400 Subject: [PATCH 2/2] bump(shepherd): pin shepherd-ai 0.2.1 -> 0.3.0 0.3.0 (upstream 2026-07-08) adds the apply settlement verb (three-way merge of a run's delta onto a moved-on workspace, fail-closed on overlap), executes grant enforcement on host Linux/macOS (retiring the 0.2.0 container-gated Landlock caveat our README carried), and tightens task registration (repo: GitRepo spelling, ambient-call refusals, fabrication fence). Live-verified on the reference WSL2 host before pinning: - 0.2.1-era .vcscore workspaces open cleanly (doctor Ready; run history from the CER-1190 runs reads back under 0.3.0) - offline quickstart green (retained -> released) - every private internal the operational runbook relies on re-probed present and unchanged (ClaudeHeadlessProvider budget_seconds default 240, transport seam, VcsCoreApp recovery API, readiness query) - executor lane still static/claude only - README claim updated to 0.3.x - adoption still hard-fails on tracked symlinks/gitlinks - re-confirmed on the 4 OPS-524 repos; limitation documented in Verified Not yet exercised on 0.3.0: jailed live-claude lane, apply verb (both flagged in README). --- packages/shepherd/README.md | 51 ++++++++++++++++++++++++------------ packages/shepherd/install.sh | 2 +- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/packages/shepherd/README.md b/packages/shepherd/README.md index b6e62f6..467d258 100644 --- a/packages/shepherd/README.md +++ b/packages/shepherd/README.md @@ -7,10 +7,10 @@ consumes: docs/eval/2026-07-06-shepherd-omp-adoption.md (adoption rationale) # shepherd [shepherd](https://github.com/shepherd-agents/shepherd) runtime substrate at -curated pin **`shepherd-ai` 0.2.1** — records agent runs as reversible +curated pin **`shepherd-ai` 0.3.0** — records agent runs as reversible execution traces; sandboxed agents produce *retained outputs* reviewed with -`shepherd run changeset` and settled with `select`/`release`/`discard` before -anything touches the working tree. Task permissions are declared per-binding +`shepherd run changeset` and settled with `select`/`release`/`apply`/`discard` +before anything touches the working tree. Task permissions are declared per-binding in the Python signature (`May[GitRepo, ReadOnly|ReadWrite]`) and enforced at the native syscall jail (Linux Landlock; macOS Seatbelt). @@ -22,7 +22,8 @@ shepherd doctor claude # core + jail + claude CLI/auth readiness (--probe = l ``` Not a harness: this package is the supervision layer next to the harnesses. -The sandboxed executor lane is **claude-only by design** in v0.2.x — the +The sandboxed executor lane is **claude-only by design** through v0.3.x +(re-verified against the 0.3.0 runtime-provider source) — the retained-run provider accepts `static` (deterministic, keyless) or `claude`; pointing it at pi/omp would be an upstream source fork, not configuration (see the adoption eval in `docs/eval/`). @@ -43,24 +44,28 @@ pointing it at pi/omp would be an upstream source fork, not configuration - **Python API note:** `uv tool install` exposes the CLIs only. Demo scripts (`agent_task.py` etc.) import `shepherd`/`shepherd_dialect`, so run them with an interpreter that has `shepherd-ai` installed as a library - (`uv run --with "shepherd-ai==0.2.1" python agent_task.py`), not the bare + (`uv run --with "shepherd-ai==0.3.0" python agent_task.py`), not the bare system `python3`. ## Placement semantics (why WSL2 works) `placement="jail"` fails closed where the OS jail is unavailable; `"auto"` -degrades visibly to advisory. Upstream validates Linux Landlock only inside -containers, but the jail is self-contained (unprivileged Landlock, kernel -5.13+): on this repo's reference WSL2 host (kernel 6.6.87.2, +degrades visibly to advisory. As of 0.3.0 upstream executes grant enforcement +on both Linux (Landlock) and macOS (Seatbelt) — the 0.2.0 container-gated +validation caveat is retired. The jail remains self-contained (unprivileged +Landlock, kernel 5.13+): on this repo's reference WSL2 host (kernel 6.6.87.2, `CONFIG_SECURITY_LANDLOCK=y`) `doctor` reports `native-jail: available` and jailed runs execute. Always gate on `shepherd doctor claude`, not assumption. ## Operational runbook (live-agent runs) The retained-run lifecycle from the quickstart demo scales to real agent work, -but the v0.2.x surface has three operational gaps the quickstart doesn't -exercise. All three were hit during a real multi-file Rust refactor run -(2026-07-09, reverie CER-1190); the fixes are mechanical and repeatable. +but the surface has three operational gaps the quickstart doesn't exercise. +All three were hit during a real multi-file Rust refactor run (2026-07-09, +reverie CER-1190, on 0.2.1); the fixes are mechanical and repeatable, and +every internal they rely on was re-probed as present and unchanged in 0.3.0 +(`budget_seconds` default still 240, transport seam, recovery API, +readiness query). ### 1. Budget: the default 240 s is too short for refactors with compile checks @@ -106,7 +111,7 @@ reclaims orphaned *operation refs* only — it will report "Nothing to repair" while the scope ref still blocks. The fix is the `VcsCoreApp` recovery API: ```python -uv run --with "shepherd-ai==0.2.1" python3 -c " +uv run --with "shepherd-ai==0.3.0" python3 -c " from vcs_core._app import VcsCoreApp, AppOpenMode with VcsCoreApp.open_existing('.', mode=AppOpenMode.RECOVERY) as app: print(app.archive_orphaned_scopes()) @@ -133,14 +138,18 @@ shepherd run changeset --read > # one file at a time or batch-extract with a loop. The retained changeset is the source of truth; `select`/`release`/`discard` are settlement actions, not filesystem -materialization. This gap is inherent to v0.2.x's world/custody model. +materialization — a gap inherent to the world/custody model. 0.3.0 adds a +fourth verb, `apply` (three-way-merge of a run's delta onto a workspace that +moved on, fail-closed on overlap); whether it also materializes the working +tree has NOT been live-exercised here yet — until verified, keep using the +changeset-extract flow below. ### Pre-flight checklist for a real agent run 1. `shepherd doctor claude --probe` — 9/9 including live auth round-trip. 2. Check readiness if the workspace has prior runs: ```python - uv run --with "shepherd-ai==0.2.1" python3 -c " + uv run --with "shepherd-ai==0.3.0" python3 -c " from vcs_core._query_readiness import evaluate_readiness, ReadinessRequest r = evaluate_readiness('.vcscore', ReadinessRequest(command='shepherd.run')) print(r.state, len(r.blockers or [])) @@ -157,9 +166,10 @@ materialization. This gap is inherent to v0.2.x's world/custody model. ## Verified CI-gated: install-script pin format + README/pin agreement (`tests/`), -shellcheck, ruff. Exercised live on WSL2 (2026-07-06, kernel 6.6.87.2): +shellcheck, ruff. Exercised live on WSL2 (0.2.1 on 2026-07-06, 0.3.0 bump on +2026-07-09; kernel 6.6.87.2): -- `install.sh` → `shepherd, version 0.2.1`, executables `shepherd` + `sp`. +- `install.sh` → `shepherd, version 0.3.0`, executables `shepherd` + `sp`. - `shepherd doctor claude --json` → 8/8 ok, including `native-jail: available` and `claude-auth` via `ANTHROPIC_API_KEY`. - Offline quickstart (static provider, keyless) → run `retained`, changeset @@ -176,6 +186,13 @@ shellcheck, ruff. Exercised live on WSL2 (2026-07-06, kernel 6.6.87.2): file-by-file via `--read`, rustfmt-clean, all 4 sites replaced, `reveried` untouched. Three operational gaps documented above were all hit and resolved during this run. +- **0.3.0 bump verification** (2026-07-09): existing 0.2.1-era `.vcscore` + workspaces open cleanly under 0.3.0 (`doctor` Ready; `shepherd run list` + reads prior run history); offline quickstart green (run `retained`, settled + `released`). Known limitation unchanged: worktree adoption still hard-fails + on tracked symlinks and gitlinks (re-confirmed on 4 repos; OPS-524, + upstream `_workspace_external.py` raises on both). Needs live exercise per-host: jail availability (kernel-dependent) and claude -auth — both reported by `shepherd doctor claude`. +auth — both reported by `shepherd doctor claude`. Not yet exercised on 0.3.0: +the jailed live-claude lane and the new `apply` settlement verb. diff --git a/packages/shepherd/install.sh b/packages/shepherd/install.sh index 5b3e37f..4b733a3 100755 --- a/packages/shepherd/install.sh +++ b/packages/shepherd/install.sh @@ -3,7 +3,7 @@ # Idempotent. uv-native install from PyPI — never pipe remote scripts to a shell. set -euo pipefail -SHEPHERD_VERSION="0.2.1" +SHEPHERD_VERSION="0.3.0" command -v uv >/dev/null 2>&1 || { echo "uv not found — run scripts/bootstrap.sh first (proto provides uv)" >&2; exit 1; }