You ask your coding agent why the orders screen is empty. It reads the client code, reads the model, and then writes a confident fix for a response shape it has never seen. The real response was a 200 with {"items":[],"error":"quota"} in the body. Nothing on screen said so, and nothing in the code could.
For web apps the agent can sometimes look at the browser. For a mobile app it can't see anything unless you give it the traffic. The steps below work with any HTTPS debugging proxy (mitmproxy, or whichever one you already use). Examples assume it listens on 127.0.0.1:8080 and that you've exported its CA certificate.
iOS Simulator. The Simulator uses the Mac's network settings, so the Mac's system proxy applies. Trust the CA once per simulator:
xcrun simctl keychain booted add-root-cert ~/proxy-ca.pem
Then restart the app.
Android emulator. Start it with the proxy:
emulator -avd Pixel_8 -http-proxy http://127.0.0.1:8080
127.0.0.1 here is your Mac, because the emulator process applies the flag on the host side. Install the CA as a user certificate. Since Android 7, apps ignore user CAs unless you opt in for debug builds:
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
Reference it from the manifest with android:networkSecurityConfig="@xml/network_security_config". debug-overrides only applies to debuggable builds, so release builds stay strict.
Flutter. Dart's HttpClient ignores the system proxy on every platform. In debug builds, set an HttpOverrides whose findProxy returns PROXY <host>:8080.
A real iPhone. Wi-Fi settings → your network → Configure Proxy → Manual, with your Mac's LAN IP and port. Install the CA profile, then switch it on under Settings → General → About → Certificate Trust Settings. Installing the profile alone is not enough.
Clear the session, do the one thing that's broken, and export just that as a HAR. A short, focused capture beats an hour of background noise. The agent reads every byte you give it.
A HAR carries everything: bearer tokens, cookies, API keys in query strings. Don't paste that into a model. This jq filter keeps one API host, masks anything that looks like a credential, and truncates very large bodies:
def secret: ascii_downcase | test("authorization|cookie|token|secret|api[-_]?key|session|signature|password");
.log.entries |= map(select(.request.url | startswith("https://api.example.com/")))
| .log.entries[] |= (
(.request.headers, .response.headers) |= map(if (.name | secret) then .value = "REDACTED" else . end)
| .request.queryString |= map(if (.name | secret) then .value = "REDACTED" else . end)
| .request.url |= gsub("(?<k>[?&][^=&]*(token|key|secret|sig|session)[^=&]*)=[^&]*"; "\(.k)=REDACTED"; "i")
| .request.cookies = [] | .response.cookies = []
| .response.content.text |= (if . == null then . else .[:20000] end)
)
jq -f redact.jq capture.har > capture.redacted.har
grep -oE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' capture.redacted.har # leftover JWTs?
The filter only looks at names. Tokens inside JSON bodies (a login response, a refresh call) still get through, which is what the grep is for. Read the file before you hand it over. A sanitized HAR still needs a look.
Put the file in the repo (and in .gitignore), then ask something specific:
capture.redacted.har has the requests from opening the Orders screen. Which request feeds the list, what did it return, and why does the UI show nothing? Point to the code path.
What tends to come back:
200 responses that carry an error in the body, which the client treats as success.orderId vs order_id, a number sent as a string), so the whole decode fails silently.
This article was created with the help of AI.