Shipping Day: Twenty-Four Issues Closed

The Pesach holiday sprint. Eighteen commits, twenty-four Linear issues closed, the first /ship skill invocation ever, and the revive bug I shipped at 1 coin instead of 50.

Eyal Harush10 min read

Until day 16, I'd been working on Geo Climber in the margins: evenings after my day job, weekends, and the ski trip (which was itself a working trip). The pace was aggressive but constrained by the reality that my primary job is running R&D at a fintech, not building this game.

Day 16 was different. Day 16 was the first of the Pesach (Passover) holiday in Israel, which meant a full week off work. No day job. No morning standups. No afternoon meetings. Just Geo Climber.

This post is about what happens when you give an aggressive side project a full day of uninterrupted focus. Spoiler: twenty-four Linear issues close, eighteen commits land, the first economy action ships (and briefly ships at a bug price of 1 coin instead of 50), the /ship skill gets used for the first time, and the "Ship the first playable iPhone beta" epic gets marked Done. Sixteen days from the car conversation that started everything.

Every day is a shipping day

Day 16 wasn't special because it was a "shipping day" in the planned-launch sense. It was special because the velocity was unusual thanks to the holiday context. But the attitude was the same I'd had every day since day 1: if it's done, it's out.

I'm a one-man show on this project. I have maybe ten TestFlight users, all of them friends and family. At that scale, the cost of shipping a bug is one quick fix and one follow-up commit that lands in ten minutes. The cost of over-caution ("I need to test this more thoroughly before I ship it") is weeks of delayed feedback. The math is obvious. At TestFlight-with-friends scale, you should be shipping aggressively. Move fast, break things, fix them faster than anyone notices.

Every day is a shipping day. I'm hammering this point because it's the attitude that made the next twenty days of the project possible. The people who burn out on side projects are usually the ones who try to build a perfect first version and drown in the perfectionism. The people who ship side projects are the ones who accept that v0.0.2 has bugs and ship the bug fixes in v0.0.3 before anyone cares.

Obviously this attitude has limits. At larger scale (public launch, App Store reviews, hundreds or thousands of users) shipping a broken revive price gets expensive in a hurry. The right attitude at Pesach-week-friends-and-family scale is not the right attitude at post-launch scale. But today I was at the first stage, and the attitude was "ship aggressively, fix fast."

Eighteen commits across the day

Here's the commit list from day 16, in timestamp order, with a one-line summary of each:

  • 00:14 PR #27: changelog module. Admin CRUD + public API + /updates page on the marketing site. Every shipped change now goes in a structured changelog that players can read.
  • 00:26 Checkpoint and changelog agents. Two custom Claude Code agents that will feed into the /ship skill later in the day.
  • 00:49 Refactor UpdatesPage layout. Small polish on the /updates page card structure.
  • 09:33 PR #28: planet selection carousel. Start screen now has a carousel showing Earth (selected) with TRAPPIST-1e teased as a future destination. Groundwork for a potential multi-planet future.
  • 09:51 Ship orchestrator agent. This is the piece that ties the checkpoint and changelog agents together. First real /ship skill scaffold.
  • 12:12 PR #30: RC build 29, UI polish, platform weights, age-gating. A substantial PR. Platform weight tuning per difficulty (easy has more small platforms, hard has fewer), UI polish across menus, and COPPA age-gating on first launch.
  • 12:28 Fix xcodeproj version mismatch (regenerate from project.yml after a version bump).
  • 12:28 CLAUDE.md update: version bumps require explicit approval. A lesson from earlier in the project codified into the project brain. I had been bumping versions without thinking about it, and every bump triggers an App Store review, so I added a rule that says "don't bump without asking me first."
  • 14:12 AdminJS admin panel (GEO-162). Web-based admin UI for the server, built on AdminJS, with CRUD over every Prisma model. Players, runs, transactions, progression, all viewable and editable in a browser from a protected admin URL.
  • 14:12 Gitignore AdminJS generated bundle files.
  • 14:14 Merge AdminJS PR.
  • 14:53 PR #31: GEO-161 coin-spend revive, first purchasable economy action. The game's first real economy transaction. Spend 50 coins to continue a run after dying.
  • 14:55 Fix revive price: 1 to 50 coins (testing value leaked into merge). Two minutes later. More on this below.
  • 15:00 Fix integration tests for revive price 50.
  • 15:02 Mock catalog in economy tests, decouple from real prices.
  • 15:04 Fix catalog GET test, assert structure not exact price.
  • 16:00 Fix revive architecture: decouple purchase from run submission. Post-merge architectural cleanup on the revive flow.
  • 17:02 Polish revive flow: consumable type, domain constants, dev log overlay, negative coin toast. The final polish pass.

Eighteen commits. Seven PRs merged. Twenty-four Linear issues closed.

The revive 1-coin saga

PR #31 shipped the coin-spend revive with the price hardcoded to 1 coin, a test value from local development that I forgot to change back before merging. I noticed two minutes later and pushed the fix. Testers are friends who would ping me on Discord before exploiting a bug, so actual user impact was zero.

