bigpowers 2.88.12 → 2.88.14

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.
Files changed (34) hide show
  1. package/.agents/skills/gate-trace/SKILL.md +2 -2
  2. package/.cline/skills/gate-trace/SKILL.md +2 -2
  3. package/.codebuddy/skills/gate-trace/SKILL.md +2 -2
  4. package/.codex/skills/gate-trace/SKILL.md +2 -2
  5. package/.copilot/skills/gate-trace/SKILL.md +2 -2
  6. package/.cursor/rules/gate-trace.mdc +2 -2
  7. package/.gemini/extensions/bigpowers/commands/prompts/gate-trace.md +2 -2
  8. package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
  9. package/.gemini/extensions/bigpowers/skills/gate-trace/SKILL.md +2 -2
  10. package/.hermes/skills/gate-trace/SKILL.md +2 -2
  11. package/.kilocode/rules/gate-trace.md +2 -2
  12. package/.mimocode/skills/gate-trace/SKILL.md +2 -2
  13. package/.opencode/skills/gate-trace/SKILL.md +2 -2
  14. package/.pi/package.json +1 -1
  15. package/.pi/prompts/gate-trace.md +2 -2
  16. package/.pi/skills/gate-trace/SKILL.md +2 -2
  17. package/.qwen/skills/gate-trace/SKILL.md +2 -2
  18. package/.trae/skills/gate-trace/SKILL.md +2 -2
  19. package/.windsurf/rules/gate-trace.md +2 -2
  20. package/.zcode/skills/gate-trace/SKILL.md +2 -2
  21. package/CHANGELOG.md +14 -0
  22. package/CLAUDE.md +1 -1
  23. package/package.json +1 -1
  24. package/scripts/golden-g13-script-orphans.sh +7 -5
  25. package/scripts/lib/__pycache__/link_utils.cpython-310.pyc +0 -0
  26. package/scripts/lib/__pycache__/srp_engine_links.cpython-310.pyc +0 -0
  27. package/scripts/lib/golden-suite-gates.sh +43 -3
  28. package/scripts/run-story-verify.sh +112 -6
  29. package/scripts/test-ci-pipeline-contract.sh +162 -0
  30. package/scripts/test-golden-gate-runner.sh +59 -0
  31. package/skills/gate-trace/SKILL.md +2 -2
  32. package/skills-lock.json +1 -1
  33. package/scripts/lib/__pycache__/link_utils.cpython-314.pyc +0 -0
  34. package/scripts/lib/__pycache__/srp_engine_links.cpython-314.pyc +0 -0
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -69,8 +69,8 @@ gate_trace:
69
69
  ## Integration Points
70
70
 
71
71
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
72
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
73
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
72
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
73
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
74
74
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
75
75
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
76
76
 
@@ -69,8 +69,8 @@ gate_trace:
69
69
  ## Integration Points
70
70
 
71
71
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
72
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
73
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
72
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
73
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
74
74
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
75
75
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
76
76
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.88.12",
3
+ "version": "2.88.14",
4
4
  "description": "81 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"
@@ -71,8 +71,8 @@ gate_trace:
71
71
  ## Integration Points
72
72
 
73
73
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
74
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
75
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
74
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
75
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
76
76
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
77
77
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
78
78
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
package/.pi/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.88.12",
3
+ "version": "2.88.14",
4
4
  "description": "81 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"
@@ -69,8 +69,8 @@ gate_trace:
69
69
  ## Integration Points
70
70
 
71
71
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
72
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
73
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
72
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
73
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
74
74
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
75
75
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
76
76
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
@@ -70,8 +70,8 @@ gate_trace:
70
70
  ## Integration Points
71
71
 
