From cb69f1c1543d9ddb13dede00422d950d9ff6a876 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 29 Dec 2025 23:42:14 -0800 Subject: [PATCH] feat: Show actor on pinned/status change events (gt-1ydd9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .beads/issues.jsonl | 1 + cmd/bd/activity.go | 11 ++++++++--- internal/rpc/server_core.go | 1 + internal/rpc/server_issues_epics.go | 17 +++++++++++++++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 8c20366b..213d71e6 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -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"} diff --git a/cmd/bd/activity.go b/cmd/bd/activity.go index 8985c649..b137873e 100644 --- a/cmd/bd/activity.go +++ b/cmd/bd/activity.go @@ -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) } diff --git a/internal/rpc/server_core.go b/internal/rpc/server_core.go index 5fc6aee0..91650451 100644 --- a/internal/rpc/server_core.go +++ b/internal/rpc/server_core.go @@ -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) diff --git a/internal/rpc/server_issues_epics.go b/internal/rpc/server_issues_epics.go index 1788fd31..dc4de216 100644 --- a/internal/rpc/server_issues_epics.go +++ b/internal/rpc/server_issues_epics.go @@ -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, + }) } }