Workspaces¶
Page status: scaffold Source state: scaffold Applies to: Shepherd v1.0-dev Owner: @docs-system-owner (TBD) Validation: not yet validated
This is a concept page; it builds the mental model. Steps live in the tutorial, signatures in the reference.
Scaffold — not yet runnable
This page is a draft against a surface that has not shipped. Treat commands and code as illustrative until the page is promoted.
A task deliberately says nothing about which model executes it, which directory it works against, or which domain objects are at hand. That silence is what keeps tasks reusable. The missing context comes from the workspace — the ambient scope your tasks run inside:
import shepherd as shp
from shepherd.providers import claude
with shp.workspace(model=claude("sonnet-4-5"), root="./my-project") as ws:
ws.bind(Repository(name="auth-service", languages=("python",)))
review = review_change(diff)
Everything called inside that block — directly or nested arbitrarily deep —
sees the same model, the same root, and the same bound Repository.
Explicit, but ambient¶
Context handling usually forces a bad choice:
- Thread it as parameters, and every signature drags configuration through layers that never use it. On a task this is worse than clutter — it corrupts the signature's meaning, because task parameters are supposed to be evidence for the model, not plumbing for the framework.
- Make it global, and configuration becomes invisible, mutable from anywhere, and hostile to tests and concurrency.
The workspace is the third option: explicit but ambient. Ambient, because
nothing threads it by hand — any task in the block's dynamic extent can reach
it. Explicit, because it is a with block: you can point at the exact line
where the context begins and the exact line where it ends. Nest a second
workspace and the inner one shadows the outer for its extent; leave the block
and the outer context is restored. It is a scope, not a mutable setting.
Bindings: shared objects, looked up by type¶
Beyond the model and the root, programs share longer-lived objects across
tasks — a repository description, a user identity, a database handle. Bind
them once on the workspace, and any task fetches them by type with
shp.current_binding — no string keys, no hidden registry, and a missing
binding is a typed error rather than a None surprise. Inner scopes can
rebind, and the innermost binding wins — which is how tests substitute a fake
without touching the code under test, and how a supervisor scopes a domain
object to one child.
The same scope will carry more¶
Model, root, and bindings are the workspace's job today. The same scope is where execution placement — choosing the contained environment the work runs in — and a workspace-wide default policy attach in the target design: one place where "the situation" is declared, whatever the situation includes.
Design vocabulary — not shipped yet
Placement and workspace-level policy are design vocabulary from docs/proposals/P-021-import-convention.md; they ship with the placement registry and the supervision product work.
The triangle: task, workspace, run¶
Three nouns are easy to blur and worth keeping sharp:
- A task is the declaration — what should happen, typed. Timeless and context-free.
- A workspace is the situation — which model, where, with what at hand. It spans many calls.
- A run is the event — one execution, fully recorded.
Call a task inside a workspace and you get a run. Same task, two different workspaces: two runs you can compare. Same workspace, many tasks: one consistent situation. Each noun answers a different question — what, where and with what, and what happened.
What a workspace is not¶
- Not a global config singleton. It is scoped, nestable, and shadowable; two workspaces can coexist in one program without ever seeing each other.
- Not a conversation. A workspace accumulates no model memory between calls. Tasks inside it remain independent invocations that happen to share configuration — context here is configuration, not history.
Where workspaces sit¶
The first Shepherd app tutorial opens its workspace in the first ten lines. Effect handlers commonly install at workspace scope, and every run carries the context it executed under as part of its record.
Going deeper¶
- Formal semantics:
docs/spec/04-constructs.md§construct-workspace, §sec-bindings (formal spec — repository reference) - Typed lookup:
docs/spec/04-constructs.md§construct-current-binding (formal spec — repository reference) - Ambient-context model:
docs/spec/02-execution-model.md§sec-ambient-context (formal spec — repository reference) - Rationale:
docs/paradigm.md(design rationale — repository reference) - Teaching source:
docs/curriculum/tutorial/07-workspaces-and-ambient-context.md(internal curriculum — repository reference)