Skip to content

feat: ACP launch control and server-side session management#5

Draft
AaronGoldsmith wants to merge 4 commits intoblock:mainfrom
AaronGoldsmith:feat/acp-launch-control
Draft

feat: ACP launch control and server-side session management#5
AaronGoldsmith wants to merge 4 commits intoblock:mainfrom
AaronGoldsmith:feat/acp-launch-control

Conversation

@AaronGoldsmith
Copy link

@AaronGoldsmith AaronGoldsmith commented Mar 3, 2026

Summary

Adds the ability to launch and manage a persistent goose orchestrator directly from the goosetown-ui server via the Agent Client Protocol (ACP).

Why ACP?

Previously, running a goosetown session required:

  1. Start a goose process in a separate terminal with the right --wall and --session flags
  2. Start goosetown-ui in another terminal, passing --session and --wall to point at the running process
  3. Manually coordinate wall files across both

This was fragile and required multiple terminals just to see goose in action. With ACP, the server can spawn a goose orchestrator as a persistent subprocess over stdio JSON-RPC — creating the wall file, tracking the session ID, and wiring up SSE events automatically. A single curl or UI button press starts everything.

What's added

  • AcpClient (~130 lines): Async JSON-RPC 2.0 client over stdio for goose acp. Handles initialize handshake, session creation, prompt streaming, and graceful shutdown.
  • /api/launch: Spawn an orchestrator with a prompt. Creates a fresh wall file, tracks the session, returns PID + session ID.
  • /api/launch/status: Check if orchestrator is running, exited, or idle.
  • /api/launch/stop: Gracefully terminate the running orchestrator.
  • /api/prompt: Send follow-up messages to a running orchestrator.
  • find_children: Now discovers sub_agent sessions launched via ACP (not just goose run style "Task X started in background").
  • wall_watcher: Tracks config changes when launch creates a new wall file. Uses wall_generation to discard stale reads during transitions.
  • positions_watcher: Emits wall_read events from .positions/ directory for goose read-position tracking.
  • Wall post nudge: When a human posts to the wall, the ACP orchestrator is auto-prompted to read and respond.
  • CLI clean-start: Server starts without requiring --session/--wall — just launch and use /api/launch.
  • CSP: Allows unsafe-inline in script-src for importmap support (needed by the upcoming 3D town view).

Part of a split

This is part of splitting PR #3 into smaller reviewable pieces:

How to test locally

1. Start the server (clean mode — no args needed):

./scripts/goosetown-ui
# Should print: 🧹 Starting clean — use Launch Orchestrator to begin

2. Launch an orchestrator via curl:

curl -s -X POST localhost:4242/api/launch \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"You are an orchestrator. Post a greeting to the wall using ./gtwall goose-orchestrator \"Hello from ACP!\""}'

Expected: JSON with ok, pid, wall_file, session_id

3. Check status:

curl -s localhost:4242/api/launch/status | python3 -m json.tool

Expected: "status": "running" with PID and elapsed time

4. Send a follow-up prompt:

curl -s -X POST localhost:4242/api/prompt \
  -H 'Content-Type: application/json' \
  -d '{"message":"Post another message to the wall saying you received this follow-up."}'

5. Verify SSE events are flowing:

curl -s localhost:4242/events | head -20

Expected: bootstrap event with session tree data, then wall events as messages appear

6. Open the existing 2D dashboard:

open http://localhost:4242

Expected: Dashboard shows the ACP-launched orchestrator and its delegates

7. Stop the orchestrator:

curl -s -X POST localhost:4242/api/launch/stop

8. Verify it exited cleanly:

curl -s localhost:4242/api/launch/status | python3 -m json.tool

Expected: "status": "exited" with exit code

9. Test with existing wall (backwards compat):

# Start a goose session manually
./goose run --name test-session

# In another terminal, point the UI at it
./scripts/goosetown-ui --session <SESSION_ID> --wall <WALL_FILE>

