Przejdź do głównej zawartości

Receptury na Elixira i Phoenix dla agentów AI to konfiguracja projektu i zestaw promptów, dzięki którym Claude Code, Codex i Cursor dostarczają godne zaufania zmiany w Phoenix 1.8: wygenerowany AGENTS.md i alias mix precommit jako definicja ukończenia, Tidewave jako dostęp do działającej aplikacji, testy pisane przed kodem oraz bramki CI, które padają na ostrzeżeniach kompilatora, naruszeniach typów i znaleziskach bezpieczeństwa.

Ta strona jest dla programistów Elixira, za których większość funkcji pisze już agent, oraz dla tech leadów, którzy chcą, żeby te same bramki obowiązywały cały zespół (zobacz job CI i tabelę akceptacji). Sytuacja, którą rozwiązuje: pull request od agenta jest zielony, tyle że LiveView trzyma wszystkie zamówienia w assigns socketu, test, który „dowodzi” działania GenServera, używa Process.sleep(100), wywołanie płatności siedzi wewnątrz Repo.transaction, a padającą asercję „naprawiono”, zmieniając oczekiwaną wartość. Phoenix daje ci już plik z regułami dla agenta, alias precommit i generatory. Poniższe receptury sprawiają, że agent zderza się z nimi w każdej turze, więc przeglądasz dowody, a nie diffy.

  • Projekt Phoenix, w którym AGENTS.md, alias mix precommit i reguły z zależności trzymają agenta przy aktualnych idiomach Phoenix, podpięte do wszystkich trzech narzędzi.
  • Skonfigurowany Tidewave MCP, dzięki któremu agent wykonuje kod w działającej aplikacji, czyta jej logi i odpytuje bazę deweloperską, zamiast zgadywać.
  • Hook Stop w Claude Code, który nie pozwala ogłosić „gotowe”, dopóki mix precommit nie przejdzie, oraz job CI, który jest mechaniczną bramką dla Codeksa i Cursora.
  • Osiem promptów do skopiowania: szkic AGENTS.md, kontekst i API JSON w trybie test-first (najpierw testy, potem kod), LiveView ze strumieniami, limiter zapytań na ETS, transakcja zamówienia w Ecto.Multi, worker Obana i release w Dockerze.
  • Tabelę akceptacji, która mówi, które zmiany agenta przegląda człowiek, a które udowadniają bramki.

Jakie wersje Elixira, Phoenix i pakietów Hex zakładają te receptury?

Dział zatytułowany „Jakie wersje Elixira, Phoenix i pakietów Hex zakładają te receptury?”

Agenci mieszają API od Phoenix 1.5 do 1.8 i od LiveView 0.x do 1.x, bo tak wyglądają ich dane treningowe. Przypnij wersje (.tool-versions lub mise.toml oraz mix.lock) i wymień je w AGENTS.md. Te były aktualne 2026-09-26:

KomponentWersjaŹródło (sprawdzone 2026-09-26)
Elixir / Erlang/OTP1.20.4 / 29.1.1tagi obrazu hexpm/elixir w Docker Hub
phoenix / phx_new1.8.15hex.pm
phoenix_live_view1.2.12hex.pm
ecto / ecto_sql3.14.2 / 3.14.0hex.pm
oban / oban_web2.24.1 / 2.13.0hex.pm
credo / sobelow / mix_audit1.7.19 / 0.15.0 / 2.1.5hex.pm
stream_data1.4.0hex.pm
tidewave / usage_rules0.9.1 / 1.2.8hex.pm

Opisane tu zachowanie Phoenix (wygenerowany AGENTS.md, alias precommit, phx.gen.release) odczytaliśmy ze źródeł instalatora phx_new 1.8.15. Flagi Elixira pochodzą z dokumentacji tasków Mix w wersji 1.20.4. Hooki i kroki CI są zgodne z dokumentacją każdego narzędzia; uruchom je na swoim projekcie, zanim zaczniesz na nich polegać.

Zacznij od tego, co generuje już mix phx.new. W Phoenix 1.8.15 tworzy on AGENTS.md (pominiesz go flagą --no-agents-md), który łączy wytyczne projektu z regułami dla Elixira, Phoenix, Ecto, HEEx i LiveView. Wśród nich: używaj strumieni LiveView dla kolekcji, unikaj LiveComponentów bez konkretnej potrzeby, uruchamiaj procesy w testach przez start_supervised!/1, nigdy nie używaj Process.sleep/1 w testach i nigdy nie wywołuj String.to_atom/1 na danych od użytkownika. Dodaje też alias, który jest naturalną definicją ukończenia:

