1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
package storage
import (
"encoding/json"
"strings"
"testing"
"github.com/thepeterstone/claudomator/internal/role"
)
func TestSeedRoleConfigs_CreatesAndActivatesBuilder(t *testing.T) {
db := testDB(t)
if err := db.SeedRoleConfigs(); err != nil {
t.Fatalf("SeedRoleConfigs: %v", err)
}
row, err := db.GetActiveRoleConfig("builder")
if err != nil {
t.Fatalf("GetActiveRoleConfig(builder): %v", err)
}
if row.Version != 1 {
t.Errorf("expected version 1, got %d", row.Version)
}
if row.ProposedBy != "seed" {
t.Errorf("expected proposed_by 'seed', got %q", row.ProposedBy)
}
var cfg role.RoleConfig
if err := json.Unmarshal([]byte(row.ConfigJSON), &cfg); err != nil {
t.Fatalf("unmarshal seeded config: %v", err)
}
if cfg.Role != "builder" {
t.Errorf("cfg.Role = %q, want builder", cfg.Role)
}
if cfg.SystemPrompt == "" {
t.Error("cfg.SystemPrompt is empty, want the decompose-or-implement guidance")
}
if len(cfg.EscalationLadder) != 2 {
t.Fatalf("expected 2 escalation tiers, got %d", len(cfg.EscalationLadder))
}
if cfg.EscalationLadder[0].Candidates[0].Provider != "claude" {
t.Errorf("tier 0 provider = %q, want claude", cfg.EscalationLadder[0].Candidates[0].Provider)
}
}
// TestSeedRoleConfigs_CreatesAndActivatesPlanner proves the arbitration
// role's system prompt is seeded too (added 2026-07-11 alongside
// finalizeArbitration's fail-closed default -- see that const's doc
// comment for why: a live run showed an arbitration agent with no system
// prompt at all never called report_verdict). Specifically checks the
// prompt actually names the report_verdict tool, since that's the whole
// point of this seed.
func TestSeedRoleConfigs_CreatesAndActivatesPlanner(t *testing.T) {
db := testDB(t)
if err := db.SeedRoleConfigs(); err != nil {
t.Fatalf("SeedRoleConfigs: %v", err)
}
row, err := db.GetActiveRoleConfig("planner")
if err != nil {
t.Fatalf("GetActiveRoleConfig(planner): %v", err)
}
if row.Version != 1 {
t.Errorf("expected version 1, got %d", row.Version)
}
var cfg role.RoleConfig
if err := json.Unmarshal([]byte(row.ConfigJSON), &cfg); err != nil {
t.Fatalf("unmarshal seeded config: %v", err)
}
if cfg.Role != "planner" {
t.Errorf("cfg.Role = %q, want planner", cfg.Role)
}
if !strings.Contains(cfg.SystemPrompt, "report_verdict") {
t.Error("cfg.SystemPrompt does not mention report_verdict -- the whole point of this seed is mandating that call")
}
if len(cfg.EscalationLadder) != 2 {
t.Fatalf("expected 2 escalation tiers, got %d", len(cfg.EscalationLadder))
}
}
func TestSeedRoleConfigs_Idempotent_DoesNotDuplicateOrOverwrite(t *testing.T) {
db := testDB(t)
if err := db.SeedRoleConfigs(); err != nil {
t.Fatalf("first SeedRoleConfigs: %v", err)
}
if err := db.SeedRoleConfigs(); err != nil {
t.Fatalf("second SeedRoleConfigs: %v", err)
}
for _, r := range []string{"builder", "planner"} {
versions, err := db.ListRoleConfigVersions(r)
if err != nil {
t.Fatalf("ListRoleConfigVersions(%s): %v", r, err)
}
if len(versions) != 1 {
t.Fatalf("%s: expected exactly 1 version after calling SeedRoleConfigs twice, got %d", r, len(versions))
}
}
}
func TestSeedRoleConfigs_DoesNotOverwriteHumanCustomizedVersion(t *testing.T) {
db := testDB(t)
// A human (or the retro role's propose_role_config tool) already
// created and activated their own version before the seed ever ran.
custom, err := db.CreateRoleConfig("builder", `{"role":"builder","system_prompt":"custom prompt"}`, "human")
if err != nil {
t.Fatalf("CreateRoleConfig: %v", err)
}
if err := db.ActivateRoleConfigVersion("builder", custom.Version); err != nil {
t.Fatalf("ActivateRoleConfigVersion: %v", err)
}
if err := db.SeedRoleConfigs(); err != nil {
t.Fatalf("SeedRoleConfigs: %v", err)
}
row, err := db.GetActiveRoleConfig("builder")
if err != nil {
t.Fatalf("GetActiveRoleConfig: %v", err)
}
if row.ProposedBy != "human" {
t.Errorf("active role config proposed_by = %q, want unchanged 'human' -- seed must not overwrite a human-authored active version", row.ProposedBy)
}
versions, err := db.ListRoleConfigVersions("builder")
if err != nil {
t.Fatalf("ListRoleConfigVersions: %v", err)
}
if len(versions) != 1 {
t.Fatalf("expected exactly 1 version (seed must not create a competing draft), got %d", len(versions))
}
}
|