@@ -44,7 +44,14 @@ defmodule Emily.MixProject do
4444 end
4545
4646 def cli do
47- [ preferred_envs: [ docs: :docs , "hex.publish": :docs , "emily.publish": :docs , precommit: :test ] ]
47+ [
48+ preferred_envs: [
49+ docs: :docs ,
50+ "hex.publish": :docs ,
51+ "emily.publish": :docs ,
52+ precommit: :test
53+ ]
54+ ]
4855 end
4956
5057 def application do
@@ -138,6 +145,7 @@ defmodule Emily.MixProject do
138145 "deps.unlock --unused" ,
139146 "format" ,
140147 "credo --strict" ,
148+ & docs_check / 1 ,
141149 "test"
142150 ] ,
143151 "compile.emily_mlx": & build_mlx / 1 ,
@@ -159,6 +167,31 @@ defmodule Emily.MixProject do
159167 ]
160168 end
161169
170+ # Build the *published* doc surface (the `:docs` env that `mix hex.publish`
171+ # uses) and fail on any ExDoc warning — autolinks to hidden/undefined
172+ # symbols, broken refs, etc. Run as a subprocess because `mix precommit`
173+ # itself runs in `:test`, where `test/support/` modules (and their
174+ # `only: :test` deps like Axon) compile in and would warn for code that
175+ # never ships. The NIF in `priv/` is shared across envs, so this reuses it
176+ # — no native rebuild, just an Elixir recompile + doc generation. CI runs
177+ # `mix precommit`, so this gates merges too.
178+ defp docs_check ( _args ) do
179+ { output , status } =
180+ System . cmd ( "mix" , [ "docs" , "--warnings-as-errors" ] ,
181+ env: [ { "MIX_ENV" , "docs" } ] ,
182+ stderr_to_stdout: true
183+ )
184+
185+ IO . write ( output )
186+
187+ if status != 0 do
188+ Mix . raise (
189+ "mix docs reported warnings (see above). Fix the reference, or if it " <>
190+ "points at a hidden/undefined symbol, add it to :skip_code_autolink_to in docs/0."
191+ )
192+ end
193+ end
194+
162195 defp clean_mlx ( _args ) do
163196 case Path . wildcard ( Path . join ( cache_dir ( ) , "mlx-*" ) ) do
164197 [ ] ->
@@ -176,6 +209,23 @@ defmodule Emily.MixProject do
176209 [
177210 main: "readme" ,
178211 source_url_pattern: "#{ @ source_url } /blob/#{ @ version } /%{path}#L%{line}" ,
212+ # Symbols ExDoc can't link, so it warns on every reference to them.
213+ # Listed explicitly on purpose: the `mix precommit` docs gate fails
214+ # with the exact unlinkable symbol when a new one appears, making each
215+ # addition here a deliberate one-liner rather than silent auto-skipping.
216+ # A module entry (`Emily.Native`) does NOT cover its members, so each
217+ # referenced function is listed too.
218+ skip_code_autolink_to: [
219+ # `Emily.Native` and its NIF shims are `@moduledoc false`.
220+ "Emily.Native" ,
221+ "Emily.Native.from_binary/3" ,
222+ "Emily.Native.conv_general/8" ,
223+ "Emily.Native.worker_queue_depth/1" ,
224+ # Hidden Nx callback + private/external Nx internals.
225+ "Emily.Backend.block/4" ,
226+ "Nx.Backend.block/4" ,
227+ "Nx.Defn.Expr.optional/3"
228+ ] ,
179229 extras: [
180230 "README.md" ,
181231 "ARCHITECTURE.md" ,
0 commit comments