feat: Wire up created_by field for beads issues (gt-u6nri)
- Add CreatedBy field to Issue struct (matches beads GH#748) - Add Actor field to CreateOptions, pass --actor to bd create - Add ActorString() method to RoleInfo for identity formatting - Update all beads.Create() callers to pass Actor - Update direct bd create exec calls with --actor: - mail/router.go: uses sender identity - patrol_helpers.go: uses role name - doctor/patrol_check.go: uses "gt-doctor" - rig/manager.go: uses "gt-rig-init" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@ type Issue struct {
|
|||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
Type string `json:"issue_type"`
|
Type string `json:"issue_type"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
|
CreatedBy string `json:"created_by,omitempty"`
|
||||||
UpdatedAt string `json:"updated_at"`
|
UpdatedAt string `json:"updated_at"`
|
||||||
ClosedAt string `json:"closed_at,omitempty"`
|
ClosedAt string `json:"closed_at,omitempty"`
|
||||||
Parent string `json:"parent,omitempty"`
|
Parent string `json:"parent,omitempty"`
|
||||||
@@ -76,6 +77,7 @@ type CreateOptions struct {
|
|||||||
Priority int // 0-4
|
Priority int // 0-4
|
||||||
Description string
|
Description string
|
||||||
Parent string
|
Parent string
|
||||||
|
Actor string // Who is creating this issue (populates created_by)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOptions specifies options for updating an issue.
|
// UpdateOptions specifies options for updating an issue.
|
||||||
@@ -315,6 +317,9 @@ func (b *Beads) Create(opts CreateOptions) (*Issue, error) {
|
|||||||
if opts.Parent != "" {
|
if opts.Parent != "" {
|
||||||
args = append(args, "--parent="+opts.Parent)
|
args = append(args, "--parent="+opts.Parent)
|
||||||
}
|
}
|
||||||
|
if opts.Actor != "" {
|
||||||
|
args = append(args, "--actor="+opts.Actor)
|
||||||
|
}
|
||||||
|
|
||||||
out, err := b.run(args...)
|
out, err := b.run(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -534,6 +539,7 @@ func (b *Beads) GetOrCreateHandoffBead(role string) (*Issue, error) {
|
|||||||
Type: "task",
|
Type: "task",
|
||||||
Priority: 2,
|
Priority: 2,
|
||||||
Description: "", // Empty until first handoff
|
Description: "", // Empty until first handoff
|
||||||
|
Actor: role,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating handoff bead: %w", err)
|
return nil, fmt.Errorf("creating handoff bead: %w", err)
|
||||||
|
|||||||
@@ -476,6 +476,7 @@ squashed_at: %s
|
|||||||
Description: digestDesc,
|
Description: digestDesc,
|
||||||
Type: "task",
|
Type: "task",
|
||||||
Priority: 4, // P4 - backlog priority for digests
|
Priority: 4, // P4 - backlog priority for digests
|
||||||
|
Actor: target,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating digest: %w", err)
|
return fmt.Errorf("creating digest: %w", err)
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func autoSpawnPatrol(cfg PatrolConfig) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the patrol wisp
|
// Create the patrol wisp
|
||||||
cmdSpawn := exec.Command("bd", "--no-daemon", "wisp", "create", protoID)
|
cmdSpawn := exec.Command("bd", "--no-daemon", "wisp", "create", protoID, "--actor", cfg.RoleName)
|
||||||
cmdSpawn.Dir = cfg.BeadsDir
|
cmdSpawn.Dir = cfg.BeadsDir
|
||||||
var stdoutSpawn, stderrSpawn bytes.Buffer
|
var stdoutSpawn, stderrSpawn bytes.Buffer
|
||||||
cmdSpawn.Stdout = &stdoutSpawn
|
cmdSpawn.Stdout = &stdoutSpawn
|
||||||
|
|||||||
@@ -230,6 +230,42 @@ func parseRoleString(s string) (Role, string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActorString returns the actor identity string for beads attribution.
|
||||||
|
// Format matches beads created_by convention:
|
||||||
|
// - Simple roles: "mayor", "deacon"
|
||||||
|
// - Rig-specific: "gastown/witness", "gastown/refinery"
|
||||||
|
// - Workers: "gastown/crew/max", "gastown/polecats/Toast"
|
||||||
|
func (info RoleInfo) ActorString() string {
|
||||||
|
switch info.Role {
|
||||||
|
case RoleMayor:
|
||||||
|
return "mayor"
|
||||||
|
case RoleDeacon:
|
||||||
|
return "deacon"
|
||||||
|
case RoleWitness:
|
||||||
|
if info.Rig != "" {
|
||||||
|
return fmt.Sprintf("%s/witness", info.Rig)
|
||||||
|
}
|
||||||
|
return "witness"
|
||||||
|
case RoleRefinery:
|
||||||
|
if info.Rig != "" {
|
||||||
|
return fmt.Sprintf("%s/refinery", info.Rig)
|
||||||
|
}
|
||||||
|
return "refinery"
|
||||||
|
case RolePolecat:
|
||||||
|
if info.Rig != "" && info.Polecat != "" {
|
||||||
|
return fmt.Sprintf("%s/polecats/%s", info.Rig, info.Polecat)
|
||||||
|
}
|
||||||
|
return "polecat"
|
||||||
|
case RoleCrew:
|
||||||
|
if info.Rig != "" && info.Polecat != "" {
|
||||||
|
return fmt.Sprintf("%s/crew/%s", info.Rig, info.Polecat)
|
||||||
|
}
|
||||||
|
return "crew"
|
||||||
|
default:
|
||||||
|
return string(info.Role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getRoleHome returns the canonical home directory for a role.
|
// getRoleHome returns the canonical home directory for a role.
|
||||||
func getRoleHome(role Role, rig, polecat, townRoot string) string {
|
func getRoleHome(role Role, rig, polecat, townRoot string) string {
|
||||||
switch role {
|
switch role {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ func (c *PatrolMoleculesExistCheck) Fix(ctx *CheckContext) error {
|
|||||||
"--title="+mol,
|
"--title="+mol,
|
||||||
"--description="+desc,
|
"--description="+desc,
|
||||||
"--priority=2",
|
"--priority=2",
|
||||||
|
"--actor=gt-doctor",
|
||||||
)
|
)
|
||||||
cmd.Dir = rigPath
|
cmd.Dir = rigPath
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ func (r *Router) Send(msg *Message) error {
|
|||||||
args = append(args, "--labels", strings.Join(labels, ","))
|
args = append(args, "--labels", strings.Join(labels, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add actor for attribution (sender identity)
|
||||||
|
args = append(args, "--actor", msg.From)
|
||||||
|
|
||||||
// Add --wisp flag for ephemeral messages (stored in single DB, filtered from JSONL export)
|
// Add --wisp flag for ephemeral messages (stored in single DB, filtered from JSONL export)
|
||||||
if r.shouldBeWisp(msg) {
|
if r.shouldBeWisp(msg) {
|
||||||
args = append(args, "--wisp")
|
args = append(args, "--wisp")
|
||||||
|
|||||||
@@ -534,6 +534,7 @@ func (m *Manager) seedPatrolMoleculesManually(rigPath string) error {
|
|||||||
"--title="+mol.title,
|
"--title="+mol.title,
|
||||||
"--description="+mol.desc,
|
"--description="+mol.desc,
|
||||||
"--priority=2",
|
"--priority=2",
|
||||||
|
"--actor=gt-rig-init",
|
||||||
)
|
)
|
||||||
cmd.Dir = rigPath
|
cmd.Dir = rigPath
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user