●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October●FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtask●LIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its own●MCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS●PLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callback●SONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15●IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
In early May 2026 I started a sweep across the four iOS wallpaper apps I run on the side. The trigger was a more concrete signal that StoreKit 1 will be deprecated, plus a need to align AdMob mediation settings across three of the titles. I only have one Mac mini M5 at hand, so opening four Xcode windows and doing it by muscle memory would have buried me. These are the notes from handing most of the work to Claude Code instead.
I am Masaki Hirokawa, an artist and indie developer running a portfolio of wallpaper apps since 2014. The four main titles (Beautiful Wallpapers, Dolice Wallpapers, Ukiyo-e Wallpapers, Illustration Wallpapers) have shipped over 50 million downloads in total. This was the first time in a while I needed to touch all four at once, and a number of judgment calls only surfaced because of the parallel work.
Deciding what NOT to touch before writing a single line
The first half-day went into deciding what not to touch. The more I bundled into the release, the more a single rejection could freeze every title at once. So I drew the line like this:
Change everywhere: the StoreKit 2 migration itself, the AdMob SDK version, the ATT prompt copy, and the privacy manifest.
Vary by title: pricing tiers, new IAP slots, UI tweaks.
Leave alone this round: the api.dolice.asia backend response schema, image storage redirects, and the in-app review prompt.
Writing this boundary into the CLAUDE.md that Claude Code reads first kept its suggestions from sprawling. On day one, Sonnet 4.6 (called from Claude Code) tried to propose UI refactors a few times; after I added the "no UI changes" line to CLAUDE.md, the suggestions converged on architecture-level work.
Laying out four repos on a single Mac mini
Memory pressure was the real bottleneck. I ended up running four Claude Code sessions in parallel from the terminal while keeping only a single live Xcode instance.
# Four parallel Claude Code sessions via ghostty + tmuxtmux new-session -d -s bw 'cd ~/repos/beautiful_wallpapers && claude'tmux new-session -d -s dw 'cd ~/repos/dolice_wallpapers && claude'tmux new-session -d -s uk 'cd ~/repos/ukiyo-e && claude'tmux new-session -d -s il 'cd ~/repos/illustration_wallpapers && claude'# Only one Xcode at a time; let Claude Code drive Swift Build CLI elsewheretmux attach -t bw
Memory settled at 18-22 GB of 28 GB, and the swap spikes I used to see with two Xcode windows (over 8 GB of swap) disappeared. Each Claude Code session ate around 600-900 MB, so four of them stayed well under 4 GB combined.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Memory and workflow numbers from running four iOS apps in parallel on one Mac mini M5
✦Five branch points where indie developers commonly stumble during the StoreKit 2 migration
✦Pitfalls when handing the AdMob + StoreKit entitlement decision over to Claude Code
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Five branch points indie developers will hit on the StoreKit 2 migration
The Apple docs are clean enough on the move from StoreKit to StoreKit 2, but handling four titles at once exposed a different shape of difficulty. Here are the five branch points I actually hit.
1. Server-side or client-side receipt validation?
Transaction.currentEntitlements lets you stay fully client-side. But if you take subscription abuse seriously, you want server validation. I unified all four titles on a server path backed by App Store Server API, exposing a new endpoint on api.dolice.asia. The client still reads Transaction.currentEntitlements as a fallback, and only falls back to the server verdict when the two disagree.
2. When to subscribe to Transaction.updates
If you don't subscribe to Transaction.updates in the very first Task after launch, you'll miss the completion event when a user comes in from an App Store promotional offer. I asked Claude Code to set the subscription up inside @main, and the first draft put it inside App.init synchronously - shaving about 200 ms off the cold start in the wrong direction. It took an explicit "use a detached background task" prompt to fix it.
3. Caching Product.products(for:)
Product.products(for:) has internal caching, but calling it on every screen open still spins forever when the device is offline. I now persist the most recent Product.id and displayPrice in UserDefaults and return the cached price when the network is unavailable. Claude Code's first stab tried to Codable-encode the whole Product, which doesn't compile because Product isn't Codable. Adding that constraint to the prompt up front would have saved a round trip.
4. Price mismatches between TestFlight and production
The displayPrice in sandbox sometimes differs from production. When a user unlocks Pro via an AdMob rewarded ad, you can end up with sandbox showing ¥120 while production shows ¥150, which makes UI copy go strange. Splitting the price label into "server-recommended price" and "StoreKit-supplied displayPrice" let me ship the production review without a single round of feedback on copy.
5. Forgetting to remove the old SKPaymentQueue listeners
This was my biggest stumble. If you keep SKPaymentTransactionObserver while subscribing to Transaction.updates, IAP state notifications fire twice. Four finishTransaction was called twice crashes showed up in TestFlight just before release, caught only at the final staging step. Asking Claude Code to grep for legacy StoreKit traces surfaced 17 spots across the four repos using SKPaymentQueue.default(); I am still working through them one by one.
Pitfalls when delegating the AdMob + entitlement decision
I did not use RevenueCat this round; the stack is Apple-native StoreKit 2 plus a self-hosted server check. AdMob rewarded ads still grant Pro access through a reward_action_count field that has lived in the backend since the very first version. That has to be OR'd with the StoreKit subscription verdict.
@MainActorfinal class Entitlement: ObservableObject { @Published private(set) var isPro: Bool = false func refresh() async { let subscribed = await checkSubscription() // From StoreKit 2 let rewardUnlocked = await checkRewardUnlock() // From AdMob + server let serverGrant = await checkServerEntitlement() // From self-hosted API isPro = subscribed || rewardUnlocked || serverGrant } private func checkSubscription() async -> Bool { for await result in Transaction.currentEntitlements { if case .verified(let tx) = result, tx.productType == .autoRenewable { return true } } return false }}
When I asked Claude Code for "a helper that ORs the subscription and reward verdicts", the first version short-circuited - if the subscription returned true it skipped the other checks. In practice you sometimes want to honor the remaining reward window even right after a subscription is canceled, so I want all three signals evaluated every time. It took another prompt round to make that explicit.
Phased rollout schedule
Releasing four titles in lockstep would have been too risky, so I staggered them like this:
Day 1: Ukiyo-e Wallpapers (the smallest DAU base) at 1% rollout.
Day 3: Bump it to 10%, watch Crashlytics and AdMob reports for 24 hours.
Day 4: With Ukiyo-e clean, start Illustration Wallpapers at 1%.
Day 7: Ukiyo-e to 100%, Dolice Wallpapers to 1% on the same day.
Day 10: Beautiful Wallpapers last at 1%, because it carries the biggest revenue weight.
That order is what caught the Transaction.updates double-fire in Illustration Wallpapers on day 3, and let me fix it before Beautiful Wallpapers ever went live. If I had shipped everything together, a full rollback would have cost roughly two days of ad revenue across the portfolio.
What I gave to Claude Code, and what I clawed back
Looking back at May, the tasks that paid off when handed to Claude Code were:
Applying the same diff pattern across four repos (the SKPaymentQueue cleanup).
Cross-checking ATT prompt copy and the privacy manifest for consistency.
Drafting the first cut of the AdMob + StoreKit entitlement helper.
Pre-release notes in four languages.
What I had to claw back:
Japanese copy review (the polite-form drift was hard to spot).
Reproducing a TestFlight-only crash (the suggested reproduction never actually crashed).
Phased rollout percentage decisions (suggestions jumped to "go 50%" without considering business days or DAU shape).
What I am trying next
The May sweep landed at a stable rollout across three of four titles, with no major crashes. The next experiment is wiring Claude Code Hooks into a git commit step to auto-check that no SKPaymentQueue traces remain and that Transaction.updates is always being set up under Task.detached.
Parallel work is past the limit of a single pair of hands, but a Mac mini with one developer can carry four titles if you give Claude Code a clear "do not touch" list and a hard ordering. If you run a similar portfolio, I would start by piping just one title through Claude Code for a week and see what the operational friction looks like before scaling up. I am still finding my own footing here, but I hope these notes are useful.
Share
Thank You for Reading
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.