#!/usr/bin/env zsh set -u repo_dir="${0:A:h}" source "$repo_dir/func" failures=0 fixture_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan build launcher.XXXXXX")" || exit 1 source_dir="$(mktemp -d "${TMPDIR:-/tmp}/plan-build-source.XXXXXX")" || exit 1 export PLAN_BUILD_CACHE_DIR="$fixture_dir/cache with spaces" export PLAN_BUILD_BASE_URL="file://$source_dir" export HOME="$fixture_dir/home with spaces" PLAN_BUILD_SKILLS_DIR="$HOME/.claude/skills" mkdir -p "$source_dir" "$PLAN_BUILD_SKILLS_DIR/plan-build" "$PLAN_BUILD_SKILLS_DIR/plan-build-architect" print -r -- "old standard" > "$PLAN_BUILD_SKILLS_DIR/plan-build/SKILL.md" print -r -- "old architect" > "$PLAN_BUILD_SKILLS_DIR/plan-build-architect/SKILL.md" trap 'rm -rf -- "$fixture_dir" "$source_dir"' EXIT fail() { print -u2 -- "FAIL: $1" failures=$((failures + 1)) } assert_equal() { [[ "$1" = "$2" ]] || fail "$3 (expected '$1', got '$2')" } assert_contains() { [[ "$1" == *"$2"* ]] || fail "$3 (missing '$2')" } assert_file_contains() { command grep -Fq -- "$2" "$1" || fail "$3" } assert_bundle_consistent() { local release="$PLAN_BUILD_CACHE_DIR/current" local script_generation skill_generation architect_generation release="${release:A}" [[ -L "$PLAN_BUILD_CACHE_DIR/current" ]] || fail "$1 current is a symlink" [[ -d "$release" ]] || fail "$1 current resolves to a release" script_generation="$(command sed -n 's/^# generation: //p' "$release/plan_build.zsh")" skill_generation="$(command sed -n 's/ standard skill$//p' "$release/SKILL.md")" architect_generation="$(command sed -n 's/ architect skill$//p' "$release/ARCHITECT.md")" assert_equal "$script_generation" "$skill_generation" "$1 script/standard generation" assert_equal "$script_generation" "$architect_generation" "$1 script/architect generation" } assert_skill_links() { [[ -L "$PLAN_BUILD_SKILLS_DIR/plan-build/SKILL.md" ]] || fail "$1 standard path is symlink" [[ -L "$PLAN_BUILD_SKILLS_DIR/plan-build-architect/SKILL.md" ]] || fail "$1 architect path is symlink" assert_equal "$PLAN_BUILD_CACHE_DIR/current/SKILL.md" \ "$(command readlink "$PLAN_BUILD_SKILLS_DIR/plan-build/SKILL.md")" "$1 standard link target" assert_equal "$PLAN_BUILD_CACHE_DIR/current/ARCHITECT.md" \ "$(command readlink "$PLAN_BUILD_SKILLS_DIR/plan-build-architect/SKILL.md")" "$1 architect link target" } write_bundle() { local generation="$1" local exit_code="${2:-0}" local marker_file="${3:-}" print -r -- '#!/usr/bin/env zsh' > "$source_dir/plan_build.zsh" print -r -- "# generation: $generation" >> "$source_dir/plan_build.zsh" print -r -- "print -r -- \"$generation:\${(j:|:)@}\"" >> "$source_dir/plan_build.zsh" print -r -- "print -r -- \"path:\${0}\"" >> "$source_dir/plan_build.zsh" print -r -- 'print -r -- "skills:${PLAN_BUILD_SKILL_PATH}|${PLAN_BUILD_ARCHITECT_SKILL_PATH}"' >> "$source_dir/plan_build.zsh" [[ -n "$marker_file" ]] && print -r -- ": > ${(q)marker_file}" >> "$source_dir/plan_build.zsh" print -r -- "exit $exit_code" >> "$source_dir/plan_build.zsh" print -r -- "$generation standard skill" > "$source_dir/SKILL.md" print -r -- "$generation architect skill" > "$source_dir/ARCHITECT.md" } run_plan_build() { output="$(plan_build "$@" 2>&1)" rc=$? } write_bundle initial run_plan_build alpha "two words" assert_equal 0 "$rc" "initial migration status" assert_contains "$output" "initial:alpha|two words" "argument forwarding" assert_skill_links "initial migration" assert_bundle_consistent "initial migration" assert_file_contains "$PLAN_BUILD_SKILLS_DIR/plan-build/SKILL.md" "initial standard" "initial standard content" assert_file_contains "$PLAN_BUILD_SKILLS_DIR/plan-build-architect/SKILL.md" "initial architect" "initial architect content" execution_path="${${(f)output}[(r)path:*]#path:}" [[ "$execution_path" == "$PLAN_BUILD_CACHE_DIR"/release.*/plan_build.zsh ]] || fail "execution uses an immutable release path" assert_contains "$output" "skills:${execution_path:h}/SKILL.md|${execution_path:h}/ARCHITECT.md" \ "execution binds immutable skill paths" old_release="$PLAN_BUILD_CACHE_DIR/current" old_release="${old_release:A}" write_bundle replacement 23 run_plan_build forwarded assert_equal 23 "$rc" "canonical status forwarding" assert_contains "$output" "replacement:forwarded" "replacement executes" new_release="$PLAN_BUILD_CACHE_DIR/current" new_release="${new_release:A}" [[ "$new_release" != "$old_release" ]] || fail "refresh activates a unique release" assert_skill_links "replacement" assert_bundle_consistent "replacement" print -r -- 'if broken' > "$source_dir/plan_build.zsh" print -r -- "bad standard skill" > "$source_dir/SKILL.md" print -r -- "bad architect skill" > "$source_dir/ARCHITECT.md" run_plan_build fallback assert_equal 23 "$rc" "failed refresh fallback status" assert_contains "$output" "Warning: Unable to refresh" "failed refresh warning" assert_contains "$output" "replacement:fallback" "failed refresh executes current" preserved_release="$PLAN_BUILD_CACHE_DIR/current" preserved_release="${preserved_release:A}" assert_equal "$new_release" "$preserved_release" "failed refresh preserves current" assert_skill_links "failed refresh" assert_bundle_consistent "failed refresh" export PLAN_BUILD_CURL_COMMAND="definitely-missing-plan-build-curl" run_plan_build no-curl assert_equal 23 "$rc" "missing curl fallback status" assert_contains "$output" "curl is unavailable" "missing curl warning" assert_contains "$output" "replacement:no-curl" "missing curl executes current" rm -f "$PLAN_BUILD_SKILLS_DIR/plan-build/SKILL.md" "$PLAN_BUILD_SKILLS_DIR/plan-build-architect/SKILL.md" run_plan_build repaired-offline assert_equal 23 "$rc" "offline skill repair status" assert_skill_links "offline skill repair" unset PLAN_BUILD_CURL_COMMAND # An already selected immutable script must not change beneath a concurrent caller. ready_file="$fixture_dir/ready" go_file="$fixture_dir/go" first_output="$fixture_dir/first.out" print -r -- '#!/usr/bin/env zsh' > "$source_dir/plan_build.zsh" print -r -- '# generation: concurrent_first' >> "$source_dir/plan_build.zsh" print -r -- 'print -r -- "concurrent_first:${(j:|:)@}"' >> "$source_dir/plan_build.zsh" print -r -- ": > ${(q)ready_file}" >> "$source_dir/plan_build.zsh" print -r -- "while [[ ! -e ${(q)go_file} ]]; do sleep 0.01; done" >> "$source_dir/plan_build.zsh" print -r -- 'print -r -- "finished:first"' >> "$source_dir/plan_build.zsh" print -r -- "concurrent_first standard skill" > "$source_dir/SKILL.md" print -r -- "concurrent_first architect skill" > "$source_dir/ARCHITECT.md" (plan_build one > "$first_output" 2>&1) & first_pid=$! for attempt in {1..500}; do [[ -e "$ready_file" ]] && break sleep 0.01 done [[ -e "$ready_file" ]] || fail "first concurrent call reached canonical execution" write_bundle concurrent_second run_plan_build two assert_equal 0 "$rc" "second concurrent status" assert_contains "$output" "concurrent_second:two" "second concurrent generation" : > "$go_file" wait "$first_pid" || fail "first concurrent status" first_text="$(<"$first_output")" assert_contains "$first_text" "concurrent_first:one" "first call retains selected release" assert_contains "$first_text" "finished:first" "first immutable script finishes" assert_bundle_consistent "concurrent final current" assert_skill_links "concurrent final links" # Cancellation while refreshing must clean staging and never execute canonical code. blocking_curl="$fixture_dir/blocking-curl" cancel_started="$fixture_dir/cancel.started" cancel_executed="$fixture_dir/cancel.executed" write_bundle cancellation-marker 0 "$cancel_executed" run_plan_build activate-cancellation-marker assert_equal 0 "$rc" "cancellation marker bundle activation" rm -f "$cancel_executed" { print -r -- '#!/bin/sh' print -r -- "touch ${(q)cancel_started}" print -r -- 'while :; do sleep 1; done' } > "$blocking_curl" chmod +x "$blocking_curl" export PLAN_BUILD_CURL_COMMAND="$blocking_curl" (plan_build cancelled > "$fixture_dir/cancel.out" 2>&1; cancel_rc=$?; print -r -- "$cancel_rc" > "$fixture_dir/cancel.rc"; exit "$cancel_rc") & cancel_pid=$! for attempt in {1..500}; do [[ -e "$cancel_started" ]] && break sleep 0.01 done kill -TERM "$cancel_pid" wait "$cancel_pid" cancel_wait_rc=$? cancel_recorded_rc="" [[ -s "$fixture_dir/cancel.rc" ]] && cancel_recorded_rc="$(<"$fixture_dir/cancel.rc")" [[ "$cancel_wait_rc" = 143 || "$cancel_recorded_rc" = 143 ]] || fail "TERM returns conventional status 143" [[ ! -e "$cancel_executed" ]] || fail "TERM does not execute canonical plan-build" unset PLAN_BUILD_CURL_COMMAND # A pre-existing lock file must not be mistaken for an active kernel lock. print -r -- "stale contents" > "$PLAN_BUILD_CACHE_DIR/.activate.lock" write_bundle stale-lock-recovery run_plan_build recovered assert_equal 0 "$rc" "stale activation lock recovery status" assert_contains "$output" "stale-lock-recovery:recovered" "stale activation lock recovery execution" # Cancellation while canonical code runs must terminate the child promptly. canonical_started="$fixture_dir/canonical.started" canonical_finished="$fixture_dir/canonical.finished" print -r -- '#!/usr/bin/env zsh' > "$source_dir/plan_build.zsh" print -r -- '# generation: canonical-cancel' >> "$source_dir/plan_build.zsh" print -r -- "trap 'exit 143' TERM" >> "$source_dir/plan_build.zsh" print -r -- ": > ${(q)canonical_started}" >> "$source_dir/plan_build.zsh" print -r -- 'while :; do sleep 1; done' >> "$source_dir/plan_build.zsh" print -r -- ": > ${(q)canonical_finished}" >> "$source_dir/plan_build.zsh" print -r -- "canonical-cancel standard skill" > "$source_dir/SKILL.md" print -r -- "canonical-cancel architect skill" > "$source_dir/ARCHITECT.md" (plan_build canonical-cancel > "$fixture_dir/canonical-cancel.out" 2>&1; print -r -- "$?" > "$fixture_dir/canonical-cancel.rc") & canonical_pid=$! for attempt in {1..500}; do [[ -e "$canonical_started" ]] && break sleep 0.01 done [[ -e "$canonical_started" ]] || fail "canonical cancellation reached execution" kill -TERM "$canonical_pid" wait "$canonical_pid" assert_equal 143 "$(<"$fixture_dir/canonical-cancel.rc")" "canonical cancellation status" [[ ! -e "$canonical_finished" ]] || fail "canonical child continued after TERM" temp_count="$(command find "$PLAN_BUILD_CACHE_DIR" -maxdepth 1 \ \( -name '.stage.*' -o -name '.current.*' \) -print | command wc -l | command tr -d ' ')" assert_equal 0 "$temp_count" "cache staging and temporary links cleaned" skill_temp_count="$(command find "$PLAN_BUILD_SKILLS_DIR" \ \( -name '*.link.*' -o -name '*.backup.*' \) -print | command wc -l | command tr -d ' ')" assert_equal 0 "$skill_temp_count" "skill temporary links and backups cleaned" if (( failures )); then exit 1 fi print -- "PASS: plan_build launcher tests"