But the bug kept paying out. The fix broke integration tests that were asserting the old price, which led to a refactor where the tests mock the catalog instead of hardcoding prices. An hour later, I realized the initial revive architecture had the purchase and the run submission sharing a transaction in a way that was uncomfortably easy to game, so I decoupled them. Then a final polish pass.

Two hours from "shipped with a bug" to "architecture improved, tests hardened, flow polished." On a larger team this would have been days of review rounds.

"Move fast and break things" isn't the full strategy. Move fast, break things, fix them fast, and use the bugs as free architectural feedback. That's what the cliche was originally supposed to mean.

COPPA age-gating and the legal thread

PR #30 (RC build 29) included COPPA age-gating, a first-launch UI gate that asks the player for their age and restricts certain features if they're under 13. COPPA (the Children's Online Privacy Protection Act) regulates how US-based apps handle data from minors. Geo Climber is a casual arcade climber, which means kids are absolutely in the potential audience, which means COPPA compliance is not optional.

The interesting thing is that Claude didn't flag the need for age-gating unprompted. I had to bring it up, because I'd been tracking "legal compliance" as a running concern since day 1. Every few days I'd ask Claude to review the project for compliance gaps. Age-gating had been on my list since before the rebrand, and day 16 was when I finally had the cognitive space to implement it.

Legal compliance is your job, not Claude's. Claude will help implement compliance features once you flag them, but it doesn't proactively audit your project for regulatory gaps unless you ask. I track compliance items in Linear with their own label ("Legal") and review them periodically.

This is the kind of detail that doesn't matter until it matters. App Store reviewers will reject your submission if your game has kids in the audience and no age gate. I'd rather handle that in week three than find out at submission review.

AdminJS: infrastructure that pays off immediately

At 14:12, the AdminJS integration landed. AdminJS is a web-based admin panel that plugs into Fastify and auto-generates CRUD interfaces for every Prisma model. Players, runs, transactions, progression, catalog, all viewable and editable in a browser, behind a protected admin URL.

Why ship this on day 16? Two reasons.

I wanted to see what was happening on the server. TestFlight was producing real runs, real transactions, real player state. Reading the database via psql was getting tedious. AdminJS gives me a dashboard I can check from any browser. And I knew I'd want an admin panel eventually anyway, when there are more users and more support requests. Shipping it now as a 6-file integration that took about an hour is cheaper than shipping it under pressure later.

The first /ship invocation

At 09:51, I used the /ship skill for the very first time.

Context: /ship is a Claude Code skill I'd been building throughout the morning. The skill orchestrates the full release lifecycle: commit staged changes, push, create a PR, run parallel CI checks and code review agents, fix any review findings in-context, merge the PR, clean up the branches, and then post-merge run a changelog agent (which writes to both the backend changelog API and the Discord #announcements channel) and a Linear agent (which updates the relevant GEO-XXX issues to Done).

Before the /ship skill, I was running each of those steps manually. Commit. Push. Open the PR page in my browser. Click review. Wait for CI. Fix issues. Merge. Delete branch. Write a changelog entry. Post to Discord. Update Linear. Eight or nine manual steps per ship.

The /ship skill collapses all of that into one invocation. I say "ship it" or "ship this," Claude runs through the checklist, pauses at the interesting moments (merge conflicts, failing CI, review findings) so I can intervene, and handles everything else automatically.

The first invocation was a breakthrough. The manual version wasn't bad (it was a known workflow, it worked) but the automated version removed so much friction that I could ship 2-3x more in a day. Every ship is now a two-minute interaction instead of a fifteen-minute ritual.

More importantly: the /ship skill is the first piece of the big extensibility overhaul that lands on day 18 (covered in the next post). Day 16's /ship was the proof-of-concept that convinced me to commit to the full plugin + skill + agent setup.

When a workflow stabilizes, automate it. The /ship skill exists because I'd been running that exact sequence by hand for about a week and it had converged. Same steps every time, same decisions at each step. Premature automation encodes the wrong patterns. But stable automation compounds forever.

Epic: done

The Linear epic "Ship the first playable iPhone beta" was marked Done at the end of day 16. Counting from day 1 (2026-03-16, the day I found the C++ project on GitHub and got it building on my Mac): sixteen days.

Sixteen days to go from "I've never done iOS, I've never done Swift, I've never done game dev" to "playable beta on TestFlight with a backend, leaderboards, an economy, an admin panel, a marketing website, a Discord server, age-gating, COPPA compliance, and an automated shipping pipeline."

I want to pause here because I think this is the moment to take stock before the project moves into its next phase. The next phase (production hardening, the recording system, the AI player, the blog itself) builds on a foundation that was laid in these sixteen days. Everything after day 16 is about turning a playable beta into a real product.

The "can this be done at all" question has been answered. Yes, it can. The remaining questions are "how polished, how scalable, how successful," different questions with different answers and different time horizons.

Sixteen days. If someone had told me on day zero that I'd be here on day 16, I would not have believed it.

This is post 10 of 18 in a series about building Geo Climber with Claude Code. The first beta is shipped. Act 1 of the series is done. Join the Discord and download Geo Climber on the App Store.

shippingpesacheconomyreviveadminjsage-gatingship-skillclaude-code
Shipping Day: Twenty-Four Issues Closed — Building Geo Climber