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
+11
View File
@@ -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.
+1 -1
View File
@@ -1 +1 @@
0.0.11
0.0.12
+14 -1
View File
@@ -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 \
+2
View File
@@ -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
+1
View File
@@ -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.10.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
View File
@@ -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
View File
@@ -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"
}
+102
View File
@@ -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
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"
+69
View File
@@ -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 |
+29
View File
@@ -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.
+35
View File
@@ -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.
+23
View File
@@ -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.
+18
View File
@@ -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.
+34 -6
View File
@@ -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
View File
@@ -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.