bigpowers 2.73.5 → 2.73.7
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 +1 -1
- package/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/scripts/check-spec-version-gap.sh +37 -9
- package/scripts/lib/migrate-version-common.sh +113 -0
- package/scripts/lib/migrate-version-execute.sh +81 -0
- package/scripts/lib/migrate-version-plan.sh +119 -0
- package/scripts/lib/migrate-version-post.sh +170 -0
- package/scripts/lib/migrate-version-run.sh +53 -0
- package/scripts/lib/migrate-version-transforms.sh +231 -0
- package/scripts/migrate-version.sh +7 -679
package/.pi/package.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.73.7](https://github.com/danielvm-git/bigpowers/compare/v2.73.6...v2.73.7) (2026-07-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **migrate-spec:** split migrate-version.sh into lib modules under 300-line cap ([#62](https://github.com/danielvm-git/bigpowers/issues/62)) ([11842f2](https://github.com/danielvm-git/bigpowers/commit/11842f297c58d88df24f451316153242e3f4c63a))
|
|
7
|
+
|
|
8
|
+
## [2.73.6](https://github.com/danielvm-git/bigpowers/compare/v2.73.5...v2.73.6) (2026-07-06)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **plan-release:** encapsulate G28 boolean logic in check-spec-version-gap ([#61](https://github.com/danielvm-git/bigpowers/issues/61)) ([613e45c](https://github.com/danielvm-git/bigpowers/commit/613e45c00a636f66783a220f35ec4e26916649ee))
|
|
14
|
+
|
|
1
15
|
## [2.73.5](https://github.com/danielvm-git/bigpowers/compare/v2.73.4...v2.73.5) (2026-07-06)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -62,6 +62,35 @@ version_lt() {
|
|
|
62
62
|
return 1
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
is_protected_branch() {
|
|
66
|
+
[[ "$1" == "main" || "$1" == "master" ]]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
on_feature_branch() {
|
|
70
|
+
local branch="$1"
|
|
71
|
+
[[ -n "$branch" ]] || return 1
|
|
72
|
+
is_protected_branch "$branch" && return 1
|
|
73
|
+
return 0
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
yaml_value_set() {
|
|
77
|
+
local value="$1"
|
|
78
|
+
[[ -n "$value" ]] || return 1
|
|
79
|
+
[[ "$value" == "null" || "$value" == "None" ]] && return 1
|
|
80
|
+
return 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
has_dirty_specs() {
|
|
84
|
+
[[ -d "$SPECS" ]] && git -C "$PROJECT_DIR" status --porcelain -- specs/ 2>/dev/null | grep -q .
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
is_blocking_active_flow() {
|
|
88
|
+
local flow="$1"
|
|
89
|
+
yaml_value_set "$flow" || return 1
|
|
90
|
+
case "$flow" in build_epic|develop_tdd|fix_bug) return 0 ;; esac
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
|
|
65
94
|
find_migrations() {
|
|
66
95
|
local ver="$1" reg="$BP_ROOT/specs/migrations/registry.okf.md"
|
|
67
96
|
[[ ! -f "$reg" ]] && { echo "[]"; return; }
|
|
@@ -83,13 +112,12 @@ print(json.dumps(out))
|
|
|
83
112
|
}
|
|
84
113
|
|
|
85
114
|
check_active_work() {
|
|
86
|
-
local branch
|
|
87
|
-
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
fi
|
|
115
|
+
local branch af
|
|
116
|
+
branch=$(git -C "$PROJECT_DIR" branch --show-current 2>/dev/null || echo "")
|
|
117
|
+
if on_feature_branch "$branch"; then echo "true"; return; fi
|
|
118
|
+
if has_dirty_specs; then echo "true"; return; fi
|
|
119
|
+
af=$(yaml_get "$SPECS/state.yaml" "active_flow")
|
|
120
|
+
if is_blocking_active_flow "$af"; then echo "true"; return; fi
|
|
93
121
|
echo "false"
|
|
94
122
|
}
|
|
95
123
|
|
|
@@ -103,7 +131,7 @@ fi
|
|
|
103
131
|
|
|
104
132
|
SV=$(yaml_get "$SPECS/state.yaml" "bigpowers_version")
|
|
105
133
|
|
|
106
|
-
if
|
|
134
|
+
if yaml_value_set "$SV"; then
|
|
107
135
|
if [[ "$SV" == "$IV" ]]; then
|
|
108
136
|
emit_json '{"gap":false,"stamp":true,"detected_version":"'"$SV"'","installed_version":"'"$IV"'","detection_method":"stamp","confidence":"high"}'
|
|
109
137
|
exit 0
|
|
@@ -123,7 +151,7 @@ for f in release-plan.yaml execution-status.yaml product/SCOPE_LATEST.yaml metri
|
|
|
123
151
|
done
|
|
124
152
|
if [[ -f "$SPECS/state.yaml" ]]; then
|
|
125
153
|
ec=$(yaml_get "$SPECS/state.yaml" "epic_cycle.current_step")
|
|
126
|
-
if
|
|
154
|
+
if yaml_value_set "$ec"; then
|
|
127
155
|
MARKERS+=("epic_cycle"); [[ "$DV" == "2.0.0" ]] && DV="2.20.0"
|
|
128
156
|
fi
|
|
129
157
|
fi
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e44s03 e44s04
|
|
3
|
+
# Shared helpers for migrate-version.sh
|
|
4
|
+
|
|
5
|
+
if [ -n "${MIGRATE_VERSION_COMMON_LOADED:-}" ]; then return 0; fi
|
|
6
|
+
MIGRATE_VERSION_COMMON_LOADED=1
|
|
7
|
+
|
|
8
|
+
migrate_usage() {
|
|
9
|
+
cat <<'EOF'
|
|
10
|
+
Usage: scripts/migrate-version.sh [flags]
|
|
11
|
+
|
|
12
|
+
Flags:
|
|
13
|
+
--help Show this message
|
|
14
|
+
--dry-run Compose migration plan and show diff, but make no changes
|
|
15
|
+
--force Skip active-work block detection
|
|
16
|
+
--no-commit Skip auto-commit (still applies transforms and stamps)
|
|
17
|
+
--verbose Show detailed progress for each transform step
|
|
18
|
+
--project <dir> Target project directory (migrations from bigpowers)
|
|
19
|
+
EOF
|
|
20
|
+
exit 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
migrate_installed_version() {
|
|
24
|
+
node -e "console.log(require('$BIGPOWERS_ROOT/package.json').version)" 2>/dev/null || echo "unknown"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
migrate_yaml_get() {
|
|
28
|
+
local file="$1" key="$2"
|
|
29
|
+
$PYTHON -c "
|
|
30
|
+
import yaml, sys
|
|
31
|
+
try:
|
|
32
|
+
d = yaml.safe_load(open('$file'))
|
|
33
|
+
val = d
|
|
34
|
+
for k in '$key'.split('.'):
|
|
35
|
+
if not isinstance(val, dict): sys.exit(1)
|
|
36
|
+
val = val.get(k)
|
|
37
|
+
if val is None: sys.exit(1)
|
|
38
|
+
print(val)
|
|
39
|
+
except: sys.exit(1)
|
|
40
|
+
" 2>/dev/null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
log_info() { echo " [INFO] $*"; }
|
|
44
|
+
log_warn() { echo " [WARN] $*"; }
|
|
45
|
+
log_done() { echo " [OK] $*"; }
|
|
46
|
+
log_fail() { echo " [FAIL] $*"; }
|
|
47
|
+
log_skip() { echo " [SKIP] $*"; }
|
|
48
|
+
|
|
49
|
+
migrate_yaml_value_set() {
|
|
50
|
+
local value="$1"
|
|
51
|
+
[[ -n "$value" ]] || return 1
|
|
52
|
+
[[ "$value" == "null" ]] && return 1
|
|
53
|
+
return 0
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
migrate_on_feature_branch() {
|
|
57
|
+
local branch="$1"
|
|
58
|
+
[[ -n "$branch" ]] || return 1
|
|
59
|
+
[[ "$branch" == "main" || "$branch" == "master" ]] && return 1
|
|
60
|
+
return 0
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
migrate_specs_clean() {
|
|
64
|
+
git -C "$REPO_ROOT" diff --quiet -- specs/ 2>/dev/null \
|
|
65
|
+
&& git -C "$REPO_ROOT" diff --cached --quiet -- specs/ 2>/dev/null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
migrate_preflight_block() {
|
|
69
|
+
local branch af
|
|
70
|
+
branch="$(git -C "$REPO_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null)" || true
|
|
71
|
+
if migrate_on_feature_branch "${branch:-}"; then
|
|
72
|
+
echo "migrate-version: BLOCKED — on non-main branch '$branch'. Use --force to override." >&2
|
|
73
|
+
return 3
|
|
74
|
+
fi
|
|
75
|
+
if ! migrate_specs_clean; then
|
|
76
|
+
echo "migrate-version: BLOCKED — uncommitted changes in specs/. Commit or stash first." >&2
|
|
77
|
+
return 3
|
|
78
|
+
fi
|
|
79
|
+
if [[ -f "$SPECS_DIR/state.yaml" ]]; then
|
|
80
|
+
af="$(migrate_yaml_get "$SPECS_DIR/state.yaml" "active_flow" 2>/dev/null)" || true
|
|
81
|
+
if migrate_yaml_value_set "${af:-}"; then
|
|
82
|
+
echo "migrate-version: BLOCKED — active_flow '$af' is set (mid-epic work). Use --force." >&2
|
|
83
|
+
return 3
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
return 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
migrate_detect_version() {
|
|
90
|
+
local stamped
|
|
91
|
+
DETECTED=""
|
|
92
|
+
if [[ -f "$SPECS_DIR/state.yaml" ]]; then
|
|
93
|
+
stamped="$(migrate_yaml_get "$SPECS_DIR/state.yaml" "bigpowers_version" 2>/dev/null)" || true
|
|
94
|
+
if migrate_yaml_value_set "${stamped:-}"; then
|
|
95
|
+
DETECTED="$stamped"
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
if [[ -z "$DETECTED" ]]; then
|
|
99
|
+
if [[ -f "$SPECS_DIR/STATE.md" && ! -f "$SPECS_DIR/state.yaml" ]]; then
|
|
100
|
+
DETECTED="1.0.0"
|
|
101
|
+
elif [[ -f "$SPECS_DIR/state.yaml" ]]; then
|
|
102
|
+
if migrate_yaml_get "$SPECS_DIR/state.yaml" "epic_cycle" >/dev/null 2>&1; then
|
|
103
|
+
DETECTED="2.20.0"
|
|
104
|
+
else
|
|
105
|
+
DETECTED="2.0.0"
|
|
106
|
+
fi
|
|
107
|
+
else
|
|
108
|
+
echo "migrate-version: cannot determine spec version" >&2
|
|
109
|
+
return 4
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e44s03
|
|
3
|
+
# Migration execution loop for migrate-version.sh
|
|
4
|
+
|
|
5
|
+
if [ -n "${MIGRATE_VERSION_EXECUTE_LOADED:-}" ]; then return 0; fi
|
|
6
|
+
MIGRATE_VERSION_EXECUTE_LOADED=1
|
|
7
|
+
|
|
8
|
+
migrate_execute_migrations() {
|
|
9
|
+
local MIG_LIST mig_json MIG_ID MIG_FILE MIG_TITLE BUNDLE_PATH TRANSFORMS T_LIST tjson action VERIFY_CMDS VERIFY_OK vcmd
|
|
10
|
+
|
|
11
|
+
echo ""
|
|
12
|
+
echo "Applying migrations …"
|
|
13
|
+
SUCCEEDED=()
|
|
14
|
+
FAILED=()
|
|
15
|
+
SKIPPED=()
|
|
16
|
+
SUCCEEDED_COUNT=0
|
|
17
|
+
FAILED_COUNT=0
|
|
18
|
+
SKIPPED_COUNT=0
|
|
19
|
+
UNCERTAINTY_COUNT=0
|
|
20
|
+
|
|
21
|
+
MIG_LIST="$(echo "$MIGRATION_PLAN" | $PYTHON -c "import json,sys; [print(json.dumps(m)) for m in json.load(sys.stdin)]")"
|
|
22
|
+
while IFS= read -r mig_json; do
|
|
23
|
+
[ -z "$mig_json" ] && continue
|
|
24
|
+
MIG_ID="$(echo "$mig_json" | $PYTHON -c "import json,sys; print(json.load(sys.stdin)['id'])")"
|
|
25
|
+
MIG_FILE="$(echo "$mig_json" | $PYTHON -c "import json,sys; print(json.load(sys.stdin)['file'])")"
|
|
26
|
+
MIG_TITLE="$(echo "$mig_json" | $PYTHON -c "import json,sys; print(json.load(sys.stdin)['title'])")"
|
|
27
|
+
|
|
28
|
+
BUNDLE_PATH="$MIGRATIONS_DIR/$MIG_FILE"
|
|
29
|
+
if [ ! -f "$BUNDLE_PATH" ]; then
|
|
30
|
+
log_warn "$MIG_ID: bundle file not found — skipping"
|
|
31
|
+
SKIPPED+=("$MIG_ID:bundle_missing")
|
|
32
|
+
continue
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
echo ""
|
|
36
|
+
echo "── $MIG_ID: $MIG_TITLE ──"
|
|
37
|
+
|
|
38
|
+
TRANSFORMS="$($PYTHON -c "
|
|
39
|
+
import yaml, json
|
|
40
|
+
with open('$BUNDLE_PATH') as f:
|
|
41
|
+
parts = f.read().split('---')
|
|
42
|
+
fm = yaml.safe_load(parts[1])
|
|
43
|
+
print(json.dumps(fm.get('transforms', []), default=str))
|
|
44
|
+
" 2>/dev/null)"
|
|
45
|
+
|
|
46
|
+
T_LIST="$(echo "$TRANSFORMS" | $PYTHON -c "import json,sys; [print(json.dumps(t, default=str)) for t in json.load(sys.stdin)]")"
|
|
47
|
+
while IFS= read -r tjson; do
|
|
48
|
+
[ -z "$tjson" ] && continue
|
|
49
|
+
action="$(echo "$tjson" | $PYTHON -c "import json,sys; print(json.load(sys.stdin).get('action',''))")"
|
|
50
|
+
apply_transform "$MIG_ID" "$action" "$tjson"
|
|
51
|
+
done <<< "$T_LIST"
|
|
52
|
+
|
|
53
|
+
VERIFY_CMDS="$($PYTHON -c "
|
|
54
|
+
import yaml, json
|
|
55
|
+
with open('$BUNDLE_PATH') as f:
|
|
56
|
+
parts = f.read().split('---')
|
|
57
|
+
fm = yaml.safe_load(parts[1])
|
|
58
|
+
for a in fm.get('actions_needed', []):
|
|
59
|
+
v = a.get('verify', '')
|
|
60
|
+
if v: print(v)
|
|
61
|
+
" 2>/dev/null)"
|
|
62
|
+
|
|
63
|
+
VERIFY_OK=true
|
|
64
|
+
while IFS= read -r vcmd; do
|
|
65
|
+
[ -z "$vcmd" ] && continue
|
|
66
|
+
log_info "verify: $vcmd"
|
|
67
|
+
if eval "$vcmd" 2>&1; then
|
|
68
|
+
log_done "verify passed"
|
|
69
|
+
else
|
|
70
|
+
log_fail "verify failed"
|
|
71
|
+
VERIFY_OK=false
|
|
72
|
+
fi
|
|
73
|
+
done <<< "$VERIFY_CMDS"
|
|
74
|
+
|
|
75
|
+
if [ "$VERIFY_OK" = "true" ]; then
|
|
76
|
+
SUCCEEDED+=("$MIG_ID")
|
|
77
|
+
else
|
|
78
|
+
FAILED+=("$MIG_ID")
|
|
79
|
+
fi
|
|
80
|
+
done <<< "$MIG_LIST"
|
|
81
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e44s03
|
|
3
|
+
# Migration plan builder and dry-run preview for migrate-version.sh
|
|
4
|
+
|
|
5
|
+
if [ -n "${MIGRATE_VERSION_PLAN_LOADED:-}" ]; then return 0; fi
|
|
6
|
+
MIGRATE_VERSION_PLAN_LOADED=1
|
|
7
|
+
|
|
8
|
+
migrate_build_plan() {
|
|
9
|
+
echo "migrate-version: building migration plan from $REGISTRY …"
|
|
10
|
+
|
|
11
|
+
MIGRATION_PLAN="$($PYTHON -c "
|
|
12
|
+
import yaml, json, sys
|
|
13
|
+
|
|
14
|
+
with open('$REGISTRY') as f:
|
|
15
|
+
parts = f.read().split('---')
|
|
16
|
+
fm = yaml.safe_load(parts[1])
|
|
17
|
+
|
|
18
|
+
detected = '$DETECTED'
|
|
19
|
+
installed = '$INSTALLED'
|
|
20
|
+
|
|
21
|
+
plan = []
|
|
22
|
+
for m in sorted(fm.get('migrations', []), key=lambda x: x.get('order', 99)):
|
|
23
|
+
sid = m['id']
|
|
24
|
+
since = m.get('since_version', '0')
|
|
25
|
+
order = m.get('order', 99)
|
|
26
|
+
deps = m.get('depends_on', [])
|
|
27
|
+
plan.append({'id': sid, 'file': m.get('file', sid + '.okf.md'),
|
|
28
|
+
'since_version': since, 'order': order,
|
|
29
|
+
'depends_on': deps, 'title': m.get('title', sid)})
|
|
30
|
+
|
|
31
|
+
applicable = [m for m in plan if m['since_version'] > detected and m['since_version'] <= installed]
|
|
32
|
+
print(json.dumps(applicable))
|
|
33
|
+
" 2>/dev/null)"
|
|
34
|
+
|
|
35
|
+
MIGRATION_COUNT="$(echo "$MIGRATION_PLAN" | $PYTHON -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null)"
|
|
36
|
+
|
|
37
|
+
if [ "${MIGRATION_COUNT:-0}" -eq 0 ]; then
|
|
38
|
+
echo "migrate-version: no applicable migrations for $DETECTED → $INSTALLED — stamping version only"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "migrate-version: $MIGRATION_COUNT migration(s) to apply"
|
|
42
|
+
|
|
43
|
+
if [ "$VERBOSE" = "true" ]; then
|
|
44
|
+
echo "$MIGRATION_PLAN" | $PYTHON -c "
|
|
45
|
+
import json,sys
|
|
46
|
+
for m in json.load(sys.stdin):
|
|
47
|
+
print(f\" {m['order']}. {m['id']}: {m['title']} (since {m['since_version']})\")
|
|
48
|
+
"
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
migrate_backup_specs() {
|
|
53
|
+
log_info "Creating backup: $BACKUP_DIR"
|
|
54
|
+
rm -rf "$BACKUP_DIR"
|
|
55
|
+
mkdir -p "$BACKUP_DIR"
|
|
56
|
+
cp -r "$SPECS_DIR"/* "$BACKUP_DIR"/ 2>/dev/null || true
|
|
57
|
+
rm -rf "$BACKUP_DIR/.pre-upgrade-backup" 2>/dev/null || true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
migrate_print_dry_run() {
|
|
61
|
+
echo ""
|
|
62
|
+
echo "=========================================="
|
|
63
|
+
echo " DRY RUN — no changes will be made"
|
|
64
|
+
echo "=========================================="
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Detected version: $DETECTED"
|
|
67
|
+
echo "Target version: $INSTALLED"
|
|
68
|
+
echo "Migrations:"
|
|
69
|
+
echo "$MIGRATION_PLAN" | $PYTHON -c "
|
|
70
|
+
import json,sys
|
|
71
|
+
for m in json.load(sys.stdin):
|
|
72
|
+
deps = m.get('depends_on', [])
|
|
73
|
+
dep_str = ' (depends on: ' + ', '.join(deps) + ')' if deps else ''
|
|
74
|
+
print(f\" [{m['order']}] {m['id']}: {m['title']}{dep_str}\")
|
|
75
|
+
"
|
|
76
|
+
|
|
77
|
+
echo ""
|
|
78
|
+
echo "Transforms preview:"
|
|
79
|
+
echo "$MIGRATION_PLAN" | $PYTHON -c "
|
|
80
|
+
import json, sys, os
|
|
81
|
+
for m in json.load(sys.stdin):
|
|
82
|
+
bundle_path = os.path.join('$MIGRATIONS_DIR', m['file'])
|
|
83
|
+
if not os.path.exists(bundle_path):
|
|
84
|
+
print(f\" WARN {m['id']}: bundle file not found ({m['file']})\")
|
|
85
|
+
continue
|
|
86
|
+
with open(bundle_path) as f:
|
|
87
|
+
parts = f.read().split('---')
|
|
88
|
+
fm = __import__('yaml').safe_load(parts[1])
|
|
89
|
+
transforms = fm.get('transforms', [])
|
|
90
|
+
print(f\"\n -- {m['id']}: {m['title']} --\")
|
|
91
|
+
for t in transforms:
|
|
92
|
+
action = t.get('action', '?')
|
|
93
|
+
note = t.get('note', '')
|
|
94
|
+
note_str = f' ({note})' if note else ''
|
|
95
|
+
if action == 'convert_md_to_yaml':
|
|
96
|
+
src = t.get('source', '?')
|
|
97
|
+
tgt = t.get('target', '?')
|
|
98
|
+
unc = t.get('uncertainty', [])
|
|
99
|
+
print(f\" {action}: {src} -> {tgt}{note_str}\")
|
|
100
|
+
for u in unc:
|
|
101
|
+
print(f\" GUESS: {u.get('field','?')} - {u.get('reason','?')}\")
|
|
102
|
+
elif action in ('rename_file', 'move_file'):
|
|
103
|
+
print(f\" {action}: {t.get('source','?')} -> {t.get('target','?')}{note_str}\")
|
|
104
|
+
elif action == 'delete_file':
|
|
105
|
+
print(f\" {action}: {t.get('path','?')}{note_str}\")
|
|
106
|
+
elif action in ('set_yaml_key', 'rename_yaml_key', 'delete_yaml_key'):
|
|
107
|
+
print(f\" {action}: {t.get('file','?')} {t.get('key','?') or t.get('old_key','?')} = {t.get('value','?')}{note_str}\")
|
|
108
|
+
elif action == 'create_file_from_template':
|
|
109
|
+
print(f\" {action}: {t.get('source','?')} -> {t.get('target','?')}{note_str}\")
|
|
110
|
+
else:
|
|
111
|
+
print(f\" {action}{note_str}\")
|
|
112
|
+
for a in fm.get('actions_needed', []):
|
|
113
|
+
v = a.get('verify', '')
|
|
114
|
+
if v: print(f\" verify: {v}\")
|
|
115
|
+
" 2>/dev/null
|
|
116
|
+
|
|
117
|
+
echo ""
|
|
118
|
+
echo "Run without --dry-run to apply these migrations."
|
|
119
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e44s03 e44s04
|
|
3
|
+
# Post-migration stamp, gates, report, and commit for migrate-version.sh
|
|
4
|
+
|
|
5
|
+
if [ -n "${MIGRATE_VERSION_POST_LOADED:-}" ]; then return 0; fi
|
|
6
|
+
MIGRATE_VERSION_POST_LOADED=1
|
|
7
|
+
|
|
8
|
+
migrate_stamp_version() {
|
|
9
|
+
if [ -f "$SPECS_DIR/state.yaml" ]; then
|
|
10
|
+
log_info "Stamping bigpowers_version=$INSTALLED in state.yaml"
|
|
11
|
+
$PYTHON "$YAML_TOOLS" set "$SPECS_DIR/state.yaml" "bigpowers_version" "$INSTALLED"
|
|
12
|
+
log_done "stamp applied"
|
|
13
|
+
fi
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
migrate_run_soft_gates() {
|
|
17
|
+
if [ -f "$SCRIPT_DIR/validate-okf.sh" ]; then
|
|
18
|
+
log_info "Running validate-okf.sh (soft gate) …"
|
|
19
|
+
if bash "$SCRIPT_DIR/validate-okf.sh" --dir "$MIGRATIONS_DIR" 2>&1; then
|
|
20
|
+
log_done "validate-okf: PASS"
|
|
21
|
+
else
|
|
22
|
+
log_warn "validate-okf: some checks failed (soft gate — non-blocking)"
|
|
23
|
+
fi
|
|
24
|
+
else
|
|
25
|
+
log_info "validate-okf.sh not found — skipping soft gate"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if [ -f "$SCRIPT_DIR/validate-specs-yaml.sh" ]; then
|
|
29
|
+
log_info "Running global validate-specs-yaml.sh …"
|
|
30
|
+
if bash "$SCRIPT_DIR/validate-specs-yaml.sh" 2>&1; then
|
|
31
|
+
log_done "validate-specs-yaml: PASS"
|
|
32
|
+
else
|
|
33
|
+
log_fail "validate-specs-yaml: FAIL"
|
|
34
|
+
FAILED+=("validate-specs-yaml")
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
migrate_check_staleness() {
|
|
40
|
+
local proj_lines bp_lines delta
|
|
41
|
+
STALE_FILES=()
|
|
42
|
+
BIGPOWERS_CLAUDE="$SCRIPT_DIR/../CLAUDE.md"
|
|
43
|
+
if [ -f "$REPO_ROOT/CLAUDE.md" ] && [ -f "$BIGPOWERS_CLAUDE" ]; then
|
|
44
|
+
proj_lines="$(wc -l < "$REPO_ROOT/CLAUDE.md")"
|
|
45
|
+
bp_lines="$(wc -l < "$BIGPOWERS_CLAUDE")"
|
|
46
|
+
delta=$((bp_lines - proj_lines))
|
|
47
|
+
if [ $delta -gt 20 ]; then
|
|
48
|
+
STALE_FILES+=("CLAUDE.md")
|
|
49
|
+
log_warn "CLAUDE.md: $delta lines behind bigpowers template — consider updating"
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
if [ -f "$REPO_ROOT/CONVENTIONS.md" ] && [ -f "$SCRIPT_DIR/../CONVENTIONS.md" ]; then
|
|
53
|
+
proj_lines="$(wc -l < "$REPO_ROOT/CONVENTIONS.md")"
|
|
54
|
+
bp_lines="$(wc -l < "$SCRIPT_DIR/../CONVENTIONS.md")"
|
|
55
|
+
delta=$((bp_lines - proj_lines))
|
|
56
|
+
if [ $delta -gt 20 ]; then
|
|
57
|
+
STALE_FILES+=("CONVENTIONS.md")
|
|
58
|
+
log_warn "CONVENTIONS.md: $delta lines behind bigpowers template — consider updating"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
migrate_write_report() {
|
|
64
|
+
local m ISO_DATE FAILED_COUNT
|
|
65
|
+
ISO_DATE="$(date -u +%Y-%m-%dT%H%M%SZ)"
|
|
66
|
+
REPORT_FILE="$SPECS_DIR/migration-report-$ISO_DATE.md"
|
|
67
|
+
|
|
68
|
+
cat > "$REPORT_FILE" <<REPORT
|
|
69
|
+
# Migration Report — $ISO_DATE
|
|
70
|
+
|
|
71
|
+
## Gap Summary
|
|
72
|
+
|
|
73
|
+
| Field | Value |
|
|
74
|
+
|-------|-------|
|
|
75
|
+
| Detected version | $DETECTED |
|
|
76
|
+
| Installed version | $INSTALLED |
|
|
77
|
+
| Migration count | ${MIGRATION_COUNT:-0} |
|
|
78
|
+
|
|
79
|
+
## Migrations Applied
|
|
80
|
+
|
|
81
|
+
REPORT
|
|
82
|
+
|
|
83
|
+
for m in "${SUCCEEDED[@]:-}"; do
|
|
84
|
+
[ -z "$m" ] && continue
|
|
85
|
+
echo "- $m PASS" >> "$REPORT_FILE"
|
|
86
|
+
done
|
|
87
|
+
FAILED_COUNT="${#FAILED[@]}"
|
|
88
|
+
if [ "${FAILED_COUNT}" -gt 0 ]; then
|
|
89
|
+
echo "" >> "$REPORT_FILE"
|
|
90
|
+
echo "## Migrations Failed" >> "$REPORT_FILE"
|
|
91
|
+
for m in "${FAILED[@]}"; do
|
|
92
|
+
echo "- $m FAIL" >> "$REPORT_FILE"
|
|
93
|
+
done
|
|
94
|
+
fi
|
|
95
|
+
if [ ${#SKIPPED[@]} -gt 0 ]; then
|
|
96
|
+
echo "" >> "$REPORT_FILE"
|
|
97
|
+
echo "## Skipped" >> "$REPORT_FILE"
|
|
98
|
+
for m in "${SKIPPED[@]}"; do
|
|
99
|
+
echo "- $m" >> "$REPORT_FILE"
|
|
100
|
+
done
|
|
101
|
+
fi
|
|
102
|
+
echo "" >> "$REPORT_FILE"
|
|
103
|
+
echo "## Uncertainties" >> "$REPORT_FILE"
|
|
104
|
+
echo "WARN count: $UNCERTAINTY_COUNT — review specs/ for GUESS markers and adjust if needed." >> "$REPORT_FILE"
|
|
105
|
+
echo "" >> "$REPORT_FILE"
|
|
106
|
+
echo "## Verification" >> "$REPORT_FILE"
|
|
107
|
+
echo "- validate-specs-yaml: ran as final gate" >> "$REPORT_FILE"
|
|
108
|
+
|
|
109
|
+
if [ ${#STALE_FILES[@]} -gt 0 ]; then
|
|
110
|
+
echo "" >> "$REPORT_FILE"
|
|
111
|
+
echo "## Staleness Notice" >> "$REPORT_FILE"
|
|
112
|
+
for f in "${STALE_FILES[@]:-}"; do
|
|
113
|
+
[ -z "$f" ] && continue
|
|
114
|
+
echo "- $f is behind the current bigpowers template" >> "$REPORT_FILE"
|
|
115
|
+
done
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
echo "" >> "$REPORT_FILE"
|
|
119
|
+
echo "## Next Steps" >> "$REPORT_FILE"
|
|
120
|
+
echo "1. Review the migration report and verify spec correctness" >> "$REPORT_FILE"
|
|
121
|
+
echo "2. Run \`bash scripts/sync-skills.sh\` to regenerate agent artifacts" >> "$REPORT_FILE"
|
|
122
|
+
echo "3. Commit changes if not auto-committed" >> "$REPORT_FILE"
|
|
123
|
+
echo "" >> "$REPORT_FILE"
|
|
124
|
+
echo "## Rollback" >> "$REPORT_FILE"
|
|
125
|
+
echo "To rollback, restore from backup:" >> "$REPORT_FILE"
|
|
126
|
+
echo "\`\`\`bash" >> "$REPORT_FILE"
|
|
127
|
+
echo "rm -rf specs/ && cp -r $BACKUP_DIR/ specs/" >> "$REPORT_FILE"
|
|
128
|
+
echo "\`\`\`" >> "$REPORT_FILE"
|
|
129
|
+
|
|
130
|
+
log_done "Migration report: $REPORT_FILE"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
migrate_print_summary() {
|
|
134
|
+
echo ""
|
|
135
|
+
echo "=========================================="
|
|
136
|
+
echo " Migration complete: $DETECTED -> $INSTALLED"
|
|
137
|
+
echo "=========================================="
|
|
138
|
+
echo " Applied: ${#SUCCEEDED[@]}"
|
|
139
|
+
echo " Failed: ${#FAILED[@]}"
|
|
140
|
+
echo " Skipped: ${#SKIPPED[@]}"
|
|
141
|
+
echo " WARN: $UNCERTAINTY_COUNT"
|
|
142
|
+
[ ${#STALE_FILES[@]} -gt 0 ] && echo " Stale: ${STALE_FILES[*]:-}"
|
|
143
|
+
echo " Report: $REPORT_FILE"
|
|
144
|
+
echo "=========================================="
|
|
145
|
+
echo " Remember: run 'bash scripts/sync-skills.sh'"
|
|
146
|
+
echo "=========================================="
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
migrate_auto_commit() {
|
|
150
|
+
local COMMIT_MSG
|
|
151
|
+
if [ "$SKIP_COMMIT" != "true" ] && [ "${MIGRATION_COUNT:-0}" -gt 0 ]; then
|
|
152
|
+
log_info "Auto-committing migration …"
|
|
153
|
+
git -C "$REPO_ROOT" add specs/ 2>/dev/null || true
|
|
154
|
+
COMMIT_MSG="migrate-version: $DETECTED -> $INSTALLED (${MIGRATION_COUNT:-0} migration(s))"
|
|
155
|
+
if git -C "$REPO_ROOT" commit -m "$COMMIT_MSG" 2>/dev/null; then
|
|
156
|
+
log_done "committed: $COMMIT_MSG"
|
|
157
|
+
else
|
|
158
|
+
log_info "nothing to commit (already clean)"
|
|
159
|
+
fi
|
|
160
|
+
fi
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
migrate_finalize() {
|
|
164
|
+
migrate_stamp_version
|
|
165
|
+
migrate_run_soft_gates
|
|
166
|
+
migrate_check_staleness
|
|
167
|
+
migrate_write_report
|
|
168
|
+
migrate_print_summary
|
|
169
|
+
migrate_auto_commit
|
|
170
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# story: e44s03 e44s04
|
|
3
|
+
# migrate-version orchestrator — sources lib modules and runs the migration pipeline
|
|
4
|
+
|
|
5
|
+
if [ -n "${MIGRATE_VERSION_RUN_LOADED:-}" ]; then return 0; fi
|
|
6
|
+
MIGRATE_VERSION_RUN_LOADED=1
|
|
7
|
+
|
|
8
|
+
_LIB="$(dirname "${BASH_SOURCE[0]}")"
|
|
9
|
+
source "$_LIB/migrate-version-common.sh"
|
|
10
|
+
source "$_LIB/migrate-version-plan.sh"
|
|
11
|
+
source "$_LIB/migrate-version-transforms.sh"
|
|
12
|
+
source "$_LIB/migrate-version-execute.sh"
|
|
13
|
+
source "$_LIB/migrate-version-post.sh"
|
|
14
|
+
|
|
15
|
+
migrate_version_main() {
|
|
16
|
+
if [ "$FORCE" != "true" ]; then
|
|
17
|
+
migrate_preflight_block || exit $?
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [ ! -d "$SPECS_DIR" ]; then
|
|
21
|
+
echo "migrate-version: no specs/ directory found" >&2
|
|
22
|
+
exit 2
|
|
23
|
+
fi
|
|
24
|
+
if [ ! -f "$REGISTRY" ]; then
|
|
25
|
+
echo "migrate-version: migration registry not found at $REGISTRY" >&2
|
|
26
|
+
exit 4
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
INSTALLED="$(migrate_installed_version)"
|
|
30
|
+
migrate_detect_version || exit $?
|
|
31
|
+
|
|
32
|
+
echo "migrate-version: detected=$DETECTED installed=$INSTALLED"
|
|
33
|
+
|
|
34
|
+
if [ "$DETECTED" = "$INSTALLED" ]; then
|
|
35
|
+
echo "migrate-version: no version gap — specs are current"
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
migrate_build_plan
|
|
40
|
+
|
|
41
|
+
if [ "$DRY_RUN" != "true" ]; then
|
|
42
|
+
migrate_backup_specs
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ "$DRY_RUN" = "true" ]; then
|
|
46
|
+
migrate_print_dry_run
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
migrate_execute_migrations
|
|
51
|
+
migrate_finalize
|
|
52
|
+
exit 0
|
|
53
|
+
}
|