🧠 Obsidian · & le vault autoportant

Chapitre 10
Les conventions : arborescence plate et front-matter normé

Objectifs du chapitre

1. L'arborescence

vault/
├── 00-inbox/          # capture brute, sans front-matter
├── 10-journal/2026/   # notes quotidiennes, un sous-dossier par année
├── 20-notes/          # fiches de connaissance, à plat
├── 30-projets/        # une note par project, à plat
├── 80-assets/         # images, PDF
├── 90-meta/           # manifeste, conventions, templates, vues, outillage
└── 99-archive/

Les préfixes numériques ne servent qu'à l'ordre d'affichage. Aucun dossier n'a de sous-dossier, sauf 10-journal/ découpé par année pour ne pas atteindre mille fichiers dans un même répertoire.

Pourquoi si peu

Une hiérarchie profonde impose de répondre à « où ça va ? » à chaque note, et cette question n'a souvent pas de bonne réponse. Une fiche sur les temps de cycle PROFINET appartient-elle au réseau, à la performance, ou au projet en cours ? Les trois. Un dossier n'en garde qu'un.

Le modèle plat déplace la question : on ne demande plus ranger, mais ce que c'est. Le classement se reconstruit ensuite à volonté par requête — et une note peut apparaître dans autant de vues qu'elle concerne, sans jamais être dupliquée ni déplacée.

Le test qui valide une arborescence : combien de temps hésites-tu avant de créer une note ? Plus d'une seconde, et la structure travaille contre toi. Avec cinq dossiers dont trois seulement reçoivent des notes rédigées, l'hésitation disparaît.

Ce que porte chaque dossier

DossierContenuFront-matter
00-inbox/Une ligne horodatée par capture, un fichier par moisaucun, par conception
10-journal/Une note par jourtype: daily
20-notes/Fiches durables, une idée par note type: concept
30-projets/Une note par projet, cible des liens projectstype: project
90-meta/Conventions, vues, templates, manifeste, outillageselon le fichier
99-archive/Ce qu'on ne veut plus voir sans vouloir le supprimernon validé

2. Le contrat, rappelé

Le chapitre 3 l'a introduit ; le voici comme règle exécutable. Cinq champs obligatoires partout :

type: daily | project | concept
title: …
created: 2026-08-02       # ISO, jamais modifié
updated: 2026-08-02       # touché à chaque édition
scope: work | personal

Plus, selon le type : status obligatoire sur un projet (active, dormant, closed), repo et milestones optionnels ; sources et see_also sur un concept ; projects sur une note quotidienne.

Ce contrat est documenté dans 90-meta/conventions.md — une note ordinaire, lisible dans Obsidian, à côté des vues qu'elle rend possibles.

3. Le validateur

$ uv run --project 90-meta/tooling 90-meta/tooling/vault.py doctor
0 probleme(s)

Il parcourt les notes du périmètre et vérifie cinq choses : le front-matter est présent et lisible, les champs obligatoires sont là, type et scope sont dans leur énumération, un projet a un status valide, et chaque lien de projects vise une note qui existe dans 30-projets/.

La précédence des problèmes

Le validateur s'arrête au premier problème d'une note plutôt que de tout accumuler :

# La précédence est délibérée : un champ manquant rend les contrôles
# suivants ininterprétables, on ne les tente pas.
for champ in CHAMPS_OBLIGATOIRES:
    if champ not in entete:
        problemes.append(...)
if problemes:
    return problemes
if entete["type"] not in TYPES:
    return [Probleme(relatif, f"type invalide : {entete['type']}")]

Signaler « type invalide » sur une note où type est absent n'aide personne. Le comportement est fixé par deux tests, pour qu'un remaniement ne le change pas par accident.

Une note illisible n'arrête pas le reste

Version initiale du validateur : yaml.safe_load sans garde. Une seule note avec un YAML mal formé — un deux-points non quoté suffit — levait une exception qui remontait jusqu'à la sortie du programme. Résultat : une note cassée masquait les diagnostics de toutes les autres, sur un outil dont le seul travail est de lister les problèmes.

La note illisible doit devenir un problème signalé comme un autre, et le parcours continuer. Même traitement pour un fichier illisible en UTF-8.

4. Le périmètre : liste blanche

DOSSIERS_VALIDES = ("10-journal", "20-notes", "30-projets", "90-meta")
DOSSIERS_EXCLUS = ("90-meta/templates", "90-meta/tooling")

Le validateur ne regarde que ces dossiers, moins ces exclusions. La formulation inverse — une liste noire de ce qu'on ignore — paraît plus naturelle et se révèle fausse.

Ce qui doit rester dehors, et pourquoi :

C'est le dernier point qui tranche. Avec une liste noire, chaque nouveau fichier apparu à la racine devient un faux positif jusqu'à ce qu'on pense à l'ajouter.

Le cas réel : 90-meta/tooling/ est dans le périmètre, et pytest y écrit .pytest_cache/README.md. Lancer la suite de tests suffisait donc à faire échouer doctor — sur un fichier généré, sans rapport avec le contenu du vault. Pire, le test d'acceptation du vault lançait doctor depuis pytest : il échouait à cause d'un fichier que pytest venait de créer lui-même.

5. Les vues comme notes

