Skip to content
Thoughts10 min read

Forcing agents to think about UX

Two Claude Code skills I wrote to find where a product tells people a job is finished when it isn’t.

  • UX
  • AI
  • Product

I build Nala and OpenStable on my own, with Claude Code agents doing much of the typing. The code moves faster than I can hold it in my head, and the core experience slips first: what someone came to do, and whether the product still gets them there. A feature ships, then another touches the same flow, and a month later people walk a journey I never designed.

Looking for a reviewer that thinks about the user

1 person
building two products, with no one else checking the experience

On a team, someone else would catch this. A researcher would bring back the interview where a user got lost, or a cofounder would try the flow on their phone and ask why it did that. Working alone, I needed my agents to be that person.

I went looking for a way to do it and couldn’t find one. You can give an agent good skills for security reviews and code reviews, and they make it check whether the code is correct. Design critique tools make it check whether a screen looks right. I found nothing that made an agent think about the experience: walk a flow the way a user lives it, follow it past the screen, and ask whether the person got what they came for.

I wrote two skills to do that, and I run them against the code again and again as it changes. ux-stress-test goes deep on one flow, with me in the loop. ux-audit explores a whole product, journey by journey, and tells me which flows to stress-test next. Both read the codebase, and both judge a flow by whether the user gets what they came to do.

Where the job gets done

one hop
how far past “sent” the job usually finishes

A stress-test in August found this on OpenStable. A horse owner finds a yard they like, taps Enquire, writes a message and sends it. The app says “Your enquiry is on its way… Most yards respond within 48 hours.” Every screen in that flow worked. But a yard had to accept an enquiry before it could read it, and a yard on the free plan got five accepts for life. Once a yard had spent them, the enquiry sat unread and nobody told the horse owner. They thought they’d made contact, and the yard owner never read a word.

I kept missing problems shaped like that one. A flow looks finished the moment the user presses the button, and the thing they came to do happens later, somewhere else: in an email job on the server, in a webhook from Stripe, on another person’s screen. A design review can’t see it, because each screen is fine. A code review misses it too, because each function does what it says. You find it by holding the whole journey up against the job.

ux-stress-test: one flow, against one job

8 slots
the job written down before any code is read

A stress-test starts by writing the job down, before it traces a line of code. Claude reads enough of the code to draft a canvas and I correct it. The canvas has eight slots: who the user is and what state they start in, what just happened to them, the job in one sentence, why they’d use this over the alternatives, what makes them give up halfway, what they’d do instead, the one observable event that means the job got done, and what looks like success but isn’t.

ux-stress-test · OpenStable · a horse owner’s first enquiry · 28 Aug 2026dashed = where it leaks
  1. Claude
    Draft the canvasEight slots: the job, why people give up, and what looks like success but isn’t.
  2. Me
    Confirm the canvasI correct it once.
  3. Claude
    Trace the flowFrom entry to the success event, past the screen. For one enquiry:
    1. Horse owner taps Send
    2. Server saves it
    3. “Sent”, reply in 48 hours ← the screen says done
    4. Yard notified, message hidden leaks
    5. Yard opens the enquiry
    6. Yard accepts, 5 for life leaks · the job is done
  4. Me
    Pick 2–3 stepsThe riskiest ones.
  5. Claude
    Deep passThe step’s type picks 8 of the 18 lenses.
  6. Claude
    Report3 P0 · 8 P1 · 3 P2, plain English first.
Figure 1 · One stress-test, start to finish: OpenStable’s first-enquiry flow, 28 August 2026. The trace follows the request past the screen to the yard.

Two of those slots carry the method. What makes them give up becomes the list of places to look for leaks. What looks like success but isn’t is the gap from the enquiry story, and it usually sits one step before the real success event. If I can’t fill those two in, the skill makes that its first finding. If the team can’t say how a flow fails, I doubt the product guards against it.

Then Claude traces the flow through the code, from the entry screen to the success event, one line per step. It follows the request past the client, to the API, the database, the background job, the email or push notification, and the screen of the person on the other end. Each trace ends with two lines:

  • The delivery chain: each hop from “submit” to the job being done, with the weak links marked.
  • The friction ledger: steps, required fields, decisions without a default, round trips before any value, and a rough time to value.

I pick the two or three riskiest steps for a deeper pass. The skill has 18 lenses, from error recovery and cognitive load to trust and accessibility, and the type of step decides which apply. A form gets prevention, recovery and microcopy; a wait gets visibility of status. Running all 18 on each step would bury the findings in noise.

A few rules keep the output honest:

  • Each finding cites its evidence and says how strong it is: confirmed in the code at a file and line, inferred from one, or needing a visual check. Code can’t tell you how big a button looks, so the skill tags those claims instead of stating them.
  • Each finding defends itself against the job. One that can’t is a taste note and goes to the bottom, unranked.
  • Twins are in scope. If the same step exists on the web and in the iPhone app, or for two kinds of user, the difference between them is a finding.
  • It doesn’t edit code. It proposes fixes for serious, mechanical problems and hands the rest to design as open questions.
  • Each finding opens in plain English. The report starts with what the user experiences, with no file paths, so someone who never opens the code can decide what to fix.

What a stress-test finds

3 P0
on OpenStable’s first enquiry, August 2026

The enquiry stress-test found three P0s and eight P1s. The worst was on the iPhone app. Someone browsing without an account who tapped Enquire went through picking a role, entering an email, typing a code, deciding on Face ID and allowing notifications, and then landed on the Home tab. Nothing brought them back to the yard, so they had to find it again and start over.

