Remediation of coder3 independent-validation blocker on PR #547. lane-brief.sh inspected only the open-PR index/title/head fields, never the PR BODY or Gitea issue linkage. A body-only "Closes #546" was therefore invisible, so issue #546 (open, with PR #547 'Closes #546' in its body) was placed under DISPATCH CANDIDATES with work-underway count 0 — re-dispatchable in-flight work, unacceptable for a dispatch-truth tool. Fix: - Fetch open PRs as JSON including `body`; resolve PR->issue links via Gitea's closing-keyword set (close/closes/closed, fix/fixes/fixed, resolve/resolves/ resolved), case-insensitive, word-boundary anchored, `#` directly following the keyword. Any issue so linked from an OPEN PR is classified WORK UNDERWAY. - Preserve the prior title/head bare-ref heuristic and per-repo behavior; require `#` immediately after the keyword so cross-repo `owner/repo#N` forms don't leak. - Bare `#N` prose mentions in a body are intentionally NOT links (e.g. "#538 line of work") to avoid marking live, dispatchable issues as in-flight. Tests (committed, RED-on-revert non-vacuity): - test-lane-brief-pr-linkage.sh: open-PR-with-'Closes #546'-in-body excludes #546 from candidates (and a reverted copy with the body-scan removed regresses #546 to a candidate — RED proof); bare #777 and substring 'hotfix #999' stay candidates (word-boundary + closing-keyword-only guards). - test-ci-wait-exit-matrix.sh: ci-wait.sh exit matrix 0 (all-success) / 1 (terminal-not-success: failure + error/killed) / 2 (usage) / 3 (timeout). shellcheck -x + bash -n clean on all four files; no secret values. ci-wait.sh unchanged (coder3 PASS preserved). Closed-issue exclusion unchanged. Refs #546, PR #547 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
115 lines
5.4 KiB
Bash
115 lines
5.4 KiB
Bash
#!/usr/bin/env bash
|
|
# Regression harness for lane-brief.sh PR->issue linkage classification.
|
|
#
|
|
# Covers the #546/#547 defect: lane-brief.sh inspected only the PR index/title/head
|
|
# fields and never the PR BODY, so an open PR whose body says "Closes #546" did not
|
|
# mark issue #546 as work-underway — #546 was listed as a DISPATCH CANDIDATE and was
|
|
# re-dispatchable in-flight work.
|
|
#
|
|
# Asserts:
|
|
# 1. an open issue closed-keyword-linked from a PR BODY ("Closes #546") is
|
|
# classified WORK UNDERWAY, not a dispatch candidate.
|
|
# 2. a BARE "#777" prose mention in a PR body does NOT classify #777 as
|
|
# work-underway (only Gitea closing keywords are a real link) — #777 stays a
|
|
# dispatch candidate.
|
|
# 3. NON-VACUITY / RED-ON-REVERT: a copy of the script with the body-scan removed
|
|
# misclassifies #546 as a dispatch candidate — proving the body-scan is exactly
|
|
# what fixes the defect and that assertion 1 fails if the fix is reverted.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LANE_BRIEF="$SCRIPT_DIR/lane-brief.sh"
|
|
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/lane-brief-pr-linkage}"
|
|
BIN_DIR="$WORK_DIR/bin"
|
|
|
|
rm -rf "$WORK_DIR"
|
|
mkdir -p "$BIN_DIR"
|
|
|
|
# --- fake `tea`: serves a fixed open-issue set and one open PR. ----------------
|
|
# PR #547 body uses a closing keyword for #546 ("Closes #546") and a BARE mention
|
|
# of #777 ("the #777 line of work"). #777 must NOT be treated as linked.
|
|
cat > "$BIN_DIR/tea" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
case "${1:-} ${2:-}" in
|
|
"issues list")
|
|
cat <<'JSON'
|
|
[
|
|
{"index":"546","title":"lane-brief + ci-wait orchestration tooling","assignees":[],"milestone":null,"labels":""},
|
|
{"index":"777","title":"unrelated downstream item","assignees":[],"milestone":null,"labels":""},
|
|
{"index":"999","title":"item only named inside the word hotfix","assignees":[],"milestone":null,"labels":""}
|
|
]
|
|
JSON
|
|
;;
|
|
"pulls list")
|
|
cat <<'JSON'
|
|
[
|
|
{"index":"547","title":"feat(framework/tools): orchestration helpers","head":"feat/orchestration-tools-lane-brief-ci-wait","body":"Two additive orchestration tools.\n\nCloses #546.\n\nLogin resolution is relevant to the #777 line of work but does not touch it.\nThis shipped as a hotfix #999 earlier — that bare reference must not link it.\n\nFixes #546\n"}
|
|
]
|
|
JSON
|
|
;;
|
|
*)
|
|
echo "fake-tea: unhandled: $*" >&2; exit 1 ;;
|
|
esac
|
|
SH
|
|
chmod +x "$BIN_DIR/tea"
|
|
|
|
run_brief() { # $1 = script path
|
|
PATH="$BIN_DIR:$PATH" "$1" -r mosaic/stack -L test-login 2>/dev/null
|
|
}
|
|
|
|
# Extract the issue numbers under a named section header until the next blank line.
|
|
section_nums() { # $1 = output $2 = header-prefix
|
|
printf '%s\n' "$1" | awk -v h="$2" '
|
|
index($0,h)==1 {grab=1; next}
|
|
grab && /^[[:space:]]*$/ {grab=0}
|
|
grab && match($0, /#[0-9]+/) { print substr($0, RSTART+1, RLENGTH-1) }
|
|
'
|
|
}
|
|
|
|
fail() { echo "FAIL: $1" >&2; exit 1; }
|
|
contains() { printf '%s\n' "$1" | grep -qx "$2"; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixed (current) script behavior
|
|
# ---------------------------------------------------------------------------
|
|
OUT="$(run_brief "$LANE_BRIEF")"
|
|
CAND="$(section_nums "$OUT" 'DISPATCH CANDIDATES')"
|
|
UNDER="$(section_nums "$OUT" 'WORK UNDERWAY')"
|
|
|
|
echo "--- lane-brief output (fixed) ---"; printf '%s\n' "$OUT"
|
|
echo "--- candidates: [$(printf '%s' "$CAND" | tr '\n' ' ')] underway: [$(printf '%s' "$UNDER" | tr '\n' ' ')] ---"
|
|
|
|
contains "$UNDER" 546 || fail "#546 (PR body 'Closes #546') should be WORK UNDERWAY"
|
|
contains "$CAND" 546 && fail "#546 must NOT be a dispatch candidate (it has an open PR)"
|
|
contains "$CAND" 777 || fail "#777 (only a bare prose mention) should remain a dispatch candidate"
|
|
contains "$UNDER" 777 && fail "#777 must NOT be work-underway — bare body mentions are not links"
|
|
contains "$CAND" 999 || fail "#999 ('hotfix #999' — keyword is a substring) should remain a candidate"
|
|
contains "$UNDER" 999 && fail "#999 must NOT be work-underway — word-boundary must reject 'hotfix'"
|
|
echo "PASS: body closing-keyword link classifies #546 underway; bare #777 / substring #999 stay candidates"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# NON-VACUITY: revert the body-scan and prove #546 regresses to a candidate.
|
|
# ---------------------------------------------------------------------------
|
|
REVERTED="$SCRIPT_DIR/.lane-brief.reverted.$$.sh"
|
|
trap 'rm -f "$REVERTED"' EXIT
|
|
# Drop the PR_BODY_REFS contribution from the union (simulates the pre-fix script
|
|
# that only looked at index/title/head). Sibling `source detect-platform.sh` still
|
|
# resolves because the copy lives in the same dir.
|
|
# shellcheck disable=SC2016 # single-quoted on purpose: sed needs the literal $PR_BODY_REFS
|
|
sed 's/"\$PR_BODY_REFS"/""/' "$LANE_BRIEF" > "$REVERTED"
|
|
chmod +x "$REVERTED"
|
|
grep -q 'PR_BODY_REFS' "$REVERTED" || fail "revert sed anchor not found — test is stale"
|
|
|
|
ROUT="$(run_brief "$REVERTED")"
|
|
RCAND="$(section_nums "$ROUT" 'DISPATCH CANDIDATES')"
|
|
RUNDER="$(section_nums "$ROUT" 'WORK UNDERWAY')"
|
|
echo "--- candidates(reverted): [$(printf '%s' "$RCAND" | tr '\n' ' ')] underway: [$(printf '%s' "$RUNDER" | tr '\n' ' ')] ---"
|
|
|
|
contains "$RCAND" 546 || fail "non-vacuity broken: reverted script should misclassify #546 as a candidate"
|
|
contains "$RUNDER" 546 && fail "non-vacuity broken: reverted script should NOT mark #546 underway"
|
|
echo "PASS (RED-on-revert): without the body-scan, #546 regresses to a dispatch candidate"
|
|
|
|
echo "ALL PASS: test-lane-brief-pr-linkage.sh"
|