Skip to content

Latest commit

 

History

History
172 lines (129 loc) · 6.06 KB

File metadata and controls

172 lines (129 loc) · 6.06 KB

Codex Runner

The Codex runner turns a plain-language QA goal into a controlled Switchify manual scanning run.

npm run qa -- --goal "Open WhatsApp" --device <device-id> --max-steps 12

Architecture

The TypeScript parent runner owns every side effect:

  • Visible emulator acquisition (or explicit headless launch); physical devices are excluded.
  • Debug app build/install from the sibling Android repository.
  • Package, bridge, run-as, and accessibility-service preflight.
  • Switchify QA priming through switchify-manual-scan-qa.ps1.
  • Screenshot, logcat, and window-state capture.
  • Switchify debug bridge broadcasts.
  • Run directory writes.
  • Switchify preference and switch-mapping restoration.

Run Lifecycle

Every normal run follows this order:

emulator -> build/install -> preflight -> backup/prime -> capture/scan -> final evidence -> restore -> summary

The default AVD is SwitchifyStorePhone and its window remains open after the run. --avd selects another AVD and --headless explicitly suppresses the window. --device accepts only a ready emulator-* serial.

The default preference mode is prime-and-restore. Restoration is attempted whenever a priming backup manifest exists, including partial prime failures, failed runs, and Ctrl+C/SIGTERM. A detached watchdog waits for the parent process and restores the backup if normal cleanup does not write cleanup-complete.json, covering forced task cancellation as well. --keep-qa-state changes the mode to prime-only; --no-prime changes it to none.

Child decisions default to gpt-5.6-sol. The runner invokes the repository-local @openai/codex dependency so the decision model does not depend on an older global CLI. Use --codex-model to test another supported model explicitly.

Each child Codex invocation is a read-only decision agent. It receives a prompt, the current screenshot, recent state, recent warnings, the action history, and the Switchify operating guide. It returns one JSON decision matching schemas/codex-decision.schema.json.

Isolation Model

Child Codex runs use:

codex exec --ephemeral --sandbox read-only --ask-for-approval never

The child must not run ADB or shell commands. It can only choose one of:

next
previous
select
capture
stop

The parent validates the action before doing anything. Unknown actions fail the run and write a summary.

The default runner is intentionally single-switch-only. It does not allow system recovery actions such as Home or Back; getting stuck should produce findings instead of escaping the workflow.

Full bridge actions are available only when the parent is launched with:

npm run qa -- --goal "Find YouTube from the launcher and open it" --device <device-id> --action-profile full

Full mode allows:

next
previous
select
home
back
recents
quick_settings
notifications
lock_screen
media_play_pause
pause
stop_scanning
change_scanning_direction
toggle_gesture_lock
toggle_gesture_lock_rearm
toggle_gesture_repeat
reload_settings
capture
stop

Switchify Operating Guide

The child prompt embeds switchify-operating-guide.md. A snapshot of that guide is also written to every run directory as switchify-operating-guide.md.

The guide explains:

  • Switchify bridge actions and how they map to SwitchAction.
  • ScanTree levels: row/tree item, group, and node.
  • Initial manual-scan setup behavior.
  • Scan highlight colors and how to read primary versus secondary highlights.
  • ScanTree escape versus Android Back.
  • Keyboard escape and Scan Keyboard behavior.
  • Switchify menu behavior and common decision patterns.
  • Android repo search hints for weird findings.

When debugging a bad agent decision, inspect both step-NNN/codex-prompt.md and the run-level switchify-operating-guide.md.

Run Directory Format

Runs are stored under runs/, which is ignored by git.

runs/
  2026-06-20_09-30-00_open-youtube/
    manifest.json
    goal.md
    summary.md
    findings.md
    events.jsonl
    switchify-operating-guide.md
    final/
      screen.png
      state.json
    step-000/
      screen.png
      state.json
      state.md
      logcat.txt
      dumpsys-window.txt
      codex-prompt.md
      codex-decision.json
      codex-events.jsonl

events.jsonl records parent-side actions. findings.md aggregates strange behavior reported by child Codex. final/ captures the post-decision device state immediately before restoration, including when the run reaches its step limit. summary.md is the handoff artifact for another debugging agent.

Manifest version 2 records the emulator/AVD, whether the runner launched it, visible versus headless mode, build/install and preflight states, the QA outcome, and the cleanup outcome. The summary keeps QA and cleanup failures separate so a restore failure cannot hide the original test result.

Decision Schema

The child returns:

{
  "observation": "What is visible now.",
  "action": "next",
  "rationale": "Why this action is best.",
  "success": false,
  "confidence": 0.75,
  "weirdFindings": []
}

Findings include severity, expected behavior, actual behavior, evidence, and optional Android repo search hints.

Finding Handoff Workflow

When a run sees strange behavior, the runner keeps going by default. At the end, summary.md includes a follow-up prompt that points to:

  • The QA run directory.
  • events.jsonl.
  • findings.md.
  • Step screenshots and logs.
  • The local Switchify Android repo path when configured.

That summary can be handed to another agent to inspect switchify-android and find the likely bug.

Known Limitations

  • The runner does not use uiautomator dump inside the Codex loop because it can disrupt accessibility service state.
  • Child Codex success is visual/state based, not a deterministic assertion engine.
  • The installed debug APK and emulator are retained after cleanup; only Switchify mappings and preferences are restored.
  • The parent defaults to the Switchify debug bridge actions needed for single-switch QA. System and reload actions require --action-profile full.