From 8fab565f35a54a2ec4e1fd8cbcd6d1c6d2e2ba16 Mon Sep 17 00:00:00 2001 From: Eduardo Koloma Jr <5549816+olpie101@users.noreply.github.com> Date: Sun, 25 Jan 2026 02:10:12 +0100 Subject: [PATCH] fix(gate): ensure add-waiter command functions (#1265) * fix(gate): use GateWait RPC for add-waiter command bd gate add-waiter was calling Update RPC which rejects waiters field (not in allowedUpdateFields). Changed to use existing GateWait RPC that handles waiters correctly. Co-Authored-By: Claude Opus 4.5 * fix(storage): allow waiters field in UpdateIssue Add waiters to allowedUpdateFields whitelist and handle JSON serialization for the array field. This enables bd gate add-waiter to work in direct mode (--no-daemon). Co-Authored-By: Claude Opus 4.5 * fix(storage/dolt): allow waiters field in UpdateIssue Mirror the SQLite fix: add waiters to allowed fields and handle JSON serialization for the array field. Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- cmd/bd/gate.go | 7 ++++--- internal/storage/dolt/issues.go | 11 +++++++++-- internal/storage/sqlite/queries.go | 10 +++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/cmd/bd/gate.go b/cmd/bd/gate.go index d52f286f..20e499c5 100644 --- a/cmd/bd/gate.go +++ b/cmd/bd/gate.go @@ -261,11 +261,12 @@ This is used by 'gt done --phase-complete' to register for gate wake notificatio // Update the gate if daemonClient != nil { - updateArgs := &rpc.UpdateArgs{ + // Use GateWait RPC which handles waiters correctly (bypasses allowedUpdateFields) + gateWaitArgs := &rpc.GateWaitArgs{ ID: gateID, - Waiters: newWaiters, + Waiters: []string{waiter}, // GateWait handles deduplication internally } - resp, uerr := daemonClient.Update(updateArgs) + resp, uerr := daemonClient.GateWait(gateWaitArgs) if uerr != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", uerr) os.Exit(1) diff --git a/internal/storage/dolt/issues.go b/internal/storage/dolt/issues.go index e911973a..915935bc 100644 --- a/internal/storage/dolt/issues.go +++ b/internal/storage/dolt/issues.go @@ -251,7 +251,14 @@ func (s *DoltStore) UpdateIssue(ctx context.Context, id string, updates map[stri columnName = "ephemeral" } setClauses = append(setClauses, fmt.Sprintf("`%s` = ?", columnName)) - args = append(args, value) + + // Handle JSON serialization for array fields stored as TEXT + if key == "waiters" { + waitersJSON, _ := json.Marshal(value) + args = append(args, string(waitersJSON)) + } else { + args = append(args, value) + } } // Auto-manage closed_at @@ -631,7 +638,7 @@ func isAllowedUpdateField(key string) bool { "hook_bead": true, "role_bead": true, "agent_state": true, "last_activity": true, "role_type": true, "rig": true, "mol_type": true, "event_category": true, "event_actor": true, "event_target": true, "event_payload": true, - "due_at": true, "defer_until": true, "await_id": true, + "due_at": true, "defer_until": true, "await_id": true, "waiters": true, } return allowed[key] } diff --git a/internal/storage/sqlite/queries.go b/internal/storage/sqlite/queries.go index a2ae1613..6a8b2dc1 100644 --- a/internal/storage/sqlite/queries.go +++ b/internal/storage/sqlite/queries.go @@ -804,6 +804,7 @@ var allowedUpdateFields = map[string]bool{ "defer_until": true, // Gate fields (bd-z6kw: support await_id updates for gate discovery) "await_id": true, + "waiters": true, } // validatePriority validates a priority value @@ -913,7 +914,14 @@ func (s *SQLiteStorage) UpdateIssue(ctx context.Context, id string, updates map[ columnName = "ephemeral" } setClauses = append(setClauses, fmt.Sprintf("%s = ?", columnName)) - args = append(args, value) + + // Handle JSON serialization for array fields stored as TEXT + if key == "waiters" { + waitersJSON, _ := json.Marshal(value) + args = append(args, string(waitersJSON)) + } else { + args = append(args, value) + } } // Auto-manage closed_at when status changes (enforce invariant)