Skip to content

Commit 3991e2d

Browse files
waynemsmithclaude
andcommitted
Fix card list to be cross-board by default
- `card list` called `defaultBoard()`, injecting the configured default board into every query and silently scoping results to a single board - This made `--tag` / `--assignee` filters appear broken when matching cards lived on other boards — the same bug fixed for `search` in basecamp#114, in the sibling `card list` command - Fix: only add `board_ids[]` when `--board` is explicitly passed, so `card list` is cross-board by default (matching `search` and the API) Note: bare `fizzy card list` now spans all boards instead of the configured default board, consistent with the cross-board API default. Follow-up to basecamp#113 / basecamp#114. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 58812c2 commit 3991e2d

2 files changed

Lines changed: 76 additions & 8 deletions

File tree

internal/commands/card.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ var cardListCmd = &cobra.Command{
4444
return err
4545
}
4646

47-
boardID := defaultBoard(cardListBoard)
4847
columnFilter := strings.TrimSpace(cardListColumn)
4948
indexedByFilter := strings.TrimSpace(cardListIndexedBy)
5049
effectiveIndexedBy := indexedByFilter
@@ -53,8 +52,12 @@ var cardListCmd = &cobra.Command{
5352
path := "/cards.json"
5453

5554
var params []string
56-
if boardID != "" {
57-
params = append(params, "board_ids[]="+boardID)
55+
// card list is cross-board by default; only scope to a board when
56+
// explicitly requested via --board. Mirrors the search fix (#114) so
57+
// --tag/--assignee/etc. span all boards instead of being silently
58+
// restricted to the configured default board.
59+
if cardListBoard != "" {
60+
params = append(params, "board_ids[]="+cardListBoard)
5861
}
5962

6063
if columnFilter != "" {

internal/commands/card_test.go

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ func TestCardList(t *testing.T) {
7474

7575
SetTestModeWithSDK(mock)
7676
SetTestConfig("token", "account", "https://api.example.com")
77-
cfg.Board = "123"
7877
defer resetTest()
7978

8079
cardListColumn = "not-now"
@@ -83,7 +82,7 @@ func TestCardList(t *testing.T) {
8382

8483
assertExitCode(t, err, 0)
8584

86-
if mock.GetWithPaginationCalls[0].Path != "/cards.json?board_ids[]=123&indexed_by=not_now" {
85+
if mock.GetWithPaginationCalls[0].Path != "/cards.json?indexed_by=not_now" {
8786
t.Errorf("expected indexed_by filter, got '%s'", mock.GetWithPaginationCalls[0].Path)
8887
}
8988
})
@@ -190,7 +189,7 @@ func TestCardList(t *testing.T) {
190189
}
191190
})
192191

193-
t.Run("uses configured board as default filter", func(t *testing.T) {
192+
t.Run("does not inject configured default board (cross-board by default)", func(t *testing.T) {
194193
mock := NewMockClient()
195194
mock.GetWithPaginationResponse = &client.APIResponse{
196195
StatusCode: 200,
@@ -205,8 +204,74 @@ func TestCardList(t *testing.T) {
205204
err := cardListCmd.RunE(cardListCmd, []string{})
206205

207206
assertExitCode(t, err, 0)
208-
if mock.GetWithPaginationCalls[0].Path != "/cards.json?board_ids[]=999" {
209-
t.Errorf("expected path '/cards.json?board_ids[]=999', got '%s'", mock.GetWithPaginationCalls[0].Path)
207+
if mock.GetWithPaginationCalls[0].Path != "/cards.json" {
208+
t.Errorf("expected no board_ids injection, got '%s'", mock.GetWithPaginationCalls[0].Path)
209+
}
210+
})
211+
212+
t.Run("scopes to board only when --board is explicit", func(t *testing.T) {
213+
mock := NewMockClient()
214+
mock.GetWithPaginationResponse = &client.APIResponse{
215+
StatusCode: 200,
216+
Data: []any{},
217+
}
218+
219+
SetTestModeWithSDK(mock)
220+
SetTestConfig("token", "account", "https://api.example.com")
221+
cfg.Board = "999"
222+
defer resetTest()
223+
224+
cardListBoard = "explicit-board"
225+
err := cardListCmd.RunE(cardListCmd, []string{})
226+
cardListBoard = ""
227+
228+
assertExitCode(t, err, 0)
229+
if mock.GetWithPaginationCalls[0].Path != "/cards.json?board_ids[]=explicit-board" {
230+
t.Errorf("expected explicit board scope, got '%s'", mock.GetWithPaginationCalls[0].Path)
231+
}
232+
})
233+
234+
t.Run("tag filter works cross-board with default board set", func(t *testing.T) {
235+
mock := NewMockClient()
236+
mock.GetWithPaginationResponse = &client.APIResponse{
237+
StatusCode: 200,
238+
Data: []any{},
239+
}
240+
241+
SetTestModeWithSDK(mock)
242+
SetTestConfig("token", "account", "https://api.example.com")
243+
cfg.Board = "999"
244+
defer resetTest()
245+
246+
cardListTag = "tag-123"
247+
err := cardListCmd.RunE(cardListCmd, []string{})
248+
cardListTag = ""
249+
250+
assertExitCode(t, err, 0)
251+
if mock.GetWithPaginationCalls[0].Path != "/cards.json?tag_ids[]=tag-123" {
252+
t.Errorf("expected tag filter without board_ids, got '%s'", mock.GetWithPaginationCalls[0].Path)
253+
}
254+
})
255+
256+
t.Run("assignee filter works cross-board with default board set", func(t *testing.T) {
257+
mock := NewMockClient()
258+
mock.GetWithPaginationResponse = &client.APIResponse{
259+
StatusCode: 200,
260+
Data: []any{},
261+
}
262+
263+
SetTestModeWithSDK(mock)
264+
SetTestConfig("token", "account", "https://api.example.com")
265+
cfg.Board = "999"
266+
defer resetTest()
267+
268+
cardListAssignee = "user-456"
269+
err := cardListCmd.RunE(cardListCmd, []string{})
270+
cardListAssignee = ""
271+
272+
assertExitCode(t, err, 0)
273+
if mock.GetWithPaginationCalls[0].Path != "/cards.json?assignee_ids[]=user-456" {
274+
t.Errorf("expected assignee filter without board_ids, got '%s'", mock.GetWithPaginationCalls[0].Path)
210275
}
211276
})
212277

0 commit comments

Comments
 (0)