fix(#1408): mosaic-agent@ condition arms on either home shape
ci/woodpecker/pr/ci Pipeline failed

The reconciler writes seat 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. The unit's
single config-home ConditionPathExists therefore skipped every seat on
brain-home estates — measured on two estates independently: 27 projections vs
0 at the config path, and 5 vs 0; the gate never fired on either.

Fix: two TRIGGERING conditions (`|` prefix — same-type conditions otherwise
AND, which can never be true across two paths). Either shape arms the unit;
start-agent-session.sh still resolves the authoritative copy itself.

test-fleet-units.sh now asserts both triggering lines and REFUSES a bare
(ANDing) ConditionPathExists spelling — that regression would disable
autostart fleet-wide while reading as "has a condition". Sabotage-controlled:
unit reverted, suite fails on the first new assertion; restored, green.

Closes #1408
This commit is contained in:
2026-08-24 19:28:28 -05:00
parent f45928c311
commit 6dbc61ec46
2 changed files with 22 additions and 1 deletions
@@ -11,7 +11,17 @@ PartOf=mosaic-tmux-holder.service
# 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.
ConditionPathExists=%h/.config/mosaic/fleet/agents/%i.env.generated
#
# #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
@@ -32,6 +32,17 @@ 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"