ZFC #4: Replace daemon identity parsing with agent self-registration

Implements role-based lifecycle configuration where agent types self-register
via role beads instead of hardcoded identity string parsing in the daemon.

Changes:
- Add RoleConfig struct with lifecycle fields (session_pattern, work_dir_pattern,
  needs_pre_sync, start_command, env_vars)
- Add ParseRoleConfig/FormatRoleConfig/ExpandRolePattern to beads package
- Add role bead ID helpers (RoleBeadID, MayorRoleBeadID, etc.)
- Refactor daemon to use single parseIdentity function as ONLY place where
  identity strings are parsed
- Daemon now looks up role beads to get lifecycle config, with fallback to
  defaults when role bead is missing or has no config
- Updated all role beads (mayor, deacon, witness, refinery, crew, polecat)
  with structured lifecycle configuration fields
- Add comprehensive unit tests for RoleConfig parsing and expansion

This makes the daemon ZFC-compliant by trusting what agents self-report in
their role beads rather than encoding agent-specific knowledge in Go code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-30 02:06:08 -08:00
parent ede5406d36
commit 437d42c7fa
4 changed files with 766 additions and 226 deletions
+65
View File
@@ -888,3 +888,68 @@ func IsAgentSessionBead(beadID string) bool {
return false
}
}
// Role bead ID naming convention:
// gt-<role>-role
//
// Examples:
// - gt-mayor-role
// - gt-deacon-role
// - gt-witness-role
// - gt-refinery-role
// - gt-crew-role
// - gt-polecat-role
// RoleBeadID returns the role bead ID for a given role type.
// Role beads define lifecycle configuration for each agent type.
func RoleBeadID(roleType string) string {
return "gt-" + roleType + "-role"
}
// MayorRoleBeadID returns the Mayor role bead ID.
func MayorRoleBeadID() string {
return RoleBeadID("mayor")
}
// DeaconRoleBeadID returns the Deacon role bead ID.
func DeaconRoleBeadID() string {
return RoleBeadID("deacon")
}
// WitnessRoleBeadID returns the Witness role bead ID.
func WitnessRoleBeadID() string {
return RoleBeadID("witness")
}
// RefineryRoleBeadID returns the Refinery role bead ID.
func RefineryRoleBeadID() string {
return RoleBeadID("refinery")
}
// CrewRoleBeadID returns the Crew role bead ID.
func CrewRoleBeadID() string {
return RoleBeadID("crew")
}
// PolecatRoleBeadID returns the Polecat role bead ID.
func PolecatRoleBeadID() string {
return RoleBeadID("polecat")
}
// GetRoleConfig looks up a role bead and returns its parsed RoleConfig.
// Returns nil, nil if the role bead doesn't exist or has no config.
func (b *Beads) GetRoleConfig(roleBeadID string) (*RoleConfig, error) {
issue, err := b.Show(roleBeadID)
if err != nil {
if errors.Is(err, ErrNotFound) {
return nil, nil
}
return nil, err
}
if issue.Type != "role" {
return nil, fmt.Errorf("bead %s is not a role bead (type: %s)", roleBeadID, issue.Type)
}
return ParseRoleConfig(issue.Description), nil
}