From bbcbb84621b5a97c348e7769a12108c22c69ba34 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Mar 2026 23:01:35 +0000 Subject: [PATCH 1/8] feat(test): add `PDU_NO_FAIL_FAST` mode to `test.sh` Add a no-fail-fast mode activated by `PDU_NO_FAIL_FAST=true` that continues running all checks instead of stopping at the first failure, then reports a summary at the end. This helps AI environments that may fail on some checks but need to see which ones pass. Update AI instruction templates to document the new flag. https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- .github/copilot-instructions.md | 1 + AGENTS.md | 1 + CLAUDE.md | 1 + template/ai-instructions/shared.md | 1 + test.sh | 40 ++++++++++++++++++++++++------ 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7afa4d56..06854cb1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,3 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes +- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail diff --git a/AGENTS.md b/AGENTS.md index 7afa4d56..06854cb1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,3 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes +- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail diff --git a/CLAUDE.md b/CLAUDE.md index f27d3302..3ff87056 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,4 +16,5 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes +- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail - `gh` (GitHub CLI) is not installed — do not attempt to use it diff --git a/template/ai-instructions/shared.md b/template/ai-instructions/shared.md index 7afa4d56..06854cb1 100644 --- a/template/ai-instructions/shared.md +++ b/template/ai-instructions/shared.md @@ -16,3 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes +- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail diff --git a/test.sh b/test.sh index dbb69fdf..9f3490d1 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,18 @@ #! /bin/bash set -o errexit -o pipefail -o nounset +# Validate PDU_NO_FAIL_FAST +no_fail_fast="${PDU_NO_FAIL_FAST:-false}" +case "$no_fail_fast" in +true | false) ;; +*) + echo "error: Invalid value for PDU_NO_FAIL_FAST: $no_fail_fast (expected 'true' or 'false')" >&2 + exit 1 + ;; +esac + +has_failures=false + run() ( echo >&2 echo "exec> $*" >&2 @@ -32,10 +44,24 @@ unit() ( eval run_if "${TEST:-true}" cargo test "${TEST_FLAGS:-}" "$@" ) -run_if "${FMT:-true}" cargo fmt -- --check -unit "$@" -unit --no-default-features "$@" -unit --all-features "$@" -unit --features cli "$@" -unit --features cli-completions "$@" -unit --features ai-instructions "$@" +execute() { + if [[ "$no_fail_fast" == "true" ]]; then + "$@" || has_failures=true + else + "$@" + fi +} + +execute run_if "${FMT:-true}" cargo fmt -- --check +execute unit "$@" +execute unit --no-default-features "$@" +execute unit --all-features "$@" +execute unit --features cli "$@" +execute unit --features cli-completions "$@" +execute unit --features ai-instructions "$@" + +if [[ "$has_failures" == "true" ]]; then + echo >&2 + echo "error: Some checks have failed. Review the output above for details." >&2 + exit 1 +fi From 791a4ea5ba8a8121c220fb164ee8ec61cc2e63ed Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Mar 2026 23:13:59 +0000 Subject: [PATCH 2/8] fix(test): move no-fail-fast logic into `run_if` and use single quotes https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- test.sh | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test.sh b/test.sh index 9f3490d1..887dbdc8 100755 --- a/test.sh +++ b/test.sh @@ -11,7 +11,8 @@ true | false) ;; ;; esac -has_failures=false +failure_marker=$(mktemp) +rm -f "$failure_marker" run() ( echo >&2 @@ -28,7 +29,13 @@ run_if() ( condition="$1" shift case "$condition" in - true) run "$@" ;; + true) + if [[ $no_fail_fast == 'true' ]]; then + run "$@" || touch "$failure_marker" + else + run "$@" + fi + ;; false) skip "$@" ;; *) echo "error: Invalid condition: $condition" >&2 @@ -44,24 +51,17 @@ unit() ( eval run_if "${TEST:-true}" cargo test "${TEST_FLAGS:-}" "$@" ) -execute() { - if [[ "$no_fail_fast" == "true" ]]; then - "$@" || has_failures=true - else - "$@" - fi -} - -execute run_if "${FMT:-true}" cargo fmt -- --check -execute unit "$@" -execute unit --no-default-features "$@" -execute unit --all-features "$@" -execute unit --features cli "$@" -execute unit --features cli-completions "$@" -execute unit --features ai-instructions "$@" +run_if "${FMT:-true}" cargo fmt -- --check +unit "$@" +unit --no-default-features "$@" +unit --all-features "$@" +unit --features cli "$@" +unit --features cli-completions "$@" +unit --features ai-instructions "$@" -if [[ "$has_failures" == "true" ]]; then +if [[ -f $failure_marker ]]; then + rm -f "$failure_marker" echo >&2 - echo "error: Some checks have failed. Review the output above for details." >&2 + echo 'error: Some checks have failed. Review the output above for details.' >&2 exit 1 fi From 3d9ac8a894a1b5e6e7006f6c8eb1d83d09a7169d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Mar 2026 23:21:58 +0000 Subject: [PATCH 3/8] fix(test): add comment and error message in no-fail-fast mode Add a comment explaining why a temporary file is used instead of a variable for the failure marker, and log the failed command with its exit code when no-fail-fast mode catches a failure. https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- test.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 887dbdc8..3efa13df 100755 --- a/test.sh +++ b/test.sh @@ -11,6 +11,8 @@ true | false) ;; ;; esac +# A temporary file is used instead of a variable because run_if and unit are +# subshells, so variable assignments inside them don't propagate to the parent. failure_marker=$(mktemp) rm -f "$failure_marker" @@ -31,7 +33,10 @@ run_if() ( case "$condition" in true) if [[ $no_fail_fast == 'true' ]]; then - run "$@" || touch "$failure_marker" + run "$@" || { + echo "error: Command $@ failed with exit code $?" >&2 + touch "$failure_marker" + } else run "$@" fi From 992bd0959f68c57d37c017fc96b98c1f868a2ce9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 22 Mar 2026 23:42:49 +0000 Subject: [PATCH 4/8] docs(ai): remove conditional preamble from no-fail-fast instruction https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3ff87056..fdc6dd28 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,5 +16,5 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes -- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail +- Set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail - `gh` (GitHub CLI) is not installed — do not attempt to use it From 89546430ca5869da45c926c7e483d5fa0d1c96e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 00:00:55 +0000 Subject: [PATCH 5/8] docs(ai): remove conditional preamble from no-fail-fast instruction in template Update the source template and regenerate all AI instruction files. https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- .github/copilot-instructions.md | 2 +- AGENTS.md | 2 +- template/ai-instructions/shared.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 06854cb1..7ecfa8dc 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,4 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes -- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail +- Set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail diff --git a/AGENTS.md b/AGENTS.md index 06854cb1..7ecfa8dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,4 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes -- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail +- Set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail diff --git a/template/ai-instructions/shared.md b/template/ai-instructions/shared.md index 06854cb1..7ecfa8dc 100644 --- a/template/ai-instructions/shared.md +++ b/template/ai-instructions/shared.md @@ -16,4 +16,4 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Minimize `unwrap()` in non-test code — use proper error handling - Install toolchain before running tests: `rustup toolchain install "$(< rust-toolchain)" && rustup component add --toolchain "$(< rust-toolchain)" rustfmt clippy` - Run `FMT=true LINT=true BUILD=true TEST=true DOC=true ./test.sh` to validate changes -- If some checks fail due to environment limitations, set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail +- Set `PDU_NO_FAIL_FAST=true` to run all checks instead of stopping at the first failure — this lets you see which checks pass and which fail From d61801718254d5f0f3d35bd7fbeb0831bffcf965 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 00:30:35 +0000 Subject: [PATCH 6/8] fix(test): address review feedback on test.sh - Use mktemp -d with a marker file inside to avoid path reuse - Quote $failure_marker in the final check for consistency - Use $* instead of $@ in error message for unambiguous logging https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- test.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test.sh b/test.sh index 3efa13df..ffb158ab 100755 --- a/test.sh +++ b/test.sh @@ -13,8 +13,8 @@ esac # A temporary file is used instead of a variable because run_if and unit are # subshells, so variable assignments inside them don't propagate to the parent. -failure_marker=$(mktemp) -rm -f "$failure_marker" +failure_dir=$(mktemp -d) +failure_marker="$failure_dir/failed" run() ( echo >&2 @@ -34,7 +34,7 @@ run_if() ( true) if [[ $no_fail_fast == 'true' ]]; then run "$@" || { - echo "error: Command $@ failed with exit code $?" >&2 + echo "error: Command $* failed with exit code $?" >&2 touch "$failure_marker" } else @@ -64,9 +64,10 @@ unit --features cli "$@" unit --features cli-completions "$@" unit --features ai-instructions "$@" -if [[ -f $failure_marker ]]; then - rm -f "$failure_marker" +if [[ -f "$failure_marker" ]]; then + rm -rf "$failure_dir" echo >&2 echo 'error: Some checks have failed. Review the output above for details.' >&2 exit 1 fi +rm -rf "$failure_dir" From 1c09536cd7c09205dcfe9895c30e49b02e71312f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 00:43:55 +0000 Subject: [PATCH 7/8] fix(test): use EXIT trap to clean up temporary directory Replace explicit rm -rf calls with a trap to ensure cleanup happens even when errexit terminates the script early in fail-fast mode. https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- test.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test.sh b/test.sh index ffb158ab..38740f72 100755 --- a/test.sh +++ b/test.sh @@ -14,6 +14,7 @@ esac # A temporary file is used instead of a variable because run_if and unit are # subshells, so variable assignments inside them don't propagate to the parent. failure_dir=$(mktemp -d) +trap 'rm -rf "$failure_dir"' EXIT failure_marker="$failure_dir/failed" run() ( @@ -65,9 +66,7 @@ unit --features cli-completions "$@" unit --features ai-instructions "$@" if [[ -f "$failure_marker" ]]; then - rm -rf "$failure_dir" echo >&2 echo 'error: Some checks have failed. Review the output above for details.' >&2 exit 1 fi -rm -rf "$failure_dir" From 0f66d5b7066cfcca10e2899146b97d6912c23b28 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 01:30:56 +0000 Subject: [PATCH 8/8] fix(test): improve error logging for failed commands in no-fail-fast mode Use `printf '%q'` with `"$@"` to preserve argument quoting and capture exit status into a variable for reliability. https://claude.ai/code/session_01VHRbHsz8VJFdNioSj6dyxe --- test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 38740f72..6c027b36 100755 --- a/test.sh +++ b/test.sh @@ -35,7 +35,10 @@ run_if() ( true) if [[ $no_fail_fast == 'true' ]]; then run "$@" || { - echo "error: Command $* failed with exit code $?" >&2 + exit_status=$? + printf 'error: Command failed with exit code %d: ' "$exit_status" >&2 + printf '%q ' "$@" >&2 + printf '\n' >&2 touch "$failure_marker" } else