docs · pre-release
Menu
Reference / What's not built

What's not built

shep is feature-complete for v1 on macOS and Linux: every verb in the CLI reference, the whistle MCP server, all four lookout panes, dogs, serve/dev/runtime, and config and packaging. Windows runs the same build and the same day-to-day loop, minus a named handful of refusals it makes out loud. This page is the honest remainder: what the newest platform does not do.

Windows: built, with named limits

This section used to say the opposite. Until 2026-08-26 every command on Windows printed shep does not yet support Windows and exited 1, and the costed decision below was to keep it that way. A Windows host became available, the tier was built, and it is kept here rather than deleted because the estimate turned out to be a good one and the record of what it got right is worth more than a tidy page.

The day-to-day loop works: start, stop, restart, reload, flock, describe, bleats, delete, save/muster, lookout, whistle and the dogs — over a named pipe instead of a unix socket, with each sheep contained in a job object instead of a process group.

careful
These limits are real and are not going to be papered over. Windows has nothing SIGTERM-shaped that can be delivered to an arbitrary process, so shep stop waits out the app's whole kill_timeout and then terminates it — unless the app opts into the shepherd channel with shutdown_with_message, which is the supported graceful path there. And shep startup is not built: boot-time supervision on Windows means a Service Control Manager service, a different program shape rather than a fifth unit template. Run shep start in your own session, or wrap shep runtime in NSSM or WinSW, tracked as issue 348.

The estimate's own shape predictions all held. The 145 cfg(unix) sites really were the cheap part — only ten files in the CLI contained a Unix API call at all, and four whole module trees contained none. The cost concentrated exactly where it was predicted to:

  • Graceful stop has no equivalent, and the runner refuses rather than pretending: an arm that returned success having done nothing would turn every shep stop into a silent wait-then-kill with no line saying why.
  • The shepherd channel is not fd 3. It is a named pipe whose path arrives as $SHEP_CHANNEL_PIPE; the app opens it itself. The wire format is untouched, so shep trigger's correlation id survives unchanged. SHEP_CHANNEL_FD is deliberately not set there, so an app can branch on which variable is present rather than on the platform.
  • user/group refuse permanently. Dropping privilege on Windows needs a logon session or a primary-token privilege: a different feature, not a different call.

Two things the estimate did not anticipate, both found only by running the code: a Windows child started with a genuinely empty environment fails before main (many Win32 APIs read %SystemRoot%), and an append-mode handle cannot be truncated there, which broke shep start's autostart outright. Neither is visible to a compiler.

A Python app can crash on its own output

Not a shep bug, but a real operator report, so it's named here where a search for the error will find it:

UnicodeEncodeError: 'charmap' codec can't encode characters in position 5-7: character maps to <undefined>

Up to CPython 3.14, Python on Windows picks its stdout/stderr encoding by whether a real console is attached. A shepherd-managed process never has one, since its output is a pipe, so CPython falls back to the legacy code page (cp1252 on a US/Western install) instead of UTF-8. The app runs fine at a terminal and crashes the moment shep supervises it, the first time it prints anything outside that code page. CPython 3.15 turns UTF-8 mode on by default (PEP 686), so upgrading fixes it too, unless something sets PYTHONUTF8=0.

Two environment variables fix it: PYTHONUTF8=1 and PYTHONIOENCODING=utf-8. Set them in the app's own env to fix that one app. Or set them once in the shepherd's own environment before shep startup or shep start: they're now on the short list of variables shep passes down to every child on Windows, same as TEMP or USERPROFILE, so an operator running several Python apps doesn't repeat them per app. Unix has LANG in that role and shep passes it down, so a shepherd whose own LANG names a UTF-8 locale gives its children one. Windows has no equivalent to pass.

Five smaller cuts, argued for rather than just missing

Spec §2 names these as deliberate v1.1+ scope, not oversights:

  • HTTP/SSE transport for whistle. Ships stdio-only first; an AI agent reaches it the way any MCP stdio server is reached today. Tracked as issue 343.
  • cgroup v2 enforcement (enforce = "kernel"). The polling LimitEnforcer is the v1 tier; a kernel-enforced ceiling is later work. Tracked asissue 344.
  • The @shep/io npm shim. Built on demand, not before anyone's asked for it. Tracked as issue 362, with Python alongside it at issue 363.
  • VCS metadata (the vcs feature). Off by default, not wired into the build. Tracked as issue 345.
  • A JSON status endpoint. Resolved rather than just deferred: the metrics dog serves Prometheus exposition text for a scraper, which turned out to be an incompatible shape for what would have been a hand-fetched dashboard payload. That's a different feature, not a narrower version of this one, so it stays its own open item rather than folding into the dog. Tracked as issue 346.

Scheduled, not decided against

Named as v1.0 scope in the spec and hasn't reached the front of the queue. Lookout's three items in this section shipped in Phase 16 (search and filter, the action keys behind the control gate, and lambs in the detail pane), which leaves one:

  • OTLP export for the metrics dog. The dog serves Prometheus exposition only today; no otel cargo feature exists yet. Tracked as issue 347.