[{"data":1,"prerenderedAt":643},["ShallowReactive",2],{"navigation_docs":3,"-decisions-0015-packaging-aot-cache-and-native-image":223,"-decisions-0015-packaging-aot-cache-and-native-image-surround":638},[4,26,96,105,202],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F1.installation","i-lucide-download",{"title":16,"path":17,"stem":18,"icon":19},"The first app","\u002Fgetting-started\u002Ffirst-app","1.getting-started\u002F2.first-app","i-lucide-play",{"title":21,"path":22,"stem":23,"icon":24},"The first test","\u002Fgetting-started\u002Ffirst-test","1.getting-started\u002F3.first-test","i-lucide-flask-conical",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Guide","i-lucide-book-open","\u002Fguide","2.guide",[32,37,42,47,52,57,62,67,72,77,81,86,91],{"title":33,"path":34,"stem":35,"icon":36},"Components","\u002Fguide\u002Fcomponents","2.guide\u002F01.components","i-lucide-component",{"title":38,"path":39,"stem":40,"icon":41},"Inputs and forms","\u002Fguide\u002Finputs-and-forms","2.guide\u002F02.inputs-and-forms","i-lucide-text-cursor-input",{"title":43,"path":44,"stem":45,"icon":46},"Scrolling and viewports","\u002Fguide\u002Fscrolling","2.guide\u002F03.scrolling","i-lucide-scroll-text",{"title":48,"path":49,"stem":50,"icon":51},"Overlays and layers","\u002Fguide\u002Foverlays","2.guide\u002F04.overlays","i-lucide-layers",{"title":53,"path":54,"stem":55,"icon":56},"Styles and theme","\u002Fguide\u002Fstyles","2.guide\u002F05.styles","i-lucide-palette",{"title":58,"path":59,"stem":60,"icon":61},"Composables","\u002Fguide\u002Fcomposables","2.guide\u002F06.composables","i-lucide-puzzle",{"title":63,"path":64,"stem":65,"icon":66},"Widgets","\u002Fguide\u002Fwidgets","2.guide\u002F07.widgets","i-lucide-blocks",{"title":68,"path":69,"stem":70,"icon":71},"The assistant","\u002Fguide\u002Fassistant","2.guide\u002F08.assistant","i-lucide-message-square",{"title":73,"path":74,"stem":75,"icon":76},"The same app on the web","\u002Fguide\u002Fweb","2.guide\u002F09.web","i-lucide-globe",{"title":78,"path":79,"stem":80,"icon":24},"Testing","\u002Fguide\u002Ftesting","2.guide\u002F10.testing",{"title":82,"path":83,"stem":84,"icon":85},"Running and packaging","\u002Fguide\u002Fpackaging","2.guide\u002F11.packaging","i-lucide-package",{"title":87,"path":88,"stem":89,"icon":90},"The terminal contract","\u002Fguide\u002Fterminal","2.guide\u002F12.terminal","i-lucide-square-terminal",{"title":92,"path":93,"stem":94,"icon":95},"Benchmarks","\u002Fguide\u002Fbenchmarks","2.guide\u002F13.benchmarks","i-lucide-gauge",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":25},"AI and agents","i-lucide-bot","\u002Fai","3.ai",[102],{"title":97,"path":103,"stem":104,"icon":98},"\u002Fai\u002Fagents","3.ai\u002F1.agents",{"title":106,"icon":107,"path":108,"stem":109,"children":110},"Decisions","i-lucide-scale","\u002Fdecisions","4.decisions",[111,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,194,198],{"title":112,"path":108,"stem":113,"icon":46},"All decisions","4.decisions\u002Findex",{"title":115,"path":116,"stem":117},"0001. kyo-ui is the DSL","\u002Fdecisions\u002F0001-kyo-ui-is-the-dsl","4.decisions\u002F0001-kyo-ui-is-the-dsl",{"title":119,"path":120,"stem":121},"0002. jatatui renders the cells","\u002Fdecisions\u002F0002-jatatui-renders-the-cells","4.decisions\u002F0002-jatatui-renders-the-cells",{"title":123,"path":124,"stem":125},"0003. Pin Kyo 1.0.0-RC7 and JDK 25","\u002Fdecisions\u002F0003-pin-kyo-rc7-and-jdk-25","4.decisions\u002F0003-pin-kyo-rc7-and-jdk-25",{"title":127,"path":128,"stem":129},"0004. Public Kyo APIs only","\u002Fdecisions\u002F0004-public-kyo-apis-only","4.decisions\u002F0004-public-kyo-apis-only",{"title":131,"path":132,"stem":133},"0005. A SOFT frame contract and an own watcher loop","\u002Fdecisions\u002F0005-soft-frame-contract-and-own-watcher-loop","4.decisions\u002F0005-soft-frame-contract-and-own-watcher-loop",{"title":135,"path":136,"stem":137},"0006. Purity: edge files, lint and a pure painter","\u002Fdecisions\u002F0006-purity-edge-files-and-lint","4.decisions\u002F0006-purity-edge-files-and-lint",{"title":139,"path":140,"stem":141},"0007. Compiler flags from sbt-tpolecat","\u002Fdecisions\u002F0007-compiler-flags-from-sbt-tpolecat","4.decisions\u002F0007-compiler-flags-from-sbt-tpolecat",{"title":143,"path":144,"stem":145},"0008. The artifact split: runner, app, testkit and core","\u002Fdecisions\u002F0008-artifact-split-runner-app-testkit-core","4.decisions\u002F0008-artifact-split-runner-app-testkit-core",{"title":147,"path":148,"stem":149},"0009. tui4s-use and tui4s-widgets, cross-built","\u002Fdecisions\u002F0009-use-and-widgets-cross-built","4.decisions\u002F0009-use-and-widgets-cross-built",{"title":151,"path":152,"stem":153},"0010. The flagship assistant is the blueprint of a real app","\u002Fdecisions\u002F0010-the-flagship-assistant-as-blueprint","4.decisions\u002F0010-the-flagship-assistant-as-blueprint",{"title":155,"path":156,"stem":157},"0011. The web target: a standalone Scala.js app and one theme","\u002Fdecisions\u002F0011-web-target-standalone-scalajs-and-one-theme","4.decisions\u002F0011-web-target-standalone-scalajs-and-one-theme",{"title":159,"path":160,"stem":161},"0012. The documentation is example-first","\u002Fdecisions\u002F0012-documentation-is-example-first","4.decisions\u002F0012-documentation-is-example-first",{"title":163,"path":164,"stem":165},"0013. Benchmarks in the repository, with their own build","\u002Fdecisions\u002F0013-benchmarks-in-repo-with-own-build","4.decisions\u002F0013-benchmarks-in-repo-with-own-build",{"title":167,"path":168,"stem":169},"0014. Release per module, by tag, as 0.1.0-RC1","\u002Fdecisions\u002F0014-release-per-module-tags-and-rc1","4.decisions\u002F0014-release-per-module-tags-and-rc1",{"title":171,"path":172,"stem":173},"0015. Packaging: the AOT cache in the start scripts, native-image as an option","\u002Fdecisions\u002F0015-packaging-aot-cache-and-native-image","4.decisions\u002F0015-packaging-aot-cache-and-native-image",{"title":175,"path":176,"stem":177},"0016. Process: lean mode and agent roles","\u002Fdecisions\u002F0016-process-lean-mode-and-agent-roles","4.decisions\u002F0016-process-lean-mode-and-agent-roles",{"title":179,"path":180,"stem":181},"0017. Deferred: templates, mouse, the kitty keyboard protocol and Windows","\u002Fdecisions\u002F0017-deferred-templates-mouse-and-kitty","4.decisions\u002F0017-deferred-templates-mouse-and-kitty",{"title":183,"path":184,"stem":185},"0018. pini4s is a vendored Git submodule","\u002Fdecisions\u002F0018-pini4s-as-a-vendored-submodule","4.decisions\u002F0018-pini4s-as-a-vendored-submodule",{"title":187,"path":188,"stem":189},"0019. Upstream issues are drafts that the user posts","\u002Fdecisions\u002F0019-upstream-kyo-issues-as-drafts","4.decisions\u002F0019-upstream-kyo-issues-as-drafts",{"title":191,"path":192,"stem":193},"0020. Release from the Actions form","\u002Fdecisions\u002F0020-release-from-the-actions-form","4.decisions\u002F0020-release-from-the-actions-form",{"title":195,"path":196,"stem":197},"0021. The rulebook is CONTRIBUTING.md","\u002Fdecisions\u002F0021-the-rulebook-is-contributing-md","4.decisions\u002F0021-the-rulebook-is-contributing-md",{"title":199,"path":200,"stem":201},"0022. The logo is text","\u002Fdecisions\u002F0022-the-logo-is-text","4.decisions\u002F0022-the-logo-is-text",{"title":203,"icon":204,"path":205,"stem":206,"children":207,"page":25},"Project","i-lucide-compass","\u002Fproject","5.project",[208,213,218],{"title":209,"path":210,"stem":211,"icon":212},"Roadmap","\u002Fproject\u002Froadmap","5.project\u002F1.roadmap","i-lucide-map",{"title":214,"path":215,"stem":216,"icon":217},"Contributing","\u002Fproject\u002Fcontributing","5.project\u002F2.contributing","i-lucide-git-pull-request",{"title":219,"path":220,"stem":221,"icon":222},"Security","\u002Fproject\u002Fsecurity","5.project\u002F3.security","i-lucide-shield",{"id":224,"title":171,"body":225,"description":631,"extension":632,"links":633,"meta":634,"navigation":635,"path":172,"seo":636,"stem":173,"__hash__":637},"docs\u002F4.decisions\u002F0015-packaging-aot-cache-and-native-image.md",{"type":226,"value":227,"toc":620},"minimark",[228,233,237,265,268,271,275,278,282,285,395,406,450,457,491,495,539,543,580,584,608,612],[229,230,232],"h2",{"id":231},"status","Status",[234,235,236],"p",{},"Accepted (2026-10-20). This work landed on 2026-10-20 (L5b):",[238,239,240,244,252,258],"ul",{},[241,242,243],"li",{},"the AOT cache in the start scripts of the examples;",[241,245,246,247,251],{},"the native-image metadata of ",[248,249,250],"code",{},"tui4s-runner",";",[241,253,254,255,251],{},"the script ",[248,256,257],{},"scripts\u002Fnative-image.sh",[241,259,260,261,264],{},"the smoke job ",[248,262,263],{},".github\u002Fworkflows\u002Fnative.yml"," on GraalVM CE.",[234,266,267],{},"A local build with GraalVM CE 25 made the native Counter. The first run of the smoke job is in CI.",[234,269,270],{},"The table below has the measurements of the spike of 2026-10-17. The numbers of the shipped start scripts and of the bench (2026-10-20) follow it.",[229,272,274],{"id":273},"date","Date",[234,276,277],{},"2026-10-17 (D16 with the numbers), 2026-10-19 (GraalVM Community in CI), 2026-10-20 (the L5b plan).",[229,279,281],{"id":280},"context","Context",[234,283,284],{},"First frame from process start, PTY 100x30, median of 15 runs, 4 vCPU Linux:",[286,287,288,307],"table",{},[289,290,291],"thead",{},[292,293,294,298,301,304],"tr",{},[295,296,297],"th",{},"Mode",[295,299,300],{},"Counter",[295,302,303],{},"Dashboard",[295,305,306],{},"RSS (counter)",[308,309,310,325,339,353,367,381],"tbody",{},[292,311,312,316,319,322],{},[313,314,315],"td",{},"Launcher today (JVM)",[313,317,318],{},"1,101 ms",[313,320,321],{},"1,233 ms",[313,323,324],{},"125 MB",[292,326,327,330,333,336],{},[313,328,329],{},"Plain Java jatatui program (JVM floor)",[313,331,332],{},"147 ms",[313,334,335],{},"n\u002Fa",[313,337,338],{},"56 MB",[292,340,341,344,347,350],{},[313,342,343],{},"JDK 25 AOT cache only",[313,345,346],{},"521 ms",[313,348,349],{},"588 ms",[313,351,352],{},"113 MB",[292,354,355,358,361,364],{},[313,356,357],{},"AOT cache + launcher flags",[313,359,360],{},"361 ms",[313,362,363],{},"433 ms",[313,365,366],{},"94 MB",[292,368,369,372,375,378],{},[313,370,371],{},"AOT + background preload + flags",[313,373,374],{},"320 ms",[313,376,377],{},"395 ms",[313,379,380],{},"93 MB",[292,382,383,386,389,392],{},[313,384,385],{},"GraalVM native-image",[313,387,388],{},"101 ms",[313,390,391],{},"104 ms",[313,393,394],{},"31 MB",[234,396,397,398,401,402,405],{},"The shipped start scripts (2026-10-20, the same kind of machine: the 4-vCPU Linux build container ",[248,399,400],{},"container-4vcpu"," of the bench, PTY 100x30, two rounds of 5 runs, the median of each; ",[248,403,404],{},"examples\u002FREADME.md","):",[286,407,408,420],{},[289,409,410],{},[292,411,412,414,416,418],{},[295,413,297],{},[295,415,300],{},[295,417,303],{},[295,419,306],{},[308,421,422,436],{},[292,423,424,427,430,433],{},[313,425,426],{},"Start script without the cache (the flags of before)",[313,428,429],{},"1,118 to 1,144 ms",[313,431,432],{},"1,344 to 1,351 ms",[313,434,435],{},"about 150 MB",[292,437,438,441,444,447],{},[313,439,440],{},"Start script with the AOT cache and the flags",[313,442,443],{},"511 to 526 ms",[313,445,446],{},"584 to 631 ms",[313,448,449],{},"104 MB",[234,451,452,453,456],{},"Thus the shipped cache makes the start about 2.2x faster, not about 3x as in the spike (361 ms). The bench on the same machine (",[248,454,455],{},"bench\u002FPERFORMANCE.md",", the median of 3 runs) measured the native Counter at 25 ms and 40 MB of RSS, and the native Dashboard at 49 ms and 80 MB.",[238,458,459,462,474,488],{},[241,460,461],{},"native-image worked the first time. The restore was correct on q, Ctrl-C, SIGTERM (143), SIGINT (130) and SIGHUP (129). The metadata is 2 entries and one Unsafe flag. A binary is 21.5 to 33 MB.",[241,463,464,465,468,469,473],{},"Kyo's blocking monitor slept 86 to 102 ms at start; ",[248,466,467],{},"blockingMonitorMinIntervalNs"," removes it (done in P1, ",[470,471,472],"a",{"href":132},"ADR 0005",").",[241,475,476,479,480,483,484,487],{},[248,477,478],{},"Tty.isatty"," let ",[248,481,482],{},"MissingForeignRegistrationError"," escape (a ",[248,485,486],{},"LinkageError","); fixed in P1.",[241,489,490],{},"D16 as approved on 2026-10-17 made native-image per platform the release format, with Windows in CI. Oracle GraalVM or Community was open; the user chose Community (2026-10-19).",[229,492,494],{"id":493},"decision","Decision",[238,496,497,508,515,524,527,530,536],{},[241,498,499,500,503,504,507],{},"The JVM start scripts have the startup flags (",[248,501,502],{},"-XX:TieredStopAtLevel=1",", ",[248,505,506],{},"-XX:+UseSerialGC",") by default.",[241,509,510,511,514],{},"The distribution ships ",[248,512,513],{},"bin\u002Faot-train",", not a cache: a cache is valid only for the JDK that made it. After one training run, a start script uses the JDK 25 AOT cache of its app, when the same JDK made it; else it starts without the cache.",[241,516,517,519,520,523],{},[248,518,250],{}," carries the native-image reachability metadata for the crossterm JNI in ",[248,521,522],{},"META-INF\u002Fnative-image",".",[241,525,526],{},"A script builds a native image of an app. It is an option, not the default distribution.",[241,528,529],{},"CI has a smoke job on GraalVM Community Edition that builds one native image and runs it in a PTY.",[241,531,532,533,473],{},"The kitty keyboard protocol flags stay deferred (",[470,534,535],{"href":180},"ADR 0017",[241,537,538],{},"This narrows D16: native-image is no longer the release format per platform.",[229,540,542],{"id":541},"alternatives-considered","Alternatives considered",[238,544,545,552,562,568,574],{},[241,546,547,551],{},[548,549,550],"strong",{},"JVM only."," Rejected: 1,101 ms to the first frame is visible to users, and the AOT cache cuts it to 361 ms in the spike (511 to 526 ms with the shipped start scripts) with no code change.",[241,553,554,557,558,561],{},[548,555,556],{},"native-image as the default distribution"," (D16 as approved). Not chosen: it loses the plain ",[248,559,560],{},"java -jar"," path, and the builds are long and per platform.",[241,563,564,567],{},[548,565,566],{},"CRaC."," Not recorded in the notes.",[241,569,570,573],{},[548,571,572],{},"Oracle GraalVM in CI."," The spike used Oracle GraalVM 25.0.4. The notes left the Oracle license as the open question; the user chose Community (2026-10-19).",[241,575,576,579],{},[548,577,578],{},"AppCDS."," Rejected in spike S4: it writes an error line into the terminal.",[229,581,583],{"id":582},"consequences","Consequences",[238,585,586,592,595,598],{},[241,587,588,589,591],{},"Good: a JVM user gets about a 2.2x faster start with the same artifacts, after one run of ",[248,590,513],{}," (the spike measured about 3x).",[241,593,594],{},"Good: a user who needs a fast start (a CLI tool) can build a native binary: 101 ms in the spike, 25 ms for the Counter of the bench.",[241,596,597],{},"Bad: the native metadata is one more thing to keep in step with jatatui and Kyo upgrades.",[241,599,600,601,604,605,473],{},"Bad: the JNI copy still leaves a ",[248,602,603],{},"\u002Ftmp\u002Fjni-*"," folder per start on macOS and Windows (",[470,606,607],{"href":188},"ADR 0019",[229,609,611],{"id":610},"references","References",[238,613,614,617],{},[241,615,616],{},"Roadmap: 2.7, D16 (and its Spanish copy). Decision log: sixteenth round (D16 edition).",[241,618,619],{},"Handoff: \"INSTRUCTION (user, 2026-10-20)\" (the L5b plan).",{"title":621,"searchDepth":622,"depth":622,"links":623},"",2,[624,625,626,627,628,629,630],{"id":231,"depth":622,"text":232},{"id":273,"depth":622,"text":274},{"id":280,"depth":622,"text":281},{"id":493,"depth":622,"text":494},{"id":541,"depth":622,"text":542},{"id":582,"depth":622,"text":583},{"id":610,"depth":622,"text":611},"The JVM start scripts have the startup flags by default and use a JDK 25 AOT cache after one run of bin\u002Faot-train; tui4s-runner carries native-image metadata for the crossterm JNI, a script builds a native image, and CI smoke-tests it on GraalVM Community.","md",null,{},true,{"title":171,"description":631},"CCua-Ol-CNQ8IOhT3LcZHRusBXKoDPnpuHPjOTf1zTw",[639,641],{"title":167,"path":168,"stem":169,"description":640,"children":-1},"Each module has its own version and tag; the first release is 0.1.0-RC1 while Kyo is an RC; a human approves every tag; the empty zio and cats modules and the deprecated useSignal go before 0.1.",{"title":175,"path":176,"stem":177,"description":642,"children":-1},"The session steers and agents do the work in a small shape (implementer, one reviewer, fixer); a skeptic reviews only concurrency and terminal-restore code; the working notes are not part of the product.",1792528757117]