Files
dev-updater/server
iris 90082bd286 Each component builds on its own, and stops reporting when it is done
A project's components were decoupled everywhere except the one place it
showed: there was a single build slot per project, and a single card state
in the app keyed by project alone. So pressing Update on one client of a
two-client project disabled the other client's button for the length of a
build it shares nothing with, drew this one's progress bar and download
percentage under the other's row, and -- had the button been pressable --
would have been a silent no-op on the server, since a second request while
one was running returned without starting anything.

The slot is now per component. `Inner` has no `building` flag; a
component's own `ComponentRun` with an open `step` is the answer, and
`claim` writes that entry synchronously under the lock the route answers
from, so nothing can read a just-claimed component as idle -- which the
phone would take for a build that had already finished. A failure is
recorded against the component whose command it was rather than in the
project's one error slot, which two components building at once cannot
share.

A pull stays exclusive with everything, because there is one checkout and
it rewrites the files every component builds from. Releasing it and
claiming what it decided to build happen under one lock: a phone polling in
the gap would find a project neither pulling nor building and call the run
over.

The app mirrors the split -- `ProjectState` for the pull and the
project-wide Rebuild, `ComponentState` keyed by component for everything
one component is asked to do. Two hierarchies rather than one keyed by a
pair, so a download has nowhere project-wide to be stored. A component's
failure is drawn in its own row beside the Retry that acts on it, which is
also where a failed service action now reports.

And a finished component shows nothing at all: the elapsed times are gone
from both halves of the wire, and its button simply goes back to being
pressable. A bar, a count and a last line all describe something happening
now, and left up they sit there looking live next to a sibling that
genuinely is.

Verified on the emulator against test-projects/two-clients, which exists
for this: while `tablet` built, its row alone carried the bar and its
button alone was disabled, `phone` stayed pressable and silent, and both
returned to normal with no timing left behind.
2026-09-01 03:09:33 -04:00
..