Skip to content

Commit 5477192

Browse files
committed
test: Stop/SubagentStop hookのエラー時期待値をallow("")に変更
公式Claude Code hooks仕様に合わせてテスト期待値を更新: - エラー時のdecision: "block" → "" (allow stop)に変更 - コマンド失敗、JSON不正、不正decision値、reason未指定をallowに - テスト名から"fail-safe block"を削除してallowを明示 - TestExecuteStopAndSubagentStopHook_FailingCommandReturnsExit2を TestExecuteStopAndSubagentStopHook_FailingCommandAllowsStopに改名
1 parent 4f70efc commit 5477192

File tree

3 files changed

+53
-52
lines changed

3 files changed

+53
-52
lines changed

actions_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,12 @@ func TestExecuteStopAction_CommandWithStubRunner(t *testing.T) {
262262
wantDecision: "",
263263
},
264264
{
265-
name: "command failure blocks stop with decision: block",
265+
name: "command failure allows stop (decision empty)",
266266
command: "exit 1",
267267
stderr: "stop command failed",
268268
exitCode: 1,
269-
// Non-zero exit → fail-safe block
270-
wantDecision: "block",
269+
// Non-zero exit → allow stop (per official spec: hook errors are non-blocking)
270+
wantDecision: "",
271271
},
272272
}
273273

