# Templating — rakennuspalaset koodigeneroinnissa

## Perusperiaate

Kielimalli päättää **mitä** rakennetaan (entiteetit, kentät, tyypit, yhteydet).
Template-funktiot päättävät **miten** se rakennetaan (importit, engine setup, testikonfiguraatio).

```
Projektikuvaus → LLM → JSON-speksi → Templateit → Koodi → Validointi
```

LLM:n kontribuutio on yksi JSON-rakenne. Kaikki muu on determinististä —
sama speksi tuottaa aina saman koodin.

## Miksi tämä toimii

Pienen kielimallin (0.5B–7B) vahvuudet ja heikkoudet ovat epäsymmetrisiä:

| Tehtävä | LLM:n kyky | Ratkaisu |
|---------|-----------|----------|
| Tunnista entiteetit kuvauksesta | Hyvä | LLM tekee |
| Valitse kenttätyypit | Hyvä | LLM tekee |
| Muista importit oikein | Huono | Template tekee |
| SQLite connect_args | Huono | Template tekee |
| Testikonfiguraatio | Huono | Template tekee |
| Dockerfile-rakenne | Huono | Template tekee |

Annetaan mallin tehdä se missä se on hyvä. Hoidetaan loput mekaanisesti.

## JSON-speksi

Kielimallin ainoa tuotos on JSON joka kuvaa projektin rakenteen:

```json
{
  "project_name": "library-app",
  "entities": [
    {
      "name": "Author",
      "table_name": "authors",
      "fields": [
        {"name": "name", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null}
      ]
    },
    {
      "name": "Book",
      "table_name": "books",
      "fields": [
        {"name": "title", "sa_type": "String(255)", "py_type": "str", "nullable": false, "default": null},
        {"name": "author_id", "sa_type": "Integer", "py_type": "int", "nullable": false, "default": null}
      ]
    }
  ],
  "relationships": [
    {"from": "Book", "field": "author_id", "to": "Author", "type": "many-to-one"}
  ],
  "extra_imports": []
}
```

Speksin laatu ratkaisee kaiken. Hyvä speksi → hyvä projekti. Huono speksi →
teknisesti toimiva mutta sisällöllisesti väärä projekti.

## Architect-promptin rooli

Architect-agentti (JSON-speksin generoija) on kriittisin kohta koko pipelinessa.
Sitä ohjataan neljällä keinolla:

1. **Chain-of-thought** — malli miettii ensin entiteetit, sitten kentät,
   sitten yhteydet, vasta lopuksi JSON
2. **Domain-esimerkit** — Todo, verkkokauppa, blogi — malli näkee miltä
   hyvä speksi näyttää eri domaineissa
3. **Anti-patternit** — turhat ID-kentät, Enum-tyypit, suomenkieliset nimet
4. **Yhteyssäännöt** — jokainen `_id`-kenttä tarvitsee relationship-merkinnän

Isompi malli tässä yhdessä kohdassa parantaisi kaikkien projektien laatua.

## Templateit

Jokainen template on funktio joka ottaa speksin ja palauttaa koodia:

```
tmplModels(spec)     → models.py      (SQLAlchemy, ForeignKey, relationship)
tmplSchemas(spec)    → schemas.py     (Pydantic Create/Response/Detail)
tmplMain(spec)       → main.py        (FastAPI CRUD + nested endpoints + FK-validointi)
tmplTests(spec)      → test_main.py   (pytest + TestClient + helper-funktiot)
tmplPyproject(spec)  → pyproject.toml (PEP 621)
tmplDockerfile()     → Dockerfile     (uv + non-root user)
```

Templateit generoivat automaattisesti:
- ForeignKey-constraintit ja relationship()-määrittelyt
- Nested endpointit (`GET /authors/{id}/books/`)
- FK-validointi (404 jos parent-entiteettiä ei ole)
- Detail-schemat (Book + author-data mukana)
- Test-helperit jotka luovat parent-entiteetit ensin
- Bad FK -testit (varmistaa että orpo-validointi toimii)

## Validointi

Generoitu koodi validoidaan mekaanisesti ennen käyttöä:

- Syntaksitarkistus (AST parse)
- Projektin sisäiset importit (löytyykö nimi lähdetiedostosta)
- SQLite connect_args
- Relatiiviset importit (kielletty)
- Testien rakenne (ei saa kopioida appia)
- pyproject.toml (ei poetryä)
- Dockerfile (ei poetryä, uv cache -oikeudet)

Docker-testi ajaa koko projektin: build → pytest → API smoke test.

## Rajoitukset

Templateit kattavat rakenteellisesti tunnetut projektit:

| Stack | Kattavuus |
|-------|-----------|
| FastAPI + SQLAlchemy CRUD | Toimii hyvin |
| Streamlit + DuckDB dashboard | Toimii hyvin |
| Muu | Ei templatea → ei toimi |

**Ei kata:**
- Custom business-logiikka (algoritmit, laskenta, ML)
- Epätyypilliset arkkitehtuurit (WebSocket, graafit, tapahtumapohjaiset)
- Frontend-sovellukset (React, Vue)
- Mikä tahansa mitä template ei tunne

Arvio: templateit kattavat ~20% kaikista mahdollisista projekteista, mutta juuri
sen 20% mitä opiskelu- ja prototyyppiympäristöissä tarvitaan useimmin.

## Laajentaminen

Uuden stackin lisääminen vaatii:

1. Uudet template-funktiot (käsityö, ~200–400 riviä per stack)
2. JSON-speksin laajennos (uudet kentät jos tarvitaan)
3. Validointisäännöt uudelle stackille
4. Docker-testikonfiguraatio

Jokainen template on staattinen — se ei opi eikä sopeudu. Kattavuus kasvaa
vain kirjoittamalla lisää templateja.

## Hybridi: seuraava askel

Paras lopputulos syntyisi yhdistelmällä:

```
Speksi → Template (runko) → LLM (business-logiikka) → Validointi
```

Template tuottaa toimivan CRUD-pohjan. LLM lisää domain-kohtaisen logiikan
pienissä palasissa (yksi funktio kerrallaan). Mekaaninen validointi
tarkistaa jokaisen lisäyksen.

Tämä palauttaa LLM:n epäluotettavuuden takaisin peliin, mutta rajattuna:
virheet ovat paikallisia (yksi funktio) eivätkä rakenteellisia (koko projekti).
