fix(create): use agent-aware prefix extraction for agent beads
The generic ValidateIDFormat() used isLikelyHash() which treated 3-character suffixes like "nux" as valid hashes, causing agent IDs like "nx-nexus-polecat-nux" to extract prefix as "nx-nexus-polecat" instead of the correct "nx". Fix: For --type=agent, validate agent ID format first and use ExtractAgentPrefix() which correctly extracts prefix from the first hyphen for agent IDs. Fixes #591 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
gastown/crew/dennis
parent
2c00d6d203
commit
87f84c5fa6
@@ -400,9 +400,23 @@ var createCmd = &cobra.Command{
|
|||||||
|
|
||||||
// Validate explicit ID format if provided
|
// Validate explicit ID format if provided
|
||||||
if explicitID != "" {
|
if explicitID != "" {
|
||||||
requestedPrefix, err := validation.ValidateIDFormat(explicitID)
|
var requestedPrefix string
|
||||||
if err != nil {
|
var err error
|
||||||
FatalError("%v", err)
|
|
||||||
|
// For agent types, use agent-aware prefix extraction.
|
||||||
|
// This fixes the bug where 3-char polecat names like "nux" in
|
||||||
|
// "nx-nexus-polecat-nux" were incorrectly treated as hash suffixes,
|
||||||
|
// causing prefix to be extracted as "nx-nexus-polecat" instead of "nx".
|
||||||
|
if issueType == "agent" {
|
||||||
|
if err := validation.ValidateAgentID(explicitID); err != nil {
|
||||||
|
FatalError("invalid agent ID: %v", err)
|
||||||
|
}
|
||||||
|
requestedPrefix = validation.ExtractAgentPrefix(explicitID)
|
||||||
|
} else {
|
||||||
|
requestedPrefix, err = validation.ValidateIDFormat(explicitID)
|
||||||
|
if err != nil {
|
||||||
|
FatalError("%v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate prefix matches database prefix
|
// Validate prefix matches database prefix
|
||||||
@@ -431,13 +445,6 @@ var createCmd = &cobra.Command{
|
|||||||
if err := validation.ValidatePrefixWithAllowed(requestedPrefix, dbPrefix, allowedPrefixes, forceCreate); err != nil {
|
if err := validation.ValidatePrefixWithAllowed(requestedPrefix, dbPrefix, allowedPrefixes, forceCreate); err != nil {
|
||||||
FatalError("%v", err)
|
FatalError("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate agent ID pattern if type is agent
|
|
||||||
if issueType == "agent" {
|
|
||||||
if err := validation.ValidateAgentID(explicitID); err != nil {
|
|
||||||
FatalError("invalid agent ID: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var externalRefPtr *string
|
var externalRefPtr *string
|
||||||
|
|||||||
@@ -170,6 +170,22 @@ func isNamedRole(s string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractAgentPrefix extracts the prefix from an agent ID.
|
||||||
|
// Agent IDs have the format: prefix-rig-role-name or prefix-role
|
||||||
|
// The prefix is always the part before the first hyphen.
|
||||||
|
// Examples:
|
||||||
|
// - "gt-gastown-polecat-nux" -> "gt"
|
||||||
|
// - "nx-nexus-polecat-nux" -> "nx"
|
||||||
|
// - "gt-mayor" -> "gt"
|
||||||
|
// - "bd-beads-witness" -> "bd"
|
||||||
|
func ExtractAgentPrefix(id string) string {
|
||||||
|
hyphenIdx := strings.Index(id, "-")
|
||||||
|
if hyphenIdx <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return id[:hyphenIdx]
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateAgentID validates that an agent ID follows the expected pattern.
|
// ValidateAgentID validates that an agent ID follows the expected pattern.
|
||||||
// Canonical format: prefix-rig-role-name
|
// Canonical format: prefix-rig-role-name
|
||||||
// Patterns:
|
// Patterns:
|
||||||
|
|||||||
@@ -404,3 +404,43 @@ func TestValidateAgentID(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractAgentPrefix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
id string
|
||||||
|
wantPrefix string
|
||||||
|
}{
|
||||||
|
// Town-level agents
|
||||||
|
{"mayor", "gt-mayor", "gt"},
|
||||||
|
{"deacon", "gt-deacon", "gt"},
|
||||||
|
{"bd mayor", "bd-mayor", "bd"},
|
||||||
|
|
||||||
|
// Per-rig agents
|
||||||
|
{"witness", "gt-gastown-witness", "gt"},
|
||||||
|
{"refinery", "bd-beads-refinery", "bd"},
|
||||||
|
|
||||||
|
// Named agents - the bug case
|
||||||
|
{"polecat 3-char name", "nx-nexus-polecat-nux", "nx"},
|
||||||
|
{"polecat regular", "gt-gastown-polecat-phoenix", "gt"},
|
||||||
|
{"crew", "gt-beads-crew-dave", "gt"},
|
||||||
|
|
||||||
|
// Hyphenated rig names
|
||||||
|
{"hyphenated rig", "gt-my-project-witness", "gt"},
|
||||||
|
{"multi-hyphen rig polecat", "bd-my-cool-app-polecat-bob", "bd"},
|
||||||
|
|
||||||
|
// Edge cases
|
||||||
|
{"no hyphen", "nohyphen", ""},
|
||||||
|
{"empty", "", ""},
|
||||||
|
{"just prefix", "gt-", "gt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := ExtractAgentPrefix(tt.id)
|
||||||
|
if got != tt.wantPrefix {
|
||||||
|
t.Errorf("ExtractAgentPrefix(%q) = %q, want %q", tt.id, got, tt.wantPrefix)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user