chore: consolidate new foundation and archive v1 (#1495)

This commit is contained in:
2026-09-07 12:32:57 -05:00
3511 changed files with 727899 additions and 10 deletions
@@ -0,0 +1,80 @@
# Mosaic tmux Fleet PoC
This directory contains the first durable tmux-backed fleet primitives for the
Mosaic software-factory model.
The lifecycle model follows the organization-neutral AI Guide playbook
`mosaicstack/aiguide:playbooks/tmux-fleet.md` (commit `2a0b0b5`): a dedicated
holder owns the tmux server/socket; agent units join it and stop only their own
exact-match session.
## Layout
- `mosaic-tmux-holder.service` — user-mode holder that owns the named tmux server.
- `[email protected]` — user-mode template for one reusable agent session.
- `[email protected]` — generic Pi operator-interaction template
that fails fast when its pinned runtime policy is incomplete or changed.
- `test-fleet-units.sh` — validates unit syntax and required relationships.
The agent template calls:
```text
~/.config/mosaic/tools/fleet/start-agent-session.sh <agent-name>
```
which starts or reuses a tmux session on `MOSAIC_TMUX_SOCKET`.
## Generated environment and local data
The roster-derived projection is written outside the package at:
```text
~/.config/mosaic/fleet/agents/<agent>.env.generated
```
Systemd does not read either environment file. It starts the launcher with a fixed cleared bootstrap
environment; before it creates, queries, or stops an exact agent tmux session, `start-agent-session.sh`
strictly parses the generated projection and the optional local data file:
```text
~/.config/mosaic/fleet/agents/<agent>.env.local
```
The local file may contain only safe machine-specific data (`MOSAIC_RUNTIME_BIN`, heartbeat paths or
interval, and Claude configuration paths). It cannot override roster-derived keys, carry a command,
or contain secret-like/unknown keys. Both files must be private regular files. Do not hand-edit the
generated projection; update the roster and regenerate it instead. A legacy `<agent>.env` is
consumed only for regeneration, strict relocation, or private quarantine and is never launch input.
See `docs/fleet/reference/generated-env-boundary.md` for the full contract.
## Manual canary sequence
Use the roster and the supported installer; do not pre-create the agent environment directory or
edit a generated projection. `mosaic fleet install` validates the roster, installs the units and
helpers, and writes private roster-derived projections before any service is started.
```bash
# Create a site-owned canary roster. Inspect an existing roster before using --force.
mosaic fleet init --profile minimal --write
mosaic fleet install
systemctl --user daemon-reload
mosaic fleet start canary-pi
tmux -L mosaic-fleet ls
```
For an operator-interaction service, first put `<agent-name>` in the roster with the pinned Pi
runtime, model, reasoning, and `operator-interaction` tool policy. Re-run `mosaic fleet install` after
that roster change so it writes `<agent-name>.env.generated`; ambient `MOSAIC_AGENT_*` values are not
launch authority. The generic unit instance uses that generated identity, and no service source is
renamed for an instance:
```bash
mosaic fleet install
systemctl --user daemon-reload
systemctl --user start mosaic-interaction-agent@<agent-name>.service
~/.config/mosaic/tools/fleet/print-interaction-effective-policy.sh <agent-name>
```
Do not use `tmux kill-server` without `-L mosaic-fleet`; this pattern is meant
to avoid disturbing the user's default tmux server.
@@ -0,0 +1,37 @@
[Unit]
Description=Mosaic tmux fleet agent %i
Documentation=https://git.mosaicstack.dev/mosaicstack/stack
Requires=mosaic-tmux-holder.service
After=mosaic-tmux-holder.service
PartOf=mosaic-tmux-holder.service
# Do not attempt a seat before its generated env exists. `install` enables this
# unit (WantedBy=default.target) but on a roster-v2 fleet the reconciler owns the
# generated env, so between `install` and the first `apply`/`regen --write` there
# is a boot window where ExecStart would run against an absent env file and the
# launcher would fail the unit. A skipped unit is the honest state for "enabled
# but not yet configured"; systemd re-evaluates the condition on every start, so
# the seat comes up on the next start once the reconciler has written env.
#
# #1408: the reconciler writes projections into the BRAIN home when one is
# active (~/.mosaic/fleet/agents, mirroring start-agent-session.sh's brain-home
# resolution), and into MOSAIC_HOME on a legacy single-tree host. A single
# config-home condition therefore skipped every seat on brain-home estates —
# measured on two estates: 27 projections vs 0, and 5 vs 0, gate never fired.
# Two TRIGGERING conditions (the `|` prefix ORs same-type conditions, which
# otherwise AND): either shape arms the unit; the launcher still resolves the
# authoritative copy itself.
ConditionPathExists=|%h/.config/mosaic/fleet/agents/%i.env.generated
ConditionPathExists=|%h/.mosaic/fleet/agents/%i.env.generated
[Service]
Type=oneshot
# Remove loader and noninteractive-shell controls before ExecStart loads env.
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
RemainAfterExit=yes
# Never preload the projection. The launcher starts from a fixed minimal
# environment and strictly validates generated/local data before tmux effects.
ExecStart=/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-agent-session.sh %i
ExecStop=-/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-agent-session.sh --stop %i
[Install]
WantedBy=default.target
@@ -0,0 +1,19 @@
[Unit]
Description=Mosaic operator interaction agent %i
Documentation=https://git.mosaicstack.dev/mosaicstack/stack
Requires=mosaic-tmux-holder.service
After=mosaic-tmux-holder.service
PartOf=mosaic-tmux-holder.service
[Service]
Type=oneshot
# Remove loader and noninteractive-shell controls before ExecStart loads env.
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
RemainAfterExit=yes
# The interaction wrapper delegates to the shared strict parser before pinned
# profile checks; no projection data reaches Bash through systemd.
ExecStart=/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-interaction-service.sh %i
ExecStop=-/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-agent-session.sh --stop %i
[Install]
WantedBy=default.target
@@ -0,0 +1,22 @@
[Unit]
Description=Mosaic lease broker daemon (framework tools/lease-broker/daemon.py)
Documentation=https://git.mosaicstack.dev/mosaicstack/stack
After=default.target
[Service]
Type=simple
# The broker socket lives under the runtime directory so it disappears with
# the user session instead of surviving as stale state across logins.
# daemon.py's secure_parent() fails closed unless this directory is exactly
# 0700, so RuntimeDirectoryMode is not cosmetic.
RuntimeDirectory=mosaic-lease
RuntimeDirectoryMode=0700
# Remove loader and noninteractive-shell controls before ExecStart loads env,
# matching the tmux fleet units in this same directory.
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
ExecStart=/usr/bin/env -i HOME=%h PATH=/usr/bin:/bin XDG_RUNTIME_DIR=%t /bin/bash --noprofile --norc %h/.config/mosaic/tools/lease-broker/start-lease-broker.sh
Restart=on-failure
RestartSec=1
[Install]
WantedBy=default.target
@@ -0,0 +1,16 @@
[Unit]
Description=Mosaic tmux fleet holder
Documentation=https://git.mosaicstack.dev/mosaicstack/stack
After=default.target
[Service]
Type=oneshot
RemainAfterExit=yes
# The holder owns the tmux server, so clear loader, shell-control, and stale
# manager/session variables before the server process starts.
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
ExecStart=/usr/bin/env -i HOME=%h PATH=/usr/bin:/bin MOSAIC_TMUX_SOCKET=mosaic-fleet MOSAIC_TMUX_HOLDER=_holder /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-tmux-holder.sh
ExecStop=-/usr/bin/env -i HOME=%h PATH=/usr/bin:/bin MOSAIC_TMUX_SOCKET=mosaic-fleet /bin/bash --noprofile --norc -c 'tmux -L "$MOSAIC_TMUX_SOCKET" kill-server'
[Install]
WantedBy=default.target
@@ -0,0 +1,41 @@
[Unit]
# Mosaic wake FALLBACK safety drain (F7 replacement-before-retirement, EPIC #892,
# W7). This is the framework-shipped canon-side FALLBACK WAKE: a LOW-FREQUENCY
# SAFETY drain that fires the canon drain (digest.sh render --from-store) on a
# per-class cadence bound, INDEPENDENT of the event-driven detector daemon
# (mosaic-wake.service). Its whole reason to exist is that a stalled/dead detector
# or daemon can never SILENTLY STARVE delivery: even with nothing pushing, this
# oneshot periodically drains the durable pending-inbox so the cumulative unacked
# set still reaches the consumer. It is the §5 retirement precondition (F7) — it
# must be live + proven-firing BEFORE the legacy mosaic-heartbeat@ timer is reaped,
# so there is never a coverage gap. Fixed-interval heartbeats are forbidden as the
# PRIMARY wake mechanism (WAKE-DOCTRINE); they survive ONLY as this per-class
# fallback cadence, bounded by urgency SLO, never as the steady state.
#
# This is a oneshot SERVICE activated by mosaic-wake-fallback.timer; the cadence
# lives on the TIMER (OnUnitActiveSec), set per-class by the A10 installer via the
# blank-reset drop-in — never here. The service therefore carries NO [Install]
# section (the TIMER is what is enabled/wanted); it is triggered, not wanted.
Description=Mosaic wake fallback safety drain (canon drain: digest.sh render --from-store)
After=default.target
[Service]
Type=oneshot
# Strip loader / noninteractive-shell controls before ExecStart, matching the
# detector, lease-broker and tmux fleet units in this same directory (defense-in-
# depth against an injected BASH_ENV/ENV/LD_PRELOAD in the user manager environment).
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
# Operator-owned runtime configuration. This EnvironmentFile carries only WAKE_*
# NAMES (the per-agent namespace WAKE_AGENT, the lane WAKE_LANE, and — reused from
# the detector — the pluggable adapter COMMANDS resolved BY NAME at runtime). It
# carries NEVER any secret and NEVER any endpoint value. The '-' prefix keeps a
# missing file from masking the installer's dedicated fail-closed install-validate.
EnvironmentFile=-%h/.config/mosaic/wake/fallback.env
# THE CANON DRAIN. digest.sh render --from-store drains the durable pending-inbox
# (store.sh drain) and renders the cumulative-state digest. Running it here, on the
# timer cadence, is the safety net: it is the SAME drain the delivery path uses, so
# a stalled detector cannot starve it. Delivery/paste of the rendered digest is the
# same operator-wired send seam the detector path uses (out of framework scope);
# this unit guarantees the DRAIN fires on a bounded cadence regardless of detector
# health. digest render exits 0 on an empty inbox, so a quiet cycle is a clean no-op.
ExecStart=/bin/bash --noprofile --norc %h/.config/mosaic/tools/wake/digest.sh render --from-store
@@ -0,0 +1,30 @@
[Unit]
# Cadence timer for the Mosaic wake FALLBACK safety drain (F7, EPIC #892, W7).
# Drives mosaic-wake-fallback.service on a LOW-FREQUENCY per-class cadence bound,
# INDEPENDENT of the event-driven detector daemon, so a stalled detector can never
# silently starve delivery. This is the framework-shipped canon-side FALLBACK WAKE:
# a heartbeat-shaped timer that survives ONLY as the per-class fallback cadence
# (WAKE-DOCTRINE) bounded by urgency SLO — never the steady-state wake mechanism.
Description=Mosaic wake fallback cadence timer (per-class safety wake)
After=default.target
[Timer]
# BASE cadence placeholder. The A10 installer OVERRIDES this per-class from the
# watch-list schema's per-class `fallback_cadence` bound, via a BLANK-RESET drop-in
# (an empty OnUnitActiveSec= reset line, then the new value) written under
# mosaic-wake-fallback.timer.d/. systemd merges base + drop-ins so exactly ONE
# effective OnUnitActiveUSec results (wake-install.sh verify-single). The base value
# here is a conservative safety floor for a host installed before any per-class
# drop-in is written — it is deliberately low-frequency (never the primary wake).
OnUnitActiveSec=1h
# Also fire shortly after boot so a freshly-booted host does not wait a full cadence
# for its first safety drain. OnBootSec is a distinct key from OnUnitActiveSec and
# does NOT count toward the exactly-one-OnUnitActiveUSec blank-reset invariant.
OnBootSec=15min
# Catch up a missed elapse (host asleep/off) rather than silently skipping it — a
# fallback that silently skips is exactly the starvation this unit exists to prevent.
Persistent=true
Unit=mosaic-wake-fallback.service
[Install]
WantedBy=timers.target
@@ -0,0 +1,31 @@
[Unit]
# Mosaic wake DETECTOR daemon (A1/W7 of the wake canon, EPIC #892). A LONG-LIVED
# single-instance detector: tools/wake/detector.sh run. This is a SERVICE, not a
# timer — the per-class SLO lives INSIDE the daemon's run-loop (WAKE_DETECTOR_INTERVAL
# poll cadence + the per-cycle off-host beacon emit), never as a systemd
# OnUnitActiveSec interval. The blank-reset cadence idiom therefore does NOT apply
# to this unit; it applies only to the legacy mosaic-heartbeat@ timer during retire.
Description=Mosaic wake detector daemon (framework tools/wake/detector.sh run)
After=default.target
[Service]
Type=simple
# Strip loader / noninteractive-shell controls before ExecStart, matching the
# lease-broker and tmux fleet units in this same directory (defense-in-depth
# against an injected BASH_ENV/ENV/LD_PRELOAD in the user manager environment).
UnsetEnvironment=LD_PRELOAD BASH_ENV ENV
# Operator-owned runtime configuration. This EnvironmentFile carries only the
# WAKE_* NAMES and the pluggable adapter COMMANDS (the off-host beacon/alarm sink
# and the HMAC key NAME) — NEVER the HMAC key material and NEVER the alarm
# endpoint value. Both are resolved BY NAME at runtime via load_credentials, so
# no secret and no endpoint is ever written into this unit. The installer's
# fail-closed install-validate (wake-install.sh validate-targets) is what proves
# the required names are configured + reachable BEFORE this unit is enabled; the
# '-' prefix keeps a missing file from masking that dedicated validation.
EnvironmentFile=-%h/.config/mosaic/wake/detector.env
ExecStart=/bin/bash --noprofile --norc %h/.config/mosaic/tools/wake/detector.sh run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
@@ -0,0 +1,193 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd)
HOLDER="$SCRIPT_DIR/mosaic-tmux-holder.service"
AGENT="$SCRIPT_DIR/[email protected]"
INTERACTION="$SCRIPT_DIR/[email protected]"
HOLDER_START="$SCRIPT_DIR/../../tools/fleet/start-tmux-holder.sh"
START_AGENT="$SCRIPT_DIR/../../tools/fleet/start-agent-session.sh"
fail() {
echo "FAIL: $*" >&2
exit 1
}
[ -f "$HOLDER" ] || fail "missing mosaic-tmux-holder.service"
[ -f "$AGENT" ] || fail "missing [email protected]"
[ -f "$INTERACTION" ] || fail "missing [email protected]"
[ -x "$HOLDER_START" ] || fail "missing executable start-tmux-holder.sh"
[ -x "$START_AGENT" ] || fail "missing executable start-agent-session.sh"
grep -qF 'ExecStart=' "$HOLDER" || fail "holder has no ExecStart"
grep -qF 'tmux -L' "$HOLDER" || fail "holder does not use named tmux socket"
grep -qF '_holder' "$HOLDER" || fail "holder session is not explicit"
grep -qF 'UnsetEnvironment=LD_PRELOAD BASH_ENV ENV' "$HOLDER" || \
fail "holder does not remove loader and shell-control variables"
grep -qF 'ExecStart=/usr/bin/env -i HOME=%h PATH=/usr/bin:/bin MOSAIC_TMUX_SOCKET=mosaic-fleet MOSAIC_TMUX_HOLDER=_holder /bin/bash --noprofile --norc %h/.config/mosaic/tools/fleet/start-tmux-holder.sh' "$HOLDER" || \
fail "holder does not clear manager environment before starting tmux"
grep -qF 'ExecStop=-/usr/bin/env -i HOME=%h PATH=/usr/bin:/bin MOSAIC_TMUX_SOCKET=mosaic-fleet /bin/bash --noprofile --norc -c' "$HOLDER" || \
fail "holder stop does not clear manager environment"
if grep -qF -- '/bin/bash -lc' "$HOLDER"; then
fail "holder must not start tmux through a login shell"
fi
grep -qF 'Requires=mosaic-tmux-holder.service' "$AGENT" || fail "agent does not require holder"
# #1408: the projection condition must arm on EITHER home shape. Both lines must
# carry the `|` triggering prefix — same-type conditions without it AND together,
# which can never be true (one file cannot exist at two paths), so a bare-spelling
# regression would disable autostart everywhere while reading as "has a condition".
grep -qF 'ConditionPathExists=|%h/.config/mosaic/fleet/agents/%i.env.generated' "$AGENT" || \
fail "agent lacks triggering condition for the config home projection"
grep -qF 'ConditionPathExists=|%h/.mosaic/fleet/agents/%i.env.generated' "$AGENT" || \
fail "agent lacks triggering condition for the brain home projection (#1408)"
if grep -qE '^ConditionPathExists=[^|]' "$AGENT"; then
fail "agent has a non-triggering ConditionPathExists — same-type conditions AND, re-arming #1408"
fi
grep -qF 'start-agent-session.sh' "$AGENT" || fail "agent unit does not call start-agent-session.sh"
if grep -qE '^Environment(File)?=' "$AGENT" "$INTERACTION"; then
fail "agent units must not accept ambient or projection environment before strict parsing"
fi
grep -qF 'UnsetEnvironment=LD_PRELOAD BASH_ENV ENV' "$AGENT" || \
fail "agent unit does not remove loader and shell-control variables"
grep -qF 'UnsetEnvironment=LD_PRELOAD BASH_ENV ENV' "$INTERACTION" || \
fail "interaction unit does not remove loader and shell-control variables"
grep -qF 'ExecStart=/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc' "$AGENT" || \
fail "agent unit does not clear bootstrap environment before strict parsing"
grep -qF 'start-agent-session.sh --stop %i' "$AGENT" || \
fail "agent stop does not use the validated exact-stop path"
grep -qF 'Requires=mosaic-tmux-holder.service' "$INTERACTION" || fail "interaction service does not require holder"
grep -qF 'ExecStart=/usr/bin/env -i HOME=%h MOSAIC_AGENT_NAME=%i PATH=/usr/bin:/bin /bin/bash --noprofile --norc' "$INTERACTION" || \
fail "interaction unit does not clear bootstrap environment before strict parsing"
grep -qF 'start-interaction-service.sh %i' "$INTERACTION" || fail "interaction service does not use shared strict parsing"
grep -qF 'start-agent-session.sh --stop %i' "$INTERACTION" || \
fail "interaction stop does not use the validated exact-stop path"
if command -v systemd-analyze >/dev/null 2>&1; then
systemd-analyze verify --user "$HOLDER" "$AGENT" "$INTERACTION" >/tmp/mosaic-fleet-systemd-verify.log 2>&1 || {
cat /tmp/mosaic-fleet-systemd-verify.log >&2
fail "systemd-analyze verify failed"
}
fi
# Real isolated socket regression: a preexisting server with an LD_PRELOAD
# constructor marker must fail closed, while a fresh named server is created.
if command -v tmux >/dev/null 2>&1 && command -v cc >/dev/null 2>&1; then
TEST_ROOT=$(mktemp -d)
TEST_SOCKET="mosaic-holder-test-$$"
trap 'tmux -L "$TEST_SOCKET" kill-server >/dev/null 2>&1 || true; rm -rf "$TEST_ROOT"' EXIT
MARKER="$TEST_ROOT/loader-marker"
LIBRARY="$TEST_ROOT/marker.so"
FIXTURE_READY="$TEST_ROOT/loader-ready"
FIXTURE_FIFO="$TEST_ROOT/loader-block"
HOLDER_HOME="$TEST_ROOT/holder-home"
mkfifo "$FIXTURE_FIFO"
mkdir -p "$HOLDER_HOME/.config/mosaic/fleet/run"
chmod 700 "$HOLDER_HOME/.config" "$HOLDER_HOME/.config/mosaic" \
"$HOLDER_HOME/.config/mosaic/fleet" "$HOLDER_HOME/.config/mosaic/fleet/run"
printf '123e4567-e89b-12d3-a456-426614174000\n' > \
"$HOLDER_HOME/.config/mosaic/fleet/run/holder-owner"
chmod 600 "$HOLDER_HOME/.config/mosaic/fleet/run/holder-owner"
cat > "$TEST_ROOT/marker.c" <<'EOF'
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) static void mark_loader(void) {
const char *path = getenv("MOSAIC_LOADER_MARKER");
if (path != NULL) {
int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0600);
if (fd >= 0) { write(fd, "loaded\\n", 7); close(fd); }
}
}
EOF
cc -shared -fPIC -o "$LIBRARY" "$TEST_ROOT/marker.c"
MOSAIC_LOADER_MARKER="$MARKER" LD_PRELOAD="$LIBRARY" \
tmux -L "$TEST_SOCKET" new-session -d -s _holder \
"touch '$FIXTURE_READY'; read _ < '$FIXTURE_FIFO'"
# tmux starts the pane asynchronously. Wait until its contaminated shell has
# loaded the constructor and reached a builtin-only FIFO barrier before
# clearing the marker; otherwise that expected constructor can race with the
# clean holder assertion below and create a false failure.
for _attempt in {1..100}; do
[ -e "$FIXTURE_READY" ] && break
sleep 0.01
done
[ -e "$FIXTURE_READY" ] || fail "contaminated fixture pane did not become ready"
[ -s "$MARKER" ] || fail "contaminated fixture did not execute loader constructor"
server_pid=$(tmux -L "$TEST_SOCKET" display-message -p '#{pid}')
: > "$MARKER"
if /usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin \
MOSAIC_TMUX_SOCKET="$TEST_SOCKET" MOSAIC_TMUX_HOLDER=_holder "$HOLDER_START" \
>"$TEST_ROOT/holder.out" 2>&1; then
fail "holder adopted contaminated named server"
fi
grep -qF 'global environment does not match the owned-server contract' "$TEST_ROOT/holder.out" || \
fail "holder did not report contaminated server environment"
[ "$(tmux -L "$TEST_SOCKET" display-message -p '#{pid}')" = "$server_pid" ] || \
fail "holder replaced a contaminated server instead of failing closed"
[ ! -s "$MARKER" ] || fail "holder execution triggered a contaminated loader"
# Agent validation must reject the same unmanaged server without cleaning its
# global environment or adding a managed session.
AGENT_HOME="$HOLDER_HOME/.config/mosaic"
AGENT_NAME=loader-safe
AGENT_WORKDIR="$AGENT_HOME/work"
AGENT_BIN="$TEST_ROOT/agent-bin"
mkdir -p "$AGENT_HOME/fleet/agents" "$AGENT_WORKDIR" "$AGENT_BIN"
chmod 700 "$AGENT_HOME/fleet/agents"
cat > "$AGENT_HOME/fleet/agents/$AGENT_NAME.env.generated" <<EOF
MOSAIC_AGENT_NAME=$AGENT_NAME
MOSAIC_GIT_IDENTITY=$AGENT_NAME
MOSAIC_AGENT_CLASS=code
MOSAIC_AGENT_RUNTIME=pi
MOSAIC_AGENT_MODEL=
MOSAIC_AGENT_REASONING=
MOSAIC_AGENT_TOOL_POLICY=code
MOSAIC_AGENT_WORKDIR=$AGENT_WORKDIR
MOSAIC_TMUX_SOCKET=$TEST_SOCKET
EOF
printf 'MOSAIC_RUNTIME_BIN=%s\n' "$AGENT_BIN" > "$AGENT_HOME/fleet/agents/$AGENT_NAME.env.local"
chmod 600 "$AGENT_HOME/fleet/agents/$AGENT_NAME.env.generated" \
"$AGENT_HOME/fleet/agents/$AGENT_NAME.env.local"
cat > "$AGENT_BIN/mosaic" <<'EOF'
#!/bin/sh
sleep 30
EOF
chmod 700 "$AGENT_BIN/mosaic"
# The launcher resolves the roster's runtime against PANE_PATH before it
# spawns anything (#1241), so the runtime this projection names has to be
# present here even though the fake `mosaic` above never execs it.
cat > "$AGENT_BIN/pi" <<'EOF'
#!/bin/sh
sleep 30
EOF
chmod 700 "$AGENT_BIN/pi"
server_environment_before=$(tmux -L "$TEST_SOCKET" show-environment -g | sort)
server_sessions_before=$(tmux -L "$TEST_SOCKET" list-sessions | sort)
if /usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin MOSAIC_HOME="$AGENT_HOME" \
"$START_AGENT" "$AGENT_NAME" >"$TEST_ROOT/agent.out" 2>&1; then
fail "agent launcher adopted contaminated named server"
fi
[ "$(tmux -L "$TEST_SOCKET" display-message -p '#{pid}')" = "$server_pid" ] || \
fail "agent launcher changed unmanaged server PID"
[ "$(tmux -L "$TEST_SOCKET" show-environment -g | sort)" = "$server_environment_before" ] || \
fail "agent launcher changed unmanaged global environment"
[ "$(tmux -L "$TEST_SOCKET" list-sessions | sort)" = "$server_sessions_before" ] || \
fail "agent launcher changed unmanaged sessions"
tmux -L "$TEST_SOCKET" kill-server
/usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin \
MOSAIC_TMUX_SOCKET="$TEST_SOCKET" MOSAIC_TMUX_HOLDER=_holder "$HOLDER_START"
tmux -L "$TEST_SOCKET" has-session -t '=_holder:0.0' || fail "fresh holder was not created"
ld_preload_env="$(tmux -L "$TEST_SOCKET" show-environment -g LD_PRELOAD 2>/dev/null)" || true
if grep -q '^LD_PRELOAD=' <<<"$ld_preload_env"; then
fail "fresh holder retained LD_PRELOAD"
fi
/usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin MOSAIC_HOME="$AGENT_HOME" \
"$START_AGENT" "$AGENT_NAME"
tmux -L "$TEST_SOCKET" has-session -t "=$AGENT_NAME:0.0" || \
fail "agent did not launch on a valid owned server"
tmux -L "$TEST_SOCKET" kill-server
trap - EXIT
rm -rf "$TEST_ROOT"
fi
echo "ok - fleet systemd unit templates"