fix: investigation: CI exhaustion pattern on chat sub-issues #707 and #712 — 3+ failures each (#742) #754

Merged
dev-bot merged 1 commit from fix/issue-742 into main 2026-04-14 22:05:36 +00:00

View file

@ -98,50 +98,38 @@ echo "syntax check done"
echo "=== 2/2 Function resolution ===" echo "=== 2/2 Function resolution ==="
# Required lib files for LIB_FUNS construction. Missing any of these means the # Enumerate ALL lib/*.sh files in stable lexicographic order (#742).
# checkout is incomplete or the test is misconfigured — fail loudly, do NOT # Previous approach used a hand-maintained REQUIRED_LIBS list, which silently
# silently produce a partial LIB_FUNS list (that masquerades as "undef" errors # became incomplete as new libs were added, producing partial LIB_FUNS that
# in unrelated scripts; see #600). # caused non-deterministic "undef" failures.
REQUIRED_LIBS=(
lib/agent-sdk.sh lib/env.sh lib/ci-helpers.sh lib/load-project.sh
lib/secret-scan.sh lib/formula-session.sh lib/mirrors.sh lib/guard.sh
lib/pr-lifecycle.sh lib/issue-lifecycle.sh lib/worktree.sh
)
for f in "${REQUIRED_LIBS[@]}"; do
if [ ! -f "$f" ]; then
printf 'FAIL [missing-lib] expected %s but it is not present at smoke time\n' "$f" >&2
printf ' pwd=%s\n' "$(pwd)" >&2
printf ' ls lib/=%s\n' "$(ls lib/ 2>&1 | tr '\n' ' ')" >&2
echo '=== SMOKE TEST FAILED (precondition) ===' >&2
exit 2
fi
done
# Functions provided by shared lib files (available to all agent scripts via source).
# #
# Included — these are inline-sourced by agent scripts: # Excluded from LIB_FUNS (not sourced inline by agents):
# lib/env.sh — sourced by every agent (log, forge_api, etc.)
# lib/agent-sdk.sh — sourced by SDK agents (agent_run, agent_recover_session)
# lib/ci-helpers.sh — sourced by pollers and review (ci_passed, classify_pipeline_failure, etc.)
# lib/load-project.sh — sourced by env.sh when PROJECT_TOML is set
# lib/secret-scan.sh — standalone CLI tool, run directly (not sourced)
# lib/formula-session.sh — sourced by formula-driven agents (acquire_run_lock, check_memory, etc.)
# lib/mirrors.sh — sourced by merge sites (mirror_push)
# lib/guard.sh — sourced by all polling-loop entry points (check_active)
# lib/issue-lifecycle.sh — sourced by agents for issue claim/release/block/deps
# lib/worktree.sh — sourced by agents for worktree create/recover/cleanup/preserve
#
# Excluded — not sourced inline by agents:
# lib/tea-helpers.sh — sourced conditionally by env.sh (tea_file_issue, etc.); checked standalone below
# lib/ci-debug.sh — standalone CLI tool, run directly (not sourced) # lib/ci-debug.sh — standalone CLI tool, run directly (not sourced)
# lib/parse-deps.sh — executed via `bash lib/parse-deps.sh` (not sourced) # lib/parse-deps.sh — executed via `bash lib/parse-deps.sh` (not sourced)
# lib/hooks/*.sh — Claude Code hook scripts, executed by the harness (not sourced) # lib/hooks/*.sh — Claude Code hook scripts, executed by the harness (not sourced)
# EXCLUDED_LIBS="lib/ci-debug.sh lib/parse-deps.sh"
# If a new lib file is added and sourced by agents, add it to LIB_FUNS below
# and add a check_script call for it in the lib files section further down. # Build the list of lib files in deterministic order (LC_ALL=C sort).
# Fail loudly if no lib files are found — checkout is broken.
mapfile -t ALL_LIBS < <(LC_ALL=C find lib -maxdepth 1 -name '*.sh' -print | LC_ALL=C sort)
if [ "${#ALL_LIBS[@]}" -eq 0 ]; then
echo 'FAIL [no-libs] no lib/*.sh files found at smoke time' >&2
printf ' pwd=%s\n' "$(pwd)" >&2
echo '=== SMOKE TEST FAILED (precondition) ===' >&2
exit 2
fi
# Build LIB_FUNS from all non-excluded lib files.
# Use set -e inside the subshell so a failed get_fns aborts loudly
# instead of silently shrinking the function list.
LIB_FUNS=$( LIB_FUNS=$(
for f in "${REQUIRED_LIBS[@]}"; do get_fns "$f"; done | sort -u set -e
for f in "${ALL_LIBS[@]}"; do
# shellcheck disable=SC2086
skip=0; for ex in $EXCLUDED_LIBS; do [ "$f" = "$ex" ] && skip=1; done
[ "$skip" -eq 1 ] && continue
get_fns "$f"
done | sort -u
) )
# Known external commands and shell builtins — never flag these # Known external commands and shell builtins — never flag these
@ -192,13 +180,14 @@ check_script() {
while IFS= read -r fn; do while IFS= read -r fn; do
[ -z "$fn" ] && continue [ -z "$fn" ] && continue
is_known_cmd "$fn" && continue is_known_cmd "$fn" && continue
if ! printf '%s\n' "$all_fns" | grep -qxF "$fn"; then # Use here-string (<<<) instead of pipe to avoid SIGPIPE race (#742):
# with pipefail, `printf | grep -q` can fail when grep closes the pipe
# early after finding a match, causing printf to get SIGPIPE (exit 141).
# This produced non-deterministic false "undef" failures.
if ! grep -qxF "$fn" <<< "$all_fns"; then
printf 'FAIL [undef] %s: %s\n' "$script" "$fn" printf 'FAIL [undef] %s: %s\n' "$script" "$fn"
# Diagnostic dump (#600): if the function is expected to be in a known lib, printf ' all_fns count: %d\n' "$(grep -c . <<< "$all_fns")"
# print what the actual all_fns set looks like so we can tell whether the printf ' LIB_FUNS contains "%s": %s\n' "$fn" "$(grep -cxF "$fn" <<< "$LIB_FUNS")"
# function is genuinely missing or whether the resolution loop is broken.
printf ' all_fns count: %d\n' "$(printf '%s\n' "$all_fns" | wc -l)"
printf ' LIB_FUNS contains "%s": %s\n' "$fn" "$(printf '%s\n' "$LIB_FUNS" | grep -cxF "$fn")"
printf ' defining lib (if any): %s\n' "$(grep -l "^[[:space:]]*${fn}[[:space:]]*()" lib/*.sh 2>/dev/null | tr '\n' ' ')" printf ' defining lib (if any): %s\n' "$(grep -l "^[[:space:]]*${fn}[[:space:]]*()" lib/*.sh 2>/dev/null | tr '\n' ' ')"
FAILED=1 FAILED=1
fi fi