#!/usr/bin/env zsh set -u typeset -r repo_dir="${0:A:h}" source "$repo_dir/plan_build.zsh" typeset -gi failures=0 functions[_plan_build_real_launch_claude]="${functions[_plan_build_launch_claude]}" functions[_plan_build_real_enhance_prompt]="${functions[_plan_build_enhance_prompt]}" functions[_plan_build_real_start_auggie_script]="${functions[_plan_build_start_auggie_script]}" functions[_plan_build_real_agentmemory_check]="${functions[_plan_build_agentmemory_check]}" fail() { print -u2 -r -- "FAIL: $1" failures=$((failures + 1)) } assert_equal() { local expected="$1" actual="$2" label="$3" [[ "$actual" == "$expected" ]] || fail "$label (expected '$expected', got '$actual')" } assert_contains() { local output="$1" expected="$2" label="$3" [[ "$output" == *"$expected"* ]] || fail "$label (missing '$expected')" } assert_not_contains() { local output="$1" unexpected="$2" label="$3" [[ "$output" != *"$unexpected"* ]] || fail "$label (unexpected '$unexpected')" } run_plan() { local args="$1" input="${2:-EOF\n}" output="$(printf '%b' "$input" | plan_build ${(z)args} 2>&1)" rc=$? } seam_events() { print -r -- "$1" | sed -n 's/^SEAM://p' | paste -sd ' ' - } count_matches() { local directory="$1" prefix="$2" local -a matches matches=("$directory"/"$prefix".*(N)) print -r -- "$#matches" } typeset fixture_dir fixture_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan-build-tests.XXXXXX")" || exit 1 trap 'rm -rf -- "$fixture_dir"' EXIT mkdir -p "$fixture_dir/tmp" export TMPDIR="$fixture_dir/tmp" command git init -q "$fixture_dir/worktree" command git init -q --bare "$fixture_dir/bare.git" (cd "$fixture_dir/worktree" && _plan_build_is_worktree) assert_equal 0 "$?" "normal Git worktree accepted" (cd "$fixture_dir/bare.git" && _plan_build_is_worktree) assert_equal 1 "$?" "bare Git repository rejected" (cd "$fixture_dir" && _plan_build_is_worktree) assert_equal 1 "$?" "non-repository rejected" plugin_state() { print -r -- "$1" | _plan_build_claude_plugin_state "$2" } assert_equal enabled "$(plugin_state $' ❯ agentmemory@agentmemory\n Version: 1\n Status: ✔ enabled' 'agentmemory@agentmemory')" "enabled Claude plugin" assert_equal disabled "$(plugin_state $' ❯ mattpocock-skills@mattpocock\n Status: disabled' 'mattpocock-skills@mattpocock')" "disabled Claude plugin" assert_equal missing "$(plugin_state $' ❯ other@market\n Status: ✔ enabled' 'agentmemory@agentmemory')" "missing Claude plugin" assert_equal installed "$(plugin_state ' ❯ agentmemory@agentmemory' 'agentmemory@agentmemory')" "Claude plugin without status" assert_equal enabled "$(print -r -- 'agentmemory@agentmemory installed, enabled 1.0 /tmp/plugin' | _plan_build_codex_plugin_state)" "enabled Codex plugin" assert_equal disabled "$(print -r -- $'agentmemory@agentmemory installed, disabled 1.0 /tmp/plugin\nother@market installed, enabled 1.0 /tmp/other' | _plan_build_codex_plugin_state)" "disabled Codex plugin is not confused by later plugin" assert_equal missing "$(print -r -- 'other@market installed, enabled 1.0 /tmp/other' | _plan_build_codex_plugin_state)" "missing Codex plugin" _plan_build_project_id_is_valid "github.com/example/project" assert_equal 0 "$?" "valid project ID" _plan_build_project_id_is_valid "invalid project id" assert_equal 1 "$?" "project ID rejects spaces" _plan_build_project_id_is_valid "x" assert_equal 1 "$?" "project ID minimum length" command git -C "$fixture_dir/worktree" remote add origin \ "https://opengist.example/owner/project.git" assert_equal "opengist.example/owner/project" \ "$(_plan_build_default_project_id "$fixture_dir/worktree")" \ "HTTPS remote normalizes to stable project ID" command git -C "$fixture_dir/worktree" remote set-url origin \ "git@opengist.example:owner/project.git" assert_equal "opengist.example/owner/project" \ "$(_plan_build_default_project_id "$fixture_dir/worktree")" \ "SSH remote normalizes to stable project ID" command git -C "$fixture_dir/worktree" remote set-url origin \ "https://opengist.example:8443/owner/project.git" assert_equal "opengist.example:8443/owner/project" \ "$(_plan_build_default_project_id "$fixture_dir/worktree")" \ "HTTPS remote preserves port in stable project ID" print -r -- "github.com/example/project" >| "$fixture_dir/worktree/.agentmemory-project" command git -C "$fixture_dir/worktree" add .agentmemory-project command git -C "$fixture_dir/worktree" -c user.name=Test -c user.email=test@example.invalid \ commit -qm "test: add project identity" project_id_output="$(cd "$fixture_dir/worktree" && _plan_build_resolve_project_id && print -r -- "$REPLY")" assert_equal "github.com/example/project" "$project_id_output" "committed project ID resolves" print -r -- "invalid project id" >| "$fixture_dir/worktree/.agentmemory-project" (cd "$fixture_dir/worktree" && _plan_build_resolve_project_id) >/dev/null 2>&1 assert_equal 1 "$?" "invalid committed project ID rejected" print -r -- "github.com/example/other" >| "$fixture_dir/worktree/.agentmemory-project" (cd "$fixture_dir/worktree" && _plan_build_resolve_project_id) >/dev/null 2>&1 assert_equal 1 "$?" "changed project ID rejected until committed" rm -f -- "$fixture_dir/worktree/.agentmemory-project" _plan_build_has_tty() { return 1 } (cd "$fixture_dir/worktree" && _plan_build_resolve_project_id) >/dev/null 2>&1 assert_equal 1 "$?" "missing project ID fails without terminal" unfunction _plan_build_has_tty typeset args expected label for args expected label in \ "--v2" "Unknown argument: --v2" "v2 rejection" \ "--unknown" "Unknown argument: --unknown" "unknown option" \ "--yolo --yolo" "Duplicate argument: --yolo" "duplicate yolo" \ "--prompt --prompt" "Duplicate argument: --prompt" "duplicate prompt" \ "--brainstorm" "Unknown argument: --brainstorm" "removed brainstorm" \ "--writing-plan" "Unknown argument: --writing-plan" "removed writing plan" \ "--architect --architect" "Duplicate argument: --architect" "duplicate architect" \ "--architect --new --new" "Duplicate argument: --new" "duplicate new" \ "--new" "--new requires --architect" "orphan new" \ "--architect --prompt" "may combine only with --new and --yolo" "architect prompt conflict"; do run_plan "$args" assert_equal 1 "$rc" "$label status" assert_contains "$output" "$expected" "$label message" done export CLAUDE_CODE_SAFE_MODE=1 output="$(_plan_build_safe_mode_preflight 2>&1)" rc=$? unset CLAUDE_CODE_SAFE_MODE assert_equal 1 "$rc" "safe mode failure" assert_contains "$output" "safe mode disables the Matt Pocock and AgentMemory plugins" "safe mode guidance" _plan_build_memory_action_from_reply retry assert_equal 2 "$?" "memory action parses retry" _plan_build_memory_action_from_reply continue assert_equal 0 "$?" "memory action parses continue" _plan_build_memory_action_from_reply stop assert_equal 1 "$?" "memory action parses stop" _plan_build_agentmemory_health() { return 0 } _plan_build_claude_agentmemory_state() { print -r -- enabled } _plan_build_codex_agentmemory_state() { print -r -- enabled } _plan_build_real_agentmemory_check >/dev/null assert_equal 0 "$?" "AgentMemory check accepts healthy shared setup" _plan_build_codex_agentmemory_state() { print -r -- disabled } _plan_build_real_agentmemory_check >/dev/null assert_equal 1 "$?" "AgentMemory check rejects disabled Codex plugin" typeset -gi memory_check_attempts=0 _plan_build_agentmemory_check() { memory_check_attempts=$((memory_check_attempts + 1)) (( memory_check_attempts >= 2 )) } _plan_build_confirm_memory_action() { return 2 } _plan_build_agentmemory_preflight >/dev/null assert_equal 0 "$?" "memory retry can recover" assert_equal 2 "$memory_check_attempts" "memory retry repeats health check" assert_equal required "$REPLY" "recovered memory remains required" _plan_build_agentmemory_check() { return 1 } _plan_build_confirm_memory_action() { return 0 } _plan_build_agentmemory_preflight >/dev/null assert_equal 0 "$?" "approved degraded memory status" assert_equal degraded "$REPLY" "approved degraded memory mode" _plan_build_confirm_memory_action() { return 1 } _plan_build_agentmemory_preflight >/dev/null assert_equal 1 "$?" "rejected degraded memory status" unfunction _plan_build_agentmemory_check _plan_build_confirm_memory_action mkdir -p "$fixture_dir/bin" "$fixture_dir/home/.augment" "$fixture_dir/enhance-tmp" print -r -- '{}' >| "$fixture_dir/home/.augment/session.json" print -r -- '#!/bin/sh' >| "$fixture_dir/bin/script" print -r -- 'printf "%s\n" "$@" > "$PLAN_BUILD_SCRIPT_ARGS"' >> "$fixture_dir/bin/script" print -r -- 'if [ "$1" = -q ] && [ "$2" = -t ]; then log_file=$4; shift 4; "$@" > "$log_file"; exit $?; fi' >> "$fixture_dir/bin/script" print -r -- 'log_file=$5; printf "Enhanced prompt: linux result\n" > "$log_file"; exit "${PLAN_BUILD_SCRIPT_STATUS:-0}"' >> "$fixture_dir/bin/script" print -r -- '#!/bin/sh' >| "$fixture_dir/bin/auggie" print -r -- 'printf "Enhanced prompt: mac result\n"; exit "${PLAN_BUILD_AUGGIE_STATUS:-0}"' >> "$fixture_dir/bin/auggie" chmod +x "$fixture_dir/bin/script" "$fixture_dir/bin/auggie" _plan_build_confirm_indexing() { return 1 } typeset platform script_args enhance_output trap 'print -u2 -r -- "test INT trap"' INT typeset caller_int_trap="$(trap -p INT)" for platform in Darwin Linux; do _plan_build_platform() { print -r -- "$platform" } : >| "$fixture_dir/script-args" enhance_output="$fixture_dir/enhanced-$platform" HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ _plan_build_real_enhance_prompt "raw prompt" "$enhance_output" rc=$? assert_equal 0 "$rc" "$platform script enhancement status" script_args="$(<"$fixture_dir/script-args")" if [[ "$platform" == Darwin ]]; then assert_contains "$script_args" $'-t\n0' "macOS script immediate flushing" assert_contains "$script_args" $'auggie\n--print' "macOS script direct executable dispatch" assert_not_contains "$script_args" $'\ncommand\n' "macOS script excludes shell builtin" assert_equal "mac result" "$(<"$enhance_output")" "macOS parsed enhancement" else assert_contains "$script_args" $'-q\n-f' "Linux script immediate flushing" assert_contains "$script_args" $'-e\n-O' "Linux script propagates child status" assert_contains "$script_args" "-c" "Linux script command mode" assert_equal "linux result" "$(<"$enhance_output")" "Linux parsed enhancement" fi assert_equal 0 "$(count_matches "$fixture_dir/enhance-tmp" plan-build-auggie)" "$platform run directory cleanup" done assert_equal "$caller_int_trap" "$(trap -p INT)" "enhancement traps remain local to caller" trap - INT platform=Darwin _plan_build_platform() { print -r -- "$platform" } PLAN_BUILD_AUGGIE_STATUS=7 HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ _plan_build_real_enhance_prompt "partial prompt" "$fixture_dir/failed-enhancement" >/dev/null 2>&1 rc=$? assert_equal 7 "$rc" "nonzero Auggie status propagated" [[ ! -e "$fixture_dir/failed-enhancement" ]] || fail "failed Auggie output was accepted" assert_equal 0 "$(count_matches "$fixture_dir/enhance-tmp" plan-build-auggie)" "failed run directory cleanup" platform=Linux PLAN_BUILD_SCRIPT_STATUS=7 HOME="$fixture_dir/home" TMPDIR="$fixture_dir/enhance-tmp" \ PATH="$fixture_dir/bin:$PATH" PLAN_BUILD_SCRIPT_ARGS="$fixture_dir/script-args" \ _plan_build_real_enhance_prompt "partial prompt" "$fixture_dir/failed-linux-enhancement" >/dev/null 2>&1 rc=$? assert_equal 7 "$rc" "Linux script child status propagated" [[ ! -e "$fixture_dir/failed-linux-enhancement" ]] || fail "failed Linux Auggie output was accepted" print -r -- $'noise\r\n\e[32m✨ Enhanced prompt: first\e[0m\r\nsecond\r\n🤖 tool' >| "$fixture_dir/parser.log" _plan_build_parse_auggie_output "$fixture_dir/parser.log" "$fixture_dir/parser.out" assert_equal $'first\nsecond' "$(<"$fixture_dir/parser.out")" "parser strips terminal output and stops at marker" print -r -- "no enhanced prompt" >| "$fixture_dir/parser-empty.log" _plan_build_parse_auggie_output "$fixture_dir/parser-empty.log" "$fixture_dir/parser-empty.out" [[ ! -s "$fixture_dir/parser-empty.out" ]] || fail "parser accepted output without marker" typeset -g enhanced_text="enhanced payload" typeset -gi enhanced_approval=0 typeset -gi common_preflight_result=0 typeset -gi enhancement_result=0 _plan_build_require_cli() { print -r -- "SEAM:cli:$1" return 0 } _plan_build_common_preflight() { print -r -- "SEAM:common:$2" return "$common_preflight_result" } _plan_build_resolve_project_id() { print -r -- "SEAM:project-id" REPLY="github.com/example/project" } _plan_build_agentmemory_preflight() { print -r -- "SEAM:memory" REPLY="required" } _plan_build_enhance_prompt() { print -r -- "SEAM:auggie:$1" print -r -- "$enhanced_text" >| "$2" return "$enhancement_result" } _plan_build_confirm_enhanced_prompt() { print -r -- "SEAM:enhanced-approval" return "$enhanced_approval" } _plan_build_launch_claude() { print -r -- "SEAM:claude" printf 'CLAUDE_YOLO=<%s>\nCLAUDE_PROJECT=<%s>\nCLAUDE_MEMORY=<%s>\nCLAUDE_PROMPT=<%s>\n' "$1" "$2" "$3" "$4" } run_plan "" "first line\nsecond line\nEOF\nignored line\n" assert_equal 0 "$rc" "Matt default mode status" assert_contains "$output" "memory-aware Matt Pocock workflow" "Matt workflow prompt" assert_contains "$output" "AgentMemory project: github.com/example/project" "memory project prompt" assert_contains "$output" "Run the mandatory recall gate" "recall gate prompt" assert_contains "$output" $'first line\nsecond line' "multiline payload" assert_not_contains "$output" "ignored line" "EOF terminates payload" assert_equal "common:1 project-id memory claude" "$(seam_events "$output")" "default preflight ordering" enhanced_approval=0 run_plan "--prompt" "raw payload\nEOF\n" assert_equal 0 "$rc" "prompt mode approved status" assert_contains "$output" "enhanced payload" "enhanced prompt used" assert_contains "$output" "Enhanced prompt approved" "enhanced approval reported" assert_equal "common:1 cli:auggie cli:script cli:perl project-id memory auggie:raw payload enhanced-approval claude" "$(seam_events "$output")" "prompt seams ordering" enhancement_result=9 run_plan "--prompt" "failed child\nEOF\n" assert_equal 9 "$rc" "prompt child failure status propagated" assert_not_contains "$output" "enhanced-approval" "failed enhancement is never parsed or approved" assert_not_contains "$output" "SEAM:claude" "failed enhancement prevents launch" assert_equal 0 "$(count_matches "$TMPDIR" plan-build-enhanced)" "enhanced temporary file cleanup" enhancement_result=0 enhanced_approval=1 run_plan "--prompt" "declined\nEOF\n" assert_equal 0 "$rc" "prompt rejection exits cleanly" assert_contains "$output" "not approved" "prompt rejection message" assert_equal "common:1 cli:auggie cli:script cli:perl project-id memory auggie:declined enhanced-approval" "$(seam_events "$output")" "prompt rejection prevents launch" enhanced_approval=0 run_plan "--architect" "design feature\nEOF\n" assert_equal 0 "$rc" "architect safe-resume status" assert_contains "$output" "Start mode: safe-resume-detection" "architect safe-resume mode" assert_contains "$output" "documentation-first architect/orchestrator role" "architect role boundary" assert_contains "$output" "Run the skill's mandatory recall gate" "architect recall gate prompt" assert_equal "common:0 project-id memory claude" "$(seam_events "$output")" "architect preflight before launch" run_plan "--architect --new" "fresh design\nEOF\n" assert_equal 0 "$rc" "architect new status" assert_contains "$output" "Start mode: archive-and-start-new" "architect new mode" run_plan "--architect --yolo" "fast design\nEOF\n" assert_equal 0 "$rc" "architect yolo status" assert_contains "$output" "CLAUDE_YOLO=<1>" "architect yolo reaches launch seam" assert_contains "$output" "including every gate" "architect yolo retains gates" common_preflight_result=1 run_plan "--architect" "must not be read\nEOF\n" assert_equal 1 "$rc" "architect preflight failure status" assert_not_contains "$output" "Reading payload" "architect preflight precedes payload" assert_equal "common:0" "$(seam_events "$output")" "architect failure stops processing" common_preflight_result=0 run_plan "" "EOF\n" assert_equal 1 "$rc" "empty payload status" assert_contains "$output" "Payload was empty" "empty payload message" print -r -- '#!/bin/sh' >| "$fixture_dir/bin/claude" print -r -- 'printf "PROJECT=<%s> MEMORY=<%s> INJECT=<%s> REAL_CLAUDE:" "$AGENTMEMORY_PROJECT_NAME" "$PLAN_BUILD_MEMORY_MODE" "$AGENTMEMORY_INJECT_CONTEXT"; printf " <%s>" "$@"; printf "\n"' >> "$fixture_dir/bin/claude" chmod +x "$fixture_dir/bin/claude" output="$(PATH="$fixture_dir/bin:$PATH" _plan_build_real_launch_claude 1 "github.com/example/project" required "payload")" assert_contains "$output" "<--dangerously-skip-permissions>" "yolo translates to real Claude argument" assert_not_contains "$output" "<--yolo>" "yolo alias not forwarded" assert_contains "$output" "PROJECT=" "project ID reaches Claude environment" assert_contains "$output" "MEMORY=" "memory mode reaches Claude environment" assert_contains "$output" "INJECT=" "context injection reaches Claude environment" output="$(PATH="$fixture_dir/bin:$PATH" _plan_build_real_launch_claude 0 "github.com/example/project" degraded "payload")" assert_contains "$output" "MEMORY=" "degraded memory mode reaches Claude environment" assert_contains "$output" "INJECT=" "degraded mode disables context injection" if (( failures )); then print -u2 -r -- "FAIL: $failures plan_build test(s)" exit 1 fi print -r -- "PASS: plan_build tests"