Last Week

Last week made the marketing problem much clearer.

After roughly ten targeted restaurant visits, I had three useful categories: restaurants with established waitlist systems that were difficult to displace, restaurants without enough current waitlist pain to justify another tool, and restaurants with visible guest waits that did not see those waits as an operational problem. The visits did not produce the first real Shokken customer, but they made the limits of Google Maps review research very obvious.

The next marketing direction was to find restaurants at moments of change—new locations, relocation, or management and ownership transitions—and to learn more directly from local operators before assuming there was a fit. On the app side, an account-deletion regression was safely contained in integration, Android 16/API 36 support and onboarding were in progress, and the iOS release signing setup needed attention.

This week, I paused the outreach long enough to think through a better strategy and ran a small goal-driven development experiment around a different restaurant problem.

A Marketing Pause and a Smaller Problem

Shokken is still live on Android and iOS, and restaurants can still request a free license. But the door-to-door effort needs more groundwork before the next round of visits will be useful.

I have now visited roughly a dozen restaurants in the Las Vegas Valley. That work showed me that finding the right conversion targets is its own project. It takes more than identifying a long-wait complaint, printing a flyer, and walking through the door. I need a better way to identify restaurants with a current need, a reason to change, and an owner or manager who is ready to evaluate a new workflow.

That work is not abandoned. It is simply not something I can solve by forcing another week of cold visits. The next update or two will likely be about narrowing the strategy and doing more preparation before restarting outreach.

In the meantime, I took a short break from Shokken’s app code and explored a smaller problem from a local all-you-can-eat sushi restaurant. It is a good test case because the problem is real, the scope is much smaller than Shokken, and the right solution has to be simple enough for a group of diners to understand without training.

What does it mean in English?

At a large all-you-can-eat restaurant, a table can have dozens or hundreds of menu choices and several people trying to order at once. One person eventually has to read the combined order to the server.

That gets messy quickly. Two people may order the same item without realizing it. Someone’s request may be missed. The table may over-order, which matters when the restaurant charges a penalty for food left unfinished.

The experiment is a small web app for coordinating that order. It should be quick to open, require no account, let a group contribute to one shared order, and make the final item quantities clear before somebody reads it to the server.

It is not a replacement for Shokken or a change in the product direction. It is a contained way to practice planning, building, testing, and reviewing a real web product while I step back and think more carefully about Shokken’s marketing strategy.

Nerdy Details

The iOS signing profile expired

The immediate Shokken maintenance issue was an expired Apple provisioning profile.

A provisioning profile is part of the signing chain that lets an iOS build be uploaded to the App Store. Mine had reached its one-year expiration, so I could no longer upload new iOS binaries until the signing setup was addressed.

I first looked at moving the release process to Apple’s managed cloud build service. The appeal is obvious: Apple holds the signing keys, builds the app on its own infrastructure, and manages the signing side of the release. That would remove the yearly key-expiration task from my pipeline.

The tradeoff is that the managed signing model requires using Apple’s compute. My existing release pipeline would no longer build the iOS app itself. That is not inherently bad—offloading builds and signing can be a good deal—but the included capacity does not fit Shokken’s current development rhythm.

The standard Apple developer membership includes 25 hours of cloud build time each month. A Kotlin Multiplatform iOS build currently takes roughly 30 to 45 minutes in that environment. Even before any retries or release builds, that translates to only a few dozen builds per month.

I already use nightly internal builds for testing. Thirty nightly builds can consume most of that monthly allowance on their own, leaving very little room for on-demand testing during an active development week. Caching could improve the build time, but I had disabled it to avoid build problems that would be difficult to diagnose.

So the managed route is attractive in theory but too constrained for the current workflow. I chose the simpler answer: keep the existing build setup and renew the provisioning profile manually once a year. I could build a credential-rotation process around it, but privileged signing access deserves more care than a quick convenience script, and an annual manual task is a reasonable tradeoff for now.

The group-ordering problem is small but real

The side project came from a restaurant that helped inspire Shokken in the first place. It is busy, has a long line, and uses an established waitlist product. That product works, even if its interface feels dated to me. The interesting part of this visit was not the waitlist. It was the ordering experience after the table sat down.

All-you-can-eat menus can be enormous. With one diner, ordering is simple. With two diners, a quick conversation is usually enough. With three, four, or more people, coordinating across a long menu becomes error-prone.

