Skip to content

Commit 7e98b2e

Browse files
feat(w21-24kl): batch 20 — RED ticket gate tests + allowlist fix + CI fixes
Add RED tests for pre-commit-ticket-gate.sh (dso-qki2): 10 test functions covering ticket ID validation, allowlist skip, merge exemption, and graceful degradation. Fix critical allowlist bug: restore .tickets/** pattern alongside the v3 worktree pattern — .tickets/ is tracked on main while the v3 worktree is on an orphan branch whose files never appear in git diff on main. Fix CI: portable xargs replacement in cutover migration test, update validate-work readonly enforcement test for ticket CLI migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0ae161d commit 7e98b2e

File tree

12 files changed

+587
-29
lines changed

12 files changed

+587
-29
lines changed

.test-index

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,4 @@ plugins/dso/skills/validate-work/SKILL.md:plugins/dso/tests/test-sprint-skill-st
102102
plugins/dso/skills/validate-work/prompts/ci-status.md:tests/hooks/test-ci-status-no-jq.sh,tests/scripts/test-ci-status-auth-ratelimit.sh,tests/scripts/test-ci-status-timeout.sh,tests/scripts/test-ci-status.sh
103103
plugins/dso/skills/verification-before-completion/SKILL.md:plugins/dso/tests/test-sprint-skill-step10-no-merge-to-main.sh,tests/plugin/test-audit-skill-resolution.sh,tests/hooks/test-fix-bug-skill.sh,tests/hooks/test-generate-claude-md-skill.sh,tests/hooks/test-init-skill.sh,tests/scripts/test-qualify-skill-refs.sh,tests/scripts/test-skill-path-refs.sh,tests/scripts/test-check-skill-refs.sh,tests/skills/test_end_skill_final_verification_step.py,tests/skills/test_implementation_plan_skill_tdd_enforcement.py,tests/skills/test-quick-ref-skill.sh,tests/skills/test_project_setup_skill_conditional_prompts.py,tests/skills/test_fix_bug_skill.py,tests/skills/test_end_skill_summary_displays_stored_learnings.py,tests/skills/test_end_skill_learnings_step_before_commit.py,tests/skills/test-design-skills-cross-stack.sh,tests/skills/test_end_skill_dirty_worktree_resolution.py,tests/skills/test_fix_bug_skill_escalated_section.py,tests/skills/test_end_skill_bug_tickets_before_commit.py
104104
plugins/dso/scripts/cutover-tickets-migration.sh: tests/scripts/test-cutover-tickets-migration.sh [test_cutover_rollback_committed_uses_revert]
105+
plugins/dso/hooks/pre-commit-ticket-gate.sh: tests/hooks/test-pre-commit-ticket-gate.sh [test_blocks_missing_ticket_id]

.tickets/.index.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,13 @@
700700
"title": "Fix: project-detect.sh short-circuit python3 subprocess when entries is empty",
701701
"type": "bug"
702702
},
703+
"dso-qxsw": {
704+
"deps": [],
705+
"priority": 1,
706+
"status": "open",
707+
"title": "Fix remaining CI failures from v3 path migration",
708+
"type": "bug"
709+
},
703710
"dso-r97p": {
704711
"deps": [],
705712
"priority": 2,

.tickets/.sync-state.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,8 +449,8 @@
449449
"last_synced": "2026-03-19T18:38:35Z",
450450
"local_hash": "14c516947a151a3db8bdec4010e2fd6e"
451451
},
452-
"last_pull_timestamp": "2026-03-23T20:17:37Z",
453-
"last_sync_commit": "d71e356f714a70cdc98c0624fac743dd3073793e",
452+
"last_pull_timestamp": "2026-03-23T20:55:35Z",
453+
"last_sync_commit": "0ae161d9e098387e6596147a999ae6bcf2d32f9d",
454454
"w21-5cqr": {
455455
"jira_hash": "bce29d76f01c58613ee99cb1dd03920d",
456456
"jira_key": "DIG-61",

.tickets/dso-qki2.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
id: dso-qki2
3-
status: open
3+
status: in_progress
44
deps: []
55
links: []
66
created: 2026-03-23T20:26:38Z
@@ -53,3 +53,38 @@ Env var injection for tests:
5353
**2026-03-23T20:29:04Z**
5454

5555
Gap Analysis AC Amendment: Ensure the test script is runnable (bash-executable). Add to acceptance criteria: 'Test file runs without bash syntax errors: Verify: bash -n $(git rev-parse --show-toplevel)/tests/hooks/test-pre-commit-ticket-gate.sh'
56+
57+
## ACCEPTANCE CRITERIA
58+
59+
- [ ] tests/hooks/test-pre-commit-ticket-gate.sh exists and is executable
60+
Verify: test -x tests/hooks/test-pre-commit-ticket-gate.sh
61+
- [ ] Test file contains at least 8 test functions
62+
Verify: grep -c "^test_" tests/hooks/test-pre-commit-ticket-gate.sh | awk "{exit (\$1 < 8)}"
63+
- [ ] .test-index entry added with RED marker
64+
Verify: grep -q "test-pre-commit-ticket-gate" .test-index
65+
- [ ] All tests fail RED (hook does not exist yet)
66+
Verify: bash tests/hooks/test-pre-commit-ticket-gate.sh 2>&1 | grep -qi fail
67+
68+
**2026-03-23T20:35:56Z**
69+
70+
CHECKPOINT 1/6: Task context loaded ✓
71+
72+
**2026-03-23T20:36:00Z**
73+
74+
CHECKPOINT 2/6: Code patterns understood ✓
75+
76+
**2026-03-23T20:37:21Z**
77+
78+
CHECKPOINT 3/6: Tests written ✓
79+
80+
**2026-03-23T20:37:25Z**
81+
82+
CHECKPOINT 4/6: Implementation complete ✓
83+
84+
**2026-03-23T20:47:54Z**
85+
86+
CHECKPOINT 5/6: Validation passed ✓ — syntax OK; 10/10 tests fail RED (hook not yet implemented); .test-index entry added with RED marker [test_blocks_missing_ticket_id]
87+
88+
**2026-03-23T20:48:07Z**
89+
90+
CHECKPOINT 6/6: Done ✓ — all 6 ACs verified: file exists, executable, 20 test_ functions (>=8 required), .test-index entry present, 10/10 tests fail RED, no bash syntax errors

.tickets/dso-qxsw.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
id: dso-qxsw
3+
status: open
4+
deps: []
5+
links: []
6+
created: 2026-03-23T20:35:48Z
7+
type: bug
8+
priority: 1
9+
assignee: Joe Oakhart
10+
---
11+
# Fix remaining CI failures from v3 path migration
12+
13+
14+
## Notes
15+
16+
<!-- note-id: qb96bnr0 -->
17+
<!-- timestamp: 2026-03-23T21:01:48Z -->
18+
<!-- origin: agent -->
19+
<!-- sync: unsynced -->
20+
21+
Investigation complete. Root causes identified and fixed:
22+
23+
1. test_phase_migrate_preserves_notes_with_timestamps (BLOCKING in CI batch 18):
24+
- Root cause: xargs -S 65536 is macOS-specific, fails on Linux/CI
25+
- Fix: replaced with portable while-read loop in test-cutover-tickets-migration.sh
26+
27+
2. test_*_names_tk_close_or_tk_status_as_prohibited (BLOCKING in CI batch 18):
28+
- Root cause: test checked for old 'tk close|tk status' commands after v3 migration renamed them to 'ticket transition|ticket create'
29+
- Fix: updated test-validate-work-readonly-enforcement.sh to check for new v3 command names
30+
31+
3. test_cutover_rollback_committed_uses_revert + test_cutover_exits_with_error (non-blocking):
32+
- These are RED marker tests in .test-index, expected to fail
33+
- Also improved _rollback_phase implementation to handle empty revert ranges
34+
35+
4. Previously-failing tests from earlier batches (skip-review, doc-migration, impl-plan-contracts, merge-squash-rebase, behavioral-equivalence, review-gate, compute-diff-hash):
36+
- All pass on current HEAD (fixed by batches 16-18)
37+
38+
ShellCheck: no violations on modified files.
39+
Local test results: Script Tests 2679 PASSED / 2 FAILED (both RED markers)

plugins/dso/hooks/compute-diff-hash.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Includes changes in the git index (staged) and modifications to tracked files.
55
# Excludes untracked files — new files must be staged before review (per COMMIT-WORKFLOW.md).
66
# This prevents temp test fixtures from causing hash mismatches between review and pre-commit.
7-
# Excludes .tickets-tracker/ files from hash — ticket metadata changes must not invalidate code reviews.
7+
# Excludes .tickets/ and .tickets-tracker/ files from hash — ticket metadata changes must not invalidate code reviews.
88
#
99
# Usage:
1010
# HASH=$(.claude/hooks/compute-diff-hash.sh)
@@ -111,6 +111,7 @@ fi
111111
# Fallback defaults when allowlist or helpers are unavailable
112112
_FALLBACK_PATHSPECS=(
113113
':!.checkpoint-needs-review'
114+
':!.tickets/**'
114115
':!.tickets-tracker/**'
115116
':!.sync-state.json'
116117
':!*.png' ':!*.jpg' ':!*.jpeg' ':!*.gif' ':!*.svg' ':!*.ico' ':!*.webp'

plugins/dso/hooks/lib/review-gate-allowlist.conf

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
# dynamically at load time by the consumer scripts. They are NOT
1111
# listed here because they vary per project.
1212

13-
# Ticket metadata
13+
# Ticket metadata — both paths needed:
14+
# .tickets/** = old flat-file tickets tracked on main (until w21-gy45 cleanup)
15+
# .tickets-tracker/** = v3 event-sourced worktree (orphan branch, currently no-op on main)
16+
.tickets/**
1417
.tickets-tracker/**
1518

1619
# Sync metadata

plugins/dso/scripts/cutover-tickets-migration.sh

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -908,33 +908,45 @@ _rollback_phase() {
908908
local commit_before="$3"
909909
local log_file="$4"
910910

911-
# Determine rollback strategy: dirty working tree → checkout, clean → revert
911+
# Determine rollback strategy:
912+
# - Commits were made since run start (HEAD moved) → git revert
913+
# - Working-tree dirty (staged/unstaged changes) → git checkout HEAD -- .
914+
# - No changes at all → no-op
915+
local current_head
916+
current_head=$(git -C "$_REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown")
917+
912918
local rollback_strategy
913-
if git -C "$_REPO_ROOT" diff --quiet HEAD 2>/dev/null; then
919+
if [[ "$commit_before" != "unknown" && "$current_head" != "$commit_before" ]]; then
914920
rollback_strategy="revert"
915-
else
921+
elif ! git -C "$_REPO_ROOT" diff --quiet HEAD 2>/dev/null; then
916922
rollback_strategy="checkout"
923+
else
924+
rollback_strategy="noop"
917925
fi
918926

919927
echo "Rollback: phase '${phase}' failed (exit ${phase_rc}); strategy=${rollback_strategy}" >&2
920928
printf '[%s] Rollback: phase "%s" failed (exit %s); strategy=%s\n' \
921929
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$phase" "$phase_rc" "$rollback_strategy" >> "$log_file"
922930

923931
local rollback_exit=0
924-
if [[ "$rollback_strategy" == "checkout" ]]; then
925-
git -C "$_REPO_ROOT" checkout HEAD -- . 2>&1 || rollback_exit=$?
926-
else
927-
# revert: undo all commits made during this phase by reverting from
928-
# commit_before up to HEAD, so multi-commit phases are fully unwound.
932+
if [[ "$rollback_strategy" == "revert" ]]; then
933+
# revert: undo all commits made during this run by reverting from
934+
# commit_before up to HEAD, so multi-phase commits are fully unwound.
929935
git -C "$_REPO_ROOT" revert --no-edit "${commit_before}..HEAD" 2>&1 || rollback_exit=$?
936+
elif [[ "$rollback_strategy" == "checkout" ]]; then
937+
git -C "$_REPO_ROOT" checkout HEAD -- . 2>&1 || rollback_exit=$?
930938
fi
931939

932940
# Remove the state file so a subsequent re-run starts fresh
933941
rm -f "$CUTOVER_STATE_FILE"
934942

935-
# Remove any untracked files/dirs created during the run (log dir, temp
936-
# files, etc.) so the working tree is left fully clean.
937-
git -C "$_REPO_ROOT" clean -fd 2>&1 || true
943+
# Remove any untracked files/dirs created during the run, preserving the
944+
# log directory so the error message path remains valid after rollback.
945+
local _clean_excludes=()
946+
if [[ -n "${CUTOVER_LOG_DIR:-}" ]]; then
947+
_clean_excludes+=("-e" "$CUTOVER_LOG_DIR")
948+
fi
949+
git -C "$_REPO_ROOT" clean -fd "${_clean_excludes[@]}" 2>&1 || true
938950

939951
if [[ "$rollback_exit" -eq 0 ]]; then
940952
echo "Rollback complete." >&2
@@ -969,6 +981,8 @@ if [[ "$_RESUME" == "true" ]]; then
969981
fi
970982
fi
971983

984+
_run_commit_before=$(git -C "$_REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown")
985+
972986
for _phase in "${PHASES[@]}"; do
973987
# Resume: skip phases already recorded in the state file
974988
if [[ "$_RESUME" == "true" ]] && _phase_is_completed "$_phase"; then
@@ -979,12 +993,9 @@ for _phase in "${PHASES[@]}"; do
979993
if [[ "$_DRY_RUN" == "true" ]]; then
980994
"_run_phase_dry" "$_phase" || { _rc=$?; echo "[DRY RUN] ERROR: phase ${_phase} failed (exit ${_rc}) — see ${_LOG_FILE}" >&2; exit "$_rc"; }
981995
else
982-
_phase_commit_before=$(git -C "$_REPO_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown")
983996
"_phase_${_phase}" || {
984997
_rc=$?
985-
_rollback_phase "$_phase" "$_rc" "$_phase_commit_before" "$_LOG_FILE"
986-
echo "ERROR: phase ${_phase} failed (exit ${_rc}) — see ${_LOG_FILE}" >&2
987-
exit "$_rc"
998+
_rollback_phase "$_phase" "$_rc" "$_run_commit_before" "$_LOG_FILE"
988999
}
9891000
_state_append_phase "$_phase"
9901001
fi

plugins/dso/scripts/skip-review-check.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ if [[ -f "$_ALLOWLIST_FILE" ]]; then
5151
else
5252
# Graceful degradation: fall back to hardcoded patterns when allowlist is missing
5353
_ALLOWLIST_PATTERNS=(
54+
".tickets/*"
5455
".tickets-tracker/*"
5556
".sync-state.json"
5657
".checkpoint-needs-review"

0 commit comments

Comments
 (0)