bigpowers 2.88.13 → 2.88.15
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/.agents/skills/gate-trace/SKILL.md +2 -2
- package/.cline/skills/gate-trace/SKILL.md +2 -2
- package/.codebuddy/skills/gate-trace/SKILL.md +2 -2
- package/.codex/skills/gate-trace/SKILL.md +2 -2
- package/.copilot/skills/gate-trace/SKILL.md +2 -2
- package/.cursor/rules/gate-trace.mdc +2 -2
- package/.gemini/extensions/bigpowers/commands/prompts/gate-trace.md +2 -2
- package/.gemini/extensions/bigpowers/gemini-extension.json +1 -1
- package/.gemini/extensions/bigpowers/skills/gate-trace/SKILL.md +2 -2
- package/.hermes/skills/gate-trace/SKILL.md +2 -2
- package/.kilocode/rules/gate-trace.md +2 -2
- package/.mimocode/skills/gate-trace/SKILL.md +2 -2
- package/.opencode/skills/gate-trace/SKILL.md +2 -2
- package/.pi/package.json +1 -1
- package/.pi/prompts/gate-trace.md +2 -2
- package/.pi/skills/gate-trace/SKILL.md +2 -2
- package/.qwen/skills/gate-trace/SKILL.md +2 -2
- package/.trae/skills/gate-trace/SKILL.md +2 -2
- package/.windsurf/rules/gate-trace.md +2 -2
- package/.zcode/skills/gate-trace/SKILL.md +2 -2
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +1 -1
- package/package.json +1 -1
- 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/golden-suite-gates.sh +5 -2
- package/scripts/test-ci-pipeline-contract.sh +235 -0
- package/skills/gate-trace/SKILL.md +2 -2
- package/skills-lock.json +1 -1
- package/scripts/lib/__pycache__/link_utils.cpython-314.pyc +0 -0
- 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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
73
|
-
blocks
|
|
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
|
-
- **
|
|
73
|
-
blocks
|
|
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
|
|
|
@@ -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
|
-
- **
|
|
75
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
@@ -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
|
-
- **
|
|
73
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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
|
-
- **
|
|
74
|
-
blocks
|
|
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.15](https://github.com/danielvm-git/bigpowers/compare/v2.88.14...v2.88.15) (2026-10-25)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **ci:** gate the GitHub Pages deploy on every check in test-build-release ([#164](https://github.com/danielvm-git/bigpowers/issues/164)) ([5f3e4de](https://github.com/danielvm-git/bigpowers/commit/5f3e4de59454d5ffe95d932a3da873db7b4e1d80))
|
|
7
|
+
|
|
8
|
+
## [2.88.14](https://github.com/danielvm-git/bigpowers/compare/v2.88.13...v2.88.14) (2026-10-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **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))
|
|
14
|
+
|
|
1
15
|
## [2.88.13](https://github.com/danielvm-git/bigpowers/compare/v2.88.12...v2.88.13) (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
|
|
71
|
+
| CI platform | GitHub Actions — one pipeline `.github/workflows/test-build-release.yml` (sync, verification-gates, skill-verify, story-verify → release, and → pages-build → pages-deploy when docs paths change); plus `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
|
Binary file
|
|
Binary file
|
|
@@ -74,8 +74,11 @@ GOLDEN_GATES=(
|
|
|
74
74
|
"specs-parse:bash scripts/validate-specs-yaml.sh:false"
|
|
75
75
|
# The runner itself: failing output is shown, skips are named and reasoned.
|
|
76
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"
|
|
77
80
|
# Tier 2 of the verify arc (#106): executes the verify: of every story marked
|
|
78
|
-
# done. Tier 1 (SKILL.md) is run-skill-verify.sh in
|
|
81
|
+
# done. Tier 1 (SKILL.md) is run-skill-verify.sh in test-build-release.yml.
|
|
79
82
|
"story-verify-selftest:bash scripts/run-story-verify.sh --self-test:false"
|
|
80
83
|
"story-verify:bash scripts/run-story-verify.sh:false"
|
|
81
84
|
)
|
|
@@ -123,7 +126,7 @@ golden_agent_dry_run() {
|
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
# GOLDEN_SKIP_GATES (comma list) skips gates another CI job already runs —
|
|
126
|
-
#
|
|
129
|
+
# test-build-release.yml skips story-verify because its story-verify job runs the
|
|
127
130
|
# same command. Local Preflight leaves it unset, so every gate runs there. A
|
|
128
131
|
# name that matches no gate is a FAIL: a typo must not look like a skip.
|
|
129
132
|
golden_is_skipped() { # $1 = gate name
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: BUG-2026-10-25-release-not-gated BUG-2026-10-25-pages-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
|
+
# 6. the Pages deploy (any job using actions/deploy-pages) lives in the
|
|
14
|
+
# pipeline, needs: every gate job and runs only for main
|
|
15
|
+
#
|
|
16
|
+
# Agent workflows (agent-locks.yml, generated agentics-maintenance.yml) are
|
|
17
|
+
# exempt from 3–4 per migration plan v2.
|
|
18
|
+
#
|
|
19
|
+
# Usage: bash scripts/test-ci-pipeline-contract.sh [--self-test] [workflows-dir]
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
|
|
22
|
+
resolve_repo_root
|
|
23
|
+
cd "$REPO_ROOT"
|
|
24
|
+
source scripts/lib/python-env.sh
|
|
25
|
+
resolve_python >/dev/null || exit 1
|
|
26
|
+
|
|
27
|
+
check_dir() { # $1 = workflows dir; prints violations, exits non-zero on any
|
|
28
|
+
"$PYTHON" - "$1" <<'PY'
|
|
29
|
+
import glob, os, re, sys, yaml
|
|
30
|
+
|
|
31
|
+
d = sys.argv[1]
|
|
32
|
+
GATES = {"sync", "verification-gates", "skill-verify", "story-verify"}
|
|
33
|
+
RETIRED = ["publish.yml", "sync-skills.yml", "golden-suite.yml"]
|
|
34
|
+
EXEMPT = {"agent-locks.yml", "agentics-maintenance.yml"}
|
|
35
|
+
bad = []
|
|
36
|
+
|
|
37
|
+
def load(path):
|
|
38
|
+
with open(path) as f:
|
|
39
|
+
return yaml.safe_load(f) or {}
|
|
40
|
+
|
|
41
|
+
def cancels(block):
|
|
42
|
+
# Literal true cancels; an expression or false does not cancel a push run.
|
|
43
|
+
return isinstance(block, dict) and block.get("cancel-in-progress") is True
|
|
44
|
+
|
|
45
|
+
def needs_of(job):
|
|
46
|
+
needs = job.get("needs", [])
|
|
47
|
+
return {needs} if isinstance(needs, str) else set(needs)
|
|
48
|
+
|
|
49
|
+
def ancestors(jobs, name):
|
|
50
|
+
seen, todo = set(), list(needs_of(jobs.get(name, {})))
|
|
51
|
+
while todo:
|
|
52
|
+
n = todo.pop()
|
|
53
|
+
if n not in seen:
|
|
54
|
+
seen.add(n)
|
|
55
|
+
todo.extend(needs_of(jobs.get(n, {})))
|
|
56
|
+
return seen
|
|
57
|
+
|
|
58
|
+
def deploys_pages(job):
|
|
59
|
+
return any(str(st.get("uses", "")).startswith("actions/deploy-pages@")
|
|
60
|
+
for st in job.get("steps") or [])
|
|
61
|
+
|
|
62
|
+
def main_only(cond):
|
|
63
|
+
return ("refs/heads/main" in cond and
|
|
64
|
+
("github.event_name == 'push'" in cond or "workflow_dispatch" in cond))
|
|
65
|
+
|
|
66
|
+
pipe_path = os.path.join(d, "test-build-release.yml")
|
|
67
|
+
if not os.path.isfile(pipe_path):
|
|
68
|
+
bad.append("test-build-release.yml missing")
|
|
69
|
+
else:
|
|
70
|
+
pipe = load(pipe_path)
|
|
71
|
+
jobs = pipe.get("jobs", {})
|
|
72
|
+
missing = GATES - set(jobs)
|
|
73
|
+
if missing:
|
|
74
|
+
bad.append(f"pipeline lacks gate jobs: {sorted(missing)}")
|
|
75
|
+
# Gate jobs: the four named ones plus any other job that needs nothing —
|
|
76
|
+
# a new root job is a gate until it says otherwise with a needs: edge.
|
|
77
|
+
gate_jobs = (GATES | {n for n, j in jobs.items() if not needs_of(j)}) - {"release"}
|
|
78
|
+
rel = jobs.get("release")
|
|
79
|
+
if not rel:
|
|
80
|
+
bad.append("pipeline has no release job")
|
|
81
|
+
pages = [n for n, j in jobs.items() if deploys_pages(j)]
|
|
82
|
+
if not pages:
|
|
83
|
+
bad.append("pipeline has no Pages deploy job")
|
|
84
|
+
for name in pages:
|
|
85
|
+
job, up = jobs[name], ancestors(jobs, name)
|
|
86
|
+
if not gate_jobs <= up:
|
|
87
|
+
bad.append(f"{name}: Pages deploy does not need: {sorted(gate_jobs - up)}")
|
|
88
|
+
conds = [str(jobs[n].get("if", "")) for n in up | {name} if n in jobs]
|
|
89
|
+
if any("always()" in c or "cancelled()" in c for c in conds):
|
|
90
|
+
bad.append(f"{name}: Pages deploy runs even when a gate fails (always()/cancelled())")
|
|
91
|
+
if not any(main_only(c) for c in conds):
|
|
92
|
+
bad.append(f"{name}: Pages deploy is not limited to main")
|
|
93
|
+
if cancels(pipe.get("concurrency")) or cancels(job.get("concurrency")):
|
|
94
|
+
bad.append(f"{name}: Pages deploy can be cancelled in progress")
|
|
95
|
+
if not isinstance(job.get("concurrency"), dict) or not job["concurrency"].get("group"):
|
|
96
|
+
bad.append(f"{name}: Pages deploy has no concurrency group")
|
|
97
|
+
if rel:
|
|
98
|
+
needs = needs_of(rel)
|
|
99
|
+
if not gate_jobs <= needs:
|
|
100
|
+
bad.append(f"release does not need: {sorted(gate_jobs - needs)}")
|
|
101
|
+
cond = str(rel.get("if", ""))
|
|
102
|
+
if "github.event_name == 'push'" not in cond or "refs/heads/main" not in cond:
|
|
103
|
+
bad.append("release is not limited to push on main")
|
|
104
|
+
if cancels(pipe.get("concurrency")) or cancels(rel.get("concurrency")):
|
|
105
|
+
bad.append("release can be cancelled in progress")
|
|
106
|
+
|
|
107
|
+
for name in RETIRED:
|
|
108
|
+
if os.path.exists(os.path.join(d, name)):
|
|
109
|
+
bad.append(f"retired workflow still present: {name}")
|
|
110
|
+
|
|
111
|
+
for path in sorted(glob.glob(os.path.join(d, "*.yml"))):
|
|
112
|
+
name = os.path.basename(path)
|
|
113
|
+
if name in EXEMPT:
|
|
114
|
+
continue
|
|
115
|
+
for job_name, job in (load(path).get("jobs") or {}).items():
|
|
116
|
+
where = f"{name}:{job_name}"
|
|
117
|
+
if name != "test-build-release.yml" and deploys_pages(job):
|
|
118
|
+
bad.append(f"{where}: Pages deploy outside the pipeline (no needs: edge to the gates)")
|
|
119
|
+
if "uses" in job: # reusable-workflow call: no runner of its own
|
|
120
|
+
continue
|
|
121
|
+
if "ubuntu-latest" in str(job.get("runs-on", "")):
|
|
122
|
+
bad.append(f"{where}: runs-on ubuntu-latest")
|
|
123
|
+
if "timeout-minutes" not in job:
|
|
124
|
+
bad.append(f"{where}: no timeout-minutes")
|
|
125
|
+
for step in job.get("steps", []):
|
|
126
|
+
uses = step.get("uses")
|
|
127
|
+
if uses and not uses.startswith("./") and not re.search(r"@[0-9a-f]{40}$", uses):
|
|
128
|
+
bad.append(f"{where}: action not SHA-pinned: {uses}")
|
|
129
|
+
|
|
130
|
+
for b in bad:
|
|
131
|
+
print(f"FAIL: {b}")
|
|
132
|
+
sys.exit(1 if bad else 0)
|
|
133
|
+
PY
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Anti-vacuity: a conforming fixture passes, and each rule fails on its own breach.
|
|
137
|
+
self_test() {
|
|
138
|
+
local tmp ok=1
|
|
139
|
+
tmp=$(mktemp -d)
|
|
140
|
+
trap "rm -rf '$tmp'" EXIT
|
|
141
|
+
local pin="actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0"
|
|
142
|
+
local deploy="actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128"
|
|
143
|
+
write_fixture() { # $1 = dir
|
|
144
|
+
mkdir -p "$1"
|
|
145
|
+
cat > "$1/test-build-release.yml" <<YAML
|
|
146
|
+
on: push
|
|
147
|
+
jobs:
|
|
148
|
+
sync: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{uses: $pin}]}
|
|
149
|
+
verification-gates: {runs-on: ubuntu-22.04, timeout-minutes: 15, steps: [{run: "true"}]}
|
|
150
|
+
skill-verify: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{run: "true"}]}
|
|
151
|
+
story-verify: {runs-on: ubuntu-22.04, timeout-minutes: 10, steps: [{run: "true"}]}
|
|
152
|
+
release:
|
|
153
|
+
needs: [sync, verification-gates, skill-verify, story-verify]
|
|
154
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
155
|
+
runs-on: ubuntu-22.04
|
|
156
|
+
timeout-minutes: 15
|
|
157
|
+
concurrency: {group: release, cancel-in-progress: false}
|
|
158
|
+
steps: [{run: "true"}]
|
|
159
|
+
pages-build:
|
|
160
|
+
needs: [sync, verification-gates, skill-verify, story-verify]
|
|
161
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
162
|
+
runs-on: ubuntu-22.04
|
|
163
|
+
timeout-minutes: 15
|
|
164
|
+
steps: [{run: "true"}]
|
|
165
|
+
pages-deploy:
|
|
166
|
+
needs: pages-build
|
|
167
|
+
runs-on: ubuntu-22.04
|
|
168
|
+
timeout-minutes: 15
|
|
169
|
+
concurrency: {group: pages, cancel-in-progress: false}
|
|
170
|
+
steps: [{uses: "$deploy"}]
|
|
171
|
+
YAML
|
|
172
|
+
}
|
|
173
|
+
expect() { # $1 = label, $2 = want pass|fail, $3 = dir
|
|
174
|
+
if check_dir "$3" >/dev/null; then got=pass; else got=fail; fi
|
|
175
|
+
if [ "$got" = "$2" ]; then echo "PASS ($1)"; else echo "FAIL ($1): got $got, want $2"; ok=0; fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
write_fixture "$tmp/good"; expect "conforming pipeline passes" pass "$tmp/good"
|
|
179
|
+
|
|
180
|
+
write_fixture "$tmp/no-needs"
|
|
181
|
+
sed -i.bak 's/needs: \[sync, verification-gates, skill-verify, story-verify\]/needs: [sync]/' "$tmp/no-needs/test-build-release.yml"
|
|
182
|
+
expect "release missing gate needs fails" fail "$tmp/no-needs"
|
|
183
|
+
|
|
184
|
+
write_fixture "$tmp/latest"
|
|
185
|
+
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"
|
|
186
|
+
expect "ubuntu-latest fails" fail "$tmp/latest"
|
|
187
|
+
|
|
188
|
+
write_fixture "$tmp/tag"
|
|
189
|
+
sed -i.bak "s|$pin|actions/checkout@v7|" "$tmp/tag/test-build-release.yml"
|
|
190
|
+
expect "tag-pinned action fails" fail "$tmp/tag"
|
|
191
|
+
|
|
192
|
+
write_fixture "$tmp/cancel"
|
|
193
|
+
sed -i.bak 's/cancel-in-progress: false/cancel-in-progress: true/' "$tmp/cancel/test-build-release.yml"
|
|
194
|
+
expect "cancellable release fails" fail "$tmp/cancel"
|
|
195
|
+
|
|
196
|
+
write_fixture "$tmp/retired"; : > "$tmp/retired/publish.yml"
|
|
197
|
+
expect "retired workflow present fails" fail "$tmp/retired"
|
|
198
|
+
|
|
199
|
+
write_fixture "$tmp/pages-needs"
|
|
200
|
+
sed -i.bak '/pages-build:/{n;s/needs: \[.*\]/needs: [sync]/;}' "$tmp/pages-needs/test-build-release.yml"
|
|
201
|
+
expect "Pages deploy missing gate needs fails" fail "$tmp/pages-needs"
|
|
202
|
+
|
|
203
|
+
write_fixture "$tmp/pages-always"
|
|
204
|
+
sed -i.bak '/pages-deploy:/{n;s/needs: pages-build/needs: pages-build\
|
|
205
|
+
if: always()/;}' "$tmp/pages-always/test-build-release.yml"
|
|
206
|
+
expect "Pages deploy with always() fails" fail "$tmp/pages-always"
|
|
207
|
+
|
|
208
|
+
write_fixture "$tmp/pages-anyref"
|
|
209
|
+
sed -i.bak '/pages-build:/{n;n;s/if: .*/if: github.event_name == '"'push'"'/;}' "$tmp/pages-anyref/test-build-release.yml"
|
|
210
|
+
expect "Pages deploy not limited to main fails" fail "$tmp/pages-anyref"
|
|
211
|
+
|
|
212
|
+
write_fixture "$tmp/pages-cancel"
|
|
213
|
+
sed -i.bak 's/group: pages, cancel-in-progress: false/group: pages, cancel-in-progress: true/' "$tmp/pages-cancel/test-build-release.yml"
|
|
214
|
+
expect "cancellable Pages deploy fails" fail "$tmp/pages-cancel"
|
|
215
|
+
|
|
216
|
+
write_fixture "$tmp/pages-split"
|
|
217
|
+
printf 'on: push\njobs:\n deploy: {runs-on: ubuntu-22.04, timeout-minutes: 15, steps: [{uses: %s}]}\n' \
|
|
218
|
+
"$deploy" > "$tmp/pages-split/docs-site.yml"
|
|
219
|
+
expect "Pages deploy in a separate workflow fails" fail "$tmp/pages-split"
|
|
220
|
+
|
|
221
|
+
[ "$ok" -eq 1 ] && echo "OK: pipeline contract self-test" || { echo "pipeline contract self-test FAILED"; exit 1; }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if [ "${1:-}" = "--self-test" ]; then
|
|
225
|
+
self_test
|
|
226
|
+
exit 0
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
dir="${1:-.github/workflows}"
|
|
230
|
+
if check_dir "$dir"; then
|
|
231
|
+
echo "OK: CI pipeline contract ($dir)"
|
|
232
|
+
else
|
|
233
|
+
echo "CI pipeline contract violated — see danielvm-git/.github docs/concept/github-actions-best-practices.md"
|
|
234
|
+
exit 1
|
|
235
|
+
fi
|
|
@@ -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
|
-
- **
|
|
79
|
-
blocks
|
|
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": "
|
|
146
|
+
"sha256": "282c950cf9db124a",
|
|
147
147
|
"path": "skills/gate-trace/SKILL.md"
|
|
148
148
|
},
|
|
149
149
|
"generate-allure-report": {
|
|
Binary file
|
|
Binary file
|