design is efficient

Shell Scripts

shell scripting · automation

I participated in facilitating a user health wearable study and built tools to improve the data gathering process.

Faatimah’s signature

Individual Contribution

I wrote seven SOPs (standard operating procedures) for setting up every device for participation in the study, and built a shell-script automation that pulls data off the devices automatically once a session is complete.

Team Members

Project Tools

Quip, Claude Code, Slack, Terminal

Project Duration

Summer 26 · 3 months

01.

Overview

A user health study comparing wearable devices.

02.

Preparation

To prepare for the study, I created SOPs on how to set up every device the same way.

  • Wrote seven SOPs for setting up every device, aligning each to the same OS and app versions.
  • Annotated screenshots of every step so the whole team could set up devices consistently and verify their work.
  • Troubleshot and prepped the team for issues and fixes, before and as they surfaced during setup.
An annotated SOP: two phone screens marked with numbered red callouts showing how to re-pair a watch
03.

Prompt Engineering

Pulling data by hand took 15+ minutes per session. I got it to under 30 seconds.

The tool handles participant data that can’t be collected twice, so it needed to be precise and careful.

Read the full PhonePull spec (13 sections)

cat phonepull-spec.md

PhonePull, Implementation & Behavior Spec

Build and maintain a macOS background tool that copies research study data off phones automatically when they’re plugged in. Follow every rule below exactly. If a rule and a “helpful” shortcut conflict, implement the rule.

Setup: each session involves three to four phones, one █████ phone (the session authority), one ███ phone, and 1-2 other phones. Two apps hold the data: ███████ (████████████) and █████████ (████████████████████████).

Overview

A one-paragraph summary of what PhonePull is and the 3–4 device / 2-app setup it operates on.

1. Pulling

When a phone is plugged in, start copying immediately. Don’t wait for a button press.

Only ever copy files off a phone. Do not delete anything unless the user explicitly picks “Delete from Phone” and confirms a second time.

Pulling

Defines that copying starts automatically on plug-in and is strictly non-destructive by default.

2. Session authority

Only read wrist assignment (left/right) from the █████ phone’s ███████ “answers” file. Every other phone’s wrist side comes from that file, not from anything else.

If that answers file is not currently present in the destination folder, don’t trust any existing wrist map, treat the session as reset. If the folder gets emptied, drop the map entirely.

If an old answers file from a previous session is still sitting in the folder, it will get treated as authoritative, so before starting a new session, empty the destination folder. Build this warning into the tool if you can (e.g., detect a stale file and flag it).

Session authority

Establishes the █████ phone’s answers file as the single source of truth for wrist assignment, and warns against stale files carrying over between sessions.

3. Folders

Each phone gets exactly one folder for the session. If the phone gets re-pulled, merge into that same folder, replace files with the same name, don’t create duplicates or a second folder.

If a typed unit name is already in use this session by a different device ID, reject it or auto-suffix it. Never let two physical phones collapse into one folder.

Folders

Ensures every phone maps to exactly one folder and prevents two different phones from ever colliding into the same one.

4. Wrist side naming

Folder names look like <unit name>_Left or <unit name>_Right.

Never put “_Left”/“_Right” anywhere except the folder name, not in the phone’s device name, not in any UI.

The session authority phone never gets a wrist side, ever.

Wrist side naming

Locks down where “_Left”/“_Right” is allowed to appear (folder name only) and who never gets a side (the authority phone).

5. Naming dialog

The first time a phone (by device ID) is seen, show one dialog: ask for a unit name, and ask Keep or Delete for the originals.

Use that name for both the phone’s device name and its folder name.

If the dialog gets no response in 120 seconds, default to Keep + the phone’s current name. Never let a timeout result in deletion.

If the same phone briefly disconnects and reconnects, don’t show the dialog again, only show it once per unique device per session.

Naming dialog

Specifies the one-time prompt for naming a phone, its safe timeout behavior, and why it shouldn’t re-trigger on reconnects.

6. Wrist resolution

When matching an answers file wrist entry to a connected phone: if the alias is 2 characters or less, require an exact whole word match. If it’s longer, use fuzzy matching with a similarity cutoff around 0.82.

If the authority phone gets pulled after the others, go back and rename their folders once its map is built. Don’t require it to be pulled first.

If both wrist fields in the answers file resolve to the same device type, don’t guess, pop a “which wrist was it on?” prompt (timeout ~300s, defaulting to no side assigned).

Every time a fuzzy (non exact) match happens, log it. Someone should be able to audit near misses later.

If two connected phones would match the same device type nickname, don’t silently assign them to the same map entry, key the match on unique device ID or unit name instead, or prompt per phone.

Wrist resolution

Details the matching logic (exact vs. fuzzy) used to assign wrist sides from the authority file, plus how to handle conflicts and ambiguous matches.

7. What to copy

█████████: always copy the whole Documents folder, no filtering.

███████ on the authority phone: only copy items whose name matches the session prefix (case insensitive). Skip drafts and the reusable template.

███████ on any other phone: copy the whole Documents folder, unless that phone also has the reusable template installed, in which case apply the same session only filter so the template doesn’t get imported as clutter.

What to copy