72
72
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
73
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
74
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
73
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
74
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
75
75
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
76
76
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
77
77
 
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [2.88.14](https://github.com/danielvm-git/bigpowers/compare/v2.88.13...v2.88.14) (2026-10-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **ci:** gate release on every check in one test-build-release pipeline ([#162](https://github.com/danielvm-git/bigpowers/issues/162)) ([14250d8](https://github.com/danielvm-git/bigpowers/commit/14250d843011da03f766de59f18371276fe16478))
7
+
8
+ ## [2.88.13](https://github.com/danielvm-git/bigpowers/compare/v2.88.12...v2.88.13) (2026-10-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **ci:** cut golden suite critical path by removing duplicate gate work ([#161](https://github.com/danielvm-git/bigpowers/issues/161)) ([2db0bb9](https://github.com/danielvm-git/bigpowers/commit/2db0bb98663ae7f44e1990f06e9f90b6db60a773))
14
+
1
15
  ## [2.88.12](https://github.com/danielvm-git/bigpowers/compare/v2.88.11...v2.88.12) (2026-10-25)
2
16
 
3
17
 
package/CLAUDE.md CHANGED
@@ -68,7 +68,7 @@ Stack: Markdown / Bash (documentation-based; skills integrate with Claude Code,
68
68
  | Lint | `bash scripts/sync-skills.sh` (validates SKILL.md syntax) |
69
69
  | Validate specs YAML | `bash scripts/validate-specs-yaml.sh` |
70
70
  | Typecheck | N/A (Markdown / Bash project) |
71
- | CI platform | GitHub Actions (`.github/workflows/publish.yml`, `sync-skills.yml`, `golden-suite.yml`) |
71
+ | CI platform | GitHub Actions — one pipeline `.github/workflows/test-build-release.yml` (sync, verification-gates, skill-verify, story-verify → release); plus `docs-site.yml`, `publish-wiki.yml` |
72
72
  | Compliance | `npm run compliance` |
73
73
  | Verification Gates | `bash scripts/run-verification-gates.sh` |
74
74
  | Traceability | `bash scripts/trace-stories.sh --strict` | grep for story tags (traceability check) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.88.12",
3
+ "version": "2.88.14",
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": {
@@ -53,12 +53,14 @@ is_manual_utility() { # $1 = script path
53
53
  }
54
54
 
55
55
  is_referenced() { # $1 = script basename, $2... = candidate file globs already expanded
56
+ # One grep over all candidate files per script, not one per (script, file)
57
+ # pair — the per-file loop forked ~100×N greps and took ~1 min in CI.
56
58
  local name="$1"; shift
57
59
  local f
58
- for f in "$@"; do
59
- [[ "$f" == *"/$name" || "$(basename "$f")" == "$name" ]] && continue # self
60
- grep -q "$name" "$f" 2>/dev/null && return 0
61
- done
60
+ while IFS= read -r f; do
61
+ [[ "$f" == *"/$name" || "${f##*/}" == "$name" ]] && continue # self
62
+ return 0
63
+ done < <(grep -lF -- "$name" "$@" 2>/dev/null)
62
64
  return 1
63
65
  }
64
66
 
@@ -161,7 +163,7 @@ if [[ -z "$ORPHANS" ]]; then
161
163
  fi
162
164
 
163
165
  echo " orphaned scripts (present but reachable from no gate, doc, or workflow):"
164
- printf '%s' "$ORPHANS"
166
+ printf '%s\n' "$ORPHANS"
165
167
  echo -e "${RED}FAIL${NC} wire them into a gate, reference them from a skill/doc, tag them '# bp-manual-utility: <reason>', or delete them"
166
168
  echo "G-13: FAIL"
167
169
  exit 1
@@ -72,8 +72,13 @@ GOLDEN_GATES=(
72
72
  "import-boundaries:bash scripts/check-import-boundaries.sh:false"
73
73
  "skill-catalog:bash scripts/validate-skill-catalog.sh:false"
74
74
  "specs-parse:bash scripts/validate-specs-yaml.sh:false"
75
+ # The runner itself: failing output is shown, skips are named and reasoned.
76
+ "golden-gate-runner:bash scripts/test-golden-gate-runner.sh:false"
77
+ # One pipeline, release needs: every gate, pinned runner and actions.
78
+ "ci-pipeline-contract-selftest:bash scripts/test-ci-pipeline-contract.sh --self-test:false"
79
+ "ci-pipeline-contract:bash scripts/test-ci-pipeline-contract.sh:false"
75
80
  # Tier 2 of the verify arc (#106): executes the verify: of every story marked
76
- # done. Tier 1 (SKILL.md) is run-skill-verify.sh in golden-suite.yml.
81
+ # done. Tier 1 (SKILL.md) is run-skill-verify.sh in test-build-release.yml.
77
82
  "story-verify-selftest:bash scripts/run-story-verify.sh --self-test:false"
78
83
  "story-verify:bash scripts/run-story-verify.sh:false"
79
84
  )
@@ -120,15 +125,48 @@ golden_agent_dry_run() {
120
125
  return 0
121
126
  }
122
127
 
128
+ # GOLDEN_SKIP_GATES (comma list) skips gates another CI job already runs —
129
+ # test-build-release.yml skips story-verify because its story-verify job runs the
130
+ # same command. Local Preflight leaves it unset, so every gate runs there. A
131
+ # name that matches no gate is a FAIL: a typo must not look like a skip.
132
+ golden_is_skipped() { # $1 = gate name
133
+ case ",${GOLDEN_SKIP_GATES:-}," in *",$1,"*) return 0 ;; esac
134
+ return 1
135
+ }
136
+
137
+ golden_check_skip_names() {
138
+ local skip gate known
139
+ for skip in $(printf '%s' "${GOLDEN_SKIP_GATES:-}" | tr ',' ' '); do
140
+ [[ -z "$skip" ]] && continue
141
+ known=false
142
+ for gate in "${GOLDEN_GATES[@]}"; do
143
+ [[ "${gate%%:*}" == "$skip" ]] && known=true
144
+ done
145
+ $known && continue
146
+ echo -e "[$skip] ${GOLDEN_RED}FAIL${GOLDEN_NC} (GOLDEN_SKIP_GATES names no such gate)"
147
+ GATE_RESULTS+=(" - name: $skip" " exit_code: 2" " duration_seconds: 0" " status: fail" " error: unknown gate in GOLDEN_SKIP_GATES")
148
+ (( FAIL_COUNT += 1 ))
149
+ done
150
+ }
151
+
123
152
  golden_run_deterministic_gates() {
124
- local gate name cmd is_optional main_cmd script_path
153
+ local gate name cmd is_optional main_cmd script_path gate_out
125
154
  PASS_COUNT=0 FAIL_COUNT=0 SKIP_COUNT=0
126
155
  GATE_RESULTS=()
156
+ golden_check_skip_names
127
157
 
128
158
  for gate in "${GOLDEN_GATES[@]}"; do
129
159
  IFS=':' read -r name cmd is_optional <<< "$gate"
130
160
  echo -n "[$name] "
131
161
 
162
+ if golden_is_skipped "$name"; then
163
+ local reason="${GOLDEN_SKIP_REASON:-listed in GOLDEN_SKIP_GATES}"
164
+ echo -e "${GOLDEN_YELLOW}SKIP${GOLDEN_NC} ($reason)"
165
+ GATE_RESULTS+=(" - name: $name" " exit_code: skipped" " duration_seconds: 0" " status: skipped" " note: $reason")
166
+ (( SKIP_COUNT += 1 ))
167
+ continue
168
+ fi
169
+
132
170
  main_cmd=$(echo "$cmd" | awk '{print $1}')
133
171
  if [[ "$main_cmd" == "bash" ]]; then
134
172
  script_path=$(echo "$cmd" | awk '{print $2}')
@@ -148,7 +186,7 @@ golden_run_deterministic_gates() {
148
186
 
149
187
  local START_TIME END_TIME DURATION_MS DURATION_SEC GATE_EXIT
150
188
  START_TIME=$(date +%s%N)
151
- eval "$cmd" >/dev/null 2>&1 && GATE_EXIT=$? || GATE_EXIT=$?
189
+ gate_out=$(eval "$cmd" 2>&1) && GATE_EXIT=$? || GATE_EXIT=$?
152
190
  END_TIME=$(date +%s%N)
153
191
  DURATION_MS=$(( (END_TIME - START_TIME) / 1000000 ))
154
192
  DURATION_SEC=$(echo "scale=2; $DURATION_MS / 1000" | bc 2>/dev/null || echo "0")
@@ -159,6 +197,8 @@ golden_run_deterministic_gates() {
159
197
  (( PASS_COUNT += 1 ))
160
198
  else
161
199
  echo -e "${GOLDEN_RED}FAIL${GOLDEN_NC} (${DURATION_SEC}s, exit $GATE_EXIT)"
200
+ # Without the tail, CI shows FAIL with no reason (#158: compliance).
201
+ printf '%s\n' "$gate_out" | tail -20 | sed 's/^/ | /'
162
202
  GATE_RESULTS+=(" - name: $name" " exit_code: $GATE_EXIT" " duration_seconds: $DURATION_SEC" " status: fail")
163
203
  (( FAIL_COUNT += 1 ))
164
204
  fi
@@ -41,7 +41,7 @@ PASS=0; FAIL=0; SKIP=0
41
41
  TARGET="${1:-}"
42
42
 
43
43
  extract_records() {
44
- "$PYTHON" "$REPO_ROOT/scripts/lib/extract-story-verify.py" "$REPO_ROOT"
44
+ "$PYTHON" "$REPO_ROOT/scripts/lib/extract-story-verify.py" "${EXTRACT_ROOT:-$REPO_ROOT}"
45
45
  }
46
46
 
47
47
  # Report a story whose directive can never fail. Kept separate from execution so
@@ -58,6 +58,55 @@ reject_non_executable() {
58
58
  FAIL=$((FAIL + 1))
59
59
  }
60
60
 
61
+ # Many stories share one verify (e61s02…e76s02 all run the same installer
62
+ # test); same command at the same commit gives the same result, so run it once
63
+ # per gate_all and reuse the verdict for every story that declares it.
64
+ # Sets RUN_RC, RUN_OUT, and RUN_REUSED (1 when nothing new ran).
65
+ run_memoized() {
66
+ local cmd="$1" key
67
+ key=$(printf '%s' "$cmd" | cksum | tr -d ' ')
68
+ if [ -n "${MEMO_DIR:-}" ] && [ -f "$MEMO_DIR/$key.rc" ]; then
69
+ RUN_RC=$(cat "$MEMO_DIR/$key.rc")
70
+ RUN_OUT=$(cat "$MEMO_DIR/$key.out")
71
+ return 0
72
+ fi
73
+ RUN_REUSED=0
74
+ RUN_OUT=$(run_with_timeout "$cmd") && RUN_RC=0 || RUN_RC=$?
75
+ if [ -n "${MEMO_DIR:-}" ]; then
76
+ printf '%s' "$RUN_RC" > "$MEMO_DIR/$key.rc"
77
+ printf '%s' "$RUN_OUT" > "$MEMO_DIR/$key.out"
78
+ fi
79
+ }
80
+
81
+ # A plain `a && b && c` chain shares no shell state between segments when it
82
+ # has no quoting, expansion, grouping, pipes, `;`, cd, export or assignment —
83
+ # so each segment can be cached on its own (test-adapter-render.sh prefixes ten
84
+ # different story verifies). Anything else is cached as one whole command.
85
+ is_plain_and_chain() {
86
+ case "$1" in
87
+ *\'*|*\"*|*\$*|*\`*|*\(*|*\)*|*\{*|*\}*|*\|*|*\;*|*=*|*\<*) return 1 ;;
88
+ esac
89
+ printf '%s' "$1" | grep -qE '(^|&&)[[:space:]]*(cd|export|source|\.)[[:space:]]' && return 1
90
+ return 0
91
+ }
92
+
93
+ run_verify_cmd() {
94
+ local cmd="$1" seg
95
+ RUN_REUSED=1
96
+ if ! is_plain_and_chain "$cmd"; then
97
+ run_memoized "$cmd"
98
+ return
99
+ fi
100
+ local rest="$cmd"
101
+ while :; do
102
+ seg=${rest%%&&*}
103
+ run_memoized "$(printf '%s' "$seg" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
104
+ [ "$RUN_RC" -eq 0 ] || return
105
+ [ "$seg" = "$rest" ] && return
106
+ rest=${rest#*&&}
107
+ done
108
+ }
109
+
61
110
  run_story_verify() {
62
111
  local sid="$1" src="$2" cmd="$3"
63
112
 
@@ -70,20 +119,23 @@ run_story_verify() {
70
119
  return 1
71
120
  fi
72
121
 
73
- local output
74
- if output=$(run_with_timeout "$cmd"); then
75
- echo "PASS: $sid"
122
+ run_verify_cmd "$cmd"
123
+ local note=""
124
+ [ "$RUN_REUSED" -eq 1 ] && note=" (shared result)"
125
+ if [ "$RUN_RC" -eq 0 ]; then
126
+ echo "PASS: $sid$note"
76
127
  PASS=$((PASS + 1))
77
128
  return 0
78
129
  fi
79
- echo "FAIL: $sid [$src] — $cmd"
80
- echo " output: $(echo "$output" | tail -3 | tr '\n' ' ')"
130
+ echo "FAIL: $sid [$src]$note — $cmd"
131
+ echo " output: $(echo "$RUN_OUT" | tail -3 | tr '\n' ' ')"
81
132
  FAIL=$((FAIL + 1))
82
133
  return 1
83
134
  }
84
135
 
85
136
  gate_all() {
86
137
  local sid src status cmd
138
+ MEMO_DIR=$(mktemp -d)
87
139
  while IFS=$'\t' read -r sid src status cmd; do
88
140
  [ -z "${sid:-}" ] && continue
89
141
  if [ -n "$TARGET" ] && [ "$sid" != "$TARGET" ]; then
@@ -96,6 +148,58 @@ gate_all() {
96
148
  fi
97
149
  run_story_verify "$sid" "$src" "$cmd" || true
98
150
  done < <(extract_records)
151
+ rm -rf "$MEMO_DIR"
152
+ MEMO_DIR=""
153
+ }
154
+
155
+ # Shared-command proof: stories that declare the same verify run it once, yet
156
+ # each story still gets its own verdict — a failing shared command fails every
157
+ # story that declares it, never just the first.
158
+ self_test_shared_commands() {
159
+ local root="$1/shared" out runs
160
+ mkdir -p "$root/specs/epics/e98-shared"
161
+ cat > "$root/specs/epics/e98-shared/epic.yaml" <<YAML
162
+ id: e98
163
+ title: shared-command capsule
164
+ stories:
165
+ - id: e98s01
166
+ status: done
167
+ verify: printf x >> $root/ok-runs && test -d $root/specs
168
+ - id: e98s02
169
+ status: done
170
+ verify: printf x >> $root/ok-runs && test -d $root/specs
171
+ - id: e98s03
172
+ status: done
173
+ verify: printf x >> $root/bad-runs && test -f $root/missing
174
+ - id: e98s04
175
+ status: done
176
+ verify: printf x >> $root/bad-runs && test -f $root/missing
177
+ - id: e98s05
178
+ status: done
179
+ verify: printf x >> $root/seg-runs && test -d $root/specs
180
+ - id: e98s06
181
+ status: done
182
+ verify: printf x >> $root/seg-runs && test -d $root/specs/epics
183
+ - id: e98s07
184
+ status: done
185
+ verify: printf x >> $root/seg-runs && test -f $root/missing
186
+ YAML
187
+ out=$(EXTRACT_ROOT="$root" TARGET="" PASS=0 FAIL=0 SKIP=0 gate_all)
188
+ local problems=0
189
+ runs=$(wc -c < "$root/ok-runs" | tr -d ' ')
190
+ [ "$runs" = "1" ] || { echo "FAIL: self-test — shared passing command ran $runs times, want 1"; problems=$((problems + 1)); }
191
+ runs=$(wc -c < "$root/bad-runs" | tr -d ' ')
192
+ [ "$runs" = "1" ] || { echo "FAIL: self-test — shared failing command ran $runs times, want 1"; problems=$((problems + 1)); }
193
+ runs=$(wc -c < "$root/seg-runs" | tr -d ' ')
194
+ [ "$runs" = "1" ] || { echo "FAIL: self-test — shared && segment ran $runs times, want 1"; problems=$((problems + 1)); }
195
+ local sid
196
+ for sid in e98s01 e98s02 e98s05 e98s06; do
197
+ echo "$out" | grep -q "^PASS: $sid" || { echo "FAIL: self-test — $sid not reported PASS"; problems=$((problems + 1)); }
198
+ done
199
+ for sid in e98s03 e98s04 e98s07; do
200
+ echo "$out" | grep -q "^FAIL: $sid" || { echo "FAIL: self-test — $sid not reported FAIL"; problems=$((problems + 1)); }
201
+ done
202
+ [ "$problems" -eq 0 ]
99
203
  }
100
204
 
101
205
  # Anti-vacuity proof: seed a capsule whose done story must fail, and one that
@@ -178,6 +282,8 @@ EOF
178
282
  rm -rf "$tmp_root"
179
283
  trap - EXIT
180
284
 
285
+ self_test_shared_commands "$tmp_root" || problems=$((problems + 1))
286
+
181
287
  if [ "$checked" -lt 5 ]; then
182
288
  echo "FAIL: self-test — extractor found $checked/5 fixture stories"
183
289
  return 1
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env bash
2
+ # story: BUG-2026-10-25-release-not-gated
3
+ # test-ci-pipeline-contract.sh — the CI shape the portfolio template requires
4
+ # (danielvm-git/.github, docs/concept/github-actions-best-practices.md):
5
+ #
6
+ # 1. one pipeline, .github/workflows/test-build-release.yml, holds every gate
7
+ # job and the release job; the workflows it replaced are gone
8
+ # 2. release needs: every gate job and runs only on push to main — a release
9
+ # that does not wait for the gates publishes unverified builds
10
+ # 3. no ubuntu-latest; every non-local action is pinned to a 40-char SHA
11
+ # 4. every job has timeout-minutes
12
+ # 5. release and Pages never cancel a run in progress
13
+ #
14
+ # Agent workflows (agent-locks.yml, generated agentics-maintenance.yml) are
15
+ # exempt from 3–4 per migration plan v2.
16
+ #
17
+ # Usage: bash scripts/test-ci-pipeline-contract.sh [--self-test] [workflows-dir]
18
+ set -euo pipefail
19
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
20
+ resolve_repo_root
21
+ cd "$REPO_ROOT"
22
+ source scripts/lib/python-env.sh
23
+ resolve_python >/dev/null || exit 1
24
+
25
+ check_dir() { # $1 = workflows dir; prints violations, exits non-zero on any
26
+ "$PYTHON" - "$1" <<'PY'
27
+ import glob, os, re, sys, yaml
28
+
29
+ d = sys.argv[1]
30
+ GATES = {"sync", "verification-gates", "skill-verify", "story-verify"}
31
+ RETIRED = ["publish.yml", "sync-skills.yml", "golden-suite.yml"]
32
+ EXEMPT = {"agent-locks.yml", "agentics-maintenance.yml"}
33
+ bad = []
34
+
35
+ def load(path):
36
+ with open(path) as f:
37
+ return yaml.safe_load(f) or {}
38
+
39
+ def cancels(block):
40
+ # Literal true cancels; an expression or false does not cancel a push run.
41
+ return isinstance(block, dict) and block.get("cancel-in-progress") is True
42
+
43
+ pipe_path = os.path.join(d, "test-build-release.yml")
44
+ if not os.path.isfile(pipe_path):
45
+ bad.append("test-build-release.yml missing")
46
+ else:
47
+ pipe = load(pipe_path)
48
+ jobs = pipe.get("jobs", {})
49
+ missing = GATES - set(jobs)
50
+ if missing:
51
+ bad.append(f"pipeline lacks gate jobs: {sorted(missing)}")
52
+ rel = jobs.get("release")
53
+ if not rel:
54
+ bad.append("pipeline has no release job")
55
+ else:
56
+ needs = rel.get("needs", [])
57
+ needs = {needs} if isinstance(needs, str) else set(needs)
58
+ gate_jobs = set(jobs) - {"release"}
59
+ if not gate_jobs <= needs:
60
+ bad.append(f"release does not need: {sorted(gate_jobs - needs)}")
61
+ cond = str(rel.get("if", ""))
62
+ if "github.event_name == 'push'" not in cond or "refs/heads/main" not in cond:
63
+ bad.append("release is not limited to push on main")
64
+ if cancels(pipe.get("concurrency")) or cancels(rel.get("concurrency")):
65
+ bad.append("release can be cancelled in progress")
66
+
67
+ for name in RETIRED:
68
+ if os.path.exists(os.path.join(d, name)):
69
+ bad.append(f"retired workflow still present: {name}")
70
+
71
+ docs = os.path.join(d, "docs-site.yml")
72
+ if os.path.isfile(docs) and cancels(load(docs).get("concurrency")):
73
+ bad.append("docs-site.yml: Pages deploy can be cancelled in progress")
74
+
75
+ for path in sorted(glob.glob(os.path.join(d, "*.yml"))):
76
+ name = os.path.basename(path)
77
+ if name in EXEMPT:
78
+ continue
79
+ for job_name, job in (load(path).get("jobs") or {}).items():
80
+ where = f"{name}:{job_name}"
81
+ if "uses" in job: # reusable-workflow call: no runner of its own
82
+ continue
83
+ if "ubuntu-latest" in str(job.get("runs-on", "")):
84
+ bad.append(f"{where}: runs-on ubuntu-latest")
85
+ if "timeout-minutes" not in job:
86
+ bad.append(f"{where}: no timeout-minutes")
87
+ for step in job.get("steps", []):
88
+ uses = step.get("uses")
89
+ if uses and not uses.startswith("./") and not re.search(r"@[0-9a-f]{40}$", uses):
90
+ bad.append(f"{where}: action not SHA-pinned: {uses}")
91
+
92
+ for b in bad:
93
+ print(f"FAIL: {b}")
94
+ sys.exit(1 if bad else 0)
95
+ PY
96
+ }
97
+
98
+ # Anti-vacuity: a conforming fixture passes, and each rule fails on its own breach.
99
+ self_test() {
100
+ local tmp ok=1
101
+ tmp=$(mktemp -d)
102
+ trap "rm -rf '$tmp'" EXIT
103
+ local pin="actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"
104
+ write_fixture() { # $1 = dir
105
+ mkdir -p "$1"
106
+ cat > "$1/test-build-release.yml" <<YAML
107
+ on: push
108
+ jobs:
109
+ sync: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{uses: $pin}]}
110
+ verification-gates: {runs-on: ubuntu-22.04, timeout-minutes: 15, steps: [{run: "true"}]}
111
+ skill-verify: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{run: "true"}]}
112
+ story-verify: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{run: "true"}]}
113
+ release:
114
+ needs: [sync, verification-gates, skill-verify, story-verify]
115
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
116
+ runs-on: ubuntu-22.04
117
+ timeout-minutes: 15
118
+ concurrency: {group: release, cancel-in-progress: false}
119
+ steps: [{run: "true"}]
120
+ YAML
121
+ }
122
+ expect() { # $1 = label, $2 = want pass|fail, $3 = dir
123
+ if check_dir "$3" >/dev/null; then got=pass; else got=fail; fi
124
+ if [ "$got" = "$2" ]; then echo "PASS ($1)"; else echo "FAIL ($1): got $got, want $2"; ok=0; fi
125
+ }
126
+
127
+ write_fixture "$tmp/good"; expect "conforming pipeline passes" pass "$tmp/good"
128
+
129
+ write_fixture "$tmp/no-needs"
130
+ sed -i.bak 's/needs: \[sync, verification-gates, skill-verify, story-verify\]/needs: [sync]/' "$tmp/no-needs/test-build-release.yml"
131
+ expect "release missing gate needs fails" fail "$tmp/no-needs"
132
+
133
+ write_fixture "$tmp/latest"
134
+ sed -i.bak 's/runs-on: ubuntu-22.04, timeout-minutes: 15/runs-on: ubuntu-latest, timeout-minutes: 15/' "$tmp/latest/test-build-release.yml"
135
+ expect "ubuntu-latest fails" fail "$tmp/latest"
136
+
137
+ write_fixture "$tmp/tag"
138
+ sed -i.bak "s|$pin|actions/checkout@v7|" "$tmp/tag/test-build-release.yml"
139
+ expect "tag-pinned action fails" fail "$tmp/tag"
140
+
141
+ write_fixture "$tmp/cancel"
142
+ sed -i.bak 's/cancel-in-progress: false/cancel-in-progress: true/' "$tmp/cancel/test-build-release.yml"
143
+ expect "cancellable release fails" fail "$tmp/cancel"
144
+
145
+ write_fixture "$tmp/retired"; : > "$tmp/retired/publish.yml"
146
+ expect "retired workflow present fails" fail "$tmp/retired"
147
+
148
+ [ "$ok" -eq 1 ] && echo "OK: pipeline contract self-test" || { echo "pipeline contract self-test FAILED"; exit 1; }
149
+ }
150
+
151
+ if [ "${1:-}" = "--self-test" ]; then
152
+ self_test
153
+ exit 0
154
+ fi
155
+
156
+ dir="${1:-.github/workflows}"
157
+ if check_dir "$dir"; then
158
+ echo "OK: CI pipeline contract ($dir)"
159
+ else
160
+ echo "CI pipeline contract violated — see danielvm-git/.github docs/concept/github-actions-best-practices.md"
161
+ exit 1
162
+ fi
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ # story: BUG-2026-10-25-ci-critical-path
3
+ # test-golden-gate-runner.sh — regression guard for golden_run_deterministic_gates.
4
+ #
5
+ # Runs the real runner against a fixture gate list and asserts:
6
+ # (a) a failing gate prints the tail of its output (CI used to show only FAIL)
7
+ # (b) GOLDEN_SKIP_GATES reports a named gate as SKIP with its reason
8
+ # (c) an unknown name in GOLDEN_SKIP_GATES is a FAIL, so a typo cannot
9
+ # silently disable nothing while claiming to skip something
10
+ # (d) without GOLDEN_SKIP_GATES every gate runs — the default stays full
11
+ #
12
+ # Usage: bash scripts/test-golden-gate-runner.sh
13
+
14
+ set -euo pipefail
15
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
16
+ resolve_repo_root
17
+ cd "$REPO_ROOT"
18
+
19
+ FAILURES=0
20
+ pass() { echo "PASS $1"; }
21
+ fail() { echo "FAIL $1"; FAILURES=$((FAILURES + 1)); }
22
+
23
+ # Run the runner in a subshell with a fixture gate list; prints its stdout and
24
+ # the final counters as "COUNTS pass fail skip".
25
+ run_fixture() {
26
+ (
27
+ source scripts/lib/golden-suite-gates.sh
28
+ GOLDEN_GATES=(
29
+ "ok-gate:true:false"
30
+ "bad-gate:sh -c 'echo marker-line-from-failing-gate; exit 3':false"
31
+ "covered-gate:true:false"
32
+ )
33
+ golden_run_deterministic_gates
34
+ echo "COUNTS $PASS_COUNT $FAIL_COUNT $SKIP_COUNT"
35
+ )
36
+ }
37
+
38
+ out=$(GOLDEN_SKIP_GATES="" run_fixture)
39
+ echo "$out" | grep -q 'marker-line-from-failing-gate' \
40
+ && pass "(a) failing gate output is printed" \
41
+ || fail "(a) failing gate output is missing"
42
+ echo "$out" | grep -q '^COUNTS 2 1 0$' \
43
+ && pass "(d) no skip list runs every gate" \
44
+ || fail "(d) default run counts wrong: $(echo "$out" | grep COUNTS)"
45
+
46
+ out=$(GOLDEN_SKIP_GATES="covered-gate" GOLDEN_SKIP_REASON="runs in its own CI job" run_fixture)
47
+ echo "$out" | grep -q '^\[covered-gate\] .*SKIP.*runs in its own CI job' \
48
+ && pass "(b) skipped gate reported with reason" \
49
+ || fail "(b) skipped gate not reported with reason"
50
+ echo "$out" | grep -q '^COUNTS 1 1 1$' \
51
+ && pass "(b) skipped gate counted as skip, not pass" \
52
+ || fail "(b) skip counts wrong: $(echo "$out" | grep COUNTS)"
53
+
54
+ out=$(GOLDEN_SKIP_GATES="no-such-gate" run_fixture)
55
+ echo "$out" | grep -q 'no-such-gate' && echo "$out" | grep -q '^COUNTS 2 2 0$' \
56
+ && pass "(c) unknown skip name fails the run" \
57
+ || fail "(c) unknown skip name accepted: $(echo "$out" | grep COUNTS)"
58
+
59
+ [ "$FAILURES" -eq 0 ] && echo "OK: golden gate runner" || { echo "$FAILURES check(s) failed"; exit 1; }
@@ -75,8 +75,8 @@ gate_trace:
75
75
  ## Integration Points
76
76
 
77
77
  - **release-branch SKILL.md** — pre-PR gate (FAIL blocks merge).
78
- - **sync-skills.yml** — runs `trace-stories.sh --strict` as its "Traceability gate" step (FAIL
79
- blocks pipeline), which shares this skill's coverage data (`traceability-matrix.json`) but
78
+ - **test-build-release.yml** — its `sync` job runs `trace-stories.sh --strict` as the "Traceability gate" step (FAIL
79
+ blocks the release job), which shares this skill's coverage data (`traceability-matrix.json`) but
80
80
  does not invoke a gate-trace PASS/CONCERNS/FAIL/WAIVED verdict directly.
81
81
  - **verify-work SKILL.md** — runs blind-spot checks; gate-trace consumes the result.
82
82
 
package/skills-lock.json CHANGED
@@ -143,7 +143,7 @@
143
143
  },
144
144
  "gate-trace": {
145
145
  "description": "\"Deterministic traceability quality gate — reads coverage matrix + blind-spot data, applies decision rules with oracle confidence downgrade, emits PASS/CONCERNS/FAIL/WAIVED verdict. Use before release-branch to gate merges on traceability.\"",
146
- "sha256": "978894220e82c96a",
146
+ "sha256": "282c950cf9db124a",
147
147
  "path": "skills/gate-trace/SKILL.md"
148
148
  },
149
149
  "generate-allure-report": {