Skip to content

feat(kaos): Keep a fetch's card up for the install it announces - #123

Merged
jbenedictocenteno merged 8 commits into
developfrom
feat/appdata-get-install
Nov 13, 2026
Merged

jbenedictocenteno merged 8 commits into
developfrom
feat/appdata-get-install

Conversation

@pabloecosystem

Copy link
Copy Markdown
Contributor

Description of Change

A host that installs an app over the air sends appdata_get and then install, and the glass shows the gap between them. The fetch's card closes when the folder is written and opens again when the install reports its 0 %. For a moment the board is back at the launcher, as if nothing were coming.

appdata_get takes an optional install=on (a KARG_BOOL, off by default, like install's run) to say the install comes next. The fetch is then the first step of that install, and its card belongs to the install:

  • The fetch ends, whatever the end: neither the done nor the failure goes out. AO_AppMgr goes back to idle with the card held at 0 % and says it again every second on beat, so the LED stays up too.
  • The install begins (FLASH_REQ_SIG or APP_RECV_REQ_SIG leaves idle): it takes the card over with nothing published. The glass goes from the fetch's 0 % to the install's, and the face the fetch just wrote stays on it.
  • The install is refused before it begins: the unnamed done ends the card at once, where the refusal is known.
  • Anything else AO_AppMgr turns to (another appdata_get, an erase, a run, keep-only): the card ends before that request publishes anything. Otherwise a second fetch's 0 % would reach the glass under the old face, and the done after it would close the new card. The remaining ways out of idle end it on idle's exit.
  • No install within KODE_APPMGR_HOLD_MS (15 s, kqp_config.h): the unnamed done ends it.
  • A fetch that fails leaves no failure sheet. The app's data is what the install looks like, never what lets it happen, and the host installs all the same.
  • While the card is held, idle answers a low-power round as busy, with the reason install announced.

The answer to the fetch (>|appdata …, >ok, >err …) is the same with or without the key, and a fetch without it is unchanged. An older board refuses the key with code=3 msg=install, so a host asks again without it.

The API change is one argument: appmgr_appdata_get_request(project, url, install, who) and AppDataGetReqEvt.install. There is one new private signal, APP_HOLD_SIG.

kgui needs nothing. The card already follows the APP_FLASH_* family alone, and keeps its face while writing stays true. KodeOS needs only the submodule move once this lands.

Test Scenarios

  • tools/host_tests.sh: 5501 cases across 8 suites, all passing (5490 on develop before the change; 11 new).
  • tools/host_tests.sh --sanitize: the same 5501 under ASan and UBSan, all passing.
  • tools/check_layers.sh: all layer rules hold.
  • ESP-IDF v6.1 on macOS (arm64). Not yet on a board; that needs a KodeOS build with this SDK.

New cases:

  • test_appmgr_ao.c [appdata_get][hold]: a fetch without the key still ends its card; the held card keeps saying 0 %; an https install and a terminal install each take it over without a done, and the hold does not outlive them; the deadline ends it; a refused install ends it at once; another fetch ends it before its own 0 %; a failed fetch leaves no failure sheet and the deadline still ends the card; the request carries the flag.
  • test_console_appdata.c: absent, off and on reach AO_AppMgr as they should; install=yes is refused with code=3 msg=install and nothing is asked.

Related links

A host that installs an app over the air sends `appdata_get` and then
`install`, and the glass shows the gap between them: the fetch's card
closes when the folder is written and opens again when the install
reports its 0 %, so the person watching sees the board go back to the
launcher for a moment, as if nothing were coming.

`appdata_get` takes `install=on` to say the install comes next. The
fetch is then the first step of that install, and its card is the
install's: whatever the fetch's end, neither the done nor the failure
goes out, and AO_AppMgr holds the card at 0 % in its idle state, saying
it again every second so the LED stays up. The install that begins
takes it over with the fetch's face still on it; one refused before it
begins, anything else AO_AppMgr turns to, or KODE_APPMGR_HOLD_MS with
no install, ends it with the unnamed done. A fetch that fails leaves no
failure sheet, since the app's data is what the install looks like,
never what lets it happen. The answer to the fetch is the same with or
without the key, and a fetch without it is unchanged.

An older board refuses the key with `code=3 msg=install`, so a host
asks again without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A terminal install took the card over like an install from a URL, with
nothing published. It announces itself only at its ready, so one that
ended before it - the console refused to arm, no plan, no room - owed
the glass no failure: the card stayed up, locked, with nobody to end it.

A terminal install now ends the held card like any other request, before
anything it publishes, and raises its own card at its ready as it always
did. Only an install from a medium or a URL takes the card over.
The held card had a timer of its own beside the beat that says its 0 %
every second, and a private signal for it. Idle already wakes on that
beat while the card is up, so the beat counts the wait and the last one
ends the card: one timer and APP_HOLD_SIG go, and AO_AppMgr's static
gets back the 24 bytes the timer took.

KODE_APPMGR_HOLD_MS stays the one place the wait is set, checked at
build time to be a whole number of beats.
The house style spells a choice out with an if; the answer to a
low-power round over a held card was the one new ternary in the file.
APP_BEAT_SIG was described as the second gone by while waiting on the
network. It is also what says a held card again and counts its wait.
No case asked a held card for its answer to a low-power round, nor sent
it a run, an erase, a keep-only or an install from a medium refused in
idle: each ends the card, and only another fetch was covered. The case
of the take-over now also says what its first report names: nobody,
until the image is read, which is what the interface keeps the fetch's
face through.
A held card had a flag and a count beside it, and with the fetch's own
flag they took AO_AppMgr's static four bytes past what it measures on
develop, in a struct that must stay under 2048 bytes of internal RAM.

The count now runs down, and a count of zero is no card held: one field
where there were two. The fetch's flag sits in a hole ahead of the
fetch's other fields. The struct measures what it does without the
hold.
@jbenedictocenteno
jbenedictocenteno merged commit c699567 into develop Nov 13, 2026
6 checks passed
@jbenedictocenteno
jbenedictocenteno deleted the feat/appdata-get-install branch November 13, 2026 17:20
jbenedictocenteno added a commit to kodediy/KodeOS that referenced this pull request Nov 13, 2026
…69)

