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:
@@ -427,3 +427,14 @@ memory alone.
|
||||
## Result (M16)
|
||||
|
||||
Release self-determination live: the system aligns itself to RELEASE without manual commands. Suites 24/68/17/14 + verify green.
|
||||
|
||||
## Phase 21: M16 hardening + M20 decision
|
||||
|
||||
- Recursion guard: release.sh's health-gate task run sets MOSAIC_ENSURE_SKIP
|
||||
so the gate's run-task cannot re-enter release self-determination.
|
||||
- run-task.sh: drift warning on pointer/RELEASE mismatch (workers and suites
|
||||
never trigger builds or model gates mid-automation).
|
||||
- ROADMAP M20 decision recorded: v2 adopts packages/* monorepo structure at
|
||||
usurpation (owner, continuity-first); restructure sequenced as M20 phase 1.
|
||||
- Live drill: drift 0.0.11 -> 0.0.12 detected, health-gated activate, no
|
||||
recursion, verify green.
|
||||
|
||||
+14
-1
@@ -47,6 +47,19 @@ fi
|
||||
TOOLS_FLAG="--no-tools"
|
||||
[ -n "${MOSAIC_TOOLS:-}" ] && TOOLS_FLAG="--tools $MOSAIC_TOOLS"
|
||||
|
||||
# Skills (M17): explicitly provided skill dirs replace discovery. When none
|
||||
# are provided the agent runs with --no-skills (nothing ambient to find).
|
||||
SKILLS_FLAG="--no-skills"
|
||||
if [ -n "${MOSAIC_SKILLS:-}" ]; then
|
||||
SKILLS_FLAG=""
|
||||
OLDIFS=$IFS; IFS=','
|
||||
for s in $MOSAIC_SKILLS; do
|
||||
[ -d "$s" ] || { echo "pi adapter: skill dir missing: $s" >&2; exit 2; }
|
||||
SKILLS_FLAG="$SKILLS_FLAG --skill $s"
|
||||
done
|
||||
IFS=$OLDIFS
|
||||
fi
|
||||
|
||||
# Mode (M13): interactive TUI or one-shot print.
|
||||
PRINT_MODE="-p"
|
||||
REQUEST_ARG=""
|
||||
@@ -68,7 +81,7 @@ PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
||||
set -- \
|
||||
--offline \
|
||||
--no-extensions \
|
||||
--no-skills \
|
||||
$SKILLS_FLAG \
|
||||
--no-prompt-templates \
|
||||
--no-themes \
|
||||
--no-context-files \
|
||||
|
||||
@@ -26,6 +26,8 @@ services:
|
||||
MOSAIC_AGENT_NAME: ${MOSAIC_AGENT_NAME:-}
|
||||
MOSAIC_AGENT_ROLE: ${MOSAIC_AGENT_ROLE:-}
|
||||
MOSAIC_AGENT_SOUL_FILE: ${MOSAIC_AGENT_SOUL_FILE:-}
|
||||
# Skill dirs explicitly provided to the seat (M17)
|
||||
MOSAIC_SKILLS: ${MOSAIC_SKILLS:-}
|
||||
# mock adapter only: verbatim response for deterministic seam tests
|
||||
MOSAIC_MOCK_RESPONSE: ${MOSAIC_MOCK_RESPONSE:-}
|
||||
# Documented container auth alternative: provider API key via
|
||||
|
||||
@@ -7,3 +7,4 @@ are never rewritten or removed; corrections are new entries.
|
||||
| Date (UTC) | Actor | Scope | Outcome / artifacts |
|
||||
|---|---|---|---|
|
||||
| 2026-09-03 | assistant (conductor + worker) | POC through M12: containerized pi proof, config layer, missions/tasks, release model, adapter seam, workspaces/capabilities, named sessions, retention, session forking, conductor auto-apply, roles/ convention | 13 tags; suites 24/58/14 + 17 conductor + verify green; releases 0.0.1–0.0.7; issues #1–#34 closed |
|
||||
| 2026-09-03 | assistant (conductor) | User layer: profile updates (pets, family), ms-user skill review/revision (confirmation rules merged, propose-not-apply, missing-file flow, privacy scope, dispatch = all of user/, rule 9 scratch-file constraint), USER.md.bak removed | skills/ms-user/SKILL.md rewritten; ~/.mosaic-dev/user/USER.md updated (Family, Pets); USER.md.bak deleted |
|
||||
|
||||
+26
-4
@@ -22,6 +22,7 @@ MISSION=""
|
||||
WORKSPACE=""
|
||||
SESSION=""
|
||||
TOOLS=""
|
||||
SKILLS=""
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
@@ -29,6 +30,7 @@ while [ $# -gt 0 ]; do
|
||||
--workspace) WORKSPACE="${2:?}"; shift 2 ;;
|
||||
--session) SESSION="${2:?}"; shift 2 ;;
|
||||
--tools) TOOLS="${2:?}"; shift 2 ;;
|
||||
--skills) SKILLS="${2:?}"; shift 2 ;;
|
||||
--help|-h) sed -n '2,12p' "$0"; exit 0 ;;
|
||||
*) NAME="$1"; shift ;;
|
||||
esac
|
||||
@@ -57,6 +59,8 @@ fi
|
||||
AGENTS_DIR="${MOSAIC_AGENTS_DIR:-agents}"
|
||||
ROLE=""
|
||||
DEFCAPS=""
|
||||
AGENT_DEF_SKILLS=""
|
||||
AGENT_DEF_SKILLS=""
|
||||
if [ -f "$AGENTS_DIR/$NAME/agent.json" ]; then
|
||||
DEFAULTS_FILE="$(mktemp)"
|
||||
node -e '
|
||||
@@ -73,13 +77,14 @@ if (p.capabilities !== undefined) {
|
||||
if (!Array.isArray(p.capabilities.tools) || p.capabilities.tools.some(t => !/^[a-z]+$/.test(t))) process.exit(2);
|
||||
tools = p.capabilities.tools.join(",");
|
||||
}
|
||||
fs.writeFileSync(process.argv[2], "AGENT_DEF_ROLE=" + (p.role || "") + "\nAGENT_DEF_CAPS=" + tools + "\n");
|
||||
fs.writeFileSync(process.argv[2], "AGENT_DEF_ROLE=" + (p.role || "") + "\nAGENT_DEF_CAPS=" + tools + "\nAGENT_DEF_SKILLS=" + ((p.skills && Array.isArray(p.skills)) ? p.skills.join(",") : "") + "\n");
|
||||
' "$AGENTS_DIR/$NAME/agent.json" "$DEFAULTS_FILE" || { rm -f "$DEFAULTS_FILE"; echo "agent: invalid agent definition" >&2; exit 2; }
|
||||
AGENT_DEF_ROLE=""; AGENT_DEF_CAPS=""
|
||||
AGENT_DEF_ROLE=""; AGENT_DEF_CAPS=""; AGENT_DEF_SKILLS=""
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
AGENT_DEF_ROLE=*) AGENT_DEF_ROLE="${line#AGENT_DEF_ROLE=}" ;;
|
||||
AGENT_DEF_CAPS=*) AGENT_DEF_CAPS="${line#AGENT_DEF_CAPS=}" ;;
|
||||
AGENT_DEF_ROLE=*) AGENT_DEF_ROLE="${line#AGENT_DEF_ROLE=}" ;;
|
||||
AGENT_DEF_CAPS=*) AGENT_DEF_CAPS="${line#AGENT_DEF_CAPS=}" ;;
|
||||
AGENT_DEF_SKILLS=*) AGENT_DEF_SKILLS="${line#AGENT_DEF_SKILLS=}" ;;
|
||||
esac
|
||||
done < "$DEFAULTS_FILE"
|
||||
rm -f "$DEFAULTS_FILE"
|
||||
@@ -106,6 +111,23 @@ export MOSAIC_INTERACTIVE=1
|
||||
if [ -z "$TOOLS" ] && [ -n "$DEFCAPS" ]; then TOOLS="$DEFCAPS"; fi
|
||||
export MOSAIC_TOOLS="${TOOLS:+$TOOLS}"
|
||||
|
||||
# Skills (M17): seat definition may declare skill names; each must be
|
||||
# enabled in <dataRoot>/skills-enabled or the launch refuses - a silently
|
||||
# under-equipped seat is the failure mode this prevents.
|
||||
SKILLS_LIST="${SKILLS:-$AGENT_DEF_SKILLS}"
|
||||
if [ -n "$SKILLS_LIST" ]; then
|
||||
mkdir -p "$MOSAIC_DEV_DIR/skills-enabled"
|
||||
RESOLVED=""
|
||||
OLDIFS=$IFS; IFS=','
|
||||
for s in $SKILLS_LIST; do
|
||||
case "$s" in *[!A-Za-z0-9._-]*|'') echo "agent: invalid skill name: '$s'" >&2; exit 2;; esac
|
||||
[ -d "$MOSAIC_DEV_DIR/skills-enabled/$s" ] || { echo "agent: skill '$s' is declared but not enabled (scripts/skill.sh activate $s)" >&2; exit 1; }
|
||||
RESOLVED="${RESOLVED:+$RESOLVED,}/var/lib/mosaic/skills-enabled/$s"
|
||||
done
|
||||
IFS=$OLDIFS
|
||||
export MOSAIC_SKILLS="$RESOLVED"
|
||||
fi
|
||||
|
||||
if [ -n "$MISSION" ]; then
|
||||
[ -r "$MISSION" ] || { echo "agent: mission file not readable: $MISSION" >&2; exit 4; }
|
||||
mkdir -p "$MOSAIC_DEV_DIR/agent-missions"
|
||||
|
||||
+3
-1
@@ -49,7 +49,9 @@ health_check() { # returns 0 only when the marker path passes; $1 = fault inject
|
||||
task="$tmp/fault-task.json"
|
||||
fi
|
||||
local rc=0
|
||||
scripts/run-task.sh run "$task" >/dev/null 2>&1 || rc=$?
|
||||
# MOSAIC_ENSURE_SKIP: the gate's run must not re-enter release
|
||||
# self-determination (recursion guard).
|
||||
MOSAIC_ENSURE_SKIP=1 scripts/run-task.sh run "$task" >/dev/null 2>&1 || rc=$?
|
||||
[ -n "$tmp" ] && rm -rf "$tmp"
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# Skill lifecycle: install / activate / deactivate / uninstall / list.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/skill.sh install <source-dir | bundled-name>
|
||||
# scripts/skill.sh activate <name>
|
||||
# scripts/skill.sh deactivate <name>
|
||||
# scripts/skill.sh uninstall <name>
|
||||
# scripts/skill.sh list
|
||||
#
|
||||
# Layout (machine-scoped, under the data root):
|
||||
# <dataRoot>/skills-available/<name> installed, not loadable
|
||||
# <dataRoot>/skills-enabled/<name> loadable by agent launches
|
||||
#
|
||||
# A skill not in skills-enabled is not enabled or available for use.
|
||||
# Lifecycle: install -> available; activate -> enabled; deactivate ->
|
||||
# available; uninstall -> removed (only from available).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
# shellcheck source=common.sh
|
||||
source scripts/common.sh
|
||||
|
||||
load_config
|
||||
load_release
|
||||
bootstrap_runtime_dir
|
||||
|
||||
ENABLED="$MOSAIC_DEV_DIR/skills-enabled"
|
||||
AVAILABLE="$MOSAIC_DEV_DIR/skills-available"
|
||||
mkdir -p "$ENABLED" "$AVAILABLE"
|
||||
|
||||
die() { local code="$1"; shift; echo "skill: $*" >&2; exit "$code"; }
|
||||
|
||||
is_skill_dir() { [ -f "$1/SKILL.md" ]; }
|
||||
|
||||
resolve_source() { # bundled name (skills/<name> in repo) or explicit path
|
||||
if [ -d "skills/$1" ]; then printf 'skills/%s' "$1"; return; fi
|
||||
if [ -d "$1" ]; then printf '%s' "$1"; return; fi
|
||||
die 4 "source skill dir not found: $1"
|
||||
}
|
||||
|
||||
frontmatter_name() { # extract name: from SKILL.md frontmatter
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const t = fs.readFileSync(process.argv[1], "utf8");
|
||||
const m = t.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (!m) process.exit(1);
|
||||
const name = (m[1].match(/^name:\s*(.+)$/m) || [])[1];
|
||||
if (!name) process.exit(1);
|
||||
process.stdout.write(name.trim());
|
||||
' "$1/SKILL.md"
|
||||
}
|
||||
|
||||
case "${1:-list}" in
|
||||
install)
|
||||
src="${2:?usage: skill.sh install <source-dir|bundled-name>}"
|
||||
resolve_source "$src" >/dev/null || die 4 "source skill dir not found: $src"
|
||||
SRC_DIR="$(resolve_source "$src")"
|
||||
is_skill_dir "$SRC_DIR" || die 4 "not a skill (missing SKILL.md): $SRC_DIR"
|
||||
NAME="$(basename "$SRC_DIR")"
|
||||
[ -d "$AVAILABLE/$NAME" ] && die 1 "already installed: $NAME"
|
||||
mkdir -p "$AVAILABLE"
|
||||
cp -r "$SRC_DIR" "$AVAILABLE/$NAME"
|
||||
echo "skill: installed $NAME -> skills-available"
|
||||
echo "skill: activate with scripts/skill.sh activate $NAME"
|
||||
;;
|
||||
activate)
|
||||
name="${2:?usage: skill.sh activate <name>}"
|
||||
[ -d "$AVAILABLE/$name" ] || die 4 "not installed: $name"
|
||||
[ -d "$ENABLED/$name" ] && die 1 "already enabled: $name"
|
||||
mv "$AVAILABLE/$name" "$ENABLED/$name"
|
||||
echo "skill: enabled $name"
|
||||
;;
|
||||
deactivate)
|
||||
name="${2:?usage: skill.sh deactivate <name>}"
|
||||
[ -d "$ENABLED/$name" ] || die 4 "not enabled: $name"
|
||||
mv "$ENABLED/$name" "$AVAILABLE/$name"
|
||||
echo "skill: deactivated $name -> skills-available"
|
||||
;;
|
||||
uninstall)
|
||||
name="${2:?usage: skill.sh uninstall <name>}"
|
||||
if [ -d "$ENABLED/$name" ]; then
|
||||
die 1 "refusing: $name is enabled - deactivate first"
|
||||
fi
|
||||
[ -d "$AVAILABLE/$name" ] || die 4 "not installed: $name"
|
||||
rm -rf "$AVAILABLE/$name"
|
||||
echo "skill: uninstalled $name"
|
||||
;;
|
||||
list)
|
||||
echo "enabled:"
|
||||
for d in "$ENABLED"/*/; do
|
||||
[ -d "$d" ] && echo " $(basename "$d")"
|
||||
done
|
||||
echo "available:"
|
||||
for d in "$AVAILABLE"/*/; do
|
||||
[ -d "$d" ] && echo " $(basename "$d")"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
echo "usage: scripts/skill.sh install <src> | activate <name> | deactivate <name> | uninstall <name> | list" >&2
|
||||
exit 4
|
||||
;;
|
||||
esac
|
||||
+40
-16
@@ -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"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
name: ms-agent-watch
|
||||
description: Use for all instances where a watch, wait, or agentic status check is needed. This skill avoid the need for wait cycles and other methods used to wait for an action outcome.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-agent-watch — self-armed condition watches
|
||||
|
||||
One CLI call arms an isolated `systemd --user` transient timer per watch. Each
|
||||
tick is a fresh, cgroup-isolated process; no loop, no script, no orphan. You
|
||||
never `sleep`, never write a watch script, never background anything.
|
||||
|
||||
## Tool
|
||||
|
||||
```bash
|
||||
tools/agent-watch/agent-watch.sh start \
|
||||
--name <lowercase-id> --session <your-tmux-session> \
|
||||
[--socket <tmux-socket>] # REQUIRED for mosaic-fleet seats using tmux
|
||||
--when '<shell command>' # exit 0 = met; quote it
|
||||
--message "text delivered to you" \
|
||||
[--class actionable|reaction|human|digest|terminal-log] \
|
||||
[--interval 30] [--timeout 3600] [--repeat] [--quiet-timeout]
|
||||
|
||||
agent-watch.sh list # your host's watches
|
||||
agent-watch.sh log <name> # what it did and why
|
||||
agent-watch.sh stop <name> # retire + clean state
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Name it for the thing watched** (`ci-pr1350-green`, `peer-orch-01-reply`),
|
||||
not for yourself — names appear in `[watch:<name>]` message prefixes and
|
||||
in `systemctl list-timers 'agent-watch-*'`.
|
||||
2. **Interval floor is 10s.** A watcher is a fallback cadence. If you find
|
||||
yourself wanting 1s polls, the real fix is an event, not a faster watch.
|
||||
3. **Timeout is real** (default 1h): on expiry you get one terminal-log note
|
||||
(unless `--quiet-timeout`) and the watch retires. A watch is never forever;
|
||||
re-arm deliberately if the condition is still pending.
|
||||
4. **Conditions are cron-style**: clean-ish environment, `cwd=$HOME`. Use
|
||||
absolute paths. Do NOT rely on ambient credentials — resolve tokens through
|
||||
the git credential helper or absolute service-credential paths.
|
||||
5. **rc=2 delivery is DELIVERED** (text reached your pane as a draft — FLEET-COMMS
|
||||
E7); the watcher never retries it. Real failures retry twice then retire loudly.
|
||||
6. **A broken condition (exit ≠ 0/1) retires the watch** with a terminal-log
|
||||
note. Check `log <name>` before re-arming — re-arming a broken condition
|
||||
without fixing it just burns another timeout.
|
||||
7. **`--repeat` re-arms after every delivery.** Default is one-shot on purpose:
|
||||
each watch should correspond to one pending fact.
|
||||
8. **Retire your watches** when the mission closes (`stop`). `list` shows
|
||||
stale-state entries whose timer is gone; stop removes those too.
|
||||
9. Fleet seats MUST pass `--socket mosaic-fleet` — the default is the default
|
||||
socket and the delivery will not reach you.
|
||||
|
||||
## When NOT to watch
|
||||
|
||||
- Waiting on another SEAT: send them a message (agent-send / comms) instead —
|
||||
a watch polling their output is a loop wearing a hat.
|
||||
- Waiting on yourself: just do the next thing.
|
||||
- Something that already has a wake path (fleet watcher injects comms
|
||||
automatically): those arrive without any watch.
|
||||
|
||||
## Failure model (what you'll see)
|
||||
|
||||
| Symptom | Meaning |
|
||||
|---|---|
|
||||
| `started watcher ...` then nothing, timer inactive | condition broke (exit ≠ 0/1) or timeout hit — `log <name>` says which |
|
||||
| `[watch:x] ... timeout after Ns` | retired; re-arm only if still relevant |
|
||||
| delivered (rc=2) in log | delivered as draft into your pane — go read the pane |
|
||||
| delivery failed rc=1 after 3 attempts | your session was gone; restart it, then re-arm |
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: ms-agent
|
||||
description: Use for ALL agent operation cycles.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-agent
|
||||
|
||||
Agent operation cycles: launch, work, verify, persist, hand off.
|
||||
|
||||
## Cycle
|
||||
|
||||
1. Launch: `scripts/agent.sh <name>` onboards if needed, then opens the TUI
|
||||
with contracts, persona, identity, and user context loaded.
|
||||
2. Work inside your workspace. Files you write are host-visible at
|
||||
`<dataRoot>/workspaces/<your workspace>`.
|
||||
3. Your session persists in `sessions/<name>`. Relaunching the same seat
|
||||
resumes where you left off.
|
||||
4. Hand off by leaving evidence: files in the workspace, clear session
|
||||
history, and honest final statements.
|
||||
|
||||
## Rules
|
||||
|
||||
- One seat, one identity. Never share sessions or workspaces across seats.
|
||||
- Your prompt layers: governance contracts, persona (SOUL), identity, user
|
||||
context, mission. Later layers refine earlier ones. Governance is never
|
||||
overridden.
|
||||
- If a capability you need is missing, say so. Do not improvise a capability
|
||||
you were not granted.
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: ms-conductor
|
||||
description: Use this skill when performing Conducting tasks.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-conductor
|
||||
|
||||
Conducting discipline: direct workers without being one.
|
||||
|
||||
## Order of operations
|
||||
|
||||
1. Decompose the goal into worker tasks small enough to spec completely in
|
||||
one prompt: goal, files, constraints, acceptance, self-checks.
|
||||
2. Dispatch through the task runner. Never raw pi; never a shell one-liner.
|
||||
3. Extract the worker's diff. Review it line by line before integration.
|
||||
4. Verify with the suites. A failure reverts; the refusal is recorded.
|
||||
5. Integrate with attribution. Update the plan and registry.
|
||||
|
||||
## Gotcha ledger
|
||||
|
||||
- Sequential dependent calls. Verify a write before claiming it done.
|
||||
- Pre-check every path before passing it to a tool. Missing paths fail
|
||||
silently in some consumers.
|
||||
- Auth and symlink ensure before TUI launch. Missing auth falls back to
|
||||
defaults silently.
|
||||
- Post-reset: the release pointer is gone and onboarding reruns. Both are
|
||||
expected; align releases with `release.sh ensure`.
|
||||
- A worker that passes for the wrong reason is a masking failure. Assert
|
||||
reasons, not just exit codes.
|
||||
|
||||
## Refusals
|
||||
|
||||
Refuse rather than guess. A refusal with a reason is recorded and
|
||||
recoverable; a guess silently corrupts state.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: ms-file-read
|
||||
description: Use this skill when reading any file.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-file-read
|
||||
|
||||
Read files before you act on them. Never act on a filename alone.
|
||||
|
||||
## Rules
|
||||
|
||||
1. Read the file before editing, summarizing, or deciding anything about it.
|
||||
2. Large files: read in chunks with offset and limit instead of dumping.
|
||||
3. Verify what you read matches what you expected before building on it.
|
||||
4. Never read credential material (auth files, tokens, keys). The name is
|
||||
warning enough.
|
||||
|
||||
## When
|
||||
|
||||
- Before any edit: the edit must match what is actually on disk.
|
||||
- Before answering questions about file contents.
|
||||
- When a run record, log, or receipt is cited as evidence.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: ms-file-write
|
||||
description: Use this skill when writing any file.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-file-write
|
||||
|
||||
Write files so the write is provable and reversible.
|
||||
|
||||
## Rules
|
||||
|
||||
1. Write to a temp file and rename for atomic replacement of an existing file.
|
||||
2. Verify the write: re-read or checksum before claiming success.
|
||||
3. Never overwrite a file that carries identity or history (seat records,
|
||||
run records, logs). They are append-only or write-once for a reason.
|
||||
4. No secrets in written files. Ever.
|
||||
5. Match the file's existing style. Do not reformat regions you did not touch.
|
||||
|
||||
@@ -1,14 +1,42 @@
|
||||
---
|
||||
name: ms-tools
|
||||
description: Contains a reference to all available tools for Mosaic Stack.
|
||||
disable-model-invocation: true
|
||||
description: Reference for all available tools in Mosaic Stack.
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# ms-tools
|
||||
|
||||
You are a Mosaic fleet agent. A maintained toolkit lives at `<dir>`.
|
||||
Use it FIRST for the tasks below — improvising with raw CLIs causes the recurring failures this
|
||||
skill exists to prevent. This is the high-frequency fast path; the full reference is the
|
||||
`# Machine Tools` section already in your system prompt.
|
||||
You are a Mosaic seat agent. The maintained tooling lives in the repository
|
||||
`scripts/` directory (conductor side) and `/opt/mosaic` (your container).
|
||||
Use it FIRST for the tasks below. Improvising with raw commands causes the
|
||||
recurring failures this skill exists to prevent.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
- scripts/bootstrap.sh creates the system config. Idempotent, never overwrites.
|
||||
- scripts/build.sh builds the release image. Tag comes from RELEASE.
|
||||
- scripts/verify.sh runs the gated startup check. Exit 0 means MOSAIC_HELLO_OK.
|
||||
- scripts/release.sh ensure aligns what is installed with RELEASE. No manual
|
||||
release commands; drift is detected and corrected at launch.
|
||||
|
||||
## Tasks and runs
|
||||
|
||||
- scripts/run-task.sh run <task.json> executes a governed task. Every run
|
||||
leaves a write-once record under <dataRoot>/runs/.
|
||||
- node scripts/mosaic-task.mjs list shows run history.
|
||||
- node scripts/mosaic-task.mjs show <runId> inspects one run: receipts,
|
||||
snapshots, stderr.
|
||||
- node scripts/mosaic-task.mjs retry <runId> re-executes a recorded task as
|
||||
a new run. The old record stays.
|
||||
|
||||
## Seats
|
||||
|
||||
- scripts/agent.sh <name> launches an interactive seat. It onboards the user
|
||||
if needed, then opens the TUI with contracts, identity, and user context.
|
||||
- agents/<name>/ holds a seat definition: agent.json plus SOUL.md.
|
||||
|
||||
## Rules
|
||||
|
||||
- Exit codes: 0 ok, 1 failed, 2 invalid input, 3 config missing, 4 usage.
|
||||
- A refusal is evidence. Diagnose it; do not route around it.
|
||||
- Full reference: docs/TOOLS.md.
|
||||
|
||||
+32
-2
@@ -4,5 +4,35 @@ description: Always use to update recorded information about the user
|
||||
disable-model-invocation: false
|
||||
---
|
||||
|
||||
# User
|
||||
Info about the tooling and usage should be listed here.
|
||||
# ms-user
|
||||
|
||||
The user context layer holds user-owned information dispatched to every
|
||||
agent launch: everything under `<dataRoot>/user/`, with `USER.md` as the
|
||||
profile.
|
||||
|
||||
## Rules
|
||||
|
||||
1. The user owns the content. It is injected into your system prompt when
|
||||
present; verify it on disk before editing.
|
||||
2. Always check for the `<dataRoot>/user/USER.md` file before attempting
|
||||
to update info.
|
||||
3. If the USER.md file is missing, guide the user through onboarding
|
||||
(`scripts/onboard.sh`) or, with their consent, create a minimal
|
||||
template (name only) and let them fill in the rest.
|
||||
4. Initial configuration uses `scripts/onboard.sh`
|
||||
(guided, name required) or the user's own edits.
|
||||
Agents propose; the user decides and authorizes; once authorized,
|
||||
the agent performs the edit.
|
||||
5. NEVER place secrets in the user layer. Everything under
|
||||
`<dataRoot>/user/` is dispatched to every agent and worker, so keep
|
||||
near-secrets out too: home address, finances, anything you would not
|
||||
hand a stranger with shell access.
|
||||
6. Propose every change to the user layer, new content or new section
|
||||
alike, and apply it only after an explicit yes.
|
||||
7. Propose updates proactively as information about the user is learned.
|
||||
8. The USER.md file is not limited to the provided template fields.
|
||||
Add sections as needed.
|
||||
9. Never block work on proposed updates to the user information files.
|
||||
Note the proposed additions in a scratch file outside
|
||||
`<dataRoot>/user/` (unconfirmed content must not be dispatched),
|
||||
then confirm with the user at a better time.
|
||||
|
||||
Reference in New Issue
Block a user