From 7abecaacb9a0bbed61ffd47392b0e9e7a10a6aef Mon Sep 17 00:00:00 2001 From: bougyman's bot Date: Fri, 25 Sep 2026 13:30:33 -0400 Subject: [PATCH 1/4] chore: update stokowski --- .ai/prompts/global.md | 4 ++-- .ai/prompts/improvement.md | 2 +- .ai/prompts/merge.md | 5 ++++- .gitignore | 3 +++ vendor/stokowski | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.ai/prompts/global.md b/.ai/prompts/global.md index 7e3d5d0..3a0bae6 100644 --- a/.ai/prompts/global.md +++ b/.ai/prompts/global.md @@ -139,7 +139,7 @@ Before starting any implementation work: ## Linear Interaction -Fantasia issues belong to the `EXT` team and the `Fantasia` project. +Linear CLI issues belong to the `EXT` team and the `Linear CLI` project. There is normally no need to interact with Linear directly. Stokowski should pass the necessary context from the linear issue. @@ -152,7 +152,7 @@ For instance, to Post a new Linear comment (for milestone of your work, guidance for the next stage, and so on. Do not try to maintain or find a single running comment to update: - mise exec -- mix lc issue comment --body-file + mise exec -- mix lc issue --team EXT --project 'Linear CLI' comment --body-file - Write the comment's full content to a file first, then pass its path — never build a multi-line comment as an inline shell argument. diff --git a/.ai/prompts/improvement.md b/.ai/prompts/improvement.md index a42ff8b..3c21745 100644 --- a/.ai/prompts/improvement.md +++ b/.ai/prompts/improvement.md @@ -8,7 +8,7 @@ not approval to create every candidate: require a comment such as With no explicit manifest, fail closed: create zero issues, report that result, and complete. For each approved item, re-check for an equivalent Linear issue, then use only `mise exec -- mix lc issue create` with non-interactive options -and a body file to create it in EXT / Fantasia. Include the source issue, +and a body file to create it in EXT / Linear CLI. Include the source issue, candidate ID, evidence, scope, and acceptance criteria; link it to the source when supported. Do not modify the source issue, merge a PR, or change the repo. diff --git a/.ai/prompts/merge.md b/.ai/prompts/merge.md index 1c25367..4e8bcd4 100644 --- a/.ai/prompts/merge.md +++ b/.ai/prompts/merge.md @@ -27,7 +27,9 @@ the approved PR for **{{ issue.identifier }}**: {{ issue.title }} gh pr merge -sd ``` 5. Update the Linear workpad with the merge confirmation. -6. Move the Linear issue to `Done`. + +The workflow runner owns the transition to `Done`. Do not move the Linear +issue to a terminal state yourself. ## Rework run @@ -50,3 +52,4 @@ If this is a rework run (merge was attempted before but failed): - Make code changes beyond conflict resolution. - Open new PRs. - Skip CI checks. +- Move the source issue to a terminal state. diff --git a/.gitignore b/.gitignore index db0ca80..b4a5161 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ # see RepoTasks.MixProject) - app/'s own equivalents are in app/.gitignore. /_build/ /deps/ + +# Stokowski +.stokowski/ diff --git a/vendor/stokowski b/vendor/stokowski index 79fa1bb..35f4a55 160000 --- a/vendor/stokowski +++ b/vendor/stokowski @@ -1 +1 @@ -Subproject commit 79fa1bb032fdb4517a28a2f6dcf074ff4571829e +Subproject commit 35f4a5585082f75b90b6ce06a23aab066ece535a From 5159e98dd2ec9571d6c16ba5680e5f9e08f06dd9 Mon Sep 17 00:00:00 2001 From: bougyman's bot Date: Fri, 25 Sep 2026 13:32:29 -0400 Subject: [PATCH 2/4] chore: update ash dependency for vulnerability patch --- app/mix.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/mix.lock b/app/mix.lock index 5b63d7d..6e11425 100644 --- a/app/mix.lock +++ b/app/mix.lock @@ -1,5 +1,5 @@ %{ - "ash": {:hex, :ash, "3.33.9", "434d424f19f4d896382e51a9cc5d78bcfc197a926a6acbaa05b1a3ca89b4ad10", [:mix], [{:crux, ">= 0.1.2 and < 1.0.0-0", [hex: :crux, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.14", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 1.0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.6.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.3", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0ce2e1093e960917815824a71b4ffaa0d08fb6c4cecd314bc40ca49eced299a9"}, + "ash": {:hex, :ash, "3.33.11", "4118ddcab0b6c3c388fa7f69cc1fe5850f550bb7c26307b34f7a642f73ae4bf2", [:mix], [{:crux, ">= 0.1.2 and < 1.0.0-0", [hex: :crux, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.14", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 1.0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.6.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.3", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f36bdbab6ccc07029d09e4a8b75f9f4a1c0f14bc62db68cc4dd7eefa16c3b8b9"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "burrito": {:hex, :burrito, "1.6.0", "7af0a75f11680e8a6e9c01370c9af51cb9d0e15b3226eddf4f438dbc68570520", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:req, ">= 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.2.0 or ~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "e636a00b032c45a69ff755d9fc53fa5fdc9e1d21bdbd229075fe4a15b05355fe"}, "castore": {:hex, :castore, "1.0.21", "0a0e8330dc267a40a3b7ad86d39302764bb71758172904e6a59d5ad6443ce307", [:mix], [], "hexpm", "e42e22723e25dbd46876d056a03f685513d6e98f6b5e555dc551321decd76c5c"}, @@ -19,7 +19,7 @@ "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, "glob_ex": {:hex, :glob_ex, "0.1.12", "7b2d9369c20e2697efcfd185d13d6e84c94cd3bfd2730fbde613141c2e015c00", [:mix], [], "hexpm", "2e2fac83f113514434c7eaf267b4c38af2f91766f1cab2c5db7053b7fc1ee0bb"}, "hex_core": {:hex, :hex_core, "0.19.0", "b9b5d3cde2d5227b85c62c4ab9f9db586e6b38cf65a9bf5b683757cae9ced5eb", [:rebar3], [], "hexpm", "c2cc414f3893a7edbcab28207cadca13829407abe41f689c82f379fe22f026a5"}, - "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, + "hpax": {:hex, :hpax, "1.1.0", "782931867cc23217c68fb5f68fe1a11f5e7544c7fda82c8a7019a5df5a4a1cdf", [:mix], [], "hexpm", "0b8d0f05832f55571d65ac720f79bf8994138ffbb133209dc4685eae0ad456a8"}, "igniter": {:hex, :igniter, "0.8.4", "f79f1bbdc2fb7b9ca030a22d12a585b060cbf5b94b9d3f23b1148578a9e05d11", [:mix], [{:ex_ast, "~> 0.5", [hex: :ex_ast, repo: "hexpm", optional: false]}, {:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4.5", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "a9b1cbec996ccb100b4f7d8130129b2dd3f18eb4224ac9a0e907e428ca90dbd7"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, From 131d00d9cc57ff686291c27e36e3c15c884e42dc Mon Sep 17 00:00:00 2001 From: bougyman's bot Date: Fri, 25 Sep 2026 14:57:56 -0400 Subject: [PATCH 3/4] chore: adds toolchain update task --- lib/mix/tasks/toolchain.update.ex | 80 ++++++++++++++++++++++++ mise.toml | 5 +- mix.exs | 12 ++-- mix.lock | 13 ++++ test/mix/tasks/toolchain.update_test.exs | 55 ++++++++++++++++ 5 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 lib/mix/tasks/toolchain.update.ex create mode 100644 mix.lock create mode 100644 test/mix/tasks/toolchain.update_test.exs diff --git a/lib/mix/tasks/toolchain.update.ex b/lib/mix/tasks/toolchain.update.ex new file mode 100644 index 0000000..015a072 --- /dev/null +++ b/lib/mix/tasks/toolchain.update.ex @@ -0,0 +1,80 @@ +defmodule Mix.Tasks.Toolchain.Update do + @shortdoc "Updates the Claude Code and Codex pins in mise.toml" + + @moduledoc """ + #{@shortdoc}. + + mix toolchain.update + + Gets the latest npm versions for Claude Code and Codex. + Then updates their pins in `mise.toml`. + """ + + use Mix.Task + + @tools [ + {"claude", "@anthropic-ai/claude-code"}, + {"codex", "@openai/codex"} + ] + + @npm_registry "https://registry.npmjs.org" + + @impl Mix.Task + def run([]) do + update("mise.toml") + end + + def run(_argv), do: Mix.raise("Usage: mix toolchain.update") + + @doc false + def update(mise_path) do + start_req!() + versions = Enum.map(@tools, fn {tool, package} -> {tool, npm_version!(package)} end) + mise_toml = File.read!(mise_path) + + File.write!(mise_path, update_versions!(mise_toml, versions)) + + Mix.shell().info("Updated Claude Code and Codex pins in #{mise_path}") + end + + @doc false + def update_versions!(mise_toml, versions) do + Enum.reduce(versions, mise_toml, fn {tool, version}, contents -> + pattern = ~r/^#{Regex.escape(tool)}\s*=\s*"[^"]+"$/m + + if Regex.match?(pattern, contents) do + Regex.replace(pattern, contents, "#{tool} = \"#{version}\"") + else + Mix.raise("mise.toml does not pin #{tool}") + end + end) + end + + defp npm_version!(package) do + case Req.get(request_options(package)) do + {:ok, %Req.Response{status: 200, body: %{"latest" => version}}} when is_binary(version) -> + version + + {:ok, %Req.Response{status: 200}} -> + Mix.raise("npm registry response for #{package} does not include a latest version") + + {:ok, %Req.Response{status: status}} -> + Mix.raise("npm registry request for #{package} exited with status #{status}") + + {:error, exception} -> + Mix.raise("npm registry request for #{package} failed: #{Exception.message(exception)}") + end + end + + defp request_options(package) do + [url: "#{@npm_registry}/-/package/#{URI.encode(package, &URI.char_unreserved?/1)}/dist-tags"] + |> Keyword.merge(Application.get_env(:repo_tasks, :npm_req_options, [])) + end + + defp start_req! do + case Application.ensure_all_started(:req) do + {:ok, _started} -> :ok + {:error, reason} -> Mix.raise("Cannot start Req: #{inspect(reason)}") + end + end +end diff --git a/mise.toml b/mise.toml index 379f239..bcc0732 100644 --- a/mise.toml +++ b/mise.toml @@ -7,6 +7,9 @@ erlang = "29.0.3" elixir = "1.20.3" python = "3.14.7" +uv = "0.12.18" +codex = "0.157.0" +claude = "2.1.282" [env] #ANTHROPIC_BASE_URL = "http://127.0.0.1:13305" #ANTHROPIC_AUTH_TOKEN = "lemonade" @@ -24,7 +27,7 @@ run = "mix setup" [tasks.deps] description = "Fetch CLI dependencies" -run = "cd app && mix deps.get" +run = "mix toolchain.update && cd app && mix deps.get" depends = ["setup"] [tasks.linear_profile] diff --git a/mix.exs b/mix.exs index f2d8c12..8a74868 100644 --- a/mix.exs +++ b/mix.exs @@ -5,10 +5,9 @@ defmodule RepoTasks.MixProject do # deliberately its own Mix project, sibling to app/, not nested inside it. # app/ is the CLI itself; this is tooling that operates ON the repo as a # whole (this file's own directory, plus ci/, oci/, .github/workflows/ - - # things app/'s own mix.exs has no business knowing about). Kept - # dependency-free on purpose: every task here just orchestrates other - # already-existing tools (mix release inside app/, the ci/*.sh scripts) - # as child OS processes, never runs them in-process. + # things app/'s own mix.exs has no business knowing about). Its dependencies + # support repo-management tasks and do not become app dependencies. Tasks + # use child processes or direct service requests as needed. def project do [ app: :repo_tasks, @@ -26,6 +25,9 @@ defmodule RepoTasks.MixProject do end defp deps do - [] + [ + {:req, "~> 0.7"}, + {:plug, "~> 1.0", only: :test} + ] end end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..f1cc286 --- /dev/null +++ b/mix.lock @@ -0,0 +1,13 @@ +%{ + "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, + "hpax": {:hex, :hpax, "1.1.0", "782931867cc23217c68fb5f68fe1a11f5e7544c7fda82c8a7019a5df5a4a1cdf", [:mix], [], "hexpm", "0b8d0f05832f55571d65ac720f79bf8994138ffbb133209dc4685eae0ad456a8"}, + "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mint": {:hex, :mint, "1.10.1", "c53e70867cf74017716884d8d33e0742b08b32e9cdb0031cbc69a429dc5555e3", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "0ba2a904605ed8406393444fb8b3356dc58eb59ee6c7fb94ac3f015e1be129e8"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "plug": {:hex, :plug, "1.20.3", "56c480c633ec2ce10140e236e15233bf576e1d323887d7c96711bd02ab5160db", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "be266aee1b8536ef6409d58cf39a3121319f0ec47cfa1b24024485aa0e76ad76"}, + "plug_crypto": {:hex, :plug_crypto, "2.2.0", "144014737daaf485407f5ed77daeaad74d651b216a28c87543f8cc7043f8efc8", [:mix], [], "hexpm", "83a95744ab1c75876542b6fab135fcc176280e0f301a111c1f757fddcec95d2c"}, + "req": {:hex, :req, "0.7.4", "23e9ffec17de032a46a4b15ed65c09793893bf4a7c680f4bbf6227fce6bdf74d", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:finch, "~> 0.21", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "4b192d63253e8dcc6221ef992ea9ebef7d3555166e8423aa5b553e86bc3c69a2"}, + "telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"}, +} diff --git a/test/mix/tasks/toolchain.update_test.exs b/test/mix/tasks/toolchain.update_test.exs new file mode 100644 index 0000000..3a08fd2 --- /dev/null +++ b/test/mix/tasks/toolchain.update_test.exs @@ -0,0 +1,55 @@ +defmodule Mix.Tasks.Toolchain.UpdateTest do + use ExUnit.Case, async: false + + alias Mix.Tasks.Toolchain.Update + + setup do + previous_options = Application.get_env(:repo_tasks, :npm_req_options) + + Application.put_env(:repo_tasks, :npm_req_options, + plug: {Req.Test, __MODULE__}, + retry: false + ) + + on_exit(fn -> + if previous_options do + Application.put_env(:repo_tasks, :npm_req_options, previous_options) + else + Application.delete_env(:repo_tasks, :npm_req_options) + end + end) + end + + test "gets each package version from the npm registry" do + versions = %{ + "/-/package/%40anthropic-ai%2Fclaude-code/dist-tags" => "claude-test-version", + "/-/package/%40openai%2Fcodex/dist-tags" => "codex-test-version" + } + + Req.Test.stub(__MODULE__, fn conn -> + version = Map.fetch!(versions, conn.request_path) + Req.Test.json(conn, %{"latest" => version}) + end) + + mise_path = + Path.join(System.tmp_dir!(), "toolchain-update-#{System.unique_integer([:positive])}.toml") + + File.write!(mise_path, "[tools]\ncodex = \"old\"\nclaude = \"old\"\n") + + on_exit(fn -> File.rm(mise_path) end) + + assert :ok = Update.update(mise_path) + + codex_version = Map.fetch!(versions, "/-/package/%40openai%2Fcodex/dist-tags") + claude_version = Map.fetch!(versions, "/-/package/%40anthropic-ai%2Fclaude-code/dist-tags") + + assert File.read!(mise_path) == + "[tools]\ncodex = \"#{codex_version}\"\nclaude = \"#{claude_version}\"\n" + end + + test "requires every tool to have a pin" do + assert_raise Mix.Error, "mise.toml does not pin codex", fn -> + Update.update_versions!(~s([tools]\nclaude = "placeholder"\n), [{"codex", "placeholder"}]) + end + end +end From 58efa50b62dd8b77661cfa51fead36938583d5ef Mon Sep 17 00:00:00 2001 From: "Tj (bougyman) Vanderpoel" Date: Fri, 25 Sep 2026 15:09:28 -0400 Subject: [PATCH 4/4] chore: fix ci task to deps.get before running --- .github/workflows/ci.yaml | 2 +- lib/mix/tasks/ci.ex | 1 + mise.toml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5ce0de8..44765ef 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -106,7 +106,7 @@ jobs: PULL_REQUEST_TITLE: >- ${{ inputs.pull_request_title != '' && inputs.pull_request_title || github.event.pull_request.title }} - run: mix ci + run: mix deps.get && mix ci working-directory: . burrito_changes: diff --git a/lib/mix/tasks/ci.ex b/lib/mix/tasks/ci.ex index 4129d7e..0e2f1c8 100644 --- a/lib/mix/tasks/ci.ex +++ b/lib/mix/tasks/ci.ex @@ -55,6 +55,7 @@ defmodule Mix.Tasks.Ci do @doc false def run([], shell) do Mix.Tasks.Toolchain.Check.run([]) + shell.("mix", ["deps.get"], []) shell.("mix", ["deps.get"], cd: "app") shell.("./ci/validate_pull_request_title.sh", [], []) shell.("./ci/validate_commit_range.sh", [], []) diff --git a/mise.toml b/mise.toml index bcc0732..03f9765 100644 --- a/mise.toml +++ b/mise.toml @@ -23,7 +23,7 @@ claude = "2.1.282" [tasks.setup] description = "Setup the project" -run = "mix setup" +run = "mix deps.get && mix setup" [tasks.deps] description = "Fetch CLI dependencies"