| name | semeno |
| description | Author, run and debug semeno database seeds. Use when the user asks to create a seed/demo scenario, populate the database with test data, reset a scenario, or when a task needs a specific DB state (E2E fixtures, manual QA states). Covers the seed contract (idempotency, reset, dependsOn, scopes) and the semeno CLI. |
semeno — seed scenario authoring
semeno manages named, idempotent, self-describing database seeds.
Two kinds: foundational (reference data, no reset) and scenario
(complete demo state with a reset hook). Seeds declare scope
(business stream), dependsOn (other seeds) and requires (endpoint
keys); the CLI orders the dependency closure topologically.
Orient first (cheap, machine-readable)
semeno list --json
semeno info <name> --json
semeno plan <name> --json
semeno validate
Never guess what a seed does — the description field is the contract.
Pick by description; if none fits, write a new seed.
Running
semeno run <name>
semeno run all --scope edu
semeno run <name> --reset
semeno reset <name>
semeno run … --dry-run
Default target is the local one. Remote targets (--target staging)
read secrets from env vars and demand typed confirmation — pass --yes
only in CI or after the user approved the target explicitly.
Authoring a new seed
- Find the project's seed directory and context type (look at
semeno.config.ts → createContext and existing seeds).
- Create
<kebab-name>.ts exporting a seed via the project's typed
helper (or defineSeed from @semeno/core):
export const mySeed = defineSeed({
name: 'my-seed',
kind: 'scenario',
scope: 'edu',
dependsOn: ['initial-data'],
requires: ['content'],
description: 'First sentence appears in `semeno list`. Then state '
+ 'EXACTLY what rows exist afterwards and what state they are in, '
+ 'so the next agent can pick this seed without reading the code.',
run: async ctx => { },
reset: async ctx => { },
})
- Register it in
semeno.config.ts (seeds: [...]).
semeno validate — must be clean.
semeno run my-seed, then re-run it to prove idempotency, then
semeno reset my-seed && semeno run my-seed if it has a reset.
The idempotency contract (non-negotiable)
run must be safe on a database that already contains the seed's
artefacts. Two proven patterns:
- Query-then-create — look the row up by a deterministic ID, insert
only when missing. Use stable, recognizable IDs (e.g. a
5eed… UUID
prefix) kept in one shared module.
- Delete-then-create —
reset wipes the slice, run recreates it;
the run itself still upserts so a bare re-run is a no-op.
reset removes only this seed's artefacts and never shared
reference data other seeds or user content depend on.
Rules of thumb
- One seed = one reviewable state. Big "everything" seeds are a smell —
compose small ones with
dependsOn.
- Shared infrastructure (personas, taxonomies) belongs in a
foundational seed others depend on, not copy-pasted into scenarios.
- Descriptions are operator/agent UX: end state, not implementation.
- Never hard-code remote URLs or tokens in a seed — everything
environment-specific comes from the target via
ctx.
- Bulk imports and per-test in-process fixtures are NOT seeds — keep
those in their own tooling.