À propos de ce document
Ce document est la référence des conventions de code de CapsuleOS. Chaque règle y est énoncée avec sa raison d'être et, le plus souvent, l'outil automatique qui la vérifie. Les exemples sont tirés du dépôt réel, pas inventés pour l'illustration.
CapsuleOS reproduit des bureaux réels (Linux, Windows, macOS, iOS, Android) en HTML5, CSS3 et ECMAScript purs, sans aucun framework, exécutables directement depuis le système de fichiers (file://) comme derrière un serveur HTTP minimal. Cette contrainte d'enveloppe, 100 pour cent statique, n'est pas une limite subie : c'est un choix d'accès, pour l'école sans chaîne d'intégration, le formateur avec une clé USB, l'élève hors ligne.
Principe directeur : une règle qui compte possède un vérificateur automatique. Une règle sans vérificateur n'est pas une norme, c'est une intention.
1. Le dépôt comme système formel
Le cœur de la méthode emprunte à la logique. L'état du projet est décrit par des prédicats : des propriétés vraies ou fausses, chacune adossée à une commande qui la vérifie. Des règles d'inférence, évaluées de haut en bas (la première admissible l'emporte), relient un prédicat non satisfait à l'unique action suivante.
Chaque règle est un objet déclaratif : une garde, un message, une commande, et le prédicat qu'elle débloque en cas de succès.
{
rule: 'R-H1',
when: () => !gates.H2 && !(gates.ManV && !gates.ManI),
message: 'base dépôt non saine : lancer la gate socle',
command: 'node usr/lib/capsuleos/tools/validate-all.mjs',
autoExecute: true,
gateOnSuccess: 'H2',
}
Un résolveur évalue les règles et exécute l'action unique quand elle est admissible ; il s'arrête dès qu'une décision requiert un humain (autoExecute à faux). Comme chaque succès verrouille un prédicat, la garde correspondante devient définitivement fausse : la progression est monotone et converge vers un état clos.
Gate ou ça n'existe pas : une affirmation qui ne peut pas être réfutée par un script n'a pas valeur de connaissance.
2. Le système en schémas
Trois schémas pour saisir l'ensemble : la pile des projections (comment le réel devient une expérience dans le navigateur), la boucle de décision formelle (comment l'outil choisit l'action suivante), et la frontière entre sources et fichiers générés.
- RéelMachine de référence et documentation
- ModèleVérité mesurée, versionnée dans le dépôt
- NoyauComportements communs, sans nom de vendor
- PeauTokens, chrome et mise en page d'un système
- FaçadePoint d'entrée stable, généré
- ExpérienceDOM hydraté dans le navigateur
- État du dépôtPrédicats vrais ou faux
- Première règle admissibleÉvaluée de haut en bas
- Action uniqueExécutée si automatisable
- GateVerrouille le prédicat, on reboucle
- Sourceshome/, window/, contrats
- Générationsync-all-views.mjs
- ProjectionsFaçades OS/, embeds var/ (jamais éditées à la main)
3. La carte du dépôt (zones Z0 à Z4)
Le dépôt se lit en cinq zones, du plus abstrait (les contrats et la vérité mesurée) au plus concret (les projections servies au navigateur). Ne pas mélanger les zones est une règle de fond : le flux va toujours de Z0 vers Z4, jamais l'inverse.
- Z0Contrats et vérité VM
- Z1Noyau et moteurs
- Z2Peaux
- Z3Façades générées
- Z4Embeds générés
| Zone | Chemin | Contenu | Écrit par |
|---|---|---|---|
| Z0 | etc/, proc/ | Contrats, registre, vérité VM | outils lab |
| Z1 | usr/lib/, usr/share/ | Noyau, moteurs, gabarits, assets, outils | humains / agents |
| Z2 | home/<Vendor>/<Version>/ | La peau de chaque OS | humains / agents |
| Z3 | OS/.../index.html | Façades (générées) | sync-all-views |
| Z4 | var/lib/capsuleos/generated/ | Embeds (générés) | sync-all-views |
On n'édite jamais à la main une façade (Z3) ni un embed (Z4). Un comportement partagé par deux OS d'un même toolkit va dans usr/lib/ (Z1), pas dupliqué dans deux peaux (Z2).
4. HTML sémantique
Chaque page déclare sa langue, encode en UTF-8, et s'organise autour de repères sémantiques (header, main, footer) avec un lien d'évitement et des régions nommées. L'accessibilité n'est pas un ajout tardif : elle est inscrite dans la structure.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Titre de la page | CapsuleOS</title>
<link rel="canonical" href="https://os.lacapsule.org/exemple/">
</head>
<body class="portal-page">
<a class="skip-link" href="#contenu">Aller au contenu</a>
<header class="header"> ... </header>
<main id="contenu"> ... </main>
<footer class="footer"> ... </footer>
</body>
</html>
Façades générées contre sources
Les points d'entrée publics des bureaux (OS/.../index.html) ne s'écrivent pas à la main : ils sont générés depuis la peau correspondante sous home/. Chaque façade porte un marqueur explicite, un lien de base recalculé, et l'identifiant de peau sur le body.
<!-- Façade URL stable : pick-os.js -> cette page, générée depuis home/Debian/Mint/ (ne pas éditer) -->
<base href="../../../../../home/Debian/Mint/">
<link rel="canonical" href="https://os.lacapsule.org/OS/linux/families/debian/mint/index.html">
...
<body id="mint">
On n'édite jamais une projection générée : toute modification visible se répercute via la régénération des vues (node usr/lib/capsuleos/tools/sync-all-views.mjs). Éditer une façade à la main, c'est un travail perdu au prochain build, et sans erreur apparente.
5. CSS par variables (design tokens)
L'interface publique du noyau, ce sont les variables CSS, pas les classes privées d'une peau. Les tokens jouent le rôle d'API stable : la peau configure, elle ne réimplémente pas. Cette thèse évite deux dérives : le pixel copié sans structure, et la logique d'interface cachée dans un framework.
Tokens primitifs
:root {
--capsule-space-unit: 0.25rem;
--capsule-radius-md: 0.5rem;
--capsule-z-window: 100;
--capsule-duration-fast: 120ms;
}
Tokens calculés
Les dimensions dérivées se composent par calc à partir d'une unité de base (ici la hauteur d'en-tête), ce qui garde l'ensemble cohérent quand une seule valeur change.
--head: 44px;
--head-quarter: calc(var(--head) / 4);
--header-font-size: calc(var(--head) * 0.4);
--win-calculator-width: 360px;
Portée par peau
Chaque règle est scopée à sa peau, par le sélecteur body#<skin> ou par html:has(#<skin>). Aucun style global ne fuit d'un bureau à l'autre : c'est la condition du cloisonnement visuel entre systèmes.
body#mint .cinnamon-panel { height: var(--head); }
html:has(#chromeos) .shelf { background: var(--chromeos-shelf-bg); }
Ordre canonique des propriétés
Dans chaque règle, les propriétés suivent un ordre fixe : position, display, dimensions, margin, padding, border, font, color, background, transform, animation, transition, overflow, z-index. La lecture d'un bloc devient prévisible.
.doctrine-codeblock figcaption {
display: flex;
gap: 0.5rem;
padding: 0.4rem 0.75rem;
border-radius: 6px 6px 0 0;
font-size: 0.8rem;
color: var(--lcap-blanc);
background: rgba(0, 0, 0, 0.35);
}
Pas d'imbrication (nesting) CSS : chaque sélecteur est écrit à plat, pour rester lisible et portable sans préprocesseur.
6. ECMAScript strict et partagé
Le JavaScript de navigateur vise un ECMAScript strict mais volontairement sobre : pas de modules, pas de sucre syntaxique récent, pour rester exécutable en file:// sans étape de construction. Les outils Node en .mjs, eux, sont exemptés et utilisent les modules ES.
La forme canonique
Un fichier du noyau est une fonction immédiatement invoquée (IIFE), en mode strict, qui expose son module sur l'objet global sans polluer la portée.
/**
* CapsuleResource : résolution logique vers physique des assets (manifest-driven).
*/
(function initCapsuleResource(global) {
'use strict';
const DEFAULT_MANIFEST = { /* ... */ };
// ...
}(this));
Le sous-ensemble du langage
Sont interdits dans le code navigateur : import / export, l'accès optionnel ?., le coalescent ??, et l'étalement d'objet {...obj}. Sont autorisés : l'étalement de tableau, l'étalement d'arguments, et le paramètre rest.
// INTERDIT (navigateur)
import { x } from './mod.js';
const v = obj?.a ?? fallback;
const clone = { ...obj, flag: true };
// AUTORISÉ
const copy = [...arr];
const out = fn(...args);
function build(first, ...rest) { /* ... */ }
Cette discipline n'est pas un vestige : elle est mesurée. Sur les fichiers du noyau, le taux de conformité au mode strict est de cent pour cent, vérifié par la gate validate-vanilla-js. Un tel chiffre n'est tenable qu'avec une vérification automatique.
Noyau partagé, sans vendor
Le noyau commun ne nomme aucune marque. Un comportement partagé par au moins deux systèmes du même toolkit remonte dans usr/lib/, il n'est jamais copié dans deux dossiers home/. Chaque identité (une distribution, un système) est scellée et n'emprunte rien à une autre.
Communication par événements
Les couches dialoguent par un bus d'événements, jamais par des dépendances dures. Émettre une tâche pédagogique depuis le noyau se fait par un point d'extension vérifié avant usage.
if (typeof dispatchCapsuleTask === 'function') {
dispatchCapsuleTask('mon-task-id');
}
// côté checklist : écoute de l'événement
document.addEventListener('capsule:task', function (e) {
validerTache(e.detail.id);
});
7. Le terminal : un moteur, des projections
Le terminal simulé repose sur un moteur unique (usr/lib/capsuleos/shells/linux/terminal/), projeté par famille (debian, redhat, suse, arch), puis par vendor, puis par peau. Forker la fonction d'exécution des commandes par distribution est interdit : les singularités se déclarent en couches, elles ne dupliquent pas le noyau.
Le rendu des sorties est fidèle : indentation, colonnes de ls, sauts de ligne, couleurs. Les couleurs viennent de classes et de tokens CSS, jamais de codes hexadécimaux en dur dans le code d'exécution.
// BON : une classe, une couleur définie en token CSS
ligne.innerHTML = '<span class="term-path">' + chemin + '</span>';
// .term-path { color: var(--term-bleu); }
// À ÉVITER : couleur codée en dur dans l'exécuteur
ligne.innerHTML = '<span style="color:#3465a4">' + chemin + '</span>';
Un comportement commun à deux systèmes ou plus va dans le moteur partagé ; seules les singularités vendor vivent dans une couche d'extension dédiée.
8. Rafraîchissement des vues
Condition sine qua non d'une reproduction fidèle : distinguer le réel de l'irréel. Une vue est réelle quand elle est la projection du modèle après une action (V = projection(M)). Elle est irréelle quand elle diverge du modèle, ou quand un rafraîchissement survient sans qu'aucune action ne l'ait justifié.
- Après une action de l'utilisateur, la vue doit être cohérente : rendu ou synchronisation explicite, pas un affichage périmé.
- Aucun rafraîchissement parasite : ni sur un simple focus, ni sur une course entre tâches, ni sur un minuteur.
- La référence (VM ou scénario de test) définit ce que doit être la projection ; on ne valide pas au ressenti.
Un slot interactif (explorateur, terminal) n'est considéré comme juste que si ses deux conditions tiennent : vue cohérente après action, et absence de rafraîchissement intempestif.
9. Internationalisation
Le français est la langue par défaut (documentation, chaînes, expérience). L'anglais et le clavier QWERTY arrivent en couche additive, jamais en fork du noyau par langue.
- Défaut du projet : fr-FR, pour les docs, les chaînes et l'interface.
- Projection internationale : chaînes en anglais et fournisseurs dédiés, ajoutés par-dessus, pas en duplication.
- Clavier : AZERTY par défaut, QWERTY activable par une variable d'environnement.
La parité avec la machine de référence utilise la locale de l'inventaire (par exemple une sortie dnf en français) : le défaut projet ne sert jamais à masquer un écart réel documenté.
10. Cloisonnement par couches
L'architecture est stratifiée, du réel observé jusqu'à l'expérience vécue dans le navigateur. Chaque couche individue un ensemble de propriétés sans absorber la précédente.
- Réel : la machine de référence et sa documentation.
- Modèle : la vérité mesurée, versionnée dans le dépôt.
- Noyau : les comportements communs, sans nom de vendor.
- Peau : les tokens, le chrome et la mise en page propres à un système.
- Façade : le point d'entrée stable, généré.
- Expérience : le DOM hydraté dans le navigateur.
Le cloisonnement est gardé des deux côtés : une liste blanche définit positivement ce qu'une peau peut être, une liste noire interdit ce qu'elle ne doit jamais charger, avec un cliquet qui transforme tout nouvel emprunt en échec. Mélanger les couches (le dock d'un système sur un autre) n'est pas une optimisation : c'est une confusion qui détruit la crédibilité des deux.
11. Clean code : écrire dans la bonne couche
Le clean code, ici, ne désigne pas un style esthétique arbitraire : c'est l'alignement des écritures (code, documentation, données) sur les bonnes couches et le bon périmètre, avec le minimum de lignes vérifiables. Écrire au mauvais endroit parasite autant les humains que les agents.
Trois surfaces, une même exigence. Le code va dans sa zone (noyau, peau, outils) sans duplication par distribution. La documentation suit le corpus existant, sans plan d'exécution parallèle. Les données portent la vérité locale, sans instantané éphémère versionné.
La règle de sobriété
Avant d'ajouter un fichier, trois questions. Si la réponse est non aux trois, on n'ajoute pas.
- Une gate ou un prédicat l'exige-t-il vraiment ?
- Un fichier existant peut-il être étendu plutôt que dupliqué ?
- La ligne ajoutée est-elle une donnée (JSON, contrat) plutôt qu'une logique ?
Diff minimal : une intention, une zone, une gate verte. Le code et le contrat portent la vérité ; on ne commente que l'invariant non évident, jamais le récit de ce que fait la ligne.
12. Données et contrats
La vérité vit dans des contrats JSON lisibles par machine ; le code lit la donnée, il ne la duplique pas. Avant d'ajouter une ligne de logique, on se demande si une ligne de donnée suffirait.
Les parcours pédagogiques, par exemple, sont entièrement déclaratifs : chaque étape nomme sa validation, et le noyau sait l'interpréter sans code spécifique.
{
"id": "s01-ouvrir-terminal",
"label": "Ouvrir un terminal",
"validation": "terminal-command",
"commandPattern": "^(gnome-terminal|konsole)$",
"hint": "Cherchez l'application Terminal dans le menu."
}
Un nouveau type de validation est d'abord une extension du noyau (le runtime qui l'interprète), avant d'être utilisé dans un contrat. On n'invente pas une règle que rien ne sait appliquer.
13. Assets : provenance et traçabilité
Tout asset vient de la machine de référence, jamais inventé ni emprunté à un autre vendor. Un artefact manquant pour une distribution est un échec visible, pas un repli sur une autre distribution ou une autre version.
Deux mondes d'assets à ne pas confondre : le système (usr/share/capsuleos/assets/) et le système de fichiers utilisateur simulé (home/public/Images/).
- Tiré du guest via pull-vm-assets.sh, tracé dans vendors/<vendor>/SOURCE-VM.txt.
- Jamais de crop de capture : on copie le fichier complet depuis la machine.
- Tout raster est converti en WebP (prepare-web-media).
- Vérifié par validate-asset-zones, validate-no-gt-crop-assets et validate-skin-vendor-isolation.
Vérité locale par identité : l'absence d'un asset pour une distribution ne se comble jamais par l'emprunt silencieux à une autre. L'écart reste visible jusqu'à ce que le bon fichier soit tiré de la bonne source.
14. Mesurer, et séparer le fait de la permission
La fidélité d'une reproduction ne se proclame pas, elle se mesure. On compare pixel par pixel l'écran de référence et son clone, à géométrie égale, en masquant les zones mouvantes (comme l'horloge). Le résultat est un score.
// compared = largeur * hauteur - surface masquée
const phi = Math.round((1 - mismatched / compared) * 1000) / 10;
// seuils : match si phi >= 88 ET écart de géométrie <= 8 px ; sinon partial
Deux questions restent soigneusement distinctes : ce qui est vrai, et ce qui est autorisé. Ce qui est mesuré (l'état réel d'une reproduction) ne se confond jamais avec ce qui est publié (la décision de mise en avant). Un défaut bloquant ne peut pas être requalifié en détail mineur pour franchir une étape.
Non mesuré n'est ni vrai ni faux : c'est un troisième état, assumé. Un manque reste visible, classé et planifié, jusqu'à sa résolution.
15. Sécurité : un simulacre à risque nul
Le paradigme de sécurité découle de la nature du projet : CapsuleOS est un simulacre fonctionnel. La ressemblance avec un vrai bureau est maximale, mais rien ne s'exécute vraiment, il n'y a ni noyau réel, ni hyperviseur, ni opération système derrière l'interface. Le risque système est nul par construction.
Cette propriété est aussi un atout pédagogique : on peut y rejouer un hameçonnage, un faux support technique ou un rançongiciel sans aucun danger réel. Les faux sites pédagogiques sont clairement isolés et non indexables.
La règle fondatrice : la ressemblance est haute, le risque reste nul. On ne présente jamais CapsuleOS comme un environnement de production pour des opérations sensibles (réseau réel, données critiques).
Surface et secrets
Le site statique n'exécute aucun contenu utilisateur côté serveur. Le paquet de production est assaini avant publication : les configurations sensibles (clés de développement, adresses d'administration) ne partent jamais en ligne.
La sécurité se vérifie, comme le reste
- Analyse statique de sécurité (validate-owasp-static).
- Aucun secret commité (validate-git-security).
- Aucune donnée personnelle identifiante (validate-no-identifying-data).
- Pages internes et faux sites pédagogiques en noindex (en-tête X-Robots-Tag via .htaccess).
- Push forcé refusé par un garde-fou ; inclusion en iframe encadrée par une politique de sécurité de contenu (CSP).
Un signal explicite aux robots : l'exploration est autorisée, mais l'entraînement de modèles est refusé (robots.txt, ai-train=no). Un choix de respect du contenu.
16. Accessibilité : conformité et choix
L'accessibilité se mesure aussi. Les pages publiques du portail visent WCAG 2.2 niveau AA, vérifié par une gate automatique (validate-a11y, moteur axe-core) : lien d'évitement, régions nommées, contrastes suffisants, navigation au clavier. Chaque page charge le socle d'accessibilité.
<a class="skip-link" href="#contenu">Aller au contenu</a>
<script src="usr/lib/capsuleos/core/capsule-a11y-bootstrap.js"></script>
<!-- panneau d'accessibilite : taille de police, contrastes -->
Un choix assumé : l'accessibilité activable dans les bureaux
À l'intérieur des bureaux simulés, l'accessibilité est une couche additive, activable, et non l'état nominal. La raison tient à la fidélité : le clone reproduit le comportement réel du système cible, souvent sans accessibilité forcée. L'imposer par défaut trahirait la reproduction. On offre donc un panneau d'accessibilité que l'apprenant active quand il le souhaite.
Deux surfaces, deux règles : le site qui présente CapsuleOS est conforme AA ; les bureaux reproduits proposent l'accessibilité en option, pour ne pas trahir le réel.
17. Éco-conception et sobriété
La contrainte « 100 pour cent statique, sans framework » est aussi un choix d'éco-conception. Pas de chaîne de construction obligatoire, pas de rendu côté serveur, pas de dépendance lourde : une page se sert telle quelle, y compris hors ligne et depuis une clé USB. Moins d'infrastructure, c'est moins d'énergie à chaque visite.
Des médias sobres
Tout raster est en WebP (sans perte pour l'interface, qualité maîtrisée pour les fonds), et le paquet de production est minifié. On transfère le minimum d'octets pour un rendu fidèle.
# convertit tout raster en WebP et réécrit les références
node usr/lib/capsuleos/tools/prepare-web-media.mjs --rewrite-refs
Sobriété logicielle
Le principe de ressource sobre, ne pas ajouter de module sans dette prouvée, limite le poids transféré comme la dette de maintenance. Un bureau simulé reste quelques fichiers lisibles, pas un empilement de dépendances tierces.
Durabilité
Le code sans framework ni bundler vieillit bien : il n'est pas l'otage d'un écosystème qui tourne et casse. Un fichier lisible aujourd'hui le restera. L'accessibilité activable et le fonctionnement hors ligne élargissent l'audience sans surcoût d'infrastructure.
Sobriété et fidélité ne s'opposent pas : on pousse l'expérience au plus près du réel dans l'enveloppe la plus légère possible.
19. Licences et conformité
Le projet documente la provenance juridique de chaque fichier. Les sources portent un en-tête SPDX, une déclaration REUSE décrit les licences par chemin, et un dossier dédié contient le texte de chaque licence utilisée.
// SPDX-FileCopyrightText: 2020-2026 les contributeurs CapsuleOS
// SPDX-License-Identifier: GPL-3.0-or-later
- REUSE.toml déclare les licences par chemin.
- Le dossier LICENSES/ contient le texte de chaque licence employée.
- Un asset tiers n'entre jamais sans licence ni provenance documentée.
La conformité se vérifie comme le reste, par gate (validate-reuse-full, validate-sbom) : la provenance n'est pas une promesse, c'est une propriété contrôlée.
20. Discipline de contribution
L'arbre de travail est partagé par plusieurs contributeurs et agents simultanément. La discipline d'indexation évite d'emporter le travail en cours des autres : on indexe par chemin, jamais en masse.
# BON : indexer ses propres fichiers
git add chemin/vers/mon-fichier.js
# REFUSÉ par le garde-fou (emporte le travail des autres)
git add -A
git add -u
git add .
Les règles sociales sont inscrites dans l'outil, pas seulement dans un guide : un garde-fou refuse mécaniquement l'indexation en masse et le push forcé. Les conflits portent presque toujours sur des fichiers générés ; on ne les fusionne pas à la main, on relance la régénération des vues.
Source contre généré, sans exception : toute projection se régénère et ne s'édite jamais. Les conflits de fusion sur les fichiers générés se dissolvent dans la régénération.
21. Glossaire des prédicats et gates
Le vocabulaire formel en un coup d'œil. Chaque prédicat est une propriété vérifiable ; chaque gate est le script qui tranche. La liste complète et les règles d'inférence R-* vivent dans la référence des prédicats du dépôt.
| Symbole | Signification | Vérifié par |
|---|---|---|
| H2 | Base du dépôt saine | validate-all |
| A | Assets présents, tirés de la VM | validate-asset-zones |
| Φ | Fidélité visuelle mesurée (diff pixel) | compare-visual-fidelity |
| Vp | Parité visuelle classée (déclarative) | classifieur, distinct de Φ |
| VΣ | États UI et effets clôturés | smoke-ui-state-effects |
| Tf | Fidélité typographique et vues | smoke-visual-fidelity |
| AppΣ | Catalogue d'applications clôturé | smoke-apps-catalog |
| TΣ | Shell terminal clôturé | validate-terminal-commands |
| Rv | Rafraîchissement sain des vues | smokes de slot |
| P0 / P1 / P2 | Classification des écarts | indices, contentGaps |
22. Transposer la méthode
Dégagés de leur domaine, ces principes se réimplantent dans tout projet logiciel exigeant.
- Nommer sa vérité-terrain : le référent externe mesurable contre lequel on se compare.
- Définir une métrique de qualité falsifiable, avec un seuil et une vérification qui échoue en rouge.
- Énumérer des prédicats, chacun avec sa commande de vérification ; aucun prédicat sans vérificateur.
- Écrire des règles ordonnées qui, pour chaque manque, désignent l'action suivante.
- Séparer rigoureusement les sources et les fichiers générés, et rendre la génération idempotente.
- Inscrire les règles sociales de contribution dans les outils, pas seulement dans un guide.
- Fonder les contraintes sur un pourquoi explicite, pour qu'elles tiennent dans la durée.
En une phrase
Une convention de code peut cesser d'être une promesse pour devenir une propriété vérifiable du système. Ce passage produit, d'un même geste, l'excellence parce qu'elle est mesurée, la robustesse parce qu'elle est réfutable, et l'efficience parce que la décision devient déduction.