Repository navigation
Scene AI: a JS app in a flow exports run(), one feeding a field get() - #563
Conversation
Asked to toggle the chess puzzle's solution from a frame button, the scene AI wired button -> toggleSolution -> dispatch render with an app.ts that exported only get(). In a flow the frame calls run(), so the press did nothing, three turns running, with no error anywhere. - scene-lint: the source contract demanded get() from every non-render app, logic apps included, so a correct run() app (and the repo's own logic/jsLogic template) bounced. Now logic needs run, data needs get, an uncategorised app either; the category is config.json's first, as on the frame. New node errors: a scene app in a flow without run(), and one feeding a field without get(). - scene guide: every event node starts a flow, not only render; where a node sits decides which export runs; the button pattern spelled out. docs/js-apps-and-code-nodes.md had "data / logic -> get" in one row. - runtime: an app without the export its node calls logs that once instead of silently returning undefined (behaviour unchanged). - evals: modify-weather-button-unit with a flow_sets_state check. Over 357 local store versions and the examples, the lint adds errors only to the scenes with this bug (chess-puzzle-of-the-day, habit-tracker) and drops a false one. Evals: 22/23 (was 20/22), judge 4.73 (was 4.50). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ESP32 firmware sizeBuilt from 8cf510f by this run; compared with the latest release v2026.10.15.
Breakdown by subsystem —
|
| Subsystem | This PR | v2026.10.15 | Δ |
|---|---|---|---|
| FrameOS core (Nim) | 480,679 | 477,042 | +3,637 (+0.8%) |
| QuickJS | 360,369 | 360,373 | -4 (-0.0%) |
| pixie | 304,370 | 304,354 | +16 (+0.0%) |
| Wi-Fi stack | 295,304 | 295,304 | ±0 |
| Nim stdlib | 251,547 | 251,436 | +111 (+0.0%) |
| FrameOS apps (Nim) | 190,416 | 190,356 | +60 (+0.0%) |
| ESP-IDF misc | 181,367 | 181,564 | -197 (-0.1%) |
| Embedded font | 146,362 | 146,362 | ±0 |
| String pool (attributed to efuse) | 136,170 | 136,134 | +36 (+0.0%) |
| fos_* firmware shell (C) | 136,121 | 132,233 | +3,888 (+2.9%) |
| mbedTLS + certificates | 132,439 | 132,439 | ±0 |
| lwIP / HTTP / WebSocket | 126,816 | 126,800 | +16 (+0.0%) |
| libc / libm / newlib | 116,263 | 116,263 | ±0 |
| Storage (SPIFFS / FatFS / SD / NVS) | 94,076 | 94,080 | -4 (-0.0%) |
| Nim packages (chrono, zippy, chroma, qrgen, ...) | 71,857 | 71,849 | +8 (+0.0%) |
| Display drivers (C) | 64,962 | 64,946 | +16 (+0.0%) |
| Crypto (monocypher) | 35,141 | 35,141 | ±0 |
| Total mapped flash | 3,124,259 | 3,116,676 | +7,583 (+0.2%) |
Inside each subsystem
FrameOS core (Nim) — 480,679
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
frameos/js_runtime |
117,577 | 115,444 | +2,133 (+1.8%) |
frameos/utils |
112,365 | 112,401 | -36 (-0.0%) |
frameos/interpreter.nim |
99,972 | 100,264 | -292 (-0.3%) |
embedded_main.nim |
19,252 | 19,252 | ±0 |
embedded_runtime.nim |
17,830 | 17,834 | -4 (-0.0%) |
lib/tz.nim |
16,722 | 16,722 | ±0 |
frameos_nim_glue.c.obj |
10,793 | 10,635 | +158 (+1.5%) |
frameos/planner.nim |
9,689 | 9,681 | +8 (+0.1%) |
frameos/event_loop.nim |
8,412 | 8,412 | ±0 |
frameos/types.nim |
7,909 | 7,909 | ±0 |
frameos/values.nim |
7,699 | 7,699 | ±0 |
frameos/single_scene_host.nim |
6,687 | 6,712 | -25 (-0.4%) |
frameos/apps.nim |
6,585 | 6,585 | ±0 |
frameos/spool.nim |
5,787 | 5,787 | ±0 |
frameos/input_state.nim |
5,079 | 3,766 | +1,313 (+34.9%) |
embedded_scene.nim |
4,945 | 4,945 | ±0 |
frameos/events_gen.nim |
3,926 | 3,948 | -22 (-0.6%) |
frameos/app_config.nim |
2,995 | 2,935 | +60 (+2.0%) |
fos_netguard.c.obj |
2,786 | 2,179 | +607 (+27.9%) |
frameos/reboot_reason.nim |
2,648 | 2,648 | ±0 |
frameos/node_config.nim |
2,618 | 2,618 | ±0 |
frameos/app_capabilities.nim |
2,489 | 2,489 | ±0 |
frameos/events.nim |
2,410 | 2,410 | ±0 |
frameos/refresh_interval.nim |
1,635 | 1,639 | -4 (-0.2%) |
frameos/runtime_diagnostics.nim |
767 | 767 | ±0 |
frameos/event_log.nim |
427 | 431 | -4 (-0.9%) |
fos_version.c.obj |
328 | 328 | ±0 |
frameos/cloud |
— | 255 | -255 (-100.0%) |
frameos/channels.nim |
152 | 152 | ±0 |
frameos/ids.nim |
137 | 137 | ±0 |
frameos/hal |
44 | 44 | ±0 |
frameos/retired_apps.nim |
14 | 14 | ±0 |
QuickJS — 360,369
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
quickjs.c.obj |
287,842 | 287,846 | -4 (-0.0%) |
libunicode.c.obj |
50,178 | 50,178 | ±0 |
libregexp.c.obj |
13,999 | 13,999 | ±0 |
dtoa.c.obj |
5,685 | 5,685 | ±0 |
cutils.c.obj |
2,141 | 2,141 | ±0 |
fos_qjs_glue.c.obj |
449 | 449 | ±0 |
fos_quickjs_tz.c.obj |
75 | 75 | ±0 |
pixie — 304,370
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
pixie/fontformats/opentype.nim |
55,460 | 55,460 | ±0 |
pixie/fileformats/webp.nim |
37,493 | 37,493 | ±0 |
pixie/fileformats/svg.nim |
36,975 | 36,983 | -8 (-0.0%) |
pixie/paths.nim |
32,895 | 32,875 | +20 (+0.1%) |
pixie/fileformats/jpeg.nim |
29,424 | 29,436 | -12 (-0.0%) |
pixie/fileformats/png.nim |
22,097 | 22,101 | -4 (-0.0%) |
pixie/images.nim |
15,771 | 15,779 | -8 (-0.1%) |
pixie/fonts.nim |
10,995 | 10,995 | ±0 |
pixie/paints.nim |
9,869 | 9,869 | ±0 |
pixie/fileformats/bmp.nim |
8,278 | 8,258 | +20 (+0.2%) |
pixie/common.nim |
7,405 | 7,413 | -8 (-0.1%) |
pixie/blends.nim |
6,516 | 6,516 | ±0 |
pixie/fileformats/gif.nim |
5,957 | 5,965 | -8 (-0.1%) |
pixie/inflatestream.nim |
5,233 | 5,233 | ±0 |
pixie.nim |
5,224 | 5,224 | ±0 |
pixie/fileformats/webp_vp8_tables.nim |
3,881 | 3,881 | ±0 |
pixie/fileformats/ppm.nim |
3,717 | 3,713 | +4 (+0.1%) |
pixie/contexts.nim |
3,054 | 3,054 | ±0 |
pixie/fileformats/qoi.nim |
2,722 | 2,702 | +20 (+0.7%) |
pixie/internal.nim |
754 | 754 | ±0 |
pixie/rgb565.nim |
385 | 385 | ±0 |
pixie/fontformats/svgfont.nim |
215 | 215 | ±0 |
pixie/decodebudget.nim |
50 | 50 | ±0 |
Wi-Fi stack — 295,304
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
libnet80211.a |
136,168 | 136,168 | ±0 |
libpp.a |
62,154 | 62,154 | ±0 |
libwpa_supplicant.a |
54,904 | 54,904 | ±0 |
libphy.a |
34,176 | 34,176 | ±0 |
libesp_wifi.a |
4,780 | 4,780 | ±0 |
libesp_phy.a |
2,541 | 2,541 | ±0 |
libesp_coex.a |
291 | 291 | ±0 |
libcore.a |
287 | 287 | ±0 |
libespnow.a |
3 | 3 | ±0 |
Nim stdlib — 251,547
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
pure/collections/tables.nim |
72,312 | 72,312 | ±0 |
system.nim |
48,501 | 48,457 | +44 (+0.1%) |
pure/unicode.nim |
17,405 | 17,405 | ±0 |
pure/times.nim |
16,884 | 16,888 | -4 (-0.0%) |
pure/json.nim |
13,722 | 13,722 | ±0 |
std/private/dragonbox.nim |
10,351 | 10,351 | ±0 |
pure/strutils.nim |
8,047 | 8,047 | ±0 |
pure/parsexml.nim |
6,003 | 6,011 | -8 (-0.1%) |
pure/algorithm.nim |
5,372 | 5,372 | ±0 |
pure/base64.nim |
5,290 | 5,290 | ±0 |
pure/hashes.nim |
4,181 | 4,181 | ±0 |
pure/xmlparser.nim |
3,555 | 3,555 | ±0 |
pure/parsejson.nim |
3,553 | 3,553 | ±0 |
std/formatfloat.nim |
3,504 | 3,504 | ±0 |
pure/uri.nim |
3,225 | 3,225 | ±0 |
pure/streams.nim |
2,820 | 2,820 | ±0 |
pure/strformat.nim |
2,546 | 2,546 | ±0 |
std/syncio.nim |
2,339 | 2,355 | -16 (-0.7%) |
pure/collections/sets.nim |
2,075 | 2,075 | ±0 |
pure/xmltree.nim |
2,047 | 2,047 | ±0 |
pure/lexbase.nim |
1,801 | 1,801 | ±0 |
pure/parseutils.nim |
1,750 | 1,750 | ±0 |
system/exceptions.nim |
1,598 | 1,598 | ±0 |
pure/strtabs.nim |
1,572 | 1,572 | ±0 |
pure/pathnorm.nim |
1,336 | 1,336 | ±0 |
pure/options.nim |
1,016 | 1,016 | ±0 |
std/private/ospaths2.nim |
994 | 994 | ±0 |
pure/collections/lists.nim |
979 | 979 | ±0 |
pure/math.nim |
841 | 841 | ±0 |
pure/collections/deques.nim |
822 | 822 | ±0 |
pure/random.nim |
696 | 696 | ±0 |
std/private/digitsutils.nim |
651 | 651 | ±0 |
std/oserrors.nim |
633 | 633 | ±0 |
std/private/osdirs.nim |
572 | 572 | ±0 |
system/dollars.nim |
529 | 529 | ±0 |
std/monotimes.nim |
501 | 406 | +95 (+23.4%) |
std/private/oscommon.nim |
329 | 329 | ±0 |
std/envvars.nim |
250 | 250 | ±0 |
std/private/decode_helpers.nim |
240 | 240 | ±0 |
std/private/ossymlinks.nim |
187 | 187 | ±0 |
pure/bitops.nim |
162 | 162 | ±0 |
pure/os.nim |
99 | 99 | ±0 |
std/private/osfiles.nim |
96 | 96 | ±0 |
std/assertions.nim |
87 | 87 | ±0 |
core/macros.nim |
65 | 65 | ±0 |
system/ctypes.nim |
9 | 9 | ±0 |
FrameOS apps (Nim) — 190,416
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
apps/data/icalJson |
50,745 | 50,733 | +12 (+0.0%) |
apps/render/calendar |
30,886 | 30,874 | +12 (+0.0%) |
apps/data/localImage |
10,432 | 10,432 | ±0 |
apps/render/split |
9,732 | 9,772 | -40 (-0.4%) |
apps/data/eventsToAgenda |
9,068 | 9,068 | ±0 |
apps/render/zoomPan |
8,668 | 8,648 | +20 (+0.2%) |
apps/apps.nim |
7,251 | 7,247 | +4 (+0.1%) |
apps/render/text |
5,483 | 5,455 | +28 (+0.5%) |
apps/data/xmlToJson |
5,428 | 5,428 | ±0 |
apps/render/svg |
5,208 | 5,192 | +16 (+0.3%) |
apps/render/image |
4,708 | 4,708 | ±0 |
apps/data/rotateImage |
3,465 | 3,465 | ±0 |
apps/logic/setAsState |
3,453 | 3,453 | ±0 |
apps/data/downloadImage |
3,321 | 3,325 | -4 (-0.1%) |
apps/render/gradient |
3,213 | 3,229 | -16 (-0.5%) |
apps/data/qr |
3,118 | 3,122 | -4 (-0.1%) |
apps/data/downloadUrl |
3,053 | 3,053 | ±0 |
apps/render/color |
2,844 | 2,824 | +20 (+0.7%) |
apps/data/newImage |
2,668 | 2,668 | ±0 |
apps/data/resizeImage |
2,558 | 2,558 | ±0 |
apps/render/opacity |
2,320 | 2,296 | +24 (+1.0%) |
apps/data/log |
2,299 | 2,299 | ±0 |
apps/data/prettyJson |
2,225 | 2,225 | ±0 |
apps/data/clock |
1,871 | 1,875 | -4 (-0.2%) |
apps/data/parseJson |
1,848 | 1,856 | -8 (-0.4%) |
apps/logic/nextSleepDuration |
1,663 | 1,663 | ±0 |
apps/logic/ifElse |
1,494 | 1,494 | ±0 |
apps/logic/breakIfRendering |
1,394 | 1,394 | ±0 |
ESP-IDF misc — 181,367
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
libesp_hw_support.a |
37,636 | 37,632 | +4 (+0.0%) |
libhal.a |
21,865 | 21,865 | ±0 |
libfreertos.a |
17,240 | 17,240 | ±0 |
libesp_system.a |
13,736 | 13,780 | -44 (-0.3%) |
libesp_driver_uart.a |
11,627 | 11,627 | ±0 |
libheap.a |
8,660 | 8,660 | ±0 |
libesp_driver_i2c.a |
8,062 | 8,062 | ±0 |
libesp_driver_spi.a |
7,756 | 7,756 | ±0 |
libconsole.a |
5,733 | 5,733 | ±0 |
libjson.a |
4,940 | 4,940 | ±0 |
libesp_driver_gpio.a |
3,878 | 3,878 | ±0 |
libbootloader_support.a |
3,813 | 3,813 | ±0 |
libxtensa.a |
3,691 | 3,691 | ±0 |
libesp_driver_usb_serial_jtag.a |
3,408 | 3,408 | ±0 |
libesp_ringbuf.a |
3,322 | 3,322 | ±0 |
libesp_event.a |
3,253 | 3,253 | ±0 |
libesp_driver_ledc.a |
3,054 | 3,054 | ±0 |
libesp_mm.a |
2,451 | 2,451 | ±0 |
libapp_update.a |
2,414 | 2,414 | ±0 |
libesp_timer.a |
2,337 | 2,337 | ±0 |
libesp_psram.a |
2,172 | 2,329 | -157 (-6.7%) |
libsoc.a |
2,316 | 2,316 | ±0 |
libesp_adc.a |
2,285 | 2,285 | ±0 |
libpthread.a |
1,397 | 1,397 | ±0 |
liblog.a |
1,375 | 1,375 | ±0 |
libesp_security.a |
1,050 | 1,050 | ±0 |
libesp_rom.a |
902 | 902 | ±0 |
libesp_app_format.a |
507 | 507 | ±0 |
libxt_hal.a |
437 | 437 | ±0 |
libesp_common.a |
26 | 26 | ±0 |
libesp_pm.a |
24 | 24 | ±0 |
Embedded font — 146,362
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
assets/fonts.nim |
146,362 | 146,362 | ±0 |
String pool (attributed to efuse) — 136,170
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
esp_efuse_utility.c.obj |
135,124 | 135,088 | +36 (+0.0%) |
esp_efuse_rtc_calib.c.obj |
566 | 566 | ±0 |
esp_efuse_table.c.obj |
268 | 268 | ±0 |
esp_efuse_api.c.obj |
151 | 151 | ±0 |
esp_efuse_startup.c.obj |
61 | 61 | ±0 |
fos_ firmware shell (C)* — 136,121
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
fos_http.c.obj |
22,185 | 22,185 | ±0 |
fos_cloud.c.obj |
19,622 | 19,258 | +364 (+1.9%) |
fos_console.c.obj |
13,950 | 13,950 | ±0 |
fos_client.c.obj |
12,933 | 12,266 | +667 (+5.4%) |
fos_settings.c.obj |
7,618 | 7,618 | ±0 |
fos_scenes.c.obj |
7,515 | 7,578 | -63 (-0.8%) |
fos_ota.c.obj |
5,467 | 5,467 | ±0 |
fos_cloud_contract.c.obj |
4,768 | 4,744 | +24 (+0.5%) |
fos_config.c.obj |
4,619 | 4,619 | ±0 |
fos_wifi.c.obj |
3,189 | 3,189 | ±0 |
fos_assets.c.obj |
3,124 | 3,124 | ±0 |
fos_sd_probe.c.obj |
2,753 | 2,753 | ±0 |
fos_buttons.c.obj |
2,737 | 2,493 | +244 (+9.8%) |
fos_schedule.c.obj |
2,614 | 2,602 | +12 (+0.5%) |
fos_assets_sd.c.obj |
2,484 | 2,484 | ±0 |
fos_events.c.obj |
2,332 | 2,320 | +12 (+0.5%) |
fos_panel_draw.c.obj |
2,229 | — | +2,229 |
fos_status_screen.c.obj |
893 | 2,049 | -1,156 (-56.4%) |
main.c.obj |
1,703 | 1,699 | +4 (+0.2%) |
fos_config_parse.c.obj |
1,467 | 1,467 | ±0 |
fos_url_guard.c.obj |
1,397 | 1,397 | ±0 |
fos_tz.c.obj |
1,294 | 1,294 | ±0 |
fos_buzzer.c.obj |
1,162 | 901 | +261 (+29.0%) |
fos_battery.c.obj |
1,140 | 1,140 | ±0 |
fos_minisig.c.obj |
1,140 | 1,140 | ±0 |
fos_backend_http.c.obj |
1,072 | 1,072 | ±0 |
fos_commands.c.obj |
940 | 940 | ±0 |
fos_lan_access.c.obj |
841 | — | +841 |
fos_scene_state.c.obj |
650 | 650 | ±0 |
fos_framebuffer.c.obj |
483 | 483 | ±0 |
fos_button_timing.c.obj |
434 | — | +434 |
fos_board.c.obj |
334 | 334 | ±0 |
fos_assets_path.c.obj |
259 | 259 | ±0 |
fos_power.c.obj |
241 | 241 | ±0 |
fos_battery_filter.c.obj |
214 | 214 | ±0 |
fos_wake.c.obj |
199 | 184 | +15 (+8.2%) |
fos_json_guard.c.obj |
119 | 119 | ±0 |
mbedTLS + certificates — 132,439
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
libmbedcrypto.a |
71,295 | 71,291 | +4 (+0.0%) |
libmbedtls.a |
45,799 | 45,799 | ±0 |
libmbedx509.a |
7,088 | 7,092 | -4 (-0.1%) |
libesp-tls.a |
6,705 | 6,705 | ±0 |
libesp_https_server.a |
1,552 | 1,552 | ±0 |
lwIP / HTTP / WebSocket — 126,816
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
liblwip.a |
78,461 | 78,445 | +16 (+0.0%) |
libhttp_parser.a |
10,350 | 10,350 | ±0 |
libesp_http_server.a |
10,020 | 10,020 | ±0 |
libtcp_transport.a |
8,245 | 8,245 | ±0 |
libespressif__esp_websocket_client.a |
6,822 | 6,822 | ±0 |
libesp_http_client.a |
6,666 | 6,666 | ±0 |
libesp_netif.a |
6,252 | 6,252 | ±0 |
libc / libm / newlib — 116,263
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
libc.a |
76,471 | 76,471 | ±0 |
libm.a |
31,563 | 31,563 | ±0 |
libnewlib.a |
6,609 | 6,609 | ±0 |
libstdc++.a |
1,476 | 1,476 | ±0 |
libgcc.a |
98 | 98 | ±0 |
libcxx.a |
46 | 46 | ±0 |
Storage (SPIFFS / FatFS / SD / NVS) — 94,076
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
libspiffs.a |
20,380 | 20,380 | ±0 |
libfatfs.a |
20,133 | 20,133 | ±0 |
libspi_flash.a |
14,168 | 14,168 | ±0 |
libnvs_flash.a |
13,681 | 13,685 | -4 (-0.0%) |
libsdmmc.a |
13,013 | 13,013 | ±0 |
libvfs.a |
6,196 | 6,196 | ±0 |
libesp_driver_sdspi.a |
3,866 | 3,866 | ±0 |
libesp_partition.a |
1,956 | 1,956 | ±0 |
libesp_vfs_console.a |
678 | 678 | ±0 |
libnvs_sec_provider.a |
5 | 5 | ±0 |
Nim packages (chrono, zippy, chroma, qrgen, ...) — 71,857
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
QRgen 3.1.0 |
22,056 | 22,060 | -4 (-0.0%) |
chrono 0.3.1 |
15,598 | 15,586 | +12 (+0.1%) |
zippy 0.10.19 |
15,200 | 15,200 | ±0 |
chroma 1.0.0 |
14,714 | 14,714 | ±0 |
checksums 0.2.1 |
3,371 | 3,371 | ±0 |
flatty 0.3.4 |
533 | 533 | ±0 |
jsony 1.1.5 |
272 | 272 | ±0 |
bumpy 1.1.3 |
113 | 113 | ±0 |
Display drivers (C) — 64,962
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
(everything else) |
21,278 | 21,282 | -4 (-0.0%) |
frameos_panel_table.c.obj |
6,685 | 6,685 | ±0 |
EPD_13in3e.c.obj |
1,796 | 1,796 | ±0 |
EPD_3in7.c.obj |
1,564 | 1,564 | ±0 |
EPD_5in79.c.obj |
1,481 | 1,481 | ±0 |
DEV_Debug.c.obj |
1,313 | 1,317 | -4 (-0.3%) |
EPD_2in7.c.obj |
1,292 | 1,292 | ±0 |
EPD_4in2.c.obj |
1,262 | 1,262 | ±0 |
EPD_7in3e.c.obj |
1,208 | 1,208 | ±0 |
EPD_4in2_V2.c.obj |
1,184 | 1,184 | ±0 |
DEV_Config_esp.c.obj |
1,180 | 1,180 | ±0 |
EPD_4in26.c.obj |
1,123 | 1,123 | ±0 |
| 29 smaller rows | 23,596 | 23,572 | +24 (+0.1%) |
Crypto (monocypher) — 35,141
| Part | This PR | v2026.10.15 | Δ |
|---|---|---|---|
monocypher.c.obj |
31,916 | 31,916 | ±0 |
monocypher-ed25519.c.obj |
3,225 | 3,225 | ±0 |
Biggest movers (per object)
| Object | Δ | This PR | Baseline |
|---|---|---|---|
frameos/input_state.nim |
+5,079 | 5,079 | — |
pkgs/QRgen-3.1.0/QRgen/renderer.nim |
-3,780 | — | 3,780 |
frameos/js_runtime/app_runtime.nim |
+2,073 | 74,708 | 72,635 |
fos_client.c.obj |
+667 | 12,933 | 12,266 |
fos_cloud.c.obj |
+364 | 19,622 | 19,258 |
frameos/interpreter.nim |
-292 | 99,972 | 100,264 |
frameos_nim_glue.c.obj |
+158 | 10,793 | 10,635 |
fos_scenes.c.obj |
-63 | 7,515 | 7,578 |
frameos/js_runtime/runtime.nim |
+52 | 31,738 | 31,686 |
nim/lib/system.nim |
+44 | 48,501 | 48,457 |
esp_efuse_utility.c.obj |
+36 | 135,124 | 135,088 |
apps/render/split/app_loader.nim |
-36 | 4,666 | 4,702 |
frameos/single_scene_host.nim |
-25 | 6,687 | 6,712 |
fos_cloud_contract.c.obj |
+24 | 4,768 | 4,744 |
frameos/events_gen.nim |
-22 | 3,926 | 3,948 |
Largest 30 objects
| Object | Subsystem | This PR | v2026.10.15 | Δ |
|---|---|---|---|---|
quickjs.c.obj |
QuickJS | 287,842 | 287,846 | -4 (-0.0%) |
assets/fonts.nim |
Embedded font | 146,362 | 146,362 | ±0 |
esp_efuse_utility.c.obj |
String pool (attributed to efuse) | 135,124 | 135,088 | +36 (+0.0%) |
frameos/interpreter.nim |
FrameOS core (Nim) | 99,972 | 100,264 | -292 (-0.3%) |
frameos/js_runtime/app_runtime.nim |
FrameOS core (Nim) | 74,708 | 72,635 | +2,073 (+2.9%) |
nim/lib/pure/collections/tables.nim |
Nim stdlib | 72,312 | 72,312 | ±0 |
pkgs/pixie-6.1.0/pixie/fontformats/opentype.nim |
pixie | 55,460 | 55,460 | ±0 |
libunicode.c.obj |
QuickJS | 50,178 | 50,178 | ±0 |
nim/lib/system.nim |
Nim stdlib | 48,501 | 48,457 | +44 (+0.1%) |
apps/data/icalJson/ical.nim |
FrameOS apps (Nim) | 45,028 | 45,016 | +12 (+0.0%) |
pkgs/pixie-6.1.0/pixie/fileformats/webp.nim |
pixie | 37,493 | 37,493 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/svg.nim |
pixie | 36,975 | 36,983 | -8 (-0.0%) |
frameos/utils/image.nim |
FrameOS core (Nim) | 35,942 | 35,942 | ±0 |
pkgs/pixie-6.1.0/pixie/paths.nim |
pixie | 32,895 | 32,875 | +20 (+0.1%) |
monocypher.c.obj |
Crypto (monocypher) | 31,916 | 31,916 | ±0 |
frameos/js_runtime/runtime.nim |
FrameOS core (Nim) | 31,738 | 31,686 | +52 (+0.2%) |
pkgs/pixie-6.1.0/pixie/fileformats/jpeg.nim |
pixie | 29,424 | 29,436 | -12 (-0.0%) |
apps/render/calendar/app.nim |
FrameOS apps (Nim) | 23,817 | 23,817 | ±0 |
fos_http.c.obj |
fos_* firmware shell (C) | 22,185 | 22,185 | ±0 |
pkgs/pixie-6.1.0/pixie/fileformats/png.nim |
pixie | 22,097 | 22,101 | -4 (-0.0%) |
fos_cloud.c.obj |
fos_* firmware shell (C) | 19,622 | 19,258 | +364 (+1.9%) |
embedded_main.nim |
FrameOS core (Nim) | 19,252 | 19,252 | ±0 |
frameos/utils/status_screen.nim |
FrameOS core (Nim) | 17,939 | 17,943 | -4 (-0.0%) |
x509_crt_bundle.S.obj |
mbedTLS + certificates | 17,928 | 17,928 | ±0 |
embedded_runtime.nim |
FrameOS core (Nim) | 17,830 | 17,834 | -4 (-0.0%) |
nim/lib/pure/unicode.nim |
Nim stdlib | 17,405 | 17,405 | ±0 |
frameos/utils/asset_colors.nim |
FrameOS core (Nim) | 17,333 | 17,333 | ±0 |
nim/lib/pure/times.nim |
Nim stdlib | 16,884 | 16,888 | -4 (-0.0%) |
lib/tz.nim |
FrameOS core (Nim) | 16,722 | 16,722 | ±0 |
pkgs/pixie-6.1.0/pixie/images.nim |
pixie | 15,771 | 15,779 | -8 (-0.1%) |
Flash = .text + .rodata from the linker map via esp_idf_size; "Inside each subsystem" splits every bucket one level down (the Nim core by directory, apps by app, packages by package, ESP-IDF by archive) over all objects, not just the largest. "String pool (attributed to efuse)" is the linker's merged string-literal pool for the whole image, not efuse code — see docs/esp32-image-size.md.
# Conflicts: # backend/app/utils/scene_guide_gen.py # cloud/apps/auth-web/src/lib/ai/scene-guide.gen.ts
The bug
The scene AI, asked to make a frame button toggle the chess puzzle's solution, wired
button→toggleSolution→dispatch render, wheretoggleSolution'sapp.tsexported onlyget(). In a flow, the frame callsrun(). So a press did nothing, for three turns, and no error showed anywhere.Three things taught the model to write
get()or let it slide:get().lintJsAppSourcetold every non-render JS app "mustexport function get(app, context)… nothing else is called", including logic apps. A correctrun()app came back as an error. The repo's ownlogic/jsLogictemplate failed it too, and so didchess-puzzle-of-the-day@11, the version that does exportrun.docs/js-apps-and-code-nodes.md(which the AI canread_doc) had one row: "data/logic→export function get".__frameosInvokereturnsundefinedwhen an export is missing. Catalog Nim apps in a flow already raise "cannot be run; use get()".The fix
docs/scene-guide.md, regenerated for both AIs and both MCPs): "Edges" now says every event node starts a flow, not justrender. "Exports" says where the node sits decides the call: a node feeding a field callsget()(data), a node in a flow callsrun()(logic), and the other export is never called. It also shows the button pattern:button→ logic app withrun()→dispatch render. The checklist says the same.scene-lint.ts):run, a data appget. An app with no category anywhere must export one of the two, and its node's place decides which (it used to default to "data" and demandget).config.jsonfirst, as on the frame. The chess app's apps entry had no top-level category.run(), and a scene app feeding a field withoutget().export function,export constandexport { x as run }. A default export is left alone.Dateis "UTC only".app_runtime.nimprelude): when a node calls an export the app does not have, the frame logs once per interpreter: "This app has no run(), so it does nothing in a flow…", and the same forget(). It only logs and changes no behaviour. A scene that "works" today with an inert app in its render flow keeps working.js-apps-and-code-nodes.mdtable now has separatedataandlogicrows.modify-weather-button-unit("when someone presses a button, switch between Celsius and Fahrenheit and redraw") with a newflow_sets_statecheck. The check walks thebuttonevent's flow and fails on any JS app there withoutrun().Measured
chess-puzzle-of-the-day@3, @4, @9, @10 andhabit-tracker@2, @3 (aget()-onlyhabitTickProcessorin both its button flow and its cloud flow). The one error that goes away is the false one onchess-puzzle-of-the-day@11.modify-weather-button-unitpassed twice. The model wrotebutton→ alogicapp exportingrun()that callsframeos.setState('temperatureUnit', …)→dispatch render, with 0 bounces.create-split-dashboardfailed in both runs. In the first, the model gave up after 4 payload-validator bounces ("Scene 0 must include nodes"), which is not this lint. The rerun failed only on the one check the baseline also failed: it drew its own weather instead of using the catalog app.Parity: the backend AI reads the same guide. The backend has no scene linter, so the lint half is cloud-only. The runtime log is the same on Linux, the ESP32 and the wasm preview, from 2026.10.16.
Tests
scene-lint.test.ts: the chess scene as delivered (two errors),run,export { toggle as run },export default, arun()-only app feeding a field, and thejsLogictemplate in a button flow. Both new tests fail on the old linter.evals/lib/checks.test.ts:flow_sets_state.tools.test.ts: the Scene AI: a scene app that no node runs bounces back to the model #559 fixture ("delivers the app together with its node") was itself the bug: aget()-only toggle wired into a button flow. It now exportsrun().test_js_app_runtime.nim: aget()-only app called asrun()logs once, arun()-only app called asget()logs once. The other 21 Nim test files that run JS apps pass.pnpm verifyfromcloud/: 28/28 tasks, auth-web 2380/2380.auth-web test:integration: 520/520. The frame-hub and ledger suites were not run: nothing they import changed.generate_scene_guide.py --checkis clean (the backend suite runs it).🤖 Generated with Claude Code