Weather Text Work Location Mode #

“The coldest winter I ever spent was a summer in San Francisco.” — Mark Twain

While Mark Twain did not actually say that, I do share the sentiment - I commute into the city (from Cupertino) 3 days a week, and every summer there are days when I realize I am underdressed for the temperatures that await me. I have long wanted to add a “work location” mode to Weather Text, my bespoke weather app. The tedium of coding the configuration UI had always put me off the idea, but it’s now mid-2026, which is a different development era than mid-2024, when the app originated1. Prompted by a recent heat wave, I had a conversation with Codex and ended up with pretty much what I always wanted:

Weather Text work mode screenshots: watch face with complication showing a 20 degree difference between home and work, and the settings to configure work mode

It turned out Codex missed some edge cases and a bit of visual polish, but the feature has been working for me pretty much as intended.

With the app getting this bit of polish, I figured I should capitalize on my momentum and actually publish it to the App Store. Doing a TestFlight build every 90 days got old after the first few times. Worse, I’d occasionally forget, and it would take me a while to notice (the failure mode is a stale complication on the watch face, and the weather around here does not change often).

My rationale for having it be TestFlight-only was that I didn’t want to accidentally end up over the free WeatherKit quota. But the other thing that’s different in 2026 is that the flood of AI-generated apps means that the average app is even less likely to be installed2. With the weather app segment being especially crowded3 I figured the odds of getting unexpected installs were near zero.

I was prepared for some runaround with the App Store review process, since it uses Apple weather data (which has attribution and display requirements) and needs location access. It’s also a watch-only app, which is a more obscure category (and presumably has fewer reviewers assigned). But so far it’s sailed through both submissions I’ve made.

The upshot is that you can now install Weather Text more directly, but please don’t tell your friends.

  1. When I had to copy/paste coding snippets from ChatGPT with gpt-4o, uphill in the snow both ways. 
  2. Since it was somewhat annoying to track down: source article of that graph, which in turn is based on this paper
  3. Even a direct search for the app name does not find it. 

How I consume Bluesky (and Twitter and Mastodon) #

tl;dr: I’ve added Sky Feeder and Tweeter Feeder to join my earlier Masto Feeder tool, allowing me (or anyone else who signs in) to follow accounts from all three social networks in a feed reader.

NetNewsWire showing Bluesky, Mastodon and Twitter posts

In what has now become a trilogy of posts1 I thought I would write up some of the custom tooling that I’ve written to allow me to keep up with Bluesky in a feed reader (in my case, still NetNewsWire). The social network started to take off in mid-2023, and though Mastodon covered my tech/retro interests pretty well, there were some folks (especially wraiters) who had migrated over. I initially used SkyBridge to allow me to treat it as a Mastodon instance in Masto Feeder, eventually moving to my own fork/instance after the shared one got overwhelmed. But the indirection2 and friction when hacking on it3 made it so that it never felt quite right. Some content (like videos) was lost in translation, and the lack of control over the timeline was frustrating - the options were either all replies (including to accounts I don’t follow) or none at all (which filtered out threads). I kept thinking of making my own native tool in Stream Spigot to generate a timeline feed for Bluesky, but the activation energy was too high.

In the meantime, Future Mihai did finally modernize Masto Feeder - in April and May of 2024 I rewrote it from Python 2.74 on App Engine as a SvelteKit app running on Cloudflare Workers. This was a way to both coalesce things on my preferred hosting platform and experiment with a new framework. I made some tweaks since then, but have not had a reason to touch SvelteKit much on this or any other project. Thus the codebase became effectively foreign to me, especially since Svelte 5 introduced runes and made other changes that I had not kept up with. This was definitely a contributing factor to the high activation energy I mentioned above.

Luckily it’s now 2026, and GPT-5.5 (in the Codex desktop app, combined with Context7) is a more qualified SvelteKit programmer than I am or have time to be at the moment. I still have opinions about architecture; for example I directed it to do some preparatory refactoring to share display code with Masto Feeder. The Codex reviews also gave me some additional confidence that I was on the right track, and so Sky Feeder was born. It generates an Atom feed from your Bluesky timeline, publishing it under a “secret” URL that can be used with any feed reader.

The only thing that was missed in the initial Codex-authored PR was how concurrent OAuth token refreshes should be handled - it turned out that serialization was necessary. I doubt that I would have avoided this bug if I had hand-coded everything - it’s the kind of thing that only happens after things have been running in production for a while. Fixing it was also a collaboration with Codex - I had it add additional diagnostic logging and then gave it access to the Cloudflare Observability MCP Server so it could inspect the output and validate the solution.

Empowered by all this, I had Codex build a third tool - Tweeter Feeder for generating a feed for any Twitter/X account. While I had mostly given up on the site, I had been using Nitter’s generated RSS feeds to follow a few people. Nitter is a great service to have around, but it’s quite inflexible5 and even less hackable6. I thus pointed Codex at the Nitter repo, and asked it to reuse the same reverse-engineered Twitter endpoints and auth mechanism for my tool. I only skimmed through the generated code (which has been getting more and more tempting), but it also pretty much worked out of the box, except for some escaping issues7.

Codex also worked out pretty well for more exploratory work. It bugged me that NetNewsWire (possibly due to this change) would show the Stream Spigot favicon for every post, adding redundant visual noise. I had a vague recollection that NNW had some support for per-post images, and Codex validated that the capability exists, but Atom feeds do not populate the avatarURL property of authors while JSON feeds do. It was thus a matter of adding this alternate output mode. Iterating on this was made pretty easy by the Codex remote control feature - it was pretty satisfying to see a bug in NetNewsWire on my phone, switch to the ChatGPT app, find the Codex thread and ask it to fix and redeploy the worker, go back to NNW and see the fix when refreshing the feed.

Stream Spigot homepage showing three tools: Masto Feeder, Sky Feeder and Tweeter Feeder
Stream Spigot: no longer a unitasker

This post came out as being more pro-Codex than I expected. I am also mourning my craft and am not sure how it will all turn out. While I have some general sense of the architecture of this codebase (since the fundamental decisions still date back to the hand-crafted era), I definitely don’t have the same intuition or map of everything in my head that I’ve had with other side projects. I did force myself to fix the escaping issue mentioned above by hand, just to prove that I can find my way around, but we’ll see how long that feeling lasts.

  1. See also 2012’s How I Consume Twitter and 2023’s How I Consume Mastodon 
  2. Bluesky (over atproto) to SkyBridge’s reimplementation of the Mastodon API to my tooling in Stream Spigot 
  3. SkyBridge is a server-side Dart app that runs on Fly.io, neither of which I was interested in learning more about 
  4. 😱 
  5. For example, the entire tweet goes into the feed item title, which worked in the days of 140 characters, but not in today’s world of think pieces disguised as tweets 
  6. I had somehow never heard of Nim before this 
  7. That universal constant - I assume the answer to the last question will also contain a wild "