Shepherd: prove an agent task is retained and least-privilege before it runs
Declare an agent task's per-repo read/write grants and hold its output to one side (retained, not applied), then validate that nothing auto-applies and every write grant is explicit, before you run it.
Run this workflow
CI-verified, 2/2 fixtures passing.
Build this with your agent
One copy-paste hands Claude Code, Codex, or Cursor the full recipe, steps included, nothing to fetch.
Intended Use
Anyone running agents who wants a real undo button and least-privilege file access. CI validates the grant manifest: apply_mode is retained (nothing auto-applies), each repo grant is read_only or read_write, and every read_write grant is explicitly acknowledged. No run, no OS call. The syscall-jail enforcement, the actual agent run, and Shepherd's alpha runtime are fenced.
Not for
- Production, Shepherd is early alpha and the maintainers say so; APIs may change between releases, study and pilot it in a sandbox
- Uniform sandbox guarantees, enforcement is exercised on macOS Seatbelt while Linux Landlock is container-gated, so the syscall-level protection is not the same on every machine
- Quoting the '5x faster than docker commit, 95% KV-cache reuse' figures, those are the authors' own benchmarks; reproduce them before you cite them
The Stack
Tested Against
arxiv.org/abs/2605.10913 + shepherd-agents/shepherd (2026-07)node@20Side effects & data flow
- Network
- none, local only
- Writes
- ./grants.json
- Credentials
- none required
Prerequisites
- Shepherd installed (pip install shepherd-ai) to actually run a task
- A supported OS (macOS Seatbelt or Linux Landlock) for kernel-level enforcement
Steps
- 1
Declare the grants and validate the safety posture
Write the task's per-repo grants and set apply_mode to retained so the output is held to one side, not applied. CI checks nothing auto-applies and that every write grant is explicitly acknowledged (least privilege: repos are read-only unless you say otherwise). Running the task and the OS-level enforcement are fenced.
cat > grants.json <<'JSON' { "task": "refactor-auth", "apply_mode": "retained", "grants": [ { "repo": "app", "access": "read_write", "acknowledged": true }, { "repo": "vendor", "access": "read_only" }, { "repo": "secrets", "access": "read_only" } ] } JSON node -e ' const fs = require("fs"); const c = JSON.parse(fs.readFileSync("grants.json", "utf8")); function bad(m) { console.error("BAD: " + m); process.exit(1); } if (c.apply_mode !== "retained") bad("apply_mode must be retained (nothing applies until you accept it)"); const grants = c.grants || []; if (grants.length < 1) bad("declare at least one repo grant"); let ro = 0, rw = 0; for (const g of grants) { if (!g.repo) bad("each grant needs a repo"); if (g.access !== "read_only" && g.access !== "read_write") bad("grant for " + g.repo + " must be read_only or read_write"); if (g.access === "read_write") { if (g.acknowledged !== true) bad("write grant for " + g.repo + " must be explicitly acknowledged (least privilege)"); rw++; } else { ro++; } } console.log("grants OK: task retained (not auto-applied); " + grants.length + " repo(s), " + ro + " read-only, " + rw + " read-write (all acknowledged)"); ' - 2
Run it, then keep or discard (the run step, not checked by CI)
Install Shepherd (pip install shepherd-ai), shepherd init, run the task; its output is retained, not applied. Inspect the changeset (shepherd run changeset --latest), then shepherd run select to keep it or shepherd run discard to throw it away. On a supported OS the read-only grants are enforced by the kernel. The run and enforcement are fenced.
Eval, 2 fixtures
Last passed: verified todaygrants-okcontainstimeout 30s · max $0Expected:
grants OK: task retained (not auto-applied); 3 repo(s), 2 read-only, 1 read-write (all acknowledged)clean-exitexit_codetimeout 30s · max $0Expected:
0
Results
Shepherd records an agent's run as a reversible, Git-like trace: the output comes back as a reviewable proposal, not a live edit, and nothing touches your files until you accept it. Permissions live in the task's function signature (read-only or read-write per repo) and on a supported OS are enforced at the native syscall jail (macOS Seatbelt, Linux Landlock), a write to a read-only repo is refused by the kernel, not just caught at a merge gate.
Did this work for you?
Our CI checks the setup runs. You tell us if the whole thing worked. Tell us straight.
Related workflows
- Vet the fine print a star count hides: real license and a gate on dual-use tools
- Agent-Reach: throwaway account, least privilege, scan before install
- Vet a SKILL.md before you install it
- SkillSpector: fail your CI build on a risky agent skill
- Claude Code Auto Mode: A Classifier Instead of an Allowlist
- Claude Code: Lock Down an Unattended Run with Permission Rules
Liked this workflow?
Get new verified workflows in WebAfterAI, three issues a week (Tue, Thu, Sat).