Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Trace d'exécution — le compile error politique du cas Dumas 1995

Tout ce qui précède dans cette série — convention de nommage, architecture et DSL — converge ici vers une trace de conception de bout en bout. Le support est la décision n°95-91 PDR du Conseil constitutionnel du 11 octobre 1995, relative au compte Balladur. Les étapes A à H décrivent le pipeline attendu du Ship 1. Le bridge complet et les sorties présentées restent à implémenter et à valider ; le scaffold Constitution/Economy adjacent est un autre prototype, de périmètre limité.

Le support historique retenu ici est la décision 95-91 PDR du 11 octobre 1995 (ouvre dans une nouvelle fenêtre), relative au compte de campagne d’Édouard Balladur, rendue sous la présidence de Roland Dumas. Son considérant 15 ajoute 5 929 628 F aux dépenses du compte ; le compte est réformé puis approuvé. Il ne s’agit pas d’une suppression de 5,9 millions de francs de financement public. Les controverses ultérieures sur les délibérations et les témoignages doivent être documentées séparément du dispositif publié.

La question politique du cadre Commons.France1995.Erard est distincte : son auteur choisit d’exiger un débat parlementaire en deux lectures pour une réformation de compte qualifiée par lui de transition Public → Contested. Cette exigence n’est pas présentée comme une obligation du droit positif de 1995. Le cas compare les déclarations encodées à cette exigence contrefactuelle ; il ne démontre pas une illégalité de la décision historique. Le vocabulaire AccountReformation ci-dessous est une extension proposée du modèle de mesures, à spécifier dans les racines Law et dans le détecteur avant implémentation.

Étape A — Le csproj charge le bridge SG comme analyzer

Le point d'entrée est un fichier projet standard. Law.France1995.Sample.csproj déclare trois familles de dépendances : les racines d'interfaces (les DSL communs), les instances M2 concrètes (les cadres spécifiques à France1995.Erard), et les analyzers/source generators chargés dans Roslyn via OutputItemType="Analyzer".

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <!-- Racines d'interfaces -->
    <ProjectReference Include="..\Common.Dsl\src\FrenchExDev.Net.Common.Dsl\FrenchExDev.Net.Common.Dsl.csproj" />
    <ProjectReference Include="..\Law.Dsl\src\FrenchExDev.Net.Law.Dsl\FrenchExDev.Net.Law.Dsl.csproj" />
    <ProjectReference Include="..\Commons.Dsl\src\FrenchExDev.Net.Commons.Dsl\FrenchExDev.Net.Commons.Dsl.csproj" />

    <!-- Instances M2 concrètes — le cadre France 1995 vu par Erard -->
    <ProjectReference Include="..\Common.France1995.Erard.Dsl\Common.France1995.Erard.Dsl.csproj" />
    <ProjectReference Include="..\Law.France1995.Erard.Dsl\Law.France1995.Erard.Dsl.csproj" />
    <ProjectReference Include="..\Commons.France1995.Erard.Dsl\Commons.France1995.Erard.Dsl.csproj" />

    <!-- Analyzers + SG par famille — OutputItemType="Analyzer" les charge dans Roslyn -->
    <ProjectReference Include="..\Law.Dsl\src\FrenchExDev.Net.Law.Dsl.Analyzers\FrenchExDev.Net.Law.Dsl.Analyzers.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    <ProjectReference Include="..\Commons.Dsl\src\FrenchExDev.Net.Commons.Dsl.Analyzers\FrenchExDev.Net.Commons.Dsl.Analyzers.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    <ProjectReference Include="..\Common.Dsl\src\FrenchExDev.Net.Common.Dsl.Analyzers\FrenchExDev.Net.Common.Dsl.Analyzers.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />

    <!-- LE BRIDGE : cette ligne transforme le projet en "loi cross-vérifiée" -->
    <ProjectReference Include="..\Law.Commons.Bridge\src\Law.Commons.Bridge.SourceGenerator\Law.Commons.Bridge.SourceGenerator.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    <ProjectReference Include="..\Law.Commons.Bridge\src\Law.Commons.Bridge.Analyzers\Law.Commons.Bridge.Analyzers.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
  </ItemGroup>
</Project>

