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>
9.8 KiB
Librodrome
Site vitrine du projet Le Librodrome — livre + chansons sur l'économie du don.
- Rôle : site éditorial en production sur
librodrome.orget hub d'intégration de l'écosystème (GrateWizard en popup ; liens libreDecisiondecision.librodrome.org, SejeteralO, sweetHome/LaPlanK dans le YAML desite/). 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
git pull --rebase origin main(règle canon ; voir § Contenu administrable)- Vérifier que
data/messages.ymlexiste — si absent, signaler avant toute opération - 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(extendsdansnuxt.config.ts, dépendancegit+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 surmain→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 litprocess.cwd()/data). Créé et semé à la main sur sonic par l'acteactes/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é parserver/plugins/seed-messages.tssidata/messages.ymlest absent - ⚠ Hors volume, rien ne persiste : éditions /admin de
site/etcontent/, 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/pagesliste,/admin/pages/{path}édite en YAML - Git sync (
server/utils/gitSync.ts, actif siADMIN_GIT_SYNC=true) : commit[admin] …puis push depuis le conteneur →git pull --rebaseobligatoire en début de session - ⚠ Constat du 26/10/2026 : la CI ne fixe pas
ADMIN_GIT_SYNC(défautfalsedans 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
- Appeler
useSeoPage()— jamaisuseHead({ title })seuluseSeoPage({ 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 }) - og:image : utiliser la couverture
/images/Couv-Economie-du-don.jpgpour les pages livre/musique ;og-default.png(logo §) pour les pages site générales - 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 dansapp.vue— ne pas redupliquer - Chapitres → og:type
articleviauseSeoPage({ type: 'article' }) - Sitemap : géré par
@nuxtjs/sitemap— ajouter manuellement les nouvelles routes statiques dansnuxt.config.ts > sitemap.urls
Analytics — Nouveaux composants
- Tout bouton CTA externe :
trackCta(label, url)depuisuseTracking() - Tout
<a target="_blank">: capturé automatiquement paruseScrollTracking()dansdefault.vue - Tout nouveau lecteur média : appeler
trackAudioPlay/trackPdfOpendepuisuseTracking()