executor_test.go

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,27 +1245,27 @@ func TestExecuteStopAndSubagentStopAction_TypeOutput(t *testing.T) {
12451245
wantErr: false,
12461246
},
12471247
{
1248-
name: "Stop: Invalid decision value -> fail-safe (decision: block)",
1248+
name: "Stop: Invalid decision value -> allow (error in systemMessage)",
12491249
eventType: "Stop",
12501250
action: Action{
12511251
Type: "output",
12521252
Message: "Invalid decision test",
12531253
Decision: stringPtr("invalid"),
12541254
},
1255-
wantDecision: "block",
1256-
wantReason: "Invalid decision test",
1255+
wantDecision: "",
1256+
wantReason: "",
12571257
wantSystemMessage: "Invalid decision value in action config: must be 'block' or field must be omitted",
12581258
wantErr: false,
12591259
},
12601260
{
1261-
name: "Stop: Empty message -> fail-safe (decision: block, reason=fixed message)",
1261+
name: "Stop: Empty message -> allow (error in systemMessage)",
12621262
eventType: "Stop",
12631263
action: Action{
12641264
Type: "output",
12651265
Message: "",
12661266
},
1267-
wantDecision: "block",
1268-
wantReason: "Empty message in Stop action",
1267+
wantDecision: "",
1268+
wantReason: "",
12691269
wantSystemMessage: "Empty message in Stop action",
12701270
wantErr: false,
12711271
},
@@ -1360,26 +1360,26 @@ func TestExecuteStopAndSubagentStopAction_TypeOutput(t *testing.T) {
13601360
wantSystemMessage: "Allowing subagent stop",
13611361
},
13621362
{
1363-
name: "SubagentStop: invalid decision value - fail-safe block",
1363+
name: "SubagentStop: invalid decision value - allow (error in systemMessage)",
13641364
eventType: "SubagentStop",
13651365
action: Action{
13661366
Type: "output",
13671367
Message: "Invalid decision",
13681368
Decision: stringPtr("invalid"),
13691369
},
1370-
wantDecision: "block",
1371-
wantReason: "Invalid decision",
1370+
wantDecision: "",
1371+
wantReason: "",
13721372
wantSystemMessage: "Invalid decision value in action config: must be 'block' or field must be omitted",
13731373
},
13741374
{
1375-
name: "SubagentStop: empty message - fail-safe block",
1375+
name: "SubagentStop: empty message - allow (error in systemMessage)",
13761376
eventType: "SubagentStop",
13771377
action: Action{
13781378
Type: "output",
13791379
Message: "",
13801380
},
1381-
wantDecision: "block",
1382-
wantReason: "Empty message in SubagentStop action",
1381+
wantDecision: "",
1382+
wantReason: "",
13831383
wantSystemMessage: "Empty message in SubagentStop action",
13841384
},
13851385
{
@@ -1550,7 +1550,7 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
15501550
wantErr: false,
15511551
},
15521552
{
1553-
name: "Stop: Command failure (exit != 0) -> fail-safe block",
1553+
name: "Stop: Command failure (exit != 0) -> allow (error in systemMessage)",
15541554
eventType: "Stop",
15551555
action: Action{
15561556
Type: "command",
@@ -1559,8 +1559,8 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
15591559
stubStdout: "",
15601560
stubStderr: "Permission denied",
15611561
stubExitCode: 1,
1562-
wantDecision: "block",
1563-
wantReason: "Command failed with exit code 1: Permission denied",
1562+
wantDecision: "",
1563+
wantReason: "",
15641564
wantSystemMessage: "Command failed with exit code 1: Permission denied",
15651565
wantErr: false,
15661566
},
@@ -1578,21 +1578,21 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
15781578
wantErr: false,
15791579
},
15801580
{
1581-
name: "Stop: Invalid JSON -> fail-safe block",
1581+
name: "Stop: Invalid JSON -> allow (error in systemMessage)",
15821582
eventType: "Stop",
15831583
action: Action{
15841584
Type: "command",
15851585
Command: "invalid-json.sh",
15861586
},
15871587
stubStdout: `{invalid json}`,
15881588
stubExitCode: 0,
1589-
wantDecision: "block",
1590-
wantReason: "Command output is not valid JSON: {invalid json}",
1589+
wantDecision: "",
1590+
wantReason: "",
15911591
wantSystemMessage: "Command output is not valid JSON: {invalid json}",
15921592
wantErr: false,
15931593
},
15941594
{
1595-
name: "Stop: decision: block + reason missing -> fail-safe with reason warning",
1595+
name: "Stop: decision: block + reason missing -> allow (error in systemMessage)",
15961596
eventType: "Stop",
15971597
action: Action{
15981598
Type: "command",
@@ -1603,13 +1603,13 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
16031603
"decision": "block"
16041604
}`,
16051605
stubExitCode: 0,
1606-
wantDecision: "block",
1607-
wantReason: "Missing required field 'reason' when decision is 'block'",
1606+
wantDecision: "",
1607+
wantReason: "",
16081608
wantSystemMessage: "Missing required field 'reason' when decision is 'block'",
16091609
wantErr: false,
16101610
},
16111611
{
1612-
name: "Stop: Invalid decision value -> fail-safe block",
1612+
name: "Stop: Invalid decision value -> allow (error in systemMessage)",
16131613
eventType: "Stop",
16141614
action: Action{
16151615
Type: "command",
@@ -1621,8 +1621,8 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
16211621
"reason": "should not matter"
16221622
}`,
16231623
stubExitCode: 0,
1624-
wantDecision: "block",
1625-
wantReason: "Invalid decision value: must be 'block' or field must be omitted entirely",
1624+
wantDecision: "",
1625+
wantReason: "",
16261626
wantSystemMessage: "Invalid decision value: must be 'block' or field must be omitted entirely",
16271627
wantErr: false,
16281628
},
@@ -1705,7 +1705,7 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
17051705
wantErr: false,
17061706
},
17071707
{
1708-
name: "SubagentStop: Command failure (exit != 0) -> fail-safe block",
1708+
name: "SubagentStop: Command failure (exit != 0) -> allow (error in systemMessage)",
17091709
eventType: "SubagentStop",
17101710
action: Action{
17111711
Type: "command",
@@ -1714,8 +1714,8 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
17141714
stubStdout: "",
17151715
stubStderr: "Permission denied",
17161716
stubExitCode: 1,
1717-
wantDecision: "block",
1718-
wantReason: "Command failed with exit code 1: Permission denied",
1717+
wantDecision: "",
1718+
wantReason: "",
17191719
wantSystemMessage: "Command failed with exit code 1: Permission denied",
17201720
wantErr: false,
17211721
},
@@ -1733,21 +1733,21 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
17331733
wantErr: false,
17341734
},
17351735
{
1736-
name: "SubagentStop: Invalid JSON -> fail-safe block",
1736+
name: "SubagentStop: Invalid JSON -> allow (error in systemMessage)",
17371737
eventType: "SubagentStop",
17381738
action: Action{
17391739
Type: "command",
17401740
Command: "invalid-json.sh",
17411741
},
17421742
stubStdout: `{invalid json}`,
17431743
stubExitCode: 0,
1744-
wantDecision: "block",
1745-
wantReason: "Command output is not valid JSON: {invalid json}",
1744+
wantDecision: "",
1745+
wantReason: "",
17461746
wantSystemMessage: "Command output is not valid JSON: {invalid json}",
17471747
wantErr: false,
17481748
},
17491749
{
1750-
name: "SubagentStop: decision: block + reason missing -> fail-safe with reason warning",
1750+
name: "SubagentStop: decision: block + reason missing -> allow (error in systemMessage)",
17511751
eventType: "SubagentStop",
17521752
action: Action{
17531753
Type: "command",
@@ -1758,13 +1758,13 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
17581758
"decision": "block"
17591759
}`,
17601760
stubExitCode: 0,
1761-
wantDecision: "block",
1762-
wantReason: "Missing required field 'reason' when decision is 'block'",
1761+
wantDecision: "",
1762+
wantReason: "",
17631763
wantSystemMessage: "Missing required field 'reason' when decision is 'block'",
17641764
wantErr: false,
17651765
},
17661766
{
1767-
name: "SubagentStop: Invalid decision value -> fail-safe block",
1767+
name: "SubagentStop: Invalid decision value -> allow (error in systemMessage)",
17681768
eventType: "SubagentStop",
17691769
action: Action{
17701770
Type: "command",
@@ -1776,8 +1776,8 @@ func TestExecuteStopAndSubagentStopAction_TypeCommand(t *testing.T) {
17761776
"reason": "should not matter"
17771777
}`,
17781778
stubExitCode: 0,
1779-
wantDecision: "block",
1780-
wantReason: "Invalid decision value: must be 'block' or field must be omitted entirely",
1779+
wantDecision: "",
1780+
wantReason: "",
17811781
wantSystemMessage: "Invalid decision value: must be 'block' or field must be omitted entirely",
17821782
wantErr: false,
17831783
},

