DEV Community

Roronoa
Roronoa

Posted on

Your First Hour in a Mobile AI Repo: Audit the Environment, Then Ship a Boring First PR

Monday, 9:40 a.m. A new engineer has a mid-range Android phone plugged into a laptop and a repo that builds an on-device transcription feature. Their first ticket says "add a retry to the transcript upload path," and it looks like a two-hour job. The app builds, the simulator opens, and then the demo screen throws a network error, because the endpoint in the local config points to a colleague's laptop that is asleep in another timezone.

That is not a code problem, and it is not a knowledge problem. It is an environment problem wearing a feature ticket as a disguise, and it will eat the first day of a junior engineer every single time. Before you write a retry loop, you need three claims you can prove on your own machine. Everything else in this article is scaffolding around those three claims.

Start With Three Claims, Not a Feature

  1. A clean clone builds and runs on this machine, with the toolchain versions written down.
  2. The model call path completes against a documented endpoint, in a documented network state, with a recorded response shape.
  3. Reverting your change returns the repo to a state where the contract test passes again.

Notice that none of these claims is "the feature works well." Quality comes later; reproducibility comes first, because a junior engineer cannot debug a system they cannot reproduce. If you can prove those three claims by lunch, your first pull request is already more valuable than a clever one.

First Hour: Record the Environment Before You Change Anything

Write the audit script before you write the feature. The point is not monitoring; the point is a fingerprint you can diff later when a test suddenly behaves differently. Here is the version I hand to new joiners, trimmed to the fields that have actually mattered:

#!/usr/bin/env bash
# scripts/first-hour-audit.sh
# Run BEFORE your first commit. Writes onboarding-evidence.json.
set -euo pipefail

OUT="${1:-onboarding-evidence.json}"
ENDPOINT="${MODEL_ENDPOINT:-http://127.0.0.1:8080/v1/models}"
SAMPLES="${SAMPLES:-5}"

git_sha="$(git rev-parse HEAD 2>/dev/null || echo unknown)"
node_v="$(node -v 2>/dev/null || echo absent)"
flutter_v="$(flutter --version 2>/dev/null | head -n 1 || echo absent)"
host_os="$(uname -srm)"

android_api="absent"
if command -v adb >/dev/null 2>&1; then
  android_api="$(adb shell getprop ro.build.version.release 2>/dev/null | tr -d '\r' || echo absent)"
fi

booted_sims="absent"
if command -v xcrun >/dev/null 2>&1; then
  booted_sims="$(xcrun simctl list devices booted 2>/dev/null | grep -c Booted || true)"
fi

tmp="$(mktemp)"
for _ in $(seq 1 "$SAMPLES"); do
  t="$(curl -s -o /dev/null -w '%{time_total}' --max-time 10 "$ENDPOINT" || echo -1)"
  echo "$t" >> "$tmp"
done
latencies="$(jq -R -s 'split("\n") | map(select(length > 0) | tonumber)' < "$tmp")"
rm -f "$tmp"

jq -n \
  --arg sha "$git_sha" \
  --arg node "$node_v" \
  --arg flutter "$flutter_v" \
  --arg host "$host_os" \
  --arg android "$android_api" \
  --arg ios "$booted_sims" \
  --arg endpoint "$ENDPOINT" \
  --argjson latency "$latencies" \
  '{git_sha:$sha, host_os:$host, node:$node, flutter:$flutter,
    android_api:$android, booted_simulators:$ios,
    endpoint:$endpoint, endpoint_latency_seconds:$latency}' > "$OUT"

echo "wrote $OUT"
Enter fullscreen mode Exit fullscreen mode

Run it once, commit the output on a throwaway branch, and read it out loud in standup. The following record is illustrative of the shape only, not a benchmark of any device or service:

{
  "git_sha": "…",
  "host_os": "Darwin arm64",
  "node": "v22.x",
  "flutter": "absent",
  "android_api": "14",
  "booted_simulators": "0",
  "endpoint": "…/v1/models",
  "endpoint_latency_seconds": [0.04, 0.11, 0.05, 0.03, 4.80]
}
Enter fullscreen mode Exit fullscreen mode

That last array is the useful part. Four requests in tens of milliseconds plus one five-second outlier tells you more about your network than any dashboard will, and it explains the "flaky test" you were about to blame on your own code. Do not treat those seconds as a service measurement; treat them as a fingerprint of your desk.

First PR: Assert the Contract, Never the Prose

A model endpoint returns text, and text changes. Assertions on text create a suite that fails for reasons unrelated to your change, which teaches a junior engineer to ignore red builds. Assert the envelope instead: status, shape, type, and a budget you control.