An app's report that named no project dropped the face of the card it
arrived on. A card can carry two transfers: the fetch of an app's data
folder and the install it announced (kodediy/kode-sdk#123), or an
install that starts again once its copies are on the card. The later
one names nobody until its image is read, so the face went for the time
a download takes to open and came back at the first chunk.

The card now keeps its face through such a stretch and asks its folder
again, once: the transfer before may be the one that wrote it, and the
folder was last asked when the card rose. That is also what puts a
freshly fetched icon on the card from the install's first report.
@jbenedictocenteno

Copy link
Copy Markdown
Contributor

Thank you for this. The idea is right, the change reads well, and the cases made it easy to see what each way out of the hold does.

We kept your commit as it is and added the following on top, so you can see exactly what changed and why:

  • fix(kaos): End a fetch's held card when a terminal install comes. A terminal install took the card over with nothing published, like an install from a URL. But it announces itself only at its ready, so one that ended before that (the console refused to arm, no plan, no room) owed the glass no failure, and nothing ever ended the card: it stayed up, locked, and the interface answered every low-power round busy. A terminal install now ends the held card like any other request and raises its own at its ready, as it always did. Only an install from a medium or a URL takes the card over, which is the flow the key is for.
  • refactor(kaos): Count a held card's wait on the beat that says it. Idle already wakes every second on beat while the card is up, so the beat counts the wait and the last one ends the card. The second timer and APP_HOLD_SIG are gone. This was about internal RAM: l_appMgr must stay under 2048 bytes, the install fixes that went into develop in fix(kaos): Guard what an install evicts: file check, copy and CRC #125 grew it too, and the two together landed exactly on the limit.
  • refactor(kaos): Keep a held card's wait as one count of beats left. The same reason, finished: the count runs down and zero means no card is held, so the flag beside it goes, and the fetch's own flag sits in a hole of the struct. l_appMgr now measures with the hold what it measures on develop without it: 2008 bytes.
  • docs(kqp): Say the beat also paces a card waiting for its install. APP_BEAT_SIG's description, now that it does both jobs.
  • refactor(kaos): Give a held card's reason to the round without a ternary. House style: a choice is an if.
  • test(kaos): Cover what ends a fetch's held card and what it answers. The cases that were missing: a held card answers a low-power round busy with install announced, and ready once it has ended; a run, an erase and a keep-only each end it; an install from a medium refused in idle ends it; and the take-over's first report names no project.

One thing in the description did not hold, and it is fixed on the other side: kgui did need a change. The install's first reports name no project until its image is read, and kgui dropped the card's face on a report that names nobody, then took it again at the first chunk. It also asked the data folder only once, when the card rose, which is before the fetch has written it. So the card stayed up, as you intended, but its face went away while the download opened, and a newly fetched icon only showed at the first chunk. KodeOS now keeps the face through such reports and asks the folder again at that moment, so the fetched face is on the card from the install's first report: kodediy/KodeOS#69.

Left as you had it: a held card whose install is refused or never comes ends with the unnamed done.

Seen on a board: after appdata_get ... install=on the card stays at 0 % wearing the app's letters; when the install is asked for, the disc becomes the fetched icon before the figure moves, and the install completes with the app in Apps. A fetch whose install never comes ends its card by itself at the deadline, and one whose install is refused ends it at once..

Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants