Notes: one carry-across, and the built-in card taking its own update

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-02 22:10:04 -04:00
1 parent a8f75be190
commit 3b058f9332
1 file changed
+55 -11
+55 -11
View File
@@ -605,17 +605,32 @@ mutable at runtime from the phone.
Update is pressable. Iris's rule for it, 2026-09-02: "it should not do
that until things actually can be updated."
- **What a build recorded is carried across the self row being
re-derived, like the settings beside it.** `reconcile_self` rebuilds
this server's own row from its declaration at every startup, and
`built_from`/`built_mode` were not among the things carried -- so the
commit written down by the build that produced the new binary was
forgotten by the process that build started, and restarting is how this
server takes an update. Its own card then had no freshness ever again:
never behind, never current, and its Update with nothing to do, since
both readings need a commit to compare against. `built_records` /
`restore_built` carry it, keyed by name *and* directory like every
other carry-across here.
- **Two places rewrite a project's components list, and one type says
what survives it.** `AppState::approve_declaration` and
`registry::reconcile_self` both replace `components` wholesale with
what the checkout declares -- right for everything the project asked
for, wrong for the three things this machine knows: the package read
out of an APK, the mode somebody chose here, and the commit and mode a
build recorded. `CarriedOver` / `carried_over` / `restore_carried_over`
hold all three, keyed by name *and* `cwd` like every other
carry-across here, and both callers take the whole thing.
It is one type because it was three collect-and-restore pairs
remembered separately, and each caller forgot a different one.
`reconcile_self` dropped `builtFrom`, so the commit written down by the
build that produced the new binary was forgotten by the process that
build started -- and restarting is how this server takes an update, so
its own card had no freshness ever again: never behind, never current,
and its Update with nothing to do, since both readings need a commit to
compare against. Then `approve_declaration` turned out to drop the same
field, which is worse because it hits *every* project: accepting a
declaration told every one of that project's cards it had never been
built here. Silent both times, because "not built here" is a true
sentence about plenty of projects and so reads as an answer rather than
as a loss -- it was found by making freshness say which kind of unknown
it was and then looking at a card that had no business saying that one.
Adding a field to a component now means adding it here once.
`reconcile_self` sets the built-in row's package *after* the restore,
so what is derived wins over an older row carrying something else.
- **One predicate decides both the "Up to date" heading and whether
Update can be pressed** (`hasWorkWaiting` in `UpdaterScreen.kt`): they
@@ -1220,6 +1235,35 @@ mutable at runtime from the phone.
survive a changed CA, port or token, since those break the connection
before any route is reached: those stay one-way doors and `--download`
stays their answer.
**It is also how the built-in card downloads its own APK**, not just
how the rescue dialog does. `downloadApk` sends the built-in project to
`/self/apk` rather than to the manifest's route, because this is the
one download whose server changed underneath it between the build and
the request -- so it takes the route nothing can rename. It carries no
`component` or `variant` and does not need to: that project has one
APK, and which build of it to serve is the mode set on the build
machine.
**And the card's Update installs this app itself**, rather than
building it and leaving a dialog to offer it. Which means it waits for
the server *twice* (`awaitServerBack`): once before fetching, and again
before handing anything to the installer. The restart is deferred a
couple of seconds past the build and then held off for as long as an
APK is going down the wire, so it lands either just before the download
or in the gap between the last byte and the install -- a download that
dies partway reads as the update having failed at the moment it worked,
and an install offered mid-restart replaces this app while the server
it must talk to is down. Bounded, and it goes ahead anyway when the
wait runs out: by the second one the APK is already here, and never
installing it would be the worse failure.
**The dialog is now offered only when the built-in card cannot offer
it** -- that card missing from the list, or no list at all because this
app is too old to read what the server now sends. `AppListScreen`
derives that from `manifestState` and reports it up; `Loading` counts
as present, since not having found out yet is not an answer and a
dialog flashing up during every load is exactly the noise it exists to
avoid. Iris's call, 2026-09-02: the card's own Update should do the
work, "though keep the modal if the dev updater card itself is missing
or fails to load."
- **There is deliberately no general migration mechanism.** Iris's call:
each app decides its own, and the updater's job is only to be able to
get both halves of *itself* to the next version. A project that renames