fix(verifier): skip llm enumeration on cross-fade frames

a navhost mid-transition carries >1 route *Screen in a collapsed
coordinate space; acting on it taps garbage (soft keyboard). real runs
showed the llm acting on 44% of steps being such frames. skip them so the
llm re-observes a settled frame next step.
This commit is contained in:
pj committed 2026-07-13 09:02:16 +05:30
1 parent c9e0f6a366
commit 133ce17d72
2 files changed
+42

No files matched your search

+24
View File
@@ -130,6 +130,22 @@ type ActionCandidate struct {
prob float64
}
// transitionalTree reports a NavHost cross-fade: more than one route *Screen
// alive at once. Mirrors runner.isTransitionalHierarchy so the LLM rejects the
// same frames the verifier's transitional handling does.
func transitionalTree(tree *hierarchy.Tree) bool {
screens := 0
for _, element := range tree.Elements {
if strings.HasSuffix(element.ResourceID, "Screen") {
screens++
if screens > 1 {
return true
}
}
}
return false
}
// verbActionKind maps a picker verb to the action kind it dispatches.
func verbActionKind(verb string) ActionKind {
switch verb {
@@ -172,6 +188,14 @@ func (v *Verifier) Candidates() []ActionCandidate {
if v.lastTree == nil {
return nil
}
// A cross-fade frame carries more than one route *Screen at once (a NavHost
// mid-transition); its layout is mid-animation, often in a collapsed
// coordinate space, so acting on it taps garbage (e.g. the soft keyboard).
// Skip it so the LLM re-observes a settled frame next step. Mirrors the
// runner's isTransitionalHierarchy.
if transitionalTree(v.lastTree) {
return nil
}
root := v.runtime.GlobalObject().Get("actions")
if root == nil || goja.IsUndefined(root) || goja.IsNull(root) {
return nil