mix.exs (generated by phx.new 1.8.15)
def cli do
[preferred_envs: [precommit: :test]]
end
defp aliases do
[
# ...
precommit: ["compile --warnings-as-errors", "deps.unlock --unused", "format", "test"]
]
end

mix precommit kompiluje z ostrzeżeniami traktowanymi jak błędy, usuwa nieużywane wpisy z mix.lock, formatuje kod i uruchamia testy. Pierwszy krok znaczy więcej w Elixirze 1.20: jego system typów wnioskuje typy między klauzulami i zgłasza naruszenia typów oraz martwy kod jako ostrzeżenia kompilatora, więc alias pada na całej klasie błędów, dla których nikt nie napisał testu. Dodaj kontrole, które agent pomija najczęściej, a ich pakiety wpisz do deps z only: [:dev, :test], runtime: false:

mix.exs
precommit: [
"compile --warnings-as-errors",
"deps.unlock --unused",
"format",
"credo --strict",
"sobelow --exit Low --skip",
"test"
]

W aplikacji utworzonej przed Phoenix 1.8 te same reguły wygenerujesz z zależności za pomocą usage_rules: dodaj {:usage_rules, "~> 1.2", only: [:dev]}, ustaw usage_rules: [file: "AGENTS.md", usage_rules: [:elixir, :otp, "phoenix:ecto", "phoenix:liveview"]] w project/0 i uruchom mix usage_rules.sync. Synchronizacja przy każdym uruchomieniu nadpisuje własną sekcję, więc reguły nadążają za mix.lock, gdy zależność dostarczy nowe.

Potem dopisz do AGENTS.md, pod wygenerowanym blokiem, linie specyficzne dla projektu:

AGENTS.md (your additions)
## Commands
- One test or file: `mix test test/shop/orders_test.exs:42`
- Failures from the last run: `mix test --failed`
- Full gate: `mix precommit` (compile, format, Credo, Sobelow, tests)
## Definition of done
`mix precommit` passes. Report the command you ran and its last lines.
## Rules
- Generate contexts, schemas and migrations with `mix phx.gen.*` and `mix ecto.gen.migration`, never by hand.
- Tests are the specification. If a test fails, fix lib/, not test/. Ask before changing an assertion.
- Never call an external service inside `Repo.transaction` or `Ecto.Multi`.
- Money is integer cents or `Decimal`, never float.

Każde narzędzie wczytuje ten plik inaczej:

Utwórz CLAUDE.md, którego pierwsza linia to @AGENTS.md, a poniżej dopisz linie przeznaczone tylko dla Claude. Claude Code od wersji 2.1.277 czyta też AGENTS.md bezpośrednio, gdy projekt nie ma CLAUDE.md (oba kanały wydań, stan na 2026-10-12); import działa w każdej wersji i na każdym kanale.

Wspólne reguły agentów wyjaśniają, jak utrzymać jedno źródło prawdy, gdy zespół używa wszystkich trzech narzędzi.

Jak dać agentowi dostęp do działającej aplikacji Phoenix?

Dział zatytułowany „Jak dać agentowi dostęp do działającej aplikacji Phoenix?”

Agent, który umie tylko czytać pliki, zgaduje, co zwraca Orders.list_orders/1 i co zalogowało ostatnie żądanie. Tidewave to otwartoźródłowy serwer MCP od Dashbit, który działa wewnątrz twojej aplikacji Phoenix w środowisku deweloperskim. Udostępnia project_eval (wykonanie kodu Elixira w działającej aplikacji), execute_sql_query (na bazie deweloperskiej), get_logs, get_source_location oraz get_docs, który czyta dokumentację dokładnie tych wersji, które są w mix.lock.

Zainstaluj go poleceniem mix igniter.install tidewave (po mix archive.install hex igniter_new) albo dodaj {:tidewave, "~> 0.9", only: :dev} i wstaw plug Tidewave w blok if Mix.env() == :dev do tuż nad if code_reloading? do w lib/my_app_web/endpoint.ex. Uruchom serwer poleceniem mix phx.server, a potem podłącz swoje narzędzie (4000 to domyślny port Phoenix):

Okno terminala
claude mcp add --transport http tidewave http://localhost:4000/tidewave/mcp

Sprawdź połączenie poleceniem /mcp w sesji. Tidewave 0.9.1 nie ma uwierzytelniania (domyślnie przyjmuje tylko połączenia z localhost), więc nie wybieraj Authenticate.

Różnica „przed i po” jest konkretna. Bez Tidewave agent czyta stack trace, poprawia changeset i ponownie uruchamia testy. Z nim wywołuje Shop.Orders.create_order/2 z problematycznymi danymi w działającej aplikacji, czyta powstałą linię logu i wiersz w tabeli orders, a potem poprawia kod raz. Tidewave domyślnie przyjmuje tylko połączenia lokalne i nigdy nie może działać na produkcji.

