From 4e1fa880763ec3881d356aebbdebe24d59859f86 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Mon, 22 Jun 2026 15:07:05 -0500 Subject: [PATCH] =?UTF-8?q?refactor(fleet):=20rename=20tmux=20socket=20mos?= =?UTF-8?q?aic-factory=20=E2=86=92=20mosaic-fleet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pure rename of the named production-isolation socket to match the product brand (Mosaic Fleet), per Jason. Behavior is identical — only the socket NAME changes. - 6 example presets: socket_name: mosaic-factory → mosaic-fleet - fleet.ts: DEFAULT_SOCKET_NAME = 'mosaic-fleet' (+ all literals) - systemd units + READMEs, roster.schema.json, start-agent-session.sh - docs/guides + fleet PRD/TASKS + scratchpads; tests updated The PoC roster is socket-LESS (default socket, no -L), so it is unaffected. docs/fleet/north-star.md is intentionally EXCLUDED (owned by the in-flight doctrine PR #629 — its mosaic-factory references are renamed there to avoid a merge conflict). The live legacy canary fleet still running on the old socket is a separate retire/migrate op, not this PR. Single find/replace — target trivially swappable if the brand is reconsidered. Verified: 172 fleet + onboarding tests green; tsc/eslint/prettier/sanitize clean. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01EsgTQzV5YUGk1JtCLP4B83 --- docs/fleet/PRD.md | 8 +-- docs/fleet/TASKS.md | 24 ++++---- docs/guides/fleet-local-canary.md | 12 ++-- .../2026-06-20-fleet-cli-local-canary.md | 4 +- .../scratchpads/fleet-observability-phase2.md | 4 +- docs/scratchpads/fleet-standup-fixes.md | 6 +- packages/mosaic/framework/fleet/README.md | 2 +- .../framework/fleet/examples/coding.yaml | 2 +- .../framework/fleet/examples/general.yaml | 2 +- .../framework/fleet/examples/hybrid.yaml | 2 +- .../fleet/examples/local-canary.yaml | 2 +- .../framework/fleet/examples/minimal.yaml | 2 +- .../framework/fleet/examples/research.yaml | 2 +- .../mosaic/framework/fleet/roster.schema.json | 4 +- .../mosaic/framework/systemd/user/README.md | 6 +- .../systemd/user/mosaic-tmux-holder.service | 2 +- .../tools/fleet/start-agent-session.sh | 2 +- .../mosaic/framework/tools/tmux/README.md | 8 +-- packages/mosaic/src/commands/fleet.spec.ts | 56 +++++++++---------- packages/mosaic/src/commands/fleet.ts | 6 +- .../mosaic/src/fleet/comms-onboarding.spec.ts | 12 ++-- 21 files changed, 84 insertions(+), 84 deletions(-) diff --git a/docs/fleet/PRD.md b/docs/fleet/PRD.md index 8a3cb04..6bd52e9 100644 --- a/docs/fleet/PRD.md +++ b/docs/fleet/PRD.md @@ -7,10 +7,10 @@ ## Problem -The durable tmux fleet runs on the isolated `mosaic-factory` socket. That isolation +The durable tmux fleet runs on the isolated `mosaic-fleet` socket. That isolation (which protects the operator's default tmux) makes the fleet **invisible** to default tooling, and truth is split across three planes no single command joins — systemd -(`systemctl --user`), tmux (`-L mosaic-factory`), and the process tree (`pstree`). +(`systemctl --user`), tmux (`-L mosaic-fleet`), and the process tree (`pstree`). `agent tail` (`capture-pane`) returns **blank for full-screen TUIs**, and `agent send` confirms only keystroke injection, not acceptance. Net: the operator has near-zero observability and no safe way to watch a session. @@ -56,7 +56,7 @@ observability and no safe way to watch a session. ## Acceptance criteria -- `mosaic fleet ps` shows all 5 live sessions on `mosaic-factory` with correct +- `mosaic fleet ps` shows all 5 live sessions on `mosaic-fleet` with correct pane/pid/idle and flags the dogfood **drift** (`canary-pi` runtime=pi but pane runs `dogfood-agent.py`) and the **boot-enable** gap (active but disabled). - Killing one agent's pane flips its row to dead/stale within one `interval`. @@ -72,7 +72,7 @@ observability and no safe way to watch a session. - Unit/CLI specs in `packages/mosaic/src/commands/fleet.spec.ts` (and a new `fleet-ps`/`watch`/`send-verify` spec) using the injected `CommandRunner` to assert exact tmux/systemd command construction and JSON shape (tenant+host present). -- Situational: run against the live `mosaic-factory` fleet; capture `fleet ps` output, +- Situational: run against the live `mosaic-fleet` fleet; capture `fleet ps` output, a kill-and-detect cycle, a read-only `watch`, and a `send --verify` pass/fail pair. ## Known limitations diff --git a/docs/fleet/TASKS.md b/docs/fleet/TASKS.md index af72679..7cd9447 100644 --- a/docs/fleet/TASKS.md +++ b/docs/fleet/TASKS.md @@ -7,18 +7,18 @@ > Mission: `mvp-20260312` · PRD: [docs/fleet/PRD.md](./PRD.md) · North star: [docs/fleet/north-star.md](./north-star.md) > Status: `not-started` | `in-progress` | `done` | `blocked` | `failed` -| id | status | description | depends_on | agent | pr | notes | -| ------------- | ----------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- | --- | ----------------------------------------------------------------------------------------------------------------------------- | -| FLEET-OBS-000 | done | Plan: north-star + Phase-2 PRD + workstream scaffolding | — | lead | — | persisted 2026-06-20 on `feat/fleet-observability` | -| FLEET-OBS-001 | done | Heartbeat protocol v1 spec finalized in PRD + framework doc | FLEET-OBS-000 | lead | — | file-based `~/.config/mosaic/fleet/run/.hb`; spec in PRD | -| FLEET-OBS-002 | in-progress | Implement heartbeat responder in `dogfood-agent.py` | FLEET-OBS-001 | fleet-coder | — | dispatched to ad-hoc `mosaic yolo` fleet agent (dogfood) | -| FLEET-OBS-003 | done | `mosaic fleet ps` — join systemd+tmux+proc+idle+heartbeat; tenant+host tagged; drift + boot-enable flags; `--json` | FLEET-OBS-001 | worker | — | commit ab47831; LIVE-verified on mosaic-factory; caught canary-pi DRIFT + BOOT-ENABLE. Polish: idleSeconds parse returns null | -| FLEET-OBS-004 | done | `mosaic agent watch ` — read-only join (no resize, no keystrokes) | FLEET-OBS-000 | worker | — | `attach -r`; verb wired | -| FLEET-OBS-005 | done | `mosaic agent send --verify` — delivery/acceptance receipt | FLEET-OBS-000 | worker | — | --verify flag; draft-heuristic verify | -| FLEET-OBS-006 | done | CLI specs for ps/watch/send-verify (tenant+host shape, command construction) | FLEET-OBS-003,004,005 | worker | — | 62 tests green (31 new); re-verified by lead | -| FLEET-OBS-007 | not-started | Framework doc: fleet observability guide + verbs | FLEET-OBS-003,004,005 | lead | — | `docs/guides/` or `framework/tools/.../README` | -| FLEET-OBS-008 | not-started | Independent review + dogfood verification on live fleet | FLEET-OBS-002..007 | reviewer | — | author ≠ reviewer; capture evidence in scratchpad | -| FLEET-OBS-009 | not-started | Open PR → green CI (queue guard) → squash-merge → close `fleet-observability-1` | FLEET-OBS-008 | lead | — | trunk merge; no direct push to main | +| id | status | description | depends_on | agent | pr | notes | +| ------------- | ----------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- | --- | --------------------------------------------------------------------------------------------------------------------------- | +| FLEET-OBS-000 | done | Plan: north-star + Phase-2 PRD + workstream scaffolding | — | lead | — | persisted 2026-06-20 on `feat/fleet-observability` | +| FLEET-OBS-001 | done | Heartbeat protocol v1 spec finalized in PRD + framework doc | FLEET-OBS-000 | lead | — | file-based `~/.config/mosaic/fleet/run/.hb`; spec in PRD | +| FLEET-OBS-002 | in-progress | Implement heartbeat responder in `dogfood-agent.py` | FLEET-OBS-001 | fleet-coder | — | dispatched to ad-hoc `mosaic yolo` fleet agent (dogfood) | +| FLEET-OBS-003 | done | `mosaic fleet ps` — join systemd+tmux+proc+idle+heartbeat; tenant+host tagged; drift + boot-enable flags; `--json` | FLEET-OBS-001 | worker | — | commit ab47831; LIVE-verified on mosaic-fleet; caught canary-pi DRIFT + BOOT-ENABLE. Polish: idleSeconds parse returns null | +| FLEET-OBS-004 | done | `mosaic agent watch ` — read-only join (no resize, no keystrokes) | FLEET-OBS-000 | worker | — | `attach -r`; verb wired | +| FLEET-OBS-005 | done | `mosaic agent send --verify` — delivery/acceptance receipt | FLEET-OBS-000 | worker | — | --verify flag; draft-heuristic verify | +| FLEET-OBS-006 | done | CLI specs for ps/watch/send-verify (tenant+host shape, command construction) | FLEET-OBS-003,004,005 | worker | — | 62 tests green (31 new); re-verified by lead | +| FLEET-OBS-007 | not-started | Framework doc: fleet observability guide + verbs | FLEET-OBS-003,004,005 | lead | — | `docs/guides/` or `framework/tools/.../README` | +| FLEET-OBS-008 | not-started | Independent review + dogfood verification on live fleet | FLEET-OBS-002..007 | reviewer | — | author ≠ reviewer; capture evidence in scratchpad | +| FLEET-OBS-009 | not-started | Open PR → green CI (queue guard) → squash-merge → close `fleet-observability-1` | FLEET-OBS-008 | lead | — | trunk merge; no direct push to main | ## Proposed MVP rollup row (for the MVP orchestrator — not written by this workstream) diff --git a/docs/guides/fleet-local-canary.md b/docs/guides/fleet-local-canary.md index 24861ff..9350aab 100644 --- a/docs/guides/fleet-local-canary.md +++ b/docs/guides/fleet-local-canary.md @@ -1,7 +1,7 @@ # Local Fleet Canary The local fleet canary runs a small tmux-backed Mosaic agent fleet on an -isolated tmux socket. The default socket is `mosaic-factory`; the commands do +isolated tmux socket. The default socket is `mosaic-fleet`; the commands do not use or stop the default tmux server. ## Files @@ -67,7 +67,7 @@ mosaic agent tail canary-pi -n 80 These commands read the roster and target the configured tmux socket. The generated systemd agent services use `start-agent-session.sh`; message delivery -uses the tmux send tools with `-L mosaic-factory`. +uses the tmux send tools with `-L mosaic-fleet`. `mosaic agent send` is operator-origin traffic unless a caller explicitly says otherwise. The CLI always passes a deterministic source label to @@ -82,7 +82,7 @@ impersonating a known handoff lane. The lower-level inter-agent wrapper Use these checks before expanding the roster: ```bash -tmux -L mosaic-factory ls +tmux -L mosaic-fleet ls tmux ls mosaic fleet verify systemctl --user status mosaic-tmux-holder.service @@ -90,7 +90,7 @@ systemctl --user status mosaic-tmux-holder.service Expected results: -- `tmux -L mosaic-factory ls` shows `_holder` and roster agent sessions. +- `tmux -L mosaic-fleet ls` shows `_holder` and roster agent sessions. - `tmux ls` shows only the default tmux server sessions and is not changed by fleet start/stop operations. - `mosaic fleet verify` checks exact session targets on the isolated socket. @@ -108,7 +108,7 @@ Run this checklist before cutting or dogfooding a fleet release: repeated `start` against the named socket; verify the default tmux server is unchanged. - Liveness verification: run `mosaic fleet verify` and confirm roster sessions - with `tmux -L mosaic-factory ls` or exact `has-session` checks. + with `tmux -L mosaic-fleet ls` or exact `has-session` checks. - Package dry-run: run `npm pack --dry-run --json` from `packages/mosaic` and confirm `framework/fleet`, `framework/systemd/user`, `framework/tools/fleet`, and `framework/tools/tmux` assets are included. @@ -140,5 +140,5 @@ This rollback leaves the default tmux server untouched. If a canary session is still present after service stop, remove only the isolated socket server: ```bash -tmux -L mosaic-factory kill-server +tmux -L mosaic-fleet kill-server ``` diff --git a/docs/scratchpads/2026-06-20-fleet-cli-local-canary.md b/docs/scratchpads/2026-06-20-fleet-cli-local-canary.md index 7f84750..82bfc11 100644 --- a/docs/scratchpads/2026-06-20-fleet-cli-local-canary.md +++ b/docs/scratchpads/2026-06-20-fleet-cli-local-canary.md @@ -17,7 +17,7 @@ Implement enough product surface to use the fleet locally: - roster schema and examples - local canary docs and rollback instructions - tests for CLI behavior where practical -- canary verification on named tmux socket `mosaic-factory` +- canary verification on named tmux socket `mosaic-fleet` ## Non-goals @@ -30,7 +30,7 @@ Implement enough product surface to use the fleet locally: - CLI can initialize a minimal roster outside product defaults. - CLI can install user systemd units and fleet helper scripts to a configurable Mosaic home. -- CLI can start/stop/status/verify a canary fleet using `mosaic-factory`. +- CLI can start/stop/status/verify a canary fleet using `mosaic-fleet`. - `mosaic agent send` uses existing named-socket/exact-target tmux tooling. - `mosaic agent reset` targets only the named agent session on the named socket. - Verification proves default tmux sessions remain untouched. diff --git a/docs/scratchpads/fleet-observability-phase2.md b/docs/scratchpads/fleet-observability-phase2.md index e499f6d..f1ce168 100644 --- a/docs/scratchpads/fleet-observability-phase2.md +++ b/docs/scratchpads/fleet-observability-phase2.md @@ -31,7 +31,7 @@ with a second agent on `dragon-lin`. ## Environment facts (verified 2026-06-20) - Fleet is live on `W-jarvis` (uid 1000, `jarvis`, `Linger=yes`) on tmux socket - `mosaic-factory`: `_holder`, `canary-pi`, `dogfood-coder`, `dogfood-orchestrator`, + `mosaic-fleet`: `_holder`, `canary-pi`, `dogfood-coder`, `dogfood-orchestrator`, `dogfood-reviewer`. All panes run `~/.config/mosaic/fleet/dogfood-agent.py` (stub), including `canary-pi` (roster says runtime=pi → **drift**). - Holder + `mosaic-agent@*` units are `active (exited)` but `UnitFileState=disabled` @@ -56,7 +56,7 @@ with a second agent on `dragon-lin`. with dragon-lin coder, commit docs, begin Phase-2 delivery (heartbeat + `fleet ps`). - 2026-06-20 (session 2): Built Phase-2 CLI via worker (commit ab47831): `fleet ps`, `agent watch`, `agent send --verify`, 62 tests. LIVE-verified `fleet ps` on - mosaic-factory — correctly flagged canary-pi DRIFT + BOOT-ENABLE, tenant_id+host in JSON. + mosaic-fleet — correctly flagged canary-pi DRIFT + BOOT-ENABLE, tenant_id+host in JSON. Heartbeat responder added to dogfood-agent.py (FLEET-OBS-002) — `fleet ps` HB now `healthy` for all 4 agents. - Coordination: dual-engine-reviewed (Claude+Codex) and merged framework PRs #572 diff --git a/docs/scratchpads/fleet-standup-fixes.md b/docs/scratchpads/fleet-standup-fixes.md index 0035def..db64a00 100644 --- a/docs/scratchpads/fleet-standup-fixes.md +++ b/docs/scratchpads/fleet-standup-fixes.md @@ -11,14 +11,14 @@ ## FIX 2 — socket default trap (absent ⇒ literal default socket, no -L everywhere) - THE TRAP (3 sites): parseRosterText fallback was DEFAULT_SOCKET_NAME; systemd unit had - `Environment=MOSAIC_TMUX_SOCKET=mosaic-factory` + `ExecStop ${…:-mosaic-factory}`; start-agent-session - defaulted `:-mosaic-factory`. All fixed → absent socket = '' = default tmux socket (no -L). + `Environment=MOSAIC_TMUX_SOCKET=mosaic-fleet` + `ExecStop ${…:-mosaic-fleet}`; start-agent-session + defaulted `:-mosaic-fleet`. All fixed → absent socket = '' = default tmux socket (no -L). - `socketArgs(name)` helper → `name ? ['-L', name] : []`; replaced all ~15 -L render sites in fleet.ts. - shellEnvValue('') now emits a **bare** `VAR=` (not `''`) — unambiguous empty in systemd EnvironmentFile (a quoted '' could become a literal socket named "''"). - start-agent-session.sh: `_tmux` wrapper passes -L only when socket set; mosaic-agent@.service: dropped the socket default + conditional ExecStop. So spawn == observe == onboarding cheat-sheet. -- CONTAINMENT: all 6 shipped presets set socket_name: mosaic-factory explicitly → unaffected; only +- CONTAINMENT: all 6 shipped presets set socket_name: mosaic-fleet explicitly → unaffected; only socket-less rosters (the PoC) get default-socket behavior. DEFAULT_SOCKET_NAME exported for explicit use. ## Verification diff --git a/packages/mosaic/framework/fleet/README.md b/packages/mosaic/framework/fleet/README.md index c8a996f..b1480fd 100644 --- a/packages/mosaic/framework/fleet/README.md +++ b/packages/mosaic/framework/fleet/README.md @@ -8,7 +8,7 @@ package, normally at: ~/.config/mosaic/fleet/roster.yaml ``` -The default tmux socket is `mosaic-factory` so fleet commands do not touch the +The default tmux socket is `mosaic-fleet` so fleet commands do not touch the default tmux server. ## Examples diff --git a/packages/mosaic/framework/fleet/examples/coding.yaml b/packages/mosaic/framework/fleet/examples/coding.yaml index 006dc3f..82616fd 100644 --- a/packages/mosaic/framework/fleet/examples/coding.yaml +++ b/packages/mosaic/framework/fleet/examples/coding.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~ diff --git a/packages/mosaic/framework/fleet/examples/general.yaml b/packages/mosaic/framework/fleet/examples/general.yaml index 91d2ab6..af01aea 100644 --- a/packages/mosaic/framework/fleet/examples/general.yaml +++ b/packages/mosaic/framework/fleet/examples/general.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~ diff --git a/packages/mosaic/framework/fleet/examples/hybrid.yaml b/packages/mosaic/framework/fleet/examples/hybrid.yaml index 0e5a1e0..d32e83b 100644 --- a/packages/mosaic/framework/fleet/examples/hybrid.yaml +++ b/packages/mosaic/framework/fleet/examples/hybrid.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~ diff --git a/packages/mosaic/framework/fleet/examples/local-canary.yaml b/packages/mosaic/framework/fleet/examples/local-canary.yaml index 5ed48a1..32b259e 100644 --- a/packages/mosaic/framework/fleet/examples/local-canary.yaml +++ b/packages/mosaic/framework/fleet/examples/local-canary.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~/src diff --git a/packages/mosaic/framework/fleet/examples/minimal.yaml b/packages/mosaic/framework/fleet/examples/minimal.yaml index 7ffa29d..b67130c 100644 --- a/packages/mosaic/framework/fleet/examples/minimal.yaml +++ b/packages/mosaic/framework/fleet/examples/minimal.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~/src diff --git a/packages/mosaic/framework/fleet/examples/research.yaml b/packages/mosaic/framework/fleet/examples/research.yaml index ee9bdea..c064312 100644 --- a/packages/mosaic/framework/fleet/examples/research.yaml +++ b/packages/mosaic/framework/fleet/examples/research.yaml @@ -1,7 +1,7 @@ version: 1 transport: tmux tmux: - socket_name: mosaic-factory + socket_name: mosaic-fleet holder_session: _holder defaults: working_directory: ~ diff --git a/packages/mosaic/framework/fleet/roster.schema.json b/packages/mosaic/framework/fleet/roster.schema.json index 5d96048..4327075 100644 --- a/packages/mosaic/framework/fleet/roster.schema.json +++ b/packages/mosaic/framework/fleet/roster.schema.json @@ -18,11 +18,11 @@ "properties": { "socket_name": { "type": "string", - "default": "mosaic-factory" + "default": "mosaic-fleet" }, "socketName": { "type": "string", - "default": "mosaic-factory" + "default": "mosaic-fleet" }, "holder_session": { "type": "string", diff --git a/packages/mosaic/framework/systemd/user/README.md b/packages/mosaic/framework/systemd/user/README.md index 56da71e..f1e0b8a 100644 --- a/packages/mosaic/framework/systemd/user/README.md +++ b/packages/mosaic/framework/systemd/user/README.md @@ -33,7 +33,7 @@ Per-agent overrides live outside the package in: Example: ```dotenv -MOSAIC_TMUX_SOCKET=mosaic-factory +MOSAIC_TMUX_SOCKET=mosaic-fleet MOSAIC_AGENT_RUNTIME=claude MOSAIC_AGENT_WORKDIR=$HOME/src/your-project # Optional escape hatch for PoC/canary agents: @@ -50,8 +50,8 @@ chmod +x ~/.config/mosaic/tools/fleet/start-agent-session.sh systemctl --user daemon-reload systemctl --user start mosaic-tmux-holder.service systemctl --user start mosaic-agent@canary.service -tmux -L mosaic-factory ls +tmux -L mosaic-fleet ls ``` -Do not use `tmux kill-server` without `-L mosaic-factory`; this pattern is meant +Do not use `tmux kill-server` without `-L mosaic-fleet`; this pattern is meant to avoid disturbing the user's default tmux server. diff --git a/packages/mosaic/framework/systemd/user/mosaic-tmux-holder.service b/packages/mosaic/framework/systemd/user/mosaic-tmux-holder.service index b82e2fb..a4ae3ae 100644 --- a/packages/mosaic/framework/systemd/user/mosaic-tmux-holder.service +++ b/packages/mosaic/framework/systemd/user/mosaic-tmux-holder.service @@ -6,7 +6,7 @@ After=default.target [Service] Type=oneshot RemainAfterExit=yes -Environment=MOSAIC_TMUX_SOCKET=mosaic-factory +Environment=MOSAIC_TMUX_SOCKET=mosaic-fleet Environment=MOSAIC_TMUX_HOLDER=_holder ExecStart=/bin/bash -lc 'tmux -L "$MOSAIC_TMUX_SOCKET" has-session -t "=${MOSAIC_TMUX_HOLDER}:0.0" 2>/dev/null || tmux -L "$MOSAIC_TMUX_SOCKET" new-session -d -s "$MOSAIC_TMUX_HOLDER" "while true; do sleep 3600; done"' ExecStop=-/bin/bash -lc 'tmux -L "$MOSAIC_TMUX_SOCKET" kill-server' diff --git a/packages/mosaic/framework/tools/fleet/start-agent-session.sh b/packages/mosaic/framework/tools/fleet/start-agent-session.sh index 59bbefa..d85fef8 100755 --- a/packages/mosaic/framework/tools/fleet/start-agent-session.sh +++ b/packages/mosaic/framework/tools/fleet/start-agent-session.sh @@ -3,7 +3,7 @@ set -euo pipefail AGENT_NAME=${1:-${MOSAIC_AGENT_NAME:-}} # Absent socket ⇒ the LITERAL default tmux socket (no -L). The roster's -# socket_name is honored when set; absent never silently becomes mosaic-factory +# socket_name is honored when set; absent never silently becomes mosaic-fleet # (spawn stays consistent with the onboarding cheat-sheet + fleet ps observe). MOSAIC_TMUX_SOCKET=${MOSAIC_TMUX_SOCKET:-} MOSAIC_AGENT_RUNTIME=${MOSAIC_AGENT_RUNTIME:-pi} diff --git a/packages/mosaic/framework/tools/tmux/README.md b/packages/mosaic/framework/tools/tmux/README.md index 943fa91..b8a20c8 100644 --- a/packages/mosaic/framework/tools/tmux/README.md +++ b/packages/mosaic/framework/tools/tmux/README.md @@ -35,7 +35,7 @@ delivers reliably to local OR remote panes. agent-send.sh -s -m "message" # Local target on a Mosaic fleet socket -agent-send.sh -L mosaic-factory -s '=coder0' -m "message" +agent-send.sh -L mosaic-fleet -s '=coder0' -m "message" # Remote target (over ssh) agent-send.sh -H user@host -s -m "message" @@ -58,9 +58,9 @@ commands do not fall back to tmux's prefix matching behavior. Durable Mosaic fleets should use a dedicated tmux socket, for example: ```bash -tmux -L mosaic-factory ls -agent-send.sh -L mosaic-factory -s '=coder0' -m "status?" -send-message.sh -L mosaic-factory -t '=coder0' -m "raw pane message" +tmux -L mosaic-fleet ls +agent-send.sh -L mosaic-fleet -s '=coder0' -m "status?" +send-message.sh -L mosaic-fleet -t '=coder0' -m "raw pane message" ``` This keeps fleet operations away from the user's default tmux server. It is the diff --git a/packages/mosaic/src/commands/fleet.spec.ts b/packages/mosaic/src/commands/fleet.spec.ts index 61f43f2..ac57919 100644 --- a/packages/mosaic/src/commands/fleet.spec.ts +++ b/packages/mosaic/src/commands/fleet.spec.ts @@ -132,14 +132,14 @@ describe('fleet roster parsing', () => { const roster = await loadFleetRoster(rosterPath); - expect(roster.tmux.socketName).toBe(''); // absent ⇒ default socket (no -L), not mosaic-factory + expect(roster.tmux.socketName).toBe(''); // absent ⇒ default socket (no -L), not mosaic-fleet expect(roster.tmux.holderSession).toBe('_holder'); expect(roster.agents).toHaveLength(1); expect(getRosterAgent(roster, 'canary-pi').runtime).toBe('pi'); }); it('socketArgs: named socket → -L ; empty → no -L (default socket)', () => { - expect(socketArgs('mosaic-factory')).toEqual(['-L', 'mosaic-factory']); + expect(socketArgs('mosaic-fleet')).toEqual(['-L', 'mosaic-fleet']); expect(socketArgs('')).toEqual([]); }); @@ -152,14 +152,14 @@ describe('fleet roster parsing', () => { 'version: 1', 'transport: tmux', 'tmux:', - ' socket_name: mosaic-factory', + ' socket_name: mosaic-fleet', 'agents:', ' - name: canary-pi', ' runtime: pi', ].join('\n'), ); const roster = await loadFleetRoster(rosterPath); - expect(roster.tmux.socketName).toBe('mosaic-factory'); + expect(roster.tmux.socketName).toBe('mosaic-fleet'); expect(buildTmuxListSessionsCommand(roster.tmux.socketName)).toContain('-L'); }); @@ -189,7 +189,7 @@ describe('fleet roster parsing', () => { JSON.stringify({ version: 1, transport: 'tmux', - tmux: { socket_name: 'mosaic-factory' }, + tmux: { socket_name: 'mosaic-fleet' }, defaults: { working_directory: '/srv/mosaic' }, agents: [{ name: 'coder0', runtime: 'codex', class: 'implementer' }], }), @@ -202,7 +202,7 @@ describe('fleet roster parsing', () => { 'MOSAIC_AGENT_RUNTIME=codex', 'MOSAIC_AGENT_MODEL=', 'MOSAIC_AGENT_WORKDIR=/srv/mosaic', - 'MOSAIC_TMUX_SOCKET=mosaic-factory', + 'MOSAIC_TMUX_SOCKET=mosaic-fleet', '', ].join('\n'), ); @@ -213,7 +213,7 @@ describe('fleet roster parsing', () => { 'MOSAIC_AGENT_NAME=coder0', 'MOSAIC_AGENT_RUNTIME=codex', 'MOSAIC_AGENT_WORKDIR=/srv/new', - 'MOSAIC_TMUX_SOCKET=mosaic-factory', + 'MOSAIC_TMUX_SOCKET=mosaic-fleet', '', ].join('\n'); const existing = [ @@ -231,7 +231,7 @@ describe('fleet roster parsing', () => { 'MOSAIC_AGENT_NAME=coder0', 'MOSAIC_AGENT_RUNTIME=codex', 'MOSAIC_AGENT_WORKDIR=/srv/new', - 'MOSAIC_TMUX_SOCKET=mosaic-factory', + 'MOSAIC_TMUX_SOCKET=mosaic-fleet', 'MOSAIC_AGENT_COMMAND=/home/jarvis/.config/mosaic/fleet/canary.sh', '# site note', '', @@ -324,7 +324,7 @@ describe('fleet roster parsing', () => { const localCanary = await loadFleetRoster(join(examplesDir, 'local-canary.yaml')); expect(minimal.agents.map((agent) => agent.name)).toEqual(['canary-pi']); - expect(localCanary.tmux.socketName).toBe('mosaic-factory'); + expect(localCanary.tmux.socketName).toBe('mosaic-fleet'); expect(localCanary.agents.map((agent) => agent.name)).toEqual(['lead', 'coder0', 'reviewer0']); expect(localCanaryText).not.toMatch(/usc|ultron|secrev/i); }); @@ -349,11 +349,11 @@ describe('fleet command construction', () => { it('builds socket-scoped agent send commands', () => { const paths = resolveFleetPaths('/home/test/.config/mosaic'); expect( - buildAgentSendCommand(paths, 'coder0', 'hello', 'mosaic-factory', 'operator:mosaic-cli'), + buildAgentSendCommand(paths, 'coder0', 'hello', 'mosaic-fleet', 'operator:mosaic-cli'), ).toEqual([ '/home/test/.config/mosaic/tools/tmux/agent-send.sh', '-L', - 'mosaic-factory', + 'mosaic-fleet', '-S', 'operator:mosaic-cli', '-s', @@ -841,10 +841,10 @@ describe('fleet ps — command construction', () => { }); it('builds exact tmux list-panes command with the correct format string', () => { - expect(buildTmuxListPanesCommand('canary-pi', 'mosaic-factory')).toEqual([ + expect(buildTmuxListPanesCommand('canary-pi', 'mosaic-fleet')).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'list-panes', '-t', '=canary-pi:0.0', @@ -1167,7 +1167,7 @@ describe('fleet install — auto-enable units for boot-survival', () => { const minimalRoster: FleetRoster = { version: 1, transport: 'tmux', - tmux: { socketName: 'mosaic-factory', holderSession: '_holder' }, + tmux: { socketName: 'mosaic-fleet', holderSession: '_holder' }, defaults: { workingDirectory: '~/src' }, runtimes: { codex: { resetCommand: '/clear' } }, agents: [{ name: 'coder0', runtime: 'codex', className: 'worker' }], @@ -1189,7 +1189,7 @@ describe('fleet install — auto-enable units for boot-survival', () => { const minimalRoster: FleetRoster = { version: 1, transport: 'tmux', - tmux: { socketName: 'mosaic-factory', holderSession: '_holder' }, + tmux: { socketName: 'mosaic-fleet', holderSession: '_holder' }, defaults: { workingDirectory: '~/src' }, runtimes: { codex: { resetCommand: '/clear' } }, agents: [{ name: 'coder0', runtime: 'codex', className: 'worker' }], @@ -1216,7 +1216,7 @@ describe('fleet install — auto-enable units for boot-survival', () => { const minimalRoster: FleetRoster = { version: 1, transport: 'tmux', - tmux: { socketName: 'mosaic-factory', holderSession: '_holder' }, + tmux: { socketName: 'mosaic-fleet', holderSession: '_holder' }, defaults: { workingDirectory: '~/src' }, runtimes: { codex: { resetCommand: '/clear' } }, agents: [{ name: 'coder0', runtime: 'codex', className: 'worker' }], @@ -1388,10 +1388,10 @@ describe('fleet ps — command sequences issued', () => { describe('buildTmuxListSessionsCommand', () => { it('builds exact list-sessions command with session_name format', () => { - expect(buildTmuxListSessionsCommand('mosaic-factory')).toEqual([ + expect(buildTmuxListSessionsCommand('mosaic-fleet')).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'list-sessions', '-F', '#{session_name}', @@ -1642,11 +1642,11 @@ describe('fleet ps — unmanaged socket sessions', () => { describe('agent watch', () => { it('builds exact grouped-viewer creation command', () => { expect( - buildAgentWatchCreateViewerCommand('canary-pi', 'canary-pi-watch-123', 'mosaic-factory'), + buildAgentWatchCreateViewerCommand('canary-pi', 'canary-pi-watch-123', 'mosaic-fleet'), ).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'new-session', '-d', '-t', @@ -1657,10 +1657,10 @@ describe('agent watch', () => { }); it('builds exact viewer attach command (read-only)', () => { - expect(buildAgentWatchAttachCommand('canary-pi-watch-123', 'mosaic-factory')).toEqual([ + expect(buildAgentWatchAttachCommand('canary-pi-watch-123', 'mosaic-fleet')).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'attach', '-r', '-t', @@ -1669,10 +1669,10 @@ describe('agent watch', () => { }); it('builds exact viewer kill command', () => { - expect(buildAgentWatchKillViewerCommand('canary-pi-watch-123', 'mosaic-factory')).toEqual([ + expect(buildAgentWatchKillViewerCommand('canary-pi-watch-123', 'mosaic-fleet')).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'kill-session', '-t', 'canary-pi-watch-123', @@ -1769,10 +1769,10 @@ describe('agent watch', () => { describe('agent send --verify', () => { it('builds exact verify capture-pane command', () => { - expect(buildAgentVerifyAcceptedCommand('canary-pi', 'mosaic-factory', 5)).toEqual([ + expect(buildAgentVerifyAcceptedCommand('canary-pi', 'mosaic-fleet', 5)).toEqual([ 'tmux', '-L', - 'mosaic-factory', + 'mosaic-fleet', 'capture-pane', '-t', '=canary-pi:0.0', @@ -2484,7 +2484,7 @@ describe('fleet add/remove — pure helpers', () => { const baseRoster: FleetRoster = { version: 1, transport: 'tmux', - tmux: { socketName: 'mosaic-factory', holderSession: '_holder' }, + tmux: { socketName: 'mosaic-fleet', holderSession: '_holder' }, defaults: { workingDirectory: '~/src' }, runtimes: { codex: { resetCommand: '/clear' } }, agents: [ @@ -2610,7 +2610,7 @@ describe('fleet add/remove — pure helpers', () => { await writeFile(rosterPath, yaml); const loaded = await loadFleetRoster(rosterPath); expect(loaded.agents.map((a) => a.name)).toEqual(['orchestrator', 'coder0']); - expect(loaded.tmux.socketName).toBe('mosaic-factory'); + expect(loaded.tmux.socketName).toBe('mosaic-fleet'); expect(loaded.agents[0]!.className).toBe('orchestrator'); } finally { await rm(dir, { recursive: true, force: true }); diff --git a/packages/mosaic/src/commands/fleet.ts b/packages/mosaic/src/commands/fleet.ts index cca0467..96e5621 100644 --- a/packages/mosaic/src/commands/fleet.ts +++ b/packages/mosaic/src/commands/fleet.ts @@ -122,7 +122,7 @@ type FleetServiceAction = 'start' | 'stop' | 'restart' | 'status'; * rosters/callers that explicitly want isolation; it is NO LONGER the silent * fallback for a socket-less roster (that now resolves to the default socket). */ -export const DEFAULT_SOCKET_NAME = 'mosaic-factory'; +export const DEFAULT_SOCKET_NAME = 'mosaic-fleet'; const DEFAULT_HOLDER_SESSION = '_holder'; const DEFAULT_WORKING_DIRECTORY = '~/src'; @@ -130,7 +130,7 @@ const DEFAULT_WORKING_DIRECTORY = '~/src'; * tmux `-L` args for a socket name. An empty/absent socket ⇒ the LITERAL default * tmux socket (no `-L`), so spawn, observe (`fleet ps`/watch), and the onboarding * cheat-sheet all agree. A named socket ⇒ `-L `. `DEFAULT_SOCKET_NAME` - * remains a constant for callers that explicitly want mosaic-factory; it is no + * remains a constant for callers that explicitly want mosaic-fleet; it is no * longer the silent fallback for a socket-less roster. */ export function socketArgs(socketName: string): string[] { @@ -1689,7 +1689,7 @@ function normalizeRoster(raw: RawFleetRoster): FleetRoster { transport: 'tmux', tmux: { // Absent socket_name ⇒ '' (the literal default tmux socket, no -L) — NOT - // mosaic-factory. Shipped presets set socket_name explicitly, so they are + // mosaic-fleet. Shipped presets set socket_name explicitly, so they are // unaffected; only socket-less rosters get default-socket behavior. socketName: stringValue( raw.tmux?.socket_name ?? raw.tmux?.socketName, diff --git a/packages/mosaic/src/fleet/comms-onboarding.spec.ts b/packages/mosaic/src/fleet/comms-onboarding.spec.ts index 40539f5..3ea6ba5 100644 --- a/packages/mosaic/src/fleet/comms-onboarding.spec.ts +++ b/packages/mosaic/src/fleet/comms-onboarding.spec.ts @@ -48,9 +48,9 @@ describe('parseRosterAgents', () => { it('parses an optional per-agent socket', () => { const peers = parseRosterAgents( - ['agents:', ' - name: a', ' class: worker', ' socket: mosaic-factory'].join('\n'), + ['agents:', ' - name: a', ' class: worker', ' socket: mosaic-fleet'].join('\n'), ); - expect(peers[0]).toMatchObject({ name: 'a', socket: 'mosaic-factory' }); + expect(peers[0]).toMatchObject({ name: 'a', socket: 'mosaic-fleet' }); }); it('stops at the next top-level key', () => { @@ -99,9 +99,9 @@ describe('renderPeerReach — same-host vs cross-host', () => { }); it('emits -L for a named socket', () => { - const peer: CommsPeer = { name: 'coder0', className: 'implementer', socket: 'mosaic-factory' }; + const peer: CommsPeer = { name: 'coder0', className: 'implementer', socket: 'mosaic-fleet' }; expect(renderPeerReach(peer, 'w-jarvis', send)).toBe( - `${send} -L mosaic-factory -s coder0 -m "…"`, + `${send} -L mosaic-fleet -s coder0 -m "…"`, ); }); @@ -111,10 +111,10 @@ describe('renderPeerReach — same-host vs cross-host', () => { className: 'implementer', host: '10.1.10.37', ssh: 'jwoltje@10.1.10.37', - socket: 'mosaic-factory', + socket: 'mosaic-fleet', }; expect(renderPeerReach(peer, 'w-jarvis', send)).toBe( - `${send} -L mosaic-factory -H jwoltje@10.1.10.37 -s coder0-0 -m "…"`, + `${send} -L mosaic-fleet -H jwoltje@10.1.10.37 -s coder0-0 -m "…"`, ); }); });