Les trois vues du vault vivent dans 90-meta/vues/ et sont des notes ordinaires, avec leur front-matter type: concept. Elles sont donc versionnées, liables, et validées comme les autres.

Ce n'est pas anodin. Une vue rangée dans la configuration serait invisible dans le vault, non liable, et perdue à la reconstruction. Comme note, elle se cite, se modifie, s'améliore au fil de l'usage.

Concepts orphelins est celle qui rend le mieux service — elle liste les fiches que rien ne cite et qui ne citent rien. C'est le détecteur de base de connaissance qui pourrit : une fiche isolée ne remontera jamais dans un lien retour, donc ne sera jamais relue, donc n'aurait pas dû être écrite sous cette forme.

6. Comment les pièces se tiennent

Chaque élément de ce chapitre dépend des autres, et c'est ce qui rend l'ensemble stable :

PièceCe qu'elle exigeCe qu'elle permet
Arborescence plateUn classement porté ailleurs Aucune hésitation au moment d'écrire
Front-matter norméDes templates qui l'appliquent Des vues qui reconstruisent le classement
TemplatesTemplater correctement déclenché Que la voie conforme soit la plus rapide
ValidateurUn périmètre bien défini Que la dérive se voie en trois secondes
VuesUn front-matter fiable Que le vault réponde à des questions

Retire une pièce et l'édifice penche. Sans templates, le front-matter dérive et les vues se vident. Sans validateur, la dérive ne se voit qu'au moment où une vue devient fausse — trop tard. Sans vues, plus rien ne justifie l'effort du front-matter, et on retourne aux dossiers.

Récapitulatif

Exercices

Exercice 1 — Placer une note

Où ranges-tu, et avec quel front-matter : (a) le compte-rendu d'un essai du 12 août ; (b) une fiche sur la norme IEC 61158 ; (c) une idée notée en réunion, pas encore réfléchie ; (d) le suivi d'un projet terminé l'an dernier ?

Voir la solution

(a) Dans la note quotidienne du 12 août, 10-journal/2026/, comme une section. Un essai est un événement, il appartient à sa date. S'il mérite une conclusion durable, celle-ci devient une fiche dans 20-notes/ que la note du jour cite.

(b) 20-notes/, type: concept, avec sources renseigné. Connaissance durable, pas datée.

(c) 00-inbox/ via la capture — pas de front-matter, pas de décision de rangement. C'est tout l'intérêt : décider coûte, et à ce moment-là tu es en réunion.

(d) Il reste dans 30-projets/ avec status: closed. Le déplacer vers 99-archive/ le sortirait du périmètre de validation et casserait les liens des notes quotidiennes qui le citent — enfin, pas les liens eux-mêmes, mais le contrôle qui vérifie qu'ils pointent quelque part. L'archive est pour ce qu'on ne veut plus voir du tout.

Exercice 2 — Étendre le contrat

Tu veux ajouter un type source pour les références externes — normes, articles, documentation constructeur. Quelles modifications, et dans quel ordre ?

Voir la solution

Cinq fichiers, dans un seul commit — c'est ce qui rend l'ajout d'un type volontairement coûteux :

  1. vaulttool/notes.py : ajouter "source" à TYPES, et les champs propres au type s'il y en a d'obligatoires.
  2. tests/test_notes.py : un test qui accepte une note type: source valide, un qui refuse une variante fautive.
  3. 90-meta/conventions.md : documenter le type et ses champs. C'est le document que tu reliras dans six mois.
  4. 90-meta/templates/source.md : le template, sans quoi le type ne sera pas utilisé correctement.
  5. Une vue, ou une colonne dans une vue existante. Un type qu'aucune vue n'expose ne sert à rien : personne ne verra jamais ces notes autrement qu'en les cherchant.

La vraie question, avant tout ça : est-ce qu'une fiche concept avec sources renseigné ne fait pas déjà le travail ? Un type se justifie quand il porte des champs différents et qu'on veut l'interroger séparément. Sinon, c'est une étiquette.

Exercice 3 — Le validateur qui certifie à tort

vault.py doctor --racine /chemin/qui/n-existe-pas retourne 0 probleme(s) et sort en code 0. Pourquoi, et est-ce grave ?

Voir la solution

Pourquoi : le parcours repose sur rglob et glob, qui sur un dossier inexistant retournent simplement une séquence vide plutôt que de lever une erreur. Zéro note parcourue, donc zéro problème trouvé, donc « tout va bien ».

Est-ce grave : en usage interactif, non — on voit qu'on s'est trompé de chemin. Dans un script, oui : un contrôle qui répond « conforme » alors qu'il n'a rien examiné est pire qu'un contrôle absent, parce qu'on lui fait confiance.

C'est la même famille de défaut que l'exercice 2 du chapitre 9 : la commande ne distingue pas « rien à signaler » de « rien examiné ». Le correctif tient en deux lignes — vérifier que la racine existe et contient au moins un des dossiers attendus, sinon échouer franchement.

Généralisation utile : tout contrôle qui peut retourner « conforme » sans avoir rien vérifié doit être capable de dire combien d'objets il a examinés. Afficher « 0 probleme(s) sur 148 notes » aurait rendu le défaut visible immédiatement.