mr-tbot/bot-code-mods/auto-device-lock
Claude Code and agent skill by mr-tbot/bot-code-mods
- community
Checking that the SKILL.md file is still in the repository.
Use at the start of every session, and before any command that touches physical hardware — adb, fastboot, scrcpy, screenshots, screen recording, esptool/idf.py/platformio, a serial console, xcrun devicectl — on a machine where more than one editor window or agent session may be running; when phones, dev boards or test devices are shared between projects; when a device behaves as though something else is driving it; when a lock looks stale after a crash; or when the user says \"/auto-device-lock\", mentions device conflicts, contention, or which phone a session should use.
Install
npx vskill@latest install mr-tbot/bot-code-mods/auto-device-lockSKILL.md
/auto-device-lock
Claim a physical device before you touch it, hold it for as long as you need it, and give it back — so that five windows working on five apps can share three phones without corrupting each other's tests, and so the ESP32 that one project is flashing is not opened by another project mid-write.
The standing law: no device operation without a lease you have verified in this same command. Not "I claimed it earlier in this session" — earlier is not now. The gap between those two is where another session's flash lands in your board.
Everything here is built on one fact about how agent sessions run: every Bash call is a fresh `bash -c` that exits within milliseconds, while the session itself lives for hours. A lock held by the shell dies instantly; an environment variable exported in one call is gone by the next. So the lease is bound to the session's own long-lived process, and the shell bindings are re-established on every call that uses them.
The helper is devlock, shipped in this skill's directory. If it is missing — a user may have installed only SKILL.md — say so and fall back to Step 8 rather than inventing a lock format.
It is not on PATH by default. Resolve it once at the top of the session and reuse that path, or offer the user the one-time symlink:
The examples below say devlock; use "$DEVLOCK" if you did not make the link.
Subagents never claim and never release. This rule is load-bearing, so it comes before the mechanism. Every in-process subagent runs under the same claude process as its parent, so the lease's owner triple is identical for all of them — it proves the session is alive, and it cannot tell two subagents apart. The practical consequence: one subagent's release --all frees a device another subagent is mid-flash on, and the registry will agree that was legitimate. The foreground session claims and hands each subagent the serial to use; a subagent that believes it needs a device it was not given should say so and stop, not claim one.
A nested claude -p spawned from a Bash call is the opposite case — it is its own process, so the ancestry walk stops at the inner claude and it becomes a separate holder that contends with its own parent. Pass the device down and let it use -s.
Step 0 — Every Session, Before Anything Else
Run this first, every session, without being asked:
status prunes dead holders as a side effect, so a crashed session's phone is available again by the time you have read the output. There is no cleanup command to remember and no daemon to be running.
If this window used a device before, take it back:
Affinity is not a nicety. A test suite whose screenshots come from a 6.1" phone on Monday and a tablet on Tuesday produces diffs that are all noise, and a recording that intercuts two devices looks like a bug report about the product. Same window, same repo, same device — unless someone else has it.
Step 1 — Claim Before You Touch
Name the devices by canonical id (devlock devices prints them). A serial, a /dev/tty*, a by-id name or a model also resolve, and an ambiguous one is rejected rather than guessed.
`--why` is required in practice even though the flag is optional. It is the entire content of the message another session sees when it collides with you, and "held by pid 41232" with no reason is what makes a person kill a lock they should have waited for.
A multi-device claim is all-or-nothing. Ask for three phones and you either get all three or you get none and nothing is left held. This is not politeness; it is what stops the deadlock where you hold phone A waiting for phone B while another session holds B waiting for A. Devices are acquired in canonical-id order, which is the same order in every session, so a cycle cannot form.
Claiming a device you already hold is not an error and does not change your epoch. Run it twice; run it in a subagent. That is deliberate — you will run it twice.
Step 2 — Bind The Shell, Every Single Call
An export does not survive to your next Bash call. So every call that touches a device begins by re-establishing the binding:
devlock env exits nonzero and exports nothing if the lease is gone, so that || exit 1 is a fence check as well as a convenience. It is the cheapest correct habit in this skill.
Never run bare `adb`. With several devices attached it fails loudly, which is fine. With exactly one attached it silently succeeds against whatever happens to be plugged in — so the command works today, and on the day a colleague plugs in a second phone it starts flashing the wrong one. -s is not optional, and ANDROID_SERIAL must come from devlock env, never from memory.
Holding several Android devices at once is supported and expected; in that case devlock env refuses to pick one for you and prints the per-device lines instead. Pass -s explicitly.
Step 3 — Verify Before Each Batch Of Work
Use check as a shell condition rather than something you read and interpret. It prints one line and returns one code, and every non-OK line ends with the exact command that fixes it:
| Code | Meaning | ||
|---|---|---|---|
| 0 | OK | you hold it, at your epoch, attached and healthy | |
| 1 | HELD | someone else holds it | |
| 2 | FREE | nobody holds it, including you | |
| 4 | POLICY | refused: the device is on never_automate | |
| 5 | ABSENT | leased to you but not attached | |
| 6 | FENCED | your lease lapsed and was re-granted — your state is stale | |
| 7 | UNHEALTHY | attached but not in a state that accepts commands |
check reaps as it runs, so a lease whose session died never blocks you, and a device you lost tells you why you lost it — crashed, frozen, or deliberately taken.
Bare devlock verify is the one command that answers "am I allowed to touch what I am about to touch". It exits nonzero if any answer is anything but OK, so it chains: devlock verify && ....
Every grant carries a monotonically increasing epoch. If your session was declared dead — frozen, suspended, its host asleep, its process stopped — and the device was re-granted to someone else, the epoch in the registry has moved past yours. verify fails, and that failure is the only thing standing between a resumed session and a write into hardware that now belongs to another test.
This is the hole every naive lock has: liveness is checked once at claim time, and the holder is assumed live forever after. Check the epoch, not your memory.
verify says | What it means | |
|---|---|---|
OK | you hold it, at your epoch, and it is attached and healthy | |
NOT HELD | no lease at all — you never claimed it, or you released it | |
STOLEN | someone else holds it now. Stop. Do not touch it | |
FENCED | your lease lapsed and was re-granted. Your state is stale; re-claim and re-check | |
ABSENT | leased to you but unplugged right now | |
UNHEALTHY | attached but unauthorized / offline / recovery, not device |
Step 4 — Release, Including When It Goes Wrong
Release when the work is done, not when the turn ends — but a crash needs no cleanup at all. A lease is valid only while the process that took it is alive, proven by boot id, PID and process start time. Kill the session and the lease evaporates on the next status, claim or devices from anyone. There is no TTL to tune, no heartbeat to miss, and no stale lock file to delete by hand.
Do not release a device a background job is still using. The foreground task finishing is not the same event as the device going idle.
Step 5 — When The Device You Want Is Taken
In order:
- Use a different one.
devlock devicesshows what is free. Note in your report that the
screenshots came from a different device than usual.
- Wait.
--wait <seconds>queues with jittered backoff, and reports who is ahead of you. - Ask the human. Show them the holder, the repo and the reason.
- Steal — only with a human's explicit yes:
--confirm exists so that stealing cannot happen by reflex. A steal breaks a live session's test mid-run; the victim finds out on its next verify, which is exactly why Step 3 is not optional.
Never work around a lock you could not get. Unplugging the device, restarting the adb server, or telling yourself the other session looks idle are all the same mistake with different steps. adb kill-server in particular drops every session's device at once, and the other sessions find out by their command failing halfway through.
Step 6 — Identity Is The Part That Silently Goes Wrong
A lock keyed on an unstable name is worse than no lock, because it reports success while protecting nothing. devlock keys on usb:<vendor>:<serial> derived from sysfs, and that choice is load-bearing:
- `transport_id` is never identity. It is reassigned on every replug and every
adb reboot. - One physical device can appear in two namespaces at once. A phone exposing a CDC interface is
both an adb serial and a /dev/ttyACM*. Lock it as a tty in one session and as an adb serial in another, and both sessions believe they have exclusive access to the same hardware. The canonical id collapses those into one lease covering the hardware, not the cable.
- `/dev/serial/by-id` is not unique. Two identical CH340 or CP2102 adapters commonly report the
same serial, and some boards have no by-id entry at all. Where the canonical id cannot be formed, devlock falls back to ID_PATH — a physical port, so moving the cable moves the identity, which is the honest trade.
- `by-path` lists the same tty twice (
usb-andusbv2-). Deduplicate by resolved device. - A board that reboots into a different USB personality after flashing — a CDC firmware replacing
a UART bridge — comes back with a different id. Re-enumerate rather than trusting the pre-flash name.
- Network adb, emulators and iOS live in their own namespaces (
net:,emu:,ios:) because
their identity has different failure modes. adb tcpip changes a device's serial mid-session: re-claim afterwards rather than assuming the lease followed it.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/auto-device-lock/SKILL.md
Popularity
- 7-day trend score
- 0
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/mr-tbot/bot-code-mods/auto-device-lock)