From d69bf4faa8a52eb9c50043c4233756dd55f2c0ea Mon Sep 17 00:00:00 2001 From: nux Date: Tue, 13 Jan 2026 00:46:50 -0800 Subject: [PATCH] Fix bd gate list: Separate closed gates into own section Previously, displayGates() always showed 'Open Gates' header even when closed gates were included via --all flag. Also, closed gates would appear mixed with open gates under the misleading 'Open Gates' header. Changes: - Modified displayGates() to accept showAll parameter - Separates gates into 'Open Gates' and 'Closed Gates' sections - Closed gates only shown when --all flag is used - Fixed handleGateList RPC handler to use ExcludeStatus instead of Status filter for consistency with CLI behavior Fixes gas-town issue go-47m --- cmd/bd/gate.go | 90 +++++++++++++++++++---------- internal/rpc/server_issues_epics.go | 4 +- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/cmd/bd/gate.go b/cmd/bd/gate.go index f3308301..b08ac73e 100644 --- a/cmd/bd/gate.go +++ b/cmd/bd/gate.go @@ -101,7 +101,7 @@ By default, shows only open gates. Use --all to include closed gates.`, return } - displayGates(issues) + displayGates(issues, allFlag) return } @@ -117,56 +117,86 @@ By default, shows only open gates. Use --all to include closed gates.`, return } - displayGates(issues) + displayGates(issues, allFlag) }, } -// displayGates formats and displays gate issues -func displayGates(gates []*types.Issue) { +// displayGates formats and displays gate issues, separating open and closed gates +func displayGates(gates []*types.Issue, showAll bool) { if len(gates) == 0 { fmt.Println("No gates found.") return } - fmt.Printf("\n%s Open Gates (%d):\n\n", ui.RenderAccent("⏳"), len(gates)) - + // Separate open and closed gates + var openGates, closedGates []*types.Issue for _, gate := range gates { - statusSym := "○" if gate.Status == types.StatusClosed { - statusSym = "●" + closedGates = append(closedGates, gate) + } else { + openGates = append(openGates, gate) } + } - // Format gate info - gateInfo := gate.AwaitType - if gate.AwaitID != "" { - gateInfo = fmt.Sprintf("%s %s", gate.AwaitType, gate.AwaitID) + // Display open gates + if len(openGates) > 0 { + fmt.Printf("\n%s Open Gates (%d):\n\n", ui.RenderAccent("⏳"), len(openGates)) + for _, gate := range openGates { + displaySingleGate(gate) } + } - // Format timeout if present - timeoutStr := "" - if gate.Timeout > 0 { - timeoutStr = fmt.Sprintf(" (timeout: %s)", gate.Timeout) + // Display closed gates only if --all was used + if showAll && len(closedGates) > 0 { + fmt.Printf("\n%s Closed Gates (%d):\n\n", ui.RenderMuted("●"), len(closedGates)) + for _, gate := range closedGates { + displaySingleGate(gate) } + } - // Find blocked step from ID (gate ID format: parent.gate-stepid) - blockedStep := "" - if strings.Contains(gate.ID, ".gate-") { - parts := strings.Split(gate.ID, ".gate-") - if len(parts) == 2 { - blockedStep = fmt.Sprintf("%s.%s", parts[0], parts[1]) - } - } - - fmt.Printf("%s %s - %s%s\n", statusSym, ui.RenderID(gate.ID), gateInfo, timeoutStr) - if blockedStep != "" { - fmt.Printf(" Blocks: %s\n", blockedStep) - } - fmt.Println() + if len(openGates) == 0 && (!showAll || len(closedGates) == 0) { + fmt.Println("No gates found.") + return } fmt.Printf("To resolve a gate: bd close \n") } +// displaySingleGate formats and displays a single gate issue +func displaySingleGate(gate *types.Issue) { + statusSym := "○" + if gate.Status == types.StatusClosed { + statusSym = "●" + } + + // Format gate info + gateInfo := gate.AwaitType + if gate.AwaitID != "" { + gateInfo = fmt.Sprintf("%s %s", gate.AwaitType, gate.AwaitID) + } + + // Format timeout if present + timeoutStr := "" + if gate.Timeout > 0 { + timeoutStr = fmt.Sprintf(" (timeout: %s)", gate.Timeout) + } + + // Find blocked step from ID (gate ID format: parent.gate-stepid) + blockedStep := "" + if strings.Contains(gate.ID, ".gate-") { + parts := strings.Split(gate.ID, ".gate-") + if len(parts) == 2 { + blockedStep = fmt.Sprintf("%s.%s", parts[0], parts[1]) + } + } + + fmt.Printf("%s %s - %s%s\n", statusSym, ui.RenderID(gate.ID), gateInfo, timeoutStr) + if blockedStep != "" { + fmt.Printf(" Blocks: %s\n", blockedStep) + } + fmt.Println() +} + // gateAddWaiterCmd adds a waiter to a gate var gateAddWaiterCmd = &cobra.Command{ Use: "add-waiter ", diff --git a/internal/rpc/server_issues_epics.go b/internal/rpc/server_issues_epics.go index 01f80791..546d32bb 100644 --- a/internal/rpc/server_issues_epics.go +++ b/internal/rpc/server_issues_epics.go @@ -2108,9 +2108,9 @@ func (s *Server) handleGateList(req *Request) Response { filter := types.IssueFilter{ IssueType: &gateType, } + // By default, exclude closed gates (consistent with CLI behavior) if !args.All { - openStatus := types.StatusOpen - filter.Status = &openStatus + filter.ExcludeStatus = []types.Status{types.StatusClosed} } gates, err := store.SearchIssues(ctx, "", filter)