Agent Skills: Phoenix Knowledge Patch

Phoenix

UncategorizedID: nevaberry/nevaberry-plugins/phoenix-knowledge-patch

Install this agent skill to your local

pnpm dlx add-skill https://github.com/Nevaberry/nevaberry-plugins/tree/HEAD/plugins/knowledge-patch/patches-codex/phoenix-knowledge-patch

Skill Files

Browse the full folder contents for phoenix-knowledge-patch.

Download Skill

Loading file tree…

plugins/knowledge-patch/patches-codex/phoenix-knowledge-patch/SKILL.md

Skill Metadata

Name
phoenix-knowledge-patch
Description
Phoenix

Phoenix Knowledge Patch

Use this skill when creating, upgrading, reviewing, or debugging Phoenix applications. Start with the quick references below, then open the topic file that matches the work at hand.

Reference index

| Reference | Topics | | --- | --- | | generators-layouts-and-projects.md | Layout components, generator inference, generated themes and configuration, interactive project creation, Git, Docker, precommit, and usage rules | | runtime-apis-and-javascript.md | Assign APIs, channel assertions and limits, parameterized pipelines, socket reconnection, LongPoll, Presence, and transport errors | | scopes-and-authentication.md | Generated scopes, ownership-aware contexts, route-aware scopes, authenticated LiveViews, magic links, sudo mode, and auth migration | | upgrades-security-and-configuration.md | Runtime requirements, controller and router deprecations, endpoint compile-time configuration, browser headers, LongPoll hardening, and log filtering |

Breaking changes and upgrade hazards

Supply controller formats

Every use Phoenix.Controller must declare :formats; use an empty list when the controller intentionally supports none.

use Phoenix.Controller, formats: [:html]

Do not carry forward the deprecated :namespace or :put_default_views options. Name the layout module instead of using a module-less layout:

put_layout(conn, html: {MyAppWeb.Layouts, :print})

Move trailing-slash URL generation away from the router's deprecated :trailing_slash option and into Phoenix.VerifiedRoutes.

Read endpoint compile-time settings explicitly

Code inside Phoenix.Endpoint cannot rely on an injected config variable. Use Application.compile_env/3:

@value Application.compile_env(
  :my_app,
  [MyAppWeb.Endpoint, :some_setting],
  :default
)

Audit settings previously supplied only at runtime; a setting needed during endpoint compilation can otherwise cause the application to fail at boot.

Treat layouts as function components

Keep root.html.heex as the single wrapper around the render pipeline. Invoke dynamic layouts such as app.html.heex from templates as components instead of configuring them as additional pipeline layouts.

<Layouts.app flash={@flash}>
  ...
</Layouts.app>

Review the browser-header policy

Without an explicit Content Security Policy, put_secure_browser_headers emits:

base-uri 'self'; frame-ancestors 'self';

Configure a policy explicitly if a third-party origin must embed the application. Do not expect x-download-options or x-frame-options; those headers are no longer emitted by this function.

Check runtime and transport limits

Use Erlang/OTP 25 or later. For Channels, remember that max_channels_per_transport defaults to 100 as of Phoenix 1.8.9; explicitly raise it for clients that intentionally multiplex more channels.

LongPoll is opt-in again as of Phoenix 1.8.2. Phoenix 1.8.9 also enforces a 100-event request batch limit. If a high-frequency application can exceed the limit, move to 1.8.7 before adopting 1.8.9 and adjust the workload.

Migrate generated authentication deliberately

Generators do not update existing authentication code. When moving from a password-at-registration flow:

  1. Add a new migration that makes hashed_password nullable; do not edit an already-used migration.
  2. Set hashed_password to nil for every still-unconfirmed account to avoid credential pre-stuffing.
  3. Account for a recently registered user losing their selected password. Deploy during low traffic, or introduce magic links without fully replacing the existing flow.

See Scopes and Authentication for the generated magic-link flow, plug ordering, and scope setup.

Scopes and authentication

Pass the scope into data access

mix phx.gen.auth Accounts User users creates Accounts.Scope and normally registers a default user scope. Generated browser and LiveView authentication assigns it as :current_scope; generated contexts receive it as the first argument and constrain queries with its owner identifier.

