diff --git a/controller/docs/operation-model-roadmap.svg b/controller/docs/operation-model-roadmap.svg index 8988cf1e..7258f2fe 100644 --- a/controller/docs/operation-model-roadmap.svg +++ b/controller/docs/operation-model-roadmap.svg @@ -381,33 +381,44 @@ Finding 2 closed. A per-poll heartbeat ages a dead session out in ~30s; the refusal names the holder. In-memory, not 4897's record. -SPANS THE BOARD — real work with no ticket, and the small things - - -NO TICKET YET -A finished run leaves no evidence it happened -A LIVE run is meant to be left alone, so the user usually arrives after it ended and finds a bare “3 Done”. The rate is dropped at completion by design — rateSuffix is called only from the not-complete branch — and -“838 MB · 32 MB/s · 4 min” is the only thing that tells a real download from a no-op. Courses can do it today (KolibriSeedState has bytes). ZIM has no clock at all. Books has neither bytes nor speed. The only start -timestamp in main/ is KolibriSeedRepository.startedAtMs, private and unreachable from the row renderer. Also here: the maps card cannot show “adding content” — its progress belongs to the module queue. - - -NO TICKET YET -Replacing the system, and the installer that does it — no ticket on purpose -Moved out of ADFA-4758, which had absorbed it. Neither item is a fix: they are capability the app does not have, on top of a flow that works. Recorded here so they are picked up on their own terms. -• Two separately invoked installers. One ACTION_START becomes five pipelines through four booleans set by three unrelated senders. -• Content-only backup, so a reinstall at another tier can keep what the user added. From zero, the box wipes and rewrites. -Not blocked by this: the tier picker reports success while doing nothing when a rootfs directory exists — implemented and wrong today, so it is a bug with its own ticket. - - -CARRIED -Too small for a card, recorded so they are met on purpose -• The Books “downloads” link is the last fragment hosting itself. Needs an index that can be opened with nothing in flight. -• The three live REST streams can still start together: one provisioner drains asynchronously, so the others see no session yet. -• EnvironmentLock.Owner.MODULE is never acquired, and hasUnfinishedWork is duplicated between EnvironmentLock and PendingContent (orphan TODO on a closed ticket). -• The build's own native-artifact download has no retry: app/build.gradle:362 calls ant.get once, so a dropped socket on a 15 MB fetch fails CI outright. - Seen 13 Aug — SocketException mid-ZIP. ant.get takes retries and maxtime, so it is one line. Same fault ADFA-4893 names, one layer up in the build. -• DONE (ADFA-5152): a clone-receive reopened during a live transfer now lands on the progress screen, not the Send/Receive fork — CloneFragment re-binds the RECEIVE side - symmetrically. Helps while the process is alive; a hard kill is still the durability problem (5146 family). + + +ADFA-5192 · DONE +Legacy tabbed UI retired +MainActivity + the invisible pager, gone. + +Terminal re-homed to TerminalActivity + +4 fragments + carved controllers deleted + +Dead companion leg removed (ADFA-5144) +SPANS THE BOARD — real work with no ticket, and the small things + + +NO TICKET YET +A finished run leaves no evidence it happened +A LIVE run is meant to be left alone, so the user usually arrives after it ended and finds a bare “3 Done”. The rate is dropped at completion by design — rateSuffix is called only from the not-complete branch — and +“838 MB · 32 MB/s · 4 min” is the only thing that tells a real download from a no-op. Courses can do it today (KolibriSeedState has bytes). ZIM has no clock at all. Books has neither bytes nor speed. The only start +timestamp in main/ is KolibriSeedRepository.startedAtMs, private and unreachable from the row renderer. Also here: the maps card cannot show “adding content” — its progress belongs to the module queue. + + +NO TICKET YET +Replacing the system, and the installer that does it — no ticket on purpose +Moved out of ADFA-4758, which had absorbed it. Neither item is a fix: they are capability the app does not have, on top of a flow that works. Recorded here so they are picked up on their own terms. +• Two separately invoked installers. One ACTION_START becomes five pipelines through four booleans set by three unrelated senders. +• Content-only backup, so a reinstall at another tier can keep what the user added. From zero, the box wipes and rewrites. +Not blocked by this: the tier picker reports success while doing nothing when a rootfs directory exists — implemented and wrong today, so it is a bug with its own ticket. + + +CARRIED +Too small for a card, recorded so they are met on purpose +• The Books “downloads” link is the last fragment hosting itself. Needs an index that can be opened with nothing in flight. +• The three live REST streams can still start together: one provisioner drains asynchronously, so the others see no session yet. +• EnvironmentLock.Owner.MODULE is never acquired, and hasUnfinishedWork is duplicated between EnvironmentLock and PendingContent (orphan TODO on a closed ticket). +• The build's own native-artifact download has no retry: app/build.gradle:362 calls ant.get once, so a dropped socket on a 15 MB fetch fails CI outright. + Seen 13 Aug — SocketException mid-ZIP. ant.get takes retries and maxtime, so it is one line. Same fault ADFA-4893 names, one layer up in the build. +• DONE (ADFA-5152): a clone-receive reopened during a live transfer now lands on the progress screen, not the Send/Receive fork — CloneFragment re-binds the RECEIVE side + symmetrically. Helps while the process is alive; a hard kill is still the durability problem (5146 family). a real figure @@ -417,30 +428,22 @@ same launch file — sequence them - - -NO TICKET YET -The legacy UI is invisible but still running — open a ticket for it -Found while writing Cancel (ADFA-5119). The launcher goes SplashActivity → LibraryActivity, and the only door left into the legacy MainActivity is Settings → “Terminal (Debian)”, which passes -EXTRA_TERMINAL_ONLY so ADFA-4987 keeps the old dashboard hidden behind the terminal sheet. But MainActivity.onCreate builds MainPagerAdapter at :292 and only learns it is terminal-only at :344 — -so DeployFragment is still constructed and still observes InstallProgressRepository from behind that sheet. Not dead code: invisible code that still runs, which is why its phase switch has to be kept in step. -The one leak a user can reach: TerminalSessionService:98 opens MainActivity WITHOUT that extra, revealing the whole legacy dashboard with a working install button. Two lines close it — the missing extra, and not building the pager in terminal-only mode. Deleting DeployFragment is the larger piece behind it. -SESSION 15 AUG — WHAT LANDED, AND WHAT IT SURFACED - - -CLONE / STATE-SPINE ARC -The receive/clone dead-ends closed — and the debt the fixes exposed -Landed and merged: ADFA-5143 (a clone in flight has a name and a way back), 5147 (a damaged system reads as damaged), 5150 (systemless Connect/Send route to Recover), 5151 -(confine the receive loop to the recover hub), 5152 (a reopen lands on progress, not the fork), 5154 (Connect + Clone Send redesigned to stacked / 2-page QR), 5157 (shared QrSection), -5155 (receive success waits for services before Home, never a fixed timer). Also landed: ADFA-5158 (get-app QR waits for the hotspot AP IP by polling, not a one-shot; broadened detection). -FOLLOW-UPS WORTH A TICKET - -Shared "poll until a resource is ready" utility (a ServiceReadyGate). The same Handler-reschedule + in-flight guard + teardown now lives in three places — SetupProgressActivity.readyPoll, -the 5155 receive-exit wait, and the 5158 AP-IP poll — with a fourth copy one bug away. Extract one loop (and a services-then-redirect gate on top for the first two). - -ADFA-5146 (finding 2, the release blocker) — LANDED, device-verified. Expires on LAST PROGRESS, not a start timestamp: the 5155/5158 lesson honored — a wall clock must never condemn a -legitimately long op, so a heartbeat the live service refreshes and a dead one lets go cold ages a killed session out in ~30s, across the three content sources (the module queue keeps its own signal). The naming half is currentHolder + BusyMessage (six gates), and isHeld now derives from currentHolder — one source of truth. - -Generalize "which IP does the peer reach me at": peerReachableIp + NetworkInterfaces pass-2 (up, non-loopback, site-local, not Wi-Fi/VPN) replaced a hardcoded 192.168.49.1. One robust -source belongs wherever a host advertises itself — the get-app URL, the copy daemon, the transfer QR — instead of a guess that only some OEMs happen to match. +SESSION 15 AUG — WHAT LANDED, AND WHAT IT SURFACED + + +CLONE / STATE-SPINE ARC +The receive/clone dead-ends closed — and the debt the fixes exposed +Landed and merged: ADFA-5143 (a clone in flight has a name and a way back), 5147 (a damaged system reads as damaged), 5150 (systemless Connect/Send route to Recover), 5151 +(confine the receive loop to the recover hub), 5152 (a reopen lands on progress, not the fork), 5154 (Connect + Clone Send redesigned to stacked / 2-page QR), 5157 (shared QrSection), +5155 (receive success waits for services before Home, never a fixed timer). Also landed: ADFA-5158 (get-app QR waits for the hotspot AP IP by polling, not a one-shot; broadened detection). +FOLLOW-UPS WORTH A TICKET + +Shared "poll until a resource is ready" utility (a ServiceReadyGate). The same Handler-reschedule + in-flight guard + teardown now lives in three places — SetupProgressActivity.readyPoll, +the 5155 receive-exit wait, and the 5158 AP-IP poll — with a fourth copy one bug away. Extract one loop (and a services-then-redirect gate on top for the first two). + +ADFA-5146 (finding 2, the release blocker) — LANDED, device-verified. Expires on LAST PROGRESS, not a start timestamp: the 5155/5158 lesson honored — a wall clock must never condemn a +legitimately long op, so a heartbeat the live service refreshes and a dead one lets go cold ages a killed session out in ~30s, across the three content sources (the module queue keeps its own signal). The naming half is currentHolder + BusyMessage (six gates), and isHeld now derives from currentHolder — one source of truth. + +Generalize "which IP does the peer reach me at": peerReachableIp + NetworkInterfaces pass-2 (up, non-loopback, site-local, not Wi-Fi/VPN) replaced a hardcoded 192.168.49.1. One robust +source belongs wherever a host advertises itself — the get-app URL, the copy daemon, the transfer QR — instead of a guess that only some OEMs happen to match.