bigpowers 2.78.1 → 2.79.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.78.1",
3
+ "version": "2.79.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.79.0](https://github.com/danielvm-git/bigpowers/compare/v2.78.1...v2.79.0) (2026-07-23)
2
+
3
+
4
+ ### Features
5
+
6
+ * **scripts:** implement tombstone-skill.sh and validate-tombstones.sh ([c81a451](https://github.com/danielvm-git/bigpowers/commit/c81a451598d8338188fb9e18b4486e20e6c177f8))
7
+
1
8
  ## [2.78.1](https://github.com/danielvm-git/bigpowers/compare/v2.78.0...v2.78.1) (2026-07-23)
2
9
 
3
10
 
package/CONVENTIONS.md CHANGED
@@ -353,6 +353,35 @@ name must return < 5 results across the repo.
353
353
 
354
354
  Any new exception requires an entry in this table before the skill is published.
355
355
 
356
+ ## Tombstone Aliases (e53s02)
357
+
358
+ Every skill rename or merge ships a tombstone alias for one release, so consumers
359
+ referencing the old name don't break immediately.
360
+
361
+ **Mechanism:**
362
+
363
+ 1. Run `bash scripts/tombstone-skill.sh <old-name> <new-name-or-merge-target>` when
364
+ renaming or merging a skill. It replaces `skills/<old-name>/SKILL.md` with a stub
365
+ pointing at the new name/location, preserves the old skill's `# story: eNNsNN` tags
366
+ for traceability, and registers the mapping in `specs/tombstones.yaml` (old name,
367
+ new name/target, creation timestamp, and the `bigpowers_version` at creation time).
368
+ 2. Run `bash scripts/validate-tombstones.sh` to confirm every registered tombstone's
369
+ stub still resolves, and to flag any tombstone whose `created_at_version` differs
370
+ from the current release — that stub has served its one-release transition window
371
+ and should be removed.
372
+ 3. **Required alongside the first real use of `tombstone-skill.sh`:** update
373
+ `docs/references/model-profiles.md`'s skill-count annotations. A tombstone stub is
374
+ still a `skills/*/SKILL.md` file — it increments the live skill count the same as
375
+ any other skill, and the count doc must reflect that.
376
+
377
+ **Rejected names:** both the old and new name must be a bare kebab-case skill name
378
+ (`^[a-z][a-z0-9-]*$`) — `tombstone-skill.sh` refuses anything else before touching the
379
+ filesystem (path segments, absolute paths, etc.).
380
+
381
+ This story (e53s02) builds the mechanism only; it creates no real tombstone stub
382
+ itself, so the live skill count doesn't move yet. The first real use is expected in
383
+ e56 (skill reclassification/merges).
384
+
356
385
  ## File-Size Exceptions
357
386
 
358
387
  The 300-line file cap exists to ensure files fit in a single agent context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigpowers",
3
- "version": "2.78.1",
3
+ "version": "2.79.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,111 @@
1
+ #!/usr/bin/env bash
2
+ # story: e53s02
3
+ # test-tombstone-mechanism.sh — exercises tombstone-skill.sh and
4
+ # validate-tombstones.sh end-to-end against throwaway fixtures.
5
+ #
6
+ # Usage: bash scripts/test-tombstone-mechanism.sh
7
+
8
+ set -euo pipefail
9
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
10
+ resolve_repo_root
11
+ cd "$REPO_ROOT"
12
+
13
+ RED='\033[0;31m'
14
+ GREEN='\033[0;32m'
15
+ NC='\033[0m'
16
+
17
+ FAILURES=0
18
+ tt_pass() { echo -e "${GREEN}PASS${NC} $1"; }
19
+ tt_fail() { echo -e "${RED}FAIL${NC} $1"; FAILURES=$((FAILURES + 1)); }
20
+ strip_ansi() { sed -E 's/\x1b\[[0-9;]*m//g'; }
21
+
22
+ FIXTURE_OLD="test-tombstone-fixture"
23
+ FIXTURE_NEW="test-tombstone-target"
24
+ FIXTURE_DIR="skills/${FIXTURE_OLD}"
25
+ TOMBSTONES_FILE="specs/tombstones.yaml"
26
+ TOMBSTONES_BACKUP=""
27
+
28
+ cleanup() {
29
+ rm -rf "$FIXTURE_DIR" "skills/${FIXTURE_NEW}"
30
+ if [[ -n "$TOMBSTONES_BACKUP" ]]; then
31
+ mv "$TOMBSTONES_BACKUP" "$TOMBSTONES_FILE"
32
+ else
33
+ rm -f "$TOMBSTONES_FILE"
34
+ fi
35
+ }
36
+ trap cleanup EXIT
37
+
38
+ if [[ -f "$TOMBSTONES_FILE" ]]; then
39
+ TOMBSTONES_BACKUP=$(mktemp)
40
+ cp "$TOMBSTONES_FILE" "$TOMBSTONES_BACKUP"
41
+ rm -f "$TOMBSTONES_FILE"
42
+ fi
43
+
44
+ # --- Scenario: zero tombstones registered (6a) ---
45
+ if bash scripts/validate-tombstones.sh 2>&1 | grep -qi "no tombstones"; then
46
+ tt_pass "validate-tombstones.sh reports clean when zero tombstones registered"
47
+ else
48
+ tt_fail "validate-tombstones.sh did not report 'no tombstones' with an empty ledger"
49
+ fi
50
+
51
+ # --- Scenario: tombstone-skill.sh generates a stub ---
52
+ mkdir -p "$FIXTURE_DIR"
53
+ cat > "${FIXTURE_DIR}/SKILL.md" <<'FIXTUREEOF'
54
+ # story: e00s00
55
+ ---
56
+ name: test-tombstone-fixture
57
+ description: throwaway fixture for test-tombstone-mechanism.sh
58
+ ---
59
+ Fixture body.
60
+ FIXTUREEOF
61
+
62
+ bash scripts/tombstone-skill.sh "$FIXTURE_OLD" "$FIXTURE_NEW"
63
+
64
+ if grep -q "TOMBSTONE" "${FIXTURE_DIR}/SKILL.md"; then
65
+ tt_pass "tombstone-skill.sh replaced SKILL.md with a stub"
66
+ else
67
+ tt_fail "SKILL.md was not replaced with a tombstone stub"
68
+ fi
69
+
70
+ if grep -q "story: e00s00" "${FIXTURE_DIR}/SKILL.md"; then
71
+ tt_pass "tombstone-skill.sh preserved the old story tag"
72
+ else
73
+ tt_fail "story tag was not preserved in the stub"
74
+ fi
75
+
76
+ if [[ -f "$TOMBSTONES_FILE" ]] && grep -q "$FIXTURE_OLD" "$TOMBSTONES_FILE"; then
77
+ tt_pass "tombstone-skill.sh registered the mapping in ${TOMBSTONES_FILE}"
78
+ else
79
+ tt_fail "mapping not registered in ${TOMBSTONES_FILE}"
80
+ fi
81
+
82
+ # --- Scenario: fresh tombstone resolves cleanly ---
83
+ if bash scripts/validate-tombstones.sh 2>&1 | strip_ansi | grep -q "OK: ${FIXTURE_OLD}"; then
84
+ tt_pass "validate-tombstones.sh confirms the fresh stub resolves"
85
+ else
86
+ tt_fail "validate-tombstones.sh did not confirm the fresh stub"
87
+ fi
88
+
89
+ # --- Scenario: expired tombstone flagged (6b) ---
90
+ python3 -c "
91
+ import yaml
92
+ d = yaml.safe_load(open('$TOMBSTONES_FILE')) or {'tombstones': []}
93
+ for t in d['tombstones']:
94
+ if t['old_name'] == '$FIXTURE_OLD':
95
+ t['created_at_version'] = '0.0.0'
96
+ yaml.dump(d, open('$TOMBSTONES_FILE', 'w'), default_flow_style=False)
97
+ "
98
+
99
+ if bash scripts/validate-tombstones.sh 2>&1 | strip_ansi | grep -qi "EXPIRED: ${FIXTURE_OLD}"; then
100
+ tt_pass "validate-tombstones.sh flags a tombstone whose expiry window has passed"
101
+ else
102
+ tt_fail "expired tombstone was not flagged"
103
+ fi
104
+
105
+ echo "---"
106
+ if [[ "$FAILURES" -eq 0 ]]; then
107
+ echo -e "${GREEN}test-tombstone-mechanism: ALL PASS${NC}"
108
+ else
109
+ echo -e "${RED}test-tombstone-mechanism: $FAILURES failure(s)${NC}"
110
+ fi
111
+ exit "$FAILURES"
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # story: e53s02
3
+ # tombstone-skill.sh — replace a renamed/merged skill's SKILL.md with a stub,
4
+ # preserving its story tags and registering the mapping in specs/tombstones.yaml
5
+ # for a one-release expiry window.
6
+ #
7
+ # Usage: bash scripts/tombstone-skill.sh <old-name> <new-name-or-merge-target>
8
+
9
+ set -euo pipefail
10
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
11
+ resolve_repo_root
12
+ cd "$REPO_ROOT"
13
+
14
+ usage() {
15
+ cat <<'EOF'
16
+ Usage: tombstone-skill.sh <old-name> <new-name-or-merge-target>
17
+
18
+ Replaces skills/<old-name>/SKILL.md with a stub pointing at <new-name>,
19
+ preserves the old name's story tags, and registers the mapping in
20
+ specs/tombstones.yaml with a one-release expiry window.
21
+ EOF
22
+ }
23
+
24
+ if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
25
+ usage
26
+ exit 0
27
+ fi
28
+
29
+ OLD_NAME="${1:-}"
30
+ NEW_NAME="${2:-}"
31
+ NAME_PATTERN='^[a-z][a-z0-9-]*$'
32
+
33
+ if [[ -z "$OLD_NAME" || -z "$NEW_NAME" ]]; then
34
+ usage >&2
35
+ exit 1
36
+ fi
37
+
38
+ # Threat-model mitigation (specs/security/epics/e53/THREAT_MODEL.md): reject
39
+ # anything that isn't a bare kebab-case skill name before any path use.
40
+ for name in "$OLD_NAME" "$NEW_NAME"; do
41
+ if [[ ! "$name" =~ $NAME_PATTERN ]]; then
42
+ echo "ERROR: '$name' is not a valid skill name (must match $NAME_PATTERN)" >&2
43
+ exit 1
44
+ fi
45
+ done
46
+
47
+ OLD_SKILL_MD="skills/${OLD_NAME}/SKILL.md"
48
+ if [[ ! -f "$OLD_SKILL_MD" ]]; then
49
+ echo "ERROR: $OLD_SKILL_MD does not exist" >&2
50
+ exit 1
51
+ fi
52
+
53
+ STORY_TAGS=$(grep -E '^#\s*story:' "$OLD_SKILL_MD" || true)
54
+ CURRENT_VERSION=$(grep '"version"' package.json 2>/dev/null | head -1 | sed 's/.*: *"\(.*\)".*/\1/')
55
+ CREATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
56
+
57
+ cat > "$OLD_SKILL_MD" <<STUBEOF
58
+ ---
59
+ name: ${OLD_NAME}
60
+ description: "TOMBSTONE — renamed/merged to ${NEW_NAME}. This stub resolves for one release then is removed."
61
+ ---
62
+
63
+ # ${OLD_NAME} (tombstoned)
64
+
65
+ This skill has been renamed or merged into \`${NEW_NAME}\`. Use \`${NEW_NAME}\` instead.
66
+
67
+ This stub exists for one release as a transition aid, then is removed once
68
+ \`scripts/validate-tombstones.sh\` flags it as expired.
69
+
70
+ ${STORY_TAGS}
71
+ STUBEOF
72
+
73
+ TOMBSTONES_FILE="specs/tombstones.yaml"
74
+ [[ -f "$TOMBSTONES_FILE" ]] || echo "tombstones: []" > "$TOMBSTONES_FILE"
75
+
76
+ python3 -c "
77
+ import yaml
78
+ d = yaml.safe_load(open('$TOMBSTONES_FILE')) or {'tombstones': []}
79
+ d.setdefault('tombstones', []).append({
80
+ 'old_name': '$OLD_NAME',
81
+ 'new_name_or_merge_target': '$NEW_NAME',
82
+ 'created_at': '$CREATED_AT',
83
+ 'created_at_version': '$CURRENT_VERSION',
84
+ })
85
+ yaml.dump(d, open('$TOMBSTONES_FILE', 'w'), default_flow_style=False)
86
+ "
87
+
88
+ echo "Tombstoned: ${OLD_NAME} -> ${NEW_NAME}"
89
+ echo "Registered in ${TOMBSTONES_FILE}"
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bash
2
+ # story: e53s02
3
+ # validate-tombstones.sh — confirm each registered tombstone's stub still
4
+ # resolves, and flag any whose one-release expiry window has passed.
5
+ #
6
+ # Usage: bash scripts/validate-tombstones.sh
7
+
8
+ set -euo pipefail
9
+ source "$(dirname "${BASH_SOURCE[0]}")/lib/skill-common.sh"
10
+ resolve_repo_root
11
+ cd "$REPO_ROOT"
12
+
13
+ RED='\033[0;31m'
14
+ GREEN='\033[0;32m'
15
+ YELLOW='\033[1;33m'
16
+ NC='\033[0m'
17
+
18
+ TOMBSTONES_FILE="specs/tombstones.yaml"
19
+ FAIL=0
20
+
21
+ if [[ ! -f "$TOMBSTONES_FILE" ]]; then
22
+ echo -e "${GREEN}OK${NC}: no tombstones registered"
23
+ exit 0
24
+ fi
25
+
26
+ CURRENT_VERSION=$(grep '"version"' package.json 2>/dev/null | head -1 | sed 's/.*: *"\(.*\)".*/\1/')
27
+
28
+ ROWS=$(python3 -c "
29
+ import yaml
30
+ d = yaml.safe_load(open('$TOMBSTONES_FILE')) or {'tombstones': []}
31
+ for t in d.get('tombstones', []) or []:
32
+ print(f\"{t['old_name']}\t{t['new_name_or_merge_target']}\t{t.get('created_at_version', '')}\")
33
+ ")
34
+
35
+ if [[ -z "$ROWS" ]]; then
36
+ echo -e "${GREEN}OK${NC}: no tombstones registered"
37
+ exit 0
38
+ fi
39
+
40
+ while IFS=$'\t' read -r old_name new_name created_version; do
41
+ [[ -z "$old_name" ]] && continue
42
+ stub="skills/${old_name}/SKILL.md"
43
+
44
+ if [[ ! -f "$stub" ]]; then
45
+ echo -e "${RED}FAIL${NC}: ${old_name} -> ${new_name} — stub missing at ${stub}"
46
+ FAIL=1
47
+ continue
48
+ fi
49
+ if ! grep -q "TOMBSTONE" "$stub"; then
50
+ echo -e "${RED}FAIL${NC}: ${old_name} — stub exists but is not a tombstone"
51
+ FAIL=1
52
+ continue
53
+ fi
54
+
55
+ if [[ -n "$created_version" && "$created_version" != "$CURRENT_VERSION" ]]; then
56
+ echo -e "${YELLOW}EXPIRED${NC}: ${old_name} -> ${new_name} — created at ${created_version}, now ${CURRENT_VERSION}; remove the stub"
57
+ continue
58
+ fi
59
+
60
+ echo -e "${GREEN}OK${NC}: ${old_name} -> ${new_name} resolves"
61
+ done <<< "$ROWS"
62
+
63
+ exit "$FAIL"