diff --git a/internal/daemon/lifecycle.go b/internal/daemon/lifecycle.go index 2b28016e..698605d1 100644 --- a/internal/daemon/lifecycle.go +++ b/internal/daemon/lifecycle.go @@ -275,15 +275,29 @@ func (d *Daemon) getRoleConfigForIdentity(identity string) (*beads.RoleConfig, * } // Look up role bead - roleBeadID := beads.RoleBeadID(parsed.RoleType) b := beads.New(d.config.TownRoot) - config, err := b.GetRoleConfig(roleBeadID) + + roleBeadID := beads.RoleBeadIDTown(parsed.RoleType) + roleConfig, err := b.GetRoleConfig(roleBeadID) if err != nil { d.logger.Printf("Warning: failed to get role config for %s: %v", roleBeadID, err) } + // Backward compatibility: fall back to legacy role bead IDs. + if roleConfig == nil { + legacyRoleBeadID := beads.RoleBeadID(parsed.RoleType) // gt--role + if legacyRoleBeadID != roleBeadID { + legacyCfg, legacyErr := b.GetRoleConfig(legacyRoleBeadID) + if legacyErr != nil { + d.logger.Printf("Warning: failed to get legacy role config for %s: %v", legacyRoleBeadID, legacyErr) + } else if legacyCfg != nil { + roleConfig = legacyCfg + } + } + } + // Return parsed identity even if config is nil (caller can use defaults) - return config, parsed, nil + return roleConfig, parsed, nil } // identityToSession converts a beads identity to a tmux session name. diff --git a/internal/daemon/role_config_integration_test.go b/internal/daemon/role_config_integration_test.go new file mode 100644 index 00000000..d5bfbc20 --- /dev/null +++ b/internal/daemon/role_config_integration_test.go @@ -0,0 +1,88 @@ +//go:build integration + +package daemon + +import ( + "io" + "log" + "os/exec" + "strings" + "testing" +) + +func runBd(t *testing.T, dir string, args ...string) string { + t.Helper() + cmd := exec.Command("bd", args...) //nolint:gosec // bd is a trusted internal tool in this repo + cmd.Dir = dir + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("bd %s failed: %v\n%s", strings.Join(args, " "), err, string(out)) + } + return string(out) +} + +func TestGetRoleConfigForIdentity_PrefersTownRoleBead(t *testing.T) { + if _, err := exec.LookPath("bd"); err != nil { + t.Skip("bd not installed") + } + + townRoot := t.TempDir() + runBd(t, townRoot, "init", "--quiet", "--prefix", "hq") + + // Create canonical role bead. + runBd(t, townRoot, "create", + "--id", "hq-witness-role", + "--type", "role", + "--title", "Witness Role", + "--description", "start_command: exec echo hq\n", + ) + + d := &Daemon{ + config: &Config{TownRoot: townRoot}, + logger: log.New(io.Discard, "", 0), + } + + cfg, parsed, err := d.getRoleConfigForIdentity("myrig-witness") + if err != nil { + t.Fatalf("getRoleConfigForIdentity: %v", err) + } + if parsed == nil || parsed.RoleType != "witness" { + t.Fatalf("parsed = %#v, want roleType witness", parsed) + } + if cfg == nil || cfg.StartCommand != "exec echo hq" { + t.Fatalf("cfg.StartCommand = %#v, want %q", cfg, "exec echo hq") + } +} + +func TestGetRoleConfigForIdentity_FallsBackToLegacyRoleBead(t *testing.T) { + if _, err := exec.LookPath("bd"); err != nil { + t.Skip("bd not installed") + } + + townRoot := t.TempDir() + runBd(t, townRoot, "init", "--quiet", "--prefix", "gt") + + // Only legacy role bead exists. + runBd(t, townRoot, "create", + "--id", "gt-witness-role", + "--type", "role", + "--title", "Witness Role (legacy)", + "--description", "start_command: exec echo gt\n", + ) + + d := &Daemon{ + config: &Config{TownRoot: townRoot}, + logger: log.New(io.Discard, "", 0), + } + + cfg, parsed, err := d.getRoleConfigForIdentity("myrig-witness") + if err != nil { + t.Fatalf("getRoleConfigForIdentity: %v", err) + } + if parsed == nil || parsed.RoleType != "witness" { + t.Fatalf("parsed = %#v, want roleType witness", parsed) + } + if cfg == nil || cfg.StartCommand != "exec echo gt" { + t.Fatalf("cfg.StartCommand = %#v, want %q", cfg, "exec echo gt") + } +}