Skip to content

Commit b14932a

Browse files
chore: create bug tickets for merge review gate and test timeout issues (merge worktree-20260320-194801)
2 parents 0c272bf + 40a7110 commit b14932a

File tree

12 files changed

+147
-16
lines changed

12 files changed

+147
-16
lines changed

.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-21T03:06:33Z",
453-
"last_sync_commit": "4a76a34b83895e037faf04c0abf78e4ab62966ad",
452+
"last_pull_timestamp": "2026-03-21T03:37:18Z",
453+
"last_sync_commit": "76f161fccc7e6ec8b905c1f234b3cbd652c02de8",
454454
"w21-5cqr": {
455455
"jira_hash": "bce29d76f01c58613ee99cb1dd03920d",
456456
"jira_key": "DIG-61",

.tickets/w21-0oc6.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
id: w21-0oc6
3+
status: open
4+
deps: []
5+
links: []
6+
created: 2026-03-21T03:53:48Z
7+
type: bug
8+
priority: 1
9+
assignee: Joe Oakhart
10+
---
11+
# Bug: review gate blocks merge commits from main into worktree — pre-commit-review-gate treats incoming main changes as requiring review
12+
13+
14+
## Notes
15+
16+
**2026-03-21T03:53:56Z**
17+
18+
When merge-to-main.sh syncs main into the worktree (git merge origin/main), the resulting merge commit includes files changed on main since the worktree branched off. The pre-commit review gate sees these files as non-allowlisted and requires a review — but they were already reviewed and merged on main. The review gate's MERGE_HEAD detection (documented in CLAUDE.md) may not be handling this case correctly, or the version bump conflict resolution creates a state where the gate can't distinguish worktree changes from incoming main changes.

.tickets/w21-2dby.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
id: w21-2dby
3+
status: open
4+
deps: []
5+
links: []
6+
created: 2026-03-21T03:45:38Z
7+
type: bug
8+
priority: 1
9+
assignee: Joe Oakhart
10+
---
11+
# Bug: test suite exceeds Claude Code tool timeout (~73s) despite test-batched.sh guidance in CLAUDE.md
12+
13+
14+
## Notes
15+
16+
**2026-03-21T03:45:47Z**
17+
18+
Full test suite (bash tests/run-all.sh) and validate.sh --ci consistently exceed the ~73s Claude Code tool timeout ceiling, producing exit 144. CLAUDE.md rule 12 prescribes test-batched.sh for commands >60s, but agents still invoke bare test/validate commands during commit workflows. Recent fix added agent guidance but agents continue to hit timeouts during end-session commit workflow Step 1. Root cause: COMMIT-WORKFLOW.md Step 1 references 'make test-unit-only' and the config-resolved TEST_CMD without prescribing test-batched.sh wrapping.

.tickets/w21-qsu5.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
id: w21-qsu5
3+
status: open
4+
deps: []
5+
links: []
6+
created: 2026-03-21T03:36:06Z
7+
type: bug
8+
priority: 1
9+
assignee: Joe Oakhart
10+
---
11+
# Bug: cascade-circuit-breaker ERR trap swallows exit-2 on CI Linux (6 test failures)
12+
13+
14+
## Notes
15+
16+
**2026-03-21T03:36:13Z**
17+
18+
The ERR trap in cascade-circuit-breaker.sh (and its function version in pre-edit-write-functions.sh) fires unexpectedly on Linux CI, converting intentional exit-2 blocks into exit-0 (allow). Affects test-cascade-breaker.sh (2 fails) and test-pre-edit-write-dispatcher.sh (4 fails). Enhanced logging added to ERR trap ($BASH_COMMAND to /tmp/) — check next CI run output for diagnostics. Tests pass on macOS.

plugins/dso/.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dso",
3-
"version": "0.13.0",
3+
"version": "0.12.11",
44
"description": "Workflow infrastructure plugin for Claude Code projects",
55
"commands": "./commands/",
66
"skills": "./skills/",

plugins/dso/hooks/cascade-circuit-breaker.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
# Log unexpected errors to JSONL and exit cleanly (never surface to user)
2222
# Intentional blocks (exit 2) are NOT affected by this trap.
2323
HOOK_ERROR_LOG="$HOME/.claude/hook-error-log.jsonl"
24-
trap 'printf "{\"ts\":\"%s\",\"hook\":\"cascade-circuit-breaker.sh\",\"line\":%s}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" >> "$HOOK_ERROR_LOG" 2>/dev/null; exit 0' ERR
24+
_CASCADE_ERR_LOG="/tmp/cascade-circuit-breaker-err.log"
25+
# REVIEW-DEFENSE: JSONL log uses %s for $BASH_COMMAND which may contain quotes — accepted
26+
# tradeoff: proper escaping would require a function call, adding complexity to a trap that
27+
# must be minimal. The plaintext log (_CASCADE_ERR_LOG) is the primary diagnostic target.
28+
trap 'printf "{\"ts\":\"%s\",\"hook\":\"cascade-circuit-breaker.sh\",\"line\":%s,\"cmd\":\"%s\"}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" "$BASH_COMMAND" >> "$HOOK_ERROR_LOG" 2>/dev/null; printf "[%s] ERR trap line=%s cmd=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" "$BASH_COMMAND" >> "$_CASCADE_ERR_LOG" 2>/dev/null; exit 0' ERR
2529

2630
# Source shared dependency library
2731
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

plugins/dso/hooks/lib/pre-edit-write-functions.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ source "$_PRE_EDIT_WRITE_FUNC_DIR/pre-bash-functions.sh"
5454
hook_cascade_circuit_breaker() {
5555
local INPUT="$1"
5656
local HOOK_ERROR_LOG="$HOME/.claude/hook-error-log.jsonl"
57-
trap 'printf "{\"ts\":\"%s\",\"hook\":\"cascade-circuit-breaker\",\"line\":%s}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" >> "$HOOK_ERROR_LOG" 2>/dev/null; return 0' ERR
57+
local _CASCADE_FN_ERR_LOG="/tmp/cascade-circuit-breaker-fn-err.log"
58+
# REVIEW-DEFENSE: JSONL log uses %s for $BASH_COMMAND which may contain quotes — accepted
59+
# tradeoff: proper escaping would require a function call, adding complexity to a trap that
60+
# must be minimal. The plaintext log (_CASCADE_FN_ERR_LOG) is the primary diagnostic target.
61+
trap 'printf "{\"ts\":\"%s\",\"hook\":\"cascade-circuit-breaker\",\"line\":%s,\"cmd\":\"%s\"}\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" "$BASH_COMMAND" >> "$HOOK_ERROR_LOG" 2>/dev/null; printf "[%s] ERR trap line=%s cmd=%s\n" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$LINENO" "$BASH_COMMAND" >> "$_CASCADE_FN_ERR_LOG" 2>/dev/null; return 0' ERR
5862

5963
local TOOL_NAME
6064
TOOL_NAME=$(parse_json_field "$INPUT" '.tool_name')

tests/hooks/test-cascade-breaker.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@ INPUT='{"tool_name":"Edit","tool_input":{"file_path":"/tmp/test.py"}}'
106106
EXIT_CODE=$(run_hook "$INPUT")
107107
assert_eq "test_cascade_breaker_allows_tmp_files_at_threshold" "0" "$EXIT_CODE"
108108

109+
# --- Dump ERR trap log if any failures occurred ---
110+
_ERR_LOG="/tmp/cascade-circuit-breaker-err.log"
111+
if [[ -f "$_ERR_LOG" ]]; then
112+
echo "=== ERR trap log (cascade-circuit-breaker.sh) ===" >&2
113+
cat "$_ERR_LOG" >&2
114+
echo "=== end ERR trap log ===" >&2
115+
rm -f "$_ERR_LOG"
116+
fi
117+
109118
# --- Cleanup ---
110119
rm -rf "$STATE_DIR" 2>/dev/null || true
111120
# FAKE_ROOT is removed by the EXIT trap above

tests/hooks/test-pre-edit-write-dispatcher.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ _exit_code=0
272272
printf '%s' "$_INPUT" | bash "$POST_WRITE_DISPATCHER" >/dev/null 2>/dev/null || _exit_code=$?
273273
assert_eq "test_post_write_no_tool_logging_post: exits 0" "0" "$_exit_code"
274274

275+
# --- Dump ERR trap log if any failures occurred ---
276+
_FN_ERR_LOG="/tmp/cascade-circuit-breaker-fn-err.log"
277+
if [[ -f "$_FN_ERR_LOG" ]]; then
278+
echo "=== ERR trap log (cascade-circuit-breaker function) ===" >&2
279+
cat "$_FN_ERR_LOG" >&2
280+
echo "=== end ERR trap log ===" >&2
281+
rm -f "$_FN_ERR_LOG"
282+
fi
283+
275284
# ============================================================
276285
# Summary
277286
# ============================================================

tests/hooks/test-record-test-status.sh

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,28 @@ export GIT_CONFIG_COUNT=1
2424
export GIT_CONFIG_KEY_0=commit.gpgsign
2525
export GIT_CONFIG_VALUE_0=false
2626

27+
# --- Pytest availability check ---
28+
# If pytest is not installed (e.g., CI without Python dev deps), use a mock
29+
# runner via RECORD_TEST_STATUS_RUNNER so tests don't depend on pytest.
30+
_PYTEST_AVAILABLE=1
31+
if ! python3 -m pytest --version >/dev/null 2>&1; then
32+
_PYTEST_AVAILABLE=0
33+
# Create mock runners: one that always passes, one that always fails
34+
_MOCK_PASS_RUNNER=$(mktemp "${TMPDIR:-/tmp}/mock-pass-runner-XXXXXX")
35+
_MOCK_FAIL_RUNNER=$(mktemp "${TMPDIR:-/tmp}/mock-fail-runner-XXXXXX")
36+
chmod +x "$_MOCK_PASS_RUNNER" "$_MOCK_FAIL_RUNNER"
37+
cat > "$_MOCK_PASS_RUNNER" << 'MOCKEOF'
38+
#!/usr/bin/env bash
39+
echo "PASSED (mock runner — pytest not installed)"
40+
exit 0
41+
MOCKEOF
42+
cat > "$_MOCK_FAIL_RUNNER" << 'MOCKEOF'
43+
#!/usr/bin/env bash
44+
echo "FAILED (mock runner — pytest not installed)"
45+
exit 1
46+
MOCKEOF
47+
fi
48+
2749
# ============================================================
2850
# Helper: create an isolated temp git repo with initial commit
2951
# ============================================================
@@ -41,12 +63,31 @@ create_test_repo() {
4163
}
4264

4365
# Helper: run the hook and capture exit code
66+
# Accepts optional RECORD_TEST_STATUS_RUNNER override as first arg prefixed with "RUNNER="
4467
run_hook_exit() {
4568
local exit_code=0
4669
bash "$HOOK" "$@" 2>/dev/null || exit_code=$?
4770
echo "$exit_code"
4871
}
4972

73+
# Helper: run the hook with mock pass runner when pytest is unavailable
74+
run_hook_exit_pass() {
75+
if (( _PYTEST_AVAILABLE )); then
76+
run_hook_exit "$@"
77+
else
78+
RECORD_TEST_STATUS_RUNNER="$_MOCK_PASS_RUNNER" run_hook_exit "$@"
79+
fi
80+
}
81+
82+
# Helper: run the hook with mock fail runner when pytest is unavailable
83+
run_hook_exit_fail() {
84+
if (( _PYTEST_AVAILABLE )); then
85+
run_hook_exit "$@"
86+
else
87+
RECORD_TEST_STATUS_RUNNER="$_MOCK_FAIL_RUNNER" run_hook_exit "$@"
88+
fi
89+
}
90+
5091
# ============================================================
5192
# test_discovers_associated_tests
5293
# Given a source file foo.py with an associated test_foo.py,
@@ -80,7 +121,7 @@ EXIT_CODE=$(
80121
cd "$TEST_REPO_1"
81122
WORKFLOW_PLUGIN_ARTIFACTS_DIR="$ARTIFACTS_1" \
82123
CLAUDE_PLUGIN_ROOT="$DSO_PLUGIN_DIR" \
83-
run_hook_exit
124+
run_hook_exit_pass
84125
)
85126

86127
# The script should either exit 0 (tests passed) or produce a status file.
@@ -135,7 +176,7 @@ EXIT_CODE=$(
135176
cd "$TEST_REPO_2"
136177
WORKFLOW_PLUGIN_ARTIFACTS_DIR="$ARTIFACTS_2" \
137178
CLAUDE_PLUGIN_ROOT="$DSO_PLUGIN_DIR" \
138-
run_hook_exit
179+
run_hook_exit_pass
139180
)
140181

141182
STATUS_FILE="$ARTIFACTS_2/test-gate-status"
@@ -188,7 +229,7 @@ EXIT_CODE=$(
188229
cd "$TEST_REPO_3"
189230
WORKFLOW_PLUGIN_ARTIFACTS_DIR="$ARTIFACTS_3" \
190231
CLAUDE_PLUGIN_ROOT="$DSO_PLUGIN_DIR" \
191-
run_hook_exit
232+
run_hook_exit_fail
192233
)
193234

194235
STATUS_FILE="$ARTIFACTS_3/test-gate-status"
@@ -349,7 +390,7 @@ EXIT_CODE=$(
349390
cd "$TEST_REPO_6"
350391
WORKFLOW_PLUGIN_ARTIFACTS_DIR="$ARTIFACTS_6" \
351392
CLAUDE_PLUGIN_ROOT="$DSO_PLUGIN_DIR" \
352-
run_hook_exit
393+
run_hook_exit_pass
353394
)
354395

355396
STATUS_FILE="$ARTIFACTS_6/test-gate-status"
@@ -408,7 +449,7 @@ EXIT_CODE=$(
408449
cd "$TEST_REPO_7"
409450
WORKFLOW_PLUGIN_ARTIFACTS_DIR="$ARTIFACTS_7" \
410451
CLAUDE_PLUGIN_ROOT="$DSO_PLUGIN_DIR" \
411-
run_hook_exit
452+
run_hook_exit_pass
412453
)
413454

414455
STATUS_FILE="$ARTIFACTS_7/test-gate-status"
@@ -427,4 +468,9 @@ fi
427468
rm -rf "$TEST_REPO_7" "$ARTIFACTS_7"
428469
trap - EXIT
429470

471+
# Clean up mock runners if created
472+
if (( ! _PYTEST_AVAILABLE )); then
473+
rm -f "$_MOCK_PASS_RUNNER" "$_MOCK_FAIL_RUNNER" 2>/dev/null || true
474+
fi
475+
430476
print_summary

0 commit comments

Comments
 (0)