| title | Starter First-End Tutorial |
|---|---|
| outline | deep |
This tutorial shows the shortest path from ztd init --starter to a small users feature that can be changed, broken, and repaired with AI help.
The tutorial uses one starter project, one smoke feature, and one users feature. The same project is reused for every scenario:
- first run
- CRUD feature creation
- DDL change
- SQL change
- DTO change
- migration artifact creation
README gives the first-run copy-paste path. This tutorial gives the scenario-level flow and the preferred CLI for each repair loop.
| Scenario | Primary CLI | Why |
|---|---|---|
| DDL repair | npx ztd query uses column users.email --specs-dir src/features/users/persistence --any-schema --view detail |
Find the impacted feature-local SQL files before editing them |
| SQL repair | npx ztd model-gen --probe-mode ztd src/features/users/persistence/users.sql --out src/features/users/persistence/users.spec.ts |
Regenerate the spec from the feature-local SQL asset |
| DTO repair | npx vitest run after the DTO change |
Verify the feature-local runtime and tests after the shape change |
| migration | npx ztd ztd-config, optionally npx ztd ddl pull --url <target-db-url> to inspect the target, then npx ztd ddl diff --url <target-db-url> --out tmp/users.diff.sql to prepare review output plus apply SQL |
Prepare a manually applied migration without asking ztd-cli to deploy it |
| tuning | npx ztd query plan <sql-file> and the perf guide under docs/guide/ |
Keep perf work in the separate tuning path, not in the starter tutorial |
ZTD_TEST_DATABASE_URL is the only implicit database owned by ztd-cli. Use --url or a complete --db-* flag set for ddl pull and ddl diff when you want to inspect any other target.
Run:
npx ztd init --starter
# Optional: install the customer-facing Codex bootstrap for the AI-guided path
npx ztd agents initThe starter generates:
src/features/smokeztd/ddl/demo.sqlcompose.yaml- optional customer-facing Codex bootstrap (installed by
npx ztd agents init) - Vitest smoke tests
Run npx ztd agents init immediately after scaffold creation when you want the customer-facing Codex bootstrap for the AI-guided path.
That opt-in bootstrap adds visible AGENTS.md, .codex/config.toml, .codex/agents/*, and .agents/skills/*.
The smallest DB-backed starter example lives in src/features/smoke/tests/smoke.queryspec.test.ts.
It uses @rawsql-ts/testkit-postgres and createPostgresTestkitClient, so a missing ZTD_TEST_DATABASE_URL, a stopped Postgres container, or a schema mismatch fails before you build a larger feature.
If you want the fixture-loading details, read packages/testkit-postgres/README.md after the starter smoke test.
Use the bundled compose file:
Make sure Docker Desktop or another Docker daemon is already running before you start the compose path, because docker compose up -d only launches the stack.
cp .env.example .env
# edit ZTD_DB_PORT=5433 if needed
docker compose up -d
npx vitest runThe starter setup derives ZTD_TEST_DATABASE_URL from .env, so changing ZTD_DB_PORT changes both the compose port and the test runtime.
If port 5432 is already in use, update ZTD_DB_PORT in .env before you rerun the compose path, for example:
cp .env.example .env
# edit ZTD_DB_PORT=5433
docker compose up -d
npx vitest runIf you are using PowerShell, the same .env file works:
Copy-Item .env.example .env
# edit ZTD_DB_PORT=5433
docker compose up -d
npx vitest runThe smoke test proves the starter wiring is sound before you add real feature work. It also proves the DB-backed ZTD path is reachable from the starter, not just the DB-free sample path.
If the project was installed with pnpm install, keep using pnpm when you add the database adapter for the SQL repair loop:
pnpm add -D @rawsql-ts/adapter-node-pgAvoid mixing npm install -D into a pnpm-managed starter project because that can fail before the adapter is added.
Use src/features/smoke as the teaching example and add src/features/users as the first real feature.
Keep the feature local:
src/features/users/domainsrc/features/users/applicationsrc/features/users/persistencesrc/features/users/tests
The feature should own its SQL, spec, and tests instead of reaching for src/catalog as the starting place.
Use the prompt from packages/ztd-cli/README.md or PROMPT_DOGFOOD.md:
This prompt is meant to be copied into another AI instance so we can observe whether the scaffold and AGENTS guidance are enough on their own.
Add a users feature to this feature-first project.
Read the nearest AGENTS.md files first. Then read `.codex/agents/*` and `.agents/skills/*` if present.
Keep handwritten SQL, specs, and tests inside src/features/users.
Do not apply migrations automatically.
Expected result:
- the agent edits the
usersfeature only - the agent keeps SQL, spec, and tests feature-local
- the next command is a normal project test run
Use the same users project for each scenario:
- change the DDL and let the agent repair the failures
- change the SQL and let the agent repair the failures
- change the DTO shape and let the agent repair the failures
Each scenario should end with vitest passing again.
For DDL repair, run npx ztd query uses column users.email --specs-dir src/features/users/persistence --any-schema --view detail first so the impacted SQL files come from the CLI, not from guesswork. Passing the feature folder as --specs-dir is a normal way to narrow the project-wide scan, not a workaround for feature-local layouts.
For SQL repair, keep the SQL assets under the feature folder, keep the query on the starter DDL's users table, and rerun model-gen against the feature-local SQL file directly. In VSA layouts, model-gen now treats the SQL file location as the primary contract source, so --sql-root is only needed for older shared-root layouts.
For migration work, use an explicit --url <target-db-url> with ddl pull or ddl diff so the target database is never inferred from the starter test database by accident.
Read the review summary first:
- the summary tells you what changed logically
- the risks section lists destructive and operational apply-plan risks separately
- even a small summary can still carry destructive risks when the generated apply SQL rebuilds a table
- the generated
.sqlfile stays SQL-only so you can review or apply it separately - the companion
.jsonfile is for AI/tools that need structured migration metadata - if you hand-edit the generated migration SQL, run
npx ztd ddl risk --file tmp/users.diff.sqlso the final SQL is re-evaluated with the same structured risk contract - current
ztd ddl diffCLI does not expose the lower-level drop-avoidance options from core, so treat drop-related risks as mandatory review points
Tuning belongs to the separate performance guide and dogfooding set, not to the starter lifecycle in this tutorial. Keep the starter path focused on CRUD, DDL, SQL, DTO, and migration repair loops.
When the schema change needs a deployable migration, keep the flow explicit:
Use a fresh AI prompt for this step so we can confirm the migration guidance works without human patching in the middle.
- Edit the DDL in
ztd/ddl/demo.sqlor the relevant schema file. - Run
npx ztd ztd-configto refresh the ZTD-generated artifacts, includingtests/generated/ztd-fixture-manifest.generated.tsfor runtime schema metadata (tableDefinitionsonly). - Optionally run
npx ztd ddl pull --url <target-db-url>to inspect the target, then runnpx ztd ddl diff --url <target-db-url> --out tmp/users.diff.sqlwhen you need a migration plan. - Read the text summary first, inspect the generated SQL second, and apply the SQL outside
ztd-cli. - Re-run
npx ztd ztd-configandnpx vitest runafter the migration lands so the generated runtime manifest stays in sync with the schema metadata.
The fixture contract is intentionally split:
- generated
tableDefinitionsare the normal runtime path afterztd-config - explicit
tableDefinitions/tableRowsare for local tests that want direct fixtures ddl.directoriesis the fallback only when no generated manifest exists
This step belongs in the tutorial because the starter path should show not only how to add a feature, but also how to evolve the schema safely without asking ztd-cli to own deployment.
After the starter flow is green, the user should be able to answer these questions without guessing:
- Where does the next feature live?
- Which files should the agent read first?
- Which command verifies the change?
- Which files stay feature-local?
- How do I prepare a migration without making
ztd-clideploy it for me?
If the answer is unclear, fix the scaffold, the prompt, or the AGENTS guidance before adding more tutorial content.