mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 20:17:09 +00:00
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:
1 parent
c9e0f6a366
commit
133ce17d72
2 files changed
+42
No files matched your search
@@ -130,6 +130,22 @@ type ActionCandidate struct {
|
|||||||
prob float64
|
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.
|
// verbActionKind maps a picker verb to the action kind it dispatches.
|
||||||
func verbActionKind(verb string) ActionKind {
|
func verbActionKind(verb string) ActionKind {
|
||||||
switch verb {
|
switch verb {
|
||||||
@@ -172,6 +188,14 @@ func (v *Verifier) Candidates() []ActionCandidate {
|
|||||||
if v.lastTree == nil {
|
if v.lastTree == nil {
|
||||||
return 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")
|
root := v.runtime.GlobalObject().Get("actions")
|
||||||
if root == nil || goja.IsUndefined(root) || goja.IsNull(root) {
|
if root == nil || goja.IsUndefined(root) || goja.IsNull(root) {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -235,6 +235,24 @@ func TestCandidatesOffRouteLeafYieldsNothing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCandidatesSkipsCrossFadeFrames(t *testing.T) {
|
||||||
|
// Two route *Screen tags alive at once is a NavHost cross-fade: its layout is
|
||||||
|
// mid-animation (collapsed coordinate space), so the LLM must NOT act on it.
|
||||||
|
crossFade := `{
|
||||||
|
"attributes": {"bounds": "[0,0,320,640]"},
|
||||||
|
"children": [
|
||||||
|
{"attributes": {"resource-id": "LedgerScreen", "bounds": "[0,0,320,640]"}, "children": [
|
||||||
|
{"attributes": {"resource-id": "TxnSubmit", "text": "Add credit", "bounds": "[20,332,300,380]"}, "clickable": true, "enabled": true, "children": []}
|
||||||
|
]},
|
||||||
|
{"attributes": {"resource-id": "AddTransactionScreen", "bounds": "[0,0,320,640]"}, "children": []}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
v := enumVerifier(t, "{kind:'builtin', verb:'taps'}", crossFade)
|
||||||
|
if got := v.Candidates(); got != nil {
|
||||||
|
t.Errorf("cross-fade frame should yield no candidates, got %v", descriptions(got))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCandidatesNilWithoutTreeOrActions(t *testing.T) {
|
func TestCandidatesNilWithoutTreeOrActions(t *testing.T) {
|
||||||
withActions := newLoadedVerifier(t, "globalThis.actions = {kind:'builtin', verb:'taps'};")
|
withActions := newLoadedVerifier(t, "globalThis.actions = {kind:'builtin', verb:'taps'};")
|
||||||
if got := withActions.Candidates(); got != nil {
|
if got := withActions.Candidates(); got != nil {
|
||||||
|
|||||||
Reference in new issue
Block a user