feat(roles): switch daemon to config-based roles, remove role beads (Phase 2+3)
Phase 2: Daemon now uses config.LoadRoleDefinition() instead of role beads - lifecycle.go: getRoleConfigForIdentity() reads from TOML configs - Layered override resolution: builtin → town → rig Phase 3: Remove role bead creation and references - Remove RoleBead field from AgentFields struct - gt install no longer creates role beads - Remove 'role' from custom types list - Delete migrate_agents.go (no longer needed) - Deprecate beads_role.go (kept for reading existing beads) - Rewrite role_beads_check.go to validate TOML configs Existing role beads are orphaned but harmless. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
544cacf36d
commit
a610283078
@@ -211,7 +211,7 @@ func (d *Daemon) executeLifecycleAction(request *LifecycleRequest) error {
|
||||
}
|
||||
|
||||
// ParsedIdentity holds the components extracted from an agent identity string.
|
||||
// This is used to look up the appropriate role bead for lifecycle config.
|
||||
// This is used to look up the appropriate role config for lifecycle management.
|
||||
type ParsedIdentity struct {
|
||||
RoleType string // mayor, deacon, witness, refinery, crew, polecat
|
||||
RigName string // Empty for town-level agents (mayor, deacon)
|
||||
@@ -220,7 +220,7 @@ type ParsedIdentity struct {
|
||||
|
||||
// parseIdentity extracts role type, rig name, and agent name from an identity string.
|
||||
// This is the ONLY place where identity string patterns are parsed.
|
||||
// All other functions should use the extracted components to look up role beads.
|
||||
// All other functions should use the extracted components to look up role config.
|
||||
func parseIdentity(identity string) (*ParsedIdentity, error) {
|
||||
switch identity {
|
||||
case "mayor":
|
||||
@@ -268,49 +268,50 @@ func parseIdentity(identity string) (*ParsedIdentity, error) {
|
||||
return nil, fmt.Errorf("unknown identity format: %s", identity)
|
||||
}
|
||||
|
||||
// getRoleConfigForIdentity looks up the role bead for an identity and returns its config.
|
||||
// Falls back to default config if role bead doesn't exist or has no config.
|
||||
// getRoleConfigForIdentity loads role configuration from the config-based role system.
|
||||
// Uses config.LoadRoleDefinition() with layered override resolution (builtin → town → rig).
|
||||
// Returns config in beads.RoleConfig format for backward compatibility.
|
||||
func (d *Daemon) getRoleConfigForIdentity(identity string) (*beads.RoleConfig, *ParsedIdentity, error) {
|
||||
parsed, err := parseIdentity(identity)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Look up role bead
|
||||
b := beads.New(d.config.TownRoot)
|
||||
// Determine rig path for rig-scoped roles
|
||||
rigPath := ""
|
||||
if parsed.RigName != "" {
|
||||
rigPath = filepath.Join(d.config.TownRoot, parsed.RigName)
|
||||
}
|
||||
|
||||
roleBeadID := beads.RoleBeadIDTown(parsed.RoleType)
|
||||
roleConfig, err := b.GetRoleConfig(roleBeadID)
|
||||
// Load role definition from config system (Phase 2: config-based roles)
|
||||
roleDef, err := config.LoadRoleDefinition(d.config.TownRoot, rigPath, parsed.RoleType)
|
||||
if err != nil {
|
||||
d.logger.Printf("Warning: failed to get role config for %s: %v", roleBeadID, err)
|
||||
d.logger.Printf("Warning: failed to load role definition for %s: %v", parsed.RoleType, err)
|
||||
// Return parsed identity even if config fails (caller can use defaults)
|
||||
return nil, parsed, nil
|
||||
}
|
||||
|
||||
// Backward compatibility: fall back to legacy role bead IDs.
|
||||
if roleConfig == nil {
|
||||
legacyRoleBeadID := beads.RoleBeadID(parsed.RoleType) // gt-<role>-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
|
||||
}
|
||||
}
|
||||
// Convert to beads.RoleConfig for backward compatibility
|
||||
roleConfig := &beads.RoleConfig{
|
||||
SessionPattern: roleDef.Session.Pattern,
|
||||
WorkDirPattern: roleDef.Session.WorkDir,
|
||||
NeedsPreSync: roleDef.Session.NeedsPreSync,
|
||||
StartCommand: roleDef.Session.StartCommand,
|
||||
EnvVars: roleDef.Env,
|
||||
}
|
||||
|
||||
// Return parsed identity even if config is nil (caller can use defaults)
|
||||
return roleConfig, parsed, nil
|
||||
}
|
||||
|
||||
// identityToSession converts a beads identity to a tmux session name.
|
||||
// Uses role bead config if available, falls back to hardcoded patterns.
|
||||
// Uses role config if available, falls back to hardcoded patterns.
|
||||
func (d *Daemon) identityToSession(identity string) string {
|
||||
config, parsed, err := d.getRoleConfigForIdentity(identity)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// If role bead has session_pattern, use it
|
||||
// If role config has session_pattern, use it
|
||||
if config != nil && config.SessionPattern != "" {
|
||||
return beads.ExpandRolePattern(config.SessionPattern, d.config.TownRoot, parsed.RigName, parsed.AgentName, parsed.RoleType)
|
||||
}
|
||||
@@ -333,7 +334,7 @@ func (d *Daemon) identityToSession(identity string) string {
|
||||
}
|
||||
|
||||
// restartSession starts a new session for the given agent.
|
||||
// Uses role bead config if available, falls back to hardcoded defaults.
|
||||
// Uses role config if available, falls back to hardcoded defaults.
|
||||
func (d *Daemon) restartSession(sessionName, identity string) error {
|
||||
// Get role config for this identity
|
||||
config, parsed, err := d.getRoleConfigForIdentity(identity)
|
||||
@@ -409,9 +410,9 @@ func (d *Daemon) restartSession(sessionName, identity string) error {
|
||||
}
|
||||
|
||||
// getWorkDir determines the working directory for an agent.
|
||||
// Uses role bead config if available, falls back to hardcoded defaults.
|
||||
// Uses role config if available, falls back to hardcoded defaults.
|
||||
func (d *Daemon) getWorkDir(config *beads.RoleConfig, parsed *ParsedIdentity) string {
|
||||
// If role bead has work_dir_pattern, use it
|
||||
// If role config has work_dir_pattern, use it
|
||||
if config != nil && config.WorkDirPattern != "" {
|
||||
return beads.ExpandRolePattern(config.WorkDirPattern, d.config.TownRoot, parsed.RigName, parsed.AgentName, parsed.RoleType)
|
||||
}
|
||||
@@ -442,9 +443,9 @@ func (d *Daemon) getWorkDir(config *beads.RoleConfig, parsed *ParsedIdentity) st
|
||||
}
|
||||
|
||||
// getNeedsPreSync determines if a workspace needs git sync before starting.
|
||||
// Uses role bead config if available, falls back to hardcoded defaults.
|
||||
// Uses role config if available, falls back to hardcoded defaults.
|
||||
func (d *Daemon) getNeedsPreSync(config *beads.RoleConfig, parsed *ParsedIdentity) bool {
|
||||
// If role bead has explicit config, use it
|
||||
// If role config is available, use it
|
||||
if config != nil {
|
||||
return config.NeedsPreSync
|
||||
}
|
||||
@@ -459,9 +460,9 @@ func (d *Daemon) getNeedsPreSync(config *beads.RoleConfig, parsed *ParsedIdentit
|
||||
}
|
||||
|
||||
// getStartCommand determines the startup command for an agent.
|
||||
// Uses role bead config if available, then role-based agent selection, then hardcoded defaults.
|
||||
// Uses role config if available, then role-based agent selection, then hardcoded defaults.
|
||||
func (d *Daemon) getStartCommand(roleConfig *beads.RoleConfig, parsed *ParsedIdentity) string {
|
||||
// If role bead has explicit config, use it
|
||||
// If role config is available, use it
|
||||
if roleConfig != nil && roleConfig.StartCommand != "" {
|
||||
// Expand any patterns in the command
|
||||
return beads.ExpandRolePattern(roleConfig.StartCommand, d.config.TownRoot, parsed.RigName, parsed.AgentName, parsed.RoleType)
|
||||
@@ -516,7 +517,7 @@ func (d *Daemon) getStartCommand(roleConfig *beads.RoleConfig, parsed *ParsedIde
|
||||
}
|
||||
|
||||
// setSessionEnvironment sets environment variables for the tmux session.
|
||||
// Uses centralized AgentEnv for consistency, plus role bead custom env vars if available.
|
||||
// Uses centralized AgentEnv for consistency, plus custom env vars from role config if available.
|
||||
func (d *Daemon) setSessionEnvironment(sessionName string, roleConfig *beads.RoleConfig, parsed *ParsedIdentity) {
|
||||
// Use centralized AgentEnv for base environment variables
|
||||
envVars := config.AgentEnv(config.AgentEnvConfig{
|
||||
@@ -529,7 +530,7 @@ func (d *Daemon) setSessionEnvironment(sessionName string, roleConfig *beads.Rol
|
||||
_ = d.tmux.SetEnvironment(sessionName, k, v)
|
||||
}
|
||||
|
||||
// Set any custom env vars from role config (bead-defined overrides)
|
||||
// Set any custom env vars from role config
|
||||
if roleConfig != nil {
|
||||
for k, v := range roleConfig.EnvVars {
|
||||
expanded := beads.ExpandRolePattern(v, d.config.TownRoot, parsed.RigName, parsed.AgentName, parsed.RoleType)
|
||||
@@ -637,10 +638,10 @@ type AgentBeadInfo struct {
|
||||
Type string `json:"issue_type"`
|
||||
State string // Parsed from description: agent_state
|
||||
HookBead string // Parsed from description: hook_bead
|
||||
RoleBead string // Parsed from description: role_bead
|
||||
RoleType string // Parsed from description: role_type
|
||||
Rig string // Parsed from description: rig
|
||||
LastUpdate string `json:"updated_at"`
|
||||
// Note: RoleBead field removed - role definitions are now config-based
|
||||
}
|
||||
|
||||
// getAgentBeadState reads non-observable agent state from an agent bead.
|
||||
@@ -699,7 +700,6 @@ func (d *Daemon) getAgentBeadInfo(agentBeadID string) (*AgentBeadInfo, error) {
|
||||
|
||||
if fields != nil {
|
||||
info.State = fields.AgentState
|
||||
info.RoleBead = fields.RoleBead
|
||||
info.RoleType = fields.RoleType
|
||||
info.Rig = fields.Rig
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user