Most weeks have one big thing. This one had a shape instead: the same problem
showing up in three codebases at once, a brand-new product going from git init
to working calculators in six days, and a Unity park that finally looks painted
instead of plastic.
The Same Card, Three Times
I wrote one Notion card — "improve iPad compatibility and adaptive layouts for the future foldable iPhone" — and then filed it against Uncaption, Mind Journeys and Speakit separately. All three apps had the same bug for the same reason: every screen was laid out for a ~390pt phone. Put any of them in a Stage Manager window, on an iPad, or on an unfolded foldable, and cards, chat bubbles and body copy stretch to the full width until a paragraph is one long line.
The fix is the same sentence in all three repos, and it's the part worth
remembering: size from the space the view is actually given, never from the
device idiom. No UIDevice.current.userInterfaceIdiom, no size classes, no
UIScreen.main.bounds. Just width caps — 640pt for reading, 900pt for hub
screens, 460pt for sign-in — keyed off the proposal the view receives. A
windowed iPad, a Split View pane and a foldable then fall out of one rule for
free, and nobody has to enumerate devices that don't exist yet.
In Speakit I built it as a small custom Layout rather than
.frame(maxWidth:). A frame always expands and re-aligns its content, which
would have nudged existing phone layouts by a pixel or two — enough to make the
diff untrustworthy. The Layout passes the proposal straight through until the
offered width exceeds the cap, so iPhone rendering is provably unchanged. On
iPad the tab bar also becomes a sidebar. Uncaption stayed TARGETED_DEVICE_FAMILY = 1 on purpose; iPad is switched on locally as a test bed only.
Uncaption Gets Rejected by Its Own Ghosts
Uncaption 1.8.0 went to App Review three times, and twice it came back because of code I deleted in 2024.
Build (2) was rejected under Guideline 2.1: the app declares App Tracking
Transparency, but reviewers couldn't find the prompt. Correct — there is no
prompt. The Facebook SDK and the only file that ever called
requestTrackingAuthorization were removed over two years ago.
NSUserTrackingUsageDescription stayed behind in the plist, and a later commit
helpfully gave it advertising copy, so the app had been claiming to track
users while doing nothing of the kind.
Build (3) came back under 5.1.2(i) for the same species of leftover: a
CFBundleURLTypes entry registering the Facebook App ID URL scheme. Also dead.
Also the last thing in there.
The lesson isn't "delete your dead code" — I did delete it. It's that removing an
SDK leaves a trail in Info.plist, in eight localised InfoPlist.strings files,
and in the App Store Connect privacy declarations, and none of those are things
the compiler will ever complain about. Deleting a dependency should come with a
grep of every plist key it ever asked you to add.
Calculador Neto, From Nothing
The new one. Calculador Neto is a Spanish net-income calculator: what an autónomo or an employee actually keeps in 2026, after RETA contributions and IRPF. It went from an empty repo on Monday to a working two-mode calculator with SEO, a content system and a publishing pipeline by Saturday.
The ordering was deliberate: design tokens, then components, then the tax engines as pure TypeScript with no framework anywhere near them, then the UI on top. The 2026 IRPF engine handles state and regional scales; the RETA engine has the fifteen tramos and the contribution-income rule; the employee engine does Social Security and withholding. Tax rules are the part that has to be right and the part most likely to need re-verifying next January, so they live where they can be unit-tested in isolation and swapped a year at a time.
The other half is the publishing side. Git is the editorial source of truth — an
MDX file in content/blog/ becomes a static page when it merges, with a
front-matter schema that requires a taxYear and a category from a fixed list.
Then AGENTS.md and a content-pipeline routine teach Claude Code and Codex how
to take an approved Notion card to a finished PR: preflight, claim the card,
research and verify the tax figures, write, open the PR, and stop. If the figures
can't be verified against a source, it fails loudly instead of guessing. There's
a plan for a manual editor at admin.calculadorneto.es that writes through the
GitHub API to a content/<slug> branch — still just a plan and some scaffolding,
and deliberately no CMS and no content database.
The Park Gets Painted
Puppy Pats spent the week on one thing: making Central Park look like the reference image instead of like Unity's default lighting. Everything now shares one toon light model where the sun term is cut into shadow, midtone and lit bands, with the band colours authored per material family — so shadows keep the surface's hue instead of turning sky-blue, which is the thing that reads as "plastic" more than any other single choice.
Trees became balls of outward-facing sprite cards with normals pointing away from the clump centre, so a crown lights bright on top and dark underneath. The ground is painted per pixel through a per-park ground map with dirt paths built from distance fields. Grass, wildflowers and reeds are cards now. Wind sways all of them, including in the shadow and depth passes.
Best gotcha of the week: I pulled the painted pass onto the Unity machine and it
came in wrong — the ground map as sRGB, the leaf atlas capped at 1024, no bark
shader. Unity imports new textures before it compiles the new asset
postprocessor, and it only re-imports when a postprocessor's version number
changes, not when its code does. So the new rules existed and nothing had been
run through them. GetVersion() => 1 fixed it, and now every rule change bumps
the version.
Elsewhere: Speakit got the storage for a scenario-first pivot — curriculum targets → scenarios → attempts → evidence → proficiency, with proficiency and confidence-in-that-proficiency as separate columns, because one number conflates "probably good" with "we barely looked." And I spent an evening poking at eve, Vercel's agent framework, which is why there are two throwaway repos in this week's log doing nothing in particular.
Next up: get 1.8.0 past review on the third try, put real content through the Calculador Neto pipeline, and see the painted park running on a phone.