bigpowers 2.80.0 → 2.81.0

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/.pi/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.80.0",
3
+ "version": "2.81.0",
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.81.0](https://github.com/danielvm-git/bigpowers/compare/v2.80.0...v2.81.0) (2026-07-23)
2
+
3
+
4
+ ### Features
5
+
6
+ * **e54s02:** advisory catalog drift-detection gate ([7761f75](https://github.com/danielvm-git/bigpowers/commit/7761f75ed2312d419f3e28f192385304e36e83f2))
7
+
1
8
  # [2.80.0](https://github.com/danielvm-git/bigpowers/compare/v2.79.1...v2.80.0) (2026-07-23)
2
9
 
3
10
 
package/CLAUDE.md CHANGED
@@ -65,7 +65,7 @@ Stack: Markdown / Bash (documentation-based; skills integrate with Claude Code,
65
65
  | Compliance | `npm run compliance` |
66
66
  | Verification Gates | `bash scripts/run-verification-gates.sh` |
67
67
  | Traceability | `bash scripts/trace-stories.sh --strict` | grep for story tags (traceability check) |
68
- | Preflight | `npm run compliance && bash scripts/run-verification-gates.sh && bash scripts/sync-skills.sh && bash scripts/trace-stories.sh --strict` | Full local green stack before forward work |
68
+ | Preflight | `npm run compliance && bash scripts/run-verification-gates.sh && bash scripts/sync-skills.sh && bash scripts/trace-stories.sh --strict && bash scripts/check-catalog-drift.sh` | Full local green stack before forward work. Trailing step is advisory only (e54s02 catalog-freeze drift check) — it always exits 0 and can never break this chain. |
69
69
  | CI | `gh pr checks` | Remote CI green when a PR is open |
70
70
 
71
71
  ### Pre-Merge Checklist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.80.0",
3
+ "version": "2.81.0",
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": {
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # story: e54s02
3
+ # check-catalog-drift.sh — advisory (Confirm-gate) diff of the live skill
4
+ # catalog against e54s01's frozen baseline. ALWAYS exits 0: this warns during
5
+ # the freeze window, it never blocks Preflight (never add to GOLDEN_GATES).
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ source "$SCRIPT_DIR/lib/skill-common.sh"
10
+ source "$SCRIPT_DIR/lib/python-env.sh"
11
+
12
+ resolve_repo_root
13
+ cd "$REPO_ROOT"
14
+
15
+ BASELINE="$(ls -1 "$REPO_ROOT"/specs/tech-architecture/CATALOG-BASELINE-*.yaml 2>/dev/null | sort | tail -1 || true)"
16
+
17
+ if [[ -z "$BASELINE" ]]; then
18
+ echo "no baseline found, skipping drift check"
19
+ exit 0
20
+ fi
21
+
22
+ "$PYTHON" - "$BASELINE" "$SKILLS_ROOT" <<'PY'
23
+ import sys, glob, os, re
24
+ import yaml
25
+
26
+ baseline_path, skills_root = sys.argv[1], sys.argv[2]
27
+ baseline = yaml.safe_load(open(baseline_path)) or {}
28
+ base_by_name = {s["name"]: s for s in baseline.get("skills") or []}
29
+
30
+ EXCEPTION_MARKER = "freeze-exception"
31
+
32
+ def has_exception(skill_md):
33
+ if not os.path.isfile(skill_md):
34
+ return False
35
+ with open(skill_md, encoding="utf-8") as f:
36
+ return EXCEPTION_MARKER in f.read()
37
+
38
+ def parse_live(skill_md):
39
+ text = open(skill_md, encoding="utf-8").read()
40
+ fm = re.search(r"^---\n(.*?)\n---", text, re.S | re.M)
41
+ fields = {}
42
+ if fm:
43
+ for line in fm.group(1).splitlines():
44
+ m = re.match(r"^(phase|effort|model):\s*(.+)$", line)
45
+ if m:
46
+ fields[m.group(1)] = m.group(2).strip()
47
+ return fields
48
+
49
+ live_by_name = {}
50
+ for skill_md in sorted(glob.glob(os.path.join(skills_root, "*", "SKILL.md"))):
51
+ name = os.path.basename(os.path.dirname(skill_md))
52
+ live_by_name[name] = {"path": skill_md, **parse_live(skill_md)}
53
+
54
+ warnings = []
55
+
56
+ for name, live in live_by_name.items():
57
+ if name not in base_by_name:
58
+ if not has_exception(live["path"]):
59
+ warnings.append(f"WARNING: addition — new skill '{name}' not in frozen baseline")
60
+ continue
61
+ base = base_by_name[name]
62
+ for field in ("phase", "effort", "model"):
63
+ base_val = base.get(field)
64
+ base_val = None if base_val in ("null", None) else str(base_val)
65
+ live_val = live.get(field)
66
+ if base_val != live_val:
67
+ if not has_exception(live["path"]):
68
+ warnings.append(
69
+ f"WARNING: structural change — '{name}' field '{field}' "
70
+ f"changed from {base_val!r} to {live_val!r}"
71
+ )
72
+
73
+ for name, base in base_by_name.items():
74
+ if name not in live_by_name:
75
+ warnings.append(f"WARNING: removal — skill '{name}' present in baseline but missing from live catalog")
76
+
77
+ for w in warnings:
78
+ print(w)
79
+ PY
80
+
81
+ exit 0