MOB-182: refuse ambiguous multi-device mob.deploy without --all - #78
Closed
GenericJam wants to merge 1 commit into
Closed
GenericJam wants to merge 1 commit into
GenericJam wants to merge 1 commit into
Conversation
`mix mob.deploy` without --device used to fan out silently to every reachable device. Documented behavior, but the failure mode was invisible: a laptop with a teammate's paired phone or a spare emulator left running got the deploy alongside the intended target, and the author had no reason to notice. MOB-150 already fixed related silent- ignore variants of this bug (typo of --device, positional-arg fumble); this closes the "no filter at all" case. `mix mob.deploy` now refuses to proceed when two or more devices are reachable (after platform narrowing) unless the run narrows explicitly with --device / --ios-device or opts in with the new --all flag. Single-device runs proceed exactly as before. The decision logic lives in a pure predicate `MobDev.Deployer.check_fanout_gate/2` — the matrix is unit-tested without adb / simctl. Gate placement: the pure check runs in TWO places on purpose. The Mix task runs it up front on a cheap pre-scan so the refusal lands before the compile / native-build tax. `MobDev.Deployer.deploy_all/1` re-runs it on its own discovery a moment later — that is the authoritative check, since a device plugged in between the pre-scan and the deploy loop would otherwise sneak past. Empty-string guard: `--device ""` (what shell scripts produce from an unset variable) doesn't count as a filter and is refused like the flag being missing. Tests are paired (permissive + refuse) so a mutation that removes the gate entirely fails both halves — the CLAUDE.md discipline check. Decision record: decisions/2026-09-11-mob-deploy-refuses-ambiguous-multi-device.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ezeu2FEZfRajeoL4ywEUDJ
Owner
Author
|
Closing as obsoleted by MOB-169 (#77 → 52b0a72), which landed a stricter gate on origin/master while this PR was in flight. MOB-169 already provides:
That surface is stronger than the Root cause: this branch was cut from origin/master at 21ba7f0 (0.7.1 bump). 52b0a72 (MOB-169) merged to origin after that point but before I pushed, and my "check for parallel agent work" only looked at open PRs, not just-merged commits since my worktree base. Memory updated to widen that check. Linear MOB-182 marked as duplicate of MOB-169. |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
mix mob.deployused to silently fan out to every reachable device when--devicewasn't set, making a run intended for a laptop's own sim also land on a teammate's paired phone or a spare emulator. This closes the case: 2+ reachable devices refuses with a listing unless the caller narrows with--device/--ios-deviceor opts in with the new--allflag.MobDev.Deployer.check_fanout_gate/2— matrix unit-tested without adb / simctl.mix mob.deployrefuses before the compile / native-build tax;MobDev.Deployer.deploy_all/1re-runs the check on its own discovery a moment later (authoritative — closes the TOCTOU window a device plugged in mid-compile would otherwise slip through).--device ""(what shell scripts produce from an unset variable) doesn't count as a filter.Test plan
MobDev.Deployer.check_fanout_gate/2— 12 tests covering: single-device, empty list, multi-device no filter refuse,--all,--device,--ios-device,all: falsesymmetry, and paired permissive+refuse polarity tests on each axis.--device ""refused).mix testfull suite — 2435 passed / 10 excluded (integration).mix credo --strict— clean.Linear: MOB-182
GH: closes mob_dev#53
🤖 Generated with Claude Code
https://claude.ai/code/session_01Ezeu2FEZfRajeoL4ywEUDJ