Skip to content

MOB-182: refuse ambiguous multi-device mob.deploy without --all - #78

Closed
GenericJam wants to merge 1 commit into
masterfrom
feat/mob-182-deploy-fanout
Closed

GenericJam wants to merge 1 commit into
masterfrom
feat/mob-182-deploy-fanout

Conversation

@GenericJam

Copy link
Copy Markdown
Owner

Summary

  • mix mob.deploy used to silently fan out to every reachable device when --device wasn'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-device or opts in with the new --all flag.
  • Decision logic sits in a pure predicate MobDev.Deployer.check_fanout_gate/2 — matrix unit-tested without adb / simctl.
  • Two-layer gate: an early pre-scan in mix mob.deploy refuses before the compile / native-build tax; MobDev.Deployer.deploy_all/1 re-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: false symmetry, and paired permissive+refuse polarity tests on each axis.
  • Empty-string guard exercised (--device "" refused).
  • mix test full suite — 2435 passed / 10 excluded (integration).
  • mix credo --strict — clean.
  • Adversarial pre-commit review by fresh subagent applied should-fix findings: consolidated gate at deploy_all as TOCTOU-safe authoritative check, added exit-status doc bullet, paired-polarity tests, empty-string guard.

Linear: MOB-182
GH: closes mob_dev#53

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ezeu2FEZfRajeoL4ywEUDJ

`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
@GenericJam

Copy link
Copy Markdown
Owner Author

Closing as obsoleted by MOB-169 (#7752b0a72), which landed a stricter gate on origin/master while this PR was in flight.

MOB-169 already provides:

  • --all-devices — every emulator / simulator
  • --all-physical — every physical device (explicit opt-in for phones — the actual safety property this ticket cares about)
  • Combined = every connected device
  • No flag with multiple dev devices → ambiguity error with hint pointing at --device <id> / --all-devices
  • Immutable target list frozen before compile/build/push (closes the TOCTOU window this PR's authoritative check was defending)

That surface is stronger than the --all I proposed here: my flag flattens phones and dev devices into one bucket, which is the specific corner MOB-169 refuses to allow.

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.

@GenericJam GenericJam closed this Sep 12, 2026
@GenericJam
GenericJam deleted the feat/mob-182-deploy-fanout branch September 12, 2026 00:41
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.

1 participant