Pull, Update, Remove -- and a card that says when its build is behind
Iris's call: three buttons on a project's row, where Pull takes the
commits and stops and Update goes the whole way -- pull, build what that
brought in, install every APK it produced. Splitting them is the same
reasoning that stopped a checkout building: bringing a checkout up to
date is cheap and something you do while looking, and the expensive half
belongs to the button that says it does it.
Update is composed from the two things the app already does rather than
given a route of its own, so each half reports where it already did. It
skips a component whose build just failed, and hands the installer one
APK at a time: an install intent is modal, so two fired together means
the second replaces the first and a component is silently never
installed. The rest wait in ComponentState.ReadyToInstall, offered again
by continuePendingInstalls -- the same machinery as a download waiting
for a wrongly-signed copy to be removed. Deriving "is the installer
free?" from the component states could not work, because handing a file
over clears the state it would have read; caught by pressing Update on a
project that builds two APKs.
The project-wide Rebuild goes with it, and ProjectState collapses to one
Working(what, status): Pull, Update and moving the checkout differ in
nothing but the label, and the old one said "Pulling and building" for
all three.
Then the two things behind "nothing was flagged as out of date, and Pull
is disabled" after switching ai-app onto another branch:
- hasWorkWaiting is now one predicate for both the "Up to date" heading
and whether Update can be pressed, and it counts a component whose
build no longer matches the checkout. Without that a branch switch
left the card filed under "Up to date" with only a small note in one
component's row.
- A moved checkout re-asks its remote (RemoteChecks::recheck, on top of
a new Checks::forget). The cached answer was about the branch just
left and was reported with checkPending false, so a branch one commit
behind its upstream read as one with nothing to pull. Measured: git
said [behind 1] while the card said newCommits false.
A component's install button says Install rather than Build where nothing
is built yet, since the button beside it now says Build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9efe08e4ce
commit
af605211e2
7 files changed
+515
-162
No files matched your search
@@ -551,6 +551,67 @@ mutable at runtime from the phone.
|
||||
process marked its own errors -- and sequences with no meaning on a
|
||||
phone are consumed rather than printed, so a cursor movement cannot
|
||||
arrive looking like a corrupted log.
|
||||
- **A project's row has three buttons -- Pull, Update, Remove -- and the
|
||||
first two are the same act at two lengths.** Pull takes the commits and
|
||||
stops (`?build=false` on the pull route, `BuildState::pull_only`);
|
||||
Update pulls, builds what that brought in, and installs every APK it
|
||||
produced. Iris asked for exactly this pair on 2026-09-02, replacing
|
||||
"Pull & Build" beside a project-wide Rebuild. The point of splitting
|
||||
them is that bringing a checkout up to date is cheap and something
|
||||
somebody does while looking, where building and installing is the thing
|
||||
they press when they want it on the phone -- the same reasoning that
|
||||
stopped a checkout building.
|
||||
Update is composed in the app out of the two things it already does
|
||||
rather than given a route of its own: the pull-and-build reports in the
|
||||
project's row exactly as Pull does, and each install reports in its own
|
||||
component's row exactly as that component's own Update does. It skips a
|
||||
component whose build just failed -- that row already says so, and
|
||||
asking again would run the same failing command to say it twice -- and
|
||||
it hands the installer **one** APK at a time. An install intent is
|
||||
modal and takes the screen, so a project building two APKs would
|
||||
otherwise have the second intent replace the first and one component
|
||||
would silently never be installed; the rest wait in
|
||||
`ComponentState.ReadyToInstall`, which `continuePendingInstalls`
|
||||
offers when the screen comes back, through the same machinery and the
|
||||
same two triggers as a download waiting for a wrongly-signed copy to be
|
||||
removed.
|
||||
A component's own install button says **Install** rather than "Build"
|
||||
where nothing is built yet: with the right-hand button now saying
|
||||
"Build" for a component that is not current, the two read as one word
|
||||
meaning two things in the same row. It is named for what you end up
|
||||
with, which is also the half it has that the Build beside it does not.
|
||||
The project-wide Rebuild is gone with it (each component still has its
|
||||
own), and with it `ProjectState.Rebuilding` -- the two remaining
|
||||
project-wide states collapsed into one `Working(what, status)` that
|
||||
carries its own words, because Pull, Update and moving the checkout
|
||||
differ in nothing but the label and the old one said "Pulling and
|
||||
building" for all three.
|
||||
|
||||
- **One predicate decides both the "Up to date" heading and whether
|
||||
Update can be pressed** (`hasWorkWaiting` in `UpdaterScreen.kt`): they
|
||||
are the same question, since Update is the button that clears
|
||||
everything the heading is about, and a card filed under "Up to date"
|
||||
above a pressable Update is the list disagreeing with itself. Three
|
||||
ways to have work waiting -- commits on the remote, an APK newer than
|
||||
the copy installed here, and **a component whose build no longer
|
||||
matches the checkout**. The third was missing, so switching branches
|
||||
from the phone left the card sitting under "Up to date" with only the
|
||||
small note in one component's row to say otherwise. Iris found it by
|
||||
switching ai-app onto another branch and seeing nothing flagged.
|
||||
|
||||
- **A moved checkout re-asks its remote, because the last answer was
|
||||
about the branch it left.** `RemoteChecks::recheck` forgets the cached
|
||||
answer and starts a fresh check, called from `move_checkout` where the
|
||||
thing that invalidated it happens. Without the forgetting the card
|
||||
answers for the previous branch with `checkPending` false -- so a
|
||||
branch one commit behind its upstream reads as one with nothing to
|
||||
pull, and Pull is disabled about it with nothing on screen saying the
|
||||
answer predates the move. Measured, not argued: git said `[behind 1]`
|
||||
while the card said `newCommits: false`. `Checks::forget` is the
|
||||
primitive, and it deliberately leaves `in_flight` alone for the reason
|
||||
`update` does -- a worker already running still owns the right to write
|
||||
its answer.
|
||||
|
||||
- **There are three sizes of refresh, and using the wrong one is what
|
||||
makes the list feel like it has a mind of its own.** `refresh()` drops
|
||||
the list to a spinner and asks every remote -- it belongs to arriving,
|
||||
|
||||
Reference in new issue
Block a user