refactor(fleet): rename tmux socket mosaic-factory → mosaic-fleet (#630)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #630.
This commit is contained in:
2026-06-22 21:08:43 +00:00
committed by jason.woltje
parent 3f69d45334
commit d539d61e0e
21 changed files with 84 additions and 84 deletions

View File

@@ -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

View File

@@ -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/<agent>.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 <name>` — 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/<agent>.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 <name>` — 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)

View File

@@ -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
```

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~/src

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~/src

View File

@@ -1,7 +1,7 @@
version: 1
transport: tmux
tmux:
socket_name: mosaic-factory
socket_name: mosaic-fleet
holder_session: _holder
defaults:
working_directory: ~

View File

@@ -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",

View File

@@ -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.

View File

@@ -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'

View File

@@ -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}

View File

@@ -35,7 +35,7 @@ delivers reliably to local OR remote panes.
agent-send.sh -s <dst_session> -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 <dst_session> -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

View File

@@ -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 <name>; 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 });

View File

@@ -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 <name>`. `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,

View File

@@ -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 <socket> 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 "…"`,
);
});
});