On the integration this post is about, one debugging iteration cost one calendar day. Not a day of engineering — a day of waiting. The constraint wasn't the code, the team, or the agents. It was the test window: thirty minutes a day on somebody else's sandbox.
Post 1 ended with this mock as a teaser. Here is the story, and the rule it left behind.
This June, a different product was heading for a MENA market. Subscriptions there run through a regional payment aggregator, and the register-to-login flow works like this: your page loads a JavaScript library from the aggregator's servers; the library rides the operator's network to identify the subscriber; a PIN goes out by SMS; the user types it back; a server-side call confirms the subscription. Carrier billing — no card, the phone bill is the wallet.
Testing that end-to-end needs three things that do not fit in a container. The aggregator's sandbox — shared, and ours for thirty minutes a day. Header enrichment — the network stamping the subscriber's identity onto the request — which only happens when the traffic rides that operator's mobile data, which means a physical handset with that market's SIM card in it, not an emulator. And people: the developers were in Poland, I work from Thailand, and the sandbox, like the SIMs, was keyed to a market in neither.
The choreography, as I remember it — and this part is recollection; git only confirms the commits on either side of it: a fix is ready by European afternoon. The window sits at an hour that suits the partner, not the fix. So the fix waits. Inside the window you get two, maybe three attempts, if nothing needs a rebuild. Then the sandbox belongs to somebody else again, and your next attempt is tomorrow. One iteration, one calendar day. Very little survives that arithmetic — not velocity, not morale, and not the habit of small safe changes: when attempts are that scarce, every change grows until it feels big enough to be worth one.
The fleet from post 1 exists precisely so that operator-side systems can be mocked. The twist this time was that the thing to mock wasn't a backend contract — it was, in part, a JavaScript library running in the browser. You can't stub that at the network layer and forget it: the page loads the aggregator's script, and the script installs a global object the page then calls. A faithful mock has to serve a library that installs the same object with the same behaviors — issue a PIN when asked, deliver it out of band, verify it for real on the backend.
On June 15th at 00:27 — seventy-one days after the fleet's base-image extraction — the mock's first commit landed: eleven files, 1,423 lines. The served JS library. The PIN flow. Real verification: the API returns success only when the PIN you typed matches the PIN it issued — a mock that can say no is worth ten that always say yes. Docker wiring on the fleet's shared base image, events streaming to the fleet dashboard — and eleven minutes later, the dashboard had a tab for it.
The PINs are never sent to any phone. They surface in that dashboard tab — the same move the fleet's messaging mocks learned in March: if a message's whole job is to reach a human, and there is no human, show it where the whole team can see it. maildev has done this for e-mail for a decade. It works just as well for subscription PINs.
My memory says the mock was one sitting. Git is stingier: a single commit has no duration, so "one sitting" stays what it is — a recollection. What the record does confirm is that March was still paying for June: the mock inherited the base image, the dashboard, the event conventions, and skipped every conversation about what a mock should look like.
The morning version had a flaw, and it is my favorite detail in this story. The library baked in one static phone number: every handset that loaded it became the same subscriber, so only one phone could test at a time. Look at what that is. The mock had faithfully reproduced the exact scarcity it was built to escape — the serialization had just moved from the partner's calendar into our own code. The window was dead, and its ghost was already doing the same job.
The evening commit, at 19:16, exorcised it: each load of the library now draws a fresh number from a pool; a number that completes verification retires and is never issued again; concurrent handsets stop colliding. The dashboard learned to filter by number within the minute, and grew a picker of known numbers seventeen minutes after that. So the honest accounting for June 15th is: the window died in the morning, and its ghost died by evening. The whole story fits inside one calendar day — the same unit that used to buy a single attempt.
The window didn't disappear. The partner's sandbox is still the only place where the real library, the real enrichment, and a real SIM meet, and no mock changes that. What changed is the window's job description. Iteration moved to the mock — any developer, any hour, as many handsets as are lying on the desk. The day after the mock landed, the team's test-automation framework registered the second product as a target it can drive. The thirty minutes became what they probably should have been from the start: a confirmation ritual. You don't go to the window to find out what's broken. You go to confirm that nothing is.
Expensive, slow, or shared resources are for verification — iteration happens on a cheap substitute. That's the rule this incident left behind, and I now apply it in three currencies at once.
Partner minutes: iterate against the mock; walk into the window with a build you already believe in, and spend the thirty minutes confirming it. Agent tokens: the same shape inside the fleet — agents iterate against mocks and fixtures, and the expensive full-environment run is a gate at the end, proving work rather than finding it. And founder attention, the scarcest of the three: my thirty minutes with a piece of work are a verification gate, not an iteration loop — a process that needs me inside the loop is a process that is mis-designed. The protocol formalizes all three as the same structure: hard gates where verification is spent deliberately, so iteration is allowed to be cheap.
This series has a rule that every number must survive hostile checking. This time the check caught the series itself. Post 1 said the entire working mock — "JS library behavior, PIN verification, dashboard tab — landed as a single commit." Git says: the dashboard tab was its own commit, in the dashboard repo, eleven minutes later — and the mock needed a second commit that evening before it could serve more than one handset at a time. Directionally right, flattering in the details. That is exactly the failure mode this series is built to catch, and apparently it applies to published prose as much as to memory. I'd argue the record's version is better anyway: the single commit was never the impressive part. The part worth keeping is that the leftover bottleneck was found and killed the same day — because iterating on the mock cost nothing.
Next post: the day my agents couldn't agree on a tech stack, and what a 'requirements repo' turned out to actually be for.