La ligne OutputItemType="Analyzer" est le mécanisme MSBuild standard qui indique que ces projets ne sont pas des dépendances runtime mais des composants Roslyn à charger dans le pipeline du compilateur. C'est exactement le même mécanisme que n'importe quel analyzer NuGet — StyleCop, SonarAnalyzer, Meziantou. La seule différence est que le contenu analysé ici est du droit, pas du style de code. Le mécanisme, lui, est identique.

Étape B — Le code source de DecisionDumas sans procédure

Voici le fichier que l'auteur du cadre a écrit pour décrire la décision Dumas. Les identifiants sont en anglais ; les commentaires en français expliquent l'intention.

using Law.France1995.Erard.Attributes;       // pour [Article], [Alinea], [Measure], [DemocraticProcedure]
using FrenchExDev.Net.Law.Dsl;                // pour MeasureKind
using Common.France1995.Erard.Assets;
using Common.France1995.Erard.Spaces;
using Common.France1995.Erard.Periods;

namespace Law.France1995.Sample.Decisions;

// Décision n°95-91 PDR du Conseil constitutionnel — 11 octobre 1995
// Le Conseil réforme et approuve le compte de campagne de Balladur
[Article("95-91-PDR",
         Space        = typeof(FR),
         Period       = typeof(Y1995),
         Codification = "Conseil constitutionnel — décision n°95-91 PDR du 11 octobre 1995")]
public sealed class DecisionDumas
{
    // Unité de modélisation pédagogique : considérant 15 du compte Balladur.
    // AccountReformation est une extension proposée, pas un enum livré.
    [Alinea(1, Text = "Le Conseil ajoute 5 929 628 F aux dépenses du compte Balladur")]
    [Measure(MeasureKind.AccountReformation, typeof(PublicCampaignFunding))]
    // ⬆️ ICI : aucune [DemocraticProcedure] sur cette méthode.
    //          C'est l'absence qui sera détectée comme LOI003.
    public void Section1() { }
}

Et la description du statut de l'actif dans le cadre Commons :

using Commons.France1995.Erard.Attributes;
using FrenchExDev.Net.Commons.Dsl;
using Common.France1995.Erard.Assets;
using Common.France1995.Erard.Spaces;
using Common.France1995.Erard.Periods;
using Common.France1995.Erard.Procedures;

namespace Law.France1995.Sample.Statuses;

// Le financement public des campagnes est un bien public entre 1988 et 1995
// et son statut est contesté en 1995 dans le scénario politique proposé à partir du compte Balladur
[Asset(typeof(PublicCampaignFunding))]
[Status(StatusKind.Public,    typeof(FR), typeof(P1988_1995))]
// Contested est le statut cible proposé ; il n’est pas aussi déclaré comme statut courant.
public sealed class PublicCampaignFundingInFrance { }

// Frontière : pour passer de Public à Contested, il faut un débat parlementaire en 2 lectures
[Boundary(StatusKind.Public, StatusKind.Contested, typeof(ParliamentaryDebateTwoReadings))]
public sealed class DemocraticLifeBoundaries { }

L'absence est le point névralgique. Il n'y a pas de [DemocraticProcedure(typeof(ParliamentaryDebateTwoReadings))] sur la méthode Section1(). C'est cette absence — pas une erreur de syntaxe, pas une valeur fausse, mais un manque structurel — que le détecteur va transformer en diagnostic LOI003.

Étape C — dotnet build et Roslyn charge le bridge SG

Quand on lance dotnet build Law.France1995.Sample.csproj, MSBuild appelle csc.exe, qui est Roslyn. Le compilateur lit tous les fichiers .cs du projet, lit la liste des composants référencés avec OutputItemType="Analyzer", charge Law.Commons.Bridge.SourceGenerator.dll, instancie chaque [Generator] et chaque [DiagnosticAnalyzer] marqué dans cette assembly, et lance le pipeline d'analyse suivi de la génération de code. Rien de tout cela n'est propre au projet — c'est le fonctionnement normal de Roslyn avec des source generators et des analyzers. La seule chose propre au projet est le contenu des generators et analyzers chargés.

Étape D — Le bridge SG construit le CrossModel

Dans LawCommonsBridgeGenerator.Initialize, les pipelines ForAttributeWithMetadataName se déclenchent. CrossModelExtractor.FromCompilation() est appelé. Pour chaque classe décorée avec un attribut implémentant ILegalContainer, l'extracteur construit un modèle sémantique typé :

