diff --git a/BUILD-LOG.md b/BUILD-LOG.md index 746952c1..7544c066 100644 --- a/BUILD-LOG.md +++ b/BUILD-LOG.md @@ -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. diff --git a/RELEASE b/RELEASE index 2cfabea2..8cbf02c3 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -0.0.11 +0.0.12 diff --git a/adapters/pi/adapter.sh b/adapters/pi/adapter.sh index c30b9cf4..13c9da51 100644 --- a/adapters/pi/adapter.sh +++ b/adapters/pi/adapter.sh @@ -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 \ diff --git a/compose.yaml b/compose.yaml index fa9b87b9..91ff1277 100644 --- a/compose.yaml +++ b/compose.yaml @@ -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 diff --git a/docs/SESSIONS.md b/docs/SESSIONS.md index 3fa9ddb6..162be5a3 100644 --- a/docs/SESSIONS.md +++ b/docs/SESSIONS.md @@ -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 | diff --git a/scripts/agent.sh b/scripts/agent.sh index 4f663960..d1c8f943 100755 --- a/scripts/agent.sh +++ b/scripts/agent.sh @@ -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 /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" diff --git a/scripts/release.sh b/scripts/release.sh index 48fc4c46..5c05375c 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -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" } diff --git a/scripts/skill.sh b/scripts/skill.sh new file mode 100755 index 00000000..dbe079e2 --- /dev/null +++ b/scripts/skill.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# Skill lifecycle: install / activate / deactivate / uninstall / list. +# +# Usage: +# scripts/skill.sh install +# scripts/skill.sh activate +# scripts/skill.sh deactivate +# scripts/skill.sh uninstall +# scripts/skill.sh list +# +# Layout (machine-scoped, under the data root): +# /skills-available/ installed, not loadable +# /skills-enabled/ 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/ 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 }" + 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 }" + [ -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 }" + [ -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 }" + 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 | activate | deactivate | uninstall | list" >&2 + exit 4 + ;; +esac diff --git a/scripts/test-task.sh b/scripts/test-task.sh index b269bfdb..113e7549 100755 --- a/scripts/test-task.sh +++ b/scripts/test-task.sh @@ -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 "$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" diff --git a/skills/ms-agent-watch/SKILL.md b/skills/ms-agent-watch/SKILL.md new file mode 100644 index 00000000..e10fb3fe --- /dev/null +++ b/skills/ms-agent-watch/SKILL.md @@ -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 --session \ + [--socket ] # REQUIRED for mosaic-fleet seats using tmux + --when '' # 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 # what it did and why +agent-watch.sh stop # 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:]` 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 ` 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 ` 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 | diff --git a/skills/ms-agent/SKILL.md b/skills/ms-agent/SKILL.md new file mode 100644 index 00000000..69327b3f --- /dev/null +++ b/skills/ms-agent/SKILL.md @@ -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 ` 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 + `/workspaces/`. +3. Your session persists in `sessions/`. 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. diff --git a/skills/ms-conductor/SKILL.md b/skills/ms-conductor/SKILL.md new file mode 100644 index 00000000..9eb7559e --- /dev/null +++ b/skills/ms-conductor/SKILL.md @@ -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. diff --git a/skills/ms-file-read/SKILL.md b/skills/ms-file-read/SKILL.md index e69de29b..f7aa7876 100644 --- a/skills/ms-file-read/SKILL.md +++ b/skills/ms-file-read/SKILL.md @@ -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. diff --git a/skills/ms-file-write/SKILL.md b/skills/ms-file-write/SKILL.md index e69de29b..964214ba 100644 --- a/skills/ms-file-write/SKILL.md +++ b/skills/ms-file-write/SKILL.md @@ -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. diff --git a/skills/ms-tools/SKILL.md b/skills/ms-tools/SKILL.md index 18d3599b..817d35bb 100644 --- a/skills/ms-tools/SKILL.md +++ b/skills/ms-tools/SKILL.md @@ -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 ``. -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 executes a governed task. Every run + leaves a write-once record under /runs/. +- node scripts/mosaic-task.mjs list shows run history. +- node scripts/mosaic-task.mjs show inspects one run: receipts, + snapshots, stderr. +- node scripts/mosaic-task.mjs retry re-executes a recorded task as + a new run. The old record stays. + +## Seats + +- scripts/agent.sh launches an interactive seat. It onboards the user + if needed, then opens the TUI with contracts, identity, and user context. +- agents// 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. diff --git a/skills/ms-user/SKILL.md b/skills/ms-user/SKILL.md index f767b676..dfa90227 100644 --- a/skills/ms-user/SKILL.md +++ b/skills/ms-user/SKILL.md @@ -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. \ No newline at end of file +# ms-user + +The user context layer holds user-owned information dispatched to every +agent launch: everything under `/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 `/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 + `/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 + `/user/` (unconfirmed content must not be dispatched), + then confirm with the user at a better time.