# CLAUDE.md — dyncoll.v2

Istruzioni per agenti AI (e umani) che lavorano su questo repository.

## 1. Cos'è

**dyncoll.v2** è la riscrittura della piattaforma *Dynamic Collections Plus*: catalogo e
curatela di collezioni digitali di reperti archeologici. Obiettivo trasversale della v2:
esporre i dati secondo lo standard di harvesting **KSamsök 2 Linked Art** del
Riksantikvarieämbetet (RAÄ) / SweDigArch, così da essere raccoglibili dall'aggregatore
nazionale svedese.

- v1 (legacy, in produzione): `../dyncoll.v1` — PHP MPA procedurale, MySQL.
- Documentazione di mapping (sorgente di verità): `../workfile/K2_tech_doc260430/shm-2b/`.
- Contesto e decisioni storiche: file di memoria in `../` (`memory/MEMORY.md` e collegati).

## 2. Stack

| Layer | Tecnologia |
|---|---|
| Backend | Laravel (PHP 8.3+/8.4), API-only |
| Auth | Sanctum + Fortify (2FA TOTP via `pragmarx/google2fa`) |
| Code/cache | Redis + Horizon |
| DB | MySQL 8.4 (schema definito da migration Laravel) |
| OpenAPI | **scegliere UNO** tra `darkaonline/l5-swagger` o `dedoc/scramble` (ora ci sono entrambi) |
| Linked Art | `ml/json-ld` (serializzazione) + `opis/json-schema` (validazione contro gli schema RAÄ) |
| Frontend | Vite + TypeScript, MPA con isole, Tailwind v4 + daisyUI, Leaflet |
| Docs | MkDocs Material (`documentation/`) |
| Qualità | SonarQube locale (`http://sonar.local`) |

## 3. Architettura dati (decisione chiave)

- **v2 ha il proprio DB**, schema da migration Laravel — NON si appoggia in read-only al DB di v1.
- I dati arrivano da v1 tramite un **comando ETL idempotente** (`php artisan v1:import`),
  non via dump/restore manuale (gli schemi divergono volutamente).
- Due connessioni in `config/database.php`: `mysql` (v2, scrivibile) e `legacy` (v1, **sola lettura**,
  variabili `DB_LEGACY_*`). Lo stesso ETL serve sia per i refresh di sviluppo sia per il cutover.
- Progettare lo schema v2 **già orientato al Linked Art**: identificatori `canonical`/`uuid` stabili,
  vocabolari come tabelle con colonna URI, audit `created/modified` puliti.

## 4. Mapping Linked Art — vincoli da rispettare

Quando si genera JSON-LD (vedi `mapping-protocol-key-facts` in memoria):

- Reperto → `HumanMadeObject` (E22). Documento annidato, **non** CRM piatto.
- Ogni record porta `RecordProvenance` (license, dateCreated/Modified, `ingestedAt`+`provider` autogenerati).
- Distinzione `id` (URI di retrieval dereferenziabile) vs `canonical` (id stabile `kulturarvsdata.se/...`).
- **Classi CRM astratte vietate** come tipo concreto: E1,E2,E3,E14–E19,E24,E28,E31,E32,E41,E63,E64,E70,E71,E72,E77,E83,E90,E92,E93,E96,E99.
- Ogni record generato DEVE validare contro `LinkedArtRecord-1.0.0.schema.json` (test obbligatorio).
- Endpoint Provider da implementare (path sotto `/linkedart/v1`):
  Tier 1 `/batch` (MUST), Tier 2 `/changes` + `/objects/{id}` (SHOULD), Tier 3 Search (MAY).
  Gli endpoint devono essere conformi a `protokoll-openapi/raa-linkedart-service-1.0.0.yaml`.

## 5. Regole di commit e qualità

Esiste un **pre-commit hook** (`.git/hooks/pre-commit`) che blocca il commit se:

1. **REUSE / licenze**: ogni file deve avere copyright + licenza SPDX (header o `REUSE.toml`).
   Licenza codice prevista: **EUPL-1.2**; docs/dati: **CC-BY-4.0**.
2. **Test + coverage** (PHP via PHPUnit, frontend via Vitest) sui file staged.
3. **SonarQube**: quality gate (`sonar.qualitygate.wait=true`) deve passare.
   Token letto da `$SONAR_TOKEN` o da `.git/sonar.token` (mai in chiaro nel repo).

Conseguenze pratiche:
- Aggiungere header SPDX a ogni nuovo file, altrimenti il commit fallisce.
- Mantenere/aggiornare i test: nuovo codice senza test fa fallire la quality gate.
- Backend e frontend devono essere up (`docker compose up`) perché l'hook gira i test nei container.

## 6. Segreti e ambiente

- `.env` e `.git/sonar.token` **non si versionano mai** (vedi `.gitignore`).
- Le variabili sono documentate in `.env.example`: aggiornarlo quando se ne aggiungono.
- Nomi DB canonici (Laravel): `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD`, `DB_ROOT_PASSWORD`.
  I compose devono mappare le `MYSQL_*` da questi (non introdurre `DB_NAME`/`DB_USER` paralleli).

