note this well
notabene
Documentation complète
Guide
Qu'est-ce que notabene ?
Qu’est-ce que notabene ?
nota bene — la marque de marge qui signifie « à bien noter ».
notabene rend le Markdown/MDX de votre repo comme un site navigable avec des commentaires de revue directement sur la page, et fournit le protocole de revue humain↔agent qui transforme ces commentaires en éditions. Le viewer est le support — le protocole est le produit.
Le problème qu’il résout
Corriger ou rédiger de la doc avec un agent IA oblige à mettre chaque changement en mots : citer le passage, nommer la section — « revoir la formulation de la section 3 » — puis espérer que l’agent retrouve l’endroit exact dans la source. Passé deux ou trois changements, c’est un pavé d’instructions dans un seul champ de chat. L’instruction réelle a toujours été plus simple : ce passage — le changer comme ça.
notabene en fait l’interface. Sélectionnez le texte exact sur la page rendue — ou un diagramme ou une image en entier — et laissez un commentaire à cet endroit. Le commentaire ancré est l’instruction : situé, sans ambiguïté, rien à citer. L’agent lit les commentaires, édite la source fidèlement, marque chaque commentaire résolu et journalise ce qui a changé & pourquoi.
La boucle, en 30 secondes
npx notabene dev→ ouvrir le site, sélectionner du texte → laisser un commentaire (ou commenter une page, un diagramme ou une image en entier). Fils, résolution, mise en attente, une vue globale/comments.- Demander à l’agent : « traite les commentaires de la doc ».
- L’agent lit
.notabene/, édite les docs fidèlement, marque chaque commentaire résolu et ajoute une entrée de journal (quoi / pourquoi / quels commentaires). - Relire la trace sur
/journal— ou valider chaque diff vous-même en mode approve.
Tout est dans votre git
Commentaires et journal sont des fichiers JSON sous .notabene/ — pas de SaaS, pas
de base de données, pas de compte. Ils voyagent avec le repo, diffent dans les PR, et
sont lisibles par n’importe quel agent : le protocole de revue est file-I/O-first
(ni serveur, ni port, ni MCP requis).
Où aller ensuite
- Installation — le renderer npm, le plugin Claude Code, ou les deux.
- Votre première revue — du commentaire à l’édition journalisée.
- Configuration — le fichier de config unique, par l’exemple : espaces, branding, liens de navigation et pied de page, page d’accueil.
- Personnaliser le rendu — branding, tokens, polices, thème de code, feuille de style.
- La boucle de revue — auto ou approve (diffs avec humain dans la boucle).
- Rédiger la doc — la palette de rendu : GFM, Mermaid, code, liens.
- Doc multilingue — EN/FR/… avec URL propres et sélecteur.
- Export PDF — vues imprimables et PDF avec signets.
- Publier un site public — un site statique en lecture seule, lisible par les agents.
Plutôt les tableaux exhaustifs ? Direction l’espace Référence : la CLI, chaque clé de config, le frontmatter, le contrat du store et le modèle de sécurité.
Installation
Installation
notabene, c’est deux briques installables : le plugin Claude Code (mise en place clé en main + la boucle de revue) et le renderer (un paquet npm + une CLI). Elles s’installent indépendamment — le plugin n’a pas besoin du paquet npm : il récupère le renderer tout seul.
Le plugin Claude Code — mise en place + revue
Dans Claude Code :
/plugin marketplace add z29k/notabene
/plugin install notabene@z29k
C’est toute l’installation — aucun npm install requis. Le plugin récupère et
exécute lui-même une version épinglée du renderer via npx (le premier lancement la
télécharge, ~30 s) ; rien n’est scaffoldé dans votre repo, qui n’a même pas besoin d’un
package.json. Le seul prérequis est Node (voir prérequis plus bas).
Puis dites simplement « configure notabene » (repo vierge) ou « traite les commentaires de la doc » (déjà configuré) — la bonne skill se déclenche d’elle-même.
Vous préférez une installation manuelle ? Copiez packages/claude-plugin/skills/notabene/ dans
le .claude/skills/ de votre projet. Vous utilisez un tout autre agent ? Le plugin ne vous
sert à rien : notabene init écrit le même protocole dans <store>/protocol.md et le
référence depuis AGENTS.md (voir
l’utiliser depuis n’importe quel agent).
Le renderer — paquet npm (sans Claude)
Pour piloter la CLI vous-même — à la main, en CI, ou depuis un autre outil — installez le paquet npm :
npm install -D @z29k/notabene # or: pnpm add -D @z29k/notabene · bun add -d @z29k/notabene
npx notabene init # writes notabene.config.mjs + creates the .notabene store
npx notabene dev # → http://localhost:3009
Le paquet npm est scopé (
@z29k/notabene) ; la commande CLI qu’il installe est simplementnotabene, doncnpx notabene …fonctionne tel quel.
init est la seule chose qui touche votre repo — il écrit notabene.config.mjs et
crée le store .notabene/ (init --detect préremplit roots[] à partir des dossiers de
doc qu’il trouve). Le renderer lui-même s’exécute depuis le paquet : rien n’est
scaffoldé ni copié dans votre repo, et la mise à jour se résume à npm update.
La surface de commandes complète (build, pdf, status, migrate, comments, journal…) est dans la référence CLI.
Installer les deux ne pose aucun problème : le plugin exécute toujours sa propre
version épinglée du renderer (alignée sur la version du plugin), indépendante de celle
de votre package.json — les deux ne se marchent jamais dessus.
Prérequis
Les deux voies partagent les mêmes prérequis :
- Node ≥ 22.12 et
npxdans votre PATH (les deux sont fournis avec Node). - N’importe quel repo avec des fichiers Markdown ou MDX — voir MDX et CommonMark pour la façon dont les deux formats sont gérés.
Ensuite : votre première revue.
Votre première revue
Votre première revue
Vous avez installé le renderer et lancé npx notabene dev. Voici la
boucle complète, une fois, de bout en bout.
1 · Commenter la page rendue
Ouvrez http://localhost:3009, naviguez vers n’importe quelle page et sélectionnez un
passage — une barre d’action apparaît ; laissez votre commentaire là, directement. Vous
pouvez aussi :
- commenter une page entière (la zone de commentaire en bas de chaque page) ;
- commenter un diagramme ou une image en entier — survolez-le et utilisez le 💬 de la barre d’outils (le ⤢ juste à côté ouvre une lightbox pan/zoom) ;
- répondre dans les fils, placer un commentaire en mise en attente (⏸ — l’agent
le sautera), et tout voir, toutes pages confondues, sur
/comments.
Sur téléphone ou tablette, la même boucle fonctionne d’abord au tactile : la nav se replie dans un tiroir, les commentaires deviennent des bottom sheets, et vous pouvez sélectionner du texte et commenter au pouce.
2 · Confier les commentaires à votre agent
Dites-le à votre agent — avec le plugin Claude Code, c’est simplement :
traite les commentaires de la doc
L’agent lit le store .notabene/ directement (aucun serveur requis), localise chaque
passage commenté dans le fichier source, applique le retour fidèlement, marque le
commentaire résolu, et ajoute une entrée de journal reliant ce qui a changé à
pourquoi. Il vérifie ensuite : le build du renderer s’exécute toujours, plus les checks
que vous listez dans verify[]. Il ne commite jamais sans
demander.
3 · Relire la trace
/journal— chaque passe, avec quoi/pourquoi/quels commentaires, par page.- Chaque commentaire résolu pointe vers son entrée de journal.
Vous voulez valider chaque édition vous-même avant qu’elle compte comme résolue — avec le vrai diff git ? C’est le mode approve : voir la boucle de revue.
Travailler à plusieurs relecteurs
Les commentaires portent une identité (nom + email optionnel), définie par navigateur via la pastille 👤 de l’en-tête — les fils s’attribuent donc par personne, pas par machine. Le store, c’est un fichier JSON par commentaire, donc les branches parallèles fusionnent sans conflit.
Configuration
Configuration
notabene.config.mjs à la racine de votre repo est le seul câblage. Les chemins sont
relatifs au repo. notabene init scaffolde un template commenté ; notabene init --detect préremplit roots[] à partir des dossiers de doc qu’il trouve. Chaque clé est
optionnelle — cette page montre celles que vous toucherez vraiment ; la
référence de config les liste toutes.
// notabene.config.mjs
export default {
siteName: "My Project",
tagline: "docs",
locale: "en",
// Format d'entrée : "commonmark" (tolérant, zéro MDX) ou "mdx" (.mdx strict + .md tolérant).
format: "commonmark",
// Espaces de doc. `key` = slug d'URL + espace du store ; `path` = dossier relatif au repo.
roots: [
{ key: "docs", label: "Docs", path: "docs", exclude: [".notabene/**"] },
{ key: "adr", label: "Decisions", path: "docs/adr", description: "Architecture decision records" },
],
store: "docs/.notabene", // commentaires + journal (committez ce dossier)
review: "auto", // "approve" = vous validez chaque édition avec un diff
verify: [], // vos propres checks post-édition (le build du renderer s'exécute toujours)
};
Espaces (roots[])
Chaque entrée devient un espace : sa propre section dans la sidebar, sa propre carte
sur la page d’accueil, son propre préfixe dans les ids de commentaires. Un root imbriqué
(comme docs/adr ci-dessus) l’emporte sur son parent pour les pages qu’il contient — le
chemin le plus spécifique fait loi. label et description acceptent une map par locale
quand l’i18n est activée.
MDX et CommonMark/GFM
Le renderer choisit le processeur selon l’extension du fichier :
.md→ CommonMark/GFM, tolérant.<email@x>,Promise<T>,{var}, le HTML brut et les tableaux GFM se rendent tous sans crash..mdx→ MDX strict (JSX/expressions) — composants importables, mais les</{hors blocs de code doivent être échappés.
format: "mdx" (le défaut de la config) active les deux, mélangeables dans un même repo.
format: "commonmark" (ce que init scaffolde) supprime entièrement la dépendance MDX —
le point de départ sûr et le plus tolérant pour un repo en Markdown pur.
Branding
Pointez l’en-tête, l’onglet du navigateur et les cartes sociales vers vos propres assets — des fichiers relatifs au repo, servis par le renderer (rien à copier nulle part) :
branding: {
logo: "assets/logo.svg", // image de la topbar, à côté du nom du site
logoDark: "assets/logo-dark.svg", // variante sombre optionnelle (sinon logo partout)
favicon: "assets/favicon.svg", // .svg / .ico / .png — non défini → une marque par défaut intégrée
socialImage: "assets/og.png", // og:image / twitter:image des builds PUBLICS
},
socialImage requiert publish.site — les crawlers
exigent une URL absolue. Le favicon couvre aussi les vues print/PDF.
Liens de navigation
Une fois dans une page, plus rien ne ramène au repo, au produit ou aux releases. nav
ajoute ces liens sortants à trois endroits — une seule forme d’entrée partout :
nav: {
// Topbar, groupe de droite. Répliqué automatiquement dans le tiroir mobile.
header: [
{ label: "GitHub", href: "https://github.com/vous/repo", icon: "github", iconOnly: true },
{ label: { en: "Product", fr: "Produit" }, href: "https://example.com" },
],
// Un bloc titré sous l'arbre des espaces (le tiroir mobile l'affiche aussi).
sidebar: {
title: { en: "Resources", fr: "Ressources" },
links: [
{ label: "Releases", href: "https://github.com/vous/repo/releases", icon: "star" },
{ label: "npm", href: "https://www.npmjs.com/package/votre-pkg", icon: "npm" },
],
},
// Le pied de page du site. Rien de configuré → aucun élément de pied de page.
footer: {
links: [{ label: "Licence", href: "/reference/licence" }],
text: { en: "© 2026 vous — MIT", fr: "© 2026 vous — MIT" },
poweredBy: false,
},
},
| Champ | Signification |
|---|---|
label | Obligatoire. Une chaîne, ou une map { <locale>: string } comme roots[].label. Sert aussi de nom accessible quand iconOnly |
href | Obligatoire. Une URL https:///http:///mailto: (ouverte dans un nouvel onglet, rel="noopener"), ou un chemin du site /… (votre publish.base est appliqué pour vous) |
icon | Au choix : github, gitlab, npm, discord, slack, x, mastodon, rss, mail, book, home, star, download, external. Monochrome — l’icône suit la couleur du lien, donc votre thème |
iconOnly | Topbar uniquement : n’affiche que l’icône (le label devient son aria-label/infobulle). Ignoré ailleurs |
publish | false garde le lien hors des builds publics — même idée que roots[].publish |
- Tout est validé au chargement de la config : une clé inconnue, une icône inconnue,
un href en double ou une URL
javascript:lèvent une erreur immédiatement plutôt que d’expédier un lien cassé — ou piégé — dans un site publié. - Ces liens relèvent de l’identité, pas de l’outillage de revue : contrairement à Commentaires/Revue/Journal, ils apparaissent en dev et dans les builds publics. Ils n’apparaissent jamais dans les vues print/PDF et restent hors de l’index de recherche.
- Gardez trois ou quatre entrées en topbar —
iconOnlyexiste précisément parce que cette rangée est chargée. Les listes longues vont dans le bloc sidebar ou le pied de page.
Page d’accueil personnalisée
Par défaut, la page d’atterrissage (/) affiche le nom du site et une carte par espace.
Pointez home vers un fichier Markdown pour rendre votre propre accueil au-dessus de
ces cartes — le classique est une intro façon README écrite pour le site, avec des
liens relatifs qui deviennent des routes :
home: "docs/home.md",
- Pipeline complet : Mermaid, coloration du code, et liens inter-docs réécrits vers
les routes du site — liez directement dans vos espaces (
[install](./guide/install.md)). - À garder de préférence hors de vos espaces (un doc dédié) : dans un espace, il se rendrait aussi comme une page normale de cet espace.
- Avec l’i18n, passez une map par locale :
home: { en: "docs/home.md", fr: "docs/home.fr.md" }— la page d’atterrissage de chaque locale (/,/fr) rend son propre fichier. - La page d’accueil de ce site, c’est exactement ça — voir
docs/home.md.
Pied de page : lien d’édition & dernière mise à jour
Deux touches sans configuration sous chaque page :
- Mis à jour le — la date d’auteur git de la page (un seul
git logstreamé par build ; une datelastUpdateden frontmatter la remplace ; silencieusement absente hors d’un repo git). Les builds publics l’émettent aussi enarticle:modified_time. - Modifier cette page — posez
editPatternet chaque page pointe vers sa source :
editPattern: "https://github.com/vous/repo/edit/main/{path}",
Labels & ordre de la sidebar
Par défaut, l’entrée d’une page dans la sidebar est son nom de fichier humanisé, et les pages sœurs se trient alphabétiquement. Surchargez l’un ou l’autre par page via le frontmatter — aucun préfixe numérique dans les noms de fichiers requis :
---
title: Internal network map # <title> de la page + fil d'Ariane (surcharge le H1)
sidebar:
label: Network map # texte de la sidebar (sinon title, sinon nom de fichier)
order: 9 # position parmi les pages sœurs (croissant)
---
ordertrie en croissant ; les entrées qui n’en ont pas continuent de se trier alphabétiquement, après celles qui sont ordonnées. Groupes et pages partagent un même ordre.- Un dossier est nommé et positionné par sa page d’atterrissage —
<folder>/index.md(oureadme.md) — dont le frontmattersidebars’applique à tout le groupe ; cette page apparaît comme une entrée Aperçu localisée (renommable viasidebar.indexLabel). - Ces labels se propagent aux fils d’Ariane et à l’export PDF.
La surface complète du frontmatter (y compris description et publish pour les
sites publics) est dans la
référence du frontmatter.
Personnaliser le rendu
Personnaliser le rendu
notabene s’exécute depuis le paquet — vous ne forkez jamais son UI. Tout ce qui suit passe par la config à la place :
- Branding — logo, favicon, image sociale.
- Tokens — surchargez les custom properties
--nb-*(ci-dessous). - Votre propre feuille de style — un fichier CSS chargé après les styles du renderer.
- Polices et images — un dossier de votre repo, servi pour cette feuille de style.
- Code et diagrammes — un thème Shiki pour les blocs de code ; Mermaid suit les tokens.
Les liens sortants (topbar, sidebar, pied de page) n’en font pas partie : ce sont des données du dépôt, pas de l’apparence — voir liens de navigation.
theme: {
// Surcharges rapides, aucun fichier requis. Une valeur simple s'applique aux DEUX schémas ;
// une paire light-dark() personnalise chacun : clair à gauche, sombre à droite.
tokens: { accent: "light-dark(#7c3aed, #b79bff)", radius: "4px" },
css: "docs/notabene-theme.css", // ou/et une feuille de style complète
},
Les deux ciblent le même contrat ; un nom de token mal orthographié lève une erreur au démarrage (jamais un no-op silencieux). Les styles du renderer vivent dans des cascade layers CSS, donc votre CSS hors layer gagne toujours — pas de guerre de spécificité, pas de loterie d’ordre.
Tout suit cette palette — le chrome, les blocs de code et les diagrammes — et le sélecteur de schéma de l’en-tête la bascule en direct, sans rebuild :
Diagrammes
Les diagrammes Mermaid suivent les tokens d’emblée : les nœuds sont
remplis en accent-soft et bordés d’accent, les libellés utilisent la couleur du
texte, les arêtes text-soft — et ils se re-rendent au changement de schéma, donc un
diagramme en mode sombre est un vrai diagramme sombre, pas une image inversée. Rien à
configurer.
Si un diagramme rend mieux avec la palette de Mermaid, désactivez :
theme: { mermaid: false }, // retour aux thèmes default/dark intégrés de Mermaid
Coloration syntaxique (theme.code)
Les blocs de code sont colorés au build : leurs couleurs sont donc figées — d’où le décalage habituel, un thème clair avec des blocs de code sombres. Nommez un thème Shiki et cela change :
theme: {
code: "github-light", // même thème dans les deux schémas
// ou un par schéma :
code: { light: "github-light", dark: "vesper" },
},
- Avec
code, les deux palettes voyagent en variables CSS et le sélecteur de schéma recolore le code instantanément — sans rebuild, sans clignotement. - Le thème de code prend alors aussi la main sur le fond du bloc (les tokens d’un
thème clair sur la dalle sombre par défaut seraient illisibles).
--nb-code-bgreste le fond pour quiconque ne définit pastheme.code. - Le PDF/print force le schéma clair : c’est donc votre thème de code clair qui atterrit sur le papier.
- Nom de thème inconnu → erreur au démarrage, comme pour chaque réglage de thème.
Polices et images (theme.assets)
Une feuille de style a généralement besoin de fichiers : une police web, un fond, une
texture. Pointez theme.assets vers un dossier de votre repo : il est servi au chemin
fixe /_nb/assets/… — en dev, dans les builds et dans les
artefacts publics, pour qu’un site publié reste autonome (pas de CDN) :
theme: { css: "docs/theme/site.css", assets: "docs/theme/assets" },
/* docs/theme/site.css */
@font-face {
font-family: "Inter";
src: url("./assets/fonts/Inter.woff2") format("woff2"); /* ← relatif, toujours */
}
:root { --nb-sans: "Inter", system-ui, sans-serif; }
- Écrivez les
url()en relatif, jamais en absolu depuis la racine. Les URL se résolvent contre la feuille servie (/_nb/theme.css), pas contre votre fichier source — donc./assets/…est la forme correcte, et elle absorbe gratuitement un sous-cheminbase(site projet GitHub Pages), là où/_nb/…casserait. - Déclarez un dossier dédié, pas
docs/: tout ce qui y est servable est émis, référencé ou non. - Seuls des assets sont servis — polices (
woff2,woff,ttf,otf), images (svg,png,jpg,webp,avif,gif,ico) etcss. Tout le reste (.md,.env, scripts), chaque fichier caché et tout lien symbolique pointant hors du dossier sont refusés.
Le contrat de tokens (--nb-*)
Ces custom properties sont la surface de theming publique — stable d’une version à
l’autre. Les défauts de couleur sont donnés sous leur paire light-dark(light, dark) :
| Token | Défaut (clair / sombre) | Rôle |
|---|---|---|
bg | #ffffff / #0e1116 | Fond de page |
bg-soft | #f6f7f9 / #151a21 | Panneaux, champs de saisie |
bg-elev | #ffffff / #161b22 | Surfaces élevées (topbar, popovers) |
border | #e4e7ec / #272e38 | Filets |
text | #1c2024 / #e7ebf0 | Premier plan principal |
text-soft | #5b6470 / #aab2bd | Premier plan secondaire |
text-faint | #8a929e / #768091 | Premier plan tertiaire |
accent | #2f6feb / #6ea0ff | Liens, focus, surlignages |
accent-soft | #e8f0ff / #182539 | Fonds d’accent |
ref / work | #2f6feb / #6ea0ff · #b5651d / #e0a060 | Pastilles d’espace dans les résultats de recherche |
code-bg | #0d1117 / #0b0e13 | Fond des blocs de code — le défaut, quand aucun theme.code n’est défini (les couleurs Shiki sont alors figées clair-sur-sombre, donc le code reste sombre dans les deux schémas) |
topbar-h / sidebar-w / toc-w / content-max | 52px / 290px / 320px / responsive | Mise en page (invariante au schéma) |
radius | 8px | Arrondi des coins |
sans / mono | piles système | Familles de polices |
Chaque token de couleur est une paire light-dark() — une seule déclaration couvre
les deux schémas, et le sélecteur clair/sombre de l’en-tête (auto / clair / sombre,
persisté par navigateur) les bascule tous d’un coup. Un thème qui change les couleurs
fait de même :
/* docs/notabene-theme.css */
:root {
--nb-accent: light-dark(#7c3aed, #b79bff);
--nb-accent-soft: light-dark(#f1e9ff, #241a3d);
}
Le sélecteur fonctionne via color-scheme + un attribut data-scheme que le renderer
pose sur <html> — un thème ne doit pas définir cet attribut (ni color-scheme sur
:root) ; surchargez des tokens, et le sélecteur continue de fonctionner sans effort.
light-dark() ne couvre que les couleurs ; pour le rare style par schéma hors couleur,
sélectionner sur :root[data-scheme="dark"] dans votre feuille de style est très
bien — c’est poser l’attribut qui est réservé.
Au-delà des tokens
Votre feuille de style peut aussi cibler un petit ensemble de points d’accroche
stables : .topbar, .brand, .sidebar, .prose (le contenu rendu), .home-cards,
.rail, plus les liens de navigation —
.nb-nav-link (tout lien sortant), .nb-sidebar-links (le bloc sous l’arbre des
espaces) et .site-footer. Tout le reste — et chaque variable CSS sans préfixe — est
interne et peut changer d’une version à l’autre.
Un thème style ces liens ; il n’en déclare jamais. Les entrées de nav, le texte
du pied de page et les assets de branding sont des données du dépôt (nav, branding au
premier niveau de la config), pas de l’apparence : une feuille de style installée ne doit
pas pouvoir injecter des liens sortants dans un site publié, ni porter des libellés dans
des langues qu’elle ne peut pas connaître.
Deux règles vous gardent en sécurité :
- Ne surchargez que les tokens
--nb-*et les points d’accroche ci-dessus. En particulier, ne touchez jamais aux variables sans préfixe : les vues print/PDF forcent une palette claire à travers elles, donc un thème limité aux tokens peut restyler tout le site sans jamais casser l’export PDF. - Vérifiez les deux schémas de couleur — le sélecteur de l’en-tête en fait un test à deux clics.
Les thèmes s’appliquent partout : dev, builds normaux, sites publics et les vues print (couleurs exceptées, à dessein).
La boucle de revue
La boucle de revue
La boucle est le produit : des commentaires en entrée, des éditions fidèles en sortie, le tout journalisé. Elle est conçue pour que n’importe quel agent puisse l’exécuter — le protocole est un fichier de skill en texte brut qui lit et écrit le store directement.
Comment l’agent travaille
Tout est découvert depuis notabene.config.mjs — rien de codé en dur, ni serveur ni
port requis :
- Lire les commentaires ouverts et non mis en attente depuis
<store>/(un fichier JSON par commentaire). - Localiser la page source via
roots[], puis résoudre l’ancre textuelle avec tolérance (l’ancre stocke le texte cité + le contexte environnant + le titre le plus proche). - Éditer les docs fidèlement — un commentaire est une décision de l’utilisateur.
- Marquer le commentaire résolu (ou
addresseden mode approve) et compléter le journal : une entrée par passe, un enregistrement de changement par page touchée, relié aux ids des commentaires. - Vérifier : le build du renderer tourne toujours, puis
notabene lint(les liens inter-docs validés contre les routes que le build vient d’émettre), puis vos checksverify[]. - Rendre compte et demander avant de committer — jamais de commit silencieux, jamais de suppression en masse.
Les commentaires qu’un relecteur met en attente (⏸) sont ignorés — ce sont vos travaux en cours.
Les étapes 4 et 5 ont des primitives CLI, pour qu’un agent n’édite jamais le JSON du store
à la main : notabene comments done <id…> --note … --journal <entryId> choisit le bon
statut selon votre mode review, et notabene comments verify audite ce qu’il a écrit
— statuts, liens commentaire↔journal dans les deux sens, disposition des fichiers. Il sort
en code non nul sur un vrai problème : c’est donc aussi un garde-fou de CI sur le store que
vos agents committent.
Mode approve : un humain valide chaque édition
Par défaut (review: "auto"), l’agent résout les commentaires directement. Passez
review: "approve" pour un flux avec humain dans la boucle :
- l’agent édite et marque chaque commentaire
addressedau lieu de le résoudre ; - vous validez sur
/review(ou via le filtre À valider de/comments) ; - vous voyez le vrai diff git de tout ce qui a changé pour ce commentaire — cascades comprises (un commentaire peut toucher plusieurs pages) ;
- approuver → résolu, ou rejeter → rouvert avec votre raison, que l’agent lit à sa prochaine passe ;
- le diff s’affiche en vue unifiée ou côte à côte, et un badge Review dans l’en-tête compte ce qui attend.
L’utiliser depuis n’importe quel agent
Le protocole est une spec en texte brut, et notabene init l’installe dans votre repo
pour qu’aucun agent n’ait à la chercher :
<store>/protocol.md— la spec complète, committée à côté des commentaires qu’elle décrit. Hors ligne, sans npm, sans réseau. C’est ce fichier que vous montrez à un agent.AGENTS.md— un bloc borné<!-- notabene:begin -->…<!-- notabene:end -->qui indique aux agents qui le lisent au démarrage (Codex CLI, Cursor, Gemini CLI, Zed, Amp…) où vivent les commentaires et le protocole. Rien hors des marqueurs n’est touché ; désactivable viainit --no-agents-md.
Les deux se rafraîchissent en relançant notabene init (idempotent) — à faire après avoir
déplacé le store ou renommé un espace, et notabene doctor signale la dérive. Ce texte,
c’est la page protocole agent de ce site — la version
canonique, avec un jumeau Markdown dans les builds publics pour les agents qui naviguent ;
npx -y @z29k/notabene@latest protocol l’imprime hors ligne.
Dans Claude Code, la skill du plugin est ce protocole : elle se déclenche sur « traite les commentaires de la doc » et n’a besoin d’aucun AGENTS.md. La forme du store est elle-même un contrat public versionné — voir la référence du store.
Doc multilingue
Doc multilingue (i18n)
Ajoutez i18n pour servir les mêmes docs en plusieurs langues avec des URL propres
préfixées (la locale par défaut sans préfixe, les autres en /<locale>/…), un
sélecteur de langue dans l’en-tête, des alternates hreflang et un chrome par
page — une page française affiche nav, boutons et dates en français.
i18n: { locales: ["en", "fr"], defaultLocale: "en", strategy: "directory" },
Deux organisations des fichiers
Choisissez l’organisation des fichiers avec strategy :
directory(par défaut) — un dossier par locale :docs/en/guide.md·docs/fr/guide.md.suffix— une seule arborescence, traduite fichier par fichier :docs/guide.md(défaut) ·docs/guide.fr.md. Idéal pour ajouter des langues à une doc existante : les fichiers de la langue par défaut ne bougent pas, donc leurs URL et leurs fils de commentaires sont préservés.
Préférence de langue & repli
Le sélecteur enregistre la langue choisie par le visiteur ; dès lors, arriver sur une
page écrite dans une autre langue qui a une traduction redirige vers celle-ci —
suivre n’importe quel lien vous garde dans votre langue. Une page sans traduction
se replie sur la langue source et affiche un bandeau discret. Les pages non liées à une
langue de contenu — /comments, /journal, /review, la page d’accueil et la
404 — suivent votre langue courante côté client et embarquent le même sélecteur.
Tout est par langue
- Les commentaires sont par langue — un commentaire sur la page FR est un fil à part, relié au fichier source FR.
- La recherche et l’export PDF (
notabene pdf --locale fr) sont limités à une langue ; un site public embarque desllms.txtet des doubles Markdown par locale. - Toute chaîne humaine de la config accepte une map par locale : les
label/descriptiond’un espace (label: { en: "Docs", fr: "Documentation" }), la page d’accueil personnalisée (home: { en: …, fr: … }), et chaque libellé de lien de navigation, titre du bloc sidebar et ligne de pied de page. Non défini pour une locale → repli sur celle par défaut.
Omettez i18n pour une seule langue — le comportement est inchangé.
Export PDF
Export PDF
Transformez n’importe quelle page, n’importe quel dossier, n’importe quel espace ou la doc entière en un document soigné. Deux chemins, un même rendu optimisé pour l’impression (page de couverture + table des matières cliquable, palette forcée en clair pour que les diagrammes en mode sombre restent lisibles sur papier).
Dans le navigateur — zéro dépendance
Le menu Export PDF de l’en-tête propose la page courante, son dossier, son espace
ou la doc entière. Il ouvre une vue /print dans un nouvel onglet et déclenche
automatiquement l’Enregistrer au format PDF de votre navigateur. Les routes /print
sont statiques — elles existent en dev et dans tout build (y compris les
sites publics).
notabene pdf — l’artefact haute fidélité
notabene pdf --scope space:docs --out docs.pdf
Construit le site, pilote un Chromium headless et écrit un PDF avec un vrai plan de
signets (le panneau latéral navigable) et des numéros de page en pied. Flags :
--scope doc|space:K|folder:K/P|page:K/I, --locale, --out, --chrome.
Requiert la peer dependency optionnelle puppeteer (ou puppeteer-core plus
--chrome <path> / PUPPETEER_EXECUTABLE_PATH pointant vers un Chrome système) :
npm i -D puppeteer
Réglages
pdf: { enabled: true, pageSize: "A4", margin: "18mm" },
enabled: false masque le menu Export et retire les routes /print.
pageSize/margin alimentent la boîte CSS @page. Couvertures et titres de section
réutilisent les labels et l’ordre de la sidebar, si bien que le
PDF se lit dans le même ordre que le site.
Publier un site public
Publier un site public
L’app de revue est un outil local de dev — mais les docs qu’elle rend méritent
souvent un foyer public. build --public produit un artefact purement statique, en
lecture seule fait pour ça :
notabene build --public --site https://you.github.io --base /your-repo --out ./_site
Ce site même est construit ainsi — les docs de notabene, rendues et publiées par notabene.
Ce qui reste, ce qui part
- Tout l’interactif disparaît — structurellement. Pas d’UI de commentaires, pas
d’invite d’identité, pas de
/comments//review//journal, pas d’API, et rien du store.notabene. Les routes ne sont pas verrouillées ; elles ne sont pas construites. - Ce qui reste est l’expérience de lecture complète : nav, recherche, Mermaid +
lightbox d’images, mode sombre, i18n (pages par locale,
sélecteur,
hreflang), routes print/PDF,404. - Né lisible par les agents. Chaque page embarque un double Markdown à
<page>/index.md(annoncé via<link rel="alternate" type="text/markdown">), le site embarque/llms.txt(un index machine de chaque page, par locale) et/llms-full.txt(la doc entière en un seul document Markdown dans l’ordre de lecture), plusrobots.txt, un sitemap, des URL canoniques, des meta OpenGraph/Twitter et du JSON-LD. Essayez ici : /llms.txt.
Recherche plein texte (optionnelle)
Le site public hérite de la recherche intégrée telle quelle. Installez
Pagefind en dépendance de dev et build --public la promeut en
recherche plein texte statique :
npm i -D pagefind
Le build indexe l’artefact final : stemming par langue (un visiteur de /fr/ cherche
dans un index français, avec les formes fléchies du français), extraits surlignés sous
chaque résultat, et un poids réseau qui reste faible quand la doc grandit — le
navigateur ne télécharge que les morceaux d’index utiles à la requête. Zéro
configuration, même champ de recherche. Non installé → le site public garde la
recherche JSON intégrée. Et le contenu privé ne peut pas fuiter dans l’index :
l’indexation s’exécute sur l’artefact, où les
pages hors périmètre n’existent pas.
La même dépendance améliore aussi l’app de revue notabene dev : son index est
construit en direct depuis vos sources Markdown et rafraîchi quand elles changent —
aucun build en jeu. Deux différences propres au dev : le site de dev (donc son index)
inclut vos pages privées, et les résultats profonds par section (ancres de titres)
restent réservés au public.
Où aller ensuite
- Configurer
publish—site,base,exclude, avec exemples. - Garder du contenu privé — portée par espace / sous-arborescence / page.
- Domaine géré côté serveur — omettre
site, rester agnostique à l’origine. - Déployer via GitHub Pages — le workflow prêt à l’emploi.
La boucle de dev est intacte : notabene dev et le simple notabene build se
comportent exactement comme avant — la publication est opt-in, par build.
Configurer publish
Configurer publish
Tout vit sous une seule clé de config optionnelle — les flags CLI (--site/--base)
la surchargent, et --out copie l’artefact vers un chemin stable (il refuse d’écraser
quoi que ce soit qu’il n’a pas généré) :
// notabene.config.mjs
export default {
// …
publish: {
// ORIGINE déployée. Grave des URL absolues dans canonical, og:url, JSON-LD,
// hreflang, llms.txt, le sitemap et la ligne Sitemap de robots.txt.
// OPTIONNELLE — omettez-la pour garder le domaine hors du repo (voir
// « Domaine géré côté serveur »). Origine seule, sans chemin : un sous-chemin va dans `base`.
site: "https://you.github.io",
// Sous-chemin quand le site est servi sous un préfixe (site de projet
// GitHub Pages → "/<repo>"). Préfixe chaque lien et URL d'asset — contrairement
// au domaine, un sous-chemin affecte toujours le rendu, il ne peut pas être côté serveur.
base: "/your-repo",
// Sous-arborescences à garder hors des builds publics — globs comparés à
// `<space key>/<page id>` (indépendant de la locale : un motif masque chaque
// traduction d'une page). `*` = un segment de chemin, `**` = toute profondeur.
exclude: ["docs/internal/**", "docs/*/draft"],
},
};
Trois configurations typiques
publish: { site: "https://you.github.io", base: "/my-repo" } // GitHub Pages, site de projet
publish: { site: "https://docs.example.com" } // domaine personnalisé à la racine
publish: { exclude: ["docs/internal/**"] } // domaine gardé hors du repo
La troisième est le mode agnostique à l’origine — le même artefact derrière n’importe quel domaine.
Métadonnées par page
Deux clés de frontmatter alimentent les surfaces publiques (voir la référence complète du frontmatter) :
---
description: One-line summary — becomes the meta description / OpenGraph.
publish: false # cette page ne part jamais dans un build public
---
Exclure du contenu du build a sa propre page : garder du contenu privé.
Garder du contenu privé
Garder du contenu privé
Trois niveaux, du plus grossier au plus fin — chacun vit là où vit ce qu’il délimite.
Un espace entier — marquez l’entrée roots[] :
roots: [
{ key: "docs", label: "Docs", path: "docs" },
{ key: "notes", label: "Notes", path: "notes", publish: false }, // l'espace entier reste privé
],
Une sous-arborescence — des globs publish.exclude sur <space key>/<page id>
(c’est le chemin d’URL de la page sans préfixe de locale, donc un seul motif masque
toutes les traductions) :
publish: { exclude: ["docs/internal/**", "docs/*/draft"] },
Une seule page — son propre frontmatter :
---
publish: false # cette page n'apparaît jamais dans un build public
---
Un lien de navigation — pas du contenu, mais la même idée : une
entrée nav marquée publish: false reste en
dev et n’atteint jamais l’artefact (un dashboard, un wiki interne) :
nav: { header: [{ label: "Dashboard ops", href: "https://ops.internal", publish: false }] },
La garantie
Le contenu privé n’est pas caché, il n’est pas construit — pas de route (l’URL
renvoie un 404), pas d’entrée dans la sidebar, pas de résultat de recherche, pas de
ligne llms.txt, pas de double .md, pas d’entrée de sitemap, pas d’inclusion
print/PDF, et le nom et le chemin de l’espace n’atteignent jamais le HTML public.
notabene dev et les builds normaux montrent toujours tout — vous
passez en revue vos docs privées exactement comme le reste. La
notion de publish n’existe que pour les builds publics.
Une réserve : les liens vers du contenu privé
Si une page publique pointe vers une page privée, ce lien renvoie un 404 dans
l’artefact public — le build ne le réécrit pas et n’émet aucun avertissement.
notabene lint attrape exactement cela : lancez-le après build --public et chaque
lien d’une page publique vers du contenu exclu par le scoping est signalé (la vérité des
routes publique ne contient tout simplement pas ces pages — voir la
référence CLI).
Domaine géré côté serveur
Domaine géré côté serveur ? Omettez site
Quand le domaine public est l’affaire du serveur — un vhost ou un reverse proxy en
frontal, plusieurs miroirs, ou un domaine pas encore choisi — ne définissez simplement
pas site (et ne passez pas de --site). L’artefact devient agnostique de
l’origine : pas une seule URL absolue n’y est embarquée, la même sortie fonctionne
donc derrière n’importe quel domaine, et changer de domaine n’exige jamais de rebuild.
Ce qui change
| Surface | Avec site | Sans |
|---|---|---|
llms.txt / llms-full.txt / doubles .md | URL absolues | chemins relatifs à la racine (les agents les résolvent contre l’origine depuis laquelle ils les ont récupérés) |
alternates hreflang | absolues | par chemins |
canonical, og:url, JSON-LD | émis | non émis — ils n’ont de sens qu’avec une origine |
sitemap + ligne Sitemap: de robots.txt | émis | non émis — les spécifications exigent des URL absolues |
Ce que cela coûte, concrètement
La visibilité dans les moteurs de recherche — rien d’autre. Sans sitemap, URL
canoniques ni hreflang valide, les crawlers ne découvrent les pages qu’en suivant les
liens, rien ne consolide les doublons si la doc répond sur plusieurs domaines, et les
pages multilingues n’envoient aucun signal de langue aux moteurs de recherche. Les
aperçus de liens sociaux gardent leur titre/description mais perdent la carte d’URL.
Les lecteurs humains et les agents IA ne perdent rien — chaque page, double et
fichier llms fonctionne à l’identique.
Règle simple : hébergement interne, miroir, ou domaine pas encore arrêté → omettez
site ; un site public dont le référencement compte → définissez site.
base reste indépendant
Définissez base dès que le site vit sous un sous-chemin, avec ou
sans domaine — un sous-chemin affecte toujours les liens rendus, il ne peut donc pas
être laissé au serveur.
Déployer via GitHub Pages
Déployer via GitHub Pages
Déployez sur n’importe quel hébergement statique. Pour GitHub Pages : Settings → Pages → Source = GitHub Actions, puis :
# .github/workflows/docs.yml
name: docs
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- run: npm ci
- run: npx notabene build --public
--site https://${{ github.repository_owner }}.github.io
--base /${{ github.event.repository.name }}
--out ./_site
- uses: actions/upload-pages-artifact@v3
with: { path: ./_site }
- id: deployment
uses: actions/deploy-pages@v4
Notes :
- Avec
publish: { site, base }défini dans votre config, les flags--site/--basepeuvent être omis entièrement. - Hébergement sur un domaine personnalisé ou un site racine utilisateur/organisation ?
Retirez
--baseet pointez--sitevers votre domaine. - L’artefact embarque un
.nojekyll, donc les assets_astro/survivent même à l’hébergement classique par branche gh-pages. npm i -D pagefinddans votre repo et ce même workflow embarque aussi la recherche plein texte —npm cil’installe, le build la détecte. Rien à changer ici.- Ce site de documentation est déployé par exactement ce workflow — voyez-le dans le repo.
Référence
Référence
Référence
Le pendant exhaustif du Guide — tableaux et contrats, une page par surface :
- CLI — chaque commande et flag
notabene. - Clés de configuration — toute la surface de
notabene.config.mjs. - Frontmatter — chaque clé qu’une page peut porter.
- Le contrat du store
.notabene— le schéma JSON versionné que les agents lisent et écrivent. - Le protocole agent — la boucle file-I/O-first que suit n’importe quel agent pour transformer les commentaires en éditions.
- Modèle de sécurité — pourquoi l’API d’écriture ne peut pas vous nuire.
CLI
CLI
Le package npm est scopé (@z29k/notabene) ; la commande installée est simplement
notabene, donc npx notabene … fonctionne tel quel dès lors que le package est
une dépendance de votre repo. Sans installation locale, utilisez toujours le nom scopé —
npx -y @z29k/notabene@latest … — car notabene non scopé n’est pas notre package.
| Commande | Ce qu’elle fait |
|---|---|
notabene doctor | État en lecture seule au format JSON : config/store/port + dossiers de doc détectés — --json |
notabene init | Écrit notabene.config.mjs + crée le store (sans effet s’il existe déjà) ; --detect auto-détecte les dossiers de doc. Écrit aussi le point d’entrée agent : <store>/protocol.md + un bloc borné dans AGENTS.md — désactivables via --no-protocol / --no-agents-md. Idempotent : relancez-le pour rafraîchir les deux |
notabene dev | Démarre le serveur de revue sur les docs de ce repo (rechargement à chaud) ; --detach le lance en démon d’arrière-plan. Avec la dépendance de dev optionnelle pagefind, sa recherche devient plein texte aussi |
notabene status | Le serveur détaché tourne-t-il ? (pid, port, URL) — --json |
notabene stop | Arrête le serveur détaché |
notabene build | Construit le site (Node standalone ; docs prérendues, pas d’API d’écriture dans l’artefact) |
notabene build --public | Site statique en lecture seule pour hébergement public — voir le guide. [--site URL] [--base /sub] [--out DIR]. Avec la dépendance de dev optionnelle pagefind, l’artefact gagne une recherche plein texte statique |
notabene preview | Sert le site construit |
notabene lint | Valide les liens inter-docs contre les routes émises par le dernier build (suggestions « did you mean » ; --json). Après build --public, attrape aussi les liens de pages publiques vers du contenu privé. Exit 1 = liens cassés, 2 = pas encore de build |
notabene pdf | Exporte un PDF via Chromium headless (sommaire de signets + numéros de page) ; --scope doc|space:K|folder:K/P|page:K/I, --locale, --out, --chrome. Nécessite la peer dep optionnelle puppeteer (ou puppeteer-core + --chrome) |
notabene migrate | Convertit le store vers la disposition un-fichier-par-commentaire (estampille schemaVersion 3) |
notabene comments ls | Liste les commentaires — --open --json --page <p> (pour agents/scripts) |
notabene comments done | Marque des commentaires traités : done <id…> [--note <texte>] [--journal <entryId>]. Le statut vient de review (auto → resolved, approve → addressed) — --status force, --force agit sur un commentaire en attente (hold). Écriture atomique ; tous les autres champs préservés |
notabene comments reopen | Renvoie des commentaires à open : reopen <id…> [--reply <texte>] [--author <nom>] — la raison devient une réponse du thread que l’agent lira à la passe suivante (le pendant CLI du rejet sur /review) |
notabene comments verify | Audite le store : statuts, liens commentaire↔journal dans les deux sens, disposition des fichiers, doublons, pages disparues. --json ; exit 1 si erreurs, 2 sans store. À lancer après une passe d’agent, ou en CI |
notabene journal add | Ajoute une entrée de journal JSON lue depuis stdin (écriture atomique ; --json renvoie { id } pour chaîner côté agent) |
notabene protocol | Imprime le protocole agent sur stdout — --path affiche son chemin, --write rafraîchit <store>/protocol.md |
Flags globaux
| Flag | Signification |
|---|---|
--root <path> | Racine du repo consommateur (défaut : cwd) |
--config <path> | Chemin de la config (défaut : <root>/notabene.config.mjs) |
--port <n> | Port du serveur de dev (sinon la config port, sinon un port libre) |
--detach | dev uniquement : démon d’arrière-plan (géré par status/stop) |
--detect | init uniquement : préremplit roots[] avec les dossiers de doc trouvés |
--no-protocol / --no-agents-md | init uniquement : ne pas écrire la copie <store>/protocol.md / le bloc AGENTS.md |
--host | Expose sur le LAN — réseaux de confiance uniquement (sécurité) |
--public / --site / --base / --out | build uniquement : l’artefact de site public |
Clés de configuration
Clés de configuration
notabene.config.mjs est un module ES de données uniquement à la racine de votre
repo ; chaque clé est optionnelle. La version narrative, avec exemples, se trouve dans
le guide de configuration.
| Clé | Défaut | Signification |
|---|---|---|
siteName / tagline | "Docs" / "docs" | Marque de l’en-tête |
locale | "en" | Langue de l’UI + collation du tri de la nav |
format | "mdx" | "mdx" (.mdx strict + .md tolérant) ou "commonmark" (pas de MDX du tout). init génère "commonmark" |
roots[] | [{docs}] | Espaces de doc : { key, label, path, exclude, description, publish }. label/description acceptent une map par locale avec l’i18n ; publish: false garde l’espace hors des builds publics |
store | "docs/.notabene" | Dossier commentaires + journal — committez-le (contrat) |
home | — | Page d’accueil personnalisée : un fichier Markdown relatif au repo (ou une map par locale) rendu au-dessus des cartes d’espaces sur / |
branding | — | Assets d’identité : { logo, logoDark, favicon, socialImage }, fichiers relatifs au repo servis sous /_nb/…. Favicon non défini → une marque par défaut intégrée |
theme | — | Personnalisation du rendu : { tokens, css, assets, code } — surcharges de tokens --nb-* (validées ; une coquille lève une erreur), une feuille de style chargée après celle du renderer (sûre vis-à-vis des cascade layers), un dossier du repo servi sous /_nb/assets/… pour les polices/images (allow-list d’extensions, pas de traversée), un thème Shiki pour le code ("github-light" ou { light, dark } — les deux palettes suivent le sélecteur de schéma), et mermaid: false pour garder la palette de diagrammes propre à Mermaid |
nav | — | Liens sortants : { header[], sidebar: { title, links[] }, footer: { links[], text, poweredBy } }. Une seule forme d’entrée — { label, href, icon, iconOnly, publish } ; label accepte une map par locale, publish: false garde un lien hors des builds publics. Validé au chargement (allow-list de schémas, noms d’icônes, doublons) |
port | 3009 | Port d’astro dev |
host | false | true/NOTABENE_HOST=1/--host expose l’API d’écriture au LAN (sécurité) |
verify[] | [] | Vérifications post-édition que l’agent exécute (le build du renderer s’exécute toujours) |
review | "auto" | "auto" = l’agent résout les commentaires ; "approve" = l’agent propose (addressed), vous validez chacun sur /review avec un diff (boucle de revue) |
author | git user.name | Auteur de commentaire par défaut ; chaque navigateur le remplace par appareil via le dialogue d’identité |
authorEmail | git user.email | E-mail d’auteur par défaut ; intégré façon git (Name <email>) pour garder les identités uniques |
editPattern | — | Lien « Modifier cette page » sous chaque page : une URL avec un placeholder {path} (chemin source relatif au repo), ex. https://github.com/o/r/edit/main/{path}. Placeholder obligatoire — validé au chargement |
pdf | { enabled: true, pageSize: "A4", margin: "18mm" } | Export PDF — enabled active le menu Export + les routes /print ; pageSize/margin définissent la boîte @page |
i18n | — | Doc multilingue : { locales, defaultLocale, strategy: "directory"|"suffix" }. Omettez pour une seule langue |
publish | — | Cible du build public : { site, base, exclude }. site optionnel — omis = artefact agnostique de l’origine |
Surcharges CLI/env
--site/--base surchargent publish.site/publish.base ; NOTABENE_HOST=1 équivaut
à host: true ; la CLI passe l’identité git du repo comme repli pour
author/authorEmail. Rien d’autre n’est configurable hors de ce fichier.
Frontmatter
Frontmatter
Du YAML optionnel tout en haut d’une page. Tout a un défaut raisonnable — un repo sans aucun frontmatter se rend très bien (noms de fichiers humanisés, ordre alphabétique).
---
title: Internal network map # <title> de la page + fil d'Ariane (prime sur le premier H1)
description: Segments and VLANs. # builds publics : meta description + OpenGraph + JSON-LD
publish: false # builds publics : exclut cette page entièrement
lastUpdated: 2026-05-04 # pied de page : prime sur la date git « Mis à jour le »
sidebar:
label: Network map # texte de la sidebar (sinon title, sinon nom de fichier humanisé)
order: 9 # position parmi les pages sœurs (croissant)
indexLabel: Start here # pages d'accueil de dossier : renomme l'entrée « Aperçu »
---
| Clé | Effet |
|---|---|
title | <title> de la page, fil d’Ariane, titre du résultat de recherche. Se replie sur le premier # H1, puis sur le nom du fichier |
description | Builds publics : <meta name="description">, description OpenGraph/Twitter, JSON-LD |
publish: false | Builds publics : la page n’est pas construite — ni route, ni nav, ni recherche, ni llms, ni double, ni sitemap. Les builds dev/normaux la montrent toujours |
lastUpdated | Prime sur la date dérivée de git dans le Mis à jour le du pied de page. Toute date que YAML sait parser. Utile quand l’historique git induit en erreur (contenu importé ou généré) |
sidebar.label | Texte de l’entrée dans la sidebar. Résolution : sidebar.label → title → nom de fichier humanisé |
sidebar.order | Clé de tri parmi les pages sœurs, croissante. Les entrées sans valeur gardent l’ordre alphabétique, après celles qui sont ordonnées. Groupes et pages partagent un seul ordre |
sidebar.indexLabel | Sur la page d’accueil d’un dossier : renomme son entrée Aperçu localisée |
Dossiers
Un dossier est nommé et positionné par sa page d’accueil — <folder>/index.md (ou
readme.md) : son frontmatter sidebar s’applique au groupe entier, et la page
elle-même apparaît comme l’entrée Aperçu du groupe. Labels et ordre se propagent aux
fils d’Ariane et aux couvertures PDF.
Les clés inconnues sont ignorées et préservées — les agents qui éditent une page doivent garder le frontmatter existant intact (le skill de revue le fait).
Le contrat du store .notabene
Le contrat du store .notabene
Le store est un contrat public versionné — committé dans votre repo, lu et écrit par
les agents. Sa forme ne change jamais silencieusement : <store>/meta.json porte
{ "schemaVersion": n } (actuellement 3), et tout changement de forme s’accompagne
d’un migrateur (notabene migrate).
Disposition
<store>/
meta.json # { "schemaVersion": 3 }
journal.json # un tableau d'entrées de journal
protocol.md # le protocole agent (écrit par `init` ; ce n'est pas de la donnée)
<page>/<comment-id>.json # UN FICHIER PAR COMMENTAIRE → merges git sans conflit
meta.json, journal.json et protocol.md sont des noms réservés à la racine du
store ; tout le reste est de la donnée de commentaire. Les lecteurs ne parsent que du
.json, donc protocol.md est inerte — il voyage avec le store pour que la spec soit
toujours à côté des commentaires.
<page> est le chemin logique de la page (docs/guide/setup — avec l’i18n c’est l’id
brut encodant la locale, les commentaires sont donc par langue). Les anciens stores v1
(un tableau par page) sont toujours lus ; toute écriture migre la page concernée.
Un commentaire
{ "id", "space", "page", "scope", // scope : "selection" | "page" | "block"
"anchor": { // selection : citation de texte façon W3C
"quote", "prefix", "suffix", "section" // texte rendu + contexte + titre le plus proche
} | { "kind", "key", "label", // block (diagramme/image) : clé dérivée du contenu
"section", "index" } | null,
"thread": [{ "author", "body", "ts" }], // author peut être façon git "Name <email>"
"status": "open" | "addressed" | "resolved",
"hold": false, // true → l'agent l'ignore (WIP du relecteur)
"resolution": { "note", "journalEntryId" } | null,
"createdAt", "updatedAt" }
addressed est l’état de la revue en deux phases : proposé
par l’agent, en attente de validation humaine sur /review.
Une entrée de journal
{ "id", "date", // YYYY-MM-DD
"title", "summary",
"changes": [{ "page", "commentIds": [], "what", "why" }] }
Un enregistrement changes[] par page réellement touchée — le diff de /review est
construit en inversant le journal, donc une page qui n’y figure pas n’apparaîtra pas
dans le diff du relecteur. Le resolution.journalEntryId de chaque commentaire résolu
pointe vers son entrée.
Règles que les agents doivent honorer
- Les écritures sont atomiques (fichier temporaire + rename) — ne jamais écrire du
JSON partiel à la main. La CLI (
comments done/reopen,journal add) s’en charge ;comments verifyaudite le résultat. - Ne jamais supprimer le store en masse ; supprimez un commentaire précis par id si on vous le demande.
- Ne traiter que
status: "open"ethold: false. - Le protocole complet vit dans
<store>/protocol.md— écrit parnotabene init, committé avec le store, rafraîchi en relançantinit. Pointez-y n’importe quel agent.
Modèle de sécurité
Modèle de sécurité
L’API de commentaires écrit dans votre git — elle est donc cloisonnée, par construction :
- Dev uniquement. Le chemin d’écriture n’existe que sous
notabene dev. Enbuild/previewles mutations renvoient403, et un build public ne contient pas ces routes du tout. - Loopback par défaut. Le serveur se lie à
127.0.0.1; l’API d’écriture n’est pas joignable depuis votre réseau sans opt-in explicite via--host/NOTABENE_HOST=1— réseaux de confiance uniquement. - Chaque écriture est gardée au-delà du bind : les requêtes cross-origin sont
refusées (anti-CSRF), un en-tête
Hostnon-loopback est refusé en mode loopback (anti-DNS-rebinding), et — quand vous définissezNOTABENE_TOKEN— chaque écriture doit porter unx-notabene-tokencorrespondant. Définir un token est recommandé avec--host. - Une identité par personne. Sur un hôte non-loopback, chaque visiteur est invité à renseigner son nom (+ e-mail optionnel) avant de naviguer, pour que les commentaires soient attribués à de vraies personnes plutôt qu’au défaut git du propriétaire du repo.
- L’agent ne committe jamais sans demander et ne supprime jamais le store en masse — cela fait partie du protocole.
- La CLI est une surface distincte. Les règles ci-dessus encadrent l’API d’écriture
HTTP. Les commandes qui écrivent dans le store (
comments done/reopen,journal add) sont des commandes locales que vous — ou un agent dans votre terminal — lancez délibérément : ni serveur, ni port, ni réseau. Écriture atomique, un commentaire à la fois, etcomments verifyaudite le résultat.
L’artefact public est l’image miroir : pas d’API d’écriture, pas de données du store, pas d’identité — rien à garder, parce que rien n’est construit.