diff --git a/internal/beads/beads.go b/internal/beads/beads.go index abfa161d..99da8da3 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -538,17 +538,27 @@ func (b *Beads) Update(id string, opts UpdateOptions) error { } // Close closes one or more issues. +// If CLAUDE_SESSION_ID is set in the environment, it is passed to bd close +// for work attribution tracking (see decision 009-session-events-architecture.md). func (b *Beads) Close(ids ...string) error { if len(ids) == 0 { return nil } args := append([]string{"close"}, ids...) + + // Pass session ID for work attribution if available + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + args = append(args, "--session="+sessionID) + } + _, err := b.run(args...) return err } // CloseWithReason closes one or more issues with a reason. +// If CLAUDE_SESSION_ID is set in the environment, it is passed to bd close +// for work attribution tracking (see decision 009-session-events-architecture.md). func (b *Beads) CloseWithReason(reason string, ids ...string) error { if len(ids) == 0 { return nil @@ -556,6 +566,12 @@ func (b *Beads) CloseWithReason(reason string, ids ...string) error { args := append([]string{"close"}, ids...) args = append(args, "--reason="+reason) + + // Pass session ID for work attribution if available + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + args = append(args, "--session="+sessionID) + } + _, err := b.run(args...) return err } diff --git a/internal/cmd/crew_lifecycle.go b/internal/cmd/crew_lifecycle.go index 5b5aafb8..c2398970 100644 --- a/internal/cmd/crew_lifecycle.go +++ b/internal/cmd/crew_lifecycle.go @@ -90,7 +90,11 @@ func runCrewRemove(cmd *cobra.Command, args []string) error { } prefix := beads.GetPrefixForRig(townRoot, r.Name) agentBeadID := beads.CrewBeadIDWithPrefix(prefix, r.Name, name) - closeCmd := exec.Command("bd", "close", agentBeadID, "--reason=Crew workspace removed") + closeArgs := []string{"close", agentBeadID, "--reason=Crew workspace removed"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Dir = r.Path // Run from rig directory for proper beads resolution if output, err := closeCmd.CombinedOutput(); err != nil { // Non-fatal: bead might not exist or already be closed diff --git a/internal/cmd/hook.go b/internal/cmd/hook.go index b660892a..0f7862d0 100644 --- a/internal/cmd/hook.go +++ b/internal/cmd/hook.go @@ -143,8 +143,12 @@ func runHook(cmd *cobra.Command, args []string) error { if !hookDryRun { if hasAttachment { // Close completed molecule bead (use bd close --force for pinned) - closeCmd := exec.Command("bd", "close", existing.ID, "--force", - "--reason=Auto-replaced by gt hook (molecule complete)") + closeArgs := []string{"close", existing.ID, "--force", + "--reason=Auto-replaced by gt hook (molecule complete)"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Stderr = os.Stderr if err := closeCmd.Run(); err != nil { return fmt.Errorf("closing completed bead %s: %w", existing.ID, err) diff --git a/internal/cmd/polecat.go b/internal/cmd/polecat.go index dfbf76b2..57e7def9 100644 --- a/internal/cmd/polecat.go +++ b/internal/cmd/polecat.go @@ -1573,7 +1573,11 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error { // Step 5: Close agent bead (if exists) agentBeadID := beads.PolecatBeadID(p.rigName, p.polecatName) - closeCmd := exec.Command("bd", "close", agentBeadID, "--reason=nuked") + closeArgs := []string{"close", agentBeadID, "--reason=nuked"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Dir = filepath.Join(p.r.Path, "mayor", "rig") if err := closeCmd.Run(); err != nil { // Non-fatal - agent bead might not exist diff --git a/internal/cmd/swarm.go b/internal/cmd/swarm.go index df92fb50..b2c41cbe 100644 --- a/internal/cmd/swarm.go +++ b/internal/cmd/swarm.go @@ -816,7 +816,11 @@ func runSwarmLand(cmd *cobra.Command, args []string) error { } // Close the swarm epic in beads - closeCmd := exec.Command("bd", "close", swarmID, "--reason", "Swarm landed to main") + closeArgs := []string{"close", swarmID, "--reason", "Swarm landed to main"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Dir = foundRig.BeadsPath() if err := closeCmd.Run(); err != nil { style.PrintWarning("couldn't close swarm epic in beads: %v", err) @@ -871,7 +875,11 @@ func runSwarmCancel(cmd *cobra.Command, args []string) error { } // Close the swarm epic in beads with cancelled reason - closeCmd := exec.Command("bd", "close", swarmID, "--reason", "Swarm cancelled") + closeArgs := []string{"close", swarmID, "--reason", "Swarm cancelled"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Dir = foundRig.BeadsPath() if err := closeCmd.Run(); err != nil { return fmt.Errorf("closing swarm: %w", err) diff --git a/internal/cmd/synthesis.go b/internal/cmd/synthesis.go index 655a0624..a5d3f5e4 100644 --- a/internal/cmd/synthesis.go +++ b/internal/cmd/synthesis.go @@ -321,7 +321,11 @@ func runSynthesisClose(cmd *cobra.Command, args []string) error { } // Close the convoy - closeCmd := exec.Command("bd", "close", convoyID, "--reason=synthesis complete") + closeArgs := []string{"close", convoyID, "--reason=synthesis complete"} + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + closeArgs = append(closeArgs, "--session="+sessionID) + } + closeCmd := exec.Command("bd", closeArgs...) closeCmd.Dir = townBeads closeCmd.Stderr = os.Stderr diff --git a/internal/mail/mailbox.go b/internal/mail/mailbox.go index e68fdb11..e4fc0108 100644 --- a/internal/mail/mailbox.go +++ b/internal/mail/mailbox.go @@ -313,7 +313,12 @@ func (m *Mailbox) markReadBeads(id string) error { // closeInDir closes a message in a specific beads directory. func (m *Mailbox) closeInDir(id, beadsDir string) error { - cmd := exec.Command("bd", "close", id) + args := []string{"close", id} + // Pass session ID for work attribution if available + if sessionID := os.Getenv("CLAUDE_SESSION_ID"); sessionID != "" { + args = append(args, "--session="+sessionID) + } + cmd := exec.Command("bd", args...) cmd.Dir = m.workDir cmd.Env = append(cmd.Environ(), "BEADS_DIR="+beadsDir)