Curated library of 38 atomic skills, 7 personas, and 1 orchestrator for Elixir and Phoenix development. Organized by category: fundamentals, phoenix, database, testing, auth, infrastructure, quality, security, integrations, tooling, frameworks, personas, and orchestration. Covers core Elixir patterns, Phoenix LiveView, Ecto, OTP, Oban, testing, security, deployment, real-time, and modern tooling (Req, Swoosh, Cachex, Broadway, Ash).
91
91%
Does it follow best practices?
Impact
91%
1.37xAverage score across 56 eval scenarios
Advisory
Suggest reviewing before use
Use this skill when extending phx.gen.auth with custom fields.
registration_changeset to cast and validate new fields — don't create a separate changesetconfirmed_at or tests will failsave/2 handler — the form must send the fieldunique_constraint + database unique index for uniqueness — never validate in application code alone# Generate auth with LiveView (recommended)
mix phx.gen.auth Accounts User usersmix ecto.gen.migration add_username_to_usersdefmodule MyApp.Repo.Migrations.AddUsernameToUsers do
use Ecto.Migration
def change do
alter table(:users) do
add :username, :string, null: false
end
create unique_index(:users, [:username])
end
end# Run migration and verify success before proceeding
mix ecto.migratedefmodule MyApp.Accounts.User do
schema "users" do
field :email, :string
field :username, :string # Add new field
field :password, :string, virtual: true, redact: true
field :hashed_password, :string, redact: true
field :confirmed_at, :utc_datetime
timestamps()
end
# Update registration_changeset to include username
def registration_changeset(user, attrs, opts \\ []) do
user
|> cast(attrs, [:email, :username, :password])
|> validate_required([:username])
|> validate_username()
|> validate_email(opts)
|> validate_password(opts)
end
defp validate_username(changeset) do
changeset
|> validate_required([:username])
|> validate_format(:username, ~r/^[a-zA-Z0-9_]+$/,
message: "only letters, numbers, and underscores"
)
|> validate_length(:username, min: 3, max: 30)
|> unsafe_validate_unique(:username, MyApp.Repo)
|> unique_constraint(:username)
end
enddef user_fixture(attrs \\ %{}) do
{:ok, user} =
attrs
|> Enum.into(%{
email: "user#{System.unique_integer([:positive])}@example.com",
username: "user#{System.unique_integer([:positive])}", # Add username
password: "hello world!",
confirmed_at: DateTime.utc_now(:second) # Confirm for tests
})
|> MyApp.Accounts.register_user()
user
end# Verify fixtures pass before updating forms
mix test<.simple_form for={@form} phx-change="validate" phx-submit="save">
<.input field={@form[:username]} type="text" label="Username" />
<.input field={@form[:email]} type="email" label="Email" />
<.input field={@form[:password]} type="password" label="Password" />
<:actions>
<.button>Register</.button>
</:actions>
</.simple_form>For optional fields like display_name or avatar_url, follow the same migration → schema → fixture sequence as above. Differences from required fields:
null: false in the migration column definition.validate_required for that field in the changeset.phx.gen.auth generates a confirmed_at field and an email-confirmation flow automatically.
confirmed_at: DateTime.utc_now(:second) in fixtures (Rule 4) so tests are not blocked by the confirmation gate.confirmed_at is populated. Guard LiveViews with require_authenticated_user and check confirmed_at explicitly where needed.email_changeset and password_changeset follow the same cast-then-validate pattern as registration_changeset. Never add a parallel changeset; extend the existing one.
| ❌ Don't | ✅ Do |
|---|---|
Edit the generated phx.gen.auth migration in place | Create a separate migration (e.g. add_username_to_users) for custom fields |
| Create a parallel changeset for the new field | Extend registration_changeset with cast/3 + validate_* |
| Validate uniqueness only in Elixir | Pair unique_constraint with a database unique_index |
Forget to update user_fixture/1 | Add the required field and confirmed_at to the fixture |
| Add the field to the schema but not the form | Update the form input AND the save/2 handler |
Leave confirmed_at nil in test fixtures | Set confirmed_at: DateTime.utc_now(:second) so password auth tests pass |
| Predecessor | This Skill | Successor |
|---|---|---|
| phoenix-liveview-auth | phoenix-auth-customization | phoenix-authorization-patterns |
| ecto-changeset-patterns | phoenix-auth-customization | testing-essentials |
Companion skills: phoenix-liveview-auth, ecto-changeset-patterns, testing-essentials
.tessl-plugin
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
scenario-42
scenario-43
scenario-44
scenario-45
scenario-46
scenario-47
scenario-48
scenario-49
scenario-50
scenario-51
scenario-52
scenario-53
scenario-54
scenario-55
scenario-56
skills
frameworks
ash-framework
infrastructure
orchestration
elixir-skill-router
personas
phoenix
quality
security
security-essentials
tooling
mix-tasks-generators