feat(release): recursion guard for the health gate; run-task drift warning; M20 packages/* decision recorded (#39)

- release.sh health gate runs with MOSAIC_ENSURE_SKIP=1: the gated task run
  cannot re-enter release self-determination
- run-task.sh warns on release drift instead of silently using a stale image
- ROADMAP: M20 decision recorded (packages/* monorepo at usurpation,
  continuity-first); restructure sequenced as M20 phase 1

Closes #39
This commit is contained in:
2026-09-03 15:58:46 -05:00
parent 9051ad179b
commit 9fd16b9739
16 changed files with 440 additions and 31 deletions
+40 -16
View File
@@ -191,22 +191,45 @@ EOF
expect_exit "retry of missing run exits 4" 4 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" node scripts/mosaic-task.mjs retry r-missing
# session fork plumbing (M11): fork source + target dir delivered
printf '{"taskVersion":1,"id":"t-forkplumb","prompt":"x","session":"fork-child","sessionForkFrom":"base"}' > "$SANDBOX/forkplumb.json"
mkdir -p "$SANDBOX/data/sessions/base"
printf '{}' > "$SANDBOX/data/sessions/base/20260903T000000-plumb.jsonl"
expect_exit "fork task runs via mock" 0 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" MOSAIC_MOCK_RESPONSE=MOCKED \
scripts/run-task.sh run "$SANDBOX/forkplumb.json"
FL="$(ls -dt "$SANDBOX/data/runs"/r-* | head -1)"
grep -q '^MOSAIC_SESSION_FORK=/var/lib/mosaic/sessions/base/20260903T000000-plumb.jsonl$' "$FL/stderr.txt" 2>/dev/null \
&& grep -q '^MOSAIC_SESSION_DIR=/var/lib/mosaic/sessions/fork-child$' "$FL/stderr.txt" 2>/dev/null \
&& check "fork source + target delivered to adapter" 0 \
|| check "fork source + target delivered to adapter" 1
printf '{"taskVersion":1,"id":"t-f2","prompt":"x","sessionForkFrom":"base"}' > "$SANDBOX/noforktarget.json"
expect_exit "fork without session target exits 2" 2 -- $TASK validate "$SANDBOX/noforktarget.json"
printf '{"taskVersion":1,"id":"t-f3","prompt":"x","session":"base","sessionForkFrom":"base"}' > "$SANDBOX/selfork.json"
expect_exit "self-fork exits 2" 2 -- $TASK validate "$SANDBOX/selfork.json"
# skills lifecycle (M17)
expect_exit "skill install bundled ms-tools" 0 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh install ms-tools
[ -d "$SANDBOX/data/skills-available/ms-tools" ] \
&& check "installed to skills-available" 0 || check "installed to skills-available" 1
expect_exit "double install refuses" 1 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh install ms-tools
expect_exit "activate enables skill" 0 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh activate ms-tools
[ -d "$SANDBOX/data/skills-enabled/ms-tools" ] \
&& check "enabled dir populated" 0 || check "enabled dir populated" 1
expect_exit "uninstall while enabled refuses" 1 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh uninstall ms-tools
expect_exit "deactivate moves back to available" 0 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh deactivate ms-tools
expect_exit "uninstall removes from available" 0 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" scripts/skill.sh uninstall ms-tools
# seat skills dispatch (mock evidence)
mkdir -p "$SANDBOX/agents/skillseat" "$SANDBOX/data/user"
printf '# User\n\nname: Jason\n' > "$SANDBOX/data/user/USER.md"
printf '{"agentVersion":1,"name":"skillseat","skills":["ms-tools"]}' > "$SANDBOX/agents/skillseat/agent.json"
printf '{"agentVersion":1,"name":"skillseat","soulPlaceholder":true}' > /dev/null # no-op
printf '# SOUL - skillseat\n\nMethodical. Verifies before claiming.\n' > "$SANDBOX/agents/skillseat/SOUL.md"
# Pre-align the release pointer so ensure does not fire under the mock
# adapter (the gate needs a real model; the mock cannot answer).
REL="$(tr -d '[:space:]' < RELEASE)"
mkdir -p "$SANDBOX/data/state"
printf '{"pointerVersion":1,"release":"%s","imageTag":"mosaic-poc-agent:0.84.4-r%s","activatedAt":"2026-01-01T00:00:00Z"}\n' "$REL" "$REL" > "$SANDBOX/data/state/active.json"
scripts/agent.sh skillseat </dev/null >"$SANDBOX/seat-stdout.txt" 2>"$SANDBOX/seat-stderr.txt"
RC=$?
if [ "$RC" -eq 0 ]; then
PASS=$((PASS+1)); echo "${C_OK}OK${C_RESET} seat with enabled skill launches"
else
FAIL=$((FAIL+1)); echo "${C_FAIL}FAIL${C_RESET} seat with enabled skill launches (exit $RC)" >&2
echo "SEATCASE stderr:" >&2; cat "$SANDBOX/seat-stderr.txt" >&2
fi
grep -q '^MOSAIC_SKILLS=/var/lib/mosaic/skills-enabled/ms-tools$' "$SANDBOX/seat-stderr.txt" 2>/dev/null \
&& check "skill path delivered to adapter" 0 || check "skill path delivered to adapter" 1
# capability policy (M9): least-privilege intersection
POL="$SANDBOX/data/workspaces"; mkdir -p "$POL"
@@ -236,6 +259,7 @@ EOF
printf '{"missionVersion":1,"id":"m-pol","objective":"o","capabilities":{"tools":["sudo"]}}' > "$SANDBOX/pol-m.json"
expect_exit "invalid mission capabilities rejected" 2 -- \
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" $TASK validate "$SANDBOX/pol-t.json"
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" $TASK validate "$SANDBOX/pol-t.json"
# live user context (M14): dispatched to every launch without rebuild
mkdir -p "$SANDBOX/data/user"