Files
librodrome/CLAUDE.md
T
YvvandClaude Opus 5.5 4057f50bd3
ci/woodpecker/push/woodpecker Pipeline was successful
actes : persistance des messages — exécuté et vérifié en production
Section d'exécution du dossier (sorties relevées par orch, session dev-65),
CLAUDE.md corrigé (volume librodrome-data sur /app/data, m4 de la relecture),
HANDOFF : état en ligne et décisions restant à Yvv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:50:34 +01:00

202 lines
9.8 KiB
Markdown
Raw 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.
# Librodrome
Site vitrine du projet Le Librodrome — livre + chansons sur l'économie du don.
- **Rôle** : site éditorial en production sur `librodrome.org` et hub d'intégration
de l'écosystème (GrateWizard en popup ; liens libreDecision
`decision.librodrome.org`, SejeteralO, sweetHome/LaPlanK dans le YAML de `site/`).
Cap : plateforme éditoriale de bassin de vie (autonomie collective, économie
monnaie libriste, boîte à outils, événementiel).
- **Port** : 3000 (dev), 3099 (smoke du build). **Dépôt** : `origin` =
`gitea@git.open.us.org:EHV/librodrome.git` ; `main` = production.
## Protocole de début de session
1. `git pull --rebase origin main` (règle canon ; voir § Contenu administrable)
2. Vérifier que `data/messages.yml` existe — si absent, signaler avant toute opération
3. Si l'objectif de la session n'est pas précisé, le demander
## Stack
- Nuxt 4 (Vue 3, TypeScript, Nitro) + Nuxt Content, Pinia, UnoCSS, VueUse, Nuxt Image
- Layer `@yvv/nuxt-base` (`extends` dans `nuxt.config.ts`, dépendance `git+https` épinglée `#v0.1.0`)
- Icônes : Lucide + Phosphor (via @iconify-json) ; pnpm épinglé 10.29.3 (`packageManager`)
- Audience : Umami (`nuxt-umami`)
- Déploiement : Woodpecker (`.woodpecker.yml`) — push sur `main` →
`docker compose -f docker/docker-compose.yml -f docker/docker-compose.fabio.yml up --build -d`,
**sans étape de test**. Secrets CI : `NUXT_ADMIN_PASSWORD`, `NUXT_ADMIN_SECRET`.
Healthcheck de l'image : `/api/health`.
## Structure
```
app/
pages/
numerique/ # index + [slug]
economique/ # index, monnaie-libre, commande, productions-collectives
modele-eco/ # sommaire + chapitres [slug]
citoyenne/ # index + [slug]
en-musique/ # player audio
admin/ # back-office (pages/, book/, songs, messages, media)
components/
book/Actions.vue # boutons partagés (player, PDF, chapitres, commande)
home/ # BookSection, AxisBlock, AxisGrid, HeroSection, HomeMessages
admin/, player/, song/, ui/
composables/ # useAudioPlayer, useBookData, useGrateWizard, usePageContent
stores/palette.ts # 4 palettes saisonnières (été par défaut, persisté localStorage)
assets/css/main.css # UnoCSS + overrides light mode
site/
pages/ # Contenu YAML administrable par section (sous-dossiers)
site.yml # Config globale (nav, footer, GrateWizard)
bookplayer.config.yml # Config player/chapitres
data/
messages.yml # Runtime — prod : volume nommé librodrome-data sur /app/data — JAMAIS dans git
server/
api/content/ # GET pages YAML (chemins imbriqués)
api/admin/content/ # PUT pages YAML + liste
api/messages/ # GET (publiés) + POST (nouveau message)
api/admin/messages/ # GET tous + PUT (type, reply, published) + DELETE
utils/content.ts # readDataYaml/writeDataYaml (data/) + readYaml/writeYaml (site/)
middleware/redirects.ts # 301 : /gestation, /modele-eco, /decision, /lire
docker/
Dockerfile, docker-compose.yml, docker-compose.dev.yml
```
## Données runtime (CRITIQUE)
- `data/messages.yml` : en prod, volume nommé `librodrome-data` (`external: true`) monté
sur `/app/data` (l'image tourne dans `/app`, le serveur lit `process.cwd()/data`).
Créé et semé à la main sur sonic par l'acte `actes/2026-10-26-persistance-messages.md` ;
un déploiement sans ce volume échoue au lieu de repartir de la graine.
- Persisté entre les rebuilds depuis le 26/10/2026 — **jamais écrasé par les commits ni
par le déploiement** (avant, il ne l'a jamais été : voir le dossier d'acte)
- Structure message : `{ id, author, email, text, type, published, createdAt, reply: { text, publishedAt } | null }`
- Types : `reaction` (ancien, affiché "Réaction", plus proposé dans les formulaires) | `question` | `suggestion` | `retour`
- En dev local : `<racine>/data/messages.yml`
- Graine : `site/messages.yml` (versionné) copié par `server/plugins/seed-messages.ts`
si `data/messages.yml` est absent
- ⚠ Hors volume, rien ne persiste : éditions /admin de `site/` et `content/`, médias
envoyés par /admin (`/app/public`, jamais servis). Décisions d'Yvv en attente (HANDOFF).
- **Avant toute migration de chemin ou écriture sur data/ : demander confirmation**
## Intégration GrateWizard
- URL dev : `app/app.config.ts` → `localhost:3001`
- URL prod : `https://gratewizard.axiom-team.fr`
## Contenu administrable
- YAML dans `site/pages/` par section ; API supporte les chemins imbriqués
- Admin : `/admin/pages` liste, `/admin/pages/{path}` édite en YAML
- Git sync (`server/utils/gitSync.ts`, actif si `ADMIN_GIT_SYNC=true`) : commit
`[admin] …` puis push depuis le conteneur → `git pull --rebase` obligatoire en début de session
- ⚠ Constat du 26/10/2026 : la CI ne fixe pas `ADMIN_GIT_SYNC` (défaut `false` dans le
compose), l'image de prod n'embarque pas `.git`, et l'historique ne contient aucun
commit `[admin]`. Les éditions /admin en prod ne reviennent donc pas dans git
(voir HANDOFF).
## Commandes
```bash
pnpm dev # Dev server :3000
pnpm build # Build production
PORT=3099 node .output/server/index.mjs # Test build prod (toujours avant commit)
```
## Vérifier
Niveaux V0 à V4 : gabarit `harness/skills/boucles/_gabarit.md`. Seuls V0 et V3 existent :
aucun test automatisé, ni lint ni typecheck (aucun script, pas de `vue-tsc`).
**V0 — build + smoke** (vert le 26/10/2026, build ≈ 1 min)
```bash
date; pnpm build # exit 0
NUXT_ADMIN_PASSWORD=jetable NUXT_ADMIN_SECRET=jetable PORT=3099 node .output/server/index.mjs &
sleep 3; for p in / /api/health /api/admin/messages; do
curl -s -o /dev/null -w "$p %{http_code}\n" http://127.0.0.1:3099$p; done # 200, 200, 401
kill $(lsof -ti :3099)
```
Lancé depuis la racine, le serveur lit et écrit le vrai `data/` : aucun POST pendant le
smoke. Le build ne lit pas `.env` : sans `NUXT_ADMIN_SECRET`, l'API admin répond 503.
**V1 — invariants (à construire)** : vitest sur les API messages (POST/GET/DELETE),
admin content (PUT YAML → fichier écrit, format préservé), auth (mauvais mot de passe →
401). `data/` et `site/` se résolvent depuis `process.cwd()` : lancer depuis un dossier jetable.
**V2 — parcours (à construire)** : Playwright, chromium bundlé (HANDOFF, Pièges), mobile +
desktop, ambiances claire et sombre : accueil sans erreur console, les 3 axes, lecteur PDF,
player, formulaire message (data jetable), login admin. Captures dans `/tmp/librodrome-shots/`.
**V3 — données**
```bash
sha256sum data/messages.yml # avant et après le run : identiques
git status --short # rien sous data/ ni .env
git log --oneline origin/main..HEAD # ce que le prochain push mettrait en ligne
```
**V4** : rapport avec sorties réelles et captures ; tout niveau sauté est annoncé.
**Trous** : la CI déploie sans gate (un commit cassé part en prod) ; V1 et V2 inexistants.
Prochaines briques : gate build + tests dans `.woodpecker.yml` avant le `up`, puis V1 et V2
exposés en `pnpm verify`.
**Gates humains (anneau 1)** : push de `main` (= mise en ligne) ; suppressions admin
(`server/api/admin/messages/[id].delete.ts`, `media/[...path].delete.ts`,
`chapters/[slug].delete.ts`) ; toute écriture sous `site/` ou `content/book/` (contenu de
prod) ; `data/messages.yml` ; lien de commande du livre (Bookelis,
`site/pages/economique/commande.yml`, aucun paiement sur le site) ; auth admin
(`server/middleware/admin-auth.ts`) et secrets `NUXT_ADMIN_*`.
## Conventions
- CSS via UnoCSS + variables CSS palettes ; composants Vue SFC `<script setup lang="ts">`
- Shadoks SVG inline thématiques sur chaque page (hidden mobile, opacity 0.18–0.28)
- Hexagramme 益 (#42 Yi, Augmentation) dans `layouts/default.vue`
- Signature § (logo calligraphique SVG gradient) dans `TheHeader.vue` — ne pas modifier sans demander
## SEO & Recherche IA — Checklist permanente
### À chaque nouvelle page Vue
1. **Appeler `useSeoPage()`** — jamais `useHead({ title })` seul
```ts
useSeoPage({
title: 'Titre page — Le Librodrome', // 50–60 chars
description: 'Description...', // 120–155 chars
image: '/images/og-specifique.jpg', // optionnel, sinon og-default.png (logo §)
type: 'website' | 'article' | 'book', // défaut: website
})
```
2. **og:image** : utiliser la couverture `/images/Couv-Economie-du-don.jpg` pour les pages livre/musique ; `og-default.png` (logo §) pour les pages site générales
3. **type: 'article'** pour les chapitres du modele-eco ; **type: 'book'** pour les index livre
### À chaque nouveau fichier YAML dans `site/pages/`
Ajouter le bloc `seo:` en tête :
```yaml
seo:
title: "Titre SEO — 50-60 chars max"
description: "Description 120-155 chars, avec mots-clés naturels"
# image: /images/og-specifique.jpg # optionnel
# keywords: [monnaie libre, TRM, économie du don]
```
### Règles description SEO
- Commence par un verbe d'action ou une affirmation forte
- Contient les mots-clés : économie du don, monnaie libre, TRM, June, bassin de vie, autonomie
- 120–155 caractères (ni trop court, ni tronqué)
- Différente du titre et du H1
### JSON-LD automatique
- JSON-LD `Organization` + `Book` : injecté globalement dans `app.vue` — ne pas redupliquer
- Chapitres → og:type `article` via `useSeoPage({ type: 'article' })`
- Sitemap : géré par `@nuxtjs/sitemap` — ajouter manuellement les nouvelles routes statiques dans `nuxt.config.ts > sitemap.urls`
### Analytics — Nouveaux composants
- Tout bouton CTA externe : `trackCta(label, url)` depuis `useTracking()`
- Tout `<a target="_blank">` : capturé automatiquement par `useScrollTracking()` dans `default.vue`
- Tout nouveau lecteur média : appeler `trackAudioPlay` / `trackPdfOpen` depuis `useTracking()`