agentleFS
MarvellbrazilMarvellbrazil/my-agentic-skills/skills/data-dummer/SKILL.md

data-dummer

Skill4 starsChanged 49 days agoMITView on GitHub

Generates seed, fixture, and factory data that satisfies the project's real schema — reading the ORM or migrations first, respecting foreign keys and constraints, generating realistic locale-aware values, and producing a deterministic, re-runnable artifact. Use when the user types /data-dummer, asks for dummy data, seed data, test fixtures, fake records, or a database seeder. Asks for volume, locale, format, and realism before generating.

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 12 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈12

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read the schema before writing a line: Never invent a column, table, enum value, or constraint.

    line 25
  2. Distinctive:

    Produce something that runs: Verify it, do not just emit it.

    line 27
  3. Distinctive:

    Ask these four in one round.

    line 69
  4. Distinctive:

    If the user says "seeder", confirm whether they also want factories.

    line 81
  5. Distinctive:

    Ask for a number per table, or a shape.

    line 85
  6. Distinctive:

    Ask for one primary locale plus optionally a mixed distribution.

    line 107
  7. Distinctive:

    Deterministic?: Default yes. Pick a seed, state it, make it overridable.

    line 127
  8. Distinctive:

    Idempotent?: Default yes — truncate-then-insert, or upsert on a natural key.

    line 128
  9. Distinctive:

    When the user asks for a mixed distribution, do not branch per row.

    line 167
  10. Distinctive:

    Enums: Read the allowed values from the schema.

    line 228
  11. Very specific:

    Uniques: Use a generator that cannot collide (faker.internet.email() can.

    line 229
  12. Very specific:

    Nullability: Only emit null where the column allows it — unless generating adversarial data.

    line 231
  13. Distinctive:

    Soft deletes: If the model uses them, seed a realistic proportion of deleted rows.

    line 234
  14. Distinctive:

    If the target is not local, stop and confirm with the user.

    line 268

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Data Dummer

Generate dummy data that the project's actual schema accepts.

Why This Is Not a One-Line Prompt

"Buatkan saya data dummy berbentuk seeder untuk project ini" produces a script that looks plausible and fails the moment it runs. The five things that make generated data actually usable, and that a plain prompt omits:

  1. Schema fidelity. The columns, types, nullability, enums, and lengths come from the real schema — read, not invented. A generator that guesses column names produces a script that crashes on line 3.
  2. Referential integrity. Foreign keys are satisfied, in dependency order, with parent rows created before children. This is the single most common reason a generated seeder fails.
  3. Determinism. A fixed seed, so the same command produces the same data. Random seed data makes tests flaky and bug reports unreproducible.
  4. Idempotency. Re-running the seeder does not duplicate or violate unique constraints.
  5. Locale realism. Names, addresses, phone numbers, and currencies match the country the user asked for — not John Doe for every row.

A generator that gets these right is a tool. One that does not is a liability.

The Contract

  • Read the schema before writing a line. Never invent a column, table, enum value, or constraint.
  • Ask the four questions below before generating. They change the artifact materially.
  • Produce something that runs. Verify it, do not just emit it.
  • Deterministic by default. Seeded randomness, and say what the seed is.

Phase 1 — Read the Schema

Find the source of truth. Do not assume; locate it.

ls -a
find . -maxdepth 3 \
  \( -name 'schema.prisma' -o -name '*.sql' -o -path '*migrations*' \
     -o -name 'models.py' -o -name 'models.rb' -o -name '*.entity.ts' \
     -o -name 'schema.rb' -o -name '*.model.ts' \) \
  -not -path './node_modules/*' -not -path './.git/*' -not -path './vendor/*' 2>/dev/null | head -40
StackSource of truthAlso read
Prismaprisma/schema.prismaprisma/migrations/ for defaults and constraints
Drizzlesrc/db/schema.tsmigration folder
TypeORM*.entity.tsormconfig / data-source.ts
Sequelizemodels/*.jsmigrations/
Knexmigrations/seed folder conventions
Laravel / Eloquentdatabase/migrations/, app/Models/database/factories/, database/seeders/
Django*/models.py, */migrations/existing fixtures/
Railsdb/schema.rb, db/migrate/db/seeds.rb, spec/factories/
SQLAlchemymodels.py, Alembic versions/existing session setup
Raw SQL.sql DDL—
MongoDBMongoose schemas, or a validator—

Extract and record, per table: column names, types, nullability, defaults, unique constraints, enums, length limits, and foreign keys with their direction.

grep -rn "ForeignKey\|references\|belongsTo\|hasMany\|@ManyToOne\|FOREIGN KEY" \
  --include='*.py' --include='*.ts' --include='*.rb' --include='*.php' --include='*.sql' \
  . 2>/dev/null | head -40

Build the dependency graph before generating anything. A table with an FK to another must be populated after it. Cycles (a users.primary_org_id and an orgs.owner_id) need a two-pass insert: create with nulls, then backfill.

Phase 2 — Ask Before Generating

Ask these four in one round. Each changes the output materially.

1. What form should the artifact take?

FormBest forProduces
Seeder scriptPopulating a dev or staging databaseA runnable script (db/seeds.rb, seeders/*.ts, seed.py)
SQL fileEnvironments with no application runtime, or for reviewA .sql file with explicit INSERTs in FK order
JSON / YAML fixturesTest suites, snapshots, deterministic assertionsData files loaded by the test framework
Factory / builderTests that need per-case variationParameterized factories (FactoryBot, factory_boy, Fishery)
BothReal projects — bulk data plus test factoriesA seeder for volume, factories for tests

If the user says "seeder", confirm whether they also want factories. They usually do, and it costs nothing to generate both.

2. How much data?

Ask for a number per table, or a shape. Vague answers are fine — propose a default and let them adjust:

  • Smoke (5-20 rows/table) — the app renders and the happy path works.
  • Realistic dev (100-1,000 rows/table, long-tail distribution) — pagination, search, and sorting are exercised; charts have shape.
  • Load (100k+ rows) — performance and index behavior. Generate with bulk insert, not an ORM loop.
  • Edge-heavy (small count, adversarial) — empty strings, max-length values, unicode, nulls where allowed, boundary dates.

3. Which country / locale?

This is the question a plain prompt never asks, and it is why generic seed data looks fake.

LocaleNamesAddressPhoneCurrency (formatted by Intl)Gotchas
en_USGiven + familyStreet, City, ST ZIP(555) 123-4567USD, $1,234,567.895-digit ZIP, state codes
en_GBGiven + familyStreet, Town, Postcode07700 900123GBP, £1,234,567.89Alphanumeric postcode
id_IDIndonesian namesJalan, Kelurahan, Kecamatan+62 812-3456-7890IDR, Rp 1.234.568No family-name convention; many single-word names
de_DEGiven + familyStraße, PLZ Ort+49 30 1234567EUR, 1.234.567,89 €Decimal comma, ß
ja_JPFamily + given (reversed)Prefecture, City, Chome090-1234-5678JPY, ¥1,234,568Zero decimal places; name order reversed
ar_SAArabic namesDistrict, Street+966 5X XXX XXXXSARRTL text; Hijri dates may be expected
pt_BRGiven + familyRua, Bairro, CEP+55 11 91234-5678BRL, R$ 1.234.567,89CPF format is 11 digits
MixedSeveral locales———Best for testing i18n; name the distribution

Ask for one primary locale plus optionally a mixed distribution. Mixed data is the strongest choice when the project is internationalized, because it surfaces formatting bugs.

Two locale-ID formats, and mixing them is a common crash. Faker providers take an underscore ID (id_ID, ja_JP) matching POSIX/ICU convention. Intl takes a hyphen BCP 47 tag (id-ID, ja-JP) and throws RangeError: Incorrect locale information provided on the underscore form. Convert between them when the same locale feeds both a faker provider and a formatter.

// Faker wants id_ID; Intl wants id-ID. This throws:
new Intl.NumberFormat("id_ID"); // RangeError
new Intl.NumberFormat("id-ID").format(1234567.89); // "Rp 1.234.568"

Note that id-ID renders IDR as Rp 1.234.568 with a space and no decimals — not Rp1.234.567. Never hand-write a currency format; format through the runtime so the output matches what the app actually shows.

4. How realistic?

  • Realistic — plausible names, coherent addresses, correct currency, sensible dates. The default.
  • Synthetic-but-valid — obviously fake (Test User 001) but schema-valid. Best when a human must never confuse it with real data.
  • Adversarial — unicode, emoji, RTL text, max-length strings, nulls, boundary numbers. Best for finding bugs.

Also confirm, without asking if the answer is discoverable:

  • Deterministic? Default yes. Pick a seed, state it, make it overridable.
  • Idempotent? Default yes — truncate-then-insert, or upsert on a natural key.
  • Where does it run? Local only, or staging too. Never seed production. If the user asks for production, refuse and explain.

Phase 3 — Generate

Determinism first

Fix the seed before generating anything. Every generator supports this; use it.

import { faker } from "@faker-js/faker";
faker.seed(42);
from faker import Faker
fake = Faker("id_ID")
Faker.seed(42)
Faker::Config.random = Random.new(42)
$faker = Faker\Factory::create("id_ID");
$faker->seed(42);

State the seed in the output and make it overridable via an env var or CLI flag. A seeder whose output changes every run cannot be used to reproduce a bug report.

Three seeding nuances that cause flaky data if missed:

  • Python's Faker.seed() is class-level. It seeds the shared RNG for every instance created afterwards. For per-instance control use fake.seed_instance(42), which creates a dedicated RNG and leaves other instances alone.
  • PHP's $faker->seed() reseeds the global Mersenne Twister. Calling it twice in one process resets the sequence; re-seed at the top of the seeder, not per row.
  • Ruby re-seeds by replacing the RNG object. Faker::Config.random = Random.new(42) and setting it to nil restores entropy-based seeding.

Mixed locales: generate one weighted instance, not N branches

When the user asks for a mixed distribution, do not branch per row. Every major faker supports a weighted multi-locale instance, which keeps the data coherent — a Japanese address is not paired with an Indonesian name.

from collections import OrderedDict
from faker import Faker

Faker.seed(42)
fake = Faker(OrderedDict([("id_ID", 8), ("en_US", 1), ("ja_JP", 1)]))
fake.name()          # ~80% Indonesian
fake["ja_JP"].name() # force a specific locale
import { Faker, base, en, id_ID, ja_JP } from "@faker-js/faker";

// Build one instance per locale up front, with `base`/`en` as fallbacks
// so a missing provider entry does not throw at generation time.
const perLocale = {
  id_ID: new Faker({ locale: [id_ID, en, base] }),
  en_US: new Faker({ locale: [en, base] }),
  ja_JP: new Faker({ locale: [ja_JP, en, base] }),
};

const LOCALES = ["id_ID", "id_ID", "id_ID", "id_ID", "en_US", "ja_JP"];
const pick = new Faker({ locale: [en, base] });
pick.seed(42);

const rowFaker = perLocale[pick.helpers.arrayElement(LOCALES)];
rowFaker.person.fullName(); // generated entirely in that locale

Generate the whole row from one locale. A row with an Indonesian name and a US ZIP code is worse than a uniform dataset — it is data that cannot exist, and it will produce false confidence when it passes validation.

Generate in dependency order

  1. Topologically sort tables by FK dependency.
  2. Generate parents first, collecting their generated IDs.
  3. Reference real parent IDs in children — never a hardcoded 1 unless the parent with id 1 is guaranteed to exist.
  4. Handle cycles with a two-pass insert.
const users = await db
  .insert(usersTable)
  .values(
    Array.from({ length: 50 }, () => ({
      email: faker.internet.email(),
      name: faker.person.fullName(),
    })),
  )
  .returning({ id: usersTable.id });

const orders = await db.insert(ordersTable).values(
  Array.from({ length: 200 }, () => ({
    userId: faker.helpers.arrayElement(users).id,
    total: faker.number.int({ min: 10_000, max: 5_000_000 }),
  })),
);

Respect every constraint

  • Enums. Read the allowed values from the schema. Never invent one.
  • Uniques. Use a generator that cannot collide (faker.internet.email() can; append the index if needed).
  • Length limits. VARCHAR(50) gets a value under 50 characters. Read the limit.
  • Nullability. Only emit null where the column allows it — unless generating adversarial data.
  • Money. Integer minor units if the schema stores cents; never a float. Match the schema's convention exactly.
  • Timestamps. Distribute across a plausible range, not all at now(). Include some at boundaries — month ends, DST transitions, leap day — when generating adversarial data.
  • Soft deletes. If the model uses them, seed a realistic proportion of deleted rows.

Bulk insert for volume

An ORM loop inserting 100k rows one at a time takes hours. Use the bulk path: createMany, bulk_create, insert_all, COPY, or a multi-row INSERT.

Phase 4 — Verify

Run it. A generated seeder that has never executed is a draft, not a deliverable.

# Adapt to the project's own command
npx prisma db seed
bun run seed
python manage.py loaddata fixtures/dev.json
php artisan db:seed
bundle exec rails db:seed
psql "$DATABASE_URL" -f seed.sql

Then confirm, with real queries:

  • The script runs to completion against a clean database.
  • Row counts match what was requested, per table.
  • No FK violation, no unique violation, no NOT NULL violation.
  • Re-running produces the same result and does not duplicate rows.
  • A representative sample reads as realistic for the chosen locale — spot-check five rows by eye.
  • The app renders the seeded data: list pages paginate, detail pages open, no empty-state-only screens.
  • Nothing points at production. Print the resolved database target before inserting.
echo "Seeding target: $DATABASE_URL"

If the target is not local, stop and confirm with the user.

Output

## Schema Read

- Source: `prisma/schema.prisma` (14 models)
- Dependency order: User → Organization → Project → Task → Comment
- Cycles: none

## Preferences

- Form: seeder script + factories
- Volume: realistic dev — 200 users, 500 projects, 5,000 tasks
- Locale: `id_ID` primary, 20% mixed (`en_US`, `ja_JP`) for i18n coverage
- Realism: realistic
- Deterministic: seed 42, overridable via `SEED`
- Idempotent: yes — truncate then insert

## Files Created

| Path                      | Purpose                         |
| ------------------------- | ------------------------------- |
| `prisma/seed.ts`          | Main seeder, dependency-ordered |
| `tests/factories/user.ts` | Per-test factory                |

## Verification

| Check                 | Result                                    |
| --------------------- | ----------------------------------------- |
| Runs against clean DB | Pass                                      |
| Row counts            | 200 / 500 / 5,000 / 12,400 — as requested |
| Re-run idempotent     | Pass — counts unchanged                   |
| Constraint violations | 0                                         |
| App renders           | `/projects` paginates; detail pages open  |

## Notes

- `ja_JP` rows use JPY with zero decimal places, per the locale.
- Task `dueDate` values span 18 months, not clustered at `now()`.

Common Mistakes

MistakeWhy It BreaksCorrect Approach
Inventing column namesThe script crashes on the first insertRead the schema; never guess
Inserting children before parentsFK violationTopologically sort; two-pass for cycles
Hardcoding userId: 1Fails when the parent does not exist or is re-seededReference the generated parent IDs
Unseeded randomnessEvery run differs; bug reports are unreproducibleFix the seed; state it; make it overridable
Non-idempotent seederRe-running duplicates rows or violates uniquesTruncate-then-insert, or upsert on a natural key
One ORM call per row at volumeTakes hours for 100k rowsBulk insert (createMany, COPY, multi-row INSERT)
All timestamps set to now()Sorting, ageing, and "last week" views are untestableDistribute across a plausible range
Float moneyRounding errors the schema never intendedInteger minor units, matching the schema
John Doe for every localeThe data is useless for testing formattingUse the locale's faker provider
Ignoring enum valuesInvalid value rejected by the DBRead the enum from the schema
VARCHAR overflowInsert truncates or errorsRead and respect the length
Emitting null into NOT NULLInsert failsCheck nullability first
Emitting null into a nullable column never in practiceDownstream code has no null branch and crashesMatch the realistic distribution
Never running the seederShips a broken scriptExecute it against a clean DB
Pointing at productionDestroys real dataPrint and confirm the target; never seed prod
Only a seeder, no factoriesTests still hand-build objectsGenerate both when the project tests with factories

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.