One Week, Four Products — What Actually Shipped
TL;DR — MangaForge got a full in-browser editor (undo, redo, AI regenerate, filters, the works) built in a single day. Polykal came back from the dead and immediately started a two-day fight with two prediction-market APIs that had quietly changed shape underneath it. I shipped two new products, AgentPost and CancelIntel, before that same week was over. And while live-testing something else entirely, I found a bug that had been silently shortchanging paying users for who knows how long — the good news is it took about twenty minutes to fix once I actually saw it.
None of these were planned as "this week's post." They just all happened in the same stretch, and looking back at the log, it's a decent snapshot of what building alone with a very fast feedback loop actually looks like.
A full manga editor, built in a day
MangaForge lets you generate AI manga/webtoon chapters, but until this week, once a chapter was generated, that was it — what you got was what you shipped. No touch-ups, no redo, no "actually make panel 4 black and white."
In one day, that changed completely. The editor now has: per-panel AI regenerate, image upload, screentone and halftone overlays, border/frame styles, art filters (grayscale, sepia, straight black & white), horizontal/vertical panel flip, speech bubbles you can actually edit as structured data instead of baked-in pixels, panel add/delete/reorder, duplicate panel and duplicate page, and full undo/redo per page. Fourteen features, one day, capped off with a combined end-to-end regression test covering the whole editor rather than fourteen separate ones.
The thing that made this possible isn't any one clever trick — it's that every page in MangaForge already re-renders from a panels JSON blob. Structure and text edits don't need new AI generations or image re-renders, so most of these features are "add a field, add a control, re-render" rather than "touch the render pipeline." Once that pattern was established with the first couple of features, the rest was mostly repetition, which is exactly the kind of work that goes fast when you're not reinventing the approach each time.
Reviving Polykal turned into a two-day fight with two APIs
Polykal — a Kalshi/Polymarket spread-alert product — had been sitting dormant for a while. The revival plan was simple: one product, one price, global alerts, ship it. Flip it back on and it should just work.
It did not just work. Over the next two days I found that both prediction-market APIs had quietly changed shape while the product wasn't looking:
- Polymarket's gamma API started serving
outcomePricesas a string-encoded array instead of numbers, so every single Polymarket price parsed as zero. - Kalshi renamed its
activemarket-status value, which meant "is this market live" silently returned false for everything. - The code was filtering games by
start_date_minwhen it should've usedend_date_min— subtly wrong in a way that let dead games through and hid live ones. - Degenerate prices from the arbitrage math produced five-digit "profit" numbers that overflowed the
matched_marketscolumn outright. - A
pq.Arrayscan target turned out to be flatly incompatible with pgx v5, which took down login with a 500 until I found it.
Each of these on its own is a one-line diff. Together, they're the real lesson: reviving old code is never "flip the switch back on." External APIs don't wait for you to come back, and every dependency you didn't touch while you were gone had two days — or two months — to quietly drift out from under you. The fun part is I also used the moment to rip out the Telegram bot setup step entirely and switch alerts to a native Discord webhook, which cut a whole onboarding step for new users.
Two more products before the week was out
In the middle of all that, two brand-new products shipped:
AgentPost — one API key that lets an agent (or a script, or a cron job) publish to X, Reddit, Telegram, and Discord without wiring up four separate SDKs and four separate sets of credentials. If you're automating anything that needs to post somewhere, this is the "stop reinventing this per-project" version of something I'd built ad hoc, differently, at least three times already.
CancelIntel — a cancel-flow survey widget: the thing you see right before a "are you sure you want to cancel" screen that actually asks why, instead of just letting the churn happen silently. I didn't just ship it — I installed it on my own products' cancel flows (HabitStack and Polykal) the same day, because a feature you don't dogfood on day one is a feature you'll forget to dogfood at all.
The bug I only found because I was testing something else
Separately, I shipped a feature for Tsuyu (StoryGen) that pulls reaction gifs out of Reddit comment threads and splices them into the generated video, with the comment's own text folded into the narration so the joke doesn't get lost. Building it was the easy part. Testing it live was where things got interesting.
While chasing that test, I went looking at why one of the test videos rendered without its background image. That led somewhere I didn't expect: five free gradient backgrounds and seven paid-tier video backgrounds had been referencing asset files that were never actually generated or uploaded to the server — probably since the day they were seeded into the database. Nobody noticed, because the render pipeline is built to fail open: if a background can't be found, it silently falls back to a generic default gradient instead of erroring out. The video still renders, it just isn't what you picked. Which means some slice of paying users have been getting a plain gradient instead of the ocean waves or neon lines they paid for, and the system was never going to tell either of us.
The fix itself was small — generate the five missing gradients, ship them, verify a real render on the live site actually shows the right one now. The bigger fix is a reminder: "the video still rendered" is not the same as "the video rendered correctly," and a pipeline that fails open needs someone occasionally going and checking what it's been quietly swallowing.
While I was in there, I found a second, smaller ghost: the email "from" address was still defaulting to the product's old pre-rebrand domain, months after everyone else had moved on to the real one. That domain was never verified for sending, so every "your video is ready" email had been failing quietly in the background too. Fixed the default, redeployed, confirmed the error message now correctly points at the real domain instead of a dead one — which just leaves one step that's out of my hands: verifying that domain with the email provider, which needs a login I don't have from here.
The takeaway
Nothing above was on a roadmap a week ago. A dead project came back and immediately needed defending against a world that moved on without it. A feature I built for fun surfaced a bug that had nothing to do with it. Two products got built because the gap they filled had already annoyed me three times.
That's most of what solo building actually is: you ship the thing you meant to ship, and you fix the things you find lying around while you're in there. Some weeks that second pile is bigger than the first.
Postscript: the irony of that first sentence wasn't lost on me — so now there's an actual public roadmap. It's a ship log: every product, every day, what actually went out the door. Weeks like this one show up as a very lit-up column.