Trepo Android — where it stands

Status as of Mon 31 Aug 2026, 1:46pm PT · autonomous build loop, sweep 677 · HEAD ff15b726

498
commits since the loop was fixed
Aug 28 01:15 → now
677
sweeps completed
~24 per day
50/50
loop runs OK
zero failures since the fix
413
test files
346 unit · 70 instrumented
$0.00
OpenAI spend, 4 days running
was $981/day on Aug 26
0
unpushed commits · clean tree
nothing stranded

Throughput

Commits per day. The model switch (gpt-5.5 → claude-opus-5) landed midday Aug 27 and the cadence barely moved — the loop is doing the same volume of work at roughly zero marginal cost.

Commits to trepo-android, 25–31 Aug 2026
Aug 25 — 143 commits143
25
Aug 26 — 266 commits266
26
Aug 27 — 180 commits · model switched midday180
27
Aug 28 — 142 commits · lock fixed 01:15142
28
Aug 29 — 132 commits132
29
Aug 30 — 135 commits135
30
Aug 31 — 99 so far (partial day, to 1:46pm)99
31*

* Aug 31 is a partial day, measured to 1:46pm PT.

Table view
DateCommitsNote
Aug 25143
Aug 26266peak; running on metered gpt-5.5
Aug 27180model switched to claude-opus-5 at 12:05pm
Aug 28142lock reaper fixed, interval widened to 30 min
Aug 29132
Aug 30135
Aug 3199partial, to 1:46pm

Loop health

Last 50 recorded runs, all after the Aug 28 lock fix.

MeasureValueRead
Runs OK50 / 50No failures. The claude-cli output-cap crashes did not come back.
Modelclaude-opus-5via claude-cli — subscription, not metered API.
Median run18.1 minComfortably inside the 30-min slot.
p90 / max29.2 / 42.0 min4 of 50 overran 30 min. See the open risk below.
Overlapping starts0Scheduler skips a slot while a run holds it.
Lock early-exits0No run has been locked out since the guard was fixed.

Shipped since Friday

R8 / release-minification campaign complete — all 4 shards green

This was the open question blocking confidence in the release path. It's closed: minification is clean.

The finding worth keeping: not one failure in the entire campaign was actually caused by the shrinker. Every red R8 produced was a pre-existing test defect it happened to surface — a fake window charging 0dp, a pixel literal copied from a different handset.

Dish-session two-hour age-out was unreachable

The snapshot carried one saved-at stamp and every persist rewrote it — including the write that installs a restored snapshot. So opening the app once inside a two-hour window carried the same rows forever.

Bites hardest on FAILED rows, which have no backend id and so can never be reconciled away by a /dishes response. Their only exit is a DISMISS tap the user has already declined to give. Fixed by ageing each row against its own createdAt — the shutter time is the one field a restore doesn't rewrite.

Duplicate-dish window on process kill, closed

One shutter press could produce two dishes if the process died between the PUT and the job id reaching disk.

Modifier.swallowTaps() — a refactor that fixed a real a11y bug

Lifted the scrim-swallow guard from 9 copy-pasted sites into one modifier. 6 of those sites had been making whole cards a TalkBack / Tab focus stop that did nothing when activated.

/recipes/{owner} data layer + 17 wire-shape tests

Groundwork for "Use what I have" — the endpoint is live and verified 200 against prod. Android has never called it. Needs your placement decision below before UI work starts.

The honesty ledger

Two bug reports were retracted this weekend. Both are worth reading, because the pattern behind them is the thing that actually slows the loop down.

Retracted: "the bottom 15% of every Actions-menu row taps the row below it"

Reported Sunday afternoon with a recommendation and three options. Retracted 24 minutes later. It was the test, not the app.

The test read the height of the clickable row and then injected the tap through the text label inside it. Compose touch coordinates are local to whatever node you poke, so "1px above the row's bottom" got applied from the label's origin and landed ~30px past the row. It fired the next row because the finger was on the next row.

What came out of it: a general instrument that mounts a screen, finds every pair of controls whose expanded touch boxes overlap where the visible ones don't, and measures the candidates by actually tapping them. Swept 6 screens including everything with a Delete that reaches the backend — Clean deck, shopping list, Profile, saved recipes, dish log. Nothing destructive is reachable from a neighbour anywhere. The other five touch-injecting test files were audited for the same fault; all clean.

Earlier the same week: the window-fit harness was measuring an impossible state

It faked a 300dp window while the screens read their real 832dp one, so "compact layout" gates were switched off while the layout was squeezed. Some past "N/N PASS" lines were never earned. Separately, a "3 known-red tests, don't read the suite as green" note sat in PARITY for 3.5 hours after the fix landed — a standing licence to wave through real regressions.