// tests/contract/model-contract.test.ts
// Node 20+: node --test tests/contract/model-contract.test.ts
import assert from "node:assert/strict";
import test from "node:test";

const ENDPOINT =
  process.env.MODEL_ENDPOINT ?? "http://127.0.0.1:8080/v1/chat/completions";

type Envelope = {
  choices?: Array<{ message?: { content?: unknown } }>;
  usage?: { total_tokens?: unknown };
};

test("transcript retry contract holds", async () => {
  const res = await fetch(ENDPOINT, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      messages: [{ role: "user", content: "Return the word: ok" }],
    }),
  });

  assert.equal(res.status, 200, `unexpected status ${res.status}`);

  const body = (await res.json()) as Envelope;
  const content = body.choices?.[0]?.message?.content;

  assert.equal(typeof content, "string", "content must be a string");
  assert.ok((content as string).length > 0, "content must be non-empty");

  // Do NOT assert the exact words. Assert the spending envelope instead.
  if (body.usage !== undefined) {
    assert.equal(typeof body.usage.total_tokens, "number");
  }
});
Enter fullscreen mode Exit fullscreen mode
Assert this Never assert this Because
HTTP status and error codes Wall-clock latency in CI Latency measures your network, not the interface
Response field types Exact generated wording Wording is model behavior, not a contract
Non-empty content A fixed character length Length varies across providers and versions
Token usage, when documented Token usage, when undocumented Undocumented fields vanish without warning
Error shape for 4xx and 5xx A specific error message string Messages get reworded between releases

Where a Free Hosted Endpoint Fits on Day One

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

The practical blocker for a new engineer is rarely the model; it is having an endpoint that answers at 9:40 a.m. on a company laptop. MonkeyCode is an open-source project that advertises free model access and a free server option, and the operator states the current free allowance as 10 million tokens. I have not independently verified that figure or the server's limits, so read the project's own current documentation instead of planning capacity from this paragraph.

What a free endpoint genuinely buys you is the ability to finish claims one and two above without waiting on procurement. It also lets you write the contract test against a real HTTP response rather than a mock you invented, which is the difference between testing your code and testing your imagination. What it cannot buy you is any measurement about your device, because it sits behind a network hop you do not control.

Day-one task Free hosted endpoint Why
Contract and schema tests Yes Needs a real envelope, not real hardware
Prompt regression fixtures Yes, with cached responses Keeps CI cheap and deterministic
Latency and battery measurements No You would be measuring the network
Permission revoke/restore drills No Needs real sensors and OS dialogs
App size and packaging checks No Independent of the model host
Thermal throttling observations No Requires sustained on-device load

First Rollback: Rehearse the Reverse Move

The rollback is the step juniors skip, and it is the step that protects them. Rehearse it while your change is small, so the mechanics are boring by the time the change is large.

  1. Tag the commit you are reverting from: git tag before-retry-<sha>.
  2. Revert without rewriting shared history: git revert --no-edit <sha>.
  3. Re-run the contract test and confirm it passes on the reverted tree.
  4. Re-run scripts/first-hour-audit.sh and diff the JSON against the committed fingerprint.
  5. Write one sentence: what changed, what you expected, what you observed.
  6. Push the revert as its own pull request, so the rollback is reviewable like any other change.

A rollback that reverts code but leaves MODEL_ENDPOINT pointing at a server you no longer intend to use is not a rollback. It is a rollback plus a future incident, and the fingerprint diff in step four is how you catch it before someone else does. If the diff shows the endpoint changed but the latency array looks identical, you probably reverted the wrong branch.

Limitations and Who Should Skip This

  • The audit script records what your shell can see; it makes no claim about NPU, GPU, or thermal headroom.
  • A passing contract test means the interface is compatible, not that the output is good or safe.
  • A hosted free endpoint cannot validate background execution, permission loss, or battery cost.
  • The 10 million token figure and the free server option are operator-supplied and may change; verify them at the source.
  • If your fixtures contain regulated user data, do not send them to a hosted endpoint at all.

Skip this approach if your onboarding blocker is hardware access, or if your repository already documents device, OS, framework, permission state, and network conditions for every test. Keep it if your first morning keeps disappearing into a config file that nobody owns.

If the thing blocking your first pull request is an endpoint rather than your code, a free tier plus a free server is a reasonable way to unblock the first hour.

When you run the audit, reply with your device model, OS version, toolchain versions, endpoint state, and whether the run recovered, restarted, or silently disappeared after the rollback. Comparable environment records are worth more than a green checkmark that nobody else can reproduce.

Top comments (0)