Covers Elixir 1.19–1.20, Phoenix 1.8, LiveView 1.0–1.1 updates including type system inference, map key typing, colocated hooks, portals, scopes, magic link auth, new APIs, breaking changes.
npx claudepluginhub nevaberry/nevaberry-plugins --plugin elixir-knowledge-patchThis skill uses the workspace's default tool permissions.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Baseline: Elixir 1.16, OTP 26, Phoenix 1.7, LiveView 0.x (pre-1.0), Ecto 3.11, GenServer, Supervisor, mix, ExUnit, dbg macro.
This patch covers Elixir 1.19–1.20-rc, Phoenix 1.8, and LiveView 1.0–1.1 (2024-12 to 2026-01).
| Topic | File | Key Features |
|---|---|---|
| Type System & Inference | references/elixir-type-system.md | Full expression inference, map key typing, struct update deprecation |
| APIs, Mix & Compilation | references/elixir-apis-and-mix.md | New stdlib APIs, mix commands, lazy module loading, deprecations |
| LiveView JS & Hooks | references/liveview-js-and-hooks.md | JS command targeting, colocated hooks, ViewHook class, programmable JS |
| LiveView Templates & Streams | references/liveview-templates-and-streams.md | Keyed comprehensions, portals, ignore_attributes, LazyHTML migration |
| Phoenix 1.8 | references/phoenix-1-8.md | Scopes, magic link auth, simplified layouts, security headers |
The compiler infers types from all expressions (not just patterns). Backward inference narrows argument types:
def sum_to_string(a, b), do: Integer.to_string(a + b)
# Infers a, b must be integer() (not float) because Integer.to_string requires integer
Cross-clause inference — later clauses know what previous clauses matched:
case System.get_env("VAR") do
nil -> :not_found
value -> String.upcase(value) # value is binary(), not nil
end
Map.put(map, :key, 123) #=> %{..., key: integer()}
Map.delete(map, :key) #=> %{..., key: not_set()}
Map.replace(map, :key, 123) #=> %{..., key: if_set(integer())}
# Deprecated — requires prior pattern match:
def update(uri), do: %URI{uri | path: "/foo"}
# Correct:
def update(%URI{} = uri), do: %{uri | path: "/foo"}
| API | Version | Purpose |
|---|---|---|
Access.values/0 | 1.19 | Traverse all values in maps/keyword lists |
String.count/2 | 1.19 | Count pattern occurrences in string |
min/2, max/2 in guards | 1.19 | Guard-compatible min/max |
Integer.ceil_div/2 | 1.20 | Ceiling division |
Integer.popcount/1 | 1.20 | Count set bits |
IO.iodata_empty?/1 | 1.20 | Check if iodata is empty |
List.first!/1, List.last!/1 | 1.20 | Raise on empty list |
# map.foo() with parens now RAISES (not warns) — use map.foo
# mod.foo without parens now RAISES — use mod.foo()
# File.stream! arg order swapped:
File.stream!(path, lines_or_bytes, modes) # NEW order
# Bitstring size requires pin:
<<data::size(^size)>> # was <<data::size(size)>>
<div id="sortable" phx-hook=".Sortable">...</div>
<script :type={Phoenix.LiveView.ColocatedHook} name=".Sortable">
export default {
mounted() { /* JS code here */ }
}
</script>
Setup: add :phoenix_live_view to compilers: in mix.exs, import in app.js:
import {hooks as colocatedHooks} from "phoenix-colocated/my_app"
const liveSocket = new LiveSocket("/live", Socket, {hooks: {...colocatedHooks}})
<.portal id="my-tooltip" target="body">
<div class="tooltip">Content here</div>
</.portal>
# Generated context functions take scope as first arg
def list_posts(%Scope{} = scope) do
Repo.all(from p in Post, where: p.user_id == ^scope.user.id)
end
# LiveViews use socket.assigns.current_scope
def mount(_params, _session, socket) do
Blog.subscribe_posts(socket.assigns.current_scope)
{:ok, stream(socket, :posts, Blog.list_posts(socket.assigns.current_scope))}
end
<Layouts.app flash={@flash}>
<:breadcrumb><.link navigate={~p"/posts"}>Posts</.link></:breadcrumb>
<p>My content</p>
</Layouts.app>
Single root.html.heex layout. Multiple app layouts: <Layouts.admin>, <Layouts.cart>, etc.
<button phx-click={JS.add_class("highlight", to: {:closest, "tr"})}>Select</button>
<div phx-click={JS.show(to: {:inner, ".details"})}>Expand</div>
<li :for={item <- @items} :key={item.id}>{item.name}</li>