#!/bin/sh # Layer 0 — clawdie-live-seed importer regression test (runs on any POSIX host). # # Exercises the REAL importer (live/operator-session/clawdie-live-seed) against a # synthetic CLAWDIESEED tree, with every path redirected to a temp sandbox via the # SEED_* override vars and CLAWDIE_SEED_TEST=1 (which skips the rc.subr handoff). # No FreeBSD, no mount, no root required — chowns fail silently by design. # # It encodes the seed->runtime propagation contract as assertions: # - operator home: .env (app keys), vault-bootstrap.env (BW_*), ssh material # - daemon home: outbound ssh material (mother-mcp key), NO authorized_keys # - provider.env: app provider keys only (no BW_*) # - staging: soul/ tree, harness.toml, agent-name, active-agent, raw env # - idempotency: re-import does not duplicate keys / known_hosts # # PENDING group: AGENTS.md -> zot home. This is the change Hermes is pushing. # It is xfail (informational) until the importer learns to install AGENTS.md; # re-run with REQUIRE_AGENTS_MD=1 after the patch to enforce it as required. # # Usage: sh tests/seed-import-test.sh # current contract must pass # REQUIRE_AGENTS_MD=1 sh tests/... # also enforce the AGENTS.md patch set -u SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) IMPORTER="${IMPORTER:-${SCRIPT_DIR}/../live/operator-session/clawdie-live-seed}" if [ ! -r "${IMPORTER}" ]; then echo "FATAL: importer not found at ${IMPORTER}" >&2 exit 2 fi WORK=$(mktemp -d "${TMPDIR:-/tmp}/seed-import-test.XXXXXX") || exit 2 trap 'rm -rf "${WORK}"' EXIT INT TERM # --- override every path the importer touches into the sandbox --------------- TEST_USER=$(id -un) export CLAWDIE_SEED_TEST=1 export SEED_MOUNT="${WORK}/seed" export SEED_LOG="${WORK}/seed.log" export SEED_USER="${TEST_USER}" export SEED_USER_HOME="${WORK}/operator-home" export SEED_DAEMON_USER="${TEST_USER}" export SEED_DAEMON_HOME="${WORK}/daemon-home" export SEED_IMPORT_ROOT="${WORK}/import-root" export SEED_PROVIDER_ENV="${WORK}/provider.env" # Contract under test for the AGENTS.md patch: importer installs it to zot's # global slot under the daemon home. Keep this in one place so it tracks the # final decision (ZOT_HOME pin) without edits scattered through the test. ZOT_HOME_REL=".local/state/zot" EXPECT_AGENTS_MD="${SEED_DAEMON_HOME}/${ZOT_HOME_REL}/AGENTS.md" mkdir -p "${SEED_USER_HOME}" "${SEED_DAEMON_HOME}" "${SEED_IMPORT_ROOT}" # --- build a synthetic single-agent seed (the live-USB case) ------------------ SEED_AGENT="${SEED_MOUNT}/clawdie" mkdir -p "${SEED_AGENT}/ssh" "${SEED_AGENT}/soul/memories" cat >"${SEED_AGENT}/env" <<'EOF' # operator-provided secrets (plaintext FAT32 by design) DEEPSEEK_API_KEY=sk-test-deepseek DEEPSEEK_MODEL=deepseek-v4 BW_CLIENTID=user.test-client BW_CLIENTSECRET=test-secret BW_PASSWORD=test-master-pw EOF cat >"${SEED_AGENT}/harness.toml" <<'EOF' harness = "zot" EOF cat >"${SEED_AGENT}/AGENTS.md" <<'EOF' # Operational rules (zot reads this as project context) - mother is OSA, reachable via the mother-mcp key on this seed - verbs: node_register, create-task, intake-task - this node is a USB operator, capability: freebsd - install Hermes from /home/clawdie/ai/hermes-bsd CANARY_AGENTS_MARKER=zot-sees-this EOF # soul tree (dormant; must be STAGED, not activated, in 0.12) cat >"${SEED_AGENT}/soul/SOUL.md" <<'EOF' # SOUL (staged for Hermes, dormant) EOF cat >"${SEED_AGENT}/soul/memories/USER.md" <<'EOF' # USER (staged) EOF # ssh material: inbound authorized_keys + outbound client (config, key, known_hosts) echo "ssh-ed25519 AAAAINBOUNDtestkey operator@laptop" >"${SEED_AGENT}/ssh/authorized_keys" cat >"${SEED_AGENT}/ssh/config" <<'EOF' Host mother HostName 100.72.229.63 User colibri IdentityFile ~/.ssh/mother-mcp EOF echo "-----BEGIN OPENSSH PRIVATE KEY-----TEST-----END-----" >"${SEED_AGENT}/ssh/mother-mcp" echo "ssh-ed25519 AAAAMOTHERPUBkey colibri@mother" >"${SEED_AGENT}/ssh/mother-mcp.pub" echo "mother-host ssh-ed25519 AAAAMOTHERhostkey" >"${SEED_AGENT}/ssh/known_hosts" # --- assertion harness ------------------------------------------------------- PASS=0 REQ_FAIL=0 PEND_FAIL=0 ok() { PASS=$((PASS+1)); printf ' ok %s\n' "$1"; } fail() { REQ_FAIL=$((REQ_FAIL+1)); printf ' FAIL %s\n' "$1"; } pend() { PEND_FAIL=$((PEND_FAIL+1)); printf ' PEND %s\n' "$1"; } # fail() vs pend() chosen by tag arg ($1 = required|pending) report() { # report if [ "$2" -eq 1 ]; then ok "$3" elif [ "$1" = pending ] && [ -z "${REQUIRE_AGENTS_MD:-}" ]; then pend "$3" else fail "$3"; fi } exists() { [ -e "$2" ] && report "$1" 1 "$3" || report "$1" 0 "$3"; } not_exists() { [ ! -e "$2" ] && report "$1" 1 "$3" || report "$1" 0 "$3"; } contains() { [ -f "$2" ] && grep -q "$3" "$2" 2>/dev/null && report "$1" 1 "$4" || report "$1" 0 "$4"; } absent_in() { { [ ! -f "$2" ] || ! grep -q "$3" "$2" 2>/dev/null; } && report "$1" 1 "$4" || report "$1" 0 "$4"; } count_is() { _c=$(grep -c "$3" "$2" 2>/dev/null || echo 0); [ "$_c" = "$4" ] && report "$1" 1 "$5 (got $_c)" || report "$1" 0 "$5 (got $_c)"; } # --- run the real importer --------------------------------------------------- # shellcheck disable=SC1090 . "${IMPORTER}" echo "== import pass 1 ==" _seed_import_tree OP_HOME="${SEED_USER_HOME}" DM_HOME="${SEED_DAEMON_HOME}" STAGE="${SEED_IMPORT_ROOT}/clawdie" echo "-- operator home --" contains required "${OP_HOME}/.env" '^DEEPSEEK_API_KEY=sk-test-deepseek' "DEEPSEEK key in operator ~/.env" absent_in required "${OP_HOME}/.env" '^BW_PASSWORD=' "BW_* NOT in operator ~/.env" contains required "${OP_HOME}/.config/vault-bootstrap.env" '^BW_PASSWORD=test-master-pw' "BW_PASSWORD in vault-bootstrap.env" exists required "${OP_HOME}/.ssh/authorized_keys" "operator authorized_keys installed" exists required "${OP_HOME}/.ssh/config" "operator ssh config installed" exists required "${OP_HOME}/.ssh/mother-mcp" "operator mother-mcp private key installed" contains required "${OP_HOME}/.ssh/known_hosts" 'mother-host' "operator known_hosts has mother host key" echo "-- daemon home (colibri spawns the outbound MCP SSH) --" exists required "${DM_HOME}/.ssh/mother-mcp" "daemon mother-mcp private key installed" exists required "${DM_HOME}/.ssh/config" "daemon ssh config installed" not_exists required "${DM_HOME}/.ssh/authorized_keys" "daemon has NO authorized_keys (inbound is operator-only)" echo "-- provider.env (daemon autospawn keys; root-owned) --" contains required "${SEED_PROVIDER_ENV}" '^DEEPSEEK_API_KEY=sk-test-deepseek' "DEEPSEEK key in provider.env" absent_in required "${SEED_PROVIDER_ENV}" '^BW_PASSWORD=' "BW_* NOT in provider.env" echo "-- staging (dormant payload) --" exists required "${STAGE}/soul/SOUL.md" "soul/ tree staged" exists required "${STAGE}/soul/memories/USER.md" "soul memories staged" contains required "${STAGE}/harness.toml" 'harness = "zot"' "harness.toml recorded" contains required "${STAGE}/agent-name" '^clawdie$' "agent-name staged" contains required "${SEED_IMPORT_ROOT}/active-agent" '^clawdie$' "active-agent recorded" exists required "${STAGE}/env" "raw env staged (0600)" echo "-- PENDING: AGENTS.md -> zot global slot (Hermes's importer patch) --" exists pending "${EXPECT_AGENTS_MD}" "AGENTS.md installed to \$ZOT_HOME" contains pending "${EXPECT_AGENTS_MD}" 'CANARY_AGENTS_MARKER=zot-sees-this' "AGENTS.md content intact at zot slot" # --- idempotency: a second import must not duplicate ------------------------- echo "== import pass 2 (idempotency) ==" _seed_import_tree count_is required "${OP_HOME}/.env" '^DEEPSEEK_API_KEY=' 1 "exactly one DEEPSEEK key in ~/.env after re-import" count_is required "${SEED_PROVIDER_ENV}" '^DEEPSEEK_API_KEY=' 1 "exactly one DEEPSEEK key in provider.env after re-import" count_is required "${OP_HOME}/.ssh/known_hosts" 'mother-host' 1 "known_hosts not duplicated after re-import" # --- summary ----------------------------------------------------------------- echo echo "RESULT: ${PASS} passed, ${REQ_FAIL} required-fail, ${PEND_FAIL} pending" if [ -n "${REQUIRE_AGENTS_MD:-}" ] && [ "${PEND_FAIL}" -gt 0 ]; then echo " (REQUIRE_AGENTS_MD set: pending failures count as required)" fi [ "${REQ_FAIL}" -eq 0 ] || exit 1 exit 0