From 8ad27e8368f17d75ca1cae5acc363db11511e96d Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 10 Nov 2026 20:39:00 +0000 Subject: [PATCH] Avoid repeated glossary selection and membership work --- docs/roadmap/remediation.md | 2 ++ internal/core/config/resolve.go | 19 +++++++++++-------- internal/core/glossary/selection.go | 5 +++++ internal/core/glossary/vocabulary.go | 22 +++++++++++----------- internal/core/glossary/vocabulary_test.go | 3 ++- internal/framework/preflight/preflight.go | 10 ++++++++-- 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index d4890d3..33795ac 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -522,6 +522,8 @@ and PromptKit still controls credential selection and availability. ## R15 — P3: Avoid Repeated Glossary Selection and Membership Scans +**Status:** Completed. List defaults clone only when inherited. Selection skips repeated collection names, readiness short-circuits without a rich projection, and contributor membership uses a set while preserving order. Existing inheritance, readiness, and glossary tests passed. + **Category** Redundant list work and quadratic deduplication in Stages 10–12. diff --git a/internal/core/config/resolve.go b/internal/core/config/resolve.go index 6aae9cf..c561019 100644 --- a/internal/core/config/resolve.go +++ b/internal/core/config/resolve.go @@ -1,6 +1,10 @@ package config -import ck "go.maximumdirect.net/configkit" +import ( + "slices" + + ck "go.maximumdirect.net/configkit" +) func cloneSelection(value *string) *string { if value == nil { @@ -10,26 +14,25 @@ func cloneSelection(value *string) *string { return &copied } -func cloneNames(value []string) []string { - if value == nil { - return nil +func defaultNames(r *ck.Resolution, path ck.Path, target *[]string, inherited []string, reason string, source ck.Path) { + if !r.IsExplicit(path) { + ck.Default(r, path, target, slices.Clone(inherited), reason, source) } - return append([]string{}, value...) } // resolveSettings records root-derived defaults after all explicit assignments. func resolveSettings(c *Config, r *ck.Resolution) error { glossaries := ck.Keys("glossaries") protection := ck.Keys("protected_terms") - ck.Default(r, protection, &c.ProtectedTerms, cloneNames(c.Glossaries), "protection defaults to selected glossaries", glossaries) + defaultNames(r, protection, &c.ProtectedTerms, c.Glossaries, "protection defaults to selected glossaries", glossaries) rootValidation := ck.Keys("llm", "validation_profile") rootValidationInfo, _ := r.EntryInfo(rootValidation) inheritedValidation := r.IsExplicit(rootValidation) && !rootValidationInfo.Null && c.LLM.ValidationProfile != nil for i := range c.Pipeline.Stages { item := &c.Pipeline.Stages[i] base := ck.Keys("pipeline", "stages").Index(i) - ck.Default(r, base.Key("glossaries"), &item.Glossaries, cloneNames(c.Glossaries), "inherit root glossaries", glossaries) - ck.Default(r, base.Key("protected_terms"), &item.ProtectedTerms, cloneNames(c.ProtectedTerms), "inherit root protection", protection) + defaultNames(r, base.Key("glossaries"), &item.Glossaries, c.Glossaries, "inherit root glossaries", glossaries) + defaultNames(r, base.Key("protected_terms"), &item.ProtectedTerms, c.ProtectedTerms, "inherit root protection", protection) threshold := ck.Keys("thresholds", item.Module) var value float64 switch item.Module { diff --git a/internal/core/glossary/selection.go b/internal/core/glossary/selection.go index 02efcd9..022c1b7 100644 --- a/internal/core/glossary/selection.go +++ b/internal/core/glossary/selection.go @@ -18,9 +18,14 @@ type identity struct{ collection, id string } // Select retains the first occurrence of each selected collection entry. func Select(collections map[string]config.GlossaryCollection, names []string) []Entry { + visited := make(map[string]struct{}) seen := make(map[identity]struct{}) var selected []Entry for _, name := range names { + if _, exists := visited[name]; exists { + continue + } + visited[name] = struct{}{} for _, item := range collections[name].Entries { key := identity{collection: name, id: item.ID} if _, exists := seen[key]; exists { diff --git a/internal/core/glossary/vocabulary.go b/internal/core/glossary/vocabulary.go index 9236370..2612122 100644 --- a/internal/core/glossary/vocabulary.go +++ b/internal/core/glossary/vocabulary.go @@ -29,6 +29,11 @@ func Vocabulary(entries, excluded []Entry) []Term { } } } + type contribution struct { + spelling string + identity Identity + } + seen := make(map[contribution]struct{}) indices := make(map[string]int) var terms []Term for _, entry := range entries { @@ -38,23 +43,18 @@ func Vocabulary(entries, excluded []Entry) []Term { if _, excluded := exclusions[key]; excluded { continue } + member := contribution{spelling: key, identity: id} + if _, exists := seen[member]; exists { + continue + } + seen[member] = struct{}{} index, exists := indices[key] if !exists { index = len(terms) indices[key] = index terms = append(terms, Term{Spelling: spelling}) } - identities := terms[index].Identities - found := false - for _, existing := range identities { - if existing == id { - found = true - break - } - } - if !found { - terms[index].Identities = append(identities, id) - } + terms[index].Identities = append(terms[index].Identities, id) } } return append(terms, exempt...) diff --git a/internal/core/glossary/vocabulary_test.go b/internal/core/glossary/vocabulary_test.go index 4c8a7bd..317bc31 100644 --- a/internal/core/glossary/vocabulary_test.go +++ b/internal/core/glossary/vocabulary_test.go @@ -10,7 +10,8 @@ func TestVocabularyKeepsContributorsAndExemptsOnlyActiveSpellings(t *testing.T) active := []Entry{{Collection: "spells", ID: "shield", Name: "Shield", Aliases: []string{"Ward"}}} protected := []Entry{ active[0], - {Collection: "items", ID: "shield", Name: "shield", Aliases: []string{"Aegis"}, Plural: &plural}, + {Collection: "items", ID: "shield", Name: "shield", Aliases: []string{"Aegis", "SHIELD"}, Plural: &plural}, + active[0], } full := Vocabulary(protected, nil) if !reflect.DeepEqual(full[0].Identities, []Identity{{Collection: "spells", ID: "shield"}, {Collection: "items", ID: "shield"}}) { diff --git a/internal/framework/preflight/preflight.go b/internal/framework/preflight/preflight.go index 1dc63aa..baaef16 100644 --- a/internal/framework/preflight/preflight.go +++ b/internal/framework/preflight/preflight.go @@ -4,10 +4,10 @@ package preflight import ( "context" "fmt" + "slices" "gitea.maximumdirect.net/eric/audita/v3/internal/core/config" "gitea.maximumdirect.net/eric/audita/v3/internal/core/diagnostics" - "gitea.maximumdirect.net/eric/audita/v3/internal/core/glossary" "gitea.maximumdirect.net/eric/audita/v3/internal/core/modulecatalog" "gitea.maximumdirect.net/eric/audita/v3/internal/core/schema" ) @@ -105,7 +105,13 @@ func CheckReadiness(ctx context.Context, cfg *config.Config, inspectProfile func return fmt.Sprintf("stage %d (%s)", index+1, item.Module) } for index, item := range cfg.Pipeline.Stages { - if item.Module == modulecatalog.KeyGlossary && len(glossary.Select(cfg.GlossaryCollections, item.Glossaries)) == 0 { + if item.Module != modulecatalog.KeyGlossary { + continue + } + hasEntries := slices.ContainsFunc(item.Glossaries, func(name string) bool { + return len(cfg.GlossaryCollections[name].Entries) > 0 + }) + if !hasEntries { return fmt.Errorf("%s has no glossary entries in selected collections %q", label(index, item), item.Glossaries) } }