Files
toir-light/docs/generation-playbook.md
2026-04-03 20:54:37 +03:00

238 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Generation Playbook
Step-by-step instructions for generating and regenerating artifacts in this repository.
Read `AGENTS.md` and `docs/source-of-truth.md` first.
---
## Pipeline overview
```
Tier 1 — Single Source of Truth (hand-authored, never generated)
domain/toir.api.dsl ──┐ enums, DTOs, endpoints, HTTP methods,
│ entity field mappings, primary keys
Tier 2 — Deterministic Preprocessing (npm scripts, no LLM)
api-summary.json ←─ npm run generate:api-summary
openapi.json ←─ npm run generate:openapi (auxiliary)
Tier 1 (api.dsl) + Tier 2 (context) + prompts/*.md ──► LLM Generation
prompts/general-prompt.md
prompts/backend-rules.md ──► server/src/modules/<entity>/
prompts/frontend-rules.md ──► client/src/resources/<entity>/
prompts/prisma-rules.md ──► server/prisma/schema.prisma
prompts/auth-rules.md ──► (auth seam reference)
prompts/runtime-rules.md ──► (env/docker reference)
prompts/validation-rules.md ──► (validation gate reference)
Tier 4 — Validation Gate
node tools/validate-generation.mjs --artifacts-only
```
---
## Prerequisites
Before any generation run:
1. `domain/*.api.dsl` is current and valid.
2. Refresh the Tier 2 intermediate context:
```bash
npm run generate:api-summary
```
3. Run `node tools/validate-generation.mjs --artifacts-only` to confirm the baseline passes.
---
## Standard generation workflow
### Step 1 — Refresh Tier 2 derived artifacts
```bash
# From repo root
npm run generate:api-summary
```
### Step 2 — Read generation inputs (context budget)
> **`prompts/general-prompt.md` is the master generation prompt.** It contains all core
> type mappings, naming conventions, DTO/controller/service/frontend rules, mutation
> boundaries, and the complete generation workflow. Load it as the single entrypoint.
>
> For artifact-specific details (Prisma FK rules, auth JWKS chain, detailed validation
> groups), load the relevant companion file: `prompts/prisma-rules.md`,
> `prompts/auth-rules.md`, `prompts/validation-rules.md`, or `prompts/runtime-rules.md`.
>
> See `prompts/general-prompt.md §CONTEXT BUDGET` for the full budget model.
1. `prompts/general-prompt.md` — master generation prompt (always load)
2. `api-summary.json §<entity>` — compact entity index (fast-path context anchor)
3. `domain/*.api.dsl §API.<EntityName>` — **only the api block + its referenced DTOs + enums** (entity-scoped)
4. **If needed:** `prompts/prisma-rules.md` (Prisma) or `prompts/auth-rules.md` (auth seams)
Before generating any DTO or component: **quote the relevant DSL field definitions verbatim first**, then generate from those quotes. This prevents training-data contamination.
### Step 3 — Generate Prisma schema
Generate `server/prisma/schema.prisma` from `domain/*.api.dsl` following `prompts/prisma-rules.md`.
If the schema changed, run Prisma migration in `server/`:
```bash
cd server
npx prisma migrate dev --name <description>
```
### Step 4 — Generate backend modules
For each `api` block in `domain/*.api.dsl`, generate:
1. `server/src/modules/<kebab>/<entity>.module.ts`
2. `server/src/modules/<kebab>/dto/create-<kebab>.dto.ts`
— fields from the `DTO.<Entity>Create` block in api.dsl
3. `server/src/modules/<kebab>/dto/update-<kebab>.dto.ts`
— fields from the `DTO.<Entity>Update` block in api.dsl
4. `server/src/modules/<kebab>/<entity>.service.ts`
— CRUD operations using Prisma; respect type mappings from `prompts/backend-rules.md`
5. `server/src/modules/<kebab>/<entity>.controller.ts`
— one method per `endpoint` in the `api` block; HTTP verb and path from the `label`
Register the module in `server/src/app.module.ts`.
### Step 5 — Generate frontend resources
For each `api` block in `domain/*.api.dsl`, generate:
1. `client/src/resources/<kebab>/<Entity>List.tsx`
— columns from `DTO.<Entity>` (response shape)
2. `client/src/resources/<kebab>/<Entity>Create.tsx`
— fields from `DTO.<Entity>Create`
3. `client/src/resources/<kebab>/<Entity>Edit.tsx`
— fields from `DTO.<Entity>Update`
4. `client/src/resources/<kebab>/<Entity>Show.tsx`
— fields from `DTO.<Entity>`
Register the resource in `client/src/App.tsx`.
### Step 6 — Verify (two-stage gate)
**Stage 1 — Structural gate:**
```bash
node tools/validate-generation.mjs --artifacts-only
```
Checks file existence, field names, class-validator decorators, auth guards, and RA component types.
Full structural verification (requires installed deps):
```bash
node tools/validate-generation.mjs
```
**Stage 2 — Eval harness:**
```bash
npm run eval:generation
```
Fixture-based semantic checks. See `tools/eval/README.md`.
Both must pass before the task is complete.
---
## Adding a new entity
1. Add the entity's enums, DTOs, and `api` block to `domain/toir.api.dsl`.
2. Run `npm run generate:api-summary`.
3. **Before generating:** create `tools/eval/fixtures/<kebab>/meta.json`, `backend.assertions.json`, and `frontend.assertions.json` with expected patterns.
4. Run `npm run eval:generation` — it will fail (entity files don't exist yet). That's expected.
5. Generate backend and frontend artifacts (Steps 35).
6. Run `npm run eval:generation` again — now it should pass.
7. Run `node tools/validate-generation.mjs --artifacts-only` — both gates must pass.
> This is **eval-driven development**: write the failing eval first (step 3), then generate to make it pass (step 6). A passing eval confirms the LLM followed the rules.
---
## Changing an existing entity
**If the domain model changes** (new field, changed type, new FK, new enum):
1. Update `domain/toir.api.dsl` (enums, DTO attributes, `map` references).
2. Run `npm run generate:api-summary`.
3. Regenerate `server/prisma/schema.prisma`, run migration.
4. Regenerate the affected modules and resources (Steps 46).
**If only the API contract changes** (different nullability, new endpoint, different HTTP method):
1. Update `domain/toir.api.dsl` only.
2. Run `npm run generate:api-summary` to refresh `api-summary.json`.
3. Run Steps 46. No Prisma migration needed.
---
## Artifact traceability matrix
| Artifact | DSL source | Prompt rule | Validator check |
| ---------------------------------------------- | ---------------------- | ---------------------------------------------- | --------------------------------------- |
| `server/prisma/schema.prisma` | `domain/*.api.dsl` | `prompts/prisma-rules.md` | `§validateBuildChecks` (file exists) |
| `api-summary.json` | `domain/*.api.dsl` | — (deterministic) | `§validateBuildChecks` (freshness) |
| `server/src/modules/<e>/*.ts` | `domain/*.api.dsl` | `prompts/backend-rules.md` | `§validateApiDslCoverage` |
| `server/src/modules/<e>/dto/create-<e>.dto.ts` | `DTO.<E>Create` fields | `prompts/backend-rules.md §DTO-field-coverage` | `§validateApiDslCoverage` (field names) |
| `server/src/modules/<e>/dto/update-<e>.dto.ts` | `DTO.<E>Update` fields | `prompts/backend-rules.md §DTO-field-coverage` | `§validateApiDslCoverage` (field names) |
| `client/src/resources/<e>/<E>*.tsx` | `domain/*.api.dsl` | `prompts/frontend-rules.md` | `§validateApiDslCoverage` |
| `client/src/auth/keycloak.ts` | — (Tier 4 handwritten) | `prompts/auth-rules.md` | `§validateAuthChecks` |
| `toir-realm.json` | — (Tier 4 handwritten) | `prompts/auth-rules.md` | `§validateRealmChecks` |
| `docker-compose.yml` | — (Tier 4 handwritten) | `prompts/runtime-rules.md` | `§validateRuntimeContractChecks` |
---
## Verification commands reference
| Command | When to use |
| ----------------------------------------------------- | ------------------------------------------ |
| `npm run generate:api-summary` | After any change to `domain/*.api.dsl` |
| `npm run generate:openapi` | To regenerate OpenAPI 3.0.3 documentation |
| `node tools/validate-generation.mjs --artifacts-only` | After every generation run (required) |
| `node tools/validate-generation.mjs` | Before committing; requires installed deps |
| `node tools/validate-generation.mjs --run-runtime` | End-to-end; requires running DB |
---
## OpenRouter invocation
Set environment variables before any LLM-mode tool call:
```
OPENAI_API_KEY=<openrouter-key>
OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_MODEL=<model-id>
```
The `OPENAI_BASE_URL` variable is consumed by `tools/api-format-to-openapi/convert.mjs --mode llm`.
For agent-driven generation via Cursor or direct API calls, the standard workflow above
applies regardless of model provider.
---
## Auxiliary tools
### `tools/api-summary-to-openapi.mjs`
Generates `openapi.json` (OpenAPI 3.0.3) from `api-summary.json` deterministically.
This is a documentation/integration artifact, not part of the core generation pipeline.
```bash
npm run generate:openapi
```
### `tools/api-format-to-openapi/`
Auxiliary integration tool for external consumers using the `api-format` JSON schema.
Not connected to the main DSL pipeline. See `docs/AID_EXPORT_README.md` for details.