def list_posts(%Scope{} = scope) do
  Repo.all(from post in Post, where: post.user_id == ^scope.user.id)
end

Place generated authenticated LiveView routes inside the authenticated live_session. Otherwise the mount hook has not established the scope before scoped operations run.

Configure generator scopes completely

Define scopes under the application's :scopes configuration. Only one may be the default. Supply the scope module and assign key, an access_path to the owner identifier, ownership column settings, and test integration.

The fixture module must export <name>_scope_fixture/0, and generated tests must be able to import the configured setup helper. Use schema_migration_type when the migration type differs from schema_type. Set schema_table: nil to generate a plain scope-id column instead of a foreign key.

For multiple scopes, pass --scope name. A route_prefix can nest generated routes, while route_access_path can expose a slug independently of the database ownership key. Load a route-selected organization through the existing user scope, then replace :current_scope in both a browser plug and a LiveView on_mount hook.

Use magic links and sudo mode

Generated authentication is magic-link-first: registration no longer asks for a password, and password authentication is opt-in. Use require_sudo_mode for sensitive operations that need recent authentication. In browser pipelines, run fetch_current_scope_for_user before require_authenticated_user.

Generators and project structure

Use shorter generator invocations

The context argument is optional for phx.gen.live, phx.gen.html, and phx.gen.json; it defaults from the plural resource name. phx.gen.context can infer the context from the schema as well.

$ mix phx.gen.live Post posts title:string
$ mix phx.new my_app --interactive

Use --scope when the resource belongs to a non-default configured scope.

Expect generated-project side effects

When Git is available, phx.new initializes a repository. --docker produces a Debian trixie-based setup. Generated projects also include:

  • daisyUI-backed light, dark, and system themes when Tailwind is enabled;
  • development support for PORT and HEEx :debug_tags_location;
  • force_ssl enabled in prod.exs;
  • a mix precommit alias;
  • an AGENTS.md compatible with usage_rules; and
  • a usage_rules directory for synchronized Phoenix guidance.

phx.gen.auth warns when esbuild is missing because its generated behavior expects phoenix_html.js in the JavaScript bundle.

Runtime APIs and JavaScript

Assign maps in bulk or from existing state

Phoenix.Socket.assign/2 accepts a function whose returned map is merged into the current assigns. Phoenix.Controller.assign/2 accepts the same functional form, plus maps and keyword lists.

socket = Phoenix.Socket.assign(socket, fn assigns ->
  %{count: assigns.count + 1}
end)

conn = Phoenix.Controller.assign(conn, current_user: user, locale: "en")

Guard channel assertions

As of Phoenix 1.8.4, constrain the received payload directly in assert_push, assert_broadcast, and assert_reply:

assert_push "updated", payload when is_map(payload)

Parameterize plugs in a scope

As of Phoenix 1.8.10, Phoenix.Router.pipe_through/1 accepts configured plugs alongside named pipelines:

scope "/admin", MyAppWeb.Admin do
  pipe_through [:browser, {MyAppWeb.RequireRole, role: :admin}]
end

Handle newer JavaScript transport behavior

The socket pauses reconnect attempts while the page is hidden. LongPoll can fall back to fetch() when XMLHttpRequest is unavailable, can receive its transport token in a header, and closes and retries after a batch POST timeout. Transport errors are identifiable, so client code can distinguish connection failures from unrelated socket errors.

Presence accepts a custom dispatcher for presence_diff broadcasts and no longer crashes when a presence key matches a member of Object.prototype.

Verification checklist

  • Confirm every controller declares :formats and every layout names a module.
  • Search endpoints for the removed injected config variable.
  • Check CSP requirements before allowing cross-origin framing.
  • Verify Channel and LongPoll workloads against their 100-item limits.
  • Keep authenticated LiveViews in the session that installs the scope hook.
  • Ensure route-derived scopes are loaded through an already-authorized scope.
  • Confirm generated-auth assets include phoenix_html.js.
  • Exercise hidden-tab reconnection, LongPoll timeout recovery, and transport error classification in JavaScript integration tests.