Defines exactly which files/folders get pulled from each app, and how the authority phone’s data is filtered differently from the others.

8. Templates

Identify the reusable template only by its file extension. Do not use the word “template” appearing in a folder name as the signal, a legitimate session data folder can have “template” in its name and still be data, not the template.

Never delete a file identified as the template, under any delete policy.

Templates

Locks in that the reusable template is identified only by file extension (never by name) and must never be deleted.

9. Deleting

Deletion requires two steps: pick “Delete” in the initial dialog, then confirm a second “Yes, delete from phone” prompt. Both steps default/timeout to the non destructive option.

Also expose delete from a menu at any time, same double confirmation.

Before allowing delete on any file, verify (hash or size) that the Mac’s copy matches the phone’s copy exactly. If it doesn’t match, don’t delete it and don’t mark the phone “uploaded.”

Don’t allow deleting the authority phone until its answers file has been successfully imported, deleting it early destroys the only source of wrist truth for the session.

Don’t enable delete on any phone until its name and (if relevant) wrist side are finalized.

Delete leaf files first, then empty folders after. Log every path touched.

If the source app is still open on the phone, pulling and deleting will fail because the files are locked. Detect this, tell the operator to force quit the app on the phone, and retry automatically once it’s closed.

Deleting

Lays out the double-confirmation delete flow and the hard prerequisites (verification, finalized naming/wrist, authority file imported) before delete is ever allowed.

10. Status UI

Show one row per phone currently connected, with device name only, never a wrist side.

Give distinct states/text for: uploaded, uploading, no data (fine, nothing to worry about), couldn’t read (needs action), and delete in progress. Don’t collapse “no data” and “couldn’t read” into the same icon.

Include a pause/resume toggle for automatic pulling. If a phone connects while paused, show it as paused/not pulling, not just blank.

Per phone actions: change name, re-pull now, change wrist side, delete files from phone, open folder.

If the status UI crashes or gets force quit, it should come back on its own within a couple seconds. The pull engine must keep working the entire time regardless of whether the UI is alive.

Status UI

Specifies what the menu bar/status display must show per phone and confirms it’s purely cosmetic, never load-bearing for the actual pull logic.

11. Reliability

Trigger a pull on USB attach/detach events. Also run a ~10s safety net poll so a missed event still gets caught.

Use a lock so only one pull runs at a time. If a lock gets left behind by a crashed process, reclaim it immediately, never let a stale lock block future pulls.

If a phone briefly drops off the USB list (flaky adapter), don’t clear its row immediately, wait a short grace window (~5s) before treating it as actually gone.

Never let a pull and a delete run at the same time on the same phone.

If a phone’s name can’t be read yet, retry with a “waiting” state, but cap the retries and surface an actionable error after some reasonable limit instead of retrying forever silently. Never import a phone under its raw device ID.

If a phone isn’t trusted or is locked, report that clearly and retry automatically once it becomes trusted/unlocked.

Reliability

Covers how the tool handles USB flakiness, locking, retries, and trust/lock states so pulls never silently fail or duplicate.

12. Install

Ship one installer. It should prompt for credentials itself, not require the caller to already be elevated.

Install shared components system wide and register background agents so they auto run for every macOS account, including standard non admin accounts, at login.

Don’t require network access at runtime, pulling is local USB only. Only the installer itself may need internet.

Re running the installer should be safe: replace existing components, don’t duplicate them, no matter who runs it or how many times.

Write and ship an uninstall procedure, don’t leave this undefined.

Install

Describes how the tool installs, runs for all users, updates safely, and can be uninstalled.

13. Non negotiable safety checks, treat these as blockers, not nice to haves

If there’s no hash/size verification comparing the phone’s file to the Mac’s copy, do not implement delete or the “uploaded” checkmark until that verification exists.

Give the operator an explicit way to start a new session (or require the destination folder be empty) so a stale map or stale files can’t silently bleed into a new session.

Reject duplicate/typo unit names within the same session instead of merging two different phones into one folder.

Show a summary state like “3/3 imported, safe to disconnect” instead of forcing the operator to check every phone’s row individually.

Don’t auto pull an unrecognized device without at least a one time confirmation, otherwise a personal phone with the same apps installed gets pulled by accident.

If the destination folder isn’t backed up anywhere, warn before enabling delete, that folder is a single point of failure once the phone side originals are gone.

Non-negotiable safety checks

Lists the launch-blocking safety gaps (verification, session boundaries, backups, unrecognized devices) that must exist before this tool is trusted with real data.

Hard constraints, do not violate these under any implementation:

Never delete without two explicit confirmations.

Never show a wrist side anywhere except the folder name.

Never let two physical phones share one folder, one wrist map entry, or one unit name.

Never make the pull engine depend on the status UI being alive.

Never mark a file as safe to delete without verifying it against the phone first.

Hard constraints

A final five-line checklist of absolute rules that override any other implementation decision if conflicts arise.

04.

Results

Data was pulled faster, and the study was published in 2026.

30

Seconds or Less

to pull a session’s data, down from 15+ minutes by hand, with no manual copy errors.

next project Daily Living Labs Take a look at this project where I designed a 3D-printed assistive feeding tray and tested it with seven users across five iterations.