Skip to content

Releases

v0.1.46

Choose a tag to compare

@github-actions github-actions released this 23 Oct 09:51
fc1578b

The jail hides the user bus

On Linux, a jailed worker could reach systemd-run --user through the session bus in $XDG_RUNTIME_DIR and start a command outside the jail. Landlock below ABI 6 does not govern connecting to a socket by path. shepherd-ai 0.3.18 (fork, pinned in pyproject.toml and uv.lock) puts bubblewrap in front of the Landlock runner and mounts an empty tmpfs over each directory where a command server keeps its socket: the runtime directory, tmux-<uid> and screen's S-<user>. The jail probe checks inside the real jail that nothing else is visible.

Without bubblewrap the worker still runs. If bubblewrap is not installed, if AppArmor denies its user namespace, or if its trial run does not finish within 10 s, the worker runs with that path open. The same goes for a user who can write to a Docker socket. The CLI prints one warning at the start naming the paths, the reason, and how to close each one: install bubblewrap, or accept running without that protection. --provider codex|grok runs under its own CLI's sandbox, which the mask does not reach, and the warning says so.

The memory limit wraps the jail

--memory-max moved out of the worker argv and now wraps the jail, so the limit covers every confined launch. A host without Landlock refuses the limit.

Host-side programs keep the user's environment

A repo's jail_env used to reach what shepherd-dev runs on the host for itself: git, cp, ps, ssh, notify, the planner's and the optimizer's claude, and the native gate's node --version and pytest lookup. Those programs now resolve once, on the user's PATH, and run with the environment shepherd-dev started with. jail_env refuses LD_*, DYLD_*, PYTHON*, SHEPHERD_DEV_*, GCONV_PATH, LOCPATH and the command servers' socket variables, and names each refused key on stderr. On Linux, the memory scope, bubblewrap and the runner also start with the user's environment; only the confined command gets the repo's values. On macOS, sandbox-exec still starts with the repo's values.

Known consequence: test command detection now answers for the user's PATH while the gate runs with the repo's. A repo that sets pytest only through jail_env should set test_cmd.

CI tests the pinned framework

The CI jobs and the release suite installed shepherd-ai from PyPI, not the pinned fork wheel. They now install uv.lock with pip install --require-hashes.

Known open item

When claude is missing from the user's PATH, the worker's provider falls back to looking it up on the live PATH, which may hold the repo's jail_env (review thread on #20).

Details for each item: docs/KNOWN_ISSUES.md.

Full Changelog: v0.1.45...v0.1.46

v0.1.45

Choose a tag to compare

@github-actions github-actions released this 21 Oct 02:37
b2f66d3

What's Changed

Full Changelog: v0.1.44...v0.1.45

v0.1.44

Choose a tag to compare

@github-actions github-actions released this 20 Oct 14:50
2254b7a

What's Changed

Full Changelog: v0.1.43...v0.1.44

v0.1.43

Choose a tag to compare

@github-actions github-actions released this 19 Oct 21:11
8bddecb

What's Changed

Full Changelog: v0.1.42...v0.1.43

v0.1.42

Choose a tag to compare

@github-actions github-actions released this 07 Oct 00:27
44e72db

What's Changed

Full Changelog: v0.1.41...v0.1.42

v0.1.41

Choose a tag to compare

@andrefogelman andrefogelman released this 06 Oct 11:44
797fd8a

