In one minute

All essays

Lore Week 3: moving an app to a new model provider, where a usage cap must live, and how a platform deadline shapes code

Our week at a glance

66 merged, about 2 hours from open to merge (median)

  • 66merged this week
  • 43last week
  • 122.6minutes, medianabout 2 hours

checks blocked

Merges by product

  • New product, not announced23
  • Lexicon15
  • AutoReal13
  • Internal tools11
  • guno3
  • lorelabs.co1

Lab, weekly paper. Week of Mon 26 Oct to Sun 1 Nov 2026, Europe/Istanbul.

This week we merged 66 pull requests, up from 43 last week. The typical one merged about 2 hours after it was opened (median 122.6 minutes). A new product we have not announced took 23 merges, Lexicon, our exam prep app, 15, AutoReal 13, guno 3 and lorelabs.co 1. The other 11 went to internal tools.

The week's most useful lesson came from Lexicon. In a few days it moved every AI model call to a new provider, behind one module, and added a daily cap that the server counts. We also watched a platform deadline reach into two of our apps, and we learned to read a red cross before counting it.

We want to teach three new ideas this week, and one short note:

1. Moving an app to a new model provider in stages. One module owns every call, and the old path leaves last.

2. Where a usage cap must live. On the server, before the model is called, and closed when it cannot count.

3. A platform deadline shapes code. Why two of our iPhone apps changed how they are built because of a rule in next year's iOS.

4. Short note: a red cross is not always a failed test.

If you are new here: CI, check minutes and why a check can stop for lack of budget were the topics of Lore Week 2. Branches, small changes and approval gates were the topics of Lore Week 1. We do not repeat them here.

Method

How we looked at the week

We listed merged and open pull requests in each of our repositories, read every one, looked at all 466 automated check runs, and compared each product's state file, a short document that says what exists right now, at the start and end of the week. This count includes internal repositories, so it is larger than the Lab build log. The build log now counts Lexicon as a product and keeps our internal work on a separate line. Where a figure comes from a pull request's own text and not from our count, we say so.

Two events framed the week. First, a correction: last week we counted at 18:12 on Sunday and reported 38 merges, and five more merged that evening, so Week 2 really had 43. Second, from 27 to 30 Oct no automated check could start on our account, because billing for check machines had stopped. We explained how that budget works in Week 2. Work went on, and pull requests said where their checks ran instead. Checks ran again from 31 Oct.

Idea 1

Moving an app to a new model provider

One door

Reviewed in two pull requests.

What it is. Lexicon helps people prepare for the verbal parts of TOEFL, IELTS and GMAT. Behind the app, a server function asks an AI model to write practice words, paragraphs and quizzes. This week we moved those calls from one model provider to another.

Why it matters. Changing a provider is not swapping one address for another. Each provider has its own request shape, its own limits, its own way to refuse, and its own price. If model calls are spread across many files, a move touches all of them at once, and a mistake reaches every user at the same time.

How it works, in simple words. Think of a building with one main door. If every delivery comes through that door, you change the lock once. Software has two well known patterns for this. An adapter layer, which Microsoft's architecture guide calls an anti-corruption layer, sits between your app and an outside system and translates between them, so the outside system's habits do not leak into your design. The strangler fig, as Martin Fowler describes it, replaces an old system gradually: the new path grows next to the old one, traffic moves over piece by piece, and the old path is removed last, so that investment and return both arrive gradually and visibly.

What happened to us. We did it in steps. First a written plan. Then one module that owns every model call on the server: every route goes through it, and it alone talks to the new provider. Small scripts outside the app still use the old provider; they move in a later step of the same plan, on purpose. The module also decides what counts as an answer. A refusal, an answer cut off by the token limit, or an empty answer is returned as a named error, never as an empty card. Its tests run against a fake client, so they check the request shape and every error case without a paid call.

What others say. Fowler argues for the gradual path because both the investment and the return stay visible at every step. Microsoft's guide is frank about the cost: the layer adds latency to every call, and it is one more piece you must manage and maintain.

How we compare. One owner for every call made the change small enough to review in two pull requests. The cost is that the scripts are still split across two providers, and we have not yet compared the quality of the new provider's answers against the old one on the same prompts. That comparison is the next step.

Idea 2

Where a usage cap must live

The cap sits on the server

What it is. A usage cap limits how many model calls one account can make. In Week 1 we explained why paid model calls need a ceiling. This week is about where that ceiling must sit.

Why it matters. An app on a phone can be changed, copied or imitated. A limit that only the app checks is a suggestion. OWASP's Authorization Cheat Sheet says it plainly: developers must never rely on client side access control checks, and such checks must be performed on the server. The same sheet asks for deny by default: when in doubt, refuse.

How it works, in simple words. A ticket gate at a station counts at the gate, not on the passenger's honour. And if the gate's counter is broken, the safe gate stays shut instead of waving everyone through. Engineers call that failing closed.

What happened to us. Lexicon now has a daily cap of 60 model calls per account, counted on the server by the same module that owns the calls. A call past the cap is refused before any model is asked, with a clear message for the user. If the guard cannot count the call, for example because the database does not answer, the call is refused too, with a different error. Users can read their own count but cannot change it. The tests cover the day boundary, the refusal at the cap, and a burst of calls sent at the same moment, which is how a naive counter lets extra calls slip through. Even the free mock model used in local tests counts toward the cap, so the limit is tested the same way it runs.

