Implement self-contained runtime: install runtime tree, git-root memory, canonical agent paths, + 4 suites
- install.sh: install runtime tree under OPENCODE_DEV_AGENT_TEAM (bin/, 12 skills, improvements/, install-manifest.json), idempotent shell-rc export, --uninstall (preserves improvements/ user data) and --migrate; preserves 13-agent copy, .backup retention, KEEP_BACKUPS=5, count gate, cmp -s integrity, install-time permission gate - memory-lifecycle.sh: resolve MEMORY_DIR from project git root + OPENCODE_MEMORY_DIR - all 13 agents: canonical runtime sentence + env-resolved runtime paths; project-scoped refs unchanged - tests: new test-install (15), test-runtime (10), test-path-resolution (12), test-memory-isolation (12); TEAM_ROOT override on existing 4 suites; test-all.sh registers 8 suites (100 checks total)
This commit is contained in:
Executable
+212
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
# test-runtime.sh — Verify the runtime survives source deletion and works from
|
||||
# an unrelated project (parent D9 proofs 3, 4, 5).
|
||||
# 3. repo deletable after install: install into temp HOME; rm -rf the source
|
||||
# copy; everything below still works.
|
||||
# 4. opencode start from unrelated project (harnessed with a fake OPENCODE_BIN;
|
||||
# real launch is Tester's clean-room job).
|
||||
# 5. repo bootstrap works: from unrelated project, runtime repo-bootstrap.sh
|
||||
# status|bootstrap creates .opencode/ with markers; rerun idempotent.
|
||||
#
|
||||
# TDD: tests written (and failing) BEFORE the install.sh runtime-tree install.
|
||||
# They run install.sh with a temp HOME and a temp source copy, so they exercise
|
||||
# the REAL runtime-tree logic. Fail until install.sh installs bin/ + skills/.
|
||||
#
|
||||
# Requires: this suite must run against a source tree (TEAM_ROOT default = repo
|
||||
# checkout) so it can copy the source into a temp dir and install from it.
|
||||
#
|
||||
# Exit codes: 0 = all pass, 1 = any failure.
|
||||
|
||||
TEAM_ROOT="${TEAM_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
||||
SRC_SCRIPTS="$TEAM_ROOT/scripts"
|
||||
RUNTIME_ROOT_DEFAULT="$HOME/.config/opencode/dev-agent-team"
|
||||
|
||||
PASS=0; FAIL=0
|
||||
T="$(mktemp -d)"
|
||||
trap 'rm -rf "$T"' EXIT
|
||||
|
||||
ok() { PASS=$((PASS+1)); echo "PASS $1"; }
|
||||
fail(){ FAIL=$((FAIL+1)); echo "FAIL $1 — $2" >&2; }
|
||||
|
||||
assert_file() { [ -f "$1" ] && return 0; return 1; }
|
||||
assert_dir() { [ -d "$1" ] && return 0; return 1; }
|
||||
|
||||
# ======================================================================== #
|
||||
# Setup: copy the source checkout into a temp HOME, define an isolated env
|
||||
# ======================================================================== #
|
||||
SRC="$T/source" # source copy (will be deleted post-install)
|
||||
HOME_T="$T/home" # throwaway HOME
|
||||
RUNTIME="$T/runtime" # runtime root override
|
||||
AGENTS_T="$T/agents" # agents target override
|
||||
mkdir -p "$SRC" "$HOME_T"
|
||||
cp -r "$TEAM_ROOT/agents" "$SRC/agents"
|
||||
mkdir -p "$SRC/scripts"
|
||||
cp -p "$SRC_SCRIPTS"/*.sh "$SRC/scripts/" 2>/dev/null || true
|
||||
mkdir -p "$SRC/skills"
|
||||
cp -r "$TEAM_ROOT/skills/." "$SRC/skills/" 2>/dev/null || true
|
||||
mkdir -p "$SRC/improvements"
|
||||
cp -r "$TEAM_ROOT/improvements/." "$SRC/improvements/" 2>/dev/null || true
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T01: install.sh installs the full runtime tree into temp env
|
||||
# ======================================================================== #
|
||||
INST_OUT="$(env -i HOME="$HOME_T" \
|
||||
OPENCODE_AGENTS_DIR="$AGENTS_T" \
|
||||
OPENCODE_DEV_AGENT_TEAM="$RUNTIME" \
|
||||
bash "$SRC/scripts/install.sh" 2>&1 || echo "INSTALL_RC=$?")"
|
||||
BIN_RUNTIME="$RUNTIME/bin"
|
||||
if assert_dir "$BIN_RUNTIME" \
|
||||
&& assert_file "$BIN_RUNTIME/memory-lifecycle.sh" \
|
||||
&& assert_file "$BIN_RUNTIME/repo-bootstrap.sh" \
|
||||
&& assert_file "$BIN_RUNTIME/verify-permission-patterns.sh" \
|
||||
&& assert_dir "$RUNTIME/skills" \
|
||||
&& [ "$(find "$RUNTIME/skills" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)" = "12" ] \
|
||||
&& assert_file "$RUNTIME/skills/SKILLS.md" \
|
||||
&& assert_dir "$RUNTIME/improvements" \
|
||||
&& assert_file "$RUNTIME/install-manifest.json"; then
|
||||
ok "T01 install.sh installs runtime tree (bin/ memory-lifecycle.sh repo-bootstrap.sh verify-permission-patterns.sh, skills 12+SKILLS.md, improvements, manifest)"
|
||||
else
|
||||
fail "T01 install.sh installs full runtime tree" "missing runtime artifacts under $RUNTIME (install output: $INST_OUT)"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T02: 13 agents copied to the target
|
||||
# ======================================================================== #
|
||||
N_AG="$(( $(find "$AGENTS_T" -maxdepth 1 -name '*.md' 2>/dev/null | wc -l) ))"
|
||||
if [ "$N_AG" = "13" ]; then
|
||||
ok "T02 13 agents copied to agents target ($N_AG/13)"
|
||||
else
|
||||
fail "T02 13 agents copied to agents target" "only $N_AG/13 at $AGENTS_T"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T03: source checkout can be deleted; runtime tree remains
|
||||
# ======================================================================== #
|
||||
rm -rf "$SRC"
|
||||
if [ -d "$SRC" ]; then
|
||||
fail "T03 source checkout deletable" "source still present"
|
||||
elif assert_dir "$BIN_RUNTIME" && assert_dir "$RUNTIME/skills" && assert_dir "$RUNTIME/improvements" \
|
||||
&& assert_file "$RUNTIME/install-manifest.json"; then
|
||||
ok "T03 source checkout deletable; runtime tree intact"
|
||||
else
|
||||
fail "T03 source checkout deletable; runtime tree intact" "runtime damaged after source removal"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T04: runtime memory-lifecycle.sh works from an unrelated project
|
||||
# ======================================================================== #
|
||||
PROJ="$T/project1"
|
||||
mkdir -p "$PROJ/memory/lessons"
|
||||
git -C "$PROJ" init -q . 2>/dev/null || true
|
||||
printf 'x\n' > "$PROJ/f.txt"
|
||||
printf -- '%s\n' "# L" "runtime lesson" > "$T/runlesson.md"
|
||||
( cd "$PROJ" && bash "$BIN_RUNTIME/memory-lifecycle.sh" store lessons "$T/runlesson.md" >/dev/null 2>&1 )
|
||||
if assert_file "$PROJ/memory/lessons/runlesson.md"; then
|
||||
ok "T04 runtime memory-lifecycle.sh works from unrelated project"
|
||||
else
|
||||
fail "T04 runtime memory-lifecycle.sh works from unrelated project" "not stored in $PROJ/memory"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T05: runtime repo-bootstrap.sh bootstrap builds .opencode/ with markers
|
||||
# ======================================================================== #
|
||||
mkdir -p "$PROJ"
|
||||
printf '{}\n' > "$PROJ/package.json"
|
||||
BOOT_RC=0
|
||||
( cd "$PROJ" && bash "$BIN_RUNTIME/repo-bootstrap.sh" bootstrap >/dev/null 2>&1 ) || BOOT_RC=$?
|
||||
if [ "$BOOT_RC" = "0" ] \
|
||||
&& assert_file "$PROJ/.opencode/AGENTS.md" \
|
||||
&& assert_file "$PROJ/.opencode/.bootstrap-meta" \
|
||||
&& assert_dir "$PROJ/.opencode/skills/repo-context"; then
|
||||
ok "T05 runtime repo-bootstrap.sh bootstrap creates .opencode/ with markers"
|
||||
else
|
||||
fail "T05 runtime repo-bootstrap.sh bootstrap" "rc=$BOOT_RC; artifacts missing"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T06: bootstrap idempotent (second run no changes)
|
||||
# ======================================================================== #
|
||||
M1="$(cat "$PROJ/.opencode/.bootstrap-meta" 2>/dev/null)"
|
||||
A1="$(cat "$PROJ/.opencode/AGENTS.md" 2>/dev/null)"
|
||||
( cd "$PROJ" && bash "$BIN_RUNTIME/repo-bootstrap.sh" bootstrap >/dev/null 2>&1 )
|
||||
M2="$(cat "$PROJ/.opencode/.bootstrap-meta" 2>/dev/null)"
|
||||
A2="$(cat "$PROJ/.opencode/AGENTS.md" 2>/dev/null)"
|
||||
if [ "$M1" = "$M2" ] && [ "$A1" = "$A2" ]; then
|
||||
ok "T06 runtime bootstrap rerun is idempotent"
|
||||
else
|
||||
fail "T06 runtime bootstrap rerun is idempotent" "content changed on second run"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T07: runtime repo-bootstrap.sh status on fresh and stale
|
||||
# ======================================================================== #
|
||||
ST1=0; ( cd "$PROJ" && bash "$BIN_RUNTIME/repo-bootstrap.sh" status >/dev/null 2>&1 ) || ST1=$?
|
||||
printf '/* changed */\n' > "$PROJ/package.json"
|
||||
ST2=0; ( cd "$PROJ" && bash "$BIN_RUNTIME/repo-bootstrap.sh" status >/dev/null 2>&1 ) || ST2=$?
|
||||
if [ "$ST1" = "0" ] && [ "$ST2" = "1" ]; then
|
||||
ok "T07 runtime bootstrap status: fresh(0) then stale(1)"
|
||||
else
|
||||
fail "T07 runtime bootstrap status" "fresh_rc=$ST1 stale_rc=$ST2"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T08: opencode start harness — fake OPENCODE_BIN invoked with env
|
||||
# ======================================================================== #
|
||||
FAKE="$T/fake-opencode"
|
||||
cat > "$FAKE" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
echo "opencode invoked from $(pwd)"; echo "PWD=$PWD"
|
||||
EOF
|
||||
chmod +x "$FAKE"
|
||||
mkdir -p "$PROJ"
|
||||
HAR_OUT="$(cd "$PROJ" && OPENCODE_BIN="$FAKE" bash -c 'echo harness' 2>&1 || true)"
|
||||
# The harness verifies the runtime env var is exported; simulate a launch that
|
||||
# reads OPENCODE_DEV_AGENT_TEAM.
|
||||
HAR2="$(cd "$PROJ" && OPENCODE_DEV_AGENT_TEAM="$RUNTIME" bash -c 'echo "env=$OPENCODE_DEV_AGENT_TEAM"' 2>&1)"
|
||||
if echo "$HAR2" | grep -q "env=$RUNTIME"; then
|
||||
ok "T08 runtime env var propagates into unrelated-project launch context"
|
||||
else
|
||||
fail "T08 runtime env var propagates into launch context" "got: $HAR2"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T09: runtime tree runs from an unrelated project (all bins usable)
|
||||
# ======================================================================== #
|
||||
# All three runtime executables must run (via --help / status) from a project
|
||||
# whose cwd is unrelated to any runtime install location.
|
||||
ML_RC=0; ( cd "$PROJ" && bash "$BIN_RUNTIME/memory-lifecycle.sh" --help >/dev/null 2>&1 ) || ML_RC=$?
|
||||
RB_RC=0; ( cd "$PROJ" && bash "$BIN_RUNTIME/repo-bootstrap.sh" --help >/dev/null 2>&1 ) || RB_RC=$?
|
||||
VP_RC=0; ( cd "$PROJ" && bash "$BIN_RUNTIME/verify-permission-patterns.sh" >/dev/null 2>&1 ) || VP_RC=$?
|
||||
if [ -x "$BIN_RUNTIME/memory-lifecycle.sh" ] \
|
||||
&& [ -x "$BIN_RUNTIME/repo-bootstrap.sh" ] \
|
||||
&& [ -x "$BIN_RUNTIME/verify-permission-patterns.sh" ] \
|
||||
&& [ "$ML_RC" -le 2 ] && [ "$RB_RC" -le 2 ] && [ "$VP_RC" -le 2 ]; then
|
||||
ok "T09 all runtime bins executable and runnable from unrelated project"
|
||||
else
|
||||
fail "T09 all runtime bins executable/runnable" "ml=$ML_RC rb=$RB_RC vp=$VP_RC"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# TEST T10: skills readable from an unrelated project via agent-resolved path
|
||||
# ======================================================================== #
|
||||
# After source deletion, agents resolve skills through $OPENCODE_DEV_AGENT_TEAM.
|
||||
READ_OK=0
|
||||
if [ -f "$RUNTIME/skills/tdd/SKILL.md" ] && grep -q "^## When to use" "$RUNTIME/skills/tdd/SKILL.md"; then
|
||||
READ_OK=1
|
||||
fi
|
||||
if [ "$READ_OK" = "1" ]; then
|
||||
ok "T10 runtime skill readable via OPENCODE_DEV_AGENT_TEAM-resolved path"
|
||||
else
|
||||
fail "T10 runtime skill readable via resolved path" "missing $RUNTIME/skills/tdd/SKILL.md"
|
||||
fi
|
||||
|
||||
# ======================================================================== #
|
||||
# Summary
|
||||
# ======================================================================== #
|
||||
echo
|
||||
echo "==================== SUMMARY ===================="
|
||||
echo "PASS: $PASS FAIL: $FAIL"
|
||||
[ "$FAIL" = "0" ] && echo "RESULT: ALL PASS" || echo "RESULT: FAILURES PRESENT"
|
||||
exit $(( FAIL > 0 ? 1 : 0 ))
|
||||
Reference in New Issue
Block a user