From a5d9793ecd5a780bb0e9a33b5e16cbc64b2c90f5 Mon Sep 17 00:00:00 2001 From: grip Date: Sat, 3 Jan 2026 13:25:08 -0800 Subject: [PATCH] fix(agent): support hyphenated rig names in parseAgentIDFields (GH#868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complements the validation fix from c6fe9d71 - the parseAgentIDFields function now also scans right-to-left for known role tokens instead of using fixed position parsing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/agent.go | 31 +++++++++++++++++-------------- cmd/bd/agent_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/cmd/bd/agent.go b/cmd/bd/agent.go index 20a1f187..ec7bb424 100644 --- a/cmd/bd/agent.go +++ b/cmd/bd/agent.go @@ -781,7 +781,7 @@ func parseAgentIDFields(agentID string) (roleType, rig string) { rigLevelRoles := map[string]bool{"witness": true, "refinery": true} namedRoles := map[string]bool{"crew": true, "polecat": true} - // Case 1: Town-level roles (gt-mayor, gt-deacon) + // Case 1: Town-level roles (gt-mayor, gt-deacon) - single part after prefix if len(parts) == 1 { role := parts[0] if townLevelRoles[role] { @@ -790,20 +790,23 @@ func parseAgentIDFields(agentID string) (roleType, rig string) { return "", "" // Unknown format } - // Case 2: Rig-level roles (--witness, --refinery) - if len(parts) == 2 { - potentialRig, potentialRole := parts[0], parts[1] - if rigLevelRoles[potentialRole] { - return potentialRole, potentialRig - } - return "", "" // Unknown format - } + // For 2+ parts, scan from the right to find a known role. + // This allows rig names to contain hyphens (e.g., "my-project"). + for i := len(parts) - 1; i >= 0; i-- { + part := parts[i] - // Case 3: Named roles (--crew-, --polecat-) - if len(parts) >= 3 { - potentialRig, potentialRole := parts[0], parts[1] - if namedRoles[potentialRole] { - return potentialRole, potentialRig + // Check for rig-level role (witness, refinery) - must be at end + if rigLevelRoles[part] && i == len(parts)-1 { + // rig is everything before role + rig = strings.Join(parts[:i], "-") + return part, rig + } + + // Check for named role (crew, polecat) - must have something after (the name) + if namedRoles[part] && i < len(parts)-1 { + // rig is everything before role + rig = strings.Join(parts[:i], "-") + return part, rig } } diff --git a/cmd/bd/agent_test.go b/cmd/bd/agent_test.go index 72a56a1d..1be19cc6 100644 --- a/cmd/bd/agent_test.go +++ b/cmd/bd/agent_test.go @@ -101,6 +101,37 @@ func TestParseAgentIDFields(t *testing.T) { wantRoleType: "polecat", wantRig: "gastown", }, + // Hyphenated rig names (GH#868) + { + name: "polecat with hyphenated rig", + agentID: "gt-my-project-polecat-nux", + wantRoleType: "polecat", + wantRig: "my-project", + }, + { + name: "crew with hyphenated rig", + agentID: "bd-infra-dashboard-crew-alice", + wantRoleType: "crew", + wantRig: "infra-dashboard", + }, + { + name: "witness with hyphenated rig", + agentID: "gt-my-cool-rig-witness", + wantRoleType: "witness", + wantRig: "my-cool-rig", + }, + { + name: "refinery with hyphenated rig", + agentID: "bd-super-long-rig-name-refinery", + wantRoleType: "refinery", + wantRig: "super-long-rig-name", + }, + { + name: "polecat with multi-hyphen rig and name", + agentID: "gt-my-awesome-project-polecat-worker-1", + wantRoleType: "polecat", + wantRig: "my-awesome-project", + }, // Edge cases { name: "no hyphen",