fix(polecat): exclude reserved infrastructure agent names from allocator (#837)

The polecat name allocator was assigning reserved infrastructure agent
names like 'witness' to polecats. Added ReservedInfraAgentNames map
containing witness, mayor, deacon, and refinery. Modified getNames()
to filter these from all themes and custom name lists.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Advaya Krishna
2026-01-21 10:30:53 -08:00
committed by GitHub
parent 535647cefc
commit 3afd1a1dcd
2 changed files with 94 additions and 7 deletions

View File

@@ -22,6 +22,15 @@ const (
DefaultTheme = "mad-max"
)
// ReservedInfraAgentNames contains names reserved for infrastructure agents.
// These names must never be allocated to polecats.
var ReservedInfraAgentNames = map[string]bool{
"witness": true,
"mayor": true,
"deacon": true,
"refinery": true,
}
// Built-in themes with themed polecat names.
var BuiltinThemes = map[string][]string{
"mad-max": {
@@ -132,19 +141,34 @@ func NewNamePoolWithConfig(rigPath, rigName, theme string, customNames []string,
}
// getNames returns the list of names to use for the pool.
// Reserved infrastructure agent names are filtered out.
func (p *NamePool) getNames() []string {
var names []string
// Custom names take precedence
if len(p.CustomNames) > 0 {
return p.CustomNames
names = p.CustomNames
} else if themeNames, ok := BuiltinThemes[p.Theme]; ok {
// Look up built-in theme
names = themeNames
} else {
// Fall back to default theme
names = BuiltinThemes[DefaultTheme]
}
// Look up built-in theme
if names, ok := BuiltinThemes[p.Theme]; ok {
return names
}
// Filter out reserved infrastructure agent names
return filterReservedNames(names)
}
// Fall back to default theme
return BuiltinThemes[DefaultTheme]
// filterReservedNames removes reserved infrastructure agent names from a name list.
func filterReservedNames(names []string) []string {
filtered := make([]string, 0, len(names))
for _, name := range names {
if !ReservedInfraAgentNames[name] {
filtered = append(filtered, name)
}
}
return filtered
}
// Load loads the pool state from disk.

View File

@@ -462,3 +462,66 @@ func TestThemeForRigDeterministic(t *testing.T) {
t.Errorf("theme not deterministic: got %q and %q", theme1, theme2)
}
}
func TestNamePool_ReservedNamesExcluded(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
if err != nil {
t.Fatal(err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
// Test all themes to ensure reserved names are excluded
for themeName := range BuiltinThemes {
pool := NewNamePoolWithConfig(tmpDir, "testrig", themeName, nil, 100)
// Allocate all available names (up to 100)
allocated := make(map[string]bool)
for i := 0; i < 100; i++ {
name, err := pool.Allocate()
if err != nil {
t.Fatalf("Allocate error: %v", err)
}
allocated[name] = true
}
// Verify no reserved names were allocated
for reserved := range ReservedInfraAgentNames {
if allocated[reserved] {
t.Errorf("theme %q allocated reserved name %q", themeName, reserved)
}
}
pool.Reset()
}
}
func TestNamePool_ReservedNamesInCustomNames(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "namepool-test-*")
if err != nil {
t.Fatal(err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
// Custom names that include reserved names should have them filtered out
custom := []string{"alpha", "witness", "beta", "mayor", "gamma"}
pool := NewNamePoolWithConfig(tmpDir, "testrig", "", custom, 10)
// Allocate all names
allocated := make(map[string]bool)
for i := 0; i < 5; i++ {
name, _ := pool.Allocate()
allocated[name] = true
}
// Should only get alpha, beta, gamma (3 non-reserved names)
// Then overflow names for the remaining allocations
if allocated["witness"] {
t.Error("allocated reserved name 'witness' from custom names")
}
if allocated["mayor"] {
t.Error("allocated reserved name 'mayor' from custom names")
}
if !allocated["alpha"] || !allocated["beta"] || !allocated["gamma"] {
t.Errorf("expected alpha, beta, gamma to be allocated, got %v", allocated)
}
}