feat(doctor): brain-home fleet-state check (#1298 follow-up, canon §6)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
mosaic-doctor now reports which tree fleet state resolves from and warns on the drift a launch would hit at runtime: - stale MOSAIC_BRAIN_HOME (no fleet/agents under it) - symlinked brain/agents paths (managed-directory boundary) - group/world-readable agents dir (0700 projection boundary) - split state: env files in BOTH config-home and brain trees Resolution mirrors brain-home.ts (#1298): MOSAIC_BRAIN_HOME wins; canonical ~/.mosaic adopted only at the default config home; else legacy. test-brain-home-check.sh: extraction-style harness (test-fleet-transport discipline — functions pulled from the shipped script, never copied), 8 cases covering every branch; wired into test:framework-shell and covered by the enumeration guard (population 53, all present). Live-run verified on dragon-lin: caught two real drifts — agents dir 755 (fixed locally) and pre-cutover split state (expected, tracked in mosaic-brain fleet/SEAT-CUTOVER.md).
This commit is contained in:
@@ -255,6 +255,68 @@ fleet_declared_transport() {
|
||||
printf '%s\n' "${declared:-tmux}"
|
||||
}
|
||||
|
||||
# Brain-home fleet-state resolution (#1298; canon STRUCTURE-CANON §2).
|
||||
#
|
||||
# Seat launch envs, roles.local overrides, and profile working copies resolve
|
||||
# from the brain home when one is active; roster, baseline roles, run/, and
|
||||
# services stay under MOSAIC_HOME. This check surfaces which tree fleet state
|
||||
# resolves from and the drift a launch would otherwise hit at runtime:
|
||||
#
|
||||
# - a stale MOSAIC_BRAIN_HOME pointing at a directory with no fleet/agents is a
|
||||
# misconfiguration the resolver honors (explicit wins) — warn, don't pass;
|
||||
# - a symlinked brain or agents dir defeats the managed-directory boundary;
|
||||
# - a group/world-readable agents dir violates the 0700 projection boundary;
|
||||
# - env files left in the config-home tree while a brain is active are split
|
||||
# state — the write path rejects NEW split writes, but nothing would ever
|
||||
# tell the operator the old files are stranded.
|
||||
resolve_brain_home() {
|
||||
local explicit="${MOSAIC_BRAIN_HOME:-}"
|
||||
if [[ -n "$(printf '%s' "$explicit" | tr -d '[:space:]')" ]]; then
|
||||
printf '%s' "$explicit"
|
||||
return
|
||||
fi
|
||||
if [[ "$(cd "$MOSAIC_HOME" 2>/dev/null && pwd -P)" == "$HOME/.config/mosaic" \
|
||||
&& -d "$HOME/.mosaic/fleet/agents" ]]; then
|
||||
printf '%s' "$HOME/.mosaic"
|
||||
return
|
||||
fi
|
||||
printf '%s' "$MOSAIC_HOME"
|
||||
}
|
||||
|
||||
check_brain_home() {
|
||||
local brain agents mode
|
||||
brain="$(resolve_brain_home)"
|
||||
|
||||
if [[ "$brain" == "$MOSAIC_HOME" ]]; then
|
||||
pass "Fleet state home: $MOSAIC_HOME (legacy single-tree; no brain adopted)"
|
||||
return
|
||||
fi
|
||||
|
||||
agents="$brain/fleet/agents"
|
||||
if [[ ! -d "$agents" ]]; then
|
||||
warn "Brain home '$brain' has no fleet/agents — seat envs will not resolve from it. Point MOSAIC_BRAIN_HOME at a brain carrying fleet/agents, or unset it."
|
||||
return
|
||||
fi
|
||||
if [[ -L "$brain" || -L "$agents" ]]; then
|
||||
warn "Brain fleet-state path resolves through a symlink ($brain) — the managed-directory boundary requires regular directories."
|
||||
return
|
||||
fi
|
||||
|
||||
mode="$(stat -c '%a' -- "$agents" 2>/dev/null)" || mode=""
|
||||
if [[ -n "$mode" ]] && (( (8#$mode & 8#077) != 0 )); then
|
||||
warn "Brain agents dir '$agents' is group/world-accessible (mode $mode) — the projection boundary requires 0700."
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -d "$MOSAIC_HOME/fleet/agents" ]] \
|
||||
&& ls "$MOSAIC_HOME/fleet/agents/"*.env* >/dev/null 2>&1; then
|
||||
warn "Fleet env files exist in BOTH trees — brain '$brain' is active but '$MOSAIC_HOME/fleet/agents' still carries env files (split state). Migrate them (mosaic fleet regen) and remove the config-home copies."
|
||||
return
|
||||
fi
|
||||
|
||||
pass "Fleet state home: $brain (brain active); roster + templates: $MOSAIC_HOME"
|
||||
}
|
||||
|
||||
check_fleet_transport() {
|
||||
local transport
|
||||
transport="$(fleet_declared_transport)"
|
||||
@@ -273,6 +335,8 @@ check_fleet_transport() {
|
||||
|
||||
check_fleet_transport
|
||||
|
||||
check_brain_home
|
||||
|
||||
# Legacy migration surfaces should no longer contain symlink trees.
|
||||
legacy_paths=(
|
||||
"$HOME/.claude/agent-guides"
|
||||
|
||||
Reference in New Issue
Block a user