Skip to content

feat(#186): Phase 6 — foreign-flow arbitration + canonical Maestro surface#276

Merged
Lykhoyda merged 9 commits into
mainfrom
feat/202-phase6-foreign-flows
Jun 11, 2026
Merged

feat(#186): Phase 6 — foreign-flow arbitration + canonical Maestro surface#276
Lykhoyda merged 9 commits into
mainfrom
feat/202-phase6-foreign-flows

Conversation

@Lykhoyda

Copy link
Copy Markdown
Owner

Summary

#202 Phase 6 — the final phase of the re-think spec (docs/superpowers/specs/2026-06-10-device-control-phase4-6-rethink-design.md §3). Closes #186.

A detected foreign Maestro/XCUITest session (standalone maestro-mcp, raw CLI) is now an arbiter input: while it drives the target simulator (UDID-scoped ps axww scan, 5 s TTL, fail-open), local L2 device_* and L3 flow tools refuse fast with BUSY_FOREIGN_FLOW — pointing at the safe L1 reads — instead of colliding into the ~44 s runner-leak cascade #186 reported. L1 introspection stays free by contract; device_screenshot serves pixels via its simctl fallback; a 10 s teardown grace after the plugin's own flows prevents self-false-positives while WDA dies (plan-review BLOCKER). The plugin's maestro_run is declared the canonical Maestro surface.

Live gate (b) — foreign refusal end-to-end (booted iPhone 17 Pro)

planted fake foreign runner pid 46178 (argv contains maestro token + CE9D3DB9-…)
PASS: device_press refused BUSY_FOREIGN_FLOW in 53ms (vs the ~44s cascade in #186)
PASS: maestro_run (L3) refused BUSY_FOREIGN_FLOW
PASS: cdp_navigation_state (L1) unaffected
PASS: device_screenshot served via simctl fallback
PASS: refusal cleared within one TTL window after the foreign session ended
GATE PASS: foreign-flow arbitration end-to-end

~830× faster failure (53 ms vs ~44 s), and it's an explained refusal instead of a cascade.

Live gate (a) — escape hatches verified closed (#201 closed)

PASS #201: clearState flow ran via maestro_run (auto --app-file)
PASS #188: runFlow conditional ran through the plugin validator
GATE PASS: both escape hatches closed — plugin surface is sufficient

The gate caught one more real bug in the shipped #201 fix: the resolver passed the installed container path as --app-file, but clearState deletes that container before maestro-runner reinstalls from it ("No such file or directory", app left uninstalled). Fixed: the container resolution is snapshotted outside the device container (APFS clonefile cp -Rc) first (8119ba1).

Plan-review hardening (Gemini + Claude research, pre-code)

  • BLOCKER: teardown grace — our own dying maestro driver matches the detector for seconds after lease release; cache-busting can't fix it (a fresh scan still sees the dying PID); DeviceSessionArbiter.lastFlowReleasedAt gates the scan.
  • ps axww — command-column truncation could silently drop a mid-path UDID (production false negatives).
  • Honest knob: RN_IOS_FOREIGN_GUARD=0 (authoritative; gates a refusal now, not a log line); RN_IOS_FOREIGN_WARN=0 stays as a deprecated alias with the same full effect.
  • udid-gated in-flight scan dedup.

Tests

1985/1985 (test:all): 8 gate + 12 arbiter-foreign unit tests (incl. teardown grace, L1-never-scans, own-flow skip, envelope extras), 2 snapshot-resolver tests + 2 updated contract tests, wiring pins. Composite one-call-one-lease verified (internal handler calls never re-enter the gate).

This is the last open front of #202 — the issue closes when this merges.

🤖 Generated with Claude Code

Lykhoyda and others added 8 commits June 11, 2026 14:15
…cal maestro surface

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- BLOCKER: teardown grace (FOREIGN_GRACE_MS) — our own dying maestro driver
  matches the detector for seconds after lease release; cache-busting can't
  fix it, an arbiter-side lastFlowReleasedAt window does
- ps axww (mid-path udid truncation = production false negatives)
- RN_IOS_FOREIGN_GUARD knob (WARN stays as deprecated alias, loudly documented)
- udid-gated in-flight dedup + screenshot lastActive clarification

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tection (+ps -ww)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… fast at the arbiter

Includes the plan-review teardown grace (FOREIGN_GRACE_MS): our own dying
maestro driver matches the detector for seconds after lease release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rState

Live-gate finding: the resolver returned the INSTALLED container path as
--app-file, but clearState uninstalls the app and deletes that container
before maestro-runner reinstalls from it — 'No such file or directory'
mid-flow, app left uninstalled. The container resolution is now snapshotted
to a temp dir (APFS clonefile cp -Rc, plain copy fallback) that survives
the uninstall; DerivedData stays the fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… rule

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement: rn-dev-agent + maestro-mcp interop — driver conflict, connection persistence, runFlow allowlist, flow-drift

1 participant