@boyingliu01/xp-gate 0.30.1 → 0.30.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.
- package/adapters/iac.sh +45 -4
- package/adapters/java.sh +82 -16
- package/adapters/powershell.sh +70 -9
- package/bin/xp-gate.js +8 -0
- package/gate-10.sh +32 -2
- package/gate-3.sh +47 -6
- package/gate-4.sh +12 -0
- package/gate-7.sh +4 -0
- package/hooks/gate-10.sh +32 -2
- package/hooks/gate-3.sh +47 -6
- package/hooks/gate-4.sh +12 -0
- package/hooks/gate-7.sh +4 -0
- package/hooks/hook-module-resolution.sh +14 -0
- package/hooks/lib/coverage-total.sh +1 -1
- package/hooks/pre-commit +138 -17
- package/hooks/pre-push +97 -6
- package/hooks/sprint-gate.sh +81 -2
- package/lib/__tests__/crap-cli.test.js +76 -0
- package/lib/__tests__/crap.test.js +137 -0
- package/lib/__tests__/hook-health.test.js +85 -0
- package/lib/__tests__/retro-gate-m.test.js +144 -0
- package/lib/coverage-total.sh +92 -0
- package/lib/crap-cli.js +191 -0
- package/lib/crap.js +196 -0
- package/lib/doctor.js +44 -0
- package/lib/hook-health.js +121 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/retro.js +139 -2
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/dsh/package.json +1 -1
- package/plugins/dsh/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +2 -2
- package/sprint-gate.sh +81 -2
package/sprint-gate.sh
CHANGED
|
@@ -181,6 +181,65 @@ read_verdict() {
|
|
|
181
181
|
json_extract "$APPROVED_FILE" '.verdict // ""' || echo ""
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
# --- Helper: sprint age in days, or empty if unknown ---
|
|
185
|
+
# Reads status + started_at from sprint-state.json. #551: the gate judged only
|
|
186
|
+
# `phase` and the delphi verdict, so a state file left at status=in_progress
|
|
187
|
+
# reported "sprint phase 6 validated" on unrelated commits for as long as
|
|
188
|
+
# nobody fixed it. An advancing sprint and an abandoned one were
|
|
189
|
+
# indistinguishable -- worse than SKIP, because SKIP is honest and PASS lies.
|
|
190
|
+
read_sprint_status() {
|
|
191
|
+
if [ ! -f "$SPRINT_STATE_JSON" ] || [ "$JSON_PARSER" = "none" ]; then
|
|
192
|
+
echo ""
|
|
193
|
+
return
|
|
194
|
+
fi
|
|
195
|
+
json_validate "$SPRINT_STATE_JSON" || { echo ""; return; }
|
|
196
|
+
json_extract "$SPRINT_STATE_JSON" '.status // ""' 2>/dev/null || echo ""
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
read_sprint_age_days() {
|
|
200
|
+
if [ ! -f "$SPRINT_STATE_JSON" ] || [ "$JSON_PARSER" = "none" ]; then
|
|
201
|
+
echo ""
|
|
202
|
+
return
|
|
203
|
+
fi
|
|
204
|
+
json_validate "$SPRINT_STATE_JSON" || { echo ""; return; }
|
|
205
|
+
local started
|
|
206
|
+
started=$(json_extract "$SPRINT_STATE_JSON" '.started_at // ""' 2>/dev/null || echo "")
|
|
207
|
+
# Older state files may carry updated_at only; either is the last sign of life.
|
|
208
|
+
if [ -z "$started" ] || [ "$started" = "null" ]; then
|
|
209
|
+
started=$(json_extract "$SPRINT_STATE_JSON" '.updated_at // ""' 2>/dev/null || echo "")
|
|
210
|
+
fi
|
|
211
|
+
if [ -z "$started" ] || [ "$started" = "null" ]; then
|
|
212
|
+
echo ""
|
|
213
|
+
return
|
|
214
|
+
fi
|
|
215
|
+
# Portable ISO-8601 -> epoch. GNU date first, BSD date second, node last; if
|
|
216
|
+
# all fail the age is unknown and the caller must not invent one.
|
|
217
|
+
local epoch=""
|
|
218
|
+
epoch=$(date -u -d "$started" +%s 2>/dev/null || true)
|
|
219
|
+
if [ -z "$epoch" ]; then
|
|
220
|
+
epoch=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$started" +%s 2>/dev/null || true)
|
|
221
|
+
fi
|
|
222
|
+
if [ -z "$epoch" ] && command -v node >/dev/null 2>&1; then
|
|
223
|
+
epoch=$(node -e "const t=Date.parse(process.argv[1]);if(Number.isFinite(t))process.stdout.write(String(Math.floor(t/1000)))" "$started" 2>/dev/null || true)
|
|
224
|
+
fi
|
|
225
|
+
if [ -z "$epoch" ]; then
|
|
226
|
+
echo ""
|
|
227
|
+
return
|
|
228
|
+
fi
|
|
229
|
+
local now
|
|
230
|
+
now=$(date -u +%s)
|
|
231
|
+
local age=$(( (now - epoch) / 86400 ))
|
|
232
|
+
[ "$age" -lt 0 ] && age=0
|
|
233
|
+
echo "$age"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# Threshold in days beyond which an in_progress sprint is considered abandoned.
|
|
237
|
+
# Configurable because the right number is project-specific: a weekly cadence
|
|
238
|
+
# wants 14, a monthly one wants more. Default 14 = two full weeks without
|
|
239
|
+
# progress: enough that a sprint paused over a holiday is not called dead, tight
|
|
240
|
+
# enough that the reported 12-day case is caught within two days of it firing.
|
|
241
|
+
SPRINT_STALE_DAYS="${SPRINT_STALE_DAYS:-14}"
|
|
242
|
+
|
|
184
243
|
# --- Pre-commit mode ---
|
|
185
244
|
if [ "$MODE" = "pre-commit" ]; then
|
|
186
245
|
PHASE=$(read_phase)
|
|
@@ -222,8 +281,28 @@ if [ "$MODE" = "pre-commit" ]; then
|
|
|
222
281
|
exit 1
|
|
223
282
|
fi
|
|
224
283
|
|
|
225
|
-
#
|
|
226
|
-
|
|
284
|
+
# #551: staleness. A sprint left at status=in_progress past the threshold is
|
|
285
|
+
# abandoned, not validated -- reporting PASS for it means the gate cannot
|
|
286
|
+
# distinguish an advancing sprint from a forgotten one, and it injects
|
|
287
|
+
# ancient phase_history into every unrelated commit's audit trail. SKIP is
|
|
288
|
+
# the honest answer: the gate declines to certify a sprint nobody is
|
|
289
|
+
# driving, without blocking work that has nothing to do with it.
|
|
290
|
+
SPRINT_STATUS=$(read_sprint_status)
|
|
291
|
+
SPRINT_AGE_DAYS=$(read_sprint_age_days)
|
|
292
|
+
if [ "$SPRINT_STATUS" = "in_progress" ] && [ -n "$SPRINT_AGE_DAYS" ] \
|
|
293
|
+
&& [ "$SPRINT_AGE_DAYS" -ge "$SPRINT_STALE_DAYS" ] 2>/dev/null; then
|
|
294
|
+
echo "{\"decision\":\"skip\",\"reason\":\"STALE SPRINT: status=in_progress for ${SPRINT_AGE_DAYS} days (threshold ${SPRINT_STALE_DAYS}) — not certifying it. Finish it (sprint-flow close) or clear .sprint-state/sprint-state.json\"}"
|
|
295
|
+
exit 0
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
# All checks passed. Name the age so "validated" is not an unconditional
|
|
299
|
+
# claim: a 29-day-old sprint under a 30-day threshold is a very different
|
|
300
|
+
# situation from one opened today, and the log should say which it is.
|
|
301
|
+
if [ -n "$SPRINT_AGE_DAYS" ]; then
|
|
302
|
+
echo "{\"decision\":\"allow\",\"message\":\"delphi-review APPROVED, sprint phase ${PHASE} validated (sprint open ${SPRINT_AGE_DAYS} day(s), staleness threshold ${SPRINT_STALE_DAYS})\"}"
|
|
303
|
+
else
|
|
304
|
+
echo "{\"decision\":\"allow\",\"message\":\"delphi-review APPROVED, sprint phase ${PHASE} validated (sprint age unknown — started_at unreadable)\"}"
|
|
305
|
+
fi
|
|
227
306
|
exit 0
|
|
228
307
|
fi
|
|
229
308
|
|