Avoid rescanning unsupported input names

This commit is contained in:
2026-10-08 21:18:45 +00:00
parent e69a847cf2
commit c7e22c0dbe
4 changed files with 39 additions and 5 deletions

View File

@@ -253,11 +253,9 @@ func (walker *requirementWalker) evaluateInputName(node parse.Node, reach requir
}
evaluation.unsupported = true
}
if evaluation.unsupported {
// Preserve directly recognizable references from a computed name without
// treating the computation itself as an evaluated dependency.
_ = walker.visit(node, requirementPossible, dot)
}
// Value analysis has already visited recognizable references, including
// the conservative fallback for unsupported expressions. Do not rescan
// the same subtree: nested computed names would multiply that work.
return walker.unresolvedInputName(evaluation)
}

View File

@@ -77,6 +77,35 @@ func TestAnalyzeRequirementsUnknownScopesAffectOptionalEntriesOnlyWhenActive(t *
}
}
func TestRequirementWalkerDoesNotRescanNestedUnsupportedNames(t *testing.T) {
for _, depth := range []int{6, 12} {
expression := ".selector"
for level := 0; level < depth; level++ {
expression = `input (printf "%s" (` + expression + `))`
}
source := "{{" + expression + "}}"
compiled := compileRequirementTemplate(t, "computed-name", source)
walker := &requirementWalker{
ctx: context.Background(), vars: map[string]string{"selector": "binding"},
inputs: make(map[string]*inputEvidence), variables: make(map[string]*variableEvidence),
issues: make(map[requirementIssueKey]domain.RequirementIssue), complete: true, rootKnown: true,
}
if err := walker.walkTemplate(compiled); err != nil {
t.Fatal(err)
}
// A generous source-relative bound detects exponential rescanning, not
// an exact visit count or a production analysis limit.
if walker.dependencies > len(source)*8 {
t.Fatalf("depth=%d: %d accesses for %d bytes of source", depth, walker.dependencies, len(source))
}
result := walker.result(domain.RequirementInspection{})
if result.Complete || len(result.Variables) != 1 || result.Variables[0].Name != "selector" ||
result.Variables[0].Kind != domain.RequirementConditional || len(result.Issues) == 0 {
t.Fatalf("nested unsupported names lost discovery metadata: %+v", result)
}
}
}
func TestAnalyzeRequirementsCollectsDeclaredAndLiteralInputs(t *testing.T) {
description := "retained metadata"
definition := &domain.PromptDefinition{ID: "capture", Version: "v1"}