From b0f7d26dd9c14d91eaaefc35d6c9fd6618a0bd92 Mon Sep 17 00:00:00 2001 From: f10-coder Date: Fri, 7 Aug 2026 11:12:17 +0000 Subject: [PATCH] fix(shell): remove test harness pipe hazards (#1106) Co-authored-by: f10-coder --- docs/reports/quality/1099-pipefail-sweep.md | 20 +++++++++- docs/scratchpads/1099-pipefail-sweep.md | 4 ++ .../systemd/user/test-fleet-units.sh | 3 +- .../tools/git/test-issue-comment-readback.sh | 10 ++++- .../tools/git/test-lane-brief-pr-linkage.sh | 2 +- .../tools/git/test-pr-review-gitea-comment.sh | 5 ++- .../git/test-pr-review-repo-host-override.sh | 4 +- .../tools/orchestrator/smoke-test.sh | 4 +- .../tools/orchestrator/test-board-roll.sh | 4 +- .../scripts/test-upgrade-durable-snapshot.sh | 8 +++- .../quality/scripts/test-upgrade-rollback.sh | 5 ++- .../tools/tmux/test-send-message-socket.sh | 18 +++++---- .../tools/tmux/test-send-message-verdict.sh | 2 +- .../pipefail-early-exit-test-baseline.json | 24 ++++++++++++ scripts/pipefail-early-exit.test.mjs | 37 ++++++++++++++++--- 15 files changed, 120 insertions(+), 30 deletions(-) create mode 100644 scripts/fixtures/pipefail-early-exit-test-baseline.json diff --git a/docs/reports/quality/1099-pipefail-sweep.md b/docs/reports/quality/1099-pipefail-sweep.md index d655ea9a..0519b72b 100644 --- a/docs/reports/quality/1099-pipefail-sweep.md +++ b/docs/reports/quality/1099-pipefail-sweep.md @@ -51,4 +51,22 @@ This is a site inventory, not a risk count. `FIXED` means the early-exiting cons | `packages/mosaic/framework/tools/qa/qa-hook-stdin.sh:32` | UNREACHABLE-AND-WHY | same withdrawn designed-input finding | | `packages/mosaic/framework/tools/qa/qa-hook-stdin.sh:34` | UNREACHABLE-AND-WHY | same withdrawn designed-input finding | -Remaining test and wake-validation sites are intentionally deferred to later review-sized tranches and are not yet assigned a safety verdict here. +## Tranche 2 — non-wake test harnesses + +All 22 baseline sites below are `FIXED`; the checked-in tranche fixture is passed through the same scanner and asserts all 22 occurrences and 21 normalized identities (the same response-split line occurs twice). + +| Baseline site(s) | Verdict | Construction | +| --- | --- | --- | +| `systemd/user/test-fleet-units.sh:148` | FIXED | capture tmux output, then grep via redirection | +| `git/test-issue-comment-readback.sh:283,302` | FIXED | parameter expansion splits status/body without `head` | +| `git/test-pr-review-gitea-comment.sh:228` | FIXED | parameter expansion splits status/body | +| `git/test-lane-brief-pr-linkage.sh:72` | FIXED | grep reads from a here-string | +| `git/test-pr-review-repo-host-override.sh:225-226` | FIXED | grep reads from a here-string | +| `orchestrator/smoke-test.sh:67,72` | FIXED | parameter expansion selects first line | +| `orchestrator/test-board-roll.sh:99-100` | FIXED | grep reads from a here-string | +| `quality/scripts/test-upgrade-durable-snapshot.sh:180` | FIXED | complete sorted output is read with `mapfile`, then indexed | +| `quality/scripts/test-upgrade-rollback.sh:339,356` | FIXED | direct `grep -m1` file reads; cleanup captures before testing | +| `tmux/test-send-message-socket.sh:37,38,44-46,68,72` | FIXED | capture commands complete before redirected grep assertions | +| `tmux/test-send-message-verdict.sh:34` | FIXED | grep reads from a here-string | + +Remaining wake-validation sites are intentionally deferred to the final review-sized tranche and are not yet assigned a safety verdict here. diff --git a/docs/scratchpads/1099-pipefail-sweep.md b/docs/scratchpads/1099-pipefail-sweep.md index 5dd36dad..30548805 100644 --- a/docs/scratchpads/1099-pipefail-sweep.md +++ b/docs/scratchpads/1099-pipefail-sweep.md @@ -21,6 +21,10 @@ Construction choices: Site-by-site verdicts: `docs/reports/quality/1099-pipefail-sweep.md`. +## Tranche 2 TDD + +Expanded the unconditional scanner over 11 non-wake test harnesses. RED named exactly 22 source lines; a second immutable-baseline fixture now asserts those 22 entries through the same scanner. Rewrites preserve command status by capturing producers before redirected assertions, use parameter expansion for line selection, and use complete `mapfile` populations where ordering matters. Current-tree finding count is zero for tranches 1 and 2. + ## Verification so far - `bash -n` on every changed shell script: pass. diff --git a/packages/mosaic/framework/systemd/user/test-fleet-units.sh b/packages/mosaic/framework/systemd/user/test-fleet-units.sh index 91b4a34e..81d958d6 100755 --- a/packages/mosaic/framework/systemd/user/test-fleet-units.sh +++ b/packages/mosaic/framework/systemd/user/test-fleet-units.sh @@ -145,7 +145,8 @@ EOF /usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin \ MOSAIC_TMUX_SOCKET="$TEST_SOCKET" MOSAIC_TMUX_HOLDER=_holder "$HOLDER_START" tmux -L "$TEST_SOCKET" has-session -t '=_holder:0.0' || fail "fresh holder was not created" - if tmux -L "$TEST_SOCKET" show-environment -g LD_PRELOAD 2>/dev/null | grep -q '^LD_PRELOAD='; then + ld_preload_env="$(tmux -L "$TEST_SOCKET" show-environment -g LD_PRELOAD 2>/dev/null)" || true + if grep -q '^LD_PRELOAD=' <<<"$ld_preload_env"; then fail "fresh holder retained LD_PRELOAD" fi /usr/bin/env -i HOME="$HOLDER_HOME" PATH=/usr/bin:/bin MOSAIC_HOME="$AGENT_HOME" \ diff --git a/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh b/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh index b0178385..9f006c54 100755 --- a/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh +++ b/packages/mosaic/framework/tools/git/test-issue-comment-readback.sh @@ -280,7 +280,10 @@ print("201") print(json.dumps(record)) PY ) - write_response "$(printf '%s' "$result" | head -n1)" "$(printf '%s' "$result" | tail -n +2)" + response_status="${result%%$'\n'*}" + response_body="" + [[ "$result" == *$'\n'* ]] && response_body="${result#*$'\n'}" + write_response "$response_status" "$response_body" elif [[ "$method" == "GET" && "$path" == "$ISSUE_COMMENT_API_BASE"/issues/comments/* ]]; then result=$(ISSUE_COMMENT_GET_ID="${path##*/}" python3 - <<'PY' import json @@ -299,7 +302,10 @@ else: print(json.dumps(match)) PY ) - write_response "$(printf '%s' "$result" | head -n1)" "$(printf '%s' "$result" | tail -n +2)" + response_status="${result%%$'\n'*}" + response_body="" + [[ "$result" == *$'\n'* ]] && response_body="${result#*$'\n'}" + write_response "$response_status" "$response_body" else echo "Unexpected curl request: $method $url" >&2 exit 97 diff --git a/packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh b/packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh index 92baf52b..eaa254d0 100644 --- a/packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh +++ b/packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh @@ -69,7 +69,7 @@ section_nums() { # $1 = output $2 = header-prefix } fail() { echo "FAIL: $1" >&2; exit 1; } -contains() { printf '%s\n' "$1" | grep -qx "$2"; } +contains() { grep -qx "$2" <<<"$1"; } # --------------------------------------------------------------------------- # Fixed (current) script behavior diff --git a/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh b/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh index 588ca0bc..24b43d62 100644 --- a/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh +++ b/packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh @@ -225,7 +225,10 @@ write_response() { emit() { # Split a two-line "status\n" python result into the response. local result="$1" - write_response "$(printf '%s' "$result" | head -n1)" "$(printf '%s' "$result" | tail -n +2)" + response_status="${result%%$'\n'*}" + response_body="" + [[ "$result" == *$'\n'* ]] && response_body="${result#*$'\n'}" + write_response "$response_status" "$response_body" } mode="${PR_REVIEW_TEST_MODE:-}" diff --git a/packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh b/packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh index c4c69437..f5699ce4 100755 --- a/packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh +++ b/packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh @@ -222,8 +222,8 @@ grep -q 'Unknown action: bogus-action' "$OUTPUT_FILE" # --- Case 2: -h/--help documents both overrides. HELP_TEXT="$("$SCRIPT_DIR/pr-review.sh" -h)" -echo "$HELP_TEXT" | grep -q -- '-r, --repo' -echo "$HELP_TEXT" | grep -q -- '-H, --host' +grep -q -- '-r, --repo' <<<"$HELP_TEXT" +grep -q -- '-H, --host' <<<"$HELP_TEXT" # --- Case 3 (comment): a TRUE no-git-origin dir + -r/-H must not silently die # and must not fail with "not a git repository or no origin remote" either. diff --git a/packages/mosaic/framework/tools/orchestrator/smoke-test.sh b/packages/mosaic/framework/tools/orchestrator/smoke-test.sh index 24b4513a..b0c70f27 100755 --- a/packages/mosaic/framework/tools/orchestrator/smoke-test.sh +++ b/packages/mosaic/framework/tools/orchestrator/smoke-test.sh @@ -64,12 +64,12 @@ if jq -e '.next_task == "T-001"' "$capsule_file" >/dev/null 2>&1; then pass_case if grep -Fq 'Target runtime:** codex' <<< "$codex_continue_output"; then pass_case "continue prompt contains target runtime codex"; else fail_case "continue prompt contains target runtime codex"; fi codex_run_prompt="$(MOSAIC_COORD_RUNTIME=codex bash "$SCRIPT_DIR/session-run.sh" --project "$tmp_project" --print)" -if [[ "$(printf '%s\n' "$codex_run_prompt" | head -n1)" == "Now initiating Orchestrator mode..." ]]; then pass_case "codex run prompt first line is mode declaration"; else fail_case "codex run prompt first line is mode declaration"; fi +if [[ "${codex_run_prompt%%$'\n'*}" == "Now initiating Orchestrator mode..." ]]; then pass_case "codex run prompt first line is mode declaration"; else fail_case "codex run prompt first line is mode declaration"; fi if grep -Fq 'Do NOT ask clarifying questions before your first tool actions' <<< "$codex_run_prompt"; then pass_case "codex run prompt includes no-questions hard gate"; else fail_case "codex run prompt includes no-questions hard gate"; fi if grep -Fq '"next_task": "T-001"' <<< "$codex_run_prompt"; then pass_case "codex run prompt embeds capsule json"; else fail_case "codex run prompt embeds capsule json"; fi claude_run_prompt="$(MOSAIC_COORD_RUNTIME=claude bash "$SCRIPT_DIR/session-run.sh" --project "$tmp_project" --print)" -if [[ "$(printf '%s\n' "$claude_run_prompt" | head -n1)" == "## Continuation Mission" ]]; then pass_case "claude run prompt remains continuation prompt format"; else fail_case "claude run prompt remains continuation prompt format"; fi +if [[ "${claude_run_prompt%%$'\n'*}" == "## Continuation Mission" ]]; then pass_case "claude run prompt remains continuation prompt format"; else fail_case "claude run prompt remains continuation prompt format"; fi echo "" echo "Smoke test summary: pass=$PASS fail=$FAIL" diff --git a/packages/mosaic/framework/tools/orchestrator/test-board-roll.sh b/packages/mosaic/framework/tools/orchestrator/test-board-roll.sh index bf1ccb62..ef45d4a3 100644 --- a/packages/mosaic/framework/tools/orchestrator/test-board-roll.sh +++ b/packages/mosaic/framework/tools/orchestrator/test-board-roll.sh @@ -96,8 +96,8 @@ L="$WORK/live5.md"; G="$WORK/ledger5.md"; echo "# LEDGER" > "$G" make_board "$L" 6 1 400 before_l=$(cat "$L"); before_g=$(cat "$G") out=$(bash "$SUT" --live "$L" --ledger "$G" --cap 2000 --dry-run 2>&1) || note "dry-run exited nonzero: $out" -echo "$out" | grep -qi "dry run" || note "dry-run did not announce itself" -echo "$out" | grep -q "would roll" || note "dry-run did not report a plan" +grep -qi "dry run" <<<"$out" || note "dry-run did not announce itself" +grep -q "would roll" <<<"$out" || note "dry-run did not report a plan" [[ "$(cat "$L")" == "$before_l" ]] || note "dry-run modified LIVE" [[ "$(cat "$G")" == "$before_g" ]] || note "dry-run modified LEDGER" diff --git a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh index 3cf82945..bd2c820c 100644 --- a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh +++ b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh @@ -176,8 +176,12 @@ run_snap() { # Resolve the single pre-update-* snapshot dir under a state dir (newest if many). snap_dir() { - find "$1/mosaic/backups" -maxdepth 1 -type d -name 'pre-update-*' 2>/dev/null \ - | LC_ALL=C sort -r | head -1 + local -a snapshots=() + mapfile -t snapshots < <( + find "$1/mosaic/backups" -maxdepth 1 -type d -name 'pre-update-*' 2>/dev/null \ + | LC_ALL=C sort -r + ) + printf '%s\n' "${snapshots[0]:-}" } echo "── Part 1/2/3: durable snapshot scope, perms, no-leak ──────────────────" diff --git a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh index 1a8ea800..58ab8569 100644 --- a/packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh +++ b/packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh @@ -336,7 +336,7 @@ chk "[reset-fail] the manual-recovery pointer is emitted (not a silent set -e ex "grep -q 'Snapshot restore could not reset' '$OUTG'" chk "[reset-fail] the recovery message points at a preserved snapshot dir" \ "grep -q 'preserved at: .*mosaic-snapshot' '$OUTG'" -SNAP_E="$(grep -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTG" | head -1)" +SNAP_E="$(grep -m1 -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTG")" chk "[reset-fail] the named snapshot directory actually survives for recovery" \ "[ -n '$SNAP_E' ] && [ -d '$SNAP_E' ]" chk "[reset-fail] operator secret value never appears in installer output" \ @@ -353,7 +353,8 @@ chk "[control] without the D2 recovery line the operator gets no snapshot pointe "! grep -q 'Snapshot restore could not reset' '$OUTH'" [ -n "${SNAP_E:-}" ] && rm -rf "$SNAP_E" # Reap any snapshot the reset-fail runs left in /tmp (reset failed → never cleaned). -grep -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTH" 2>/dev/null | head -1 | while read -r s; do rm -rf "$s"; done +orphan_snapshot="$(grep -m1 -o '/[^ ]*mosaic-snapshot[^ ]*' "$OUTH" 2>/dev/null || true)" +[ -n "$orphan_snapshot" ] && rm -rf "$orphan_snapshot" # Cleanup (generated installer controls are also removed by the EXIT trap). for d in "$HA" "$REFA" "$HB" "$REFB" "$HC" "$HD" "$HE" "$REFE" "$HF" "$REFF" "$HG" "$HH"; do rm -rf "$d"; done diff --git a/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh b/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh index bbc67d62..8f2d90bb 100755 --- a/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh +++ b/packages/mosaic/framework/tools/tmux/test-send-message-socket.sh @@ -34,16 +34,20 @@ tmux new-session -d -s "$DEFAULT_TARGET" -c "$TMPDIR" 'PS1="❯ " exec bash --no "$SEND_MESSAGE" -L "$SOCKET" -t "=$TARGET" -m "named socket hello" >/tmp/send-message-named.out sleep 0.2 -capture_named | grep -qF "named socket hello" || fail "send-message.sh did not deliver to named socket" -if capture_default | grep -qF "named socket hello"; then +named_pane="$(capture_named)" || fail "could not capture named socket pane" +grep -qF "named socket hello" <<<"$named_pane" || fail "send-message.sh did not deliver to named socket" +default_pane="$(capture_default)" || fail "could not capture default socket pane" +if grep -qF "named socket hello" <<<"$default_pane"; then fail "send-message.sh leaked named-socket message to default tmux server" fi "$AGENT_SEND" -L "$SOCKET" -S "tester:source" -s "=$TARGET" -m "agent socket hello" >/tmp/agent-send-named.out sleep 0.2 -capture_named | grep -qF "[tester:source ->" || fail "agent-send.sh did not include preamble" -capture_named | grep -qF "agent socket hello" || fail "agent-send.sh did not deliver to named socket" -if capture_default | grep -qF "agent socket hello"; then +named_pane="$(capture_named)" || fail "could not capture named socket pane" +grep -qF "[tester:source ->" <<<"$named_pane" || fail "agent-send.sh did not include preamble" +grep -qF "agent socket hello" <<<"$named_pane" || fail "agent-send.sh did not deliver to named socket" +default_pane="$(capture_default)" || fail "could not capture default socket pane" +if grep -qF "agent socket hello" <<<"$default_pane"; then fail "agent-send.sh leaked named-socket message to default tmux server" fi @@ -65,11 +69,11 @@ done sleep 0.2 for i in $(seq 1 "$CONC_N"); do pane=$(tmux -L "$SOCKET" capture-pane -t "=conc-$i:0.0" -p) - printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${i}-END" \ + grep -qF "CONCPAYLOAD-${i}-END" <<<"$pane" \ || fail "concurrent send dropped payload for pane conc-$i" for j in $(seq 1 "$CONC_N"); do [ "$j" = "$i" ] && continue - if printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${j}-END"; then + if grep -qF "CONCPAYLOAD-${j}-END" <<<"$pane"; then fail "concurrent send cross-delivered payload $j to pane conc-$i" fi done diff --git a/packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh b/packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh index b1f9bbfe..f306b693 100755 --- a/packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh +++ b/packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh @@ -31,7 +31,7 @@ tmux -L "$SOCKET" new-session -d -s repl -c "$TMP" \ 'PS1="❯ " exec bash --noprofile --norc -i' sleep 0.3 out=$("$SEND" -L "$SOCKET" -t "=repl" -m "verdict fixture one delivered ok" 2>"$TMP/e1"); rc=$? -if [ "$rc" -eq 0 ] && printf '%s' "$out" | grep -qF "✓ delivered"; then +if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then ok "delivered: ❯-prompt REPL that submits => exit 0 ✓ delivered" else no "delivered: ❯-prompt REPL that submits => exit 0 ✓ delivered" "rc=$rc out=[$out] err=[$(cat "$TMP/e1")]" diff --git a/scripts/fixtures/pipefail-early-exit-test-baseline.json b/scripts/fixtures/pipefail-early-exit-test-baseline.json new file mode 100644 index 00000000..8e27105b --- /dev/null +++ b/scripts/fixtures/pipefail-early-exit-test-baseline.json @@ -0,0 +1,24 @@ +[ + "packages/mosaic/framework/systemd/user/test-fleet-units.sh:if tmux -L \"$TEST_SOCKET\" show-environment -g LD_PRELOAD 2>/dev/null | grep -q '^LD_PRELOAD='; then", + "packages/mosaic/framework/tools/git/test-issue-comment-readback.sh:write_response \"$(printf '%s' \"$result\" | head -n1)\" \"$(printf '%s' \"$result\" | tail -n +2)\"", + "packages/mosaic/framework/tools/git/test-issue-comment-readback.sh:write_response \"$(printf '%s' \"$result\" | head -n1)\" \"$(printf '%s' \"$result\" | tail -n +2)\"", + "packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh:contains() { printf '%s\\n' \"$1\" | grep -qx \"$2\"; }", + "packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh:write_response \"$(printf '%s' \"$result\" | head -n1)\" \"$(printf '%s' \"$result\" | tail -n +2)\"", + "packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh:echo \"$HELP_TEXT\" | grep -q -- '-r, --repo'", + "packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh:echo \"$HELP_TEXT\" | grep -q -- '-H, --host'", + "packages/mosaic/framework/tools/orchestrator/smoke-test.sh:if [[ \"$(printf '%s\\n' \"$codex_run_prompt\" | head -n1)\" == \"Now initiating Orchestrator mode...\" ]]; then pass_case \"codex run prompt first line is mode declaration\"; else fail_case \"codex run prompt first line is mode declaration\"; fi", + "packages/mosaic/framework/tools/orchestrator/smoke-test.sh:if [[ \"$(printf '%s\\n' \"$claude_run_prompt\" | head -n1)\" == \"## Continuation Mission\" ]]; then pass_case \"claude run prompt remains continuation prompt format\"; else fail_case \"claude run prompt remains continuation prompt format\"; fi", + "packages/mosaic/framework/tools/orchestrator/test-board-roll.sh:echo \"$out\" | grep -qi \"dry run\" || note \"dry-run did not announce itself\"", + "packages/mosaic/framework/tools/orchestrator/test-board-roll.sh:echo \"$out\" | grep -q \"would roll\" || note \"dry-run did not report a plan\"", + "packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh:find \"$1/mosaic/backups\" -maxdepth 1 -type d -name 'pre-update-*' 2>/dev/null | LC_ALL=C sort -r | head -1", + "packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh:SNAP_E=\"$(grep -o '/[^ ]*mosaic-snapshot[^ ]*' \"$OUTG\" | head -1)\"", + "packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh:grep -o '/[^ ]*mosaic-snapshot[^ ]*' \"$OUTH\" 2>/dev/null | head -1 | while read -r s; do rm -rf \"$s\"; done", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:capture_named | grep -qF \"named socket hello\" || fail \"send-message.sh did not deliver to named socket\"", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:if capture_default | grep -qF \"named socket hello\"; then", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:capture_named | grep -qF \"[tester:source ->\" || fail \"agent-send.sh did not include preamble\"", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:capture_named | grep -qF \"agent socket hello\" || fail \"agent-send.sh did not deliver to named socket\"", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:if capture_default | grep -qF \"agent socket hello\"; then", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:printf '%s' \"$pane\" | grep -qF \"CONCPAYLOAD-${i}-END\" || fail \"concurrent send dropped payload for pane conc-$i\"", + "packages/mosaic/framework/tools/tmux/test-send-message-socket.sh:if printf '%s' \"$pane\" | grep -qF \"CONCPAYLOAD-${j}-END\"; then", + "packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh:if [ \"$rc\" -eq 0 ] && printf '%s' \"$out\" | grep -qF \"✓ delivered\"; then" +] diff --git a/scripts/pipefail-early-exit.test.mjs b/scripts/pipefail-early-exit.test.mjs index 6d99875f..8de31293 100644 --- a/scripts/pipefail-early-exit.test.mjs +++ b/scripts/pipefail-early-exit.test.mjs @@ -7,6 +7,7 @@ import test from 'node:test'; const ROOT = new URL('../', import.meta.url); const EXPECTED_BASELINE_SITES = 26; +const EXPECTED_TEST_BASELINE_SITES = 22; const TARGETS = [ 'tools/matrix-presence-harness/run.sh', 'tools/e2e-install-test.sh', @@ -25,6 +26,17 @@ const TARGETS = [ 'packages/mosaic/framework/tools/wake/detector.sh', 'packages/mosaic/framework/tools/wake/digest.sh', 'packages/mosaic/framework/tools/wake/reconcile.sh', + 'packages/mosaic/framework/systemd/user/test-fleet-units.sh', + 'packages/mosaic/framework/tools/git/test-issue-comment-readback.sh', + 'packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh', + 'packages/mosaic/framework/tools/git/test-pr-review-gitea-comment.sh', + 'packages/mosaic/framework/tools/git/test-pr-review-repo-host-override.sh', + 'packages/mosaic/framework/tools/orchestrator/smoke-test.sh', + 'packages/mosaic/framework/tools/orchestrator/test-board-roll.sh', + 'packages/mosaic/framework/tools/quality/scripts/test-upgrade-durable-snapshot.sh', + 'packages/mosaic/framework/tools/quality/scripts/test-upgrade-rollback.sh', + 'packages/mosaic/framework/tools/tmux/test-send-message-socket.sh', + 'packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh', ]; // These statuses are explicitly non-load-bearing or unreachable at designed input. @@ -67,18 +79,31 @@ async function currentSources() { ); } -test('the registered baseline denominator is exactly 26 unsafe sites', async () => { - const baseline = JSON.parse( - await readFile(new URL('scripts/fixtures/pipefail-early-exit-baseline.json', ROOT), 'utf8'), - ); - assert.equal(baseline.length, EXPECTED_BASELINE_SITES); - assert.equal(new Set(baseline).size, EXPECTED_BASELINE_SITES); +async function assertBaselineFixture(file, expectedCount, expectedUnique = expectedCount) { + const baseline = JSON.parse(await readFile(new URL(file, ROOT), 'utf8')); + assert.equal(baseline.length, expectedCount); + assert.equal(new Set(baseline).size, expectedUnique); const fixtureSources = baseline.map((site) => { const separator = site.indexOf(':'); assert.ok(separator > 0, `invalid baseline site: ${site}`); return [site.slice(0, separator), site.slice(separator + 1)]; }); assert.deepEqual(scan(fixtureSources), baseline); +} + +test('the registered runtime baseline denominator is exactly 26 unsafe sites', async () => { + await assertBaselineFixture( + 'scripts/fixtures/pipefail-early-exit-baseline.json', + EXPECTED_BASELINE_SITES, + ); +}); + +test('the registered test baseline denominator is exactly 22 unsafe sites', async () => { + await assertBaselineFixture( + 'scripts/fixtures/pipefail-early-exit-test-baseline.json', + EXPECTED_TEST_BASELINE_SITES, + 21, + ); }); test('load-bearing pipefail paths do not pipe into early-exiting consumers', async () => {