## 6bis. Workflow host/container (IMPORTANTE — leggere prima di lanciare comandi)

Regola d'oro: **chi scrive file lo fa come utente host (`beppe`, UID 1000); mai mischiare
`node_modules` musl (container Alpine) e glibc (host).** Usare i target del `Makefile`.

| Operazione | Dove | Comando |
|---|---|---|
| Editare `composer.json` / `package.json` / codice | HOST | editor / tool agente (sempre beppe-owned) |
| Avviare lo stack di sviluppo (Vite HMR + backend + …) | CONTAINER | `make up` |
| `composer install/update/require` | HOST | `make be-install` / `make be-update` / `make composer c="…"` |
| Aggiornare deps frontend: edita `package.json` (host) → | HOST + rebuild img | `make fe-install` poi `make fe-rebuild` |
| artisan con servizi (`migrate`, `db:*`, `queue`, `tinker`, `v1:import`) | CONTAINER come UID host | `make migrate` / `make import` / `make artisan c="…"` |
| Permessi `storage` per php-fpm (www-data) | HOST, una tantum | `make permissions` |

Motivi e asimmetria backend/frontend:
- **Backend**: host PHP 8.4 == container; `vendor/` è codice PHP portabile ed è **bind-montato**
  (`./backend:/var/www/html`), quindi composer gira sull'host e il container lo vede subito.
  `config.platform` in `composer.json` copre le estensioni mancanti sull'host (redis, gd, bcmath).
- **Frontend**: il dev server di Vite gira **nel container** (`make up`, stage `development`). Il
  sorgente è bind-montato (`./frontend:/app`) → HMR; i `node_modules` **non** sono bind-montati ma
  vivono in un **volume anonimo** (`/app/node_modules`), così quelli musl dell'immagine schermano
  quelli glibc dell'host (mai mischiati). Si installano nella *build dell'immagine*, mai in un
  container in esecuzione. `npm install` su host serve solo a `package-lock` + type-check IDE; dopo
  modifiche a `package.json` rigenerare l'immagine con `make fe-rebuild`.
- I comandi artisan con servizi girano nel container (dove `db`, `redis`, `mailpit` risolvono) ma con
  `-u $(id -u):$(id -g)` per non creare file root-owned.

**Regole per l'agente AI:**
- Editare `composer.json`/`package.json`/codice coi propri tool (host, beppe-owned): OK.
- NON lanciare `composer install/update` o `npm install` di propria iniziativa (l'ambiente host
  dell'utente è autoritativo). Se serve, usare i target `make` qui sopra.
- Per artisan che tocca i servizi usare **sempre** `make artisan c="…"` (container come UID host).
- Non bind-montare mai `node_modules` in un container Alpine.

> Nota drift: l'IDE/type-check usa il Node dell'host (20.19/npm 9); il dev server di Vite usa il
> Node 24 dell'immagine. Conviene comunque allineare l'host a Node 22/24 LTS (Vite 8 richiede ≥ 20.19/22.12).

## 7. Comandi utili

Usare i target del `Makefile` (incapsulano la strategia host/container del §6bis):

```bash
make up                       # avvia l'intero stack dev (Vite HMR + backend + db + redis + docs + mailpit)
make migrate                  # artisan migrate (container come UID host)
make import                   # ETL da v1 (quando implementato)
make artisan c="test --coverage-clover=coverage.xml"   # test backend
make be-update                # composer update (host)
make fe-install               # npm install host (package-lock + type-check IDE)
make fe-rebuild               # ricostruisce l'immagine frontend dopo modifiche a package.json
make permissions              # fix permessi storage (una tantum, sudo)
make help                     # elenco completo
```

Frontend in dev: `make up` → Vite con HMR su `https://dyncoll-dev.local` (Traefik) o
`http://localhost:${APP_FRONTEND_DEVPORT}`. Le modifiche al sorgente si ricaricano da sole; per
nuove dipendenze npm: edita `package.json` → `make fe-install` → `make fe-rebuild`.

## 8. Convenzioni di codice

- PHP: PSR-12, formattato con **Laravel Pint** (`./vendor/bin/pint`).
- TypeScript: `strict` attivo (vedi `frontend/tsconfig.json`); evitare `any`.
- Commenti in italiano ammessi (coerenti col resto del repo).
- Non reintrodurre pattern v1 non sicuri: **query parametrizzate** sempre, mai interpolazione in SQL.

## 9. Stato dello scaffolding

Lo scaffolding iniziale è stato adattato da un altro progetto ("Valdarno Trails"/RMV) e
contiene residui da ripulire (riferimenti RMV in `frontend/vite.config.ts`,
`frontend/frontend.Dockerfile`, `frontend/nginx.conf`) e alcuni bug nei compose/Dockerfile.
Verificare sempre lo stato reale dei file prima di darli per buoni; vedere le note di review.
