diff --git a/docs/install-for-codex.md b/docs/install-for-codex.md index 2c70a1c..37de3ea 100644 --- a/docs/install-for-codex.md +++ b/docs/install-for-codex.md @@ -26,12 +26,12 @@ This will: - Sync `humanize`, `humanize-gen-plan`, `humanize-refine-plan`, and `humanize-rlcr` into `${CODEX_HOME:-~/.codex}/skills` - Copy runtime dependencies into `${CODEX_HOME:-~/.codex}/skills/humanize` - Install/update native Humanize Stop hooks in `${CODEX_HOME:-~/.codex}/hooks.json` -- Enable the experimental `codex_hooks` feature in `${CODEX_HOME:-~/.codex}/config.toml` when `codex` is available +- Enable the native `hooks` feature in `${CODEX_HOME:-~/.codex}/config.toml` when `codex` is available - Seed `~/.config/humanize/config.json` with a Codex/OpenAI `bitlesson_model` when that key is not already set - Mark the install as `provider_mode: "codex-only"` when using `--target codex` - Use RLCR defaults: `codex exec` with `gpt-5.5:high`, `codex review` with `gpt-5.5:high` -Requires Codex CLI `0.114.0` or newer for native hooks. Older Codex builds are not supported by the Codex install path. +Requires Codex CLI `0.114.0` or newer for native hooks. The hooks feature was renamed to `hooks`; older Codex builds that still expose `codex_hooks` are not supported by the Codex install path. ## Verify @@ -70,12 +70,12 @@ Installed files/directories: Verify native hooks: ```bash -codex features list | rg codex_hooks +codex features list | rg '^hooks\s' sed -n '1,220p' "${CODEX_HOME:-$HOME/.codex}/hooks.json" ``` Expected: -- `codex_hooks` is `true` +- `hooks` is present in `codex features list` - `hooks.json` contains `loop-codex-stop-hook.sh` - `${XDG_CONFIG_HOME:-~/.config}/humanize/config.json` contains `bitlesson_model` set to a Codex/OpenAI model such as `gpt-5.5` - for `--target codex`, `${XDG_CONFIG_HOME:-~/.config}/humanize/config.json` also contains `provider_mode: "codex-only"` @@ -110,6 +110,8 @@ ls -la "${CODEX_HOME:-$HOME/.codex}/skills/humanize/scripts" If native exit gating does not trigger: ```bash -codex features enable codex_hooks +codex features enable hooks sed -n '1,220p' "${CODEX_HOME:-$HOME/.codex}/hooks.json" ``` + +If the installer reports that your config or installed Codex still uses `codex_hooks`, upgrade Codex first or change `${CODEX_HOME:-~/.codex}/config.toml` to `[features]\nhooks = true`. diff --git a/hooks/loop-codex-stop-hook.sh b/hooks/loop-codex-stop-hook.sh index 0c191d4..c15c300 100755 --- a/hooks/loop-codex-stop-hook.sh +++ b/hooks/loop-codex-stop-hook.sh @@ -1172,9 +1172,9 @@ mkdir -p "$CACHE_DIR" CODEX_DISABLE_HOOKS_ARGS=() _CODEX_FEATURE_CACHE="$CACHE_DIR/.codex-disable-hooks-supported" if [[ -f "$_CODEX_FEATURE_CACHE" ]]; then - [[ "$(cat "$_CODEX_FEATURE_CACHE")" == "yes" ]] && CODEX_DISABLE_HOOKS_ARGS=(--disable codex_hooks) + [[ "$(cat "$_CODEX_FEATURE_CACHE")" == "yes" ]] && CODEX_DISABLE_HOOKS_ARGS=(--disable hooks) elif codex --help 2>&1 | grep -q -- '--disable'; then - CODEX_DISABLE_HOOKS_ARGS=(--disable codex_hooks) + CODEX_DISABLE_HOOKS_ARGS=(--disable hooks) echo "yes" > "$_CODEX_FEATURE_CACHE" 2>/dev/null else echo "no" > "$_CODEX_FEATURE_CACHE" 2>/dev/null diff --git a/scripts/bitlesson-select.sh b/scripts/bitlesson-select.sh index fd19a44..1f781f5 100755 --- a/scripts/bitlesson-select.sh +++ b/scripts/bitlesson-select.sh @@ -193,7 +193,7 @@ run_selector() { local codex_exec_args=() # Probe whether the installed Codex CLI supports --disable flag if codex --help 2>&1 | grep -q -- '--disable'; then - codex_exec_args+=("--disable" "codex_hooks") + codex_exec_args+=("--disable" "hooks") fi # Probe for --skip-git-repo-check and --ephemeral support if codex exec --help 2>&1 | grep -q -- '--skip-git-repo-check'; then diff --git a/scripts/install-codex-hooks.sh b/scripts/install-codex-hooks.sh index 407fe66..87dcfc3 100755 --- a/scripts/install-codex-hooks.sh +++ b/scripts/install-codex-hooks.sh @@ -12,6 +12,7 @@ RUNTIME_ROOT="$CODEX_CONFIG_DIR/skills/humanize" DRY_RUN="false" ENABLE_FEATURE="true" HOOKS_TEMPLATE="$REPO_ROOT/config/codex-hooks.json" +HOOK_FEATURE_ENABLED="" usage() { cat <<'EOF' @@ -23,7 +24,7 @@ Usage: Options: --codex-config-dir PATH Codex config dir (default: ${CODEX_HOME:-~/.codex}) --runtime-root PATH Installed Humanize runtime root (default: /skills/humanize) - --skip-enable-feature Do not run `codex features enable codex_hooks` + --skip-enable-feature Do not run `codex features enable hooks` --dry-run Print actions without writing -h, --help Show help EOF @@ -72,14 +73,40 @@ done HOOKS_FILE="$CODEX_CONFIG_DIR/hooks.json" -require_codex_hooks_support() { +config_uses_legacy_codex_hooks() { + local config_file="$CODEX_CONFIG_DIR/config.toml" + + [[ -f "$config_file" ]] || return 1 + + grep -Eq '^[[:space:]]*(features\.)?codex_hooks[[:space:]]*=' "$config_file" +} + +require_native_hooks_support() { if ! command -v codex >/dev/null 2>&1; then die "Codex CLI with native hooks support is required. Install Codex 0.114.0+ first." fi - if ! codex features list 2>/dev/null | grep -qE '^codex_hooks[[:space:]]'; then - die "Installed Codex CLI does not expose the codex_hooks feature. Humanize Codex install requires Codex 0.114.0+." + if config_uses_legacy_codex_hooks; then + die "Codex config uses the legacy feature key 'codex_hooks'. Current Codex uses 'hooks'. Update $CODEX_CONFIG_DIR/config.toml to use 'hooks = true' under [features], or upgrade Codex if 'codex features list' does not show 'hooks'." + fi + + local features + local line + features="$(CODEX_HOME="$CODEX_CONFIG_DIR" codex features list 2>/dev/null)" || { + die "failed to inspect Codex features. Humanize Codex install requires the native 'hooks' feature." + } + + line="$(printf '%s\n' "$features" | awk '$1 == "hooks" { print; exit }')" + if [[ -n "$line" ]]; then + HOOK_FEATURE_ENABLED="$(awk '{ print $NF }' <<<"$line")" + return 0 + fi + + if printf '%s\n' "$features" | awk '$1 == "codex_hooks" { found = 1 } END { exit found ? 0 : 1 }'; then + die "Installed Codex exposes only the legacy 'codex_hooks' feature. Humanize now requires the renamed 'hooks' feature. Upgrade Codex, then rerun the installer." fi + + die "Installed Codex CLI does not expose the native 'hooks' feature. Upgrade Codex, then rerun the installer." } merge_hooks_json() { @@ -177,10 +204,15 @@ enable_feature() { [[ "$ENABLE_FEATURE" == "true" ]] || return 0 - if CODEX_HOME="$config_dir" codex features enable codex_hooks >/dev/null 2>&1; then - log "enabled codex_hooks feature in $config_dir/config.toml" + if [[ "$HOOK_FEATURE_ENABLED" == "true" ]]; then + log "native hooks feature already enabled in $config_dir/config.toml" + return 0 + fi + + if CODEX_HOME="$config_dir" codex features enable hooks >/dev/null 2>&1; then + log "enabled hooks feature in $config_dir/config.toml" else - die "failed to enable codex_hooks feature automatically in $config_dir/config.toml" + die "failed to enable hooks feature automatically in $config_dir/config.toml" fi } @@ -188,12 +220,12 @@ log "codex config dir: $CODEX_CONFIG_DIR" log "runtime root: $RUNTIME_ROOT" log "hooks file: $HOOKS_FILE" -require_codex_hooks_support +require_native_hooks_support if [[ "$DRY_RUN" == "true" ]]; then log "DRY-RUN merge $HOOKS_TEMPLATE -> $HOOKS_FILE" if [[ "$ENABLE_FEATURE" == "true" ]]; then - log "DRY-RUN enable codex_hooks feature in $CODEX_CONFIG_DIR/config.toml" + log "DRY-RUN enable hooks feature in $CODEX_CONFIG_DIR/config.toml" fi exit 0 fi diff --git a/tests/run-all-tests.sh b/tests/run-all-tests.sh index 169537a..00000ad 100755 --- a/tests/run-all-tests.sh +++ b/tests/run-all-tests.sh @@ -133,6 +133,12 @@ ZSH_TESTS=( "test-zsh-monitor-safety.sh" ) +# Signal-heavy runtime tests are more stable when they run after the +# parallel batch finishes. +SERIAL_TESTS=( + "test-monitor-runtime.sh" +) + # Temp directory for per-suite output files OUTPUT_DIR=$(mktemp -d) trap "rm -rf $OUTPUT_DIR" EXIT @@ -161,6 +167,16 @@ needs_zsh() { return 1 } +needs_serial() { + local suite="$1" + for serial_test in "${SERIAL_TESTS[@]}"; do + if [[ "$suite" == "$serial_test" ]]; then + return 0 + fi + done + return 1 +} + # Format milliseconds as human-readable duration format_ms() { local ms="$1" @@ -169,10 +185,79 @@ format_ms() { echo "${s}.${frac}s" } -# Launch all test suites in parallel +run_suite_capture() { + local suite="$1" + local out_file="$2" + local exit_file="$3" + local time_file="$4" + local suite_path="$SCRIPT_DIR/$suite" + + if needs_zsh "$suite"; then + ( + t_start=$(date +%s%3N) + zsh "$suite_path" >"$out_file" 2>&1 + echo $? >"$exit_file" + echo $(( $(date +%s%3N) - t_start )) >"$time_file" + ) + else + ( + t_start=$(date +%s%3N) + "$suite_path" >"$out_file" 2>&1 + echo $? >"$exit_file" + echo $(( $(date +%s%3N) - t_start )) >"$time_file" + ) + fi +} + +collect_suite_result() { + local suite="$1" + local safe_name="$2" + local out_file="$3" + local exit_file="$4" + local time_file="$5" + local exit_code + local output + local elapsed_ms + local elapsed_display + local output_stripped + local passed + local failed + local line + local zsh_label + + exit_code=$(cat "$exit_file" 2>/dev/null || echo "1") + output=$(cat "$out_file" 2>/dev/null || echo "") + elapsed_ms=$(cat "$time_file" 2>/dev/null || echo "0") + elapsed_display=$(format_ms "$elapsed_ms") + + # Strip ANSI escape codes and extract pass/fail counts + output_stripped=$(echo "$output" | sed "s/${esc}\\[[0-9;]*m//g") + passed=$(echo "$output_stripped" | grep -oE 'Passed:[[:space:]]*[0-9]+' | grep -oE '[0-9]+$' | tail -1 || echo "0") + failed=$(echo "$output_stripped" | grep -oE 'Failed:[[:space:]]*[0-9]+' | grep -oE '[0-9]+$' | tail -1 || echo "0") + + TOTAL_PASSED=$((TOTAL_PASSED + passed)) + TOTAL_FAILED=$((TOTAL_FAILED + failed)) + + if [[ $exit_code -ne 0 ]] || [[ "$failed" -gt 0 ]]; then + FAILED_SUITES+=("$suite") + line=$(echo -e "${RED}FAILED${NC}: $suite (exit code: $exit_code, failed: $failed, ${elapsed_display})") + printf '%d\t%s\n' "$elapsed_ms" "$line" >> "$SORT_FILE" + # Preserve the full suite log so CI surfaces the exact failing assertion. + printf '%s\n' "$output" > "$OUTPUT_DIR/${safe_name}.detail" + else + zsh_label="" + needs_zsh "$suite" && zsh_label=" (zsh)" + line=$(echo -e "${GREEN}PASSED${NC}: $suite${zsh_label} ($passed tests, ${elapsed_display})") + printf '%d\t%s\n' "$elapsed_ms" "$line" >> "$SORT_FILE" + fi +} + +# Launch all test suites in parallel, except signal-heavy runtime tests which +# run serially after the parallel batch finishes. declare -A PIDS # suite -> PID declare -A SKIPPED # suite -> reason ACTIVE_PIDS=() +SERIAL_SUITES=() for suite in "${TEST_SUITES[@]}"; do suite_path="$SCRIPT_DIR/$suite" @@ -186,25 +271,21 @@ for suite in "${TEST_SUITES[@]}"; do continue fi + if needs_serial "$suite"; then + SERIAL_SUITES+=("$suite") + continue + fi + if needs_zsh "$suite"; then if ! command -v zsh &>/dev/null; then SKIPPED["$suite"]="zsh not available" continue fi - ( - t_start=$(date +%s%3N) - zsh "$suite_path" >"$out_file" 2>&1 - echo $? >"$exit_file" - echo $(( $(date +%s%3N) - t_start )) >"$time_file" - ) & - else - ( - t_start=$(date +%s%3N) - "$suite_path" >"$out_file" 2>&1 - echo $? >"$exit_file" - echo $(( $(date +%s%3N) - t_start )) >"$time_file" - ) & fi + + ( + run_suite_capture "$suite" "$out_file" "$exit_file" "$time_file" + ) & PIDS["$suite"]=$! ACTIVE_PIDS+=("${PIDS[$suite]}") @@ -228,7 +309,7 @@ for suite in "${TEST_SUITES[@]}"; do done done -# Wait for all and collect results +# Wait for parallel suites and collect results. TOTAL_PASSED=0 TOTAL_FAILED=0 FAILED_SUITES=() @@ -239,6 +320,7 @@ SORT_FILE="$OUTPUT_DIR/sortable.txt" esc=$'\033' for suite in "${TEST_SUITES[@]}"; do [[ -n "${SKIPPED[$suite]+x}" ]] && continue + [[ " ${SERIAL_SUITES[*]} " == *" $suite "* ]] && continue pid="${PIDS[$suite]}" wait "$pid" 2>/dev/null @@ -247,32 +329,18 @@ for suite in "${TEST_SUITES[@]}"; do out_file="$OUTPUT_DIR/${safe_name}.out" exit_file="$OUTPUT_DIR/${safe_name}.exit" time_file="$OUTPUT_DIR/${safe_name}.time" + collect_suite_result "$suite" "$safe_name" "$out_file" "$exit_file" "$time_file" +done - exit_code=$(cat "$exit_file" 2>/dev/null || echo "1") - output=$(cat "$out_file" 2>/dev/null || echo "") - elapsed_ms=$(cat "$time_file" 2>/dev/null || echo "0") - elapsed_display=$(format_ms "$elapsed_ms") - - # Strip ANSI escape codes and extract pass/fail counts - output_stripped=$(echo "$output" | sed "s/${esc}\\[[0-9;]*m//g") - passed=$(echo "$output_stripped" | grep -oE 'Passed:[[:space:]]*[0-9]+' | grep -oE '[0-9]+$' | tail -1 || echo "0") - failed=$(echo "$output_stripped" | grep -oE 'Failed:[[:space:]]*[0-9]+' | grep -oE '[0-9]+$' | tail -1 || echo "0") - - TOTAL_PASSED=$((TOTAL_PASSED + passed)) - TOTAL_FAILED=$((TOTAL_FAILED + failed)) +# Run serial suites after the parallel batch finishes. +for suite in "${SERIAL_SUITES[@]}"; do + safe_name="$(echo "$suite" | tr '/' '_')" + out_file="$OUTPUT_DIR/${safe_name}.out" + exit_file="$OUTPUT_DIR/${safe_name}.exit" + time_file="$OUTPUT_DIR/${safe_name}.time" - if [[ $exit_code -ne 0 ]] || [[ "$failed" -gt 0 ]]; then - FAILED_SUITES+=("$suite") - line=$(echo -e "${RED}FAILED${NC}: $suite (exit code: $exit_code, failed: $failed, ${elapsed_display})") - printf '%d\t%s\n' "$elapsed_ms" "$line" >> "$SORT_FILE" - # Preserve the full suite log so CI surfaces the exact failing assertion. - printf '%s\n' "$output" > "$OUTPUT_DIR/${safe_name}.detail" - else - zsh_label="" - needs_zsh "$suite" && zsh_label=" (zsh)" - line=$(echo -e "${GREEN}PASSED${NC}: $suite${zsh_label} ($passed tests, ${elapsed_display})") - printf '%d\t%s\n' "$elapsed_ms" "$line" >> "$SORT_FILE" - fi + run_suite_capture "$suite" "$out_file" "$exit_file" "$time_file" + collect_suite_result "$suite" "$safe_name" "$out_file" "$exit_file" "$time_file" done # Print skipped suites first diff --git a/tests/test-bitlesson-select-routing.sh b/tests/test-bitlesson-select-routing.sh index 68ecfa1..012f94e 100755 --- a/tests/test-bitlesson-select-routing.sh +++ b/tests/test-bitlesson-select-routing.sh @@ -8,7 +8,8 @@ source "$SCRIPT_DIR/test-helpers.sh" BITLESSON_SELECT="$PROJECT_ROOT/scripts/bitlesson-select.sh" # Keep PATH isolation strict in missing-binary tests to avoid picking up # real codex/claude from user-local directories (e.g. ~/.nvm, ~/.local/bin). -SAFE_BASE_PATH="/usr/bin:/bin:/usr/sbin:/sbin" +# On NixOS, the shell toolchain itself lives under /run/current-system/sw/bin. +SAFE_BASE_PATH="/run/current-system/sw/bin:/usr/bin:/bin:/usr/sbin:/sbin" echo "==========================================" echo "Bitlesson Select Routing Tests" @@ -481,7 +482,7 @@ captured_args="$(cat "$CAPTURE_ARGS")" if [[ $exit_code -eq 0 ]] \ && echo "$stdout_out" | grep -q "BL-20260315-tracker-drift" \ && echo "$captured_args" | grep -q -- '--disable' \ - && echo "$captured_args" | grep -q -- 'codex_hooks' \ + && echo "$captured_args" | grep -q -- 'hooks' \ && echo "$captured_args" | grep -q -- '--skip-git-repo-check' \ && echo "$captured_args" | grep -q -- '--ephemeral' \ && echo "$captured_args" | grep -q -- 'read-only' \ diff --git a/tests/test-codex-hook-install.sh b/tests/test-codex-hook-install.sh index da20fb9..60b4fcc 100755 --- a/tests/test-codex-hook-install.sh +++ b/tests/test-codex-hook-install.sh @@ -43,15 +43,15 @@ set -euo pipefail if [[ "${1:-}" == "features" && "${2:-}" == "list" ]]; then cat <<'LIST' -codex_hooks under development false +hooks stable false LIST exit 0 fi -if [[ "${1:-}" == "features" && "${2:-}" == "enable" && "${3:-}" == "codex_hooks" ]]; then +if [[ "${1:-}" == "features" && "${2:-}" == "enable" && "${3:-}" == "hooks" ]]; then printf 'CODEX_HOME=%s\n' "${CODEX_HOME:-}" >> "${TEST_CODEX_FEATURE_LOG:?}" mkdir -p "${CODEX_HOME:?}" - : > "${CODEX_HOME}/.codex-hooks-enabled" + : > "${CODEX_HOME}/.hooks-enabled" exit 0 fi @@ -133,10 +133,10 @@ else fail "Codex install writes hooks.json" "$HOOKS_FILE exists" "missing" fi -if [[ -f "$CODEX_HOME_DIR/.codex-hooks-enabled" ]]; then - pass "Codex install enables codex_hooks feature" +if [[ -f "$CODEX_HOME_DIR/.hooks-enabled" ]]; then + pass "Codex install enables hooks feature" else - fail "Codex install enables codex_hooks feature" ".codex-hooks-enabled marker exists" "missing" + fail "Codex install enables hooks feature" ".hooks-enabled marker exists" "missing" fi if [[ -f "$HUMANIZE_USER_CONFIG" ]]; then @@ -287,6 +287,83 @@ else fail "Codex feature enable runs on each Codex install/update" "2 log entries" "$(cat "$FEATURE_LOG")" fi +LEGACY_CONFIG_HOME="$TEST_DIR/codex-home-legacy-config" +mkdir -p "$LEGACY_CONFIG_HOME" +cat > "$LEGACY_CONFIG_HOME/config.toml" <<'EOF' +[features] +codex_hooks = true +EOF + +set +e +PATH="$FAKE_BIN:$PATH" TEST_CODEX_FEATURE_LOG="$FEATURE_LOG" \ + "$INSTALL_SCRIPT" \ + --target codex \ + --codex-config-dir "$LEGACY_CONFIG_HOME" \ + --codex-skills-dir "$LEGACY_CONFIG_HOME/skills" \ + > "$TEST_DIR/install-legacy-config.log" 2>&1 +LEGACY_CONFIG_EXIT=$? +set -e + +if [[ "$LEGACY_CONFIG_EXIT" -ne 0 ]]; then + pass "Codex install rejects legacy codex_hooks config" +else + fail "Codex install rejects legacy codex_hooks config" "non-zero exit" "exit 0" +fi + +if grep -q "legacy feature key 'codex_hooks'" "$TEST_DIR/install-legacy-config.log" \ + && grep -q "hooks = true" "$TEST_DIR/install-legacy-config.log"; then + pass "Legacy codex_hooks config failure explains hooks rename" +else + fail "Legacy codex_hooks config failure explains hooks rename" \ + "error mentioning legacy codex_hooks and hooks = true" \ + "$(cat "$TEST_DIR/install-legacy-config.log")" +fi + +LEGACY_ONLY_BIN="$TEST_DIR/bin-legacy-only" +LEGACY_ONLY_HOME="$TEST_DIR/codex-home-legacy-only" +mkdir -p "$LEGACY_ONLY_BIN" "$LEGACY_ONLY_HOME" + +cat > "$LEGACY_ONLY_BIN/codex" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +if [[ "${1:-}" == "features" && "${2:-}" == "list" ]]; then + cat <<'LIST' +codex_hooks under development false +LIST + exit 0 +fi + +echo "unexpected fake codex invocation: $*" >&2 +exit 1 +EOF +chmod +x "$LEGACY_ONLY_BIN/codex" + +set +e +PATH="$LEGACY_ONLY_BIN:$PATH" \ + "$INSTALL_SCRIPT" \ + --target codex \ + --codex-config-dir "$LEGACY_ONLY_HOME" \ + --codex-skills-dir "$LEGACY_ONLY_HOME/skills" \ + > "$TEST_DIR/install-legacy-only.log" 2>&1 +LEGACY_ONLY_EXIT=$? +set -e + +if [[ "$LEGACY_ONLY_EXIT" -ne 0 ]]; then + pass "Codex install rejects Codex builds exposing only legacy codex_hooks" +else + fail "Codex install rejects Codex builds exposing only legacy codex_hooks" "non-zero exit" "exit 0" +fi + +if grep -q "legacy 'codex_hooks' feature" "$TEST_DIR/install-legacy-only.log" \ + && grep -q "Upgrade Codex" "$TEST_DIR/install-legacy-only.log"; then + pass "Legacy-only feature failure asks user to upgrade Codex" +else + fail "Legacy-only feature failure asks user to upgrade Codex" \ + "error mentioning legacy codex_hooks and Upgrade Codex" \ + "$(cat "$TEST_DIR/install-legacy-only.log")" +fi + UNSUPPORTED_BIN="$TEST_DIR/bin-unsupported" UNSUPPORTED_HOME="$TEST_DIR/codex-home-unsupported" mkdir -p "$UNSUPPORTED_BIN" "$UNSUPPORTED_HOME" @@ -323,11 +400,12 @@ else fail "Codex install rejects builds without native hooks support" "non-zero exit" "exit 0" fi -if grep -q "codex_hooks feature" "$TEST_DIR/install-unsupported.log"; then - pass "Unsupported Codex failure explains missing codex_hooks feature" +if grep -q "native 'hooks' feature" "$TEST_DIR/install-unsupported.log" \ + && grep -q "Upgrade Codex" "$TEST_DIR/install-unsupported.log"; then + pass "Unsupported Codex failure explains missing hooks feature" else - fail "Unsupported Codex failure explains missing codex_hooks feature" \ - "error mentioning codex_hooks feature" \ + fail "Unsupported Codex failure explains missing hooks feature" \ + "error mentioning native hooks feature and Upgrade Codex" \ "$(cat "$TEST_DIR/install-unsupported.log")" fi diff --git a/tests/test-disable-nested-codex-hooks.sh b/tests/test-disable-nested-codex-hooks.sh index c240ad6..3cbce63 100755 --- a/tests/test-disable-nested-codex-hooks.sh +++ b/tests/test-disable-nested-codex-hooks.sh @@ -77,7 +77,7 @@ if [[ "\$1" == "--help" ]]; then Usage: codex [OPTIONS] Options: - --disable Disable a specific Codex hook (e.g. codex_hooks) + --disable Disable a specific Codex hook (e.g. hooks) --skip-git-repo-check Skip git repo validation HELP exit 0 @@ -188,22 +188,22 @@ REPO_IMPL="$TEST_DIR/repo-impl" setup_repo "$REPO_IMPL" run_loop_hook "$REPO_IMPL" "$TEST_DIR/impl.args" "false" -if grep -q -- 'exec --disable codex_hooks' "$TEST_DIR/impl.args"; then - pass "implementation-phase stop hook disables codex_hooks for codex exec" +if grep -q -- 'exec --disable hooks' "$TEST_DIR/impl.args"; then + pass "implementation-phase stop hook disables hooks for codex exec" else - fail "implementation-phase stop hook disables codex_hooks for codex exec" \ - "exec --disable codex_hooks" "$(cat "$TEST_DIR/impl.args" 2>/dev/null || echo missing)" + fail "implementation-phase stop hook disables hooks for codex exec" \ + "exec --disable hooks" "$(cat "$TEST_DIR/impl.args" 2>/dev/null || echo missing)" fi REPO_REVIEW="$TEST_DIR/repo-review" setup_repo "$REPO_REVIEW" run_loop_hook "$REPO_REVIEW" "$TEST_DIR/review.args" "true" -if grep -q -- 'review --disable codex_hooks' "$TEST_DIR/review.args"; then - pass "review-phase stop hook disables codex_hooks for codex review" +if grep -q -- 'review --disable hooks' "$TEST_DIR/review.args"; then + pass "review-phase stop hook disables hooks for codex review" else - fail "review-phase stop hook disables codex_hooks for codex review" \ - "review --disable codex_hooks" "$(cat "$TEST_DIR/review.args" 2>/dev/null || echo missing)" + fail "review-phase stop hook disables hooks for codex review" \ + "review --disable hooks" "$(cat "$TEST_DIR/review.args" 2>/dev/null || echo missing)" fi echo "" diff --git a/tests/test-monitor-runtime.sh b/tests/test-monitor-runtime.sh index e146ada..dee3d43 100755 --- a/tests/test-monitor-runtime.sh +++ b/tests/test-monitor-runtime.sh @@ -354,8 +354,8 @@ trap '_cleanup' INT TERM ) & child_pid=$! -# Wait for signal (up to 1 second) -for i in {1..10}; do +# Wait for signal (up to 5 seconds); parallel CI runners can be slow. +for i in {1..50}; do sleep 0.1 if [[ "$cleanup_triggered" == "true" ]]; then break @@ -454,8 +454,8 @@ TRAPINT() { ) & child_pid=$! -# Wait for signal (up to 1 second) -for i in {1..10}; do +# Wait for signal (up to 5 seconds); parallel CI runners can be slow. +for i in {1..50}; do sleep 0.1 if [[ "$cleanup_triggered" == "true" ]]; then break diff --git a/tests/test-templates-comprehensive.sh b/tests/test-templates-comprehensive.sh index d27dac4..aca988a 100755 --- a/tests/test-templates-comprehensive.sh +++ b/tests/test-templates-comprehensive.sh @@ -329,7 +329,7 @@ echo "" echo "Testing backslashes in values..." result=$(render_template "Code: {{CODE}}" "CODE=\$HOME\\npath") # Note: backslashes may be interpreted by awk -if echo "$result" | grep -q "Code:"; then +if grep -q "Code:" <<<"$result"; then pass "Backslashes in values (no crash)" else fail "Backslashes in values" "Code: ..." "$result" @@ -338,7 +338,7 @@ fi echo "" echo "Testing quotes in values..." result=$(render_template "Quote: {{MSG}}" "MSG=He said \"hello\" and 'bye'") -if echo "$result" | grep -q "Quote:"; then +if grep -q "Quote:" <<<"$result"; then pass "Quotes in values" else fail "Quotes in values" "Quote: ..." "$result" @@ -350,7 +350,7 @@ result=$(render_template "Message: {{MSG}}" "MSG=Hello World") if [[ "$result" == "Message: Hello World" ]]; then # CJK in variable value result2=$(render_template "CJK: {{CJK}}" "CJK=Chinese Text Here") - if echo "$result2" | grep -q "CJK:"; then + if grep -q "CJK:" <<<"$result2"; then pass "CJK characters handling" else fail "CJK characters handling" "CJK: ..." "$result2" @@ -373,7 +373,7 @@ fi echo "" echo "Testing markdown formatting in values..." result=$(render_template "Formatted: {{TEXT}}" "TEXT=**bold** and _italic_ and \`code\`") -if echo "$result" | grep -q "Formatted:"; then +if grep -q "Formatted:" <<<"$result"; then pass "Markdown formatting in values" else fail "Markdown formatting in values" "Formatted: ..." "$result" @@ -395,7 +395,7 @@ multiline_value="Line 1 Line 2 Line 3" result=$(render_template "Content: {{CONTENT}}" "CONTENT=$multiline_value") -if echo "$result" | grep -q "Content:"; then +if grep -q "Content:" <<<"$result"; then pass "Multiline values (no crash)" else fail "Multiline values" "Content: ..." "$result" @@ -446,7 +446,7 @@ echo "" echo "Testing load_and_render_safe with missing template..." fallback="Fallback: {{VAR}}" result=$(load_and_render_safe "$TEMPLATE_DIR" "non-existing-file.md" "$fallback" "VAR=test") -if echo "$result" | grep -q "Fallback: test"; then +if grep -q "Fallback: test" <<<"$result"; then pass "Fallback used for missing template" else fail "Fallback used for missing template" "Fallback: test" "$result" @@ -456,7 +456,7 @@ echo "" echo "Testing load_and_render_safe with existing template..." fallback="This should NOT appear" result=$(load_and_render_safe "$TEMPLATE_DIR" "block/git-push.md" "$fallback") -if echo "$result" | grep -q "Git Push Blocked" && ! echo "$result" | grep -q "should NOT appear"; then +if grep -q "Git Push Blocked" <<<"$result" && ! grep -q "should NOT appear" <<<"$result"; then pass "Real template used when available" else fail "Real template used when available" "Git Push Blocked" "$result" @@ -503,10 +503,10 @@ result=$(load_and_render "$TEMPLATE_DIR" "block/wrong-round-number.md" \ "CURRENT_ROUND=5" \ "CORRECT_PATH=/tmp/round-5-summary.md") -if echo "$result" | grep -q "Wrong Round Number" && \ - echo "$result" | grep -q "round-3-summary" && \ - echo "$result" | grep -q "current round is \*\*5\*\*" && \ - echo "$result" | grep -q "/tmp/round-5-summary.md"; then +if grep -q "Wrong Round Number" <<<"$result" && \ + grep -q "round-3-summary" <<<"$result" && \ + grep -q "current round is \*\*5\*\*" <<<"$result" && \ + grep -q "/tmp/round-5-summary.md" <<<"$result"; then pass "Real template rendering with all variables" else fail "Real template rendering with all variables" @@ -519,9 +519,9 @@ result=$(load_and_render "$TEMPLATE_DIR" "block/unpushed-commits.md" \ "AHEAD_COUNT=3" \ "CURRENT_BRANCH=feature-branch") -if echo "$result" | grep -q "Unpushed Commits" && \ - echo "$result" | grep -q "3 unpushed" && \ - echo "$result" | grep -q "feature-branch"; then +if grep -q "Unpushed Commits" <<<"$result" && \ + grep -q "3 unpushed" <<<"$result" && \ + grep -q "feature-branch" <<<"$result"; then pass "Real template: unpushed-commits.md" else fail "Real template: unpushed-commits.md" @@ -532,8 +532,8 @@ echo "Testing real template: codex/goal-tracker-update-section.md..." result=$(load_and_render "$TEMPLATE_DIR" "codex/goal-tracker-update-section.md" \ "GOAL_TRACKER_FILE=.humanize/rlcr/20240101/goal-tracker.md") -if echo "$result" | grep -q "Goal Tracker Update Requests" && \ - echo "$result" | grep -q ".humanize/rlcr/20240101/goal-tracker.md"; then +if grep -q "Goal Tracker Update Requests" <<<"$result" && \ + grep -q ".humanize/rlcr/20240101/goal-tracker.md" <<<"$result"; then pass "Real template: goal-tracker-update-section.md" else fail "Real template: goal-tracker-update-section.md"