* feat: add --wait-stable for Android AX tree and --first/--last for find
Address the #1 token efficiency issue on Android: stale accessibility
trees after navigation. `snapshot --wait-stable <ms>` polls until two
consecutive uiautomator dumps match (tree stabilized) or the timeout
expires, eliminating retry loops caused by AX lag after screen transitions.
Also adds `find --first` / `find --last` flags to disambiguate when
multiple elements match, avoiding the fallback to raw snapshot +
coordinate taps that burned 4-6 extra tool calls.
https://claude.ai/code/session_01FzX5t9qATyT9iWnYjssShC
* cleanup: simplify find disambiguation and tighten flag descriptions
- Use consistent array reassignment for --first/--last instead of
.length mutation
- Inline FIND_DISAMBIGUATE_FLAGS (only 2 entries, no reuse)
- Shorten --wait-stable usage description
https://claude.ai/code/session_01FzX5t9qATyT9iWnYjssShC
* docs: add flag plumbing checklist and --wait-stable/--first/--last guidance
Add a 7-step checklist to AGENTS.md for adding new CLI flags — this was
the main non-obvious cost in the implementation (7 files for one flag).
Update exploration skill to recommend --wait-stable for Android AX
staleness recovery and --first/--last for ambiguous find matches.
https://claude.ai/code/session_01FzX5t9qATyT9iWnYjssShC
* docs: improve skill clarity for weaker models
- Split "Most common mistake" into three labeled subsections (stale
refs, Android AX lag, React Native overlays) so each is scannable
independently
- Convert Android AX recovery from prose into a numbered list
- Add --wait-stable to debugging.md for consistency with exploration.md
- Replace vague "meaningful UI changes" with concrete triggers
- Tighten QA recovery sentence that was getting unwieldy
https://claude.ai/code/session_01FzX5t9qATyT9iWnYjssShC
* fix: make Android snapshot freshness recovery internal
* fix: tighten Android freshness baselines
* docs: clarify Android freshness tradeoffs
* docs: clarify empty interactive freshness warning
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Add unhandledRejection handler in daemon to prevent silent crashes
- Add timeout handling to runCmdStreaming matching runCmd pattern
- Fix port allocation race in getFreePort by resolving inside close callback
- Replace synchronous appendFileSync with async appendFile in logChunk
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add unhandledRejection handler in daemon to prevent silent crashes
- Add timeout handling to runCmdStreaming matching runCmd pattern
- Fix port allocation race in getFreePort by resolving inside close callback
- Replace synchronous appendFileSync with async appendFile in logChunk
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove all test-only DI parameters from handleSessionCommands (signature
drops from ~30 params to 5: req, sessionName, logPath, sessionStore,
invoke) and from the remaining sub-handlers (session-inventory,
session-observability, session-replay, session-close, session-state).
Migrate all 89 remaining node:test unit test files to vitest. Tests that
passed DI overrides now use vi.mock instead. Update vitest.config.ts to
include *.test.ts alongside *.vitest.ts, and remove the dual-runner
node --test from package.json scripts.
Add a permanent CI lint guard that fails if optional typeof DI seams
reappear in production code.
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: remove JS test wrappers, run .ad replay suites directly on CI
The platform JS test files (ios.test.ts, android.test.ts, macos.test.ts)
were thin wrappers that shelled out to `agent-device test` and asserted
on JSON counts. Since the CLI already exits non-zero on failure, the
wrappers added no value. CI now invokes the replay suites directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add local replay entrypoints and restore physical device test
- Add pnpm scripts for each replay suite (test:replay:ios,
test:replay:ios-device, test:replay:android, test:replay:macos)
so local runs still exercise platform replays
- Restore physical-device iOS replay step in CI workflow
(conditional on IOS_UDID variable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove ~35 DI parameters from session-close, session-open, session-deploy,
session-device-utils, session-runtime, record-trace-recording, and
request-router. All sub-handlers now use direct imports instead of
threaded typeof params. Tests rewritten from node:test to vitest with
vi.mock() replacing manual dependency injection.
Closes#283
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: migrate interaction + snapshot chains from test-only DI to vitest mocking
Remove ~25 DI parameters threaded through the interaction and snapshot
handler chains that existed solely for testability. Functions now import
their dependencies directly; tests use vi.mock to intercept them.
Interaction chain: dispatch, readAndroidScreenSize removed from
interaction.ts, interaction-touch.ts, interaction-common.ts,
interaction-read.ts, interaction-selector.ts, interaction-snapshot.ts,
interaction-get.ts, interaction-is.ts, interaction-scroll.ts, find.ts.
Snapshot chain: dispatchSnapshotCommand, runnerCommand,
sessionlessRunnerCleanup removed from snapshot.ts, snapshot-wait.ts,
snapshot-alert.ts, snapshot-capture.ts.
Old node:test files replaced with vitest equivalents using vi.mock.
Closes#282
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: align vitest interaction handler coverage
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>