bigpowers 2.58.1 → 2.58.3

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.
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ # story: e38s06
3
+ # waiver-utils.sh — common waiver subsystem for compliance scripts.
4
+ # Provides has_waiver() for bash 3.2+ compatibility.
5
+ # Waived steps are excluded from the denominator; expired waivers re-enter as FAILs.
6
+ # Source this file in scripts that need waiver checking.
7
+ # Requires: WAIVER_FILE variable set before sourcing (defaults to specs/verifications/waivers.yaml).
8
+
9
+ : "${WAIVER_FILE:=specs/verifications/waivers.yaml}"
10
+
11
+ has_waiver() {
12
+ local step_sanitized="$1"
13
+ local waiver_file="${WAIVER_FILE:-specs/verifications/waivers.yaml}"
14
+
15
+ if [[ ! -f "$waiver_file" ]]; then
16
+ return 1 # No waiver file → not waived
17
+ fi
18
+
19
+ # Check if step has any waiver entry
20
+ if ! grep -q "step_sanitized: $step_sanitized" "$waiver_file" 2>/dev/null; then
21
+ return 1 # Not found → not waived
22
+ fi
23
+
24
+ # Extract review_date for this step (parses the YAML block)
25
+ local review_date
26
+ review_date=$(awk -v slug="$step_sanitized" '
27
+ $0 ~ "step_sanitized: " slug { found=1; next }
28
+ found && /review_date:/ { gsub(/[^0-9-]/, "", $0); print; exit }
29
+ ' "$waiver_file")
30
+
31
+ # Check expiry: if review_date < today, waiver is expired
32
+ local today
33
+ today=$(date +%Y-%m-%d)
34
+ if [[ -n "$review_date" && "$review_date" < "$today" ]]; then
35
+ return 2 # Expired waiver → counts as FAIL
36
+ fi
37
+
38
+ return 0 # Valid waiver → excluded from denominator
39
+ }
@@ -44,13 +44,15 @@
44
44
  #
45
45
  set -euo pipefail
46
46
 
47
- IDLE_SECONDS=$(( 120 * 60 )) # git-hours maxCommitDiffInMinutes
48
- PAD_SECONDS=$(( 120 * 60 )) # git-hours firstCommitAdditionInMinutes
47
+ IDLE_SECONDS=$(( 120 * 60 ))
48
+ PAD_SECONDS=$(( 120 * 60 ))
49
49
  STORY_KEY="Story"
50
+ # git-hours engine — partition + whole_range_hours (additivity oracle)
51
+ true && source "$(dirname "${BASH_SOURCE[0]}")/lib/git-hours.sh"
50
52
 
51
53
  die() { echo "record-cycle-time: $*" >&2; exit 1; }
52
54
 
53
- usage() {
55
+ usage_cycle() {
54
56
  cat <<'EOF'
55
57
  Usage: scripts/record-cycle-time.sh <command> [flags]
56
58
 
@@ -82,50 +84,6 @@ EOF
82
84
  command -v git >/dev/null 2>&1 || die "git not found"
83
85
  ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || die "not inside a git repo"
84
86
 
85
- # ---- engine ---------------------------------------------------------------
86
- # Emits, per story bucket, a TSV line: story <TAB> effort_hours <TAB> commit_count <TAB> first_epoch <TAB> last_epoch
87
- # Reads the whole range once and partitions globally, so the lines are additive.
88
- partition() {
89
- local range="$1" key="$2"
90
- # US (\x1f) between fields; one commit per line, oldest→newest by author date.
91
- git -C "$ROOT" log --no-merges "$range" \
92
- --pretty=format:"%at%x1f%H%x1f%(trailers:key=${key},valueonly,separator=%x2C)" \
93
- | sort -n \
94
- | awk -F '\x1f' -v IDLE="$IDLE_SECONDS" -v PAD="$PAD_SECONDS" '
95
- {
96
- ts = $1 + 0
97
- story = $3
98
- gsub(/[[:space:]]/, "", story)
99
- if (story == "") story = "unattributed"
100
- count[story]++
101
- seen[story] = 1
102
- if (NR > 1) {
103
- gap = ts - prev
104
- if (gap < 0) gap = 0 # guard against author-date skew
105
- if (gap < IDLE) eff[story] += gap # real elapsed effort -> later commit
106
- else eff[story] += PAD # new session pad -> later commit
107
- }
108
- if (!(story in first)) first[story] = ts
109
- last[story] = ts
110
- prev = ts
111
- }
112
- END {
113
- for (s in seen)
114
- printf "%s\t%.4f\t%d\t%d\t%d\n", s, eff[s] / 3600.0, count[s], first[s], last[s]
115
- }' \
116
- | sort
117
- }
118
-
119
- # Whole-range effort in one bucket — the independent additivity oracle.
120
- whole_range_hours() {
121
- local range="$1"
122
- git -C "$ROOT" log --no-merges "$range" --pretty=format:"%at" \
123
- | sort -n \
124
- | awk -v IDLE="$IDLE_SECONDS" -v PAD="$PAD_SECONDS" '
125
- { ts=$1+0; if(NR>1){gap=ts-prev; if(gap<0)gap=0; if(gap<IDLE)e+=gap; else e+=PAD} prev=ts }
126
- END { printf "%.4f\n", e/3600.0 }'
127
- }
128
-
129
87
  cmd_report() {
130
88
  local range="HEAD" key="$STORY_KEY"
131
89
  while [ $# -gt 0 ]; do
@@ -322,11 +280,11 @@ cmd_append() {
322
280
  echo "record-cycle-time: OKF bundle written for $story → $file (effort=$eff h, lead=$lead_min min, commits=$count)"
323
281
  }
324
282
 
325
- [ $# -ge 1 ] || { usage; }
283
+ [ $# -ge 1 ] || { usage_cycle; }
326
284
 
327
285
  # Trap --help before subcommand dispatch
328
286
  for a in "$@"; do
329
- if [ "$a" = "--help" ] || [ "$a" = "-h" ]; then usage; fi
287
+ if [ "$a" = "--help" ] || [ "$a" = "-h" ]; then usage_cycle; fi
330
288
  done
331
289
 
332
290
  sub="$1"; shift
@@ -334,7 +292,7 @@ case "$sub" in
334
292
  report) cmd_report "$@" ;;
335
293
  append) cmd_append "$@" ;;
336
294
  self-test) cmd_report "$@" ;;
337
- help) usage ;;
338
- --help) usage ;;
339
- *) die "unknown subcommand '$sub' (run 'record-cycle-time.sh help' for usage)" ;;
295
+ help) usage_cycle ;;
296
+ --help) usage_cycle ;;
297
+ *) die "unknown subcommand '$sub' (run 'record-cycle-time.sh help' for help)" ;;
340
298
  esac