Memoize flag alias resolution by context

This commit is contained in:
2026-11-07 15:54:36 +00:00
parent 02cd49d6b6
commit 0d3f7299cc
4 changed files with 73 additions and 32 deletions

View File

@@ -1,6 +1,6 @@
# Consolidated audit catalog
Status: **12 distinct open findings: 12 P3**, consolidated from 30
Status: **11 distinct open findings: 11 P3**, consolidated from 30
numbered entries in seven audit documents. Reviewed audit state:
`db732e3a1de55c844f77870e1c6db683c2685d5d`.
@@ -274,6 +274,10 @@ ownership; it is separate from the closed-reporting-handle retention in F07.
## F13 — P3: Flag-alias compilation repeatedly walks shared chain suffixes
**Complete.** Each effective context memoizes its target resolutions, including
inactive endpoints, while retaining canonical precedence and cycle diagnostics.
Focused alias and discovery tests pass, including shared suffixes and registration.
For A chained effective flag mappings across C contexts, independent target
walks can perform Θ(CA²) target examinations, including inactive chains. Each
source repeats work already performed for another source's suffix.

View File

@@ -1,6 +1,6 @@
# Audit remediation implementation plan
Status: in progress; Stages 1–14 complete.
Status: in progress; Stages 1–15 complete.
Baseline: `d492c7747175c01a53261206c2b4356e53453621`. Before this sprint,
intervening changes recorded decisions and retired the completed older
@@ -500,6 +500,12 @@ clear, with selected syntax and provenance preserved.
## Stage 15 — Memoize each context's flag-alias graph
**Complete.** Context-local traversal memoizes canonical and unavailable
endpoints, with an active stack for cycle diagnostics. Source review confirms
resolved suffixes are reused within each context. Focused alias, registration,
cycle and discovery tests passed offline with Go 1.27.0 and `-mod=readonly`,
including shared suffixes and unavailable destinations.
Findings: F13. Source: `flag_aliases.go`.
1. Replace independent suffix walks with context-local visitation and memoized

View File

@@ -230,14 +230,54 @@ func resolveFlagAliases(context flagAliasContext, mappings map[string]flagAliasM
sources = append(sources, source)
}
sort.Strings(sources)
type terminal struct {
spelling capturedFlagSpelling
active bool
}
resolved := make(map[string]terminal)
visiting := make(map[string]int)
var stack []capturedFlagSpelling
var resolve func(capturedFlagSpelling) (terminal, error)
resolve = func(current capturedFlagSpelling) (terminal, error) {
key := flagSpellingKey(current)
if endpoint, exists := context.canonical[key]; exists {
return terminal{spelling: endpoint, active: true}, nil
}
if result, exists := resolved[key]; exists {
return result, nil
}
if start, exists := visiting[key]; exists {
cycle := append(append([]capturedFlagSpelling(nil), stack[start:]...), current)
spellings := make([]string, 0, len(cycle))
for _, spelling := range cycle {
spellings = append(spellings, spelling.token())
}
return terminal{}, fmt.Errorf("flag alias cycle: %s", strings.Join(spellings, " → "))
}
mapping, exists := mappings[key]
if !exists {
resolved[key] = terminal{}
return terminal{}, nil
}
visiting[key] = len(stack)
stack = append(stack, current)
result, err := resolve(mapping.target)
stack = stack[:len(stack)-1]
delete(visiting, key)
if err != nil {
return terminal{}, err
}
resolved[key] = result
return result, nil
}
for _, sourceKey := range sources {
mapping := mappings[sourceKey]
source := spellingFromKey(sourceKey)
target, active, err := resolveFlagTarget(source, mapping.target, mappings, context.canonical)
terminal, err := resolve(mapping.target)
if err != nil {
return resolvedFlagAliases{}, fmt.Errorf("%s: %w", flagContextDescription(context), err)
}
if !active {
if !terminal.active {
if mapping.scoped {
return resolvedFlagAliases{}, fmt.Errorf("scoped flag alias %s has no canonical destination in %s", source.token(), flagContextDescription(context))
}
@@ -246,38 +286,11 @@ func resolveFlagAliases(context flagAliasContext, mappings map[string]flagAliasM
if _, collision := context.canonical[sourceKey]; collision {
return resolvedFlagAliases{}, fmt.Errorf("flag alias %s conflicts with a canonical spelling in %s", source.token(), flagContextDescription(context))
}
result.bySpelling[sourceKey] = resolvedFlagAlias{source: source, target: target}
result.bySpelling[sourceKey] = resolvedFlagAlias{source: source, target: terminal.spelling}
}
return result, nil
}
func resolveFlagTarget(source, target capturedFlagSpelling, mappings map[string]flagAliasMapping, canonical map[string]capturedFlagSpelling) (capturedFlagSpelling, bool, error) {
visited := map[string]int{flagSpellingKey(source): 0}
chain := []capturedFlagSpelling{source}
current := target
for {
key := flagSpellingKey(current)
if endpoint, exists := canonical[key]; exists {
return endpoint, true, nil
}
if start, exists := visited[key]; exists {
cycle := append(append([]capturedFlagSpelling(nil), chain[start:]...), current)
spellings := make([]string, 0, len(cycle))
for _, spelling := range cycle {
spellings = append(spellings, spelling.token())
}
return capturedFlagSpelling{}, false, fmt.Errorf("flag alias cycle: %s", strings.Join(spellings, " → "))
}
visited[key] = len(chain)
chain = append(chain, current)
next, exists := mappings[key]
if !exists {
return capturedFlagSpelling{}, false, nil
}
current = next.target
}
}
func spellingFromKey(key string) capturedFlagSpelling {
if strings.HasPrefix(key, "short:") {
return capturedFlagSpelling{name: strings.TrimPrefix(key, "short:"), short: true}

View File

@@ -295,3 +295,21 @@ func capturedSpelling(token string) capturedFlagSpelling {
issues := []error(nil)
return captureFlagSpelling("test", token, &issues)
}
func TestFlagAliasSharedSuffixAndUnavailableDestination(t *testing.T) {
app, err := New(Identity{Name: "Example"}, flagAliasCommand("active", stringFlag("color", "")), flagAliasCommand("inactive"),
FlagAlias("--first", "--common"), FlagAlias("--second", "--common"), FlagAlias("--common", "--color"))
if err != nil {
t.Fatal(err)
}
for _, name := range []string{"first", "second", "common"} {
key := flagSpellingKey(capturedFlagSpelling{name: name})
active := app.flagAliases[commandpath.Path{"active"}.Key()].bySpelling
if alias, ok := active[key]; !ok || alias.target.token() != "--color" {
t.Fatalf("active %s = %#v", name, alias)
}
if _, ok := app.flagAliases[commandpath.Path{"inactive"}.Key()].bySpelling[key]; ok {
t.Fatalf("inactive %s was installed", name)
}
}
}