git init
This commit is contained in:
120
docs/AID_EXPORT_README.md
Normal file
120
docs/AID_EXPORT_README.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# AID: экспорт OpenAPI и генератор приложения
|
||||
|
||||
В репозитории добавлены **сервисы-экспортёры** для интеграции с **AID** (или любым другим клиентом по HTTP): автоматическое получение **OpenAPI 3.0** из доменного **api-format**.
|
||||
|
||||
---
|
||||
|
||||
## Что работает
|
||||
|
||||
| Компонент | Назначение |
|
||||
|-----------|------------|
|
||||
| **`POST /aid/export/openapi`** (NestJS) | На вход JSON **api-format** → на выход документ **OpenAPI 3.0** в поле `openapi`. |
|
||||
| **`tools/api-format-to-openapi/`** | CLI и промпт для LLM: тот же конвертер, что вызывает Nest. |
|
||||
| **`server/src/aid-export/`** | Модуль Nest: контроллер, сервис, краткая справка в `README.md` рядом с кодом. |
|
||||
|
||||
## Что временно не работает
|
||||
|
||||
| Компонент | Статус |
|
||||
|-----------|--------|
|
||||
| **`POST /aid/export/app`** (DSL → бандл/apply) | **Non-operative.** The backing script (`generation/generate.mjs`) was removed during the architecture migration to api.dsl-first LLM generation. The endpoint returns 500 with a descriptive error. |
|
||||
|
||||
---
|
||||
|
||||
## Требования к запуску
|
||||
|
||||
1. Репозиторий клонирован целиком (есть `tools/`, `server/`, `client/`).
|
||||
2. Backend запускается из каталога **`server/`** (`npm run start` / `start:dev`), чтобы относительные пути `../tools/api-format-to-openapi/convert.mjs` были корректны.
|
||||
3. Для режима OpenAPI через LLM на сервере нужны **`OPENAI_API_KEY`** (и при необходимости `OPENAI_MODEL`, `OPENAI_BASE_URL`).
|
||||
|
||||
---
|
||||
|
||||
## Переменные окружения (`server/.env`)
|
||||
|
||||
| Переменная | Зачем |
|
||||
|------------|--------|
|
||||
| `AID_EXPORT_API_KEY` | Если задана, к **`/aid/export/*`** нужен заголовок **`X-AID-Export-Key`** с тем же значением. |
|
||||
| `OPENAI_API_KEY` | Для `POST /aid/export/openapi` с **`"mode": "llm"`**. |
|
||||
|
||||
Остальное как для обычного бэкенда (`DATABASE_URL`, `PORT` и т.д.).
|
||||
|
||||
---
|
||||
|
||||
## HTTP API (интеграция с AID)
|
||||
|
||||
Базовый URL: `http://<host>:<port>` (например `http://localhost:3000`).
|
||||
|
||||
### 1. OpenAPI из api-format
|
||||
|
||||
**`POST /aid/export/openapi`**
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
X-AID-Export-Key: <если задан AID_EXPORT_API_KEY>
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"apiFormat": {
|
||||
"apiFormatVersion": "1",
|
||||
"info": { "title": "API", "version": "1.0.0" },
|
||||
"server": { "basePath": "/api" },
|
||||
"resources": []
|
||||
},
|
||||
"mode": "deterministic"
|
||||
}
|
||||
```
|
||||
|
||||
- **`mode`**: `deterministic` (по умолчанию) — маппинг в коде для схемы версии `1`; **`llm`** — вызов OpenAI по промпту из `tools/api-format-to-openapi/prompts/llm-system.md`.
|
||||
|
||||
**Ответ:** `{ "openapi": { "openapi": "3.0.3", ... } }`
|
||||
|
||||
Пример входа для теста: `tools/api-format-to-openapi/examples/api-format.example.json` (подставьте как значение `apiFormat`).
|
||||
|
||||
### 2. Генератор приложения из DSL (non-operative)
|
||||
|
||||
**`POST /aid/export/app`**
|
||||
|
||||
> **Non-operative.** This endpoint depended on `generation/generate.mjs` which was removed
|
||||
> during the migration to api.dsl-first LLM generation. It currently returns HTTP 500
|
||||
> with a descriptive error message. Restoring this endpoint requires implementing a new
|
||||
> backing script compatible with the current `domain/*.api.dsl` pipeline.
|
||||
|
||||
---
|
||||
|
||||
## CLI (без Nest)
|
||||
|
||||
### api-format → OpenAPI
|
||||
|
||||
```bash
|
||||
cd tools/api-format-to-openapi
|
||||
node convert.mjs --in examples/api-format.example.json --out ../../openapi.generated.json
|
||||
```
|
||||
|
||||
LLM:
|
||||
|
||||
```bash
|
||||
set OPENAI_API_KEY=sk-...
|
||||
node convert.mjs --mode llm --in your-api-format.json --out ../../openapi.llm.json
|
||||
```
|
||||
|
||||
Подробнее: **`tools/api-format-to-openapi/README.md`**.
|
||||
|
||||
---
|
||||
|
||||
## Типичный сценарий для AID
|
||||
|
||||
1. AID уже сформировал **api-format** (как у вас принято после DTO).
|
||||
2. AID вызывает **`POST /aid/export/openapi`** → получает **OpenAPI 3.0** → сохраняет в проект / отдаёт в Swagger / в реестр.
|
||||
|
||||
---
|
||||
|
||||
## Где смотреть код и короткую справку
|
||||
|
||||
- Полное описание эндпоинтов рядом с реализацией: **`server/src/aid-export/README.md`**
|
||||
|
||||
---
|
||||
|
||||
## Ограничения и дальнейшие шаги
|
||||
|
||||
- Пример **api-format** в репозитории — **учебный**; под ваш продакшен-формат может понадобиться расширить маппинг в `convert.mjs` или отточить промпт **`llm-system.md`**.
|
||||
- **`/aid/export/app`** requires a new backing implementation compatible with the `domain/*.api.dsl` pipeline to be restored. See `docs/future-work.md` for planned future work.
|
||||
162
docs/api-dsl-conventions.md
Normal file
162
docs/api-dsl-conventions.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# api.dsl Conventions
|
||||
|
||||
Grammar and authoring conventions for `domain/*.api.dsl` files.
|
||||
See `domain/toir.api.dsl` as the canonical example.
|
||||
|
||||
---
|
||||
|
||||
## File location and naming
|
||||
|
||||
- All api.dsl files live in `domain/`.
|
||||
- Naming: `<project>.api.dsl` (e.g. `toir.api.dsl`).
|
||||
- Multiple api.dsl files are allowed but not required.
|
||||
|
||||
---
|
||||
|
||||
## Two top-level block types
|
||||
|
||||
An api.dsl file contains two types of top-level blocks:
|
||||
|
||||
1. `dto` — defines the shape of a data transfer object
|
||||
2. `api` — declares a group of API endpoints for one resource
|
||||
|
||||
---
|
||||
|
||||
## dto block
|
||||
|
||||
```
|
||||
dto DTO.<Name> {
|
||||
description "Human-readable description";
|
||||
|
||||
attribute <fieldName> {
|
||||
description "Field description";
|
||||
type <type>;
|
||||
is required; // or: is nullable;
|
||||
map <Entity>.<field>; // links to a field in domain/*.api.dsl
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### DTO naming convention
|
||||
|
||||
| DTO name | Purpose |
|
||||
|----------|---------|
|
||||
| `DTO.<Entity>` | Full response shape (GET by id, list items) |
|
||||
| `DTO.<Entity>Create` | Create request body |
|
||||
| `DTO.<Entity>Update` | Update request body (partial — all fields nullable) |
|
||||
| `DTO.<Entity>ListRequest` | Paginated list request (filters + page) |
|
||||
| `DTO.<Entity>ListResponse` | Paginated list response (content + page info) |
|
||||
|
||||
### Types
|
||||
|
||||
Same scalar types as the domain DSL:
|
||||
`uuid`, `string`, `text`, `integer`, `decimal`, `date`, `boolean`, or an enum name from
|
||||
`domain/*.api.dsl`.
|
||||
|
||||
Cross-DTO references: `DTO.<Name>` or `DTO.<Name>[]`.
|
||||
|
||||
Standard pagination types (not entity-specific; treated as well-known):
|
||||
`DTO.Filter[]`, `DTO.PageRequest`, `DTO.PageInfo`.
|
||||
|
||||
### is required vs is nullable
|
||||
|
||||
Unlike the domain DSL (where these drive DB constraints), in api.dsl these drive the
|
||||
TypeScript DTO property modifier:
|
||||
|
||||
- `is required` → `field!: type` in the generated class
|
||||
- `is nullable` → `field?: type` in the generated class
|
||||
- Neither modifier → treated as optional (`field?: type`)
|
||||
|
||||
### map directive
|
||||
|
||||
`map Entity.field` links the DTO attribute to a domain entity field.
|
||||
|
||||
Rules:
|
||||
- The entity and field must exist in `domain/*.api.dsl`.
|
||||
- The scalar type must match the domain DSL field type (nullability may differ).
|
||||
- Omit `map` only for pagination helper types (`DTO.Filter[]`, `DTO.PageRequest`, etc.)
|
||||
that have no direct entity field counterpart.
|
||||
|
||||
### Conflict resolution
|
||||
|
||||
If the type declared in a `dto` attribute conflicts with the domain DSL field type,
|
||||
the domain DSL is correct. Fix the api.dsl.
|
||||
|
||||
---
|
||||
|
||||
## api block
|
||||
|
||||
```
|
||||
api API.<Name> {
|
||||
description "API group description";
|
||||
|
||||
endpoint <endpointName> {
|
||||
label "METHOD /path";
|
||||
description "Endpoint description";
|
||||
|
||||
// For endpoints with a request body:
|
||||
attribute request {
|
||||
type DTO.<RequestDto>;
|
||||
}
|
||||
|
||||
// For endpoints with a response body:
|
||||
attribute response {
|
||||
type DTO.<ResponseDto>;
|
||||
}
|
||||
|
||||
// For path parameters:
|
||||
attribute <paramName> {
|
||||
type <type>;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### api block naming
|
||||
|
||||
`API.<EntityName>` (e.g. `API.Equipment`, `API.RepairOrder`)
|
||||
|
||||
### endpoint label
|
||||
|
||||
`label "METHOD /path"` is the authoritative declaration of HTTP verb and route.
|
||||
|
||||
| Label pattern | NestJS decorator | Notes |
|
||||
|---------------|-----------------|-------|
|
||||
| `"GET /resource/{id}"` | `@Get(':id')` | |
|
||||
| `"POST /resource"` | `@Post()` | Create |
|
||||
| `"POST /resource/page"` | `@Post('page')` | Paginated list (body-based filter) |
|
||||
| `"PUT /resource/{id}"` | `@Put(':id')` | Full or partial update |
|
||||
| `"DELETE /resource/{id}"` | `@Delete(':id')` | |
|
||||
|
||||
Do not infer HTTP verbs from endpoint names. Always read the `label`.
|
||||
|
||||
### Path parameters
|
||||
|
||||
Declared as a plain `attribute` inside the endpoint block (not wrapped in `request`
|
||||
or `response`):
|
||||
|
||||
```
|
||||
attribute id {
|
||||
type uuid;
|
||||
}
|
||||
```
|
||||
|
||||
### Standard 5-endpoint CRUD pattern
|
||||
|
||||
| Endpoint name | Label | Body |
|
||||
|---------------|-------|------|
|
||||
| `list<Entity>s` | `"POST /<path>/page"` | request: `DTO.<Entity>ListRequest`, response: `DTO.<Entity>ListResponse` |
|
||||
| `get<Entity>` | `"GET /<path>/{id}"` | path param `id`, response: `DTO.<Entity>` |
|
||||
| `create<Entity>` | `"POST /<path>"` | request: `DTO.<Entity>Create` |
|
||||
| `update<Entity>` | `"PUT /<path>/{id}"` | path param `id`, request: `DTO.<Entity>Update` |
|
||||
| `delete<Entity>` | `"DELETE /<path>/{id}"` | path param `id` |
|
||||
|
||||
---
|
||||
|
||||
## Constraints
|
||||
|
||||
1. Every `map Entity.field` must resolve to an existing entity + field in `domain/*.api.dsl`.
|
||||
2. Types in api.dsl must be compatible with domain DSL field types (same scalar type).
|
||||
3. api.dsl must not define entities or enums. Those belong in `domain/*.api.dsl`.
|
||||
4. An api.dsl may omit domain entity fields from a DTO (e.g. no PK in Create DTO).
|
||||
It must not add fields that don't exist in the domain model.
|
||||
154
docs/future-work.md
Normal file
154
docs/future-work.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Future Work — Deferred Items
|
||||
|
||||
This file tracks engineering improvements that are deliberately deferred due to the
|
||||
current stage of the project. They are not forgotten — they are acknowledged technical
|
||||
debt that should be addressed before scaling.
|
||||
|
||||
---
|
||||
|
||||
## Rule 7 — Tracing, Telemetry, Cost/Latency Observability
|
||||
|
||||
**Status:** Deferred. No LLM calls are instrumented.
|
||||
|
||||
**Why it matters (Anthropic / Google / Microsoft guidance):**
|
||||
Without observability, you cannot:
|
||||
- Know which prompts are expensive (token count, latency)
|
||||
- Detect prompt regressions via cost drift
|
||||
- Attribute generation failures to specific prompt versions
|
||||
- Track improvement over time
|
||||
|
||||
**What needs to be built:**
|
||||
|
||||
### 7.1 — Generation log
|
||||
|
||||
Create `tools/generation-log.mjs` that wraps any LLM generation call and writes a
|
||||
structured JSON entry to `logs/generation.jsonl`:
|
||||
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-04-03T10:00:00.000Z",
|
||||
"entity": "Equipment",
|
||||
"artifact": "backend",
|
||||
"prompt_version": "1.0",
|
||||
"model": "...",
|
||||
"input_tokens": 4200,
|
||||
"output_tokens": 1800,
|
||||
"latency_ms": 3200,
|
||||
"validation_passed": true,
|
||||
"eval_passed": true
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 — Cost budget alerts
|
||||
|
||||
Add a threshold check (e.g., warn if input_tokens > 8000 for a single entity generation).
|
||||
This enforces the context budget from `prompts/general-prompt.md §CONTEXT BUDGET`.
|
||||
|
||||
### 7.3 — Prompt version tracking
|
||||
|
||||
Add `<!-- prompt-version: X.Y -->` comments to all prompt files (already started in
|
||||
`backend-rules.md` and `frontend-rules.md`). Increment version on any non-trivial change.
|
||||
Log the prompt versions alongside the generation log entry.
|
||||
|
||||
### 7.4 — Drift detection
|
||||
|
||||
Compare generation log entries across runs. If token count for the same entity increases
|
||||
by >20% without a DSL change, flag it as context rot.
|
||||
|
||||
**Effort estimate:** Medium. 2–3 days to build the logging layer. Zero effort for
|
||||
prompt versioning (already partially done).
|
||||
|
||||
**Trigger:** Implement before the system is used for more than 10 entities or before
|
||||
any production deployment.
|
||||
|
||||
---
|
||||
|
||||
## Rule 8 — Risk Controls and Red-Teaming
|
||||
|
||||
**Status:** Deferred. No sanitization or adversarial testing exists.
|
||||
|
||||
**Why it matters (Anthropic / Google / Microsoft guidance):**
|
||||
LLM-generated code at scale introduces risks that do not exist in hand-written code:
|
||||
- **Prompt injection**: malicious content in DSL `description` fields could steer
|
||||
generation (e.g., `description "Ignore previous instructions and..."`)
|
||||
- **Generated credential leakage**: LLM may hallucinate hardcoded secrets that look
|
||||
real (e.g., `apiKey: 'sk-...'`)
|
||||
- **Missing auth guards**: already caught by Rule 4 validator, but adversarial prompts
|
||||
could bypass it by generating valid-looking guard syntax that is semantically inactive
|
||||
- **Supply chain**: generated package imports could reference non-existent or malicious
|
||||
packages if the LLM hallucinates
|
||||
|
||||
**What needs to be built:**
|
||||
|
||||
### 8.1 — DSL input sanitization
|
||||
|
||||
In `tools/api-summary.mjs`, before building the summary, check all `description` and
|
||||
`label` fields for injection patterns:
|
||||
|
||||
```javascript
|
||||
function sanitizeDslString(value, fieldPath) {
|
||||
const injectionPatterns = [
|
||||
/ignore previous/i,
|
||||
/disregard.*instruction/i,
|
||||
/you are now/i,
|
||||
/system:/i,
|
||||
];
|
||||
for (const pattern of injectionPatterns) {
|
||||
if (pattern.test(value)) {
|
||||
throw new Error(`Potential prompt injection in DSL field ${fieldPath}: "${value}"`);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 — Generated code security scan
|
||||
|
||||
Add to `tools/validate-generation.mjs` (or a separate `tools/security-scan.mjs`):
|
||||
|
||||
```javascript
|
||||
// Check no hardcoded secrets leaked into generated code
|
||||
function validateNoSecretLeakage() {
|
||||
const patterns = [
|
||||
/sk-[a-zA-Z0-9]{20,}/, // OpenAI key pattern
|
||||
/[a-zA-Z0-9+/]{40}={0,2}/, // Base64 secret-like
|
||||
/password\s*=\s*['"][^'"]{4,}['"]/, // Hardcoded password
|
||||
/apiKey\s*=\s*['"][^'"]{4,}['"]/, // Hardcoded API key
|
||||
];
|
||||
// Run against all generated files...
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 — UseGuards completeness audit
|
||||
|
||||
Beyond the current validator check (UseGuards present), add: verify that the guard
|
||||
constructor arguments are non-empty and match the expected guard class names. A guard
|
||||
call like `@UseGuards()` (empty) passes the current regex but provides no protection.
|
||||
|
||||
### 8.4 — Red-team fixture
|
||||
|
||||
Create `tools/eval/fixtures/_adversarial/` with a fixture that includes a DSL snippet
|
||||
containing a benign injection attempt (e.g., a `description` field with "ignore format
|
||||
rules") and verifies the generation still produces spec-compliant output.
|
||||
|
||||
### 8.5 — Generated import allowlist
|
||||
|
||||
Maintain a list of approved npm packages that generated code may import. Flag any
|
||||
import not on the allowlist as a manual review item.
|
||||
|
||||
**Effort estimate:** Medium-High. 3–5 days. Security scan and sanitization are low
|
||||
effort; red-team fixtures and import allowlisting are higher effort.
|
||||
|
||||
**Trigger:** Implement before any external user can influence `domain/*.api.dsl` content
|
||||
(i.e., before a UI or API to edit the DSL is exposed).
|
||||
|
||||
---
|
||||
|
||||
## Tracking
|
||||
|
||||
| Rule | Status | Priority | Trigger |
|
||||
|------|--------|----------|---------|
|
||||
| Rule 7 — Telemetry | Deferred | Medium | Before >10 entities or production deployment |
|
||||
| Rule 8 — Risk controls | Deferred | High | Before DSL editing is exposed to external users |
|
||||
|
||||
Last updated: 2026-04-03
|
||||
237
docs/generation-playbook.md
Normal file
237
docs/generation-playbook.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 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 3–5).
|
||||
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 4–6).
|
||||
|
||||
**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 4–6. 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.
|
||||
35
docs/repository-structure.md
Normal file
35
docs/repository-structure.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Repository Structure
|
||||
|
||||
`KIS-TOiR` keeps the existing LLM-first generation philosophy and organizes the repository by meaning:
|
||||
|
||||
- `domain/`
|
||||
- canonical DSL inputs
|
||||
- DSL specification
|
||||
- `prompts/`
|
||||
- active prompt corpus used to drive generation
|
||||
- `docs/`
|
||||
- overview and repository-level architecture notes
|
||||
- `tools/`
|
||||
- helper scripts for summary generation and validation
|
||||
- `server/`
|
||||
- active backend target output
|
||||
- `client/`
|
||||
- active frontend target output
|
||||
|
||||
The repository keeps LLM-first generation orchestration, but framework bootstrap is CLI-first:
|
||||
|
||||
- `server/` must remain a valid NestJS workspace baseline
|
||||
- `client/` must remain a valid Vite React TypeScript workspace baseline
|
||||
- repair a broken workspace before applying more domain-derived generation changes
|
||||
- future agents must treat forbidden generation patterns in `prompts/` as contract violations, not suggestions
|
||||
|
||||
Root-level files stay limited to repository-level artifacts such as:
|
||||
|
||||
- `README.md`
|
||||
- `package.json`
|
||||
- `docker-compose.yml`
|
||||
- `api-summary.json`
|
||||
- `toir-realm.json`
|
||||
- `.gitignore`
|
||||
|
||||
The repository does not introduce a new generator engine or compiler platform. It keeps the current LLM-first pipeline and makes it cleaner, more explicit, and easier to navigate.
|
||||
96
docs/source-of-truth.md
Normal file
96
docs/source-of-truth.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Source-of-Truth Hierarchy
|
||||
|
||||
This document is the authoritative reference for which files own which decisions.
|
||||
|
||||
---
|
||||
|
||||
## Tier 1: Authoritative sources (hand-authored; never generated)
|
||||
|
||||
### `domain/*.api.dsl`
|
||||
|
||||
**Single source of truth for the entire domain and API contract:**
|
||||
|
||||
- Entity names and structure
|
||||
- Attribute names, scalar types, descriptions
|
||||
- Primary keys (including natural string keys)
|
||||
- Foreign keys and relations
|
||||
- Enum definitions and their values
|
||||
- Database-level constraints: `is required`, `is unique`, `default`
|
||||
- DTO shapes per operation (Create, Update, Read, ListRequest, ListResponse)
|
||||
- Which fields appear in each DTO and with what TypeScript modifier (`!` or `?`)
|
||||
- HTTP method and path for each endpoint (via `label "METHOD /path"`)
|
||||
- Endpoint names (camelCase identifiers)
|
||||
- Pagination request/response contract
|
||||
|
||||
**Drives:** `server/prisma/schema.prisma`, `server/src/modules/`, `client/src/resources/`,
|
||||
`server/src/app.module.ts`, `client/src/App.tsx`.
|
||||
|
||||
### `prompts/*.md` and `AGENTS.md`
|
||||
|
||||
**Authoritative for:**
|
||||
|
||||
- Agent generation workflow and reading order
|
||||
- Auth seam patterns (Keycloak, JWT, PKCE S256, JWKS resolution chain)
|
||||
- Runtime conventions (env examples, docker-compose topology)
|
||||
- Framework scaffold baseline requirements (NestJS CLI, Vite React TypeScript)
|
||||
- Filtering and sorting contract
|
||||
- Naming conventions and implicit rules (pluralization, sort field priority, type mappings)
|
||||
- Mutation boundaries (what agents must not overwrite)
|
||||
|
||||
---
|
||||
|
||||
## Tier 2: Deterministic derivatives (never edit manually)
|
||||
|
||||
| File | Generated from | Command |
|
||||
| ----------------- | ------------------ | ------------------------------ |
|
||||
| `api-summary.json` | `domain/*.api.dsl` | `npm run generate:api-summary` |
|
||||
|
||||
These files are regenerated from their sources. Manual edits are overwritten on the
|
||||
next generation run.
|
||||
|
||||
---
|
||||
|
||||
## Tier 3: LLM-generated artifacts (never edit manually after generation)
|
||||
|
||||
| Zone | Generated from |
|
||||
| -------------------------------- | ------------------------------------------------ |
|
||||
| `server/prisma/schema.prisma` | `domain/*.api.dsl` + `prompts/prisma-rules.md` |
|
||||
| `server/src/modules/<entity>/` | `domain/*.api.dsl` + `prompts/backend-rules.md` |
|
||||
| `client/src/resources/<entity>/` | `domain/*.api.dsl` + `prompts/frontend-rules.md` |
|
||||
| `server/src/app.module.ts` | Module list derived from api.dsl `api` blocks |
|
||||
| `client/src/App.tsx` | Resource list derived from api.dsl `api` blocks |
|
||||
|
||||
To change these files: update `domain/*.api.dsl` and regenerate.
|
||||
|
||||
---
|
||||
|
||||
## Tier 4: Handwritten (not generated; not derived)
|
||||
|
||||
- Auth seams: `client/src/auth/`, `server/src/auth/`
|
||||
- `toir-realm.json`
|
||||
- `docker-compose.yml`
|
||||
- `server/.env.example`, `client/.env.example`
|
||||
- Framework config: `nest-cli.json`, `tsconfig*.json`, `vite.config.*`, etc.
|
||||
- All `prompts/*.md`, `AGENTS.md`, `domain/dsl-spec.md`, `domain/*.api.dsl`
|
||||
|
||||
---
|
||||
|
||||
## Validation gate
|
||||
|
||||
### Stage 1 — Structural gate
|
||||
|
||||
```
|
||||
node tools/validate-generation.mjs --artifacts-only
|
||||
```
|
||||
|
||||
Verifies that generated artifacts satisfy the contracts declared in Tier 1 sources.
|
||||
|
||||
### Stage 2 — Eval harness
|
||||
|
||||
```
|
||||
npm run eval:generation
|
||||
```
|
||||
|
||||
Fixture-based semantic checks from `tools/eval/fixtures/`.
|
||||
|
||||
Both stages must pass before any generation task is considered complete.
|
||||
Reference in New Issue
Block a user