feat: Show actor on pinned/status change events (gt-1ydd9)

- Add Actor field to MutationEvent struct
- Use new assignee from update args instead of old issue state
- Include actor (who performed the action) in mutation events
- Display actor in bd activity output, falling back to assignee

When pinning/updating status, the activity feed now shows who performed
the action (e.g., "@gastown/crew/jack") instead of showing nothing.

🤖 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 23:42:14 -08:00
parent e5cf9b3199
commit cb69f1c154
4 changed files with 25 additions and 5 deletions

View File

@@ -359,6 +359,7 @@
{"id":"bd-g7eq","title":"Agent beads: structured labels for filtering","description":"## Agent Beads Need Queryable Labels\n\nCurrently agent beads have role_type/rig in description text, not as labels. This breaks @group resolution in gt mail.\n\n## Current State\n\n```json\n{\n \"id\": \"gt-gastown-witness\",\n \"issue_type\": \"agent\",\n \"description\": \"...\\\\nrole_type: witness\\\\nrig: gastown\\\\n...\"\n}\n```\n\nCannot query: `bd list --type=agent --label=role_type:witness` returns nothing.\n\n## Required\n\nAgent bead creation should add labels:\n- `role_type:\u003ctype\u003e` (witness, refinery, crew, polecat, dog, mayor, deacon)\n- `rig:\u003crig\u003e` (gastown, beads, or \"town\" for town-level)\n\n## Where to Fix\n\ngt polecat/crew/agent creation commands should add labels:\n```bash\nbd create --type=agent ... --labels=\"role_type:witness,rig:gastown\"\n```\n\n## Queries Enabled\n\n```bash\nbd list --type=agent --label=role_type:witness # All witnesses\nbd list --type=agent --label=rig:gastown # All gastown agents\nbd list --type=agent --label=role_type:dog # All dogs\n```\n\n## Acceptance\n- New agent beads created with role_type/rig labels\n- Existing agent beads backfilled (one-time script)\n- @group patterns work in gt mail router","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-29T20:47:33.950733-08:00","created_by":"gastown/crew/joe","updated_at":"2025-12-29T21:16:00.746383-08:00","closed_at":"2025-12-29T21:16:00.746383-08:00","close_reason":"Implemented: role_type/rig labels for agent beads, auto-labeling on create/update, backfill command"}
{"id":"bd-g9eu","title":"Investigate TestRoutingIntegration failure","description":"TestRoutingIntegration/maintainer_with_SSH_remote failed during pre-commit check with \"expected role maintainer, got contributor\".\nThis occurred while running `go test -short ./...` on darwin/arm64.\nThe failure appears unrelated to storage/sqlite changes.\nNeed to investigate if this is a flaky test or environmental issue.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-20T15:55:19.337094-08:00","updated_at":"2025-12-23T23:49:49.11706-08:00","closed_at":"2025-12-23T23:49:49.11706-08:00"}
{"id":"bd-gfo3","title":"Merge: bd-ykd9","description":"branch: polecat/Doctor\ntarget: main\nsource_issue: bd-ykd9\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-23T13:34:43.778808-08:00","updated_at":"2025-12-23T19:12:08.353427-08:00","closed_at":"2025-12-23T19:12:08.353427-08:00"}
{"id":"bd-gigi","title":"bd admin compact --older-than=0 ignores the flag, uses default 30 days","description":"When running bd admin compact --prune --older-than=0, the output says 'No expired tombstones to prune (TTL: 30 days)' - the flag value is ignored.\n\nRoot cause: In runCompactPrune() in compact_tombstone.go:180, the check 'if compactOlderThan \u003e 0' means that --older-than=0 is treated the same as not passing the flag, defaulting to 30 days.\n\nFix: Change the default flag value in compact.go:785 to -1 (sentinel for 'use default'), and treat 0 as 'expire all tombstones' by passing a negative TTL to pruneExpiredTombstones().\n\nMoved from gt-65gwa (gastown) - was filed in wrong rig.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-29T23:34:41.255352-08:00","created_by":"gastown/polecats/dementus","updated_at":"2025-12-29T23:34:41.255352-08:00"}
{"id":"bd-gjla","title":"Test Thread","description":"Initial message for threading test","status":"tombstone","priority":2,"issue_type":"message","created_at":"2025-12-16T18:19:51.704324-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","dependencies":[{"issue_id":"bd-gjla","depends_on_id":"bd-f5cc","type":"duplicates","created_at":"2025-12-18T13:45:31.137191-08:00","created_by":"migration"}],"deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"message"}
{"id":"bd-gocx","title":"Run bump-version.sh 0.32.1","description":"Execute ./scripts/bump-version.sh 0.32.1 to update all version references","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:53:18.470174-08:00","updated_at":"2025-12-20T21:54:54.500836-08:00","closed_at":"2025-12-20T21:54:54.500836-08:00","dependencies":[{"issue_id":"bd-gocx","depends_on_id":"bd-an4s","type":"parent-child","created_at":"2025-12-20T21:53:18.471793-08:00","created_by":"daemon"},{"issue_id":"bd-gocx","depends_on_id":"bd-x3j8","type":"blocks","created_at":"2025-12-20T21:53:29.688436-08:00","created_by":"daemon"}]}
{"id":"bd-gqxd","title":"Enrich MutationEvent with title and assignee","description":"Current MutationEvent only has IssueID, no context. Add Title and Assignee fields so activity feeds can display meaningful info without extra lookups. Emit these fields when creating mutation events in server_core.go.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T16:26:34.907259-08:00","updated_at":"2025-12-23T16:39:39.229462-08:00","closed_at":"2025-12-23T16:39:39.229462-08:00"}

View File

@@ -39,6 +39,7 @@ type ActivityEvent struct {
NewStatus string `json:"new_status,omitempty"`
ParentID string `json:"parent_id,omitempty"`
StepCount int `json:"step_count,omitempty"`
Actor string `json:"actor,omitempty"`
}
var activityCmd = &cobra.Command{
@@ -299,6 +300,7 @@ func formatEvent(e rpc.MutationEvent) ActivityEvent {
NewStatus: e.NewStatus,
ParentID: e.ParentID,
StepCount: e.StepCount,
Actor: e.Actor,
}
}
@@ -340,7 +342,7 @@ func getEventDisplay(e rpc.MutationEvent) (symbol, message string) {
}
}
// buildEventContext creates a context string from title and assignee
// buildEventContext creates a context string from title and actor/assignee
func buildEventContext(e rpc.MutationEvent) string {
var parts []string
@@ -350,8 +352,11 @@ func buildEventContext(e rpc.MutationEvent) string {
parts = append(parts, title)
}
// Add assignee if present
if e.Assignee != "" {
// For status changes, prefer showing actor (who performed the action)
// For other events, show assignee
if e.Actor != "" {
parts = append(parts, "@"+e.Actor)
} else if e.Assignee != "" {
parts = append(parts, "@"+e.Assignee)
}

View File

@@ -84,6 +84,7 @@ type MutationEvent struct {
IssueID string // e.g., "bd-42"
Title string // Issue title for display context (may be empty for some operations)
Assignee string // Issue assignee for display context (may be empty)
Actor string // Who performed the action (may differ from assignee)
Timestamp time.Time
// Optional metadata for richer events (used by status, bonded, etc.)
OldStatus string `json:"old_status,omitempty"` // Previous status (for status events)

View File

@@ -613,18 +613,31 @@ func (s *Server) handleUpdate(req *Request) Response {
// Emit mutation event for event-driven daemon (only if any updates or label/parent operations were performed)
if len(updates) > 0 || len(updateArgs.SetLabels) > 0 || len(updateArgs.AddLabels) > 0 || len(updateArgs.RemoveLabels) > 0 || updateArgs.Parent != nil {
// Determine effective assignee: use new assignee from update if provided, otherwise use existing
effectiveAssignee := issue.Assignee
if updateArgs.Assignee != nil && *updateArgs.Assignee != "" {
effectiveAssignee = *updateArgs.Assignee
}
// Check if this was a status change - emit rich MutationStatus event
if updateArgs.Status != nil && *updateArgs.Status != string(issue.Status) {
s.emitRichMutation(MutationEvent{
Type: MutationStatus,
IssueID: updateArgs.ID,
Title: issue.Title,
Assignee: issue.Assignee,
Assignee: effectiveAssignee,
Actor: actor,
OldStatus: string(issue.Status),
NewStatus: *updateArgs.Status,
})
} else {
s.emitMutation(MutationUpdate, updateArgs.ID, issue.Title, issue.Assignee)
s.emitRichMutation(MutationEvent{
Type: MutationUpdate,
IssueID: updateArgs.ID,
Title: issue.Title,
Assignee: effectiveAssignee,
Actor: actor,
})
}
}