The failure modes are ordinary but annoying:

  • someone forgets to include another diner’s item
  • two people each assume the other included a shared item
  • the table double-counts something and orders too much
  • the server has to wait while the group reconstructs a final list

Over-ordering can have a cost because the restaurant may charge for food that remains unfinished. That makes a shared, visible total more useful than a casual verbal summary.

The intended experience is deliberately narrow. Diners share one session and add items to the same order, with the table able to see the running quantities before placing it. There is no restaurant account, no diner account, no permanent profile, and no need to turn the prototype into another full restaurant platform.

Static delivery with temporary shared state

The architecture follows the scope of the problem: keep the site mostly static and add only enough server-side state to coordinate one live table session.

Browser → Cloudflare Workers static assets → per-session Durable Object → shared order state

Cloudflare Workers serves the static application. A Durable Object holds the state for an active order session. That gives the group one shared view without asking each diner to create an account or introducing long-term user data that the product does not need.

The distinction matters. Shokken needs a richer backend because it manages businesses, waitlists, messaging, and persistent operational data. This order-coordination tool only needs a temporary shared workspace. Keeping the scope light reduces cost, avoids unnecessary attack surface, and makes the product faster to load and easier to reason about.

The session state is enough for a table to coordinate in the moment. It is not intended to become a durable order-history system. That is a feature, not a limitation: the less information the product retains, the less setup and data management it asks of diners.

A goal-driven workflow still needs concrete slices

Before implementation, I worked through the product model, the user flow, and the stack in detail. The goal was not to throw a vague request at a blank repository and hope for a complete application. It was to produce a clear requirements list, initialize the repository with checks and deployment infrastructure, then work through the product in small verifiable pieces.

The resulting plan had roughly fifteen capabilities. Each one was treated as its own slice of work: implement it, run the relevant tests, check the browser behavior, review the result, fix anything that surfaced, and only then move to the next feature.

That sequence is important. A whole web application rarely reaches a usable quality bar in one pass. The better mental model is not a single vertical leap from specification to finished product. It is horizontal progress across a list of concrete capabilities, with verification built into every step.

For this project, browser checks matter as much as unit or integration checks. A group-ordering tool is almost entirely interaction design. It needs to be obvious what the current order contains, who can add to it, and what a diner should do next. Playwright lets those flows be exercised in a real browser rather than trusting that a component-level test means the experience is understandable.

Review needs a fresh perspective

The experiment also reinforced the value of a separate review pass.

An implementation pass carries the context of every decision it just made. That helps it move quickly, but it also makes it easier to miss its own assumptions. A reviewer coming in fresh can question the edge cases, inspect the final browser behavior, and catch gaps that feel invisible to the person who assembled the first version.

There are diminishing returns. Five independent review passes cost much more than one and do not produce five times the improvement. For this kind of project, one dedicated review pass plus eventual human review is the practical balance.

The hard part is coordinating the handoff. There is no built-in conversation channel between an implementation pass and a review pass, so I used a GitHub issue as the visible state machine and audit trail. The issue records which feature is active, whether it is ready for review, what the review found, and whether the fix has been verified.

Each pass checks the issue on a ten-minute interval, for up to thirty checks. When it sees that its turn has started, it does the work and leaves the next state in a comment. The other pass then picks up from that comment. It is a simple mechanism, but it provides both coordination and a readable record of why a decision was made.

Long-running work changes the way progress is observed

This process is not fast in the way an interactive coding conversation is fast.

I started a batch of roughly a dozen goals in the morning, and it was still running later in the day. That is normal when every capability is implemented, tested, exercised in the browser, independently checked, and corrected before the next capability starts.

The GitHub issue log becomes important precisely because the work takes time. I can open the record and see where the active feature stands, what failed, what was fixed, and what needs attention next. A concise summary of that record is also more useful than trying to reconstruct the entire process from memory.

The most useful result is not only the small order-coordination app. It is a clearer picture of how I want future Shokken development to run: well-defined goals, small slices, visible handoffs, browser-level verification, independent review, and a human decision point before a real release.

Next Week

Next week I need to return to the two main tracks.

For Shokken, that means finishing the signing maintenance, getting the next release path healthy again, and continuing the existing app work once the integration issues are fixed. For marketing, it means turning the first dozen conversations into a better target-selection strategy before making another round of cold visits.

I will also see the order-coordination experiment through and decide which parts of its planning, verification, and review workflow belong in the regular Shokken process. The side project was a pause from the main app, but it produced useful lessons about building small, testable slices of work without losing sight of the customer problem.