You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implement the fully automated setup wizard and fleet lifecycle management. The wizard
SSHs into the target machine, validates connectivity, configures both machines
(fleet.json, guard, tunnel, canonical server), and validates end-to-end. Post-setup,
the panel supports adding machines, removing machines, dismantling the fleet, and
reconfiguring the canonical server's coordinates.
Acceptance Criteria
Create Fleet
"Create Fleet" button visible when standalone; launches the wizard
Topology choice: "This machine is the server" or "A remote machine is the server"
SSH target input: text input for SSH alias or user@host; validates connectivity
with a test command (ssh <target> echo ok)
Pre-flight checklist (rendered live, step by step):
SSH reachable (green check or red X with error)
Opencode binary present on remote (if missing: offer to SCP it)
Target port available on remote (lsof check)
Remote configuration (over SSH):
Writes ~/.amico/ops/fleet/fleet.json with role: "server"
Installs guard script to ~/.local/bin/
Starts canonical server as launchd service (macOS) or systemd (Linux)
Generates fleet token, stores at 0600
Local configuration:
Writes fleet.json with role: "client" + canonical coordinates
Installs guard + tunnel plist
Stores fleet token locally
End-to-end validation: tunnel connects, health probe passes, topology graph
updates to show two nodes with solid connection line
Failure handling: any step failure shows specific error + suggested fix; wizard
does not proceed past a failed step; user can retry or cancel
Add Machine
"Add Machine" button in topology section (visible when fleet exists)
Same SSH validation + pre-flight as Create, but skips server setup
Configures new machine as client; node appears in graph on success
Remove Machine
Node popover → "Remove from Fleet" action
SSHs into target, writes {"role": "standalone"} to fleet.json, unloads
tunnel/guard; node disappears from graph
Cannot remove the server node (shows explanation: dismantle instead)
Dismantle Fleet
Offered when removing the last client, or via overflow menu
Stops canonical server on remote, reverts all machines to standalone
Removes canonical from fleet.json (not just sets role to standalone) — this is
what distinguishes "dismantled" from "disconnected"
Topology graph returns to single-node standalone state with "Create Fleet" button
Reconnect Fleet
When fleet.json has role: "standalone" BUT canonical coordinates are present
(user went standalone but the fleet still exists), panel shows "Reconnect Fleet"
button instead of "Create Fleet"
One click: validates SSH/tunnel connectivity to the stored canonical, switches role
back to client, reinstalls tunnel, validates end-to-end
If validation fails: shows error with option to retry or dismantle (remove canonical)
Reconfigure
Edit canonical coordinates (host, port, SSH alias) from the panel
Validates new connection before committing the change
Updates fleet.json on both machines; restarts tunnel
Testing Decisions
Unit test: fleet_wizard.test.ts — mock SSH execution (child_process.spawn), verify
correct command sequences for each flow (create, add, remove, dismantle)
Unit test: pre-flight check logic — given SSH success/failure, binary present/absent,
port available/taken → correct checklist state
Integration test: full wizard flow with mocked SSH → fleet.json written correctly on
both sides, topology message emitted to webview
Manual verification (HITL): test against a real remote machine before merge
Key Decisions
Decision
Rationale
SSH via child_process.spawn("ssh", [...])
No external SSH library; Node.js native, works with user's existing SSH config/keys
Key-based auth only (no password prompt)
Extension context cannot do interactive TTY auth; key auth is the only reliable path
Wizard is idempotent
Re-running on an already-configured machine checks state, skips done steps, validates final state
One fleet at a time
fleet.json declares a single canonical; reconfigure to change, dismantle to start fresh
Constraints & Invariants
Server survives client disconnect: the canonical server is a system service
(launchd/systemd) with NO dependency on any client or editor being connected. Sessions
and solves continue running when all clients close. Zero connected clients is normal.
SSH commands MUST use the user's SSH config (-F ~/.ssh/config or default) — never
bypass their key/host setup
Wizard MUST NOT store SSH passwords or private keys — it uses whatever auth the user
has configured
Fleet token generated with crypto.randomBytes(32).toString("hex"); stored at 0600
All remote file writes use atomic tmp+mv pattern (same as fleet_registry.ts)
Additional Acceptance Criteria: Server Independence
Canonical server installed as a system service (launchd on macOS, systemd on
Linux) — NOT as a child process of any VS Code instance or editor session
Sessions and solves launched on the server continue running when ALL clients
disconnect (close laptop, quit VS Code, lose network)
On client reconnect, the panel/dropdown reflect the current state of sessions that
ran while disconnected (completed solves show results, running sessions show progress)
The server process has NO dependency on any client being connected — zero clients
connected is a normal operating state
Source
Part of #350
Blocked by #352, #353
ADR 0007: docs/adr/0007-fleet-panel-and-profile-ui.md
Important
Approach
Implement the fully automated setup wizard and fleet lifecycle management. The wizard
SSHs into the target machine, validates connectivity, configures both machines
(fleet.json, guard, tunnel, canonical server), and validates end-to-end. Post-setup,
the panel supports adding machines, removing machines, dismantling the fleet, and
reconfiguring the canonical server's coordinates.
Acceptance Criteria
Create Fleet
user@host; validates connectivitywith a test command (
ssh <target> echo ok)lsofcheck)~/.amico/ops/fleet/fleet.jsonwithrole: "server"~/.local/bin/fleet.jsonwithrole: "client"+ canonical coordinatesupdates to show two nodes with solid connection line
does not proceed past a failed step; user can retry or cancel
Add Machine
Remove Machine
{"role": "standalone"}to fleet.json, unloadstunnel/guard; node disappears from graph
Dismantle Fleet
canonicalfrom fleet.json (not just sets role to standalone) — this iswhat distinguishes "dismantled" from "disconnected"
Reconnect Fleet
role: "standalone"BUTcanonicalcoordinates are present(user went standalone but the fleet still exists), panel shows "Reconnect Fleet"
button instead of "Create Fleet"
back to
client, reinstalls tunnel, validates end-to-endReconfigure
Testing Decisions
fleet_wizard.test.ts— mock SSH execution (child_process.spawn), verifycorrect command sequences for each flow (create, add, remove, dismantle)
port available/taken → correct checklist state
both sides, topology message emitted to webview
Key Decisions
child_process.spawn("ssh", [...])Constraints & Invariants
(launchd/systemd) with NO dependency on any client or editor being connected. Sessions
and solves continue running when all clients close. Zero connected clients is normal.
-F ~/.ssh/configor default) — neverbypass their key/host setup
has configured
crypto.randomBytes(32).toString("hex"); stored at 0600Additional Acceptance Criteria: Server Independence
Linux) — NOT as a child process of any VS Code instance or editor session
disconnect (close laptop, quit VS Code, lose network)
ran while disconnected (completed solves show results, running sessions show progress)
connected is a normal operating state
Source
Part of #350
Blocked by #352, #353
ADR 0007:
docs/adr/0007-fleet-panel-and-profile-ui.md