8.5 KiB
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.mde 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) elegacy(v1, sola lettura, variabiliDB_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/uuidstabili, vocabolari come tabelle con colonna URI, auditcreated/modifiedpuliti.
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+providerautogenerati). - Distinzione
id(URI di retrieval dereferenziabile) vscanonical(id stabilekulturarvsdata.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 aprotokoll-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:
- 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. - Test + coverage (PHP via PHPUnit, frontend via Vitest) sui file staged.
- SonarQube: quality gate (
sonar.qualitygate.wait=true) deve passare. Token letto da$SONAR_TOKENo 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
.enve.git/sonar.tokennon 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 leMYSQL_*da questi (non introdurreDB_NAME/DB_USERparalleli).
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.platformincomposer.jsoncopre le estensioni mancanti sull'host (redis, gd, bcmath). - Frontend: il dev server di Vite gira nel container (
make up, stagedevelopment). Il sorgente è bind-montato (./frontend:/app) → HMR; inode_modulesnon 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 installsu host serve solo apackage-lock+ type-check IDE; dopo modifiche apackage.jsonrigenerare l'immagine conmake fe-rebuild. - I comandi artisan con servizi girano nel container (dove
db,redis,mailpitrisolvono) 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/updateonpm installdi propria iniziativa (l'ambiente host dell'utente è autoritativo). Se serve, usare i targetmakequi sopra. - Per artisan che tocca i servizi usare sempre
make artisan c="…"(container come UID host). - Non bind-montare mai
node_modulesin 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):
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:
strictattivo (vedifrontend/tsconfig.json); evitareany. - 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.