# Building Starbridge in four days

2026-10-08 · https://tomvaucourt.com/posts/building-starbridge/

I built [Starbridge](https://github.com/T0mSIlver/starbridge) in four days. The idea came on 4 October around 18:50, and v0.1.0 was tagged on 8 October at 10:21. In between, about 200 Claude Code sessions merged 450 pull requests, and I wrote them 443 prompts.

Starbridge puts a coding agent's question on your phone and in your browser. You answer with one tap, and the waiting session carries on with your answer as its next prompt.

<figure style="margin:2rem 0"><img src="/images/building-starbridge/demo.gif" alt="Demo: in a terminal, Claude Code tunes a prompt and asks through starbridge ask which model to run the full eval on. The question appears as a notification on an Android phone with three options; the answer comes back into the session as its next prompt, and the eval's progress then shows on the lock screen." width="960" height="540" style="width:100%;height:auto"><figcaption style="font-size:0.875rem;color:var(--fg-muted);margin-top:0.5rem;text-align:center">Claude Code asks which model to run an eval on. The question lands on the phone, the tap goes back into the session as its next prompt, and the run's progress shows on the lock screen. I recorded it on my own phone.</figcaption></figure>

## The idea came out of a conversation

I run many coding agent sessions at once, mostly Claude Code, some Codex and opencode. My Mac and my phone buzz all day, and the one notification that matters, an agent stuck on a question, gets lost in the rest.

Starbridge didn't start there. On 4 October I was asking a Claude Code session for an Android app to build for [localvoxtral](https://github.com/T0mSIlver/localvoxtral), my dictation app. Every idea was a way to queue work from the phone, and I didn't want any of them. I dictated this:

> When I'm on the phone, I'm not orchestrating multiple agents, I'm only talking to the orchestrator.

I had it close that issue, then asked the same session whether my workflow gave it an idea for another app. It proposed "an app for supervising your agents from the phone": decisions as notifications, and the quota windows of my AI plans. I dictated 605 words back. Two sentences from them became the product:

> I would love for this app to be the center of the orchestration of all of my agents. So this would be a global `CLAUDE.md` rule: whenever you need me, use this skill that explains how to get the things to me.

The spec was committed 12 minutes later, at 18:57. At 20:28 I typed "starbridge it is", and at 20:40 the orchestrator session started in the new repo.

## I talk to agents a lot

Most of what I tell agents, I say out loud, with localvoxtral. Dictating changes what I say. I give more detail, and I don't second-guess or correct myself: I say things as they come to me, as I understand them. So the agent knows what I'm actually thinking, and it can steer me and show me exactly where I'm wrong.

Agent prompts are full of project names, and speech models don't know them: "VidTech" for [vidtheque](https://github.com/T0mSIlver/vidtheque), "star bridge" for Starbridge, "harder pain" for a herdr pane. A week before Starbridge, I had [given localvoxtral my repo and skill names](https://github.com/T0mSIlver/localvoxtral/issues/1024). On a test built from my own dictations, it got 97% of the 182 names right, against 48% without them.

In four days I sent 443 prompts, about 15,400 words. Half of them are 16 words or fewer: "merge it", "yes", a correction. The long ones carry the ideas. 7% of my prompts are 100 words or more, and they hold 40% of the words.

Two thirds of my prompts went to the orchestrator. I wrote directly to 18 of the 206 sessions, fewer than one in ten.

The rest of what the orchestrator needed from me came as questions, and they need one place of their own. An orchestrator does so much that its chat gets long, and a question asked there gets buried. Before Starbridge, mine put its questions on a [needs-you](https://github.com/T0mSIlver/skills/tree/main/needs-you) page I kept open in the browser. During the build they came to my phone: agents posted 138 questions through `starbridge ask`, 70 of them on 6 October.

On 7 October I dictated how answering from the phone had changed the way I steer agents:

> So it's basically steer, but you're not watching how the agent reacted; you're steering, etc. You just wait for the next notification.

<figure style="margin:2rem auto;max-width:300px;text-align:center"><picture><source media="(prefers-color-scheme: dark)" srcset="/images/building-starbridge/decision-card-dark.webp"><img src="/images/building-starbridge/decision-card-light.webp" alt="Starbridge on Android: a question card over the Quotas screen. It asks whether to rename a database column now or after Friday's release, gives the reason in one line, and offers two answers, with After the release recommended." width="720" height="1280" style="width:100%;height:auto;border:1px solid var(--border);border-radius:12px"></picture><figcaption style="font-size:0.875rem;color:var(--fg-muted);margin-top:0.5rem">A question card on Android. The recommended answer comes first.</figcaption></figure>

## One orchestrator, about 200 sessions

The orchestrator is one Claude Code session in Claude Desktop, on Opus 5.5 at medium effort, following an orchestration skill I wrote. It doesn't write features. It splits the work into GitHub issues, 383 by launch, and suggests a chip for each, a button in Claude Desktop that I click to start a new session. It suggested 174. Each session gets its own git worktree and branch, and opens one PR.

The sessions and the orchestrator talk through Claude Code's new inter-session messages: the orchestrator sent 702 over the four days. It merged the PRs one at a time, through a queue that waited for each deploy to pass, and archived 241 finished sessions. Two rules from my skill did most of the work. State lives in files, because sessions run out of context and get restarted: a planning repo, the orchestrator's memory, and a handoff on each PR. And a session left idle past its one-hour prompt cache gets archived after its handoff, never woken up, because waking it re-sends its whole context at twice the input price.

This is why I don't use Claude Projects, Anthropic's new feature for the same job, still in preview. Its coordinator only routes work to other sessions, and you can't really see what it does. My orchestrator does a lot: it schedules, briefs, merges and asks me questions, and its context fills so fast that I often compact it. Orchestrating is hard, so mine runs at medium effort, where Projects' coordinator runs at low.

The orchestrator can't be archived, so it has to stay warm, and its context sat around 327,000 tokens. I built a small Claude Code mod for it, orchestrator-cache, and I use it all the time to save my Claude limits. It sends a one-word keepalive after 50 idle minutes. During the build it fired 9 times, mostly the night before launch while I slept. At API prices a keepalive costs about €0.06, and a cold wake-up about €2.30.

## Reviews

Every change got a review from a model by another vendor before merge, through my [cross-review skill](https://github.com/T0mSIlver/skills/tree/main/cross-review). It runs the reviewer headless and read-only on a frozen copy of the change: GLM 5.3 through opencode first, GLM 5.3 on the Mistral API through Vibe next, and Codex last. That made 494 review runs.

<figure style="margin:2rem 0">
<svg viewBox="0 0 704 164" role="img" aria-label="Cross-vendor review runs per day, by reviewer. GLM 5.3 through opencode: 58, 118, 90 and 26 runs from 5 to 8 October, 292 in all. GLM 5.3 through Vibe: 46, 45, 10 and 0, 101 in all. gpt-6.1-sol through Codex: 2, 33, 53 and 2, 90 in all. Daily totals 106, 196, 153 and 28." style="max-width:100%;height:auto">
  <style>
    .f2-ax { font-size: 11px; fill: var(--fg-muted); }
    .f2-lbl { font-size: 12px; fill: currentColor; }
    .f2-in { font-size: 11px; font-weight: 600; fill: var(--bg); }
    .f2-in2 { font-size: 11px; font-weight: 600; fill: currentColor; }
    .f2-g { fill: var(--accent); }
    .f2-v { fill: var(--accent); opacity: .45; }
    .f2-c { fill: currentColor; opacity: .55; }
  </style>
  <rect class="f2-g" x="52" y="8" width="10" height="10"/>
  <text class="f2-lbl" x="67" y="17">GLM 5.3 · opencode: 292</text>
  <rect class="f2-v" x="237.79999999999998" y="8" width="10" height="10"/>
  <text class="f2-lbl" x="252.79999999999998" y="17">GLM 5.3 · Vibe: 101</text>
  <rect class="f2-c" x="397.19999999999993" y="8" width="10" height="10"/>
  <text class="f2-lbl" x="412.19999999999993" y="17">gpt-6.1-sol · Codex: 90</text>
  <text class="f2-lbl" x="0" y="47">5 Oct</text>
  <rect class="f2-g" x="52.0" y="32" width="154.3" height="22"/>
  <text class="f2-in" x="129.1" y="47" text-anchor="middle">58</text>
  <rect class="f2-v" x="206.3" y="32" width="122.4" height="22"/>
  <text class="f2-in2" x="267.5" y="47" text-anchor="middle">46</text>
  <rect class="f2-c" x="328.6" y="32" width="5.3" height="22"/>
  <text class="f2-ax" x="340.0" y="47">Codex 2  = 106</text>
  <text class="f2-lbl" x="0" y="79">6 Oct</text>
  <rect class="f2-g" x="52.0" y="64" width="313.9" height="22"/>
  <text class="f2-in" x="208.9" y="79" text-anchor="middle">118</text>
  <rect class="f2-v" x="365.9" y="64" width="119.7" height="22"/>
  <text class="f2-in2" x="425.7" y="79" text-anchor="middle">45</text>
  <rect class="f2-c" x="485.6" y="64" width="87.8" height="22"/>
  <text class="f2-in2" x="529.5" y="79" text-anchor="middle">33</text>
  <text class="f2-ax" x="579.4" y="79">= 196</text>
  <text class="f2-lbl" x="0" y="111">7 Oct</text>
  <rect class="f2-g" x="52.0" y="96" width="239.4" height="22"/>
  <text class="f2-in" x="171.7" y="111" text-anchor="middle">90</text>
  <rect class="f2-v" x="291.4" y="96" width="26.6" height="22"/>
  <text class="f2-in2" x="304.7" y="111" text-anchor="middle">10</text>
  <rect class="f2-c" x="318.0" y="96" width="141.0" height="22"/>
  <text class="f2-in2" x="388.5" y="111" text-anchor="middle">53</text>
  <text class="f2-ax" x="465.0" y="111">= 153</text>
  <text class="f2-lbl" x="0" y="143">8 Oct</text>
  <rect class="f2-g" x="52.0" y="128" width="69.2" height="22"/>
  <text class="f2-in" x="86.6" y="143" text-anchor="middle">26</text>
  <rect class="f2-c" x="121.2" y="128" width="5.3" height="22"/>
  <text class="f2-ax" x="132.5" y="143">Codex 2  = 28</text>
</svg>
<figcaption style="font-size:0.875rem;color:var(--fg-muted);margin-top:0.5rem;text-align:center">Cross-vendor review runs per day, by reviewer. Each run reviews one change, or one round of fixes to it. The number after each bar is the day's total; 11 more runs ended with no review.</figcaption>
</figure>

The reviews follow the merges: 104, 203, 89 and 30 PRs on those four days. On 5 October Vibe took almost half the reviews, because Z.ai's 5-hour window ran out overnight and again that evening, and Codex's weekly limit was nearly spent. 6 October had the most merges, and Codex reviewed in the late morning until its window ran out at noon. 7 October merged fewer PRs, but that afternoon the orchestrator caught up on reviews after merging: 18 PRs without a GLM review, and 17 on critical paths without a Codex one. 8 October was launch day, behind a code freeze.

Security got its own reviews. On the night of 5 October, Codex ran two audit rounds and found 26 issues for about 0.9 million fresh tokens, which are tokens not read from the cache. All were fixed or deferred to after launch ([#260](https://github.com/T0mSIlver/starbridge/issues/260), [#274](https://github.com/T0mSIlver/starbridge/issues/274)). The next morning, a single Opus 5.5 session found 4 more for 0.25 million ([#302](https://github.com/T0mSIlver/starbridge/issues/302)).

Then I tried the claude-security plugin's scan workflow on the protocol ([#366](https://github.com/T0mSIlver/starbridge/issues/366)). It ran 133 Fable 5.1 agents for 84 minutes and used about 11 million fresh tokens, plus 108 million cache reads. It verified 11 findings out of 27 candidates, and all 11 were real. That is under one real finding per million fresh tokens, against 16 for the single Opus session. Three of its most severe findings came from the auditor reproducing them, not from the agents' vote.

It also ran on borrowed limits. My weekly Claude limit was at 98% the night before, and I spent a rate-limit reset Anthropic had handed out a few weeks earlier. At 18:00 on 6 October the orchestrator started three more scans of the day's changes, and I stopped all three within 20 minutes. The crypto review on 7 October found two real problems, with Codex and one Fable session, for under half a million tokens each ([#811](https://github.com/T0mSIlver/starbridge/pull/811), [#812](https://github.com/T0mSIlver/starbridge/pull/812)).

I don't think multi-agent workflows are worth it, unless your inference is free and unlimited. One session that knows what I want, because I told it at length, plus an adversarial review by another vendor, finds more for each token.

## The tokens

| Harness | Model | Sessions | Tokens | Cache reads | At API prices |
|---|---|---:|---:|---:|---:|
| Claude Code | Opus 5.5, some Sonnet 5.5 and Fable 5.1 | 206 | 8.78 billion | 8.69 billion | €2,419 |
| opencode | GLM 5.3 | 446 | 276 million | 251 million | €104 |
| Vibe | GLM 5.3 | 104 | 83 million | 74 million | €36 |
| Codex | gpt-6.1-sol | 102 | 58 million | 51 million | €20 |

opencode has more than twice as many sessions as Claude Code because a review is a session of its own. Each review starts fresh on a frozen copy of the change, reads the diff, and ends, so it uses 0.6 million tokens on average. An implementation session stays open through CI and fixes, and averages 42 million.

At API prices, the four days would have cost about €2,580 before VAT, and about €3,090 with France's 20%. I paid for plans instead, VAT included: €216 a month for Claude Max, about €23 for ChatGPT Plus, which runs Codex, €18 for Mistral, and a Z.ai Lite plan I bought a year ago for $36 a year. That is about €260 a month, or about €34 for four days.

Cache reads alone would have cost €1,562 at API prices. Without the cache, the same tokens at full input price would cost about €31,700.

## Activity over time

<figure style="margin:2rem 0">
<svg viewBox="0 0 704 344" role="img" aria-label="Hourly activity, Paris time, from 4 October 16:00 to 8 October 17:00. Three strips share the time axis. Pull requests merged per hour peak at 23; my prompts per hour peak at 31; Claude tokens per hour peak at 546 million. Dashed lines mark the first commit, the v0.1.0 tag and the public launch. Activity stops most nights; 6 October is the busiest day." style="max-width:100%;height:auto">
  <style>
    .f1-lbl { font-size: 12px; font-weight: 600; fill: currentColor; }
    .f1-ax { font-size: 10.5px; fill: var(--fg-muted); }
    .f1-a { fill: var(--accent); }
    .f1-b { fill: currentColor; opacity: .55; }
    .f1-c { fill: var(--accent); opacity: .5; }
    .f1-base { stroke: var(--border); stroke-width: 1; }
    .f1-ev { stroke: var(--fg-muted); stroke-width: 1; stroke-dasharray: 3 3; }
  </style>
  <text class="f1-ax" x="31.0" y="14" text-anchor="start">first commit</text>
  <text class="f1-ax" x="616.0" y="14" text-anchor="end">v0.1.0</text>
  <text class="f1-ax" x="653.5" y="14" text-anchor="start">launch</text>
  <text class="f1-lbl" x="8" y="44">Pull requests merged, per hour (max 23)</text>
  <line class="f1-ev" x1="619.0" x2="619.0" y1="20" y2="52"/>
  <line class="f1-ev" x1="650.5" x2="650.5" y1="20" y2="52"/>
  <line class="f1-ev" x1="28.0" x2="28.0" y1="52" y2="116"/>
  <line class="f1-ev" x1="619.0" x2="619.0" y1="52" y2="116"/>
  <line class="f1-ev" x1="650.5" x2="650.5" y1="52" y2="116"/>
  <rect class="f1-a" x="35.1" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="41.8" y="89.9" width="5.4" height="26.1"/>
  <rect class="f1-a" x="48.6" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="55.3" y="95.1" width="5.4" height="20.9"/>
  <rect class="f1-a" x="62.1" y="95.1" width="5.4" height="20.9"/>
  <rect class="f1-a" x="68.9" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="116.2" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="123.0" y="95.1" width="5.4" height="20.9"/>
  <rect class="f1-a" x="129.7" y="105.6" width="5.4" height="10.4"/>
  <rect class="f1-a" x="136.5" y="92.5" width="5.4" height="23.5"/>
  <rect class="f1-a" x="143.3" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="156.8" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="177.1" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="183.8" y="105.6" width="5.4" height="10.4"/>
  <rect class="f1-a" x="190.6" y="92.5" width="5.4" height="23.5"/>
  <rect class="f1-a" x="197.4" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="204.1" y="89.9" width="5.4" height="26.1"/>
  <rect class="f1-a" x="210.9" y="74.3" width="5.4" height="41.7"/>
  <rect class="f1-a" x="217.6" y="87.3" width="5.4" height="28.7"/>
  <rect class="f1-a" x="224.4" y="97.7" width="5.4" height="18.3"/>
  <rect class="f1-a" x="231.2" y="74.3" width="5.4" height="41.7"/>
  <rect class="f1-a" x="237.9" y="69.0" width="5.4" height="47.0"/>
  <rect class="f1-a" x="244.7" y="108.2" width="5.4" height="7.8"/>
  <rect class="f1-a" x="285.3" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="292.0" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="298.8" y="92.5" width="5.4" height="23.5"/>
  <rect class="f1-a" x="305.6" y="97.7" width="5.4" height="18.3"/>
  <rect class="f1-a" x="312.3" y="97.7" width="5.4" height="18.3"/>
  <rect class="f1-a" x="319.1" y="89.9" width="5.4" height="26.1"/>
  <rect class="f1-a" x="325.9" y="74.3" width="5.4" height="41.7"/>
  <rect class="f1-a" x="332.6" y="82.1" width="5.4" height="33.9"/>
  <rect class="f1-a" x="339.4" y="79.5" width="5.4" height="36.5"/>
  <rect class="f1-a" x="346.1" y="89.9" width="5.4" height="26.1"/>
  <rect class="f1-a" x="352.9" y="71.7" width="5.4" height="44.3"/>
  <rect class="f1-a" x="359.7" y="79.5" width="5.4" height="36.5"/>
  <rect class="f1-a" x="366.4" y="97.7" width="5.4" height="18.3"/>
  <rect class="f1-a" x="373.2" y="92.5" width="5.4" height="23.5"/>
  <rect class="f1-a" x="380.0" y="66.4" width="5.4" height="49.6"/>
  <rect class="f1-a" x="386.7" y="56.0" width="5.4" height="60.0"/>
  <rect class="f1-a" x="393.5" y="87.3" width="5.4" height="28.7"/>
  <rect class="f1-a" x="400.2" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="407.0" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="461.1" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="474.6" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="481.4" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="494.9" y="89.9" width="5.4" height="26.1"/>
  <rect class="f1-a" x="501.7" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="508.5" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="515.2" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="522.0" y="108.2" width="5.4" height="7.8"/>
  <rect class="f1-a" x="528.7" y="105.6" width="5.4" height="10.4"/>
  <rect class="f1-a" x="535.5" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="542.3" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="549.0" y="105.6" width="5.4" height="10.4"/>
  <rect class="f1-a" x="555.8" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="562.6" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="569.3" y="103.0" width="5.4" height="13.0"/>
  <rect class="f1-a" x="609.9" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="616.7" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="623.4" y="113.4" width="5.4" height="2.6"/>
  <rect class="f1-a" x="630.2" y="110.8" width="5.4" height="5.2"/>
  <rect class="f1-a" x="636.9" y="100.3" width="5.4" height="15.7"/>
  <rect class="f1-a" x="643.7" y="113.4" width="5.4" height="2.6"/>
  <line class="f1-base" x1="8" x2="664" y1="116" y2="116"/>
  <text class="f1-lbl" x="8" y="142">My prompts, per hour (max 31)</text>
  <line class="f1-ev" x1="28.0" x2="28.0" y1="150" y2="214"/>
  <line class="f1-ev" x1="619.0" x2="619.0" y1="150" y2="214"/>
  <line class="f1-ev" x1="650.5" x2="650.5" y1="150" y2="214"/>
  <rect class="f1-b" x="8.0" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="14.8" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="21.5" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="35.1" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="41.8" y="183.0" width="5.4" height="31.0"/>
  <rect class="f1-b" x="48.6" y="198.5" width="5.4" height="15.5"/>
  <rect class="f1-b" x="55.3" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="62.1" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="68.9" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="116.2" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="129.7" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="136.5" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="143.3" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="150.0" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="156.8" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="163.5" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="170.3" y="204.3" width="5.4" height="9.7"/>
  <rect class="f1-b" x="177.1" y="196.6" width="5.4" height="17.4"/>
  <rect class="f1-b" x="183.8" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="190.6" y="173.4" width="5.4" height="40.6"/>
  <rect class="f1-b" x="197.4" y="161.7" width="5.4" height="52.3"/>
  <rect class="f1-b" x="204.1" y="154.0" width="5.4" height="60.0"/>
  <rect class="f1-b" x="210.9" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="224.4" y="198.5" width="5.4" height="15.5"/>
  <rect class="f1-b" x="231.2" y="185.0" width="5.4" height="29.0"/>
  <rect class="f1-b" x="237.9" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="285.3" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="292.0" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="298.8" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="305.6" y="204.3" width="5.4" height="9.7"/>
  <rect class="f1-b" x="312.3" y="208.2" width="5.4" height="5.8"/>
  <rect class="f1-b" x="319.1" y="196.6" width="5.4" height="17.4"/>
  <rect class="f1-b" x="325.9" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="332.6" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="339.4" y="185.0" width="5.4" height="29.0"/>
  <rect class="f1-b" x="346.1" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="352.9" y="196.6" width="5.4" height="17.4"/>
  <rect class="f1-b" x="359.7" y="192.7" width="5.4" height="21.3"/>
  <rect class="f1-b" x="366.4" y="198.5" width="5.4" height="15.5"/>
  <rect class="f1-b" x="373.2" y="200.5" width="5.4" height="13.5"/>
  <rect class="f1-b" x="380.0" y="190.8" width="5.4" height="23.2"/>
  <rect class="f1-b" x="386.7" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="393.5" y="194.6" width="5.4" height="19.4"/>
  <rect class="f1-b" x="400.2" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="440.8" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="447.6" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="454.4" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="461.1" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="467.9" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="474.6" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="481.4" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="488.2" y="204.3" width="5.4" height="9.7"/>
  <rect class="f1-b" x="494.9" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="501.7" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="508.5" y="194.6" width="5.4" height="19.4"/>
  <rect class="f1-b" x="515.2" y="181.1" width="5.4" height="32.9"/>
  <rect class="f1-b" x="522.0" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="528.7" y="206.3" width="5.4" height="7.7"/>
  <rect class="f1-b" x="535.5" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="542.3" y="202.4" width="5.4" height="11.6"/>
  <rect class="f1-b" x="549.0" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="576.1" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="582.8" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="589.6" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="596.4" y="210.1" width="5.4" height="3.9"/>
  <rect class="f1-b" x="603.1" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="609.9" y="212.1" width="5.4" height="1.9"/>
  <rect class="f1-b" x="616.7" y="204.3" width="5.4" height="9.7"/>
  <rect class="f1-b" x="623.4" y="169.5" width="5.4" height="44.5"/>
  <rect class="f1-b" x="630.2" y="192.7" width="5.4" height="21.3"/>
  <rect class="f1-b" x="636.9" y="196.6" width="5.4" height="17.4"/>
  <rect class="f1-b" x="643.7" y="198.5" width="5.4" height="15.5"/>
  <rect class="f1-b" x="650.5" y="175.3" width="5.4" height="38.7"/>
  <rect class="f1-b" x="657.2" y="212.1" width="5.4" height="1.9"/>
  <line class="f1-base" x1="8" x2="664" y1="214" y2="214"/>
  <text class="f1-lbl" x="8" y="240">Claude tokens (millions), per hour (max 546)</text>
  <line class="f1-ev" x1="28.0" x2="28.0" y1="248" y2="312"/>
  <line class="f1-ev" x1="619.0" x2="619.0" y1="248" y2="312"/>
  <line class="f1-ev" x1="650.5" x2="650.5" y1="248" y2="312"/>
  <rect class="f1-c" x="35.1" y="308.8" width="5.4" height="3.2"/>
  <rect class="f1-c" x="41.8" y="300.5" width="5.4" height="11.5"/>
  <rect class="f1-c" x="48.6" y="294.0" width="5.4" height="18.0"/>
  <rect class="f1-c" x="55.3" y="296.0" width="5.4" height="16.0"/>
  <rect class="f1-c" x="62.1" y="291.9" width="5.4" height="20.1"/>
  <rect class="f1-c" x="68.9" y="278.9" width="5.4" height="33.1"/>
  <rect class="f1-c" x="116.2" y="305.2" width="5.4" height="6.8"/>
  <rect class="f1-c" x="123.0" y="301.6" width="5.4" height="10.4"/>
  <rect class="f1-c" x="129.7" y="306.1" width="5.4" height="5.9"/>
  <rect class="f1-c" x="136.5" y="301.8" width="5.4" height="10.2"/>
  <rect class="f1-c" x="143.3" y="307.1" width="5.4" height="4.9"/>
  <rect class="f1-c" x="150.0" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="156.8" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="163.5" y="310.6" width="5.4" height="1.4"/>
  <rect class="f1-c" x="170.3" y="304.8" width="5.4" height="7.2"/>
  <rect class="f1-c" x="177.1" y="300.5" width="5.4" height="11.5"/>
  <rect class="f1-c" x="183.8" y="284.8" width="5.4" height="27.2"/>
  <rect class="f1-c" x="190.6" y="300.1" width="5.4" height="11.9"/>
  <rect class="f1-c" x="197.4" y="303.6" width="5.4" height="8.4"/>
  <rect class="f1-c" x="204.1" y="294.6" width="5.4" height="17.4"/>
  <rect class="f1-c" x="210.9" y="282.9" width="5.4" height="29.1"/>
  <rect class="f1-c" x="217.6" y="301.2" width="5.4" height="10.8"/>
  <rect class="f1-c" x="224.4" y="289.9" width="5.4" height="22.1"/>
  <rect class="f1-c" x="231.2" y="265.9" width="5.4" height="46.1"/>
  <rect class="f1-c" x="237.9" y="293.2" width="5.4" height="18.8"/>
  <rect class="f1-c" x="244.7" y="310.8" width="5.4" height="1.2"/>
  <rect class="f1-c" x="285.3" y="310.4" width="5.4" height="1.6"/>
  <rect class="f1-c" x="292.0" y="285.4" width="5.4" height="26.6"/>
  <rect class="f1-c" x="298.8" y="276.7" width="5.4" height="35.3"/>
  <rect class="f1-c" x="305.6" y="273.8" width="5.4" height="38.2"/>
  <rect class="f1-c" x="312.3" y="281.6" width="5.4" height="30.4"/>
  <rect class="f1-c" x="319.1" y="275.5" width="5.4" height="36.5"/>
  <rect class="f1-c" x="325.9" y="292.3" width="5.4" height="19.7"/>
  <rect class="f1-c" x="332.6" y="281.3" width="5.4" height="30.7"/>
  <rect class="f1-c" x="339.4" y="280.6" width="5.4" height="31.4"/>
  <rect class="f1-c" x="346.1" y="278.2" width="5.4" height="33.8"/>
  <rect class="f1-c" x="352.9" y="284.5" width="5.4" height="27.5"/>
  <rect class="f1-c" x="359.7" y="290.8" width="5.4" height="21.2"/>
  <rect class="f1-c" x="366.4" y="295.0" width="5.4" height="17.0"/>
  <rect class="f1-c" x="373.2" y="279.1" width="5.4" height="32.9"/>
  <rect class="f1-c" x="380.0" y="252.0" width="5.4" height="60.0"/>
  <rect class="f1-c" x="386.7" y="279.9" width="5.4" height="32.1"/>
  <rect class="f1-c" x="393.5" y="298.0" width="5.4" height="14.0"/>
  <rect class="f1-c" x="400.2" y="307.4" width="5.4" height="4.6"/>
  <rect class="f1-c" x="407.0" y="310.1" width="5.4" height="1.9"/>
  <rect class="f1-c" x="440.8" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="447.6" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="454.4" y="310.1" width="5.4" height="1.9"/>
  <rect class="f1-c" x="461.1" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="467.9" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="474.6" y="305.8" width="5.4" height="6.2"/>
  <rect class="f1-c" x="481.4" y="308.5" width="5.4" height="3.5"/>
  <rect class="f1-c" x="488.2" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="494.9" y="303.4" width="5.4" height="8.6"/>
  <rect class="f1-c" x="501.7" y="307.0" width="5.4" height="5.0"/>
  <rect class="f1-c" x="508.5" y="310.3" width="5.4" height="1.7"/>
  <rect class="f1-c" x="515.2" y="303.1" width="5.4" height="8.9"/>
  <rect class="f1-c" x="522.0" y="308.0" width="5.4" height="4.0"/>
  <rect class="f1-c" x="528.7" y="303.1" width="5.4" height="8.9"/>
  <rect class="f1-c" x="535.5" y="305.5" width="5.4" height="6.5"/>
  <rect class="f1-c" x="542.3" y="301.9" width="5.4" height="10.1"/>
  <rect class="f1-c" x="549.0" y="302.7" width="5.4" height="9.3"/>
  <rect class="f1-c" x="555.8" y="302.8" width="5.4" height="9.2"/>
  <rect class="f1-c" x="562.6" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="569.3" y="310.0" width="5.4" height="2.0"/>
  <rect class="f1-c" x="576.1" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="582.8" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="589.6" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="596.4" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="603.1" y="311.0" width="5.4" height="1.0"/>
  <rect class="f1-c" x="609.9" y="310.1" width="5.4" height="1.9"/>
  <rect class="f1-c" x="616.7" y="309.7" width="5.4" height="2.3"/>
  <rect class="f1-c" x="623.4" y="308.4" width="5.4" height="3.6"/>
  <rect class="f1-c" x="630.2" y="305.6" width="5.4" height="6.4"/>
  <rect class="f1-c" x="636.9" y="305.3" width="5.4" height="6.7"/>
  <rect class="f1-c" x="643.7" y="309.4" width="5.4" height="2.6"/>
  <rect class="f1-c" x="650.5" y="309.5" width="5.4" height="2.5"/>
  <rect class="f1-c" x="657.2" y="310.9" width="5.4" height="1.1"/>
  <line class="f1-base" x1="8" x2="664" y1="312" y2="312"/>
  <line class="f1-base" x1="62.1" x2="62.1" y1="312" y2="316"/>
  <text class="f1-ax" x="62.1" y="328" text-anchor="middle">5 Oct</text>
  <line class="f1-base" x1="143.3" x2="143.3" y1="312" y2="316"/>
  <text class="f1-ax" x="143.3" y="328" text-anchor="middle">noon</text>
  <line class="f1-base" x1="224.4" x2="224.4" y1="312" y2="316"/>
  <text class="f1-ax" x="224.4" y="328" text-anchor="middle">6 Oct</text>
  <line class="f1-base" x1="305.6" x2="305.6" y1="312" y2="316"/>
  <text class="f1-ax" x="305.6" y="328" text-anchor="middle">noon</text>
  <line class="f1-base" x1="386.7" x2="386.7" y1="312" y2="316"/>
  <text class="f1-ax" x="386.7" y="328" text-anchor="middle">7 Oct</text>
  <line class="f1-base" x1="467.9" x2="467.9" y1="312" y2="316"/>
  <text class="f1-ax" x="467.9" y="328" text-anchor="middle">noon</text>
  <line class="f1-base" x1="549.0" x2="549.0" y1="312" y2="316"/>
  <text class="f1-ax" x="549.0" y="328" text-anchor="middle">8 Oct</text>
  <line class="f1-base" x1="630.2" x2="630.2" y1="312" y2="316"/>
  <text class="f1-ax" x="630.2" y="328" text-anchor="middle">noon</text>
  <text class="f1-ax" x="664" y="342" text-anchor="end">Paris time</text>
</svg>
<figcaption style="font-size:0.875rem;color:var(--fg-muted);margin-top:0.5rem;text-align:center">Each bar is one hour, Paris time. Dashed lines mark the first commit, the v0.1.0 tag and the launch.</figcaption>
</figure>

The busiest hour for merges was midnight on 6 to 7 October, with 23 PRs. That evening I dogfooded Starbridge on my own devices, and the sessions fixed what I found in parallel: setup's first run, sign-in and pairing errors, the limits on machines and requests per account, and the inbox. Claude used 546 million tokens in the hour before, the most of the build.

The gaps are the nights I slept. On 7 October I had about 10% of my weekly Claude limit left until it reset at 22:00, so I spent it on a few things: the first release candidate and the npm package name, the Play closed test, installing Starbridge on my Mac as a new user, and the launch material. I sent the most prompts on 5 October between 21:00 and 22:00, 31 of them.

## CI

CI ran on every PR: unit tests, typecheck, lint, web end-to-end tests, Android screenshot tests, and the CLI on Windows.

On 5 October GitHub stopped starting Starbridge's CI jobs. The private repo had used about 2,040 Actions minutes in five days, more than its monthly allowance. The orchestrator moved CI to self-hosted runners, first on my dev box, then also on a mini PC ([#137](https://github.com/T0mSIlver/starbridge/pull/137)). The Android build went from 7.8 minutes to 1.7.

Then CI became the bottleneck: one end-to-end job in ten waited 17 minutes or more for a runner. I asked the orchestrator to [measure it](https://github.com/T0mSIlver/starbridge/issues/380). It skipped jobs a change can't affect, kept the build caches, and gave the runners four times the CPU weight of the agent sessions, and the end-to-end job went from 258 to 115 seconds.

All seven runners came off before the repo went public. On a public repo, anyone can open a pull request, and its CI would run their code on my machines.

## Load test

A load test answers a different question: will the server hold when people show up. At the size of the real server, 3000 simulated users cost it 190 MB of memory and 19% of one core, with a p99 latency of 14 ms.

## The agents used my browser

The launch needed accounts and consoles, and the agents did most of it in my own Chrome, through Claude in Chrome. Eight sessions made about 665 browser actions. They created the app in the Google Play Console, filled its listing and data safety form, uploaded the bundle, and sent the closed test for review. They moved the domain's DNS from Porkbun to Cloudflare, and worked through the Google Group for Play testers and npm's publishing settings.

They stopped where it was my call: the legal declarations in the Play Console, and the final "send for review", which a permission check wouldn't let an agent click. I clicked those myself.

## What I learned

Agents made building almost free. Everything around building still runs at my speed: knowing whether it's right, whether it's safe, whether the machines hold, and whether anyone wants it.

I used Starbridge as a user from the first night. At 00:33 on 5 October I installed the Android app and paired my phone, and that evening I walked through every flow with a session, reporting what felt wrong as I went. The rough edges kept coming as the features did. On 7 October, about 15 hours before the tag, the new CLI setup on my Mac showed the server as `[https://starbridge.run]`, and nothing about the brackets said "press Enter". It also asked again for the quota accounts my dev box already sends. The agents' tests passed every time. Tests prove the code does what the agents meant, and only using it shows whether it's what a user expects, so the dogfooding never stops.

I also shipped cryptography I can't judge myself. Claude built the encryption, Codex and a Fable session reviewed it, and nobody has audited it from outside. Agents let me ship past my own depth. What I can answer for is set by how far I can check, not by how fast they write. For anything people have to trust, I want an outside audit, or a plain statement of what was checked and what wasn't.

The bottleneck kept moving: AI plan limits, then GitHub's CI minutes, then RAM, then my own attention. Adding sessions only helps until a shared resource saturates. The sandbox ran out of RAM at about nine sessions, and past that, more agents meant more waiting and more crashes. I couldn't have predicted which resource would run out next: each one only showed up once the one before it was fixed. What worked was noticing it fast and measuring it. CI got faster the day I asked the orchestrator to measure it, and Gradle got its rules the afternoon the sandbox thrashed.

And building isn't distribution. I spent four days building and one post launching, and the post died at once. Next time a handful of people who run many agents get the app before launch. Google Play's closed test needs 12 testers anyway.

That's the bottleneck from my [About page](/about/): feedback. I use Starbridge all the time now. Try it, and tell me where it's wrong for you.

The code is at [github.com/T0mSIlver/starbridge](https://github.com/T0mSIlver/starbridge), and the free server is at [starbridge.run](https://starbridge.run).
