[{"data":1,"prerenderedAt":721},["ShallowReactive",2],{"navigation_docs":3,"-decisions-0020-release-from-the-actions-form":223,"-decisions-0020-release-from-the-actions-form-surround":716},[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":191,"body":225,"description":709,"extension":710,"links":711,"meta":712,"navigation":713,"path":192,"seo":714,"stem":193,"__hash__":715},"docs\u002F4.decisions\u002F0020-release-from-the-actions-form.md",{"type":226,"value":227,"toc":698},"minimark",[228,233,242,246,249,253,359,363,544,548,609,613,669,673],[229,230,232],"h2",{"id":231},"status","Status",[234,235,236,237,241],"p",{},"Accepted. It changes the trigger of ",[238,239,240],"a",{"href":168},"ADR 0014",": the versions, the tags and the order of 0014 stay.",[229,243,245],{"id":244},"date","Date",[234,247,248],{},"2026-10-20.",[229,250,252],{"id":251},"context","Context",[254,255,256,267,278,281,292,344],"ul",{},[257,258,259,261,262,266],"li",{},[238,260,240],{"href":168}," released one module per tag push. ",[263,264,265],"code",{},"scripts\u002Frelease.sh"," made the checks on the computer of the maintainer, asked for a confirmation, and pushed the tag. The workflow then tested and published that module.",[257,268,269,270,273,274,277],{},"That design needs a computer with ",[263,271,272],{},"gh",", sbt and JDK 25, a clean checkout of ",[263,275,276],{},"main",", and six runs of the script for a release of all the modules. GitHub ignores a push of more than three tags, so the tags go one at a time, in the order of the dependencies.",[257,279,280],{},"The gate was a prompt of the script. The tag existed before the release checks of the workflow, and the ruleset of the release tags blocks a move or a deletion. A tag that failed in the workflow stayed.",[257,282,283,284,287,288,291],{},"The release notes came only from the pull requests (",[263,285,286],{},"--generate-notes","). The first release has no pull-request history, and ",[263,289,290],{},"CHANGELOG.md"," already holds the text.",[257,293,294,297,298,301,302,305,306,309,310,313,314,317,318,321,322,325,326,313,329,332,333,336,337,340,341,343],{},[263,295,296],{},"project\u002FModuleVersioning.scala"," takes the version of a module from git (sbt-dynver with the prefix ",[263,299,300],{},"\u003Cmodule>-v","). The version is ",[263,303,304],{},"x.y.z"," only when a tag ",[263,307,308],{},"\u003Cmodule>-v\u003Cx.y.z>"," is on HEAD and the tree is clean, or when the last tag of the module is older but no path of the module changed since then. A path of a module is its directory, ",[263,311,312],{},"build.sbt"," and ",[263,315,316],{},"project"," (the composite ",[263,319,320],{},"core"," also watches ",[263,323,324],{},"runner",", ",[263,327,328],{},"app",[263,330,331],{},"testkit","). In all other cases the version is a snapshot. ",[263,334,335],{},"assertReleasable"," fails when the version of the module or of an inter-project dependency is a snapshot, or when it is not ",[263,338,339],{},"RELEASE_VERSION",". Thus the check needs the tag on HEAD: ",[263,342,339],{}," alone only compares.",[257,345,346,347,350,351,354,355,358],{},"sbt-ci-release 1.12.1 publishes a release only when it sees a tag (",[263,348,349],{},"CI_COMMIT_TAG",", or a ",[263,352,353],{},"GITHUB_REF"," under ",[263,356,357],{},"refs\u002Ftags","). With no tag and a snapshot root version, it publishes snapshots of all the projects.",[229,360,362],{"id":361},"decision","Decision",[254,364,365,391,443,487,490,517,541],{},[257,366,367,368,371,372,375,376,379,380,383,384,313,387,390],{},"The only trigger of ",[263,369,370],{},".github\u002Fworkflows\u002Frelease.yml"," is ",[263,373,374],{},"workflow_dispatch",", with the inputs ",[263,377,378],{},"module"," (one module, or ",[263,381,382],{},"all","), ",[263,385,386],{},"version",[263,388,389],{},"dry_run",". A tag that a person pushes starts no release.",[257,392,393,394,397,398,400,401,404,405,407,408,411,412,415,416,418,419,422,423,426,427,313,429,432,433,325,435,325,437,313,439,442],{},"The job ",[263,395,396],{},"prepare"," has no environment and no secret. It checks, before a tag exists: the branch is ",[263,399,276],{},"; the version form; for each module, the version is greater than its last tag (the Maven order of ",[263,402,403],{},"scripts\u002Frelease-lib.sh",") and the new tag does not exist; ",[263,406,290],{}," has the notes of each module; a check run ",[263,409,410],{},"Format, compile, test, compatibility, packaging"," passed on the commit (any successful run counts, so a cancelled duplicate run does not block). Then it tags the commit in its own checkout only, in the order of the dependencies, and runs ",[263,413,414],{},"stageModule \u003Cm>"," (",[263,417,335],{}," and an unsigned publish into ",[263,420,421],{},"target\u002Fsona-staging",") and ",[263,424,425],{},"\u003Cm>\u002FversionCheck"," for each module. The local tags let the dependency checks of ",[263,428,320],{},[263,430,431],{},"widgets"," see the new versions of ",[263,434,324],{},[263,436,328],{},[263,438,331],{},[263,440,441],{},"use",". The job cannot push. A dry run stops after this job, with a summary.",[257,444,393,445,448,449,452,453,455,456,459,460,463,464,467,468,325,470,325,473,476,477,467,480,483,484,486],{},[263,446,447],{},"publish"," uses the environment ",[263,450,451],{},"maven-central",": its deployment policy allows ",[263,454,276],{},", and its required reviewer (the maintainer) approves the run. One approval covers all the modules. One step loops over the modules in the order of the dependencies. For each module, it pushes the tag with ",[263,457,458],{},"GITHUB_TOKEN"," (such a push starts no workflow), runs ",[263,461,462],{},"sbt ci-release"," as before (",[263,465,466],{},"CI_RELEASE",": ",[263,469,335],{},[263,471,472],{},"versionCheck",[263,474,475],{},"publishSigned","; ",[263,478,479],{},"CI_SONATYPE_RELEASE",[263,481,482],{},"project \u003Cm>; sonaRelease",") with ",[263,485,349],{}," set to the tag, and creates the GitHub release.",[257,488,489],{},"A failure stops the loop. \"Re-run failed jobs\" continues: a tag on the same commit stays, a module with a GitHub release is skipped, and a version that is on Maven Central is not published again. A tag on another commit stops the job.",[257,491,492,494,495,498,499,502,503,506,507,510,511,513,514,516],{},[263,493,290],{}," is the one source of the release notes. The release pull request renames ",[263,496,497],{},"## [Unreleased]"," to ",[263,500,501],{},"## [\u003Cversion>] - \u003CYYYY-MM-DD>",", with a subsection ",[263,504,505],{},"### tui4s-\u003Cmodule>"," for each module. ",[263,508,509],{},"scripts\u002Fchangelog-section.sh"," takes the text of a subsection; ",[263,512,396],{}," fails when it is missing, and ",[263,515,447],{}," puts it before the notes that GitHub makes from the pull requests.",[257,518,519,522,523,526,527,530,531,533,534,313,537,540],{},[263,520,521],{},"scripts\u002Frelease.sh \u003Cmodule|all> \u003Cversion> [--dry-run]"," only fills the form (",[263,524,525],{},"gh workflow run",") and shows the run. It has no ",[263,528,529],{},"--yes",". The rules of the versions and of the modules are in ",[263,532,403],{},", which the script and the workflow both source; ",[263,535,536],{},"scripts\u002Fversion-order-test.sh",[263,538,539],{},"scripts\u002Fchangelog-section-test.sh"," test them in CI.",[257,542,543],{},"An agent can start a dry run at any time. A real release needs a human: the gate is the required reviewer of the environment, not a prompt of a script.",[229,545,547],{"id":546},"alternatives-considered","Alternatives considered",[254,549,550,560,566,572,578,587,593],{},[257,551,552,556,557,559],{},[553,554,555],"strong",{},"The script with a tag push (ADR 0014)."," Replaced: it needs a computer with ",[263,558,272],{}," and a clean checkout, and six runs for all the modules. Its gate was a prompt, and the tag existed before the checks of the workflow.",[257,561,562,565],{},[553,563,564],{},"GitHub Packages as the registry."," Rejected: its Maven registry needs a token for each read, also for a public package. Only Maven Central is resolved by default by sbt, coursier, Scala CLI and Mill, and Scaladex indexes only Maven Central.",[257,567,568,571],{},[553,569,570],{},"release-please with conventional commits."," Rejected for one maintainer at RC1: a convention on every commit, a bot pull request for each module, and the order of the dependencies needs more work.",[257,573,574,577],{},[553,575,576],{},"The GitHub Release UI as the trigger."," Rejected: the tag exists before the checks, and the ruleset of the release tags makes it permanent.",[257,579,580,583,584,586],{},[553,581,582],{},"Release notes from the labels of the pull requests."," Rejected: RC1 has no pull-request history, and ",[263,585,290],{}," already holds the text. One source is sufficient.",[257,588,589,592],{},[553,590,591],{},"Release assets (the zip of the examples)."," Deferred: the examples are not a module.",[257,594,595,604,605,608],{},[553,596,597,600,601,603],{},[263,598,599],{},"publishLocal"," as the packaging check of ",[263,602,396],{},"."," Not used: ",[263,606,607],{},"stageModule"," publishes into the directory of the real bundle with the same settings, so it is the nearer check.",[229,610,612],{"id":611},"consequences","Consequences",[254,614,615,621,624,630,633,636,643,653,660],{},[257,616,617,618,620],{},"Good: a release needs only a browser (or ",[263,619,272],{},"). All the checks run before a tag exists, so a failed check leaves no tag.",[257,622,623],{},"Good: one run and one approval release all the modules in the order of the dependencies.",[257,625,626,627,629],{},"Good: the release notes come from ",[263,628,290],{},", and a missing section stops the run before the tag.",[257,631,632],{},"Good: the rules of the versions are in one sourced file with a table test; the workflow does not copy them.",[257,634,635],{},"Bad: a failed publish leaves a tag with no GitHub release. Re-run the failed job. A new run stops on the tags that exist.",[257,637,638,639,642],{},"Bad: Central can show a new version on ",[263,640,641],{},"repo1.maven.org"," some minutes after it accepts the upload. A re-run in that time publishes again, and Central refuses it. Wait, then re-run.",[257,644,645,646,648,649,652],{},"Bad: the job ",[263,647,447],{}," builds each module again after ",[263,650,651],{},"clean"," (one sbt run for each module), so a release of all the modules takes longer than one sbt run.",[257,654,655,656,659],{},"Bad: a re-run uses the workflow and the scripts of the commit of the run. A fix of ",[263,657,658],{},"release.yml"," or of a script cannot help a re-run: the next version takes it.\n",[257,661,662,663,665,666,668],{},"Bad: the environment ",[263,664,451],{}," needs a one-time manual setup (the branch ",[263,667,276],{},", the reviewer, the four secrets). A ruleset file cannot hold it.",[229,670,672],{"id":671},"references","References",[254,674,675,687,694],{},[257,676,677,325,679,325,681,325,683,325,685,603],{},[263,678,370],{},[263,680,265],{},[263,682,403],{},[263,684,509],{},[263,686,296],{},[257,688,689,690,693],{},"CONTRIBUTING.md: \"Releasing\", \"Branch and tag rules\". ",[263,691,692],{},".github\u002Frulesets\u002FREADME.md",": \"The environment of the release\". ",[257,695,696,603],{},[238,697,240],{"href":168},{"title":699,"searchDepth":700,"depth":700,"links":701},"",2,[702,703,704,705,706,707,708],{"id":231,"depth":700,"text":232},{"id":244,"depth":700,"text":245},{"id":251,"depth":700,"text":252},{"id":361,"depth":700,"text":362},{"id":546,"depth":700,"text":547},{"id":611,"depth":700,"text":612},{"id":671,"depth":700,"text":672},"A release starts from the form of the workflow Release on main; every check runs before a tag exists; one approval of the environment publishes all the modules of the run in the order of the dependencies; CHANGELOG.md is the one source of the release notes.","md",null,{},true,{"title":191,"description":709},"4KCXC8QGYFbY-JSBoZtbst39OYAZi5KMnPENlF730Eo",[717,719],{"title":187,"path":188,"stem":189,"description":718,"children":-1},"The session writes upstream bug reports and feature requests as drafts; only the user posts them; tui4s works around each bug in its own code until upstream fixes it.",{"title":195,"path":196,"stem":197,"description":720,"children":-1},"CONTRIBUTING.md is the rulebook for humans and for AI agents; ARCHITECTURE.md tells how the runner works; AGENTS.md is short, holds the rules for agents only, and imports the two other files.",1792528757593]