LawArticleModel {
    ContainingType = ArticleFqn("global::Law.France1995.Sample.Decisions.DecisionDumas"),
    Number = "95-91-PDR",
    Space = SpaceFqn("global::Common.France1995.Erard.Spaces.FR"),
    Period = PeriodFqn("global::Common.France1995.Erard.Periods.Y1995"),
    Alineas = [
        LawAlineaModel {
            Index = 1,
            Method = AlineaFqn("...DecisionDumas.Section1()"),
            Measures = [
                LawMeasureModel {
                    Kind = MeasureKindCode.AccountReformation,
                    TargetAsset = AssetFqn("global::Common.France1995.Erard.Assets.PublicCampaignFunding")
                }
            ],
            Procedures = [ ]    // ← VIDE — aucune [DemocraticProcedure] sur Section1
        }
    ]
}

CommonsAssetStatusModel {
    Asset = AssetFqn("global::Common.France1995.Erard.Assets.PublicCampaignFunding"),
    Statuses = [
        CommonsStatusModel { Kind = Public,    Space = "...FR", Period = "...P1988_1995" },
        CommonsStatusModel { Kind = Contested, Space = "...FR", Period = "...Y1995" }
    ]
}

CommonsBoundaryModel {
    From = StatusKindCode.Public,
    To = StatusKindCode.Contested,
    RequiredProcedure = ProcedureFqn("global::Common.France1995.Erard.Procedures.ParliamentaryDebateTwoReadings")
}

Tous les pivots sont des FQN typés — des record structs immutables, pas des strings. Le compilateur a déjà vérifié que les typeof(...) dans le code source sont des types valides au moment où il a compilé DecisionDumas.cs. Si PublicCampaignFunding n'existait pas comme type dans le cadre Common.France1995.Erard, on aurait eu un compile error C# normal — CS0246: The type or namespace name 'PublicCampaignFunding' could not be found — avant même que le bridge SG ne tourne. Le type system C# est la première ligne de défense ; le bridge SG est la seconde.

Étape E — FrontierCrossingDetector détecte la violation LOI003

Le FrontierCrossingDetector est une fonction pure. Il prend un CrossModel en entrée et retourne une liste de CrossingViolation. Pour la mesure de Section1, le raisonnement est le suivant.

Premièrement, le détecteur cherche le statut de l'actif ciblé par la mesure. Jointure sur AssetFqn : trouvé — PublicCampaignFunding est déclaré dans le cadre Commons.France1995.Erard. Deuxièmement, le détecteur vérifie que l'espace de l'article (FR) correspond à l'un des espaces déclarés dans les statuts de l'actif : oui, trouvé. Troisièmement, le détecteur détermine le statut courant de l'actif à la période de l'article (Y1995) : le statut le plus récent applicable est Public (issu de P1988_1995, qui couvre 1995). Quatrièmement, le détecteur calcule le statut cible impliqué par la mesure : ComputeTargetStatus(Public, AccountReformation) retourne Contested selon la règle politique proposée par cet auteur. Cette transition est un choix du cadre, pas une conséquence juridique universelle d’une réformation comptable. Cinquièmement, le détecteur cherche la frontière applicable : Boundary(Public → Contested, requires ParliamentaryDebateTwoReadings) — trouvée. Sixièmement, le détecteur cherche si la procédure requise est déclarée sur l'alinéa : alinea.Procedures.Any(p => p.Procedure == boundary.RequiredProcedure). La liste alinea.Procedures est vide. Any(...) retourne false. Violation confirmée.

// Résultat de l'analyse — la violation matérialisée comme valeur
violations.Add(new CrossingViolation(
    Code:     ViolationCode.LOI003_MissingProcedure,
    Severity: ViolationSeverity.Error,
    Article:  ArticleFqn("global::Law.France1995.Sample.Decisions.DecisionDumas"),
    Alinea:   1,
    Message:  "Measure AccountReformation on global::Common.France1995.Erard.Assets.PublicCampaignFunding " +
              "requires [DemocraticProcedure(typeof(global::Common.France1995.Erard.Procedures" +
              ".ParliamentaryDebateTwoReadings))] — absent"));

