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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.89.0",
3
+ "version": "2.89.1",
4
4
  "description": "80 skills — 73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "keywords": [
6
6
  "gemini-extension"
package/.pi/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.89.0",
3
+ "version": "2.89.1",
4
4
  "description": "80 skills — 73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "keywords": [
6
6
  "pi-package"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.89.0",
3
+ "version": "2.89.1",
4
4
  "description": "73 agent skills synthesizing 17 years of software engineering discipline into a prescriptive methodology for solo developers",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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
- | head -n "$LIMIT" \
49
- | awk '{printf "%4d %s\n", $1, $2}'
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
- [[ -d "$SPECS" ]] && git -C "$PROJECT_DIR" status --porcelain -- specs/ 2>/dev/null | grep -q .
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() {
@@ -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
- if git log "$DEFAULT_BRANCH..$FEATURE_BRANCH" --format="%B" 2>/dev/null | grep -qiE '^co[- ]authored[- ]by:'; then
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
- if git worktree list --porcelain 2>/dev/null | grep -q "^worktree $WORKTREE_PATH$"; then
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
@@ -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
- if git worktree list | grep -q "$BASE"; then
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
 
@@ -46,7 +46,8 @@ run_single_url_check() {
46
46
 
47
47
  # Assert content signal
48
48
  if [ -n "$content_signal" ]; then
49
- if echo "$body" | grep -qiE "$content_signal"; then
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 echo "$body" | grep -qiE "$content_signal"; then
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" | head -20 >&2
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
- | head -10 >&2
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" | head -5 >&2
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 ─────────────────────────────────────────────────