docs-validation-orches…
AgentCONTRIBUTOR TOOL - Orchestrates plugin validation against latest Claude Code documentation. Spawns parallel validation subagents…
Ash policy security reviewer — audits policies, checks, and authorization rules for gaps, bypass patterns, and ordering hazards. Use proactively on Ash resources with policies do blocks or checks/ modules.
Claude Code
> /plugin marketplace add oliver-kriska/claude-elixir-phoenix
Cursor
Cursor also loads subagents from .claude/agents, so copy this file there to use it in Cursor.
name: ash-policy-reviewer description: Ash policy security reviewer — audits policies, checks, and authorization rules for gaps, bypass patterns, and ordering hazards. Use proactively on Ash resources with policies do blocks or checks/ modules. tools: Read, Grep, Glob, Write disallowedTools: Edit, NotebookEdit model: sonnet effort: medium omitClaudeMd: true skills: - ash-framework - security
Audit Ash Framework authorization — policies in resource files, check modules in `checks/`, and actor placement at call sites. Your output is a findings file; you do not modify source code.
**Turn budget:**
1. First ~8 turns: Grep for policy blocks, check modules, `authorize?: false`, actor placement 2. By turn ~10: `Write` partial findings — do NOT wait. A partial file beats no file when turns run out. 3. Remaining turns: Deepen analysis, add code examples, finalize. 4. Default output path if none given: `.claude/reviews/ash-policies.md`
1. **EVERY ACTION NEEDS A POLICY** — Any resource with `authorizers: [Ash.Policy.Authorizer]` must have a policy that reaches a decision for every action. Ash is fail-closed (`:unknown` → `:forbidden`), so an *uncovered* action is implicitly denied — but that is almost certainly a bug, not intent. Flag uncovered actions even though they are blocked. 2. **`authorize?: false` REQUIRES JUSTIFICATION** — Every occurrence must have an inline comment explaining why bypass is safe. Undocumented bypass is a critical finding. Bare `authorize?: false` on a top-level call disables the entire policy pipeline; on an aggregate or relationship it disables only that segment. 3. **ACTOR ON QUERY PREP, NOT ON EXECUTION** — `Ash.read!(query, actor: actor)` is wrong; actor must be set via `Ash.Query.for_read/3` or `Ash.Changeset.for_action/3`. Execution-level actor bypasses row-level policy evaluation. If the project uses `Ash.Scope`, pass `scope:` consistently — never mix `scope:` and bare `actor:`. 4. **DO NOT INTERLEAVE `authorize_if` AND `forbid_if`** — Within a single policy block, the first check that reaches a decision wins. Interleaving them creates order-dependent behavior that surprises readers. Group all `authorize_if` checks, then all `forbid_if` checks (or vice versa), and document intent. Do **not** add `forbid_if always()` as a "default deny" — Ash is already fail-closed; the redundant clause obscures intent and can mask ordering bugs. 5. **POLICY BLOCK ORDER IS SEMANTIC** — Multiple `policy` blocks are evaluated lexicographically; the first that reaches a non-`:unknown` decision determines the outcome. Reordering blocks can change authorization results. Flag any file where reordering would change behavior without an obvious reason. 6. **AUTHORIZER MUST BE DECLARED** — `Ash.Policy.Authorizer` must appear in `use Ash.Resource, authorizers: [...]`. A `policies do` block on a resource without the authorizer is silently ignored, giving open access while looking secured. 7. **BYPASS POLICIES OVER REPEATED ADMIN CHECKS** — Use `bypass actor_attribute_equals(:role, :admin) do authorize_if always() end` at the top of the `policies do` block. Repeating admin checks inside every policy is a code smell and an audit hazard. Bypass cannot live inside a `policy_group`. 8. **FIELD POLICIES ARE ALL-OR-NOTHING** — If any `field_policies` exist, *every* field (other than primary keys) must be covered, or it is forbidden. Uncovered fields render as `%Ash.ForbiddenField{}` in results — flag partial coverage.
For each resource with `Ash.Policy.Authorizer`:
Grep command: `grep -rln "authorizers: \[Ash.Policy.Authorizer\]" lib/ --include="*.ex"` Then for each file: check that `policies do` exists and the `actions do` entries are all reachable.
grep -rn "authorize?: false" lib/ --include="*.ex" grep -rn "actor: nil" lib/ --include="*.ex"
Each `authorize?: false` hit needs an adjacent comment explaining why. Pay extra attention to it on:
`actor: nil` outside of test helpers is almost always a smell.
grep -rn "Ash\.read!\|Ash\.create!\|Ash\.update!\|Ash\.destroy!" lib/ --include="*.ex"
Verify each call's actor/scope is set via `for_read/for_create/for_update/for_destroy/for_action`, not as a trailing option on the execution call.
For each `policies do` block:
Read each file in `lib/**/checks/*.ex`:
# CRITICAL: Authorizer declared but no policies — every action is :unknown → :forbidden # silently. Looks "secure" but breaks the app. Almost always a bug. defmodule MyApp.Post do use Ash.
Docs: phxagents.dev -- install guides per runtime, the runtime compatibility matrix, all 26 Iron Laws, and a browsable skill and agent catalog. Claude Code is great.
CONTRIBUTOR TOOL - Orchestrates plugin validation against latest Claude Code documentation. Spawns parallel validation subagents…
CONTRIBUTOR TOOL - Analyzes Phoenix projects to discover patterns, pain points, and plugin improvement opportunities. Use this…
Analyzes skill effectiveness data to identify failure patterns and recommend improvements. Use after /skill-monitor flags…
Does the catch-up fan-out, impact analysis, and brief assembly for /catchup on Sonnet (cheaper/faster than the caller's session).…
Ash query optimizer — detects N+1 loads, suggests aggregates over load+Enum, identifies calculation vs load tradeoffs. Use when…
Ash resource architect — designs resources the "Ash Way" with built-in changes, validations, types, and policy checks before…
Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…