From 03be6211ed1c14e005f38604fecdde90cd414e6d Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 8 Sep 2026 06:04:47 -0400 Subject: [PATCH] Add test-all.sh runner; fix bootstrap idempotency flake and test isolation - test-all.sh: single entry point running all four test suites (51 checks), non-zero exit on any failure, --verbose shows failing suite output. - repo-bootstrap.sh: write_meta now compares stable fields excluding the second-resolution generated_at timestamp, so two runs straddling a second boundary no longer rewrite .bootstrap-meta (T02 idempotency flake fixed). - test-integration.sh: pre-clean memory residue before I01/I02 so checks are independent of prior interrupted-run state. - README.md: document test-all.sh as the top-level test entry point. --- README.md | 26 +++++++++++---- scripts/repo-bootstrap.sh | 18 +++++++---- scripts/test-all.sh | 64 +++++++++++++++++++++++++++++++++++++ scripts/test-integration.sh | 6 ++++ 4 files changed, 101 insertions(+), 13 deletions(-) create mode 100755 scripts/test-all.sh diff --git a/README.md b/README.md index d151e37..6039d85 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,11 @@ dev_agent_team/ │ ├── install.sh # one-command installer │ ├── repo-bootstrap.sh # repository intelligence bootstrap tool │ ├── memory-lifecycle.sh # memory CRUD operations +│ ├── test-all.sh # single entry point that runs all test suites │ ├── test-repo-bootstrap.sh # test suite for the bootstrap │ ├── test-agent-architecture.sh # structural tests for the agent architecture │ ├── test-memory-system.sh # structural tests for memory/skills/improvements +│ ├── test-integration.sh # end-to-end memory/skills/lifecycle integration tests │ └── verify-permission-patterns.sh # permission engine verifier └── docs/ ├── PROMPT_INSTALL.md # paste-ready prompt for installing from inside opencode @@ -136,15 +138,25 @@ repo-bootstrap.sh bootstrap repo-bootstrap.sh refresh ``` -Run `bash scripts/test-repo-bootstrap.sh` to verify bootstrap behavior -(11 tests covering all 10 acceptance criteria). +Run all test suites with a single command (aggregates the four suites below): -Run `bash scripts/test-agent-architecture.sh` to verify the agent architecture -contains the required adaptive/evidence-driven elements -(16 structural tests). +```bash +bash scripts/test-all.sh +``` -Run `bash scripts/test-memory-system.sh` to verify the memory, skills, and -improvement systems are structurally sound (12 tests). +51 individual checks across 4 suites (16 architecture + 12 memory + 11 bootstrap ++ 12 integration). Any suite failing makes the overall exit code non-zero. + +Individual suites: + +- `test-agent-architecture.sh` — architecture contains the required + adaptive/evidence-driven elements (16 structural tests). +- `test-memory-system.sh` — memory, skills, and improvement systems are + structurally sound (12 tests). +- `test-repo-bootstrap.sh` — bootstrap behavior and idempotency + (11 tests covering all 10 acceptance criteria). +- `test-integration.sh` — end-to-end memory/skills/improvements integration + (12 tests). ## Manual install alternative diff --git a/scripts/repo-bootstrap.sh b/scripts/repo-bootstrap.sh index 2f09b34..f9b1e41 100755 --- a/scripts/repo-bootstrap.sh +++ b/scripts/repo-bootstrap.sh @@ -421,22 +421,28 @@ write_meta() { # write_meta local root="$1" mkdir -p "$root/.opencode" current_fingerprint "$root" - local new_meta - new_meta="$(printf '%s\n' \ + # Compare only the stable fields (excluding generated_at) so a second run in an + # unchanged repo is a true no-op — even across a second boundary — and the + # original timestamp is preserved. Without this, idempotency flakes whenever + # two runs straddle a second boundary (generated_at would otherwise differ). + local stable + stable="$(printf '%s\n' \ "schema=1" \ "tool=dev_agent_team-repo-bootstrap" \ "version=$VERSION" \ -"generated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ "git_head=$GIT_HEAD" \ "manifest_fingerprint=$MANIFEST_FP" \ "top_level_fingerprint=$TOPLEVEL_FP" \ "signals=$SIGNAL_TXT")" - # Idempotent: skip rewrite if content is identical (preserves timestamp). - local existing="" + local existing="" existing_stable="" [ -f "$root/.opencode/$META_FILE" ] && existing="$(cat "$root/.opencode/$META_FILE")" - if [ "$existing" = "$new_meta" ]; then + [ -n "$existing" ] && existing_stable="$(printf '%s\n' "$existing" | grep -v '^generated_at=' || true)" + if [ "$existing_stable" = "$stable" ]; then return 0 fi + local new_meta + new_meta="$(printf '%s\n' "$stable" \ +"generated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)")" printf '%s\n' "$new_meta" > "$root/.opencode/$META_FILE" } diff --git a/scripts/test-all.sh b/scripts/test-all.sh new file mode 100755 index 0000000..16c07e4 --- /dev/null +++ b/scripts/test-all.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# +# test-all.sh — Run every test suite in the repo and report a combined result. +# +# bash scripts/test-all.sh +# +# Exit code: 0 if all suites pass, non-zero if any suite fails. +# Optional: pass --verbose to show each suite's full output on failure. + +set -uo pipefail +TEAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +VERBOSE=0 +[ "${1:-}" = "--verbose" ] && VERBOSE=1 + +SUITES=(test-agent-architecture test-memory-system test-repo-bootstrap test-integration) + +BOLD="" +RESET="" +if [ -t 1 ]; then + BOLD="\033[1m" + RESET="\033[0m" +fi + +pass=0; fail=0 +printf "${BOLD}=== Agent Team: full test run ===${RESET}\n\n" + +for s in "${SUITES[@]}"; do + script="$TEAM_ROOT/scripts/$s.sh" + if [ ! -f "$script" ]; then + printf " %-28s ${BOLD}MISSING${RESET}\n" "$s" + fail=$((fail + 1)) + continue + fi + out="$(bash "$script" 2>&1)" + rc=$? + + # Count individual PASS/FAIL lines (uniform format: "PASS T01 ..." / "PASS I01 ..."; + # excludes the summary lines that also start with "PASS:"/"FAIL:"). + npass="$(printf '%s\n' "$out" | grep -cE '^PASS [TI]' || true)" + nfail="$(printf '%s\n' "$out" | grep -cE '^FAIL [TI]' || true)" + + if [ "$rc" = "0" ]; then + printf " %-28s ${BOLD}PASS${RESET} (%s checks)\n" "$s" "$npass" + pass=$((pass + 1)) + else + printf " %-28s ${BOLD}FAIL${RESET} (%s check(s) failed)\n" "$s" "$nfail" + fail=$((fail + 1)) + if [ "$VERBOSE" = "1" ]; then + printf '\n--- %s output ---\n%s\n--------------------------\n' "$s" "$out" + fi + fi +done + +printf '\n%s=== RESULT ===%s\n' "$BOLD" "$RESET" +printf 'suites passed: %d suites failed: %d (total %d suites)\n' \ + "$pass" "$fail" "${#SUITES[@]}" + +if [ "$fail" = "0" ]; then + printf 'ALL SUITES PASS\n' + exit 0 +else + printf 'FAILURES PRESENT\n' + exit 1 +fi \ No newline at end of file diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 8e759c7..e1bc1cc 100644 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -56,6 +56,10 @@ SC_OK=0 # into `grep -q`. grep -q closes the pipe early (SIGPIPE, rc=141), which fails # the pipeline under `pipefail`. STORE_OK=0 +# Test isolation: remove any residue a previous interrupted/crashed run may have +# left behind before storing. Writing a fresh entry makes the check independent +# of the memory directory's pre-existing state. +rm -f "$MEMORY/lessons/"*integration-test* 2>/dev/null || true if bash "$LS" store lessons "$LESSON_FILE" >/dev/null 2>&1; then STORE_OK=1 fi @@ -80,6 +84,8 @@ fi # TEST I02: Session lifecycle — create, list, cleanup # ======================================================================== # Sess="$MEMORY/sessions/2026-09-08_integration-session-test.md" +# Test isolation: remove any residue a previous interrupted/crashed run left. +rm -f "$Sess" 2>/dev/null || true printf -- '%s\n' "Status: active" "# Integration session test" "## State" "in progress" > "$Sess" SESS_OUT="$(bash "$LS" sessions 2>&1 || true)" SESS_OK=0