You wouldn’t catch the rest by looking at screens one at a time. The website wouldn’t let you enquire without a horse on file; the app never asked; the server cared only if you had two. Sending waited on two emails before it answered. And the analytics recorded “enquiry sent” the moment the server said OK, with nothing for the moment a yard accepted, so the dashboard counted the anti-success as success.

I’ve run it 43 times since May: 25 on OpenStable and 18 on Nala.

ux-audit: the whole product, at once

47 journeys
mapped in Nala before the audit started hunting

A stress-test tells me whether one flow works. To know which flow to worry about, I need the whole product in view, and that’s too much for one conversation. ux-audit splits the work across many agents.

ux-audit · Nala · quick run · 12 Nov 202623 agents · dashed boxes run in parallel
  1. 3 recon agents, in parallel
    Map the product
    • Journeys
    • Screens
    • Code habits
  2. Agents
    43 journeysA draft canvas for each.
  3. Me
    CheckpointI confirm the table once. 5 hunted, 38 deferred.
  4. 5 hunters, in parallel
    Stress-test each journey
    • Pay and get a workspace
    • Finish a sign-up
    • Wait out the first analysis
    • Buy more interviews
    • Fix a failed payment
  5. Agents
    Critic+4 journeys proposed for a next wave of hunters.
  6. 14 verifiers, in parallel
    Try to refute each candidate
    • 12 confirmed, P1
    • 2 need validation
    • 0 rejected
  7. Code
    2 scriptsCheck evidence and coverage.
  8. Code
    Report“partial: 5 of 47” on line one.
  9. Code
    3 stubsThe worst journeys, ranked.
  10. Next
    ux-stress-testPicks up each stub with me.
Figure 2 · One audit, start to finish: a quick run on Nala, 12 November 2026. I take part once; everything after the checkpoint runs without me.

In order:

  1. Recon. Three agents map the product: the user journeys, the routes and screens, and the codebase’s own patterns, like how it shows a toast or records an event. Each journey gets a draft canvas, and where I’ve already stress-tested a journey, the audit uses that confirmed canvas instead.
  2. One checkpoint. I see the journeys in a single table, with each one’s success event, anti-success and reasons users give up, and I accept, edit, merge or drop rows. The audit asks me nothing else. Any canvas I skip stays unconfirmed, and its findings can’t rank above P1.
  3. Hunters. Each hunter is an agent that takes one journey and stress-tests it. The audit reads its lenses and severity rules from the stress-test skill itself, so the two can’t drift apart.
  4. Critics. A critic looks for what the hunters missed: screens nobody visited, journeys recon didn’t map. The audit sends a new wave of hunters after what it proposes.
  5. Verifiers. Each candidate finding goes to a fresh agent whose job is to refute it. A finding gets a severity only if it survives, with evidence in the code or from the live app. The audit lists the rest with the fact that would settle them, or records them as rejected with the reason, so the next run doesn’t raise them again.

Before I write a word of the report, two small scripts check the output. One confirms each finding has its evidence. The other checks coverage: each journey has to end hunted or deferred with a reason, and each screen reached or explained. If either check fails, the report has to open by saying the run is incomplete.

A default run uses about three recon agents, twelve hunters, two or three critics and one verifier per finding. A quick run hunts the top five journeys. A deep run hunts them all and keeps going until a critic finds nothing new.

What an audit finds

12 P1
confirmed in a quick audit of Nala, five journeys deep

On 12 November I ran a quick audit on Nala. Recon mapped 43 journeys and the critic proposed four more. The quick profile hunted five, starting with the ones where money changes hands or a user first gets value.

It confirmed twelve P1s and no P0s. The worst was the first analysis. Nala tells you that you can close the page while it analyses your interviews, and then nothing tells you it’s finished: no email, no notification, no change in the tab. If the analysis failed, the studies page filed it under “not set up yet”. The job ends when the editor reads their Brief, and nothing on the chain brought them back to it.

Billing came second. A failed card payment sent one email, then nothing during Stripe’s later retries, and nothing on the day the workspace turned read-only. An owner who missed that first email found out when their team couldn’t work.

Each screen in those journeys did what I built it to do. Two findings went to “needs validation”, because settling them meant a Stripe test clock and a card change, and the audit isn’t allowed to make either.

How they fit together

3 stubs
the audit’s hand-off to the next stress-tests

The audit ends by ranking journeys for a stress-test: a confirmed P0 first, then a blocked hunt, then the most P1s. For the top three it writes a stub, a canvas and a trace already filled in, and the next stress-test starts from it. A finished stress-test hands its confirmed canvas back to the next audit, so each run makes the other cheaper.

Each run makes the other cheaper
ux-auditWide. Runs on its own, and trusts only what a verifier failed to refute.
3 stubs: canvas, trace, findings
its confirmed canvas seeds the next audit
ux-stress-testDeep. Runs with me, so it can ask what the job really is.
Figure 3 · The audit tells me where to look; the stress-test tells the next audit what each job is.

The audit is broad and runs without me, so it trusts nothing it can’t verify. The stress-test is narrow and runs with me, so it can ask me what the job is. I still have to talk to users. A canvas is only as good as what I know about the people using the product, and my best ones came out of interviews.

Both have limits. Reading code can’t tell you how heavy a button looks, so the skill tags visual claims until I run it against the live staging app. Signed in there, it can browse and type but can’t submit, pay or send, so some steps still need me. And a quick audit covers five journeys out of 47, which the report states on its first line.

Working alone, I can’t walk each flow myself every time it changes. These two keep asking, on my behalf, whether each one still does the job.

Talk about this post

Get in touch

I’m happy to walk you through the work behind it.