@windyroad/risk-scorer 0.18.1 → 0.18.2

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.
@@ -310,5 +310,5 @@
310
310
  }
311
311
  },
312
312
  "name": "wr-risk-scorer",
313
- "version": "0.18.1"
313
+ "version": "0.18.2"
314
314
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wr-risk-scorer",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "Pipeline risk scoring, commit/push gates, and secret leak detection",
5
5
  "author": {
6
6
  "name": "Windy Road Technology",
@@ -32,21 +32,37 @@ if [ -n "$SESSION_ID" ] && [ -f "$RELEASE_SCORE_FILE" ]; then
32
32
  fi
33
33
 
34
34
  # --- Read appetite from RISK-POLICY.md ---
35
- # Default 4 (Low) MUST match the canonical gate parser default in
36
- # packages/risk-scorer/hooks/lib/risk-gate.sh (P377/RFC-029: a default of 5
37
- # here was an ADR-065 confirmation violation — the canonical default is 4).
38
- # The authoritative parser lives in risk-gate.sh; this guidance script mirrors
39
- # its 3 tolerated phrasings (Threshold: N | exceeds N | N / Low appetite).
40
- APPETITE="4"
41
- if [ -f "RISK-POLICY.md" ]; then
42
- EXTRACTED=$(grep -oP '(?:Threshold:\s*|exceeds\s+)\K[0-9]+' RISK-POLICY.md 2>/dev/null | head -1 || echo "")
43
- if [ -z "$EXTRACTED" ]; then
44
- EXTRACTED=$(grep -oP '[0-9]+(?=\s*/\s*Low appetite)' RISK-POLICY.md 2>/dev/null | head -1 || echo "")
45
- fi
46
- if [ -n "$EXTRACTED" ]; then
47
- APPETITE="$EXTRACTED"
48
- fi
49
- fi
35
+ # Mirror the canonical risk-gate parser: scope matches to the Risk Appetite
36
+ # section and default to the ADR-086 Low ceiling of 5.
37
+ APPETITE=$(python3 -c "
38
+ import re
39
+ try:
40
+ text = open('RISK-POLICY.md', encoding='utf-8').read()
41
+ except Exception:
42
+ text = ''
43
+ section = re.search(r'##\s*Risk Appetite\s*(.*?)(?=\n##\s|\Z)', text, re.DOTALL | re.IGNORECASE)
44
+ scope = section.group(1) if section else text
45
+ value = None
46
+ for pattern in (r'Threshold:\s*(\d+)', r'exceeds\s+(\d+)', r'(\d+)\s*/\s*Low appetite'):
47
+ match = re.search(pattern, scope, re.IGNORECASE)
48
+ if match:
49
+ value = match.group(1)
50
+ break
51
+ print(value or '5')
52
+ " 2>/dev/null || echo "5")
53
+
54
+ emit_json() {
55
+ HOOK_SYSTEM_MESSAGE="$1" python3 -c "
56
+ import json, os
57
+ print(json.dumps({
58
+ 'hookSpecificOutput': {
59
+ 'hookEventName': 'PreToolUse',
60
+ 'permissionDecision': 'allow',
61
+ 'systemMessage': os.environ['HOOK_SYSTEM_MESSAGE'],
62
+ }
63
+ }))
64
+ "
65
+ }
50
66
 
51
67
  # --- P096 Phase 2 — once-per-session gating (ADR-038 progressive disclosure) ---
52
68
  # First EnterPlanMode of a session emits the full advisory body; subsequent
@@ -54,27 +70,17 @@ fi
54
70
  # ADR-038 budget). Pipeline state and appetite are unchanged across plan-mode
55
71
  # entries within one session, so re-emitting full prose is repetition.
56
72
  if has_announced "risk-scorer-plan-guidance" "$SESSION_ID"; then
57
- cat <<EOF
58
- {
59
- "hookSpecificOutput": {
60
- "hookEventName": "PreToolUse",
61
- "permissionDecision": "allow",
62
- "systemMessage": "MANDATORY release-risk gate active (RISK-POLICY.md present). Release risk: ${RELEASE_SCORE}; appetite: ${APPETITE}. ExitPlanMode will FAIL plans projected above appetite. See first-EnterPlanMode emission for full guidance."
63
- }
64
- }
65
- EOF
73
+ emit_json "MANDATORY release-risk gate active (RISK-POLICY.md present). Release risk: ${RELEASE_SCORE}; appetite: ${APPETITE}. ExitPlanMode will FAIL plans projected above appetite. See first-EnterPlanMode emission for full guidance."
66
74
  exit 0
67
75
  fi
68
76
 
69
77
  # --- First emission: full advisory (compressed per audit recommendation) ---
70
78
  mark_announced "risk-scorer-plan-guidance" "$SESSION_ID"
71
- cat <<EOF
72
- {
73
- "hookSpecificOutput": {
74
- "hookEventName": "PreToolUse",
75
- "permissionDecision": "allow",
76
- "systemMessage": "RELEASE RISK GUIDANCE FOR PLANNING:\nUnreleased queue:\n${UNRELEASED_SUMMARY}\n\nRelease risk: ${RELEASE_SCORE}. Appetite threshold: ${APPETITE} (Medium).\n\nIf projected release risk would exceed appetite, the plan MUST include a release strategy (release queue first, split into smaller batches, or risk-reducing steps). See RISK-POLICY.md for option details. ExitPlanMode runs the risk-scorer and FAILS plans above appetite without a strategy."
77
- }
78
- }
79
- EOF
79
+ emit_json "RELEASE RISK GUIDANCE FOR PLANNING:
80
+ Unreleased queue:
81
+ ${UNRELEASED_SUMMARY}
82
+
83
+ Release risk: ${RELEASE_SCORE}. Appetite threshold: ${APPETITE}.
84
+
85
+ If projected release risk would exceed appetite, the plan MUST include a release strategy (release queue first, split into smaller batches, or risk-reducing steps). See RISK-POLICY.md for option details. ExitPlanMode runs the risk-scorer and FAILS plans above appetite without a strategy."
80
86
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/risk-scorer",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "Pipeline risk scoring, commit/push gates, and secret leak detection",
5
5
  "scripts": {
6
6
  "prepack": "node scripts/sync-codex-skills.mjs --pack",