Expected: Works exactly as before — explicit args still honored

Test plan checklist

  • Clean start (./scripts/goosetown-ui with no args) prints clean-start message
  • /api/launch returns success with pid, session_id, wall_file
  • /api/launch/status shows running while orchestrator is alive
  • /api/prompt sends follow-up to running orchestrator
  • /api/launch/stop terminates orchestrator gracefully
  • /api/launch/status shows exited after stop
  • SSE events include orchestrator's wall posts
  • 2D dashboard shows ACP-launched session tree
  • Wall post from dashboard auto-nudges ACP orchestrator
  • Backwards compat: --session and --wall flags still work

🤖 Generated with Claude Code

AaronGoldsmith and others added 4 commits March 2, 2026 15:53
Add the ability to launch and manage a persistent goose orchestrator
directly from the goosetown-ui server via the Agent Client Protocol (ACP).

Previously, running a goosetown session required manually starting a
goose process in a separate terminal with the right wall/telepathy files,
then pointing the UI at it with --session and --wall flags. This was
fragile and required coordinating multiple terminals.

Now the server can spawn a goose orchestrator as a persistent ACP
subprocess, creating the wall file, tracking the session, and wiring
up SSE events automatically. The UI (or curl) just POSTs a prompt to
/api/launch and everything connects.

Key additions:
- AcpClient: async JSON-RPC 2.0 client over stdio for goose acp
- /api/launch: spawn orchestrator, create wall, track session
- /api/launch/status: check if orchestrator is running/exited
- /api/launch/stop: gracefully terminate the orchestrator
- /api/prompt: send follow-up messages to running orchestrator
- find_children: now discovers ACP-launched sub_agent sessions
- wall_watcher: tracks config changes when launch creates new wall
- positions_watcher: emits wall_read events from .positions/ dir
- wall_post: auto-nudges ACP orchestrator when human posts
- CLI: clean-start mode (no --session/--wall required)
- CSP: allow unsafe-inline for importmap script tags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
44 tests covering parsing, inference, ACP client JSON-RPC protocol,
HTTP launch/status/stop/prompt endpoints, config safety, and routing.
Integration tests with real goose ACP are gated behind
GOOSETOWN_INTEGRATION=1 env var.

Also reverts premature CSP loosening (unsafe-inline in script-src)
that was pre-emptive for a future PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cpClient

refactor: remove unnecessary read lock in AcpClient's stdin processing
@AaronGoldsmith
Copy link
Author

🔍 Pre-Review Triage — Review When Convenient ⚠️ Contested

Adds a full ACP launch-control layer to goosetown-ui — spawn, prompt, and stop goose orchestrators via a JSON-RPC stdio client — a meaningful feature addition with good test coverage and no production urgency.

Full triage analysis — models disagreed

Why now: Part of a planned split from PR #3 — the core feature being incrementally landed.
Cost of not merging: No operational impact; development continues on a branch slightly longer than needed.
Linked ticket: None found in body, branch name, or commits.
Production code changes?: No — this is a personal dev tool (block/goosetown), not a customer-facing production service.

Claude's take (Review When Convenient): New feature with 502 lines of tests and passing CI. No Jira ticket, no incident driving urgency, personal dev tool with limited blast radius if a bug ships.

GPT's take (Needs Review Soon): Introduces a new stdio JSON-RPC control plane and multiple process-lifecycle endpoints — meaningful expansion of request surface. Concerns about lifecycle cleanup, auth/abuse boundaries, and failure handling around orchestrator spawning warrant timely review.

Opus tiebreaker (Review When Convenient): Personal dev tool with 502 lines of tests and passing CI — blast radius is limited to Aaron's local workflow, not production or customers. GPT's concerns about lifecycle cleanup and auth boundaries are valid code quality points worth addressing in review, but they don't elevate urgency for a non-production tool.


🤖 Claude vs GPT disagreed — Opus arbitrated. Generated via /triage-prs

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