Skip to content

Getting the same update into every version (plain English)

The decision, in one sentence: everything WorkWingman ships is built from one shared codebase and released through one pipeline, so a fix lands in every version without anyone rebuilding it per version.

Decided 2026-08-05. Ticket WING-334. The engineering version is repo-topology-multi-target.

What this page is not about. How the apps themselves work — the cloud service, the browser-automation runner it talks to, the desktop apps, the phone app streaming its screen — is already decided and isn't discussed here. This is only about how updates get delivered.


The question

WorkWingman ships in a lot of shapes: a cloud service, a browser-automation runner, a Windows app, with Mac, Linux, and phone versions coming. (The marketing website and the browser extension are built separately and are not part of this — they do not share the product's code.)

The ones listed above share most of their code. So when someone fixes something, how do you know that fix actually reached all of them — and reached them as the same fix?

Why one codebase

Because almost every change is a change everyone needs.

Picture a fix to how salary figures are read. With one shared codebase, that fix is made once and it's in every version from then on. With a separate copy per version, someone carries that same fix by hand into Windows, then cloud, then phone, then Mac, then Linux — every time, forever.

The work doesn't add up, it multiplies: every change × every version. Miss one and that version quietly drifts. It still builds. It still passes its tests. It's just slightly wrong, and nobody finds out until a person hits it.

The real problem isn't the codebase — it's the building

Here's what we found when we actually looked.

The Windows installer builds the app's screens from scratch. The cloud container also builds the app's screens from scratch. Same source, two separate builds, no connection between them.

That's two versions today. Add Mac, Linux, and the phone app and it becomes five separate builds of the same screens — five opportunities to build from a slightly different starting point and ship five versions that aren't quite identical, with nothing anywhere forcing them to match. (The automation runner doesn't include the screens at all, but it does take the same engine, so it has the same problem one layer down.)

There's also no automated release process. Someone runs the right script from the right folder on their own machine. That works right up until it doesn't.

The fix: build it once, hand it to everyone

One snapshot of the code produces one set of finished shared pieces. Every version is then assembled from those exact pieces.

  1. Build the shared parts once. The app's screens — what you actually look at — get built one time. The engine behind them, the part that does the work and talks to the internet, gets built once for each kind of computer (Windows, Mac, Linux).
  2. Assemble each version from those parts. The Windows installer, the cloud container, the automation runner, the Mac app, the Linux package, the phone app — each is a wrapping step around the same finished pieces. None of them rebuilds the shared code. (Some still have to build their own wrapper — a phone app genuinely has to be assembled with Apple's and Google's tools — but the shared parts inside it are never rebuilt.)
  3. Ship by promoting what was already built. Nothing is rebuilt in order to release it.

That is what makes it reliable rather than merely tidy: a step that isn't allowed to rebuild the shared code cannot quietly change it. It either finds the piece built from that snapshot or it stops. There's no path where it produces something slightly different, because there's no path where it produces that piece at all.

The cloud side already works this way between its test and live environments — the same built package, pointed at different settings. Installers work slightly differently: you build one signed app per version, and it learns which environment it's in when it runs, rather than having that baked in. Either way, nothing gets rebuilt just to ship it.

Proving it landed

Every release records a line per version: which snapshot, which finished file, which platform, where it went.

So "did that fix reach the Mac build?" is something you look up rather than something you believe. And a version missing from that list didn't ship — which is visible immediately instead of a month later.

Why this matters for testing

This is the real payoff, and it is worth being blunt about what the pipeline does and does not do: it does not stop bugs. It makes every version have the same bugs.

That sounds like a low bar. It isn't. A bug in the shared code that behaves identically everywhere is one bug — a tester finds it once, it gets fixed once, and it is gone from every version at the same time. Bugs that differ between versions are separate bugs that each have to be found separately, on their own machine, by someone holding that machine. That is the cost that grows every time a version is added, and no amount of tooling absorbs it.

Worth being honest about the limit, since the lab gets budgeted against it: this covers the shared parts. A phone, a desktop app, and a web browser are genuinely different environments, and each can break in its own way no matter how identical the shared pieces are. That is precisely what the per-device list below is for.

So the point of building everything from one snapshot is that a test result travels — for the shared parts. "It passed on the test cloud instance" tells you something real about the Mac app's shared behavior, because the Mac app is provably made from the same pieces. It tells you nothing about whether that app installs cleanly or whether macOS trusts it.

This is the plan, not today. The single build-and-share process described above does not exist yet; each version is still built separately right now.

Until then, each version needs its own full check — nothing tested on one can be assumed about another, because nothing yet forces them to be built the same.

What still has to be tested on each device, once the shared build exists. Everything shared gets tested once on the test instance. What genuinely differs per version — and therefore needs a real machine in front of a real person:

  • installing and updating (each desktop system)
  • whether the operating system trusts the app on a clean machine it has never seen
  • the parts unique to that shell — how the phone app finds the cloud, the share sheet, screen streaming
  • how it fits into the operating system: file locations, permission prompts, links that open the app
  • a quick pass over the main features on that platform, because parts of the app really do behave differently per operating system

That list is short on purpose. If it starts growing, it usually means something shared has leaked into one version — the exact drift this approach exists to prevent, showing up as testing work rather than as a bug report. Treat it as an alarm, not as scope. The honest exception: a new item belongs there if it documents a genuine difference between operating systems, named and evidenced. What does not belong is the same shared behaviour being rebuilt separately per version.

The release record also tells the lab what to do: which build goes on which device, and which versions a given round of testing has not covered yet.

Keeping the checks fast

Checking every version on every single change would be slow enough that people stop waiting for it, and a check nobody waits for isn't a check.

So: every change gets the quick shared checks, every time. The slow work of producing actual installers happens nightly and at release. Apple's notarization step — where they inspect and approve a Mac app — only runs at release, because it's a round trip to Apple with unpredictable timing, and it shouldn't be able to fail a nightly build for reasons that have nothing to do with the code.

Things already worth fixing

  • Shared code gets rebuilt separately for each version. That's the root cause of everything above.
  • There's no automated release process at all, so shipping depends on one person running the right steps in the right order from memory.
  • The desktop app can only be built for Windows today, which is exactly what Mac and Linux need changed.
  • The Windows app has a fixed internal address written into it. That address was accidentally taken over once by a development tool, and the installed app quietly started talking to the wrong place.
  • Two near-identical build recipes exist for the website — which eventually means the public site and the preview site behave differently, and nobody knows which one is right.
  • The automation runner isn't part of the repo's build process at all — a shipped piece with no pipeline is one that drifts without anyone watching.

How this was decided

Three AI teammates were asked independently and reviewed each other's answers without knowing whose was whose. All three agreed on one codebase, no dissent.

The draft was then reviewed line by line against the real code, which caught several claims in it that simply weren't true. An earlier version of this page also tried to redesign how the apps work internally — that was out of scope and was removed. Those decisions were already made.