What others say. OWASP's list of risks for LLM applications names the cost side "unbounded consumption", which we covered in Week 1. The cheat sheet adds the placement rule: one check that is missed is enough for an attacker.

How we compare. The placement follows the guidance. The number does not yet come from data: we picked 60 a day, and we have not measured whether real users hit it or never come close.

Idea 3

A platform deadline shapes code

Two paths, one deadline

What it is. Apple builds iPhone apps with a software kit, the SDK, that changes every year. Apple's documentation now states that beginning in iOS 27, apps built with the latest SDK must adopt the scene based life cycle, a newer way to start and manage an app's screens, or they fail to launch.

Why it matters. The rule does not wait for you to change your code. It is triggered by the tool you build with. A build machine that quietly moves to the newest tools can turn a working app into one that closes at launch.

How it works, in simple words. Imagine a city that changes its building code on a known date. Houses already standing are fine. A house built under the new code must have the new fire exit, or it cannot open. You can either build the exit, or keep using the old approved plans until you do.

What happened to us. Our two iPhone apps took the two paths. guno pinned its build machine image to the previous Xcode version, and its state file explains why: the newest image would build with the iOS 27 SDK, and without the scene life cycle the app would be killed at launch. Lexicon took the other path and upgraded its app framework by several versions, and its app now carries a scene delegate, the piece the new rule requires.

What others say. Apple's technical note TN3187 explains how to migrate, and it shows that the deadline was announced early: UIKit has logged a warning about it since iOS 18.4.

How we compare. Pinning is fast and safe for now, but it is a debt with a date on it. guno's state file names the exit condition: move to the new tools only with scene support. We will report when it does.

Short note

A red cross is not always a failed test

A red cross, read twice
  • 466check runs
  • 191failed
  • 181under 30 seconds, of the 191

That reads as 41 percent. 181 ended with no step. They were refused during the billing block.

Of 466 check runs this week, 191 failed. That reads as 41 percent. But 181 of those 191 ended in under 30 seconds without running a single step: they were refused during the billing block, not failed by our code. Before you count a red cross, look at how long it ran and how many steps it took. From now on we report refusals and real failures as separate numbers.

Lessons

What we learned

Numbers from the lessons
  • 16.0days, two files
  • 6.4days, two files
  • 14percent, same line count
  • 85percent, Lexicon state file
  • 33,000lines added, about
  • 370files in that change

The two slowest pull requests changed two files each and took 16.0 and 6.4 days. One state file grew 14 percent in characters and held its line count. Lexicon's grew 85 percent. One pull request added about 33,000 lines across 370 files.

One door is easier to guard. Moving every model call into one module made both the provider change and the cap a small, reviewable change.

A limit is only real where the user cannot reach it. The cap is counted on the server and stays shut when it cannot count.

Your build tools can break your app without a code change. A pinned build image bought time, and a framework upgrade paid the debt.

Count after the day ends. Our Week 2 count missed five merges because we counted too early.

Small changes waited longer this week. The two slowest pull requests changed only two files each and took 16.0 and 6.4 days. Last week the slowest were the biggest.

State files keep growing in characters. For the third week running, one product's state file held its line count while growing 14 percent in characters. Lexicon's grew 85 percent.

What surprised us. The new product we have not announced landed one pull request with almost 33,000 added lines across 370 files, built by a team of agent roles in one run. We do not yet know how to review a change that size well.

Next

What happens next

From our open pull requests, plans and decision records. Anything marked Proposal is ours and is not decided.

  • Lexicon: a plan to launch on the App Store, the last scripts moving to the new provider, and a performance pass on the app's tabs.
  • AutoReal: subscriptions and a new post engine for everyone, both as drafts behind switches that stay off until approved.
  • lorelabs.co: product pages, and a build log that counts more of our repositories while keeping internal work apart.
  • guno: a tester round and usage caps on the server.
  • Work continues on the new product we have not announced.
  • Proposal: before switching any app to a new model provider, run the same prompts through both and compare the answers.
  • Proposal: set each usage cap from a month of real use, not from a guess.
  • Open questions: when does guno move to the new build tools? How should we review a change of 33,000 lines?

Limits

What we could not verify

  • Whether the new provider's answers are as good as the old ones. We did not compare them.
  • Whether 60 calls a day fits real use.
  • Billed minutes for the month and the exact time checks were restored.
  • Our hosting platforms' own deploy logs, and direct pushes that never became a pull request.
  • External claims are as each source states them.

1 Nov 2026

Sources

We opened each link on 1 Nov 2026 and checked that it loads and says what we use it for.

  1. Martin Fowler, Strangler Fig Application: replacing a system gradually, with investment and return both gradual and visible.
  2. Microsoft Azure Architecture Center, Anti-corruption Layer pattern: a facade or adapter layer that translates between subsystems.
  3. OWASP Cheat Sheet Series, Authorization Cheat Sheet: never rely on client side checks; deny by default.
  4. OWASP GenAI Security Project, LLM10:2025 Unbounded Consumption: the cost risk of model calls (taught in Week 1).
  5. Apple Developer, TN3187: Migrating to the UIKit scene-based life cycle: the requirement, the warnings since iOS 18.4, and how to migrate.
  6. Apple Developer, Transitioning to the UIKit scene-based life cycle: "apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch", beginning in iOS 27.

Everything else is from our own records, taken 1 Nov 2026. Our repositories are private, so we link nothing from them.