Added

  • AGENTS.md: the repository's rules for agents, ordered so that the invariants, the commands table and the language rule sit inside the worker's instruction budget; the start-of-task gate, the four steps, git rules and the release ritual follow. .artifacts/ is gitignored for evidence captured while proving a change. (#8)
  • tests/test_agents_md_budget.py pins the real AGENTS.md inside INSTRUCTIONS_BUDGET: the worker-facing sections land before the truncation marker, and the visible slice never ends inside a code fence or an inline code span. (#8)
  • Pullfrog reviews every PR; the workflow carries no API key, the credential is the account's subscription in Pullfrog's store. (#5, #6)

Fixed

  • The pack's truncation marker and its reserve were two hand-typed copies and a bare 20; TRUNCATION_MARKER and _MARKER_RESERVE now carry both, and a plan longer than PLAN_TEXT_CAP is covered by a test. Behaviour unchanged. (#8)
  • shepherd-ai 0.3.4 (fork andrefogelman/shepherd-ai): upstream v0.3.1 merged in, plus the two carried fixes — FileManifest.created_at is tz-aware, and the dialect picks the workspace carrier by platform instead of assuming APFS clonefile. The fork's own red tests on the symlink contract are fixed. (d2ad89d)
  • actions/checkout pinned to v7 in the Pullfrog workflow (dependabot, #7).

v0.1.40

Choose a tag to compare

@github-actions github-actions released this 12 Sep 16:12

Full Changelog: v0.1.39...v0.1.40

v0.1.39

Choose a tag to compare

@andrefogelman andrefogelman released this 02 Sep 11:38

Twelve improvements from a measured study of 285 recorded runs (128 real ones on three repositories), plus three defects the first real runs of the new code exposed. Every item is pinned by tests (973 in the suite) and written up in docs/KNOWN_ISSUES.md with the evidence.

Fixed

  • The worker read its prompt as a source dump and an escaped JSON blob. The substrate's envelope sent the whole of tasks.py and the arguments as ASCII-escaped JSON on one line; the pack travelled under the name guidance. Measured: a median 18 tool calls exploring before the first edit, pack or no pack. Prompts are now rendered as a document (task prompt + one fenced section per input); context, guidance and the gate command travel apart. (promptrender)
  • The jailed worker reached the user's ssh agent, MCP connectors and sub-agents. Twenty-odd workers with no local toolchain shipped the repository over ssh to compile it elsewhere. The launch policy denies sub-agents, task-list, tool-search, web and plan tools, loads no MCP server, ignores the repo's own settings, disables the browser bridge, and unsets credential-shaped variables for the worker. (launch)
  • Any finding rejected the proposal, so most rejections meant nothing. 58% of gate-passing proposals were rejected; the human then accepted 60% of those. Findings now carry a severity; only blocking ones reject, drive rework and block auto-settle; advisories are shown and learned. A rejection naming only advisories is recorded as approved, with a note.
  • No run recorded what it cost or which model produced it. Every launch records tokens, cache, cost, turns, API time and the served model; the summary, --review-report, history and status show them.
  • A worker reaped at its budget was recorded as an unexplained run failure. 13 attempts of ~915 s read as run_failed; they now read timed_out and the retry gets the timeout guidance.
  • optimize hung two minutes per replay case on an inherited stdin (its init asked the review-panel question). Replays run with stdin closed; init asks only on a terminal.
  • A dead login or a spent allowance cost an attempt to discover. An auth preflight resolves the credential before the pack, refreshes a subscription token that would not last the run, and stops on a spent allowance (with its reset time), a dead login, or a stale ~/.claude/.credentials.json shadowing a live keychain login — the shape behind the history's Not logged in failures.
  • The reviewer read the wrong tree. The proposal is applied into the reviewer's working copy before its CLI starts; custody compares the proposal's files by bytes instead of exempting their paths.
  • The planner's targets were the first thing the pack dropped. The pack keeps 15k of its 25k for file content, trims the tree to fit, and places the planner's targets first.
  • The worker had no toolchain. Each launch gets its own writable copy of the repo's jail_seed caches as an extra writable root of the jail (jail_seed_links for caches that must sit in the tree, e.g. node_modules).
  • Bytecode the worker's own test run left behind became part of the proposal, and its NULs killed the reviewer's launch (embedded null byte). Build residue is filtered before it is read; a binary file is named, not rendered.

Added

  • --model, --reviewer-model, --effort and the models config block: model, effort and fallback per role (worker, reviewer, planner).
  • limits.worker_budget / limits.gate_timeout / limits.max_attempts in .shepherd-dev.json, with the flags still winning.
  • speculative_review: auto — the reviewer overlaps the gate where this repo's recent gate-pass rate is high; --no-speculative-review to force it off.
  • status reports tokens, cost, models, exploring calls before the first edit and the reviewer's tool calls per run.
  • worker.prompt, worker.launch, worker.init, worker.result, worker.overlay, worker.seed events in every run's log.

Smoke-tested end to end on a toy repository: worker with zero exploration (2 reads, 2 edits, ran the gate itself, 6 turns), reviewer reading the applied tree and approving, 52 s wall clock, $0.29 as the CLI reported it.

v0.1.38

Choose a tag to compare

@andrefogelman andrefogelman released this 02 Sep 11:38

Fixed

  • The dependency pin moves to the fork's shepherd_ai-0.3.3-py3-none-any.whl, which carries five upstream fixes the fork was missing on top of the two local ones (the .git-as-tree-entry crash and the symlink refusal in the copy carrier). See the pyproject.toml comment for the supply-chain note: a published wheel is never replaced; a change gets a new tag.

Tag created retroactively at the bump commit (ebc41d8): the release step was missed when 0.1.38 shipped.

v0.1.37

Choose a tag to compare

@github-actions github-actions released this 08 Aug 19:57

Full Changelog: v0.1.36...v0.1.37