feat(namepool): auto-select theme per rig based on name hash
Each rig now gets a deterministic theme based on its name instead of always defaulting to mad-max. Uses a prime multiplier hash (×31) for good distribution across themes. Same rig name always gets the same theme. Users can still override with `gt namepool set`. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
fbc67e89e1
commit
74050cd0ab
@@ -529,8 +529,9 @@ func TestReconcilePoolWith(t *testing.T) {
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
// Create rig and manager (nil tmux for unit test)
|
||||
// Use "myrig" which hashes to mad-max theme
|
||||
r := &rig.Rig{
|
||||
Name: "testrig",
|
||||
Name: "myrig",
|
||||
Path: tmpDir,
|
||||
}
|
||||
m := NewManager(r, nil, nil)
|
||||
@@ -591,8 +592,9 @@ func TestReconcilePoolWith_Allocation(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
// Use "myrig" which hashes to mad-max theme
|
||||
r := &rig.Rig{
|
||||
Name: "testrig",
|
||||
Name: "myrig",
|
||||
Path: tmpDir,
|
||||
}
|
||||
m := NewManager(r, nil, nil)
|
||||
@@ -627,8 +629,9 @@ func TestReconcilePoolWith_OrphanDoesNotBlockAllocation(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
// Use "myrig" which hashes to mad-max theme
|
||||
r := &rig.Rig{
|
||||
Name: "testrig",
|
||||
Name: "myrig",
|
||||
Path: tmpDir,
|
||||
}
|
||||
m := NewManager(r, nil, nil)
|
||||
|
||||
@@ -103,7 +103,7 @@ type NamePool struct {
|
||||
func NewNamePool(rigPath, rigName string) *NamePool {
|
||||
return &NamePool{
|
||||
RigName: rigName,
|
||||
Theme: DefaultTheme,
|
||||
Theme: ThemeForRig(rigName),
|
||||
InUse: make(map[string]bool),
|
||||
OverflowNext: DefaultPoolSize + 1,
|
||||
MaxSize: DefaultPoolSize,
|
||||
@@ -352,6 +352,21 @@ func ListThemes() []string {
|
||||
return themes
|
||||
}
|
||||
|
||||
// ThemeForRig returns a deterministic theme for a rig based on its name.
|
||||
// This provides variety across rigs without requiring manual configuration.
|
||||
func ThemeForRig(rigName string) string {
|
||||
themes := ListThemes()
|
||||
if len(themes) == 0 {
|
||||
return DefaultTheme
|
||||
}
|
||||
// Hash using prime multiplier for better distribution
|
||||
var hash uint32
|
||||
for _, b := range []byte(rigName) {
|
||||
hash = hash*31 + uint32(b)
|
||||
}
|
||||
return themes[hash%uint32(len(themes))]
|
||||
}
|
||||
|
||||
// GetThemeNames returns the names in a specific theme.
|
||||
func GetThemeNames(theme string) ([]string, error) {
|
||||
if names, ok := BuiltinThemes[theme]; ok {
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestNamePool_Allocate(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// First allocation should be first themed name (furiosa)
|
||||
name, err := pool.Allocate()
|
||||
@@ -41,7 +41,7 @@ func TestNamePool_Release(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// Allocate first two
|
||||
name1, _ := pool.Allocate()
|
||||
@@ -68,7 +68,7 @@ func TestNamePool_PrefersOrder(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// Allocate first 5
|
||||
for i := 0; i < 5; i++ {
|
||||
@@ -209,7 +209,7 @@ func TestNamePool_Reconcile(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// Simulate existing polecats from filesystem
|
||||
existing := []string{"slit", "valkyrie", "some-other-name"}
|
||||
@@ -234,7 +234,7 @@ func TestNamePool_IsPoolName(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -263,7 +263,7 @@ func TestNamePool_ActiveNames(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
pool.Allocate() // furiosa
|
||||
pool.Allocate() // nux
|
||||
@@ -287,7 +287,7 @@ func TestNamePool_MarkInUse(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// Mark some slots as in use
|
||||
pool.MarkInUse("dementus")
|
||||
@@ -417,7 +417,7 @@ func TestNamePool_Reset(t *testing.T) {
|
||||
}
|
||||
defer func() { _ = os.RemoveAll(tmpDir) }()
|
||||
|
||||
pool := NewNamePool(tmpDir, "testrig")
|
||||
pool := NewNamePoolWithConfig(tmpDir, "testrig", "mad-max", nil, DefaultPoolSize)
|
||||
|
||||
// Allocate several names
|
||||
for i := 0; i < 10; i++ {
|
||||
@@ -441,3 +441,24 @@ func TestNamePool_Reset(t *testing.T) {
|
||||
t.Errorf("expected furiosa after reset, got %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestThemeForRig(t *testing.T) {
|
||||
// Different rigs should get different themes (with high probability)
|
||||
themes := make(map[string]bool)
|
||||
for _, rigName := range []string{"gastown", "beads", "myproject", "webapp"} {
|
||||
themes[ThemeForRig(rigName)] = true
|
||||
}
|
||||
// Should have at least 2 different themes across 4 rigs
|
||||
if len(themes) < 2 {
|
||||
t.Errorf("expected variety in themes, got only %d unique theme(s)", len(themes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestThemeForRigDeterministic(t *testing.T) {
|
||||
// Same rig name should always get same theme
|
||||
theme1 := ThemeForRig("myrig")
|
||||
theme2 := ThemeForRig("myrig")
|
||||
if theme1 != theme2 {
|
||||
t.Errorf("theme not deterministic: got %q and %q", theme1, theme2)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user