feat: Phase 5 — maps_navigate, and read the screen before the input
The Maps recipe: open the search box, type the place, and confirm the result list names it. Every label comes from a live uiautomator dump (search_omnibox_text_box, typed_suggest_container, the account sheet), never from a guess. Text only, one step_timeout/total_timeout, guards for the account sheet. The first live run exposed an ordering bug in expect_change. A dump is not instant, so a baseline taken after the input already showed the app's response and the check could never see a wedge; it failed against a healthy Maps. The engine now reads the screen before the step above runs and hands that signature to the check. Each retry re-reads it. Live: maps_navigate ok in 27.6s, result list names "Melbourne Museum". The wedge reason and its bounded retry are demonstrated with a probe recipe whose input changes nothing; a real wedge could not be forced, and docs/phase-5-maps-recipe.md says so plainly. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -50,34 +50,33 @@ func (h *Hierarchy) Signature(foreground string) string {
|
||||
return foreground + "\n" + strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
// ExpectChange waits until the screen no longer looks like it did when the
|
||||
// function was called, and returns the signatures before and after.
|
||||
// WaitForChange waits until the screen no longer matches before, and returns
|
||||
// the new signature.
|
||||
//
|
||||
// This is the wedged-app check. An app that has stopped consuming input still
|
||||
// draws the screen it had, so a delivered tap changes nothing. A timeout here
|
||||
// means the last input was accepted by Android but never acted on by the app,
|
||||
// which the recipe reports as "the app stopped consuming input" instead of
|
||||
// blaming the label it was waiting for (AGENTS.md section 11 Phase 5).
|
||||
func ExpectChange(r *adb.Runner, timeout time.Duration) (before, after string, err error) {
|
||||
// The caller captures before with ScreenSignature *before* the input it wants to
|
||||
// judge. That ordering is the whole check: an app that has stopped consuming
|
||||
// input still draws the screen it had, so the signature after the input is
|
||||
// identical to the one before it. A dump is not instant, so a baseline taken
|
||||
// after the input would already include the app's response and could never see
|
||||
// the wedge.
|
||||
func WaitForChange(r *adb.Runner, before string, timeout time.Duration) (string, error) {
|
||||
if strings.TrimSpace(before) == "" {
|
||||
return "", fmt.Errorf("no screen signature was captured before the input")
|
||||
}
|
||||
if timeout <= 0 {
|
||||
timeout = 10 * time.Second
|
||||
}
|
||||
before, err = ScreenSignature(r)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(timeout)
|
||||
for {
|
||||
sig, derr := ScreenSignature(r)
|
||||
if derr != nil {
|
||||
return before, "", derr
|
||||
sig, err := ScreenSignature(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if sig != before {
|
||||
return before, sig, nil
|
||||
return sig, nil
|
||||
}
|
||||
if !time.Now().Before(deadline) {
|
||||
return before, before, fmt.Errorf("the screen did not change within %s", timeout)
|
||||
if time.Now().After(deadline) {
|
||||
return before, fmt.Errorf("the screen did not change within %s", timeout)
|
||||
}
|
||||
time.Sleep(PollInterval)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user