Jak powstrzymać agenta przed ogłoszeniem „gotowe”, zanim testy przejdą?

Dział zatytułowany „Jak powstrzymać agenta przed ogłoszeniem „gotowe”, zanim testy przejdą?”

Instrukcja w AGENTS.md to rada. Hook albo kontrola w CI to bramka. Claude Code dostaje lokalną bramkę w postaci hooka Stop. Konfiguracje Codeksa i Cursora poniżej to wciąż instrukcje, więc dla nich jedyną mechaniczną bramką jest job CI pod zakładkami.

Hook Stop uruchamia się, gdy Claude kończy odpowiedź. Kod wyjścia 2 blokuje zakończenie i pokazuje Claude’owi stderr hooka, więc Claude pracuje dalej:

.claude/hooks/elixir-gate.sh
#!/usr/bin/env bash
# Refuse "done" until mix precommit passes.
input=$(cat)
# Already continuing because of this hook: stop and report instead of looping.
[ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ] && exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
# Nothing under lib, test, config or priv changed: nothing to check.
[ -z "$(git status --porcelain -- lib test config priv mix.exs mix.lock)" ] && exit 0
if ! out=$(mix precommit 2>&1); then
echo "mix precommit failed. Fix lib/, never test/. Re-run failures with mix test --failed:" >&2
echo "$out" | tail -40 >&2
exit 2
fi
exit 0

Zarejestruj skrypt w .claude/settings.json w hooks → Stop jako handler typu command wskazujący na ${CLAUDE_PROJECT_DIR}/.claude/hooks/elixir-gate.sh z "timeout": 600 i nadaj mu prawo wykonywania. Skrypt wymaga jq. Ponieważ kończy się kodem 0, gdy stop_hook_active ma wartość true, padająca bramka wymusza jedną poprawkę, a potem Claude raportuje, zamiast krążyć w pętli nad testem, który nie może przejść, na przykład gdy PostgreSQL nie działa. mix precommit uruchamia format, więc hook przy okazji przeformatowuje pliki. Automatyzacja hookami pokazuje pełny blok ustawień i sposób na więcej prób.

Niezależnie od tego, co działało lokalnie, decyduje CI. Uruchom tam te same kontrole z tokenem tylko do odczytu i jednorazową usługą PostgreSQL:

.github/workflows/elixir-gates.yml
name: elixir-gates
on:
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
permissions:
contents: read
jobs:
gates:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_HOST_AUTH_METHOD: trust # disposable CI database, no credential
ports: ["5432:5432"]
options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=5
env:
MIX_ENV: test
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 2 # the PR merge commit and its base parent
- name: Existing tests unchanged
if: ${{ !contains(github.event.pull_request.labels.*.name, 'test-change-approved') }}
run: |
changed=$(git diff --name-only --diff-filter=MDR HEAD^1 HEAD -- test/)
if [ -n "$changed" ]; then
echo "Existing tests modified, moved or deleted; a reviewer must add the test-change-approved label:"
echo "$changed"
exit 1
fi
- uses: erlef/setup-beam@v1
with:
otp-version: "29.1.1"
elixir-version: "1.20.4"
- uses: actions/cache@v6
with:
path: |
deps
_build
key: mix-${{ runner.os }}-${{ hashFiles('mix.lock') }}
- run: mix deps.get
- run: mix deps.unlock --check-unused
- run: mix hex.audit # retired packages
- run: mix deps.audit # known advisories (mix_audit)
- run: mix format --check-formatted
- run: mix compile --warnings-as-errors
- run: mix credo --strict
- run: mix sobelow --exit Low --skip
- run: mix ecto.create && mix ecto.migrate && mix ecto.rollback --step 1 && mix ecto.migrate
- run: mix test --warnings-as-errors

Krok „Existing tests unchanged” pada, gdy pull request modyfikuje, przenosi lub usuwa plik, który już istnieje w test/; nowe pliki testów przechodzą. Gdy człowiek przejrzy zmianę testu, dodaje etykietę test-change-approved, co uruchamia nowy przebieg z pominięciem tego kroku (ponowne uruchomienie starego joba nie zadziała, bo używa pierwotnego zdarzenia). Krok jest bramką tylko wtedy, gdy pull request nie może go usunąć: GitHub uruchamia plik workflow z samego pull requesta, więc obejmij /.github/ regułą CODEOWNERS i wymuś tę recenzję przez ochronę gałęzi lub ruleset. Krok z rollbackiem dowodzi, że najnowsza migracja jest odwracalna. Wygenerowane aplikacje Phoenix domyślnie łączą się jako użytkownik postgres, którego usługa z metodą trust akceptuje.

