# Benchmark an AI Coding Task Across Mobile Backgrounding, Network Switches, and Battery Pressure

> Source: <https://dev.to/roronoa_/benchmark-an-ai-coding-task-across-mobile-backgrounding-network-switches-and-battery-pressure-39e5>
> Published: 2026-07-31 10:37:48+00:00

Most AI coding assistants are demoed on a desk: laptop plugged in, stable Wi-Fi, full attention. But the moment I approve or monitor a long-running AI task from a phone, the environment becomes hostile — the app gets backgrounded, the device hops from Wi-Fi to LTE, iOS starts throttling background execution, and the battery drops below 20%.

This article is a reproducible benchmark method for one question: **does an AI coding task survive the mobile lifecycle, and can you tell the difference between 'recovered', 'restarted', and 'silently dropped'?**

*Disclosure: This article was prepared as part of MonkeyCode's product outreach.*

Any agent backend with a task ID you can poll works for this method; the benchmark is about the lifecycle, not the vendor.

For each run, classify the final state:

| Outcome | Definition |
|---|---|
Recovered |
Same task ID, output continues from where the connection dropped |
Restarted |
New task ID or duplicate execution; original work discarded or redone |
Silent loss |
UI shows 'running' or a spinner, but the backend has no live task |

Silent loss is the dangerous one. It looks like progress.

The client just needs to launch a task, poll by ID, and log transitions with timestamps. Pseudocode you can adapt to React Native, Flutter, or a plain fetch loop:

```
// lifecycle-bench.mjs — run in your app's debug console or a Node harness
const log = [];
const stamp = (event, extra = {}) =>
  log.push({ t: Date.now(), event, ...extra });

async function launchTask(prompt) {
  const res = await fetch(`${BACKEND}/tasks`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ prompt }),
  });
  const { taskId } = await res.json();
  stamp("launched", { taskId });
  return taskId;
}

async function poll(taskId) {
  let lastStatus = null;
  for (;;) {
    try {
      const res = await fetch(`${BACKEND}/tasks/${taskId}`);
      const task = await res.json();
      if (task.status !== lastStatus) {
        stamp("status", { status: task.status, progress: task.progress });
        lastStatus = task.status;
      }
      if (["done", "failed"].includes(task.status)) return task;
    } catch (e) {
      stamp("poll_error", { error: String(e) });
    }
    await new Promise((r) => setTimeout(r, 2000));
  }
}

// On app foregrounding, run: poll(savedTaskId)
// Then diff the log against the backend's task history.
```

After each run, compare the client log with server-side task history. If the client saw `poll_error`

for 4 minutes and the server shows the task still executing, that's a *recovered* run. If the server shows the task was killed at the disconnect, your UI lied — classify accordingly.

Run the same prompt (a medium-length task, e.g. 'refactor this module and run the tests') under each condition. One variable per run:

Expected observations from my runs: conditions 2–4 are survivable when the client persists the task ID and the backend treats tasks as resumable by ID. Condition 5 is where I've seen silent loss most often — the backend's idle timeout fires while iOS defers the client's keepalive. Condition 6 fails entirely if the task ID only lives in memory.

`taskId`

(Keychain/Keystore, not just AsyncStorage if it's sensitive) and re-attach on launch before offering a 'start new task' button.`lastSeenAt`

. If server `lastSeenAt`

is stale but UI shows 'running', surface 'connection lost — task status unknown' instead of a spinner.If your tasks are sub-second, this whole matrix is overkill — just retry. And if the task handles sensitive code or credentials on a device you don't control, a free hosted server is the wrong target entirely; test continuity against infrastructure you're authorized to use.

If you run this matrix on your own setup, I'd like to compare notes: device, OS version, which transition you triggered, and whether the task recovered, restarted, or silently disappeared. That last one is the number nobody's dashboard shows.