Ce raisonnement est déterministe, reproductible, et testable unitairement (c'est le régime 1 de la Partie 12). Il ne contient aucune heuristique, aucune probabilité, aucun réseau de neurones. C'est une jointure typée sur des FQN, suivie d'un test de présence sur une collection. La complexité est dans la modélisation (les types, les attributs, les frontières), pas dans le détecteur — et c'est voulu, parce que la modélisation est vérifiable par un juriste alors qu'un algorithme opaque ne l'est pas.

Étape F — Du modèle au Diagnostic Roslyn, en build et en temps réel

Le bridge SG transforme chaque violation en un Diagnostic Roslyn, avec sa Location précise dans le code source :

// Dans le source generator — au moment du build
foreach (var v in violations)
{
    // LocationIndex a été construit pendant l'extraction du CrossModel :
    // pour chaque [Measure] rencontré, on a stocké la Location du SyntaxNode
    var loc = locations.TryGet(v.Article, v.Alinea, v.Code) ?? Location.None;
    spc.ReportDiagnostic(BridgeDiagnostics.From(v, loc));
}

Le LocationIndex (vivant dans Bridge.Roslyn) a été construit pendant l'extraction du CrossModel : pour chaque attribut [Measure] rencontré dans l'arbre syntaxique, il a stocké la Location du SyntaxNode dans une table indexée par (ArticleFqn, AlineaIndex). Le bridge SG retrouve donc la ligne et la colonne précises du code source où l'attribut manquant devrait se trouver.

Mais le source generator n'est que la moitié de l'histoire. En parallèle — et c'est ce qui rend l'expérience démocratique vivante — le LawCommonsCoherenceAnalyzer (qui vit dans Bridge.Analyzers) fait exactement le même travail mais en temps réel dans l'IDE. Pendant que l'auteur tape [Measure(MeasureKind.AccountReformation, typeof(PublicCampaignFunding))], l'analyzer détecte immédiatement l'absence de [DemocraticProcedure(...)] requise par la frontière Public → Contested du cadre Commons.France1995.Erard, et souligne la ligne en rouge avant même que le fichier soit sauvegardé. Hover sur le soulignement affiche le message complet. Ctrl+. ouvre le MissingProcedureCodeFix qui propose d'ajouter la procédure manquante, avec la liste des procédures déclarées dans le cadre Commons référencé.

L'auteur ne lit pas un rapport : il dialogue avec le compilateur. Le SG viendra plus tard, au build, pour produire l'attestation cryptographique signée et le fichier .g.cs matérialisé. Mais le travail interactif — l'enseignement, la confrontation, la découverte du manque — a déjà eu lieu dans l'IDE. C'est la distinction fonctionnelle entre les deux : l'analyzer enseigne, le SG atteste (cf. Partie 9 — Le bridge).

Étape G — MSBuild échoue

$ dotnet build Law.France1995.Sample/Law.France1995.Sample.csproj

  Law.France1995.Sample -> ...

Law.France1995.Sample/Decisions/DecisionDumas.cs(13,6): error LOI003: Measure AccountReformation
on global::Common.France1995.Erard.Assets.PublicCampaignFunding requires
[DemocraticProcedure(typeof(global::Common.France1995.Erard.Procedures.ParliamentaryDebateTwoReadings))]
— absent
[/.../Law.France1995.Sample.csproj]

Build FAILED.

    1 Error(s)

Le compile error politique est produit. Ligne 13, colonne 6 — précisément le [Measure(...)] dans VS Code, Visual Studio, Rider, ou Lex Studio (cf. Partie 15). Hover dessus affiche le message complet. Click droit puis Quick Fix propose MissingProcedureCodeFix.AddDemocraticProcedure. Le message d'erreur contient les FQN complets des types impliqués, ce qui permet F12 (Go to Definition) sur chacun d'eux : on peut naviguer jusqu'à la définition de PublicCampaignFunding, de ParliamentaryDebateTwoReadings, de la frontière qui les relie. Le diagnostic est traçable de bout en bout.

Étape H — Déclaration contrefactuelle et validation structurelle attendue

L'auteur applique le Quick Fix, ou ajoute manuellement la procédure manquante :

// Après application du Quick Fix — la procédure démocratique est maintenant déclarée
[Alinea(1, Text = "Le Conseil rogne les comptes de campagne...")]
[Measure(MeasureKind.AccountReformation, typeof(PublicCampaignFunding))]
[DemocraticProcedure(typeof(ParliamentaryDebateTwoReadings), ReferenceDate = "1995-10-10")]   // ← ajouté
public void Section1() { }

Dans ce scénario de conception, l’ajout de l’attribut satisferait la condition structurelle du cadre et permettrait de produire ValidatedCrossings. Ce résultat atteste seulement la conformité des déclarations encodées aux règles exécutées. Il ne prouve pas qu’un débat a eu lieu. La date 1995-10-10 de l’exemple est fictive : cet ajout est une variante contrefactuelle, pas une correction du dossier historique. Une interface réelle doit demander une source et une validation humaine de la procédure, ou laisser le diagnostic ouvert si les pièces manquent.

Le moment de vérité métacratique

C'est ici que la pluralité des cadres prend son sens politique complet.

La décision Dumas, codée dans le cadre Law.France1995.Erard, exigeait sa propre traçabilité démocratique pour compiler à l'intérieur de ce cadre-là. Le cadre ne condamne pas la décision — il exige sa documentation dans un type system manipulable, selon ses propres spécifications. La spécification de ce cadre-ci dit : « pour passer un bien public à un statut contesté, il faut un débat parlementaire en deux lectures ». C'est un choix de l'auteur du cadre, pas une loi de la nature.

Un autre auteur publiant Law.France1995.LaQuadrature pourrait imposer des spécifications plus strictes — par exemple exiger un référendum pour toute mesure touchant le financement démocratique, pas juste un débat parlementaire. La même DecisionDumas échouerait encore plus fort dans ce cadre-là. Un troisième auteur, Law.France1995.InstitutMontaigne, pourrait au contraire ne déclarer aucune frontière sur le financement public des campagnes, considérant que le Conseil constitutionnel est souverain en la matière et n'a besoin d'aucune procédure supplémentaire. La même DecisionDumas compilerait sans erreur dans ce troisième cadre.

Le ConsensusScorer (Partie 12 — Les six régimes, régime 5) peut alors agréger les verdicts des trois cadres et exhiber publiquement : « la décision Dumas compile dans 1 cadre sur 3 — divergence sur l'exigence de procédure démocratique pour les mesures touchant le financement public des campagnes ». Le désaccord devient typé, traçable, cadré. Personne ne peut prétendre que « tout le monde sait » ou que « c'est évident ».

Le compile error n'est pas une vérité absolue — c'est le verdict d'un cadre spécifique. Et c'est précisément ce qui rend ce verdict politiquement actionnable. Un verdict universel serait dogmatique — il prétendrait clore le débat. Un verdict spécifique est démocratique — il ouvre le débat en rendant les hypothèses explicites, les divergences mesurables, les alternatives navigables. Le même Quick Fix qui ajoute la procédure permet aussi de constater qu’aucune pièce attestant la procédure choisie par ce cadre n’est fournie dans cet exemple — et donc que le Quick Fix pourrait être refusé, et que le compile error reste rouge à perpétuité dans ce cadre-là. C'est un diff Git éternel entre l'état du droit selon ce cadre et l'état où la procédure aurait été respectée pour ce cadre. La force démocratique du dispositif est exactement dans la spécificité du cadre, pas dans une prétention à l'universalité.

Ce que cette trace spécifie et ce qui reste à démontrer

Cette trace décrit le fonctionnement attendu d’un bridge Roslyn sur un cas historiquement référencé et une exigence politique contrefactuelle. Les blocs Build FAILED, diagnostics et fichiers générés sont des sorties attendues illustratives, pas un journal d’exécution vérifié du Ship 1. Les mécanismes Roslyn cités sont des possibilités d’implémentation ; leur assemblage complet doit encore être livré et exécuté.

La validation attendue comporte quatre objets distincts : la fidélité de l’encodage aux sources historiques ; la spécification explicite du cadre politique ; le fonctionnement technique reproductible du détecteur ; la vérification humaine des pièces attestant les procédures déclarées. Un build réussi ne peut se substituer à ces quatre examens.

La pluralité des cadres reste l’hypothèse constructive : le même cas, décrit à partir des mêmes pièces, doit pouvoir être évalué dans plusieurs cadres dont les choix sont visibles. Les résultats ne valent que relativement à ces cadres et aux données fournies.

⬇ Download