diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54dc4e5..ad54505 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,13 +38,14 @@ jobs: ${{ runner.os }}-mix- - name: Install Mix dependencies run: nix develop -c mix deps.get - - name: Compile project - run: nix develop -c mix compile --warnings-as-errors + # Type-check gate: replaces Dialyzer. Elixir 1.20's built-in + # set-theoretic type checker runs during `mix compile`; its findings + # surface as compile warnings, and --warnings-as-errors fails CI on them. + - name: Compile project (type-check gate) + run: nix develop -c mix compile --warnings-as-errors --force - name: Run tests run: nix develop -c mix test - name: Check code formatting run: nix develop -c mix format --check-formatted - name: Run Credo run: nix develop -c mix credo - - name: Run Dialyzer - run: nix develop -c mix dialyzer diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index ba78813..b33ef6f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -52,16 +52,15 @@ Configuration is in `.credo.exs`. Current settings: - Strict consistency checks enabled - TODOs allowed (warning only) -### Dialyzer +### Type checking -Static type analysis using Dialyzer: +Static type analysis is handled by Elixir's built-in set-theoretic +(gradual) type checker, which runs automatically during `mix compile`. +Treat its warnings as errors to gate on them: ```bash -# Run Dialyzer (first run will be slow while building PLT) -mix dialyzer - -# Clean PLT cache -rm -rf priv/plts/ +# Type-check the project (forces a full recompile so all warnings surface) +mix compile --warnings-as-errors --force ``` PLT files are cached in `priv/plts/` and excluded from git. @@ -140,10 +139,9 @@ Tests run on multiple Elixir and OTP versions: - Runs Credo in strict mode - Uses dependency and build caching -2. **Dialyzer Job** - - Runs on latest Elixir/OTP only - - Caches PLT files for faster runs - - Performs static type analysis +2. **Type-check gate** + - Part of the compile step (`mix compile --warnings-as-errors --force`) + - Surfaces the built-in set-theoretic type checker's findings as errors ## Code Style Guidelines @@ -240,14 +238,11 @@ mix deps.get mix test ``` -### Dialyzer Issues +### Type-check Issues ```bash -# Clean PLT cache -rm -rf priv/plts/ - -# Rebuild PLT -mix dialyzer +# Force a full recompile so all type-checker warnings surface +mix compile --warnings-as-errors --force ``` ### Formatting Conflicts @@ -274,5 +269,4 @@ mix format --check-formatted - **Mix tasks**: `mix help` - **Credo**: `mix credo --help` -- **Dialyzer**: `mix dialyzer --help` - **Tests**: `mix help test` diff --git a/README.md b/README.md index f15cd85..1548a84 100644 --- a/README.md +++ b/README.md @@ -380,8 +380,9 @@ mix credo --strict # Generate documentation mix docs -# Run Dialyzer (type checking) -mix dialyzer +# Type checking (Elixir's built-in set-theoretic type checker runs at compile time; +# treat its warnings as errors to gate on them) +mix compile --warnings-as-errors --force ``` ### Pre-commit Hook diff --git a/lib/agent_obs/req_llm.ex b/lib/agent_obs/req_llm.ex index 27913ce..5bcd67e 100644 --- a/lib/agent_obs/req_llm.ex +++ b/lib/agent_obs/req_llm.ex @@ -713,7 +713,6 @@ defmodule AgentObs.ReqLLM do - `[:agent_obs, :tool, :exception]` - If tool execution fails """ @spec trace_tool_execution(struct(), map(), keyword()) :: {:ok, term()} | {:error, term()} - @dialyzer {:nowarn_function, trace_tool_execution: 3} def trace_tool_execution(tool, tool_call, opts \\ []) do _ = opts @@ -726,7 +725,7 @@ defmodule AgentObs.ReqLLM do {:error, error} # ReqLLM.Tool.execute can return raw values when the callback doesn't wrap in {:ok, _} - # Dialyzer thinks this is unreachable, but it happens in practice (see integration tests) + # (the typespec marks this as unreachable, but it happens in practice — see integration tests) result -> {:ok, result, %{result: result}} end diff --git a/mix.exs b/mix.exs index 1276154..41ed22d 100644 --- a/mix.exs +++ b/mix.exs @@ -14,9 +14,6 @@ defmodule AgentObs.MixProject do description: description(), package: package(), docs: docs(), - dialyzer: [ - plt_file: {:no_warn, "priv/plts/dialyzer.plt"} - ], aliases: aliases() ] end @@ -43,7 +40,6 @@ defmodule AgentObs.MixProject do # Development and testing dependencies {:ex_doc, "~> 0.40", only: :dev, runtime: false}, - {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false} ] end diff --git a/mix.lock b/mix.lock index e2b19c1..70eac9f 100644 --- a/mix.lock +++ b/mix.lock @@ -10,10 +10,8 @@ "crontab": {:hex, :crontab, "1.2.0", "503611820257939d5d0fd272eb2b454f48a470435a809479ddc2c40bb515495c", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "ebd7ef4d831e1b20fa4700f0de0284a04cac4347e813337978e25b4cc5cc2207"}, "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, - "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "dotenvy": {:hex, :dotenvy, "1.1.1", "00e318f3c51de9fafc4b48598447e386f19204dc18ca69886905bb8f8b08b667", [:mix], [], "hexpm", "c8269471b5701e9e56dc86509c1199ded2b33dce088c3471afcfef7839766d8e"}, "earmark_parser": {:hex, :earmark_parser, "1.4.45", "cba8369ab2a1342e419bc2760eec731b17be828941dcf494045d44766227e1d5", [:mix], [], "hexpm", "d3ec045bf122965db20c0bdb420e19ee1415843135327124918473feb4b328e8"}, - "erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"}, "ex_aws_auth": {:hex, :ex_aws_auth, "1.4.1", "2f4faf59dd62933c52faf5c026f22b6b9b7d143cf30ee462204a646e3304761f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: true]}], "hexpm", "42ea14c586689652f51906c797378a07822b65b32febd22067b4e93ff9dc7d4f"}, "ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"}, "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"},