diff --git a/pkg/agentdrain/coordinator.go b/pkg/agentdrain/coordinator.go index e3238e76e12..4073a57b04c 100644 --- a/pkg/agentdrain/coordinator.go +++ b/pkg/agentdrain/coordinator.go @@ -52,25 +52,6 @@ func (c *Coordinator) AnalyzeEvent(evt AgentEvent) (*MatchResult, *AnomalyReport return m.AnalyzeEvent(evt) } -// Stages returns the list of stage names managed by this Coordinator. -func (c *Coordinator) Stages() []string { - c.mu.RLock() - defer c.mu.RUnlock() - stages := make([]string, 0, len(c.miners)) - for s := range c.miners { - stages = append(stages, s) - } - return stages -} - -// MinerForStage returns the Miner for the given stage, or false if not found. -func (c *Coordinator) MinerForStage(stage string) (*Miner, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - m, ok := c.miners[stage] - return m, ok -} - // AllClusters returns a map from stage name to the list of clusters in that miner. func (c *Coordinator) AllClusters() map[string][]Cluster { c.mu.RLock() diff --git a/pkg/agentdrain/miner_test.go b/pkg/agentdrain/miner_test.go index 1cf7ae3533a..35c48addb8f 100644 --- a/pkg/agentdrain/miner_test.go +++ b/pkg/agentdrain/miner_test.go @@ -154,32 +154,6 @@ func TestFlattenEvent(t *testing.T) { } } -func TestPreTrainTemplate(t *testing.T) { - cfg := DefaultConfig() - cfg.SimThreshold = 0.4 - m, err := NewMiner(cfg) - if err != nil { - t.Fatalf("NewMiner: %v", err) - } - - m.PreTrainTemplate("stage=tool_call tool=<*> latency_ms=<*>", 5) - if m.ClusterCount() != 1 { - t.Fatalf("PreTrainTemplate: expected 1 cluster, got %d", m.ClusterCount()) - } - - // A real line matching that pattern should hit the pre-trained cluster. - result, ok, err := m.Match("stage=tool_call tool=search latency_ms=<*>") - if err != nil { - t.Fatalf("Match: %v", err) - } - if !ok { - t.Error("Match: expected to find pre-trained cluster, got no match") - } - if result != nil && result.ClusterID == 0 { - t.Error("Match: expected valid cluster ID") - } -} - func TestSaveLoadJSON(t *testing.T) { cfg := DefaultConfig() m, err := NewMiner(cfg) @@ -260,17 +234,6 @@ func TestStageRouting(t *testing.T) { } } - for _, stage := range stages { - m, ok := coord.MinerForStage(stage) - if !ok { - t.Errorf("MinerForStage(%q): not found", stage) - continue - } - if m.ClusterCount() == 0 { - t.Errorf("stage %q: expected at least one cluster", stage) - } - } - // Unknown stage should error. _, err = coord.TrainEvent(AgentEvent{Stage: "unknown", Fields: map[string]string{}}) if err == nil { diff --git a/pkg/agentdrain/pretrain.go b/pkg/agentdrain/pretrain.go deleted file mode 100644 index d7fd96a25f3..00000000000 --- a/pkg/agentdrain/pretrain.go +++ /dev/null @@ -1,58 +0,0 @@ -package agentdrain - -import ( - "strings" - - "github.com/github/gh-aw/pkg/logger" -) - -var pretrainLog = logger.New("agentdrain:pretrain") - -// PreTrainTemplate seeds the miner with a known template string and a -// synthetic observation count. The template is tokenized but not masked, -// so callers should pass already-normalized templates. -func (m *Miner) PreTrainTemplate(template string, count int) { - tokens := Tokenize(template) - if len(tokens) == 0 { - return - } - pretrainLog.Printf("PreTraining template: tokenCount=%d, seedCount=%d", len(tokens), count) - - m.mu.Lock() - defer m.mu.Unlock() - - // Check for an existing identical template. - candidates := m.tree.search(tokens, m.cfg.Depth, m.cfg.ParamToken) - for _, id := range candidates { - c, ok := m.store.get(id) - if !ok { - continue - } - if strings.Join(c.Template, " ") == strings.Join(tokens, " ") { - c.Size += count - return - } - } - - // Create a new cluster pre-seeded with the desired count. - c := m.store.add(tokens, "") - c.Size = count - m.tree.addCluster(tokens, c.ID, m.cfg.Depth, m.cfg.MaxChildren, m.cfg.ParamToken) -} - -// PreTrainTemplates seeds the miner with a slice of template strings, each -// with an initial count of 1. -func (m *Miner) PreTrainTemplates(templates []string) { - pretrainLog.Printf("PreTraining with %d templates", len(templates)) - for _, t := range templates { - m.PreTrainTemplate(t, 1) - } -} - -// PreTrainTemplateCounts seeds the miner with a map of template strings to -// their initial observation counts. -func (m *Miner) PreTrainTemplateCounts(templates map[string]int) { - for t, count := range templates { - m.PreTrainTemplate(t, count) - } -} diff --git a/pkg/workflow/action_pins.go b/pkg/workflow/action_pins.go index b0123a2ebe5..ad0ec20af44 100644 --- a/pkg/workflow/action_pins.go +++ b/pkg/workflow/action_pins.go @@ -144,25 +144,6 @@ func getActionPinsByRepo(repo string) []ActionPin { return cachedActionPinsByRepo[repo] } -// sortPinsByVersion sorts action pins by version in descending order (highest first). -// This function returns a new sorted slice without modifying the input. -// This is an immutable operation for better safety and clarity. -func sortPinsByVersion(pins []ActionPin) []ActionPin { - // Create a copy to avoid mutating the input - result := make([]ActionPin, len(pins)) - copy(result, pins) - - sort.Slice(result, func(i, j int) bool { - // Strip 'v' prefix for comparison - v1 := strings.TrimPrefix(result[i].Version, "v") - v2 := strings.TrimPrefix(result[j].Version, "v") - // Return true if v1 > v2 to get descending order - return compareVersions(v1, v2) > 0 - }) - - return result -} - // GetActionPin returns the pinned action reference for a given action repository // When multiple versions exist for the same repo, it returns the latest version by semver // If no pin is found, it returns an empty string diff --git a/pkg/workflow/action_pins_test.go b/pkg/workflow/action_pins_test.go index 469c9579de5..9ccd22f98fe 100644 --- a/pkg/workflow/action_pins_test.go +++ b/pkg/workflow/action_pins_test.go @@ -708,104 +708,6 @@ func TestGetActionPinWithData_AlreadySHA(t *testing.T) { } } -func TestSortPinsByVersion(t *testing.T) { - tests := []struct { - name string - input []ActionPin - expected []ActionPin - }{ - { - name: "versions in ascending order", - input: []ActionPin{ - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - {Repo: "actions/checkout", Version: "v2", SHA: "abc2"}, - {Repo: "actions/checkout", Version: "v3", SHA: "abc3"}, - }, - expected: []ActionPin{ - {Repo: "actions/checkout", Version: "v3", SHA: "abc3"}, - {Repo: "actions/checkout", Version: "v2", SHA: "abc2"}, - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - }, - }, - { - name: "versions already in descending order", - input: []ActionPin{ - {Repo: "actions/checkout", Version: "v3", SHA: "abc3"}, - {Repo: "actions/checkout", Version: "v2", SHA: "abc2"}, - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - }, - expected: []ActionPin{ - {Repo: "actions/checkout", Version: "v3", SHA: "abc3"}, - {Repo: "actions/checkout", Version: "v2", SHA: "abc2"}, - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - }, - }, - { - name: "mixed version order with patch versions", - input: []ActionPin{ - {Repo: "actions/checkout", Version: "v2.1.0", SHA: "abc210"}, - {Repo: "actions/checkout", Version: "v3.0.0", SHA: "abc300"}, - {Repo: "actions/checkout", Version: "v2.0.1", SHA: "abc201"}, - {Repo: "actions/checkout", Version: "v1.0.0", SHA: "abc100"}, - }, - expected: []ActionPin{ - {Repo: "actions/checkout", Version: "v3.0.0", SHA: "abc300"}, - {Repo: "actions/checkout", Version: "v2.1.0", SHA: "abc210"}, - {Repo: "actions/checkout", Version: "v2.0.1", SHA: "abc201"}, - {Repo: "actions/checkout", Version: "v1.0.0", SHA: "abc100"}, - }, - }, - { - name: "empty slice", - input: []ActionPin{}, - expected: []ActionPin{}, - }, - { - name: "single element", - input: []ActionPin{ - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - }, - expected: []ActionPin{ - {Repo: "actions/checkout", Version: "v1", SHA: "abc1"}, - }, - }, - { - name: "versions without v prefix", - input: []ActionPin{ - {Repo: "actions/checkout", Version: "1.0.0", SHA: "abc100"}, - {Repo: "actions/checkout", Version: "2.0.0", SHA: "abc200"}, - {Repo: "actions/checkout", Version: "1.5.0", SHA: "abc150"}, - }, - expected: []ActionPin{ - {Repo: "actions/checkout", Version: "2.0.0", SHA: "abc200"}, - {Repo: "actions/checkout", Version: "1.5.0", SHA: "abc150"}, - {Repo: "actions/checkout", Version: "1.0.0", SHA: "abc100"}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // sortPinsByVersion now returns a new sorted slice (immutable operation) - result := sortPinsByVersion(tt.input) - - if len(result) != len(tt.expected) { - t.Errorf("sortPinsByVersion() length = %d, want %d", len(result), len(tt.expected)) - return - } - - for i := range result { - if result[i].Repo != tt.expected[i].Repo || - result[i].Version != tt.expected[i].Version || - result[i].SHA != tt.expected[i].SHA { - t.Errorf("sortPinsByVersion() at index %d = %+v, want %+v", - i, result[i], tt.expected[i]) - } - } - }) - } -} - func TestApplyActionPinsToTypedSteps(t *testing.T) { // Create a minimal WorkflowData for testing data := &WorkflowData{