[{"data":1,"prerenderedAt":545},["ShallowReactive",2],{"navigation_docs":3,"-decisions-0008-artifact-split-runner-app-testkit-core":223,"-decisions-0008-artifact-split-runner-app-testkit-core-surround":540},[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":143,"body":225,"description":533,"extension":534,"links":535,"meta":536,"navigation":537,"path":144,"seo":538,"stem":145,"__hash__":539},"docs\u002F4.decisions\u002F0008-artifact-split-runner-app-testkit-core.md",{"type":226,"value":227,"toc":522},"minimark",[228,233,237,241,252,256,301,305,404,408,444,448,500,504],[229,230,232],"h2",{"id":231},"status","Status",[234,235,236],"p",{},"Accepted",[229,238,240],{"id":239},"date","Date",[234,242,243,244,251],{},"2026-10-19 (D25, issue ",[245,246,250],"a",{"href":247,"rel":248},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Ftui4s\u002Fissues\u002F17",[249],"nofollow","#17","; the user: \"do it before L3\"). Done at 132358f.",[229,253,255],{"id":254},"context","Context",[257,258,259,279,286,289,298],"ul",{},[260,261,262,266,267,270,271,274,275,278],"li",{},[263,264,265],"code",{},"tui4s-core"," held three layers under one name: the runner (renderer, embedder, component model), the app shell (",[263,268,269],{},"TuiApp",") and the test kit (",[263,272,273],{},"Headless",", ",[263,276,277],{},"Screen",").",[260,280,281,282,285],{},"A kyo-ui user who wants only \"run this UI in a terminal\" (a library, or an app with its own ",[263,283,284],{},"main",") had no artifact that says so.",[260,287,288],{},"The spec of 2026-10-01 put the test kit inside core (Q6: \"one artifact fewer to version and release\").",[260,290,291,292,274,295,278],{},"Each module already had its own version and tag (",[263,293,294],{},"\u003Cmodule>-v\u003Csemver>",[245,296,297],{"href":168},"ADR 0014",[260,299,300],{},"Nothing was published, so the split was free before 0.1 and a breaking change after it.",[229,302,304],{"id":303},"decision","Decision",[257,306,307,353,363,378,391,394],{},[260,308,309,310,313,314,274,317,274,320,274,323,274,326,274,329,274,331,334,335,338,339,334,341,338,344,346,347,349,350,352],{},"The repository publishes four artifacts for the terminal: ",[263,311,312],{},"tui4s-runner"," (Layout, Paint, Canvas, Runner, Mount, Dispatch, the terminal code, ",[263,315,316],{},"Hooks",[263,318,319],{},"Component",[263,321,322],{},"Tui.run",[263,324,325],{},"TuiOptions",[263,327,328],{},"Tui.size",[263,330,277],{},[263,332,333],{},"tui4s.spi","), ",[263,336,337],{},"tui4s-app"," (",[263,340,269],{},[263,342,343],{},"tui4s-testkit",[263,345,273],{},", aliases of the ",[263,348,277],{}," types) and the composite ",[263,351,265],{},".",[260,354,355,356,358,359,362],{},"The jar of ",[263,357,265],{}," holds only ",[263,360,361],{},"object tui4s.Core",". It depends on the three, and its test sources are the integration suites.",[260,364,365,366,274,369,274,372,274,374,377],{},"All artifacts use the same packages (",[263,367,368],{},"tui4s",[263,370,371],{},"tui4s.internal",[263,373,333],{},[263,375,376],{},"tui4s.testkit","), so imports do not change.",[260,379,380,382,383,386,387,390],{},[263,381,371],{}," is sealed: each top-level definition is ",[263,384,385],{},"private[tui4s]",". ",[263,388,389],{},"PublicApiTest"," checks the seal across the four jars, and that no public signature names an internal type.",[260,392,393],{},"The release order is runner, then app and testkit, then core. The composite watches the three directories for the version policy.",[260,395,396,397,400,401,352],{},"The names stay ",[263,398,399],{},"tui4s-*",", not ",[263,402,403],{},"kyo-ui-*",[229,405,407],{"id":406},"alternatives-considered","Alternatives considered",[257,409,410,420,426,438],{},[260,411,412,419],{},[413,414,415,416,418],"strong",{},"One artifact ",[263,417,265],{}," with the test kit inside"," (spec Q6, the default of 2026-10-01). Superseded: the runner had no artifact of its own.",[260,421,422,425],{},[413,423,424],{},"A separate repository for the runner."," D25 says \"in-repo, not a separate repository\". The reason is not recorded beyond that.",[260,427,428,434,435,437],{},[413,429,430,431,352],{},"Core depends on an external ",[263,432,433],{},"kyo-ui-terminal-runner"," Rejected for the name: ",[263,436,403],{}," is the namespace of upstream. Other reasons are not recorded.",[260,439,440,443],{},[413,441,442],{},"Split after 0.1."," Rejected: the split is free now and breaking later.",[229,445,447],{"id":446},"consequences","Consequences",[257,449,450,463,472,489,495],{},[260,451,452,453,455,456,458,459,462],{},"Good: a kyo-ui user takes ",[263,454,312],{}," alone, and ",[263,457,343],{}," in the ",[263,460,461],{},"Test"," scope.",[260,464,465,466,468,469,471],{},"Good: ",[263,467,269],{}," and ",[263,470,273],{}," evolve with their own versions.",[260,473,474,475,478,479,274,482,274,485,488],{},"Bad: four tags to release in order, and three more deployment tag patterns in the ",[263,476,477],{},"maven-central"," environment (",[263,480,481],{},"runner-v*",[263,483,484],{},"app-v*",[263,486,487],{},"testkit-v*","): a human step.",[260,490,491,492,494],{},"Bad: ",[263,493,385],{}," is public in the bytecode. MiMa sees the internal types, so an internal change can break binary compatibility across the jars.",[260,496,497,498,278],{},"Note: the planned zio and cats modules depended on the runner. They were removed on 2026-10-20 (",[245,499,297],{"href":168},[229,501,503],{"id":502},"references","References",[257,505,506,513,519],{},[260,507,508,509,512],{},"Decision log: D25. Issue ",[245,510,250],{"href":247,"rel":511},[249]," (closed). Spec section 9, Q6 (superseded).",[260,514,515,516,518],{},"Gate 1 answer 1 (the seal of ",[263,517,371],{},", 2026-10-13).",[260,520,521],{},"ARCHITECTURE.md: \"What tui4s is\" (the module table). CONTRIBUTING.md: \"Compatibility and versioning\", \"Releasing\". ",{"title":523,"searchDepth":524,"depth":524,"links":525},"",2,[526,527,528,529,530,531,532],{"id":231,"depth":524,"text":232},{"id":239,"depth":524,"text":240},{"id":254,"depth":524,"text":255},{"id":303,"depth":524,"text":304},{"id":406,"depth":524,"text":407},{"id":446,"depth":524,"text":447},{"id":502,"depth":524,"text":503},"tui4s-core becomes a composite of three artifacts in this repository: tui4s-runner, tui4s-app and tui4s-testkit, each with its own version and tag, in the same packages, with tui4s.internal sealed.","md",null,{},true,{"title":143,"description":533},"Gab8CRdVRle9cayPKgLPCo8oUhTRVGh5DN2A7EvH3Pg",[541,543],{"title":139,"path":140,"stem":141,"description":542,"children":-1},"sbt-tpolecat sets the scalac options; warnings stay warnings on a developer machine, and CI (SBT_TPOLECAT_CI) turns them into errors.",{"title":147,"path":148,"stem":149,"description":544,"children":-1},"Composables (tui4s-use) and widgets (tui4s-widgets) are published modules of this repository, plain kyo-ui code for the JVM and Scala.js, with no dependency on tui4s-core.",1792528756441]