The loop catches these itself, usually within a sweep or two, and it's building permanent instruments out of them rather than one-off fixes. That's the right response. But a meaningful share of the last five days went into auditing its own instruments rather than shipping app changes — worth watching that it stays a phase and not the pattern.

Needs a decision from you

Blocks the next sweep's UI work

Where does "Use what I have" go in Android's COOK?

iOS's COOK is four stacked cards and this is the first one — AI-generated recipes split into "make now" vs "need groceries". Android's COOK isn't card-based; it's a section bar: RECIPES | THIS WEEK | EXPLORE. None of those is this.

  1. A fourth chip on the section bar — cleanest conceptually, but that bar is already tight at 300dp.
  2. A surface inside RECIPES — fits, but buries it relative to iOS.

The loop wants your call rather than picking one and rewriting it next sweep. Data layer is already built and tested.

Not urgent — decide sometime

Action-menu rows are ~39dp against Android's 48dp minimum

iOS is under its own 44pt minimum in the same way. Now that the mis-tap report is retracted, nothing is being dispatched wrongly — this is purely a spec/cosmetic question. Fixing it grows the menu ~45dp and visibly diverges from iOS.

One env var

TREPO_RELAY_TOKEN isn't in the loop's environment

So it can't reach the iOS agent over the relay and falls back to the repo channel, which is slow. That's why the eight questions below are sitting. Worth fixing if you want cross-agent findings to land in minutes instead of sweeps.

Waiting on the iOS side

Eight questions filed, none answered. Two of them describe possible live iOS bugs, not Android parity gaps.

Possible: the "Use what I have" feed generates against an empty kitchen unconfirmed

With the App Review kitchen emptied to zero, the document settled at status ready, is_stale false, versions agreeing — carrying 20 recipes built from deleted chicken/rice/broccoli and reporting 37 of their ingredients as "have" against an empty kitchen. Every freshness signal a client has said it was fresh.

Explicitly not called confirmed: the healthy capture has the same version offset, so from the client the correct document and the wrong one are indistinguishable. Three readings, and only one of them — generation genuinely running against the empty kitchen — is a real user-facing bug. That one means recommending food the user has thrown away.

~525 lines of iOS bulk check-in look unreachable

BulkSessionManager's entire write path has zero call sites — addPhoto, startSession, startUpload. The ~20 remaining uses are all restores of a session blob nothing writes, and both gallery entry points gate on it being non-empty.

Either multi-photo bulk check-in was retired on purpose — fine, and it removes the biggest item on the port list — or it lost its entry point in a refactor and has silently stopped working for iOS users with no crash and no error. The bulkSessionReadyToReview push route is still wired, which is what makes the second reading plausible.

All eight open questions
FiledQuestion
08-30 09:40Does DishSessionManager.resumePollingIfNeeded re-save the snapshot it just restored?
08-30 10:20Does the pending-jobs registry carry a registry-level saved-at stamp? Plus an image-folder question.
08-30 11:45Duplicate-upload window measured at 0.42s per capture.
08-30 12:15Does DishSessionManager write the capture's JPEG to disk on the main thread?
08-30 12:20Does an iOS dish upload retry create a NEW presign, or re-PUT the same one?
08-30 16:40Does resumePollingIfNeeded read the saved image on the main thread?
08-31 09:20Is multi-photo bulk check-in retired, or did it lose its entry point?
08-31 10:20Does recipe_generation_version describe the rows, or the kitchen they were generated against?

Open risk

The reaper still fires at 25 minutes; runs go to 42

The Aug 28 fix corrected the ownership guard, so a stale reaper can no longer delete another run's lock. But a long run's own reaper still deletes its own lock at the 25-minute mark while it's still working, and the next fire is 5 minutes later. 4 of the last 50 runs exceeded 30 minutes; the longest was 42.

Nothing has actually overlapped — the scheduler skips a slot while a run is in flight, which is doing the real work here. But the lock is not the thing protecting you, and it reads like it is.

The clean fix: the lock snippet already has a find -mmin +55 stale-breaker at the top, so the 25-minute reaper is largely redundant and is the part that misfires. Either raise it to ~2700s or drop it entirely. That's a judgement call about the loop's concurrency design, not a typo — hasn't been made.

Bottom line

The app is in materially better shape than it was on Thursday, and it now costs essentially nothing to keep improving it — 498 commits over four days against a $0 metered bill. The release path's last open question (R8) is closed.

What's actually slowing things down is not the loop's throughput, it's the two-way latency: eight unanswered questions to the iOS side, one blocked UI decision, and a missing relay token that turns minutes into sweeps. Three small unblocks would compound more than another 500 commits.