bigpowers 2.89.0 → 2.89.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.pi/package.json +1 -1
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/scripts/bp-churn-rank.sh +3 -2
- package/scripts/check-spec-version-gap.sh +2 -1
- package/scripts/land-branch.sh +7 -3
- package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
- package/scripts/lib/__pycache__/srp_engine_okf.cpython-310.pyc +0 -0
- package/scripts/lib/golden-suite-gates.sh +2 -0
- package/scripts/lib/parallel-review-worktrees.sh +3 -1
- package/scripts/run-smoke.sh +3 -2
- package/scripts/test-pipefail-pipe-race.sh +68 -0
- package/scripts/validate-doctrine.sh +3 -3
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.89.1](https://github.com/danielvm-git/bigpowers/compare/v2.89.0...v2.89.1) (2026-11-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **scripts:** remove pipefail SIGPIPE races from git and long-output pipelines ([#186](https://github.com/danielvm-git/bigpowers/issues/186)) ([f55cbb7](https://github.com/danielvm-git/bigpowers/commit/f55cbb7d8883dd0af4777ac2467d5b550fde23db))
|
|
7
|
+
|
|
1
8
|
# [2.89.0](https://github.com/danielvm-git/bigpowers/compare/v2.88.18...v2.89.0) (2026-11-02)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/scripts/bp-churn-rank.sh
CHANGED
|
@@ -45,5 +45,6 @@ git log --since="$SINCE" --name-only --pretty=format: $PATHSPEC \
|
|
|
45
45
|
| sort \
|
|
46
46
|
| uniq -c \
|
|
47
47
|
| sort -rn \
|
|
48
|
-
|
|
|
49
|
-
|
|
48
|
+
| awk -v limit="$LIMIT" 'NR <= limit {printf "%4d %s\n", $1, $2}'
|
|
49
|
+
# awk reads to the end instead of `head -n`, which closed the pipe early and
|
|
50
|
+
# made `sort` die with SIGPIPE under pipefail (BUG-2026-11-02-pipefail-pipe-race).
|
|
@@ -81,7 +81,8 @@ yaml_value_set() {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
has_dirty_specs() {
|
|
84
|
-
|
|
84
|
+
# Test the captured output, not `git status | grep -q .` — a pipefail race (BUG-2026-11-02-pipefail-pipe-race).
|
|
85
|
+
[[ -d "$SPECS" ]] && [[ -n "$(git -C "$PROJECT_DIR" status --porcelain -- specs/ 2>/dev/null)" ]]
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
is_blocking_active_flow() {
|
package/scripts/land-branch.sh
CHANGED
|
@@ -86,8 +86,11 @@ if ! git show-ref --verify --quiet "refs/heads/$FEATURE_BRANCH"; then
|
|
|
86
86
|
land_branch_deny "Feature branch '$FEATURE_BRANCH' does not exist"
|
|
87
87
|
fi
|
|
88
88
|
|
|
89
|
-
# Scan all commits in feature branch for Co-authored-by: footers
|
|
90
|
-
|
|
89
|
+
# Scan all commits in feature branch for Co-authored-by: footers.
|
|
90
|
+
# Capture first: `git log | grep -q` under pipefail fails at random when grep
|
|
91
|
+
# exits before git finishes writing (SIGPIPE) — BUG-2026-11-02-pipefail-pipe-race.
|
|
92
|
+
FEATURE_BODIES="$(git log "$DEFAULT_BRANCH..$FEATURE_BRANCH" --format="%B" 2>/dev/null || true)"
|
|
93
|
+
if grep -qiE '^co[- ]authored[- ]by:' <<<"$FEATURE_BODIES"; then
|
|
91
94
|
land_branch_deny "Feature branch '$FEATURE_BRANCH' contains Co-authored-by: footer(s). Amend commits to remove all AI agent attribution before landing."
|
|
92
95
|
fi
|
|
93
96
|
|
|
@@ -194,7 +197,8 @@ fi
|
|
|
194
197
|
|
|
195
198
|
# Worktree cleanup
|
|
196
199
|
WORKTREE_PATH="../$FEATURE_BRANCH"
|
|
197
|
-
|
|
200
|
+
WORKTREES="$(git worktree list --porcelain 2>/dev/null || true)"
|
|
201
|
+
if grep -q "^worktree $WORKTREE_PATH$" <<<"$WORKTREES"; then
|
|
198
202
|
echo "==> Removing worktree $WORKTREE_PATH"
|
|
199
203
|
git worktree remove "$WORKTREE_PATH" 2>/dev/null || git worktree remove -f "$WORKTREE_PATH"
|
|
200
204
|
fi
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -79,6 +79,8 @@ GOLDEN_GATES=(
|
|
|
79
79
|
"specs-parse:bash scripts/validate-specs-yaml.sh:false"
|
|
80
80
|
# The runner itself: failing output is shown, skips are named and reasoned.
|
|
81
81
|
"golden-gate-runner:bash scripts/test-golden-gate-runner.sh:false"
|
|
82
|
+
# Pipefail SIGPIPE races: fix shapes hold, git output never feeds grep -q/head.
|
|
83
|
+
"pipefail-pipe-race:bash scripts/test-pipefail-pipe-race.sh:false"
|
|
82
84
|
# One pipeline, release needs: every gate, pinned runner and actions.
|
|
83
85
|
"ci-pipeline-contract-selftest:bash scripts/test-ci-pipeline-contract.sh --self-test:false"
|
|
84
86
|
"ci-pipeline-contract:bash scripts/test-ci-pipeline-contract.sh:false"
|
|
@@ -11,7 +11,9 @@ cd "$REPO_ROOT"
|
|
|
11
11
|
BASE=".bigpowers/worktrees/review-${CHECK}"
|
|
12
12
|
mkdir -p "$(dirname "$BASE")"
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# Capture first: `git worktree list | grep -q` is a pipefail race (BUG-2026-11-02-pipefail-pipe-race).
|
|
15
|
+
WORKTREES="$(git worktree list)"
|
|
16
|
+
if grep -q "$BASE" <<<"$WORKTREES"; then
|
|
15
17
|
git worktree remove --force "$BASE" 2>/dev/null || true
|
|
16
18
|
fi
|
|
17
19
|
|
package/scripts/run-smoke.sh
CHANGED
|
@@ -46,7 +46,8 @@ run_single_url_check() {
|
|
|
46
46
|
|
|
47
47
|
# Assert content signal
|
|
48
48
|
if [ -n "$content_signal" ]; then
|
|
49
|
-
|
|
49
|
+
# Here-string, not `echo "$body" | grep -q`: a large body races pipefail (BUG-2026-11-02-pipefail-pipe-race).
|
|
50
|
+
if grep -qiE "$content_signal" <<<"$body"; then
|
|
50
51
|
echo " PASS: body matches \"$content_signal\""
|
|
51
52
|
else
|
|
52
53
|
echo " FAIL: body does not match \"$content_signal\""
|
|
@@ -165,7 +166,7 @@ run_parsed_check() {
|
|
|
165
166
|
|
|
166
167
|
# Assert content signal
|
|
167
168
|
if [ -n "$content_signal" ]; then
|
|
168
|
-
if
|
|
169
|
+
if grep -qiE "$content_signal" <<<"$body"; then
|
|
169
170
|
echo " PASS: body matches \"$content_signal\""
|
|
170
171
|
checks_passed=$((checks_passed + 1))
|
|
171
172
|
else
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: BUG-2026-11-02-pipefail-pipe-race
|
|
3
|
+
# test-pipefail-pipe-race.sh — regression guard for SIGPIPE races under pipefail.
|
|
4
|
+
#
|
|
5
|
+
# `producer | grep -q` (or `| head`) fails at random under `set -o pipefail`:
|
|
6
|
+
# the reader exits early, the producer dies with SIGPIPE (141), and pipefail
|
|
7
|
+
# reports the pipeline as failed — a found match reads as "not found".
|
|
8
|
+
#
|
|
9
|
+
# (a) anti-vacuity: the racy shape really fails on this host
|
|
10
|
+
# (b) each fix shape used in the repo passes RUNS times with a long producer
|
|
11
|
+
# (c) static guard: no pipefail script pipes git output into an early-exit reader
|
|
12
|
+
#
|
|
13
|
+
# Usage: bash scripts/test-pipefail-pipe-race.sh
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
|
|
17
|
+
resolve_repo_root
|
|
18
|
+
cd "$REPO_ROOT"
|
|
19
|
+
|
|
20
|
+
source scripts/lib/test-assertions.sh
|
|
21
|
+
TA_PASS=0
|
|
22
|
+
TA_FAIL=0
|
|
23
|
+
RUNS=20
|
|
24
|
+
|
|
25
|
+
# ~2 MB of output: far past any pipe buffer, so an early reader always wins.
|
|
26
|
+
long_producer() { seq 1 300000; }
|
|
27
|
+
|
|
28
|
+
# Runs "$@" RUNS times; prints how many runs returned non-zero.
|
|
29
|
+
count_failures() {
|
|
30
|
+
local i fails=0
|
|
31
|
+
for ((i = 0; i < RUNS; i++)); do
|
|
32
|
+
"$@" || fails=$((fails + 1))
|
|
33
|
+
done
|
|
34
|
+
echo "$fails"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
racy_grep_q() { long_producer | grep -q '^1$'; }
|
|
38
|
+
fixed_capture() { local out; out="$(long_producer)"; grep -q '^1$' <<<"$out"; }
|
|
39
|
+
fixed_nonempty() { [[ -n "$(long_producer)" ]]; }
|
|
40
|
+
fixed_awk_limit() { long_producer | awk -v limit=3 'NR <= limit' >/dev/null; }
|
|
41
|
+
fixed_sed_range() { long_producer | sed -n '1,5p' >/dev/null; }
|
|
42
|
+
|
|
43
|
+
echo "=== test-pipefail-pipe-race.sh ==="
|
|
44
|
+
|
|
45
|
+
fails=$(count_failures racy_grep_q)
|
|
46
|
+
[[ "$fails" -gt 0 ]] \
|
|
47
|
+
&& ta_pass "(a) racy 'producer | grep -q' fails under pipefail ($fails/$RUNS)" \
|
|
48
|
+
|| ta_fail "(a) racy shape never failed — test cannot detect the race on this host"
|
|
49
|
+
|
|
50
|
+
for fix in fixed_capture fixed_nonempty fixed_awk_limit fixed_sed_range; do
|
|
51
|
+
fails=$(count_failures "$fix")
|
|
52
|
+
[[ "$fails" -eq 0 ]] \
|
|
53
|
+
&& ta_pass "(b) $fix: 0/$RUNS failures" \
|
|
54
|
+
|| ta_fail "(b) $fix: $fails/$RUNS failures"
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# run-skill-verify.sh holds `git diff ... | head -20` as a fixture string of
|
|
58
|
+
# fail-open verify directives, not as a command it runs.
|
|
59
|
+
hits=$(grep -rlE 'pipefail' scripts hooks --include='*.sh' \
|
|
60
|
+
| grep -v '^scripts/run-skill-verify\.sh$' \
|
|
61
|
+
| xargs grep -nE 'git [a-z-]+[^|]*\|[[:space:]]*(grep -[a-zA-Z]*[qm]|head)' \
|
|
62
|
+
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' || true)
|
|
63
|
+
[[ -z "$hits" ]] \
|
|
64
|
+
&& ta_pass "(c) no pipefail script pipes git output into grep -q/-m or head" \
|
|
65
|
+
|| ta_fail "(c) git output piped into an early-exit reader:"$'\n'"$hits"
|
|
66
|
+
|
|
67
|
+
echo "$TA_PASS passed, $TA_FAIL failed"
|
|
68
|
+
[[ "$TA_FAIL" -eq 0 ]]
|
|
@@ -55,7 +55,7 @@ else
|
|
|
55
55
|
grep -rE "PLAN\.md|STATE\.md|PROJECT\.md|CONTEXT\.md|SUMMARY\.md|VERIFICATION\.md|RESEARCH\.md|RELEASE-PLAN\.md" \
|
|
56
56
|
docs/ 2>/dev/null \
|
|
57
57
|
| grep -v "docs/archive/" \
|
|
58
|
-
| grep -v "specs/archive" |
|
|
58
|
+
| grep -v "specs/archive" | sed -n '1,20p' >&2 # sed reads to the end; `head` races pipefail (BUG-2026-11-02-pipefail-pipe-race)
|
|
59
59
|
fi
|
|
60
60
|
|
|
61
61
|
# ── Epic 1: Skill count consistency ───────────────────────────────────────────
|
|
@@ -94,7 +94,7 @@ else
|
|
|
94
94
|
2>/dev/null \
|
|
95
95
|
| grep -v "specs/archive\|specs/epics/archive\|docs/archive/\|\.git\|node_modules\|\.gemini\|\.cursor" \
|
|
96
96
|
| grep -v "CHANGELOG\.md\|specs/verifications/reports/\|PLAN-evolve-structure\.md" \
|
|
97
|
-
|
|
|
97
|
+
| sed -n '1,10p' >&2
|
|
98
98
|
fi
|
|
99
99
|
|
|
100
100
|
# ── Epic 3: BCP slop guard ────────────────────────────────────────────────────
|
|
@@ -107,7 +107,7 @@ if [[ "$BCP_SLOP" -eq 0 ]]; then
|
|
|
107
107
|
else
|
|
108
108
|
doctrine_fail "'Build Commit Point' found ($BCP_SLOP hits) — canonical term is 'Business Complexity Points'; see docs/references/bcp.md"
|
|
109
109
|
grep -ri "build commit point" . 2>/dev/null \
|
|
110
|
-
| grep -v "specs/archive\|\.git\|node_modules\|\.gemini\|\.cursor\|validate-doctrine\.sh" |
|
|
110
|
+
| grep -v "specs/archive\|\.git\|node_modules\|\.gemini\|\.cursor\|validate-doctrine\.sh" | sed -n '1,5p' >&2
|
|
111
111
|
fi
|
|
112
112
|
|
|
113
113
|
# ── Epic 4: SKILL.md size cap ─────────────────────────────────────────────────
|