---
name: lockstep
description: Controls the user's iPhone Screen Time through their Lockstep app. Use when the user wants to block, unblock, lock, or unlock apps, set focus rules, or check what is currently locked.
---

# Lockstep

You can lock and unlock the apps the user chose in the Lockstep iPhone app.

You cannot see which apps are protected, change the protected list, or see app usage. You can see:

- whether the apps are locked,
- an optional reason and end time,
- recent actions,
- how many emergency unlocks happened today,
- the rules the user asked you to remember.

## Setup

Install the CLI. The copy served with this skill already points at the right
API origin:

```bash
mkdir -p ~/.local/bin
curl -fsSL https://blockyourapps.com/skill/lockstep -o ~/.local/bin/lockstep
chmod +x ~/.local/bin/lockstep
export PATH="$HOME/.local/bin:$PATH"
```

If the user gives you a connection code (format `XXXX-XXXX`):

1. Pair with the code:
   ```bash
   lockstep pair --code 7K4P-2M9Q --name "Claude" --kind claude
   ```
   Set `--kind` to your product when you know it: `claude`, `chatgpt`,
   `grok`, `muse`, `hermes`, or `other`. The app uses it to show your icon.
2. If pairing times out, retry **once with the same code**. The code is only
   consumed when pairing succeeds. A `410` means it was already used, so ask
   for a new code.
3. The CLI stores the token at `~/.config/lockstep/credentials.json` with
   `0600` permissions. Never echo it back into the chat.
4. Confirm: "Connected to your iPhone. I can lock and unlock the apps you chose."
5. Ask the user to open the app and pick their protected apps if they have not already.

If you have no shell access but can make HTTP requests, call `POST /v1/pair`
with `{"code": "...", "agent_name": "...", "agent_kind": "..."}` and store the
returned `agent_token` yourself. Use one path or the other, not both.

If the user has no code, ask them to open the Lockstep app and tap **Connect your agent**.

## Every session

Call `context` first:

```bash
lockstep context
```

Use it to load:

- `state.locked`, `state.reason`, `state.lock_until`
- `applied_at` — when the phone acknowledged this state. `null` means it has not applied it yet.
- `server_time` — anchor relative times to this, not your own clock
- `cli_latest` — compare with `lockstep --version`; reinstall if yours is older
- `device.timezone`
- `rules`
- `emergency_unlocks_today`
- `blocked_attempts.last_hour` — how many times the shield appeared in the last hour
- `blocked_attempts.by_app` — the same count grouped by an anonymous app hash
- `recent_actions`

Do not assume the state from an earlier conversation.

## Locking

```bash
lockstep lock --until 2026-10-25T17:00:00-04:00 --reason "deep work"
```

- `until` is optional. Omit it for an indefinite lock.
- `reason` is optional. Keep it short; the app displays it if present.
- If the user says "for two hours," compute an absolute ISO 8601 time using `device.timezone`.
- If the user says "until I'm done," omit `until` and unlock when they say they are done.
- A new lock replaces the previous lock and expiry.
- The phone enforces a timed lock locally. You do not need to send an unlock at the end.

If the API returns `409 emergency_cooldown`, do not retry. The user has just used an emergency unlock. Acknowledge it, do not argue, and wait for the cooldown before locking again.

If the response says `delivery: "queued"`, tell the user it will apply when their phone reconnects.

Retries are safe. The CLI sends an `Idempotency-Key` and retries once on a timeout with the same key, so a slow cold start cannot lock twice. If you call the API yourself, generate one key per action and reuse it when you retry; reusing it within 24 hours returns the original response instead of applying the change again.

## Unlocking

```bash
lockstep unlock
```

Unlocking is always allowed, including during the emergency cooldown. Do not ask for confirmation; the user asked you to manage this.

## Disconnecting

```bash
lockstep disconnect
```

This revokes your token on the server and deletes the local credentials. The
user can also disconnect you from Settings in the app. You cannot reconnect
without a new code from them.

## Rules

The rules are your memory. The app does not enforce them; you do when you run.

When the user describes how they want to use their Screen Time:

1. Summarize the rules in plain language.
2. Ask them to confirm.
3. Save them:
   ```bash
   lockstep rules --set "Weekdays 9-5: lock my apps. Weekends: unlocked unless I ask. If I emergency unlock, do not re-lock for the rest of the hour."
   ```
4. Follow them on every check-in.

Be honest about scheduling: if you cannot run on a schedule, say so. You can still lock with an `until` time, and the phone will unlock locally.

## Blocked attempts

`blocked_attempts.last_hour` counts how often the shield was shown on the phone. Treat it as a lower bound, not an audit: iOS reports shield renders, not app launches, and the phone debounces repeats.

The app hashes are opaque and per-device. You cannot map one to an app name, and the phone never sends names or screen time. What the hashes do tell you is whether the attempts came from one app or several — one hash with a high count means the same app keeps coming up.

Use it gently. "You hit the wall eleven times this hour, want to move the end time earlier?" is useful. Nothing about this number justifies scolding, moralizing, or adding rules the user did not agree to.

## Emergency unlock

The user can always unlock from the app. You cannot prevent it, and you must not try.

If `emergency_unlocks_today` is high, mention it and suggest adjusting the rules. Do not lecture, punish, or immediately re-lock. The server will refuse a re-lock for a few minutes after an emergency unlock; respect that.

## What not to do

- Do not claim you can see app usage or which apps are protected. `blocked_attempts` is a count of shield renders, not a list of apps.
- Do not ask for the user's Apple ID, password, or Screen Time passcode.
- Do not promise instant delivery when the phone is offline or force-quit.
- Do not add caps, approvals, or schedules that the user did not ask for.
- Do not claim the lock is tamper-proof.
- Do not re-lock during an emergency cooldown.
- Do not store the connection code after pairing.

## Examples

**"Block my apps for two hours."**

```bash
lockstep context   # read device.timezone
lockstep lock --until 2026-10-25T17:00:00-04:00 --reason "focus"
```

**"I'm done for today."**

```bash
lockstep unlock
```

**"What's my status?"**

```bash
lockstep context
```

**"Block social after 9pm."**

Save the rule, then lock at 9pm if you can run then:

```bash
lockstep rules --set "Every day at 21:00, lock my apps until I unlock them."
lockstep lock --reason "evening wind-down"
```

If you cannot run at 9pm, tell the user and offer to lock now with an `until` time.

## API

Base URL: `https://screentime-api-3cqu.onrender.com`

Authentication: `Authorization: Bearer <agent_token>`.

| Method | Path | Purpose |
|---|---|---|
| `POST` | `/v1/pair` | Exchange a one-time code for a token |
| `GET` | `/v1/context` | State, rules, recent actions, blocked attempts |
| `GET` | `/v1/me` | Agent identity and device |
| `POST` | `/v1/lock` | Lock; optional `until` and `reason` |
| `POST` | `/v1/unlock` | Clear the lock |
| `PUT` | `/v1/rules` | Save the opaque rules notebook |
| `DELETE` | `/v1/me` | Revoke the calling agent token |

The full contract is in `api/openapi.yaml`.

## Errors

- `401` — token missing or revoked. Ask the user to reconnect.
- `409` — emergency cooldown. Do not retry; talk to the user.
- `410` — connection code expired or already used. Ask for a new code.
- `429` — rate limited. Back off and tell the user.
- Network error — keep the last known state; do not claim a lock was applied.
