feat: add structured labels for agent beads (bd-g7eq)

Add role_type and rig labels to agent beads for filtering queries.

Changes:
- Add RoleType/Rig to CreateArgs and UpdateArgs in RPC protocol
- Auto-add role_type:<value> and rig:<value> labels when creating/updating agents
- Add --role-type and --agent-rig flags to bd create (requires --type=agent)
- Add bd agent backfill-labels command to update existing agent beads

This enables queries like:
  bd list --type=agent --label=role_type:witness
  bd list --type=agent --label=rig:gastown

🤖 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-29 21:15:46 -08:00
parent 22fb3ff56b
commit a8748936e4
4 changed files with 318 additions and 0 deletions

View File

@@ -97,6 +97,9 @@ type CreateArgs struct {
CreatedBy string `json:"created_by,omitempty"` // Who created the issue
// Molecule type (for swarm coordination)
MolType string `json:"mol_type,omitempty"` // swarm, patrol, or work (default)
// Agent identity fields (only valid when IssueType == "agent")
RoleType string `json:"role_type,omitempty"` // polecat|crew|witness|refinery|mayor|deacon
Rig string `json:"rig,omitempty"` // Rig name (empty for town-level agents)
}
// UpdateArgs represents arguments for the update operation
@@ -134,6 +137,9 @@ type UpdateArgs struct {
// Agent state fields
AgentState *string `json:"agent_state,omitempty"` // Agent state (idle|running|stuck|stopped|dead)
LastActivity *bool `json:"last_activity,omitempty"` // If true, update last_activity to now
// Agent identity fields
RoleType *string `json:"role_type,omitempty"` // polecat|crew|witness|refinery|mayor|deacon
Rig *string `json:"rig,omitempty"` // Rig name (empty for town-level agents)
}
// CloseArgs represents arguments for the close operation

View File

@@ -115,6 +115,13 @@ func updatesFromArgs(a UpdateArgs) map[string]interface{} {
if a.LastActivity != nil && *a.LastActivity {
u["last_activity"] = time.Now()
}
// Agent identity fields
if a.RoleType != nil {
u["role_type"] = *a.RoleType
}
if a.Rig != nil {
u["rig"] = *a.Rig
}
return u
}
@@ -198,6 +205,9 @@ func (s *Server) handleCreate(req *Request) Response {
CreatedBy: createArgs.CreatedBy,
// Molecule type
MolType: types.MolType(createArgs.MolType),
// Agent identity fields
RoleType: createArgs.RoleType,
Rig: createArgs.Rig,
}
// Check if any dependencies are discovered-from type
@@ -283,6 +293,28 @@ func (s *Server) handleCreate(req *Request) Response {
}
}
// Auto-add role_type/rig labels for agent beads (enables filtering queries)
if issue.IssueType == types.TypeAgent {
if issue.RoleType != "" {
label := "role_type:" + issue.RoleType
if err := store.AddLabel(ctx, issue.ID, label, s.reqActor(req)); err != nil {
return Response{
Success: false,
Error: fmt.Sprintf("failed to add role_type label: %v", err),
}
}
}
if issue.Rig != "" {
label := "rig:" + issue.Rig
if err := store.AddLabel(ctx, issue.ID, label, s.reqActor(req)); err != nil {
return Response{
Success: false,
Error: fmt.Sprintf("failed to add rig label: %v", err),
}
}
}
}
// Add dependencies if specified
for _, depSpec := range createArgs.Dependencies {
depSpec = strings.TrimSpace(depSpec)
@@ -482,6 +514,29 @@ func (s *Server) handleUpdate(req *Request) Response {
}
}
// Auto-add role_type/rig labels for agent beads when these fields are set
// This enables filtering queries like: bd list --type=agent --label=role_type:witness
if issue.IssueType == types.TypeAgent {
if updateArgs.RoleType != nil && *updateArgs.RoleType != "" {
label := "role_type:" + *updateArgs.RoleType
if err := store.AddLabel(ctx, updateArgs.ID, label, actor); err != nil {
return Response{
Success: false,
Error: fmt.Sprintf("failed to add role_type label: %v", err),
}
}
}
if updateArgs.Rig != nil && *updateArgs.Rig != "" {
label := "rig:" + *updateArgs.Rig
if err := store.AddLabel(ctx, updateArgs.ID, label, actor); err != nil {
return Response{
Success: false,
Error: fmt.Sprintf("failed to add rig label: %v", err),
}
}
}
}
// Handle reparenting
if updateArgs.Parent != nil {
newParentID := *updateArgs.Parent