specweave 0.33.2 → 0.33.4
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/CLAUDE.md +133 -19
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
- package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
- package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
- package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
- package/dist/src/cli/cleanup-zombies.js +8 -5
- package/dist/src/cli/cleanup-zombies.js.map +1 -1
- package/dist/src/config/types.d.ts +203 -1208
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/core/config/config-manager.d.ts.map +1 -1
- package/dist/src/core/config/config-manager.js +58 -0
- package/dist/src/core/config/config-manager.js.map +1 -1
- package/dist/src/core/config/types.d.ts +80 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/living-docs/cross-project-sync.d.ts +87 -15
- package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/cross-project-sync.js +147 -28
- package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +26 -22
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/living-docs/types.d.ts +24 -3
- package/dist/src/core/living-docs/types.d.ts.map +1 -1
- package/dist/src/core/types/config.d.ts +79 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/importers/jira-importer.d.ts +10 -0
- package/dist/src/importers/jira-importer.d.ts.map +1 -1
- package/dist/src/importers/jira-importer.js +55 -5
- package/dist/src/importers/jira-importer.js.map +1 -1
- package/dist/src/init/architecture/types.d.ts +33 -140
- package/dist/src/init/architecture/types.d.ts.map +1 -1
- package/dist/src/init/compliance/types.d.ts +30 -27
- package/dist/src/init/compliance/types.d.ts.map +1 -1
- package/dist/src/init/repo/types.d.ts +11 -34
- package/dist/src/init/repo/types.d.ts.map +1 -1
- package/dist/src/init/research/src/config/types.d.ts +15 -82
- package/dist/src/init/research/src/config/types.d.ts.map +1 -1
- package/dist/src/init/research/types.d.ts +38 -93
- package/dist/src/init/research/types.d.ts.map +1 -1
- package/dist/src/init/team/types.d.ts +4 -42
- package/dist/src/init/team/types.d.ts.map +1 -1
- package/dist/src/sync/closure-metrics.d.ts +102 -0
- package/dist/src/sync/closure-metrics.d.ts.map +1 -0
- package/dist/src/sync/closure-metrics.js +267 -0
- package/dist/src/sync/closure-metrics.js.map +1 -0
- package/dist/src/sync/sync-coordinator.d.ts +49 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +399 -37
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/notification-constants.d.ts +85 -0
- package/dist/src/utils/notification-constants.d.ts.map +1 -0
- package/dist/src/utils/notification-constants.js +129 -0
- package/dist/src/utils/notification-constants.js.map +1 -0
- package/dist/src/utils/platform-utils.d.ts +13 -3
- package/dist/src/utils/platform-utils.d.ts.map +1 -1
- package/dist/src/utils/platform-utils.js +17 -6
- package/dist/src/utils/platform-utils.js.map +1 -1
- package/dist/src/utils/project-resolver.d.ts +156 -0
- package/dist/src/utils/project-resolver.d.ts.map +1 -0
- package/dist/src/utils/project-resolver.js +587 -0
- package/dist/src/utils/project-resolver.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/commands/specweave-increment.md +46 -0
- package/plugins/specweave/commands/specweave-jobs.md +153 -8
- package/plugins/specweave/hooks/hooks.json +10 -0
- package/plugins/specweave/hooks/spec-project-validator.sh +24 -2
- package/plugins/specweave/hooks/universal/hook-wrapper.cmd +26 -26
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -16
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -16
- package/plugins/specweave/hooks/user-prompt-submit.sh +105 -3
- package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
- package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -0
- package/plugins/specweave/scripts/session-watchdog.sh +278 -130
- package/plugins/specweave/skills/increment-planner/SKILL.md +48 -18
- package/plugins/specweave/skills/increment-planner/templates/spec-multi-project.md +27 -14
- package/plugins/specweave/skills/increment-planner/templates/spec-single-project.md +16 -5
- package/plugins/specweave/skills/spec-generator/SKILL.md +74 -15
- package/plugins/specweave-ado/lib/per-us-sync.js +247 -0
- package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
- package/plugins/specweave-github/lib/github-client-v2.js +10 -3
- package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
- package/plugins/specweave-github/lib/per-us-sync.js +241 -0
- package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
- package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
- package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -738
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -1107
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# per-us-project-validator.sh
|
|
4
|
+
#
|
|
5
|
+
# Pre-tool-use hook that validates EACH User Story in spec.md has
|
|
6
|
+
# **Project**: (and **Board**: for 2-level) fields.
|
|
7
|
+
#
|
|
8
|
+
# This is the CRITICAL ENFORCEMENT for increment 0137.
|
|
9
|
+
#
|
|
10
|
+
# Activation:
|
|
11
|
+
# - tool_name: Write (also Edit if full content provided)
|
|
12
|
+
# - file_path matches: .specweave/increments/*/spec.md
|
|
13
|
+
#
|
|
14
|
+
# Rules:
|
|
15
|
+
# - Each ### US-XXX section MUST have **Project**: <value> on next few lines
|
|
16
|
+
# - For 2-level structures, each US MUST also have **Board**: <value>
|
|
17
|
+
# - Project values MUST match configured projects (not generic keywords)
|
|
18
|
+
# - Fallback allowed for existing specs via SPECWEAVE_LEGACY_SPEC=1
|
|
19
|
+
#
|
|
20
|
+
# Returns exit code 1 (block) if validation fails, 0 (allow) otherwise.
|
|
21
|
+
#
|
|
22
|
+
# Bypass:
|
|
23
|
+
# - SPECWEAVE_FORCE_PROJECT=1 - Skip all project validation
|
|
24
|
+
# - SPECWEAVE_LEGACY_SPEC=1 - Allow specs without per-US project (legacy mode)
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
set -e
|
|
28
|
+
|
|
29
|
+
# Source common utilities if available
|
|
30
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
PLUGIN_ROOT="${SCRIPT_DIR}/../.."
|
|
32
|
+
|
|
33
|
+
# Logging (silent by default)
|
|
34
|
+
log_debug() {
|
|
35
|
+
if [ "$SPECWEAVE_DEBUG" = "1" ]; then
|
|
36
|
+
echo "[per-us-project] $*" >&2
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Check for force bypass
|
|
41
|
+
if [ "$SPECWEAVE_FORCE_PROJECT" = "1" ]; then
|
|
42
|
+
echo '{"decision": "allow", "message": "⚠️ Per-US project validation bypassed (SPECWEAVE_FORCE_PROJECT=1)"}'
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Check for legacy mode bypass
|
|
47
|
+
if [ "$SPECWEAVE_LEGACY_SPEC" = "1" ]; then
|
|
48
|
+
echo '{"decision": "allow", "message": "⚠️ Per-US project validation skipped (SPECWEAVE_LEGACY_SPEC=1 - legacy mode)"}'
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Read tool input from stdin
|
|
53
|
+
INPUT=$(cat)
|
|
54
|
+
|
|
55
|
+
# Extract tool name
|
|
56
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
57
|
+
|
|
58
|
+
# Only validate Write tool calls (Edit doesn't provide full content)
|
|
59
|
+
if [ "$TOOL_NAME" != "Write" ]; then
|
|
60
|
+
echo '{"decision": "allow"}'
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Extract file path
|
|
65
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
66
|
+
|
|
67
|
+
# Only validate spec.md files in increments folder
|
|
68
|
+
if [[ ! "$FILE_PATH" =~ \.specweave/increments/[0-9]{3,4}E?-[^/]+/spec\.md$ ]]; then
|
|
69
|
+
echo '{"decision": "allow"}'
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
log_debug "Validating per-US project fields for: $FILE_PATH"
|
|
74
|
+
|
|
75
|
+
# Extract file content
|
|
76
|
+
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
|
|
77
|
+
|
|
78
|
+
# Count User Stories (### US-XXX pattern)
|
|
79
|
+
US_PATTERN='### US-[0-9]+'
|
|
80
|
+
TOTAL_US=$(echo "$CONTENT" | grep -cE "$US_PATTERN" || echo 0)
|
|
81
|
+
|
|
82
|
+
log_debug "Total User Stories found: $TOTAL_US"
|
|
83
|
+
|
|
84
|
+
# If no user stories, allow (might be tasks-only spec or overview)
|
|
85
|
+
if [ "$TOTAL_US" -eq 0 ]; then
|
|
86
|
+
echo '{"decision": "allow"}'
|
|
87
|
+
exit 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Extract User Story sections and check for **Project**: field
|
|
91
|
+
# Strategy: For each ### US-XXX, look at next 10 lines for **Project**:
|
|
92
|
+
|
|
93
|
+
MISSING_PROJECT=()
|
|
94
|
+
MISSING_BOARD=()
|
|
95
|
+
MULTI_PROJECT=() # USs with multiple projects (comma-separated)
|
|
96
|
+
MULTI_BOARD=() # USs with multiple boards (comma-separated)
|
|
97
|
+
US_WITH_PROJECT=0
|
|
98
|
+
|
|
99
|
+
# Use awk to extract US sections and check for Project field
|
|
100
|
+
while IFS= read -r us_line; do
|
|
101
|
+
# Extract US ID (e.g., US-001)
|
|
102
|
+
US_ID=$(echo "$us_line" | grep -oE 'US-[0-9]+' | head -1)
|
|
103
|
+
|
|
104
|
+
if [ -z "$US_ID" ]; then
|
|
105
|
+
continue
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Get line number of this US heading
|
|
109
|
+
LINE_NUM=$(echo "$CONTENT" | grep -nE "^### $US_ID:" | head -1 | cut -d: -f1)
|
|
110
|
+
|
|
111
|
+
if [ -z "$LINE_NUM" ]; then
|
|
112
|
+
continue
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Extract next 10 lines after heading
|
|
116
|
+
SECTION=$(echo "$CONTENT" | tail -n +$((LINE_NUM + 1)) | head -n 10)
|
|
117
|
+
|
|
118
|
+
# Check for **Project**: field
|
|
119
|
+
PROJECT_LINE=$(echo "$SECTION" | grep -E '^\*\*Project\*\*:\s*\S' | head -1)
|
|
120
|
+
|
|
121
|
+
if [ -n "$PROJECT_LINE" ]; then
|
|
122
|
+
US_WITH_PROJECT=$((US_WITH_PROJECT + 1))
|
|
123
|
+
|
|
124
|
+
# Extract project value
|
|
125
|
+
PROJECT_VALUE=$(echo "$PROJECT_LINE" | sed 's/^\*\*Project\*\*:\s*//' | tr -d '[:space:]')
|
|
126
|
+
|
|
127
|
+
# Check for comma (multiple projects - FORBIDDEN)
|
|
128
|
+
if echo "$PROJECT_VALUE" | grep -q ','; then
|
|
129
|
+
MULTI_PROJECT+=("$US_ID (has: $PROJECT_VALUE)")
|
|
130
|
+
log_debug "$US_ID has MULTIPLE projects (FORBIDDEN): $PROJECT_VALUE"
|
|
131
|
+
else
|
|
132
|
+
log_debug "$US_ID has **Project**: field ✓ ($PROJECT_VALUE)"
|
|
133
|
+
fi
|
|
134
|
+
else
|
|
135
|
+
MISSING_PROJECT+=("$US_ID")
|
|
136
|
+
log_debug "$US_ID MISSING **Project**: field ✗"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
done < <(echo "$CONTENT" | grep -E "^### US-[0-9]+:")
|
|
140
|
+
|
|
141
|
+
log_debug "User Stories with **Project**: $US_WITH_PROJECT / $TOTAL_US"
|
|
142
|
+
|
|
143
|
+
# Get project root for structure detection
|
|
144
|
+
PROJECT_ROOT="${FILE_PATH%%/.specweave/*}"
|
|
145
|
+
cd "$PROJECT_ROOT" 2>/dev/null || true
|
|
146
|
+
|
|
147
|
+
# Get structure level from config
|
|
148
|
+
STRUCTURE_LEVEL=1
|
|
149
|
+
if command -v specweave >/dev/null 2>&1; then
|
|
150
|
+
CONTEXT_OUTPUT=$(specweave context projects 2>/dev/null || echo '{"level": 1}')
|
|
151
|
+
STRUCTURE_LEVEL=$(echo "$CONTEXT_OUTPUT" | jq -r '.level // 1')
|
|
152
|
+
AVAILABLE_PROJECTS=$(echo "$CONTEXT_OUTPUT" | jq -r '.projects[].id // empty' | tr '\n' ', ' | sed 's/,$//')
|
|
153
|
+
else
|
|
154
|
+
# Fallback: check for 2-level indicators in config
|
|
155
|
+
if [ -f "$PROJECT_ROOT/.specweave/config.json" ]; then
|
|
156
|
+
HAS_AREA_PATH=$(jq -r '.sync.profiles | to_entries | map(select(.value.config.areaPathMapping or .value.config.areaPaths)) | length' "$PROJECT_ROOT/.specweave/config.json" 2>/dev/null || echo 0)
|
|
157
|
+
HAS_BOARD_MAPPING=$(jq -r '.sync.profiles | to_entries | map(select(.value.config.boardMapping.boards | length > 0)) | length' "$PROJECT_ROOT/.specweave/config.json" 2>/dev/null || echo 0)
|
|
158
|
+
|
|
159
|
+
if [ "$HAS_AREA_PATH" -gt 0 ] || [ "$HAS_BOARD_MAPPING" -gt 0 ]; then
|
|
160
|
+
STRUCTURE_LEVEL=2
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
log_debug "Structure level: $STRUCTURE_LEVEL"
|
|
166
|
+
|
|
167
|
+
# For 2-level structures, also check for **Board**: field
|
|
168
|
+
if [ "$STRUCTURE_LEVEL" = "2" ]; then
|
|
169
|
+
while IFS= read -r us_line; do
|
|
170
|
+
US_ID=$(echo "$us_line" | grep -oE 'US-[0-9]+' | head -1)
|
|
171
|
+
|
|
172
|
+
if [ -z "$US_ID" ]; then
|
|
173
|
+
continue
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
LINE_NUM=$(echo "$CONTENT" | grep -nE "^### $US_ID:" | head -1 | cut -d: -f1)
|
|
177
|
+
|
|
178
|
+
if [ -z "$LINE_NUM" ]; then
|
|
179
|
+
continue
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
SECTION=$(echo "$CONTENT" | tail -n +$((LINE_NUM + 1)) | head -n 10)
|
|
183
|
+
|
|
184
|
+
# Check for **Board**: field
|
|
185
|
+
BOARD_LINE=$(echo "$SECTION" | grep -E '^\*\*Board\*\*:\s*\S' | head -1)
|
|
186
|
+
|
|
187
|
+
if [ -n "$BOARD_LINE" ]; then
|
|
188
|
+
# Extract board value
|
|
189
|
+
BOARD_VALUE=$(echo "$BOARD_LINE" | sed 's/^\*\*Board\*\*:\s*//' | tr -d '[:space:]')
|
|
190
|
+
|
|
191
|
+
# Check for comma (multiple boards - FORBIDDEN)
|
|
192
|
+
if echo "$BOARD_VALUE" | grep -q ','; then
|
|
193
|
+
MULTI_BOARD+=("$US_ID (has: $BOARD_VALUE)")
|
|
194
|
+
log_debug "$US_ID has MULTIPLE boards (FORBIDDEN): $BOARD_VALUE"
|
|
195
|
+
else
|
|
196
|
+
log_debug "$US_ID has **Board**: field ✓ ($BOARD_VALUE)"
|
|
197
|
+
fi
|
|
198
|
+
else
|
|
199
|
+
MISSING_BOARD+=("$US_ID")
|
|
200
|
+
log_debug "$US_ID MISSING **Board**: field ✗"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
done < <(echo "$CONTENT" | grep -E "^### US-[0-9]+:")
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# Build error message if validation fails
|
|
207
|
+
ERRORS=""
|
|
208
|
+
|
|
209
|
+
if [ ${#MISSING_PROJECT[@]} -gt 0 ]; then
|
|
210
|
+
MISSING_LIST=$(printf ", %s" "${MISSING_PROJECT[@]}")
|
|
211
|
+
MISSING_LIST=${MISSING_LIST:2} # Remove leading ", "
|
|
212
|
+
|
|
213
|
+
ERRORS="$ERRORS\n❌ User Stories MISSING **Project**: field:\n ${MISSING_LIST}\n"
|
|
214
|
+
ERRORS="$ERRORS\n Each User Story MUST have **Project**: <project_id> after the heading.\n"
|
|
215
|
+
ERRORS="$ERRORS\n Example:\n"
|
|
216
|
+
ERRORS="$ERRORS ### US-001: Login Form\n"
|
|
217
|
+
ERRORS="$ERRORS **Project**: frontend-app\n"
|
|
218
|
+
|
|
219
|
+
if [ -n "$AVAILABLE_PROJECTS" ]; then
|
|
220
|
+
ERRORS="$ERRORS\n Available projects: ${AVAILABLE_PROJECTS}\n"
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
if [ ${#MISSING_BOARD[@]} -gt 0 ]; then
|
|
225
|
+
MISSING_LIST=$(printf ", %s" "${MISSING_BOARD[@]}")
|
|
226
|
+
MISSING_LIST=${MISSING_LIST:2}
|
|
227
|
+
|
|
228
|
+
ERRORS="$ERRORS\n❌ User Stories MISSING **Board**: field (2-level structure):\n ${MISSING_LIST}\n"
|
|
229
|
+
ERRORS="$ERRORS\n For 2-level structures, each US MUST have **Board**: <board_id>\n"
|
|
230
|
+
ERRORS="$ERRORS\n Example:\n"
|
|
231
|
+
ERRORS="$ERRORS ### US-001: Login Form\n"
|
|
232
|
+
ERRORS="$ERRORS **Project**: acme-corp\n"
|
|
233
|
+
ERRORS="$ERRORS **Board**: mobile-team\n"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Check for multiple projects (1:1 violation)
|
|
237
|
+
if [ ${#MULTI_PROJECT[@]} -gt 0 ]; then
|
|
238
|
+
MULTI_LIST=$(printf ", %s" "${MULTI_PROJECT[@]}")
|
|
239
|
+
MULTI_LIST=${MULTI_LIST:2}
|
|
240
|
+
|
|
241
|
+
ERRORS="$ERRORS\n❌ User Stories with MULTIPLE projects (FORBIDDEN - 1:1 mapping required):\n ${MULTI_LIST}\n"
|
|
242
|
+
ERRORS="$ERRORS\n Each User Story MUST map to EXACTLY ONE project.\n"
|
|
243
|
+
ERRORS="$ERRORS Split cross-project features into separate USs:\n"
|
|
244
|
+
ERRORS="$ERRORS\n WRONG:\n"
|
|
245
|
+
ERRORS="$ERRORS ### US-001: OAuth Implementation\n"
|
|
246
|
+
ERRORS="$ERRORS **Project**: frontend-app, backend-api ← FORBIDDEN\n"
|
|
247
|
+
ERRORS="$ERRORS\n CORRECT:\n"
|
|
248
|
+
ERRORS="$ERRORS ### US-001: OAuth Login Form\n"
|
|
249
|
+
ERRORS="$ERRORS **Project**: frontend-app\n"
|
|
250
|
+
ERRORS="$ERRORS\n ### US-002: OAuth API Endpoints\n"
|
|
251
|
+
ERRORS="$ERRORS **Project**: backend-api\n"
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# Check for multiple boards (1:1 violation)
|
|
255
|
+
if [ ${#MULTI_BOARD[@]} -gt 0 ]; then
|
|
256
|
+
MULTI_LIST=$(printf ", %s" "${MULTI_BOARD[@]}")
|
|
257
|
+
MULTI_LIST=${MULTI_LIST:2}
|
|
258
|
+
|
|
259
|
+
ERRORS="$ERRORS\n❌ User Stories with MULTIPLE boards (FORBIDDEN - 1:1 mapping required):\n ${MULTI_LIST}\n"
|
|
260
|
+
ERRORS="$ERRORS\n Each User Story MUST map to EXACTLY ONE board.\n"
|
|
261
|
+
ERRORS="$ERRORS Split cross-board features into separate USs.\n"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# If errors found, block the write
|
|
265
|
+
if [ -n "$ERRORS" ]; then
|
|
266
|
+
# Add bypass instructions
|
|
267
|
+
ERRORS="$ERRORS\n💡 To bypass this validation:\n"
|
|
268
|
+
ERRORS="$ERRORS - SPECWEAVE_FORCE_PROJECT=1 (skip all validation)\n"
|
|
269
|
+
ERRORS="$ERRORS - SPECWEAVE_LEGACY_SPEC=1 (allow specs without per-US project)\n"
|
|
270
|
+
|
|
271
|
+
# Escape for JSON
|
|
272
|
+
REASON=$(echo -e "$ERRORS" | jq -Rs .)
|
|
273
|
+
|
|
274
|
+
echo "{\"decision\": \"block\", \"reason\": $REASON}"
|
|
275
|
+
exit 0
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
# All validations passed
|
|
279
|
+
log_debug "All $TOTAL_US User Stories have required fields ✓"
|
|
280
|
+
echo '{"decision": "allow"}'
|
|
281
|
+
exit 0
|
|
@@ -120,6 +120,35 @@ case "$EVENT" in
|
|
|
120
120
|
run_with_timeout 30 node "$SYNC_SCRIPT" "$INC_ID" >/dev/null 2>&1 &
|
|
121
121
|
fi
|
|
122
122
|
fi
|
|
123
|
+
|
|
124
|
+
# ========================================================================
|
|
125
|
+
# CRITICAL FIX (v0.34.0): Close GitHub/JIRA/ADO issues on increment.done
|
|
126
|
+
# ========================================================================
|
|
127
|
+
# Root cause: sync-increment-closure.js was NEVER being called when
|
|
128
|
+
# increments completed. The EDA architecture routes increment.done to
|
|
129
|
+
# this handler, but only sync-living-docs.js was called (updates docs).
|
|
130
|
+
#
|
|
131
|
+
# sync-increment-closure.js contains the actual logic to:
|
|
132
|
+
# - Close GitHub issues linked to User Stories
|
|
133
|
+
# - Close JIRA/ADO issues (with proper gates)
|
|
134
|
+
# - Post completion comments on external issues
|
|
135
|
+
#
|
|
136
|
+
# Without this, GitHub issues stayed OPEN forever after increment closure!
|
|
137
|
+
# See: GitHub Issues #817-#822 (increment 0136) stayed open until manual fix
|
|
138
|
+
CLOSURE_SCRIPT=""
|
|
139
|
+
for path in \
|
|
140
|
+
"$PROJECT_ROOT/plugins/specweave/lib/hooks/sync-increment-closure.js" \
|
|
141
|
+
"$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/sync-increment-closure.js" \
|
|
142
|
+
"${CLAUDE_PLUGIN_ROOT:-}/lib/hooks/sync-increment-closure.js"; do
|
|
143
|
+
[[ -f "$path" ]] && { CLOSURE_SCRIPT="$path"; break; }
|
|
144
|
+
done
|
|
145
|
+
|
|
146
|
+
if [[ -n "$CLOSURE_SCRIPT" ]]; then
|
|
147
|
+
cd "$PROJECT_ROOT" || exit 0
|
|
148
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Calling sync-increment-closure.js for $INC_ID" >> "$LOG_FILE" 2>/dev/null
|
|
149
|
+
# Run closure sync in background (can take 10-30s for multiple issues)
|
|
150
|
+
run_with_timeout 60 node "$CLOSURE_SCRIPT" "$INC_ID" >> "$LOG_FILE" 2>&1 &
|
|
151
|
+
fi
|
|
123
152
|
;;
|
|
124
153
|
|
|
125
154
|
increment.archived)
|