hooks_execute_test.go

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -466,19 +466,20 @@ func TestExecuteUserPromptSubmitHooks(t *testing.T) {
466466
}
467467
}
468468

469-
// TestExecuteStopAndSubagentStopHook_FailingCommandReturnsExit2 tests that failing commands
470-
// result in decision="block" (fail-safe) from executeStopHooks and executeSubagentStopHooks.
471-
func TestExecuteStopAndSubagentStopHook_FailingCommandReturnsExit2(t *testing.T) {
469+
// TestExecuteStopAndSubagentStopHook_FailingCommandAllowsStop tests that failing commands
470+
// result in decision="" (allow) from executeStopHooks and executeSubagentStopHooks.
471+
// Per official Claude Code hooks spec, hook errors are non-blocking (allow stop).
472+
func TestExecuteStopAndSubagentStopHook_FailingCommandAllowsStop(t *testing.T) {
472473
tests := []struct {
473474
name string
474475
eventType string // "Stop" or "SubagentStop"
475476
}{
476477
{
477-
name: "Stop: failing command returns decision block",
478+
name: "Stop: failing command allows stop (decision empty)",
478479
eventType: "Stop",
479480
},
480481
{
481-
name: "SubagentStop: failing command returns decision block",
482+
name: "SubagentStop: failing command allows subagent stop (decision empty)",
482483
eventType: "SubagentStop",
483484
},
484485
}
@@ -544,9 +545,9 @@ func TestExecuteStopAndSubagentStopHook_FailingCommandReturnsExit2(t *testing.T)
544545
continueValue = output.Continue
545546
}
546547

547-
// Fail-safe: decision should be "block"
548-
if decision != "block" {
549-
t.Errorf("Expected decision 'block' for failing command, got %q", decision)
548+
// Per official spec: hook errors are non-blocking, decision should be "" (allow stop)
549+
if decision != "" {
550+
t.Errorf("Expected decision '' (allow) for failing command, got %q", decision)
550551
}
551552

552553
// Continue should always be true
@@ -1913,7 +1914,7 @@ func TestExecuteStopAndSubagentStopHooksJSON(t *testing.T) {
19131914
wantErr: false,
19141915
},
19151916
{
1916-
name: "Stop: 8. Action error - fail-safe block",
1917+
name: "Stop: 8. Action error - allow (error in systemMessage)",
19171918
eventType: "Stop",
19181919
stopConfig: []StopHook{
19191920
{
@@ -1926,7 +1927,7 @@ func TestExecuteStopAndSubagentStopHooksJSON(t *testing.T) {
19261927
},
19271928
},
19281929
wantContinue: true,
1929-
wantDecision: "block",
1930+
wantDecision: "",
19301931
wantErr: false,
19311932
},
19321933
{
@@ -2075,7 +2076,7 @@ func TestExecuteStopAndSubagentStopHooksJSON(t *testing.T) {
20752076
wantErr: false,
20762077
},
20772078
{
2078-
name: "SubagentStop: 8. Action error - fail-safe block",
2079+
name: "SubagentStop: 8. Action error - allow (error in systemMessage)",
20792080
eventType: "SubagentStop",
20802081
subagentConfig: []SubagentStopHook{
20812082
{
@@ -2088,8 +2089,8 @@ func TestExecuteStopAndSubagentStopHooksJSON(t *testing.T) {
20882089
},
20892090
},
20902091
wantContinue: true,
2091-
wantDecision: "block",
2092-
wantReason: "Empty message in SubagentStop action",
2092+
wantDecision: "",
2093+
wantReason: "",
20932094
wantErr: false,
20942095
},
20952096
}

0 commit comments

Comments
 (0)