Każdy prompt poniżej zakłada AGENTS.md i bramkę opisane wyżej. Każdy zaczyna od testów, bo test napisany przez agenta przed kodem to najtańsza wyrocznia, jaką masz.

Poproś o polecenie generatora i wyłącznie o padające testy, sprawdź, czy nowe testy padają z właściwego powodu, a o implementację poproś w drugim prompcie. Rozdzielenie tur powstrzymuje agenta przed pisaniem testów dopasowanych do własnego kodu.

Zachowaj w pierwszym prompcie krok zatwierdzenia: mix phx.gen.json tworzy naraz kontekst, schemat, migrację, kontroler i testy, a zatwierdzenie jednego polecenia kosztuje mniej niż przegląd plików, które ono wygeneruje.

Starszy kod trzyma listy w assigns albo używa temporary_assigns; reguły generowane przez Phoenix 1.8 każą agentowi używać strumieni dla kolekcji i unikać LiveComponentów bez konkretnej potrzeby. Egzekwuj je.

Problemem rzadko jest sam GenServer; problemem jest test. Agent, który czeka za pomocą Process.sleep/1, tworzy zestaw testów przechodzący na laptopie i niestabilny w CI.

Transakcja, która woła bramkę płatności, trzyma połączenie z bazą przez cały sieciowy round trip i nie potrafi wycofać obciążenia, które się powiodło. Powiedz agentowi, gdzie przebiega granica.

Wydaj release, o którym agent udowodni, że się uruchamia

Dział zatytułowany „Wydaj release, o którym agent udowodni, że się uruchamia”

mix phx.gen.release --docker generuje lib/my_app/release.ex, rel/overlays/bin/migrate, rel/overlays/bin/server, Dockerfile i .dockerignore. Dockerfile buduje się z debianowego obrazu hexpm/elixir przypiętego do wersji Elixira i OTP, z którymi go wygenerowano. Pozwól generatorowi napisać pliki, a od agenta zażądaj dowodu, że działają. Utwardzanie kontenera wykraczające poza to opisują wzorce Dockera.

Zielone testy napisane przez agenta dowodzą tylko tego, że kod zgadza się z agentem. Trzy tanie kontrole podnoszą poprzeczkę bez czytania każdej linii:

  • Testy właściwości dla changesetów i czystych funkcji. Dodaj {:stream_data, "~> 1.4", only: :test} i poproś: „For Task.changeset/2, write ExUnitProperties properties: any title shorter than 3 characters is invalid; any valid attrs round-trip through create_task/1 and get_task!/1; no status transition out of :done except to :cancelled is valid.” Właściwości generują dane wejściowe, o których agent nie pomyślał. Metodę opisuje strona testy oparte na właściwościach.
  • Zepsuj kod celowo. W osobnej sesji poproś agenta, żeby wprowadził po jednym błędzie w każdej funkcji kontekstu (odwrócone porównanie, usunięty where, pominięta walidacja) i po każdym uruchomił testy. Każdy błąd, którego testy nie złapią, oznacza brakujący test; zatrzymaj testy, odrzuć błędy.
  • Polowanie na niestabilne testy. mix test --repeat-until-failure 50 test/shop/rate_limiter_test.exs (Elixir 1.17+) powtarza plik, aż test padnie, co ujawnia wyścigi z Process.sleep i współdzielonym stanem, które zostawił agent.

Potem zdecyduj, co nadal przegląda człowiek:

ZmianaCzym udowodnionaAkceptacja człowieka
Funkcje kontekstu, changesety, kontrolery, LiveViewTesty napisane przed kodem, mix precommit, CINiepotrzebna, gdy bramki przechodzą, a istniejące testy są nietknięte
Zmodyfikowane istniejące testyEtykieta test-change-approvedTak: osoba, która dodaje etykietę
Migracje, które usuwają, zmieniają nazwy lub uzupełniają daneKrok rollbacku w CITak: sprawdź czas blokad i utratę danych (zobacz wzorce migracji)
Drzewo nadzoru i strategia restartuTesty, które zabijają proces potomny i sprawdzają, że rodzeństwo przetrwałoTak: zła strategia wychodzi dopiero pod obciążeniem
Uwierzytelnianie, autoryzacja, raw/1 w szablonachSobelow, testy ścieżek odmowyTak

Dołącz wynik mix precommit i przebieg CI do pull requesta jako jego pakiet dowodów, żeby recenzent sprawdzał dowody, a nie kod.

Czy ten artykuł Ci pomógł?

Jedno kliknięcie, bez logowania.

wcale bardzo