返回 DeepSeek-Reasonix
subagent_ablation_test.go
根目录 / internal / boot / subagent_ablation_test.go
1 package boot
2
3 import (
4 "context"
5 "strings"
6 "testing"
7
8 "reasonix/internal/ablation"
9 "reasonix/internal/skill"
10 )
11
12 // The arm is only a control if it removes delegation whichever tool reaches it.
13 // A measured no-subagent run spent a child on `explore` before this gate.
14 func TestSubagentArmRefusesProfileSkillDelegation(t *testing.T) {
15 called := false
16 run := skill.SubagentRunner(func(context.Context, skill.Skill, string, skill.SubagentRunOptions) (string, error) {
17 called = true
18 return "ran", nil
19 })
20 sk := skill.Skill{Name: "explore", RunAs: skill.RunSubagent}
21
22 gated := gateSubagentArm(ablation.New(ablation.Subagent), run)
23 if _, err := gated(context.Background(), sk, "task", skill.SubagentRunOptions{}); err == nil ||
24 !strings.Contains(err.Error(), "subagent ablation arm") {
25 t.Fatalf("err = %v, want a refusal naming the arm", err)
26 }
27 if called {
28 t.Fatal("the gate must refuse before spawning a child")
29 }
30
31 // Without the arm the runner is untouched, so the normal path keeps its
32 // exact behaviour rather than routing through a wrapper.
33 if _, err := gateSubagentArm(ablation.Set{}, run)(context.Background(), sk, "task", skill.SubagentRunOptions{}); err != nil {
34 t.Fatalf("ungated run: %v", err)
35 }
36 if !called {
37 t.Fatal("the ungated runner must still run")
38 }
39 }
40
41 // Under the arm the control run must not even carry the wrapper schemas.
42 func TestSubagentArmDropsBuiltinWrapperTools(t *testing.T) {
43 run := skill.SubagentRunner(func(context.Context, skill.Skill, string, skill.SubagentRunOptions) (string, error) {
44 return "", nil
45 })
46 if got := builtinSubagentTools(ablation.New(ablation.Subagent), skill.New(skill.Options{}), run); len(got) != 0 {
47 t.Fatalf("arm still registered %d wrapper tools", len(got))
48 }
49 if got := builtinSubagentTools(ablation.Set{}, skill.New(skill.Options{}), run); len(got) == 0 {
50 t.Fatal("the ordinary run must keep explore/research/review")
51 }
52 }
53
53 lines GO