Skip to content

Agent-safe dev/spin lifecycle manager - #59

Merged
SushantGautam merged 1 commit into
mainfrom
feat/agent-safe-dev-lifecycle
Oct 16, 2026
Merged

SushantGautam merged 1 commit into
mainfrom
feat/agent-safe-dev-lifecycle

Conversation

@SushantGautam

Copy link
Copy Markdown
Collaborator

Summary

Adds an ownership-safe lifecycle manager so agents can start, reuse, stop, restart, reset and uninstall a local stack without killing unrelated processes or resetting real data. Previously there was no safe way for an agent to manage a running dev/spin instance — repeated starts could double-bind ports, and devctl.sh used destructive kill -9/pgrep port-based cleanup.

What changed

  • simpleaudit_studio/lifecycle.py (new) — single lifecycle manager shared by dev and spin:
    • Instance identity = sha256 of canonical checkout path (dev) or data dir (spin).
    • File locks: owner.lock (non-blocking, prevents double-start), ports.lock (blocking, serializes port allocation).
    • Ports allocated before Django starts; worktree baseline 8100 (not 8000).
    • _live() validates PID running + create_time + UID to reject PID-reuse false positives.
    • Destructive boundaries: refuses custom SIMPLEAUDIT_DATA_DIR/SIMPLEAUDIT_SQLITE_PATH on reset/uninstall; uv cache cleanup is package-scoped only (never global uv cache clean).
  • manage.py — dev dispatches subcommands before django.setup().
  • simpleaudit_studio/cli.py — spin gains an action positional; lifecycle dispatch after arg parse.
  • infra/management/commands/dev_server.py — bug fix: _stop_web now SIGTERMs the runserver reloader subtree by PID (psutil), never by process group. The reloader tree shares the owner's process group with Caddy/Open WebUI, so a group kill would nuke the whole stack, and terminating only the guard left an orphaned runserver holding the internal port.
  • devctl.sh — rewritten as a thin compatibility wrapper; all legacy kill -9/pgrep/lsof port-cleanup removed.
  • Docs — docs/development-lifecycle.md, AGENTS.md, README.md.
  • Tests — tests/test_lifecycle.py (10 network-free tests).

Verified by actual execution

Check Method Result
dev/spin agent-info, status isolated env exit 0, no Django import, read-only, no instance record ✅
Repeated start mock owner harness 2nd start reuses lock, no new port, no kill ✅
Worktree concurrency 2 worktrees, shared state home non-overlapping ports; stopping A left B untouched ✅
PID-reuse protection _live() checks rejects dead/stale PID, accepts valid ✅
Django autoreload edited template, hit page hot-reloaded without restart ✅
Full stack live real dev start healthz/readyz 200; desktop + mobile render ✅
Reset/uninstall boundaries disposable data custom paths refused; uv cleanup package-scoped ✅
dev down orphans before/after proc+listener check zero leftover processes/listeners after fix ✅
Genuine git uvx uvx --from git+...@feat/agent-safe-dev-lifecycle spin … agent-info/status/uninstall-refusal all correct ✅

Test results

  • Ruff: All checks passed!
  • Full suite (pytest -m "not embedded_hatchet"): 1283 passed, 4 failed — all 4 pre-existing (fail on base too: catalog metadata drift, retrieval-source INCONCLUSIVE, 2× verify_agentic_audit SystemExit). This branch introduces zero new failures.
  • Lifecycle tests: 10/10 pass.

Known limitations (documented in docs/development-lifecycle.md)

  • Worker is NOT hot-reloaded — it runs in the owner's main thread (embedded Hatchet singleton requires the main thread). Worker-code changes need a planned restart. Not implementable without a separate worker-process phase.
  • uvx may retain shared cached environments that can't be safely identified by package on uninstall.
  • Shared-PostgreSQL / multi-instance is out of scope for this single-process manager.

⚠️ Unverified: real data-deletion path of spin uninstall/reset

The genuine data-deletion code path (uninstall/reset against the default spin data dir) was not exercised end-to-end because this machine's ~/.simpleaudit-studio holds ~2.5GB of real user data and the safety boundary forbids resetting it. The refusal path (custom data dir → refused) was verified through a real git-based uvx install. This PR should stay unmerged until spin uninstall/reset cleanup is validated from a genuine uvx installation on a machine where the default data dir is disposable.

Notes

  • Stacked on main; does not merge the earlier agentic-demo work.
  • No unrelated processes killed, no real user data touched, no paid model APIs used during verification.

Adds simpleaudit_studio/lifecycle.py so agents can safely start, reuse,
stop, restart, reset and uninstall a local stack without killing unrelated
processes or resetting real data.

- Instance identity: sha256 of canonical checkout path (dev) or data dir
  (spin); file locks (owner.lock non-blocking, ports.lock blocking).
- Port allocation before Django starts; worktree baseline 8100.
- _live() validates PID running + create_time + UID to reject PID reuse.
- Destructive boundaries: refuses custom SIMPLEAUDIT_DATA_DIR /
  SIMPLEAUDIT_SQLITE_PATH on reset/uninstall; uv cache cleanup is
  package-scoped only (never global `uv cache clean`).
- manage.py `dev` and spin CLI dispatch subcommands before django.setup().
- devctl.sh rewritten as a thin compatibility wrapper; all legacy
  kill -9 / pgrep / lsof port-based cleanup removed.

Fix orphaned runserver on shutdown: _stop_web now SIGTERMs the reloader
subtree by PID (psutil), never by process group, because the reloader tree
shares the owner's group with Caddy/Open WebUI which must keep running.

Verified live: healthz 200, Django template autoreload works without
restart, desktop + mobile render correctly, spin agent-info/status are
read-only, and `dev down` leaves no orphaned processes.

Docs: docs/development-lifecycle.md, AGENTS.md, README.md.
Tests: tests/test_lifecycle.py (10 network-free tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SushantGautam
SushantGautam marked this pull request as ready for review October 16, 2026 22:53
@SushantGautam
SushantGautam merged commit 80a3ce5 into main Oct 16, 2026
1 of 2 checks passed
@SushantGautam
SushantGautam deleted the feat/agent-safe-dev-lifecycle branch October 16, 2026 22:54
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