Skip to content

release: 1.1.0 — daemon mode (--start/--attach/--stop) + network-orphan extras - #39

Merged
rappdw merged 1 commit into
mainfrom
release/v1.1.0
Jul 14, 2026
Merged

release: 1.1.0 — daemon mode (--start/--attach/--stop) + network-orphan extras#39
rappdw merged 1 commit into
mainfrom
release/v1.1.0

Conversation

@rappdw

@rappdw rappdw commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Squashed release commit for the 1.1.0 daemon-mode milestone (#17 + #26). Closes both on merge.

  • Daemon mode: --start / --attach / --stop, supervisor architecture, container-label state, DEC-A/B/C exit-code contract, introspection additions (schema_version stays 1).
  • Network-orphan extras: --prune-orphans, orphan_networks, prune-on-startup.
  • Host-validated: test/acceptance-daemon.sh green on real Docker (macOS), 661/661 unit tests, full integration suite.

Fast-forward main to this commit after CI, then tag v1.1.0.

🤖 Generated with Claude Code

…an extras

THE DAEMON-MODE RELEASE. A sandy session's lifetime is no longer tied to the
launching client: start detached, attach/detach clients at will (including
from sandy-ui), survive a host reboot, stop explicitly. Additive and
backward-compatible: bare `sandy` semantics unchanged, sandbox forward-compat
promise holds, introspection schema_version stays 1.

Daemon mode (#17):
- `--start`: forks a detached supervisor (nohup+disown; portable — no setsid,
  no `sleep infinity`, both absent/broken on macOS) which owns the workspace
  lock, the helper processes, and the cleanup trap; the container runs
  detached with --restart unless-stopped (reboot survival) and no --rm.
  Returns 0 only once the inner tmux session is attachable; streams the
  supervisor log; fails fast on a crash-looping container with docker-logs
  diagnostics. Idempotent; refuses headless (-p would restart-loop).
- `--attach`: last-wins concurrent attach (tmux attach -d). Exit codes:
  0 = session ended while attached, 3 = clean detach (session lives),
  4 = no such session, 5 = attach failed.
- `--stop`: signals a live supervisor so its own trap releases the #14 lock;
  tears down directly (container, networks, stale lock) when the supervisor
  is gone (reboot case). Exit 0/4/5.
- Bare `sandy` over a live daemon session: error-with-hint, exit 1 — keyed
  off the container label, so a supervisor-less rebooted session is
  respected, not clobbered.
- State = container labels (sandy.daemon/workspace_path/session/started_at/
  daemon_pid); container existence is the durable source of truth.
- All daemon tmux probes exec as the host uid (-u): the image has no USER
  directive, so a bare docker exec runs as root and can never see the
  gosu-dropped user's tmux socket.
- Introspection: cli_flags += --start/--attach/--stop; running_containers[]
  += sandbox (sandboxes[].name join key), daemon (bool), attached_clients
  (int|null, failure-guarded).

Network-orphan extras (#26):
- `--prune-orphans`: pre-preflight fast-path maintenance command; exit 0
  (incl. "found none") / 1 (docker unreachable).
- `orphan_networks` in --print-state (both modes; null when docker absent).
- Prune-on-startup on every launch; single source of truth lister
  (_sandy_orphan_networks_list) shared by reaper, count, and flag — a net is
  an orphan only if its owner PID is dead AND no container is attached, so
  live/mid-setup/reboot-resurrected sessions are never touched.

Verification: end-to-end acceptance harness (test/acceptance-daemon.sh) green
on a real macOS Docker host — including agent-process PID identity across an
abruptly killed attach client (in-memory state preserved by construction) —
plus 661/661 unit tests and the full integration suite.

Fixes #17
Fixes #26

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rappdw
rappdw merged commit 57d7c35 into main Jul 14, 2026
2 checks passed
@rappdw
rappdw deleted the release/v1.1.0 branch July 14, 2026 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant