Install
$ agentstack add skill-vinnie357-claude-skills-ecto Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Dangerous shell/eval execution.
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Ecto
Canonical reference for Ecto data work in Elixir. Verified against ecto 3.13.5 / ecto_sql 3.13.5 (signatures pulled from installed dep source). Newer minor versions add APIs but keep these signatures stable.
The default rule
Ecto schemas + changesets are the default. Raw SQL via Ecto.Adapters.SQL.query/4 is the fallback.
Reads can use raw SQL when no Ecto query DSL covers the case. Writes go through changesets so validation and constraints run. Bypassing changesets silently ships malformed rows — bugs surface at read time, not write time.
Reach for raw SQL only when one of the [escape-hatch criteria](#when-raw-sql-is-appropriate) applies. Default to Repo.all, Repo.insert, Repo.update, Repo.delete and changesets.
Reads — query DSL
Use the Ecto.Query DSL (from, where, join, select, fragment) over string SQL. The DSL composes, type-checks against the schema, and prevents SQL injection.
import Ecto.Query
# Good — composable, type-safe
def list_open_epics(repo_filter) do
from(e in Epic,
where: e.status == "open" and e.target_repos == ^repo_filter,
order_by: [desc: e.priority],
select: e
)
|> Repo.all()
end
# Use fragment/1 only for SQL the DSL cannot express
def case_insensitive_search(term) do
from(e in Epic,
where: fragment("? ILIKE ?", e.title, ^"%#{term}%")
)
|> Repo.all()
end
# Bad — string SQL, no schema typing, no composition
Ecto.Adapters.SQL.query!(Repo,
"SELECT * FROM epics WHERE status = $1",
["open"]
)
Repo.get/3 (single by primary key), Repo.one/2 (assert single result), Repo.all/2 (list), Repo.exists?/2 (boolean) cover most read entry points.
Writes — changesets
Every write builds a %Ecto.Changeset{} from a struct + attrs, then runs through Repo.insert/2, Repo.update/2, or Repo.delete/2.
Verified signatures (ecto 3.13.5):
Ecto.Changeset.cast(data, params, permitted, opts \\ [])
Ecto.Changeset.validate_required(changeset, fields, opts \\ [])
Ecto.Changeset.unique_constraint(changeset, field_or_fields, opts \\ [])
Ecto.Changeset.validate_format(changeset, field, regex, opts \\ [])
Real exemplar from Vantageex.Epics.Epic:
def changeset(epic, attrs) do
epic
|> cast(attrs, [:external_id, :title, :slug, :objective, :status, :priority])
|> validate_required([:external_id, :title, :slug, :objective])
|> validate_inclusion(:status, @valid_statuses)
|> unique_constraint(:slug)
end
Return tuples are {:ok, struct} on success or {:error, %Ecto.Changeset{}} on validation/constraint failure. Pattern-match both branches:
def create_epic(attrs) do
%Epic{}
|> Epic.changeset(attrs)
|> Repo.insert()
end
case create_epic(attrs) do
{:ok, epic} -> handle_success(epic)
{:error, changeset} -> render_errors(changeset)
end
For bang-vs-non-bang and idiomatic error returns, see /elixir:style.
Batch writes
Repo.update_all/3 and Repo.delete_all/2 skip changesets — no per-row validation, no before_*/after_* hooks. Use them for mass updates where validation is irrelevant or already enforced upstream.
# Bulk: clear stale fields across many rows
from(e in Epic, where: e.status == "stale")
|> Repo.update_all(set: [last_peek_output: nil])
When validation matters, prefer N changeset updates inside a transaction over one update_all. Measure before bulk-updating "for performance" — Postgres handles thousands of indexed updates in milliseconds.
Migrations
Schema changes use add, alter, drop, create index, create table macros. Verified signatures (ecto_sql 3.13.5):
Ecto.Migration.add(column, type, opts \\ [])
Ecto.Migration.execute(command) # binary, 0-arity fn, or list
defmacro create(object, do: block) # for tables/indexes with column blocks
defmodule MyApp.Repo.Migrations.AddPriorityToEpics do
use Ecto.Migration
def change do
alter table(:epics) do
add :priority, :integer, default: 10, null: false
end
create index(:epics, [:priority])
end
end
Data migrations — local snapshot schema pattern
Data migrations transform existing rows. Two rules:
- Never
aliasthe live application schema. The live schema evolves; a migration written againstMyApp.Epics.Epictoday breaks when fields get renamed or removed in a future migration. Replay from scratch fails. - Define a local snapshot schema inside the migration module. It captures the schema's shape at this point in history.
defmodule MyApp.Repo.Migrations.BackfillEpicPriority do
use Ecto.Migration
import Ecto.Query
defmodule Epic do
use Ecto.Schema
schema "epics" do
field :priority, :integer
field :status, :string
end
end
def change do
repo = repo()
from(e in Epic, where: is_nil(e.priority))
|> repo.all()
|> Enum.each(fn epic ->
epic
|> Ecto.Changeset.change(priority: priority_for_status(epic.status))
|> repo.update!()
end)
end
defp priority_for_status("urgent"), do: 1
defp priority_for_status(_), do: 10
end
This pattern dogfoods Ecto end-to-end: queries via DSL, writes via changesets, schemas frozen to migration time. See references/migrations.md for additional recipes.
When execute/1 is appropriate
execute/1 runs raw SQL inside a migration. Use it for DB-specific DDL the macros do not cover:
- Postgres extensions:
execute("CREATE EXTENSION IF NOT EXISTS pgcrypto") - Custom indexes:
execute("CREATE INDEX CONCURRENTLY ...")(note:CONCURRENTLYrequires@disable_ddl_transaction true) - Trigger functions, materialized views, partitioning DDL
execute/1 is never the right tool for data transformations. Use the local-snapshot pattern above.
Multi-step atomic ops — Ecto.Multi
Ecto.Multi chains operations into one transaction. If any step fails, prior steps roll back. As of ecto 3.13, Repo.transaction/2 is deprecated — use Repo.transact/2.
Verified signatures:
Ecto.Multi.new() # empty %Ecto.Multi{}
Ecto.Multi.insert(multi, name, changeset_or_struct_or_fn, opts \\ [])
Ecto.Multi.update(multi, name, changeset_or_fn, opts \\ [])
Repo.transact(multi_or_fn, opts \\ [])
def transfer_funds(from_id, to_id, amount) do
Ecto.Multi.new()
|> Ecto.Multi.update(:debit, debit_changeset(from_id, amount))
|> Ecto.Multi.update(:credit, credit_changeset(to_id, amount))
|> Ecto.Multi.insert(:audit, fn %{debit: debit} ->
AuditLog.changeset(%AuditLog{}, %{from: debit.id, amount: amount})
end)
|> Repo.transact()
end
On success: {:ok, %{debit: ..., credit: ..., audit: ...}}. On failure: {:error, failed_step_name, failed_value, changes_so_far}. Pattern-match the four-tuple for the failed-step branch.
The third argument to Multi.insert/4 and Multi.update/4 accepts a 1-arity function fn changes -> changeset end — that is how downstream steps reference upstream results.
When raw SQL IS appropriate
Escape hatch criteria. Each one requires evidence, not assumption.
- Complex CTEs / window functions Ecto's DSL does not expose. Document the missing DSL feature in a comment.
- DB-specific features: PostGIS spatial queries, full-text search ranking with custom dictionaries, advisory locks.
- Perf-critical hot paths with measured Ecto overhead. Benchmark first; "it feels slow" is not evidence.
- One-off reads in tidewave eval (single-cell exploration). Never one-off writes — those go through a changeset even at the REPL.
# Acceptable: PostGIS — no DSL covers ST_DWithin
{:ok, %{rows: rows}} =
Ecto.Adapters.SQL.query(
Repo,
"SELECT id FROM places WHERE ST_DWithin(location, ST_MakePoint($1, $2)::geography, $3)",
[lng, lat, radius]
)
Anti-patterns
repo().query!("UPDATE ...") in migrations
Bypasses Ecto, hides errors, locks the migration to the current row shape. Replace with the local-snapshot pattern.
# Bad
def change do
repo().query!("UPDATE epics SET priority = 1 WHERE status = 'urgent'")
end
# Good — snapshot schema + Repo.update_all (or Repo.update for per-row validation)
defmodule Epic do
use Ecto.Schema
schema "epics" do
field :priority, :integer
field :status, :string
end
end
def change do
from(e in Epic, where: e.status == "urgent")
|> repo().update_all(set: [priority: 1])
end
Bypassing changeset validation via insert_all
Repo.insert_all/3 inserts raw maps without changesets. Use it only when you have already validated the data upstream OR when the rows are infrastructure (e.g. seed data). For user input, always go through a changeset.
Importing live application schema into old migrations
# Bad — couples migration N to whatever Epic looks like at HEAD
defmodule MyApp.Repo.Migrations.OldMigration do
alias MyApp.Epics.Epic # WRONG
...
end
A schema change six months from now breaks migration replay. Define a local snapshot schema instead (see [Migrations](#migrations) above).
"Raw SQL because it's faster"
Without measurement, this is fabrication. Ecto's overhead vs raw SQL is microseconds per row. Most perceived "slow Ecto" is N+1 queries (fix with Repo.preload/3 or a join), missing indexes, or unbounded result sets — not the DSL itself.
References
references/migrations.md— local snapshot schema deep dive, common DDL recipes, reversible migrations,@disable_ddl_transactionrulesreferences/migrations-audit.md— repeatable two-phase (haiku indexer + opus reviewer) template for auditing a repo's migrations for/elixir:ectocompliance; reuses across repos by swapping the app namespacereferences/changesets.md—cast_assoc,validate_*catalog, custom validations,prepare_changes/2- HexDocs: Ecto, Ecto.Changeset, Ecto.Query, Ecto.Multi, Ecto.Migration
/elixir:tidewave— runtime introspection of schemas viamcp__tidewave__get_ecto_schemasand live API doc lookup/elixir:phoenix— context-pattern integration (changeset usage in Phoenix contexts)/elixir:style— bang-vs-non-bang return semantics, idiomatic error returns
Anti-fabrication
Every API signature in this skill is verified from the installed source under deps/ecto/ and deps/ecto_sql/ (ecto 3.13.5). When this skill is loaded:
- Cite signatures from
mcp__tidewave__get_docsor the installed dep source — not memory. - Mark any unverified function as "requires investigation".
- See
/core:anti-fabricationfor the authoritative guide.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: vinnie357
- Source: vinnie357/claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.