Avoid rescanning unsupported input names
This commit is contained in:
@@ -80,6 +80,8 @@ entries are reported as conditional rather than inactive-only or definitively
|
|||||||
optional. Direct root-map `with` forms retain caller-map identity. Known scalar
|
optional. Direct root-map `with` forms retain caller-map identity. Known scalar
|
||||||
values remain separate from unknown scopes; accesses through untracked local
|
values remain separate from unknown scopes; accesses through untracked local
|
||||||
maps report incomplete discovery rather than silently losing caller keys.
|
maps report incomplete discovery rather than silently losing caller keys.
|
||||||
|
Unsupported name expressions reuse value analysis's conservative syntax scan
|
||||||
|
instead of recursively rescanning the same subtree.
|
||||||
Invoked named templates are scanned conservatively, and
|
Invoked named templates are scanned conservatively, and
|
||||||
dependency-bearing range scopes make discovery incomplete. Uncalled definitions
|
dependency-bearing range scopes make discovery incomplete. Uncalled definitions
|
||||||
and bypassed session templates are not traversed. This internal walker does not
|
and bypassed session templates are not traversed. This internal walker does not
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ discovery. Retain known-string dot isolation and inactive-path behavior.
|
|||||||
|
|
||||||
### Finding 2 — Avoid Recursive Rescanning Of Unsupported Names (P2)
|
### Finding 2 — Avoid Recursive Rescanning Of Unsupported Names (P2)
|
||||||
|
|
||||||
|
**Status: Resolved.** The duplicate fallback traversal was removed. A focused
|
||||||
|
source-relative work regression failed before the fix (4,096 accesses for a
|
||||||
|
277-byte expression) and passed afterward, preserving partial-result metadata.
|
||||||
|
No production limits or caches were added; prompt/use-case race tests passed.
|
||||||
|
|
||||||
Location: [requirement walker](../../internal/prompt/requirements.go),
|
Location: [requirement walker](../../internal/prompt/requirements.go),
|
||||||
`evaluateInputName` (lines 231–244), through `evaluateValue`,
|
`evaluateInputName` (lines 231–244), through `evaluateValue`,
|
||||||
`evaluateCommand`, and `visitCommand`.
|
`evaluateCommand`, and `visitCommand`.
|
||||||
|
|||||||
@@ -253,11 +253,9 @@ func (walker *requirementWalker) evaluateInputName(node parse.Node, reach requir
|
|||||||
}
|
}
|
||||||
evaluation.unsupported = true
|
evaluation.unsupported = true
|
||||||
}
|
}
|
||||||
if evaluation.unsupported {
|
// Value analysis has already visited recognizable references, including
|
||||||
// Preserve directly recognizable references from a computed name without
|
// the conservative fallback for unsupported expressions. Do not rescan
|
||||||
// treating the computation itself as an evaluated dependency.
|
// the same subtree: nested computed names would multiply that work.
|
||||||
_ = walker.visit(node, requirementPossible, dot)
|
|
||||||
}
|
|
||||||
return walker.unresolvedInputName(evaluation)
|
return walker.unresolvedInputName(evaluation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
func TestAnalyzeRequirementsCollectsDeclaredAndLiteralInputs(t *testing.T) {
|
||||||
description := "retained metadata"
|
description := "retained metadata"
|
||||||
definition := &domain.PromptDefinition{ID: "capture", Version: "v1"}
|
definition := &domain.PromptDefinition{ID: "capture", Version: "v1"}
|
||||||
|
|||||||
Reference in New Issue
Block a user