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

9.8 KiB
Raw Blame History

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

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)

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

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
    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 :

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()