Aller au contenu
learn

Journal

  • Code du cours : applications basées sur des fichiers, extraits refusés et solutions des exercices, comparés à leur sortie attendue par check.sh
  • CI sous Linux, Windows et macOS
  • Leçon 1 : installer .NET et exécuter ton premier programme
  • Leçon 2 : variables, types et saisie
  • Leçon 3 : conditions et boucles
  • Leçon 4 : méthodes, tableaux et listes
  • Leçon 5 : classes et objets
  • Leçon 6 : records, structs et enums
  • Leçon 7 : interfaces et héritage
  • Leçon 8 : exceptions et sécurité face à null
  • Leçon 9 : collections et LINQ
  • Leçon 10 : fichiers et texte
  • Leçon 11 : tests unitaires
  • Leçon 12 : un petit projet
Attendu Ce qui se passe Où Mesure Statut
Afficher un modèle d’accord de GA montre son Name, comme le veut le ToString() => Name du record de base Les records dérivés TonalModal et Analytical synthétisent chacun leur propre ToString, qui énumère toutes les propriétés : TonalModal { Name = Major 7th, PitchClassSet = 0 4 7 E, … }. Un record dérivé ne garde le ToString de la base que s’il est sealed (records, mise en forme intégrée) ChordTemplate.cs:84 ; un appelant, ContextualChordsController.cs:301, passe template.ToString() à un service qui le journalise (FretboardServices.cs:536) Une sonde console qui référence GA.Domain.Core au commit 5c3a52a, SDK 10.0.112 : le dump pour ToString() et $"{template}" sur les deux records ; une réplique avec sealed a affiché maj7 (entrée) Reproduit ; correctif proposé dans la PR GA n° 816, pas encore fusionnée (2026-10-04) ; toujours ouverte le 2026-10-06, le défaut inchangé sur main 40d33747 (2026-10-06)
Les avertissements de nullabilité apparaissent dans le code de GA : GA.Domain.Core et GA.Core activent <Nullable>enable</Nullable> Directory.Build.props place 15 avertissements de nullabilité (CS8600, CS8602, CS8618, CS8625…) dans NoWarn « to achieve a clean baseline », et Directory.Build.targets les répète. La base est propre sans eux : les listes ne cachent que les nouvelles erreurs Directory.Build.props:21, Directory.Build.targets:7 Un fichier contenant sept erreurs de nullabilité, ajouté à GA.Domain.Core au commit 5c3a52a : 0 avertissement avec la configuration de GA, 7 sans les deux listes ; sans ce fichier, 0 dans GA.Domain.Core et GA.Core (entrée, ga-probes) Reproduit ; non signalé à GA
Les services de connaissances musicales de GA chargent ChordProgressions.yaml, GuitarTechniques.yaml et SpecializedTunings.yaml Aucun des trois ne se désérialise : une chaîne là où la classe a une List<string>, des chaînes là où elle attend des objets, une ligne qui n’est pas du YAML valide. Chaque chargeur attrape l’exception, affiche Error loading … et continue avec un seul élément par défaut ChordProgressions.yaml:93 face à ChordProgressionsConfigLoader.cs:16 ; GuitarTechniques.yaml:23 face à GuitarTechniquesConfigLoader.cs:20 ; SpecializedTunings.yaml:95 ; le catch de ChordProgressionsConfigLoader.cs:137, et le même dans les deux autres chargeurs artists.cs au commit 5c3a52a, SDK 10.0.112 : 17 accords emblématiques, puis 1 élément pour chacun des trois fichiers, et 16 artistes distincts (entrée). Les trois lignes sont inchangées sur le main de GA, ae196bf Reproduit ; signalé dans l’issue GA n° 797 le 2026-10-03 ; correctif proposé dans la PR GA n° 808, pas encore fusionnée (2026-10-04)
GetArtistBreakdown renvoie les 20 artistes qui ont le plus d’entrées, du plus au moins, comme le dit son commentaire // Top 20 artists Take(20) coupe la liste alphabétique avant le tri par nombre d’entrées, et le résultat trié va dans un Dictionary, dont la documentation laisse l’ordre indéfini MusicalKnowledgeService.cs:226 et :235 ; GetAllArtists trie par nom en :125 La même sonde : avec 16 artistes, aucun n’est écarté, et le dictionnaire sort par nombre d’entrées, les égalités par ordre alphabétique. La coupe n’agit qu’au-delà de 20 artistes, une fois la ligne précédente corrigée Latent, lu dans le code ; correctif proposé dans la PR GA n° 811, pas encore fusionnée (2026-10-04)
PitchClassSet.ScaleVideoUrl donne l’URL de la vidéo d’une gamme listée dans scale_video_urls.json, qui en contient 1 490 Le fichier n’est ni intégré à la bibliothèque ni copié à côté d’elle : GA.Domain.Core.csproj n’a aucune règle pour lui. Le nom de ressource GA.Domain.Core.Scales.Data.scale_video_urls.json et le chemin de repli Scales/Data/ manquent tous deux son dossier, Theory/Tonal/Scales/Data/, et LoadVideoUrls renvoie un dictionnaire vide sans message ScaleVideoUrlById.cs:30, :36-38; PitchClassSet.cs:350, ScaleMetadataRegistry.cs:36 files.cs au commit 5c3a52a, SDK 10.0.112 : 0 ressource dans GA.Domain.Core, pas de fichier de repli, 0 gamme avec une URL (entrée). Le nom de ressource et le chemin de repli sont inchangés sur le main de GA le 2026-10-03 Reproduit ; correctif proposé dans la PR GA n° 817, pas encore fusionnée (2026-10-04) ; toujours ouverte le 2026-10-06, le défaut inchangé sur main 40d33747 (2026-10-06)
IconicChordsConfigLoader.ReloadConfiguration() relit IconicChords.yaml après une modification Il affecte le champ static readonly _configuration avec FieldInfo.SetValue, qui lève FieldAccessException depuis .NET Core 3.0 (changement cassant) IconicChordsConfigLoader.cs:107-111, :12 files.cs : FieldAccessException: Cannot set initonly static field '_configuration' after type 'GA.Domain.Services.IconicChordsConfigLoader' is initialized. (entrée). Inchangé sur le main de GA le 2026-10-03 Reproduit ; correctif proposé dans la PR GA n° 811, pas encore fusionnée (2026-10-04)
Un IconicChords.yaml manquant est signalé FindYamlFile essaie quatre chemins, dont deux relatifs au dossier courant. Quand aucun n’existe, un catch vide avale son FileNotFoundException, et le chargeur renvoie un seul accord par défaut, sans message IconicChordsConfigLoader.cs:94-104, :25-28 files.cs, après suppression des fichiers YAML à côté de lui : 1 accord, « C Major Triad », lancé depuis ga-probes ; 17 depuis la racine du code de GA, par <dossier courant>/Common/GA.Business.Config ; rien d’affiché (entrée) Reproduit ; un choix de conception, non signalé à GA
GaCLI lit le appsettings.yaml que sa compilation copie à côté du programme SetBasePath(Directory.GetCurrentDirectory()) le lit dans le dossier courant, comme un fichier facultatif : lancé depuis un autre dossier, GaCLI tournerait sans ses réglages, en silence Program.cs:17-19, GaCLI.csproj:59-61 Compilé à 5c3a52a et lancé depuis un autre dossier : il affiche sa liste de commandes. Les deux sections du fichier, EmbeddingService et LmStudio, ne sont lues qu’à un endroit, sync-mongodb, dans des réglages qu’aucun service ne résout (entrée) Vérifié : le fichier ne change rien à ce commit ; non signalé à GA
Chaque ligne du CSV de naturalité de GA a les 6 valeurs de son en-tête :F2 et {deltaStretch} utilisent la culture de la machine : avec fr-FR, 2.50 s’écrit 2,50, et une ligne reçoit 8 valeurs ; le lecteur coupe aux , NaturalnessTrainingDataGenerator.cs:160, :21; TrainNaturalnessModelCommand.cs:51-54 l10_culture.cs avec la chaîne de format de GA : 6 valeurs avec en-CA, 8 avec fr-FR. Le générateur de GA lui-même n’a pas tourné : il lui faut un corpus de tablatures (entrée) Reproduit avec la chaîne de format ; correctif proposé dans la PR GA n° 811, pas encore fusionnée (2026-10-04)
La commande de GA affiche le nombre de lignes de données qu’elle a écrites csvContent.Split('\n').Length - 1 retire l’élément vide après le dernier saut de ligne mais compte l’en-tête : une ligne de trop GenerateNaturalnessDataCommand.cs:38 L’arithmétique de Split('\n'), mesurée par l10_line_endings.cs : 3 lignes donnent 4 éléments. La commande de GA n’a pas tourné Latent, lu dans le code ; correctif proposé dans la PR GA n° 811, pas encore fusionnée (2026-10-04)
Un test des services de connaissances musicales de GA échoue quand leurs fichiers YAML ne se chargent pas Trois des huit tests sont ignorés avec [Ignore("Configuration files not loaded in test environment")]. Des cinq autres, trois vérifient seulement qu’un nombre est supérieur à zéro ou qu’une liste n’est pas vide, ce que satisfait le seul élément par défaut de chaque chargeur en échec MusicalKnowledgeServiceTests.cs:108-110, :141-143, :207-209; :16, :31-33, :179-183 Sonde knowledge-tests, fichier de test de GA compilé sans changement, SDK 10.0.112 : 5 réussis, 3 ignorés, 0 échec, alors que trois des quatre fichiers YAML ne se chargent pas. La recherche de « jazz » a réussi avec 1 résultat, la progression d’accords par défaut (entrée). Inchangé sur le main de GA, 0843879 Reproduit ; lié à l’issue GA n° 797 ; correctif proposé dans la PR GA n° 808, pas encore fusionnée (2026-10-04)
Le [SetUpFixture] TestEnvironment de GA s’exécute avant les tests de configuration : il définit GA_TEST_MODE=1 et place le dossier courant à la racine du dépôt Il est seul dans le namespace GA.Business.Core.Tests.TestBootstrap, et NUnit n’exécute un setup fixture que pour les tests de son namespace et des namespaces qu’il contient (NUnit) : il ne s’exécute jamais TestEnvironment.cs:1, :7-14 Sonde knowledge-tests : un test dans GA.Business.Core.Tests.Configuration a affiché GA_TEST_MODE: (not set) et Current directory is the test's bin folder: True (entrée). Sur main, 0843879, le namespace ne contient toujours que ce fichier Reproduit ; correctif proposé dans la PR GA n° 816, pas encore fusionnée (2026-10-04) ; toujours ouverte le 2026-10-06, le défaut inchangé sur main 40d33747 (2026-10-06)
Les fichiers de test de GA.Business.Core.Tests sont compilés et exécutés Le fichier projet retire 35 fichiers de la compilation, « Exclude test files with missing service implementations » : 5 des noms ne correspondent à aucun fichier, et les 30 autres contiennent 333 lignes qui commencent par [Test] ou [TestCase] GA.Business.Core.Tests.csproj:39-75 git ls-tree et un décompte au commit 5c3a52a ; le projet a 136 fichiers .cs. Les 35 entrées sont inchangées sur main, 0843879 Compté dans le code ; non signalé à GA
La CI de GA exécute les tests de chaque projet de test Ses jobs compilent et testent AllProjects.slnx, qui liste 75 des 111 fichiers de projet. Six projets de test sont en dehors et aucun workflow ne les nomme ; leurs fichiers comptent 102 lignes avec un attribut de test. L’un d’eux, GA.Business.Core.Graphiti.Tests, ne compile pas : il utilise GA.Business.Graphiti et ne référence aucun projet AllProjects.slnx, ci.yml:43, :103, GA.Business.Core.Graphiti.Tests.csproj lesson12_counts.py à 5c3a52a ; la compilation des tests Graphiti s’arrête sur 5 erreurs, CS0234 sur ses deux lignes using GA.Business.Graphiti... et CS0246 Compté et compilé ; correctif proposé dans la PR GA n° 812, pas encore fusionnée (2026-10-04)
Les éléments PackageReference Update du Directory.Build.props de GA alignent les versions des packages, sous « Centralized package version alignment and vulnerability remediation » Directory.Build.props est importé avant les éléments propres du projet, donc Update ne trouve rien à mettre à jour. 23 déclarations des six packages concernés gardent une autre version, dont 13 dans des projets de la solution Directory.Build.props:32-58 Restauration de GA.Business.Core.Graphiti.Tests : Microsoft.Extensions.Http 9.0.10, comme le demande son fichier de projet. Avec la même ligne Update dans Directory.Build.targets : 10.0.0 Reproduit, avec un témoin ; correctif proposé dans la PR GA n° 812, pas encore fusionnée (2026-10-04)
Le job d’analyse de sécurité de GA signale les packages vulnérables Directory.Build.props et Directory.Build.targets désactivent NU1902, NU1903 et NU1904 pour tous les projets. L’étape d’analyse a continue-on-error: true, et dotnet list package --vulnerable se termine avec le code 0 quand il liste un package vulnérable : le job ne peut pas échouer Directory.Build.props:16-17, Directory.Build.targets:5-11, ci.yml:398-401 Code de sortie 0 avec un package vulnérable, mesuré sur un projet d’essai (tableau des expériences) ; l’analyse de GA n’a pas été lancée Lu dans le code ; correctif proposé dans la PR GA n° 812, pas encore fusionnée (2026-10-04)
Le workflow Quality Check de GA lance un benchmark de qualité de GaCLI benchmark-quality lève NotImplementedException, comme 12 des 28 commandes de GaCLI, y compris analyze-chord, le premier exemple de son aide. L’étape a continue-on-error: true quality_check.yml:26-29, Program.cs:179-182 Compilation locale à 5c3a52a : NotImplementedException non gérée. Run 37167745677 de GA, sur un commit plus récent dont le workflow et Program.cs sont les mêmes fichiers : code de sortie 134, run conclu en succès Reproduit ; non signalé à GA
Une commande de GaCLI qui échoue le dit à son appelant sync-mongodb attrape son exception, affiche Error during sync: Unable to resolve service for type 'GA.Data.MongoDB.Services.MusicalObjectsService' while attempting to activate 'GaCLI.Commands.Runner'. et se termine avec le code 0 : Runner a besoin d’un service que la commande n’enregistre pas Program.cs:356, :364-368 Lancé depuis bin/Debug/net10.0 et depuis un autre dossier : le même message, code de sortie 0 Reproduit ; correctif proposé dans la PR GA n° 811, pas encore fusionnée (2026-10-04)
Question Hypothèse, écrite avant de mesurer Résultat Verdict Entrée et code
Combien d’avertissements de nullabilité le NoWarn de GA cache-t-il dans GA.Domain.Core ? Plus de 100 0 dans GA.Domain.Core et GA.Core sans les deux listes ; un fichier de test contenant sept erreurs reçoit 7 avertissements sans les listes, 0 avec Réfutée entrée, ZzNullableCanary.cs
Que fait le Fret de GA avec 50 ? new Fret(50), Fret.FromValue(50) et Fret f = 50; lèvent une ArgumentOutOfRangeException ; Fret.TryCreate(50) renvoie l’échec Fret number must be between -1 (muted) and 36, got 50 Les trois lèvent l’exception, pour 50 et pour -2 ; le message de TryCreate correspond mot pour mot Confirmée entrée, probe.cs, results.txt
new PositionLocation(5, 3), avec la corde et la frette inversées sous forme de simples nombres, compile-t-il sans rien dire ? Les deux ordres compilent sans avertissement ; l’appel inversé construit la corde 5, frette 3 0 avertissement sur les lignes de la sonde ; elle affiche 5,3 Confirmée entrée, probe.cs
Dans quel ordre un foreach lit-il un dictionnaire après un Remove et un Add ? Clés C, D, E, G, A, puis Remove("D") et Add("F#") : F# prend la place de D, C F# E G A, pas la dernière place C F# E G A Confirmée entrée, l09_dictionary_order.cs
Modifier un dictionnaire dans son propre foreach lève-t-il une exception, comme pour une liste ? Remove ne lève rien sous .NET 10 ; ajouter une clé lève une exception ; List<T>.Remove lève une InvalidOperationException Remove laisse C E sans exception ; ajouter B, et le Remove de la liste, lèvent tous deux Collection was modified; enumeration operation may not execute. Confirmée entrée, l09_modify_dictionary.cs, l09_modify_while_looping.cs
Quels artistes le GetArtistBreakdown de GA écarte-t-il avec Take(20) ? Plus de 20 artistes distincts : les 20 premiers de l’alphabet sont gardés, et au moins un artiste écarté a plus d’entrées que le plus petit nombre gardé ; le dictionnaire sort par nombre d’entrées décroissant 16 artistes, aucun écarté, parce que trois des quatre fichiers YAML ne se chargent pas (tableau QA). Le dictionnaire sort bien par nombre d’entrées, les égalités par ordre alphabétique Réfutée entrée, artists.cs, artists-results.txt
Sur un fichier de 3 lignes qui finit par un saut de ligne, que renvoient ReadAllText(path).Split('\n') et ReadAllLines, en LF et en CRLF ? Split donne 4 éléments, le dernier vide, et en CRLF les trois premiers finissent par \r ; ReadAllLines donne 3 lignes sans \r dans les deux cas Longueurs LF 2 2 2 0 et 2 2 2, CRLF 3 3 3 0 et 2 2 2 ; en CRLF, split[0] == "E2" vaut False Confirmée entrée, l10_line_endings.cs
Combien d’octets écrivent WriteAllText(path, "Ré") et WriteAllText(path, "Ré", Encoding.UTF8) ? 3 sans encodage, 6 avec Encoding.UTF8, en commençant par EF BB BF ; ReadAllText renvoie Ré dans les deux cas 52C3A9, puis EFBBBF52C3A9 ; Ré, 2 caractères Confirmée entrée, l10_bom.cs
File.Exists("data/ga-projects.csv") dépend-il du dossier d’où part dotnet run ? True depuis code/csharp-beginner, False depuis la racine du dépôt ; un chemin construit à partir de EntryPointFileDirectoryPath existe depuis les deux Comme prévu, et False aussi depuis examples ; check.sh exécute maintenant les deux cas sur trois OS. EntryPointFileDirectoryPath lui-même était connu avant l’hypothèse Confirmée entrée, l10_where.cs
Fichiers YAML supprimés à côté de la sonde, combien d’accords emblématiques GA charge-t-il, lancé depuis la racine de GA et depuis ga-probes ? 17 depuis la racine de GA, par le troisième chemin ; 1 accord par défaut depuis ga-probes, sans exception ni message 17, « Hendrix Chord » en premier, et 1, « C Major Triad » ; rien d’affiché Confirmée entrée, files.cs, files-without-yaml.txt, files-from-ga-root.txt
Que fait IconicChordsConfigLoader.ReloadConfiguration() de GA sous .NET 10 ? Il lève FieldAccessException : SetValue sur un champ static readonly est refusé depuis .NET Core 3.0 FieldAccessException: Cannot set initonly static field '_configuration' … Confirmée entrée, files.cs, files-results.txt
Combien de valeurs séparées par des virgules a une ligne du CSV de naturalité de GA avec en-CA et avec fr-FR ? 6 avec en-CA, 1,2.50,2.50,2,1,4 ; 8 avec fr-FR, 1,2,50,2,50,2,1,4. Connu avant l’hypothèse : $"{2.5}" donne 2,5 avec fr-FR Exactement ces deux lignes Confirmée entrée, l10_culture.cs
Quelle taille a un fichier après StreamWriter.Write("E2 A2 D3"), avant et après Dispose ? 0 octet avant, 8 après 0, puis 8 Confirmée entrée, l10_dispose.cs
ScaleVideoUrlById de GA trouve-t-il ses données ? Pas de ressource nommée …scale_video_urls.json dans GA.Domain.Core, pas de fichier de repli à côté de la sonde, un résultat vide, pas d’exception. La lecture d’un sous-agent l’a suggéré avant que je lise le code 0 ressource en tout, pas de fichier de repli, 0 gamme avec une URL Confirmée entrée, files.cs
Que crée dotnet new xunit avec le SDK 10.0.112, et qu’affiche dotnet test ? xUnit v2, package xunit 2.9.x, avec xunit.runner.visualstudio, Microsoft.NET.Test.Sdk et coverlet.collector ; exécuté par VSTest, avec une ligne Passed! à la fin xunit 2.9.3, xunit.runner.visualstudio 3.1.4, Microsoft.NET.Test.Sdk 17.14.1, coverlet.collector 6.0.4 ; VSTest 18.0.2 ; Passed! - Failed: 0, Passed: 1, … Confirmée entrée, Fretboard.Tests.csproj
Une [Theory] avec quatre lignes [InlineData] compte-t-elle pour 4 tests ou pour 1 ? 4 15 tests dans le projet : la théorie comptée une seule fois en aurait donné 12 Confirmée entrée, GuitarTests.cs
Que font Assert.Equal(0.3, 0.1 + 0.2) et Assert.Equal(0.3, 0.1 + 0.2, 10) ? Le premier échoue et affiche la valeur obtenue 0.30000000000000004 ; le second réussit Comme prévu. Non prévu : la valeur attendue est affichée 0.29999999999999999 Confirmée entrée, PitfallTests.cs
Quand la méthode lève ArgumentOutOfRangeException, que font Assert.Throws<ArgumentException> et Assert.ThrowsAny<ArgumentException> ? Throws échoue, il veut le type exact ; ThrowsAny réussit Assert.Throws() Failure: Exception type was not an exact match ; ThrowsAny réussit Confirmée entrée, PitfallTests.cs, GuitarTests.cs
xUnit crée-t-il une nouvelle instance de la classe de test pour chaque test ? Oui : un champ incrémenté par chacun de deux tests vaut 1 dans les deux Les deux tests réussissent Confirmée entrée, InstanceTests.cs
Exécutés sans changement contre les services de GA au commit 5c3a52a, que donnent les MusicalKnowledgeServiceTests de GA ? 0 échec, 3 ignorés, 5 réussis, dont le test des accordages spéciaux, car le seul accordage par défaut rend sa liste non vide. Écrite après lecture du fichier de test, avant de l’exécuter 5 réussis, 3 ignorés, 0 échec ; 1 accordage spécial, « Overall Valid: True » Confirmée entrée, knowledge-tests, knowledge-results.txt
Le [SetUpFixture] TestEnvironment de GA s’exécute-t-il avant les tests de GA.Business.Core.Tests.Configuration ? Non : il est seul dans son namespace. Un test de sonde à côté de ceux de GA voit GA_TEST_MODE non défini et son dossier bin comme dossier courant. Écrite après lecture du bootstrap, avant l’exécution GA_TEST_MODE: (not set), Current directory is the test's bin folder: True Confirmée entrée, knowledge-tests, knowledge-results.txt
Que crée dotnet new sln avec le SDK 10.0.112 ? Un fichier .slnx, pas un .sln Fretboard.slnx : <Solution> et </Solution>, aucun projet Confirmée entrée, l12-project
Dans le dossier d’une solution avec une bibliothèque, une application console et un projet de test, que compilent et exécutent dotnet build et dotnet test ? dotnet build compile les trois projets ; dotnet test exécute seulement les tests du projet de test dotnet build : Fretboard, Fretboard.App, Fretboard.Tests. dotnet test : un assembly de test, 6 réussis. Non prévu : après dotnet clean, dotnet test laisse Fretboard.App non compilé Confirmée entrée, l12-project
Avec une erreur de compilation dans l’application console, que font dotnet test et dotnet build dans le dossier de la solution ? Écrite après avoir vu l’application non compilée, avant cette exécution dotnet test réussit et se termine avec le code 0 ; dotnet build échoue dotnet test : réussi, code de sortie 0. dotnet build : compilation échouée, code de sortie 1 Confirmée entrée
Qu’écrit dotnet add package CsvHelper, et CsvHelper lit-il une valeur entre guillemets qui contient une virgule comme un seul champ ? La dernière version stable ; oui, là où Split(',') donne un champ de trop Version="33.1.0" le 3 octobre 2026 ; Split : 4 champs, CsvHelper : 3 Confirmée entrée, l12_csvhelper.cs
#:package fonctionne-t-il dans une application basée sur un fichier, avec et sans version ? #:package CsvHelper@33.1.0 restaure et s’exécute ; sans @version la restauration le refuse 3 champs ; sans la version, error NU1015: The following PackageReference item(s) do not have a version specified: CsvHelper, code de sortie 1 Confirmée entrée, l12_package_no_version.cs
GetRecords<Project>() de CsvHelper peut-il remplir un record positionnel qui a aussi une propriété calculée ? Oui, par son constructeur, d’après les noms de l’en-tête, en ignorant SourceFiles 12 records et 632 fichiers source ; leur liste est égale à une liste construite à la main Confirmée entrée, ProjectFileTests.cs
Que fait dotnet pack sans option d’une bibliothèque qui référence CsvHelper ? Une compilation Release, bin/Release/Fretboard.1.0.0.nupkg, un zip avec lib/net10.0/Fretboard.dll et un .nuspec qui liste CsvHelper « >= 33.1.0 » Comme prévu ; le .nuspec écrit version="33.1.0", la notation de NuGet pour 33.1.0 ou plus, avec exclude="Build,Analyzers". Non prévu : le message sur le readme, le commit de HEAD dans le .nuspec, et un second dotnet pack sans changement qui n’affiche aucune des deux lignes Confirmée entrée, l12_ex_package.cs
Un package qui a un avis de sécurité connu provoque-t-il un avertissement, et dotnet list package --vulnerable échoue-t-il ? Newtonsoft.Json 12.0.1 donne NU1903 à la restauration ; la commande le liste et se termine avec le code 0 NU1903 avec GHSA-5crp-9r3c-p9vr, affiché deux fois par une application basée sur un fichier ; dotnet list package --vulnerable et dotnet package list --vulnerable affichent le tableau et se terminent avec le code 0 Confirmée entrée, l12_audit.cs
Combien de temps prennent deux Task.Delay(500), attendus l’un après l’autre puis avec Task.WhenAll ? Au moins 1000 ms ; moins de 900 ms Cinq exécutions : de 1014 à 1023 ms, puis de 508 à 514 ms Confirmée entrée, l12_async.cs
Que devient un appel async sans await dans un programme à instructions de niveau supérieur ? Avertissement CS4014, et la ligne écrite après son délai n’apparaît jamais CS4014 en (2,1) ; Before, After, code de sortie 0 Confirmée entrée, l12_forgotten_await.cs
Que dit le compilateur d’un await dans un constructeur, et d’un constructeur async ? CS4033 ; CS0106, modificateur non valide CS4033, comme prévu. public async VoicingIndex() donne CS0246 pour un type async et CS0542, un membre nommé comme son type : l’analyseur lit une méthode Partiellement réfutée entrée, l12_constructor_await.cs
AllProjects.slnx de GA liste-t-il tous les fichiers de projet du dépôt ? Non : au moins 5 .csproj ou .fsproj manquent 36 des 111 manquent ; aucun n’est listé sans exister Confirmée entrée, lesson12_counts.py, lesson12-results.txt
Les projets de test de GA hors de la solution tournent-ils dans la CI, et des projets hors de la solution sont-ils compilés quand même ? Écrite après le comptage précédent Au moins un projet de test n’est nommé par aucun workflow ; au moins un projet hors de la solution est référencé par un projet listé Six projets de test, 102 lignes avec un attribut de test, nommés par aucun workflow ; 7 projets hors de la solution référencés, GA.Business.Core par 4 Confirmée entrée, lesson12_counts.py, lesson12-results.txt
GA.Business.Core.Graphiti.Tests compile-t-il ? Écrite après avoir lu son fichier de projet et son test Non : il utilise GA.Business.Graphiti sans ProjectReference, donc CS0246 5 erreurs : CS0234 aux lignes 7 et 8, puis CS0246 Confirmée entrée, ga-probes
Un package est-il déclaré avec plusieurs versions dans les fichiers de projet de GA ? Oui, au moins un 35 packages ; Microsoft.Extensions.Hosting et MongoDB.Driver avec cinq versions chacun Confirmée entrée, lesson12_counts.py, lesson12-results.txt
Les éléments PackageReference Update du Directory.Build.props de GA changent-ils une version ? Écrite après avoir lu le fichier Non : Microsoft.Extensions.Http reste à la 9.0.10 du fichier de projet des tests Graphiti 9.0.10 ; avec la même ligne dans Directory.Build.targets, 10.0.0, alors que Microsoft.Extensions.Options, mis à jour dans le seul fichier props, reste à 9.0.10 Confirmée entrée, ga-probes
Le code de GA hors tests bloque-t-il sur des tâches à au moins 20 endroits ? Oui 27 correspondances de .Result, .Wait() et GetAwaiter().GetResult() : 4 sont des SemaphoreSlim.Wait(), 7 lisent des tâches après await Task.WhenAll, 5 sont dans des fichiers retirés de la compilation ; il en reste 11, dont 3 dans des constructeurs Réfutée entrée, lesson12_counts.py, lesson12-results.txt
GA a-t-il une méthode async void qui n’est pas un gestionnaire d’événement ? Oui, au moins une Aucune Réfutée entrée, lesson12_counts.py
GaCLI tourne-t-il depuis un autre dossier sans appsettings.yaml, et sync-mongodb démarre-t-il ? Écrite après avoir lu Program.cs Il affiche son aide ; sync-mongodb s’arrête sur un MusicalObjectsService manquant, depuis les deux dossiers Aide affichée ; Error during sync: Unable to resolve service for type 'GA.Data.MongoDB.Services.MusicalObjectsService'... depuis les deux dossiers, code de sortie 0 Confirmée entrée, ga-probes

2026-09-14 — Le SDK et les applications basées sur des fichiers

Section intitulée « 2026-09-14 — Le SDK et les applications basées sur des fichiers »
  • Ma machine a deux SDK : 10.0.112 et 11.0.100-preview.3.26207.106. Sans global.json, dotnet choisit la préversion. Le dossier du cours fixe 10.0.100 avec rollForward: latestFeature, ce qui sélectionne ici 10.0.112.
  • Le même jour, WinGet propose Microsoft.DotNet.SDK.10 en version 10.0.401 : une bande de fonctionnalités plus récente que la mienne. La page des applications basées sur des fichiers indique que #:include est disponible à partir du SDK 10.0.300 ; la leçon 1 dit donc qu’une application basée sur un fichier tient en un seul fichier par défaut, et que #:include en ajoute d’autres à partir de ce SDK. Je n’ai pas essayé #:include : mon SDK est plus ancien.
  • Les exemples sont des applications basées sur des fichiers plutôt qu’un projet par leçon : un débutant tape un fichier et l’exécute, sans rien configurer. Un simple dotnet run app.cs a pris 0,9 s la première fois et 0,2 s la suivante.
  • Les autres fichiers .cs du même dossier ne sont pas compilés avec l’application : hello.cs a fonctionné à côté d’un fichier plein d’erreurs.
  • Les avertissements ne s’affichent que quand le SDK compile. Un second dotnet run d’un fichier inchangé n’affiche aucun avertissement, et dotnet run --no-cache n’y change rien : il saute la vérification « à jour » de l’application basée sur un fichier, mais MSBuild trouve toujours la sortie compilée à jour et n’appelle pas le compilateur. Un dotnet clean app.cs avant chaque exécution fait revenir les avertissements ; check.sh le fait, et la leçon 3 le dit au lecteur.
  • Les erreurs du compilateur vont sur la sortie standard, avec le chemin complet du fichier ; The build failed. Fix the build errors and run again. va sur la sortie d’erreur. check.sh fusionne les deux et ne garde que le nom du fichier, pour que les fichiers attendus soient les mêmes sur toutes les machines.
  • Les erreurs de syntaxe cachent les autres : dans le programme cassé de l’exercice 2 de la leçon 1, la faute de frappe Writeline (CS0117) n’est signalée qu’une fois le ; et le guillemet manquants corrigés. L’exercice repose là-dessus.
  • Entrée standard : quand l’entrée vient d’un fichier, le texte tapé n’apparaît pas dans la sortie, donc les fichiers attendus contiennent les questions directement suivies des réponses. Les leçons montrent le terminal tel qu’une personne le voit, avec les lignes tapées, et le disent.
  • Culture : la culture de mon Windows est en-CA. Avec es-ES, double.TryParse("1.5") renvoie true et 15, car le point sépare les milliers en espagnol ; avec fr-FR, il renvoie false. L’exemple fixe chaque culture explicitement, donc la sortie est la même sur tous les OS. Les autres exemples n’utilisent que des formats qui s’affichent de la même façon en en-CA, en en-US et dans la culture invariante du runner Linux.
  • Commit b19a296, exécution 34914488934 : verte sur les trois OS. actions/setup-dotnet, avec le global.json du cours, a installé le SDK 10.0.401 sur les trois runners, alors que les sorties ont été capturées avec 10.0.112 : tous les messages du compilateur sont identiques. Les jobs ont pris 54 s sous Linux, 1 min 36 s sous macOS et 2 min 18 s sous Windows, avec un dotnet clean et une compilation pour chacune des 58 applications basées sur des fichiers.
  • Math.Pow(2, 7 / 12.0) affiché avec tous ses chiffres, 164.81377845643496, est le même sur les trois OS.
  • La ligne shebang #!/usr/bin/env -S dotnet -- fonctionne sur les runners Linux et macOS après chmod +x, et dotnet run l’ignore sous Windows.
  • Les codes de sortie d’une exception non gérée diffèrent, donc check.sh les affiche et ne compare que exit crash :
    • Linux et macOS : 134 (le processus s’interrompt, SIGABRT) pour les trois exemples qui plantent ;
    • Windows, vu depuis Git Bash : 127 pour IndexOutOfRangeException et SwitchExpressionException, et 139 avec un message « Segmentation fault » pour NullReferenceException ;
    • Windows, vu depuis PowerShell : 0xE0434352 (-532462766), le code d’une exception .NET, pour IndexOutOfRangeException, mais 0xC0000005 (-1073741819), une violation d’accès, pour NullReferenceException.
  • Les exemples utilisent Guitar Alchemist comme petites données : l’accordage standard de Tuning.Default au commit a826864, et douze noms de projets de code/ladybugdb/data/ga/projects.csv, extraits au commit a26a7893. Rien dans ces leçons n’a révélé de problème dans GA.
  • La leçon 5 présente la construction, l’identité des instances, les champs privés, les propriétés publiques, les méthodes et un compteur static partagé, puis propose de créer une classe PracticeSession. Les pages anglaise, française et espagnole contiennent les mêmes extraits C# testés et les mêmes sorties du compilateur.
  • Sous Windows, avec le SDK 10.0.112, C:/Program Files/Git/bin/bash.exe check.sh a réussi pour les exemples, les solutions et les extraits refusés du cours, y compris les nouveaux fichiers l05_*. Les deux nouveaux extraits refusés ont produit respectivement CS0122 et CS0200. Le bash par défaut de la machine était celui de WSL et ne trouvait pas le dotnet Windows : ce premier échec venait de l’environnement de test, pas du code.
  • Ce résultat local ne remplace pas une nouvelle CI sur trois OS et ne prouve pas que la leçon est publiée. Les leçons 6 à 12 restent au stade de plan.

2026-09-30 — La leçon 5 dans la CI sur trois OS

Section intitulée « 2026-09-30 — La leçon 5 dans la CI sur trois OS »
  • Le run 36664604662 du workflow C# for beginners examples, sur le commit 669d42a de la PR #60, a réussi sous ubuntu-latest, windows-latest et macos-latest. Dans la sortie de chaque job, check.sh affiche ok pour l05_objects, l05_ex_practice, l05_get_only_property et l05_private_field : les sorties et les diagnostics CS0122 et CS0200 correspondent à expected/ sur les trois OS.
  • La leçon 6 compare une structure et une classe (copie à l’affectation, copie en argument, Equals), puis présente les records (== sur les données, ToString, with, readonly record struct) et les énumérations (numérotées à partir de 0, valeur par défaut, casts, Enum.IsDefined, Enum.Parse). Elle compte quatre exemples, trois exercices et cinq extraits refusés, dont les diagnostics sont CS0019, CS1612, CS8852 (trois fois dans deux fichiers) et CS0266. Un script a vérifié que les trois langues partagent les mêmes blocs de code, commentaires mis à part, et que chaque bloc correspond à son fichier dans code/csharp-beginner et expected/.
  • Sous Windows, avec le SDK 10.0.112 et Git Bash, check.sh s’est terminé avec le code 0 et 75 lignes ok, dont les 12 fichiers l06_*. Ce n’est pas encore une exécution de la CI sur trois OS.
  • Puis dans la CI : le run 36865237779 du workflow C# for beginners examples, sur le commit 114b5ed de la PR #93, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 12 fichiers l06_* ; les jobs comptent 76 lignes ok sous Linux et macOS et 75 sous Windows, car check.sh ne lance l’exemple à shebang avec ./ que sous Linux et macOS.
  • Surprises, toutes reprises dans la leçon : CS8524 avertit pour une expression switch qui a une branche par nom, et son exemple est (ChordQuality)5 alors que le programme échoue sur 7 ; le littéral 0 se convertit en énumération sans cast, alors que 2 donne CS0266 ; une List<T> de structures modifiables refuse shape[0].Fret = 5 avec CS1612.
  • Un brouillon de l’exercice 3 disait que shape[i].Fret += 2 sur une liste de readonly record struct donne le même CS1612. Une sonde compilée avant publication a donné CS8852 ; le texte a été corrigé, et compile_fail/l06_readonly_position.cs conserve désormais les deux lignes CS8852. Deux autres phrases reposent sur des sondes restées hors du code du cours : ranger une copie modifiée avec shape[0] = position; a affiché 5, et List<T>.Contains sur une classe sans égalité propre a répondu False.
  • Dogfooding : au commit 5c3a52a de GA, ChordQuality commence par Other, et PositionLocation, Str et Fret sont des types readonly record struct. Rien dans cette leçon n’a révélé de problème dans GA.
  • La leçon 7 construit Guitar et Ukulele sur une classe de base StringInstrument (base(...), virtual, override, base.Describe(), ToString), puis une classe abstraite Instrument avec une méthode abstraite Play, puis une interface IHasRange qu’une classe et un record implémentent tous deux. Elle compte quatre exemples, dont un qui compile avec l’avertissement CS0114, trois exercices et cinq extraits refusés, dont les diagnostics sont CS0506, CS0509, CS0144, CS0534 et CS0535. Le script utilisé pour la leçon 6 a vérifié les 20 blocs de code des trois langues contre code/csharp-beginner et expected/.
  • Sous Windows, avec le SDK 10.0.112 et Git Bash, check.sh s’est terminé avec le code 0 et 87 lignes ok, dont les 12 fichiers l07_*. Ce n’est pas encore une exécution de la CI sur trois OS.
  • Puis dans la CI : le run 36893689911 du workflow C# for beginners examples, sur le commit b347f7d de la PR #95, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 12 fichiers l07_* ; les jobs comptent 88 lignes ok sous Linux et macOS et 87 sous Windows, pour la même raison qu’à la leçon 6.
  • Deux phrases du brouillon ne reposaient sur aucune exécution et ont changé avant publication. Que seule une variable de type Ukulele trouve une méthode qui masque est désormais une ligne de examples/l07_hiding_warning.cs, qui affiche a ukulele. « Les numéros MIDI sont ceux de la leçon 4, où do4 vaut 60 » était faux, puisque la leçon 4 ne donne aucun numéro MIDI ; la phrase dit maintenant que les numéros MIDI comptent des demi-tons, 12 par octave.
  • Dogfooding : au commit 5c3a52a de GA, ChordTemplate est un record abstrait dont les records dérivés TonalModal et Analytical redéfinissent Name, et dont le propre ToString() => Name n’est pas sealed. La page sur les records dit qu’un ToString sealed empêche le compilateur d’en synthétiser un dans les records dérivés (mise en forme intégrée pour l’affichage) ; celui-ci devait donc être masqué. Une sonde l’a confirmé : un projet console qui référence GA.Domain.Core et GA.Domain.Services au commit 5c3a52a, compilé avec le SDK 10.0.112, a affiché TonalModal { Name = Major 7th, PitchClassSet = 0 4 7 E, … } pour ToString() et pour $"{template}" sur un TonalModal, sur un Analytical et sur ChordTemplateFactory.CreateModalChords(...).First(), et la réflexion a désigné le record dérivé comme le type qui déclare ToString. Une réplique de même forme avec public sealed override string ToString() => Name a affiché maj7. Un seul site d’appel en dépend : ContextualChordsController.cs:301 passe template.ToString() à GetVoicingsForChordAsync, qui le journalise (FretboardServices.cs:536) ; la réponse HTTP prend le nom de l’accord dans la route, pas dans cette chaîne. Le constat figure dans le tableau QA ; il n’a pas été signalé à GA.

2026-10-02 — Exceptions et sécurité face à null

Section intitulée « 2026-10-02 — Exceptions et sécurité face à null »
  • La leçon 8 attrape des exceptions avec try et catch, avec un diagramme des classes d’exception ; exécute finally après un return, après un catch et avant qu’une exception quitte la méthode ; lève une ArgumentOutOfRangeException avec nameof ; puis revient sur null : CS8600 et CS8618, required, l’opérateur !, et WarningsAsErrors=nullable défini avec #:property. Elle compte six exemples, quatre fichiers d’exercice (dont le point de départ de l’exercice 3, avec quatre avertissements) et cinq extraits refusés, dont les diagnostics sont CS0160, CS0165, CS0029, CS9035 et CS8602 en erreur. Le script utilisé pour les leçons 6 et 7 a vérifié les 28 blocs de code des trois langues contre code/csharp-beginner et expected/.
  • Sous Windows, avec le SDK 10.0.112 et Git Bash, check.sh s’est terminé avec le code 0 et 102 lignes ok, dont les 15 fichiers l08_*. Ce n’est pas encore une exécution de la CI sur trois OS.
  • La première exécution de la CI de la PR #109 a échoué sous Linux seulement (run 36968274101) : le runner Linux a affiché les avertissements de l08_null_warnings.cs et l08_ex_null_start.cs dans l’ordre des lignes, alors que Windows, en local comme dans la CI, et macOS affichaient CS8618 en premier. Les messages eux-mêmes étaient identiques. check.sh place désormais les messages du compilateur en premier, les erreurs avant les avertissements, chaque groupe trié par ligne et colonne, et la leçon montre cet ordre. Aucun fichier attendu des leçons 1 à 7 n’a changé : leurs messages venaient déjà dans cet ordre.
  • Puis, avec cette correction : le run 37009963497 du workflow C# for beginners examples, sur le commit e2a95ef de la PR #109, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 15 fichiers l08_* ; les jobs comptent 103 lignes ok sous Linux et macOS et 102 sous Windows, pour la même raison qu’à la leçon 6.
  • Surprises, toutes reprises dans la leçon : throw "fret out of range"; donne CS0029, une erreur de conversion, là où j’attendais CS0155 (The type caught or thrown must be derived from System.Exception) ; string capo = null; sur une variable locale donne CS8600, pas CS8625, alors que le fichier de test de GA ci-dessous a reçu CS8625 pour un argument null ; dans l08_null_warnings.cs, CS8618 se trouve sur la déclaration de la propriété et la ligne qui plante n’a aucun avertissement ; tuner off s’affiche avant la valeur que renvoie Tune.
  • Une phrase repose sur une sonde restée hors du code du cours : sans sa vérification, NoteOfString(standard, 7) échoue avec une IndexOutOfRangeException.
  • Dogfooding au commit 5c3a52a de GA, avec les hypothèses écrites avant de mesurer dans le tableau des expériences, et les sondes et leur mode d’emploi dans code/csharp-beginner/ga-probes :
    • NoWarn liste quinze avertissements de nullabilité dans Directory.Build.props:21, et à nouveau dans Directory.Build.targets:7. J’attendais plus de 100 avertissements cachés dans GA.Domain.Core ; sans les deux listes, GA.Domain.Core et GA.Core n’en ont aucun. ZzNullableCanary.cs, sept erreurs de nullabilité ajoutées à GA.Domain.Core, a compilé sans avertissement avec les listes, et avec ses sept avertissements (CS8600 ×2, CS8602 ×3, CS8618, CS8625) sans elles. L’hypothèse est réfutée, et le tableau QA garde le constat : les listes ne cachent que les erreurs à venir.
    • new Fret(50), Fret.FromValue(50) et Fret f = 50; lèvent tous une ArgumentOutOfRangeException, pour 50 et pour -2, comme prévu, et Fret.TryCreate(50) renvoie Failure(Fret number must be between -1 (muted) and 36, got 50). Le message de l’exception nomme une constante privée, GA.Domain.Core.Instruments.Primitives.Fret value (50) cannot be greater than _maxValue (36). (Parameter 'value'), parce que IRangeValueObject.cs le construit à partir des expressions de l’appelant, et son ActualValue reste vide.
    • La question ouverte du 2026-10-01 a sa réponse : new PositionLocation(5, 3), avec la corde et la frette inversées sous forme de simples nombres, compile sans avertissement et construit la corde 5, frette 3. Une compilation avec tous les analyseurs (AnalysisLevel=latest-all) n’a demandé, sur ces lignes, que des types explicites à la place de var. Les conversions implicites depuis int (Str.cs:45, Fret.cs:72) laissent passer l’inversion.
  • La leçon 9 couvre Dictionary<TKey, TValue> : l’indexeur, Add, TryGetValue, le comptage avec GetValueOrDefault, et l’ordre d’un foreach. Elle couvre HashSet<T>, avec ses opérations d’ensemble sur des copies et SetEquals. Elle présente les lambdas, puis LINQ sur les projets de GA de la leçon 4, devenus des records : Count, Where, Select, OrderBy, ThenBy, First, FirstOrDefault, Any, All, Take et ToList. Elle finit par l’exécution différée, et par la modification d’une collection dans son propre foreach. Elle compte dix exemples, quatre exercices et quatre extraits refusés : CS1503, CS0021, CS0266, et CS0029 avec CS1662 sur la même colonne. Le vérificateur de blocs a mis en correspondance les 37 blocs de code de chaque langue avec code/csharp-beginner et expected/.
  • La leçon 4 annonçait que la leçon 5 remplacerait ses deux tableaux de projets par une seule liste ; la leçon 5 ne l’a pas fait. La leçon 9 le fait, et la leçon 4 renvoie désormais vers elle.
  • Sous Windows, avec le SDK 10.0.112 et Git Bash, check.sh s’est terminé avec le code 0 et 120 lignes ok, dont les 18 fichiers l09_*. Ce n’est pas encore une exécution de la CI sur trois OS.
  • Puis dans la CI : le run 37061776660 du workflow C# for beginners examples, sur le commit 690201d de la PR #113, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 18 fichiers l09_* ; les jobs comptent 121 lignes ok sous Linux et macOS et 120 sous Windows, pour la même raison qu’à la leçon 6.
  • Mesuré avant d’écrire :
    • C# 14 n’a pas d’expression de dictionnaire. Dictionary<string, int> d = ["C": 0, "E": 4]; donne CS1003 et huit autres erreurs de syntaxe, donc la leçon utilise un initialiseur d’index, { ["C"] = 0 }.
    • First sans correspondance lève Sequence contains no matching element. Cette sonde est restée hors du code du cours.
    • Les deux expériences sur les dictionnaires du tableau. La documentation appuie les deux : « The order in which the items are returned is undefined », et Remove « may be safely called without invalidating active enumerators » depuis .NET Core 3.0.
  • Dogfooding au commit 5c3a52a de GA, avec l’hypothèse écrite avant de mesurer dans le tableau des expériences :
    • Un relevé des collections dans le code de GA, lu à ce commit, a fourni les exemples de la leçon :
      • ChordVocabulary.PitchClasses (ChordVocabulary.cs:23), un dictionnaire insensible à la casse lu avec TryGetValue, où "C#" et "Db" donnent tous deux 1. Ses remarques disent que deux copies antérieures « had drifted » (avaient divergé) ; un git grep de ["Db"] = 1 ou { "Db", 1 } trouve 18 fichiers avec leur propre table, celui-ci compris.
      • PitchClassSet, un IReadOnlySet<PitchClass> construit sur un ImmutableSortedSet (PitchClassSet.cs:37), égal à un autre quand leurs Id, calculés à partir des notes, le sont.
    • MusicalKnowledgeService.GetArtistBreakdown garde GetAllArtists().Take(20), les 20 premiers par nom, puis les trie par nombre d’entrées dans un Dictionary. Je m’attendais à plus de 20 artistes, et à des artistes écartés qui ont plus d’entrées qu’un artiste gardé. artists.cs, compilé avec GA.Domain.Services extrait par git archive dans le dossier .ga ignoré, en a trouvé 16 : aucun n’est écarté. Deux exécutions ont donné la même sortie.
    • La raison est la première ligne QA du jour : trois des quatre fichiers YAML ne se désérialisent pas, et chaque chargeur attrape l’exception et garde un élément par défaut. La sonde affiche les messages des chargeurs. Un test de désérialisation, hors du code du cours, a localisé les trois lignes, et j’ai lu chacune face à la classe qu’elle remplit. Les trois lignes sont inchangées sur le main de GA, ae196bf, le plus récent que connaît le clone local.
    • MusicalKnowledgeServiceTests affiche ArtistBreakdown.Take(5) sous === Top Artists === (MusicalKnowledgeServiceTests.cs:51), ce qui dépend aussi de l’ordre du dictionnaire.

2026-10-03 — Un schéma du manche dessiné en C#

Section intitulée « 2026-10-03 — Un schéma du manche dessiné en C# »
  • La leçon 3 montre maintenant les notes des cases 0 à 5 sous forme de schéma, sous la sortie de l03_fretboard.cs. La question était de savoir si ComfyUI ou Blender devaient dessiner certaines images du cours. Une image générée peut se tromper sur le nombre de cordes ou de cases, et ce schéma doit montrer exactement les notes que le programme affiche : c’est donc un programme C# qui le dessine, tools/fretboard.cs, avec les mêmes cordes à vide et les mêmes noms de notes que la leçon.
  • Le SVG ne contient aucun mot, seulement des noms de notes et des nombres, donc les trois langues partagent un seul fichier, src/assets/csharp-beginner/l03-fretboard.svg. La leçon l’importe comme un composant, comme le cours de théorie musicale, pour qu’il suive le thème clair ou sombre. La leçon 3 est devenue un fichier .mdx pour cela ; son adresse ne change pas. La description pour les lecteurs d’écran est traduite.
  • check.sh lance tools/fretboard.cs -- --check, qui échoue si le SVG du dépôt diffère de ce que le programme dessine. Avant de m’y fier, j’ai changé une note du fichier à la main : la vérification a affiché is not up to date et s’est terminée avec le code 1. Une copie avec des fins de ligne Windows passe. Sous Windows, check.sh affiche maintenant 121 lignes ok.
  • L’outil trouve le dépôt à partir de son propre dossier, AppContext.GetData("EntryPointFileDirectoryPath"), et non du dossier courant, et écrit chaque coordonnée comme un int : avec la culture fr-FR, $"{2.5}" donne 2,5. Les deux points comptent pour les fichiers, le sujet de la leçon 10.
  • Rendu avec Edge sans fenêtre dans une page claire, une page sombre et à 343 pixels de large, la largeur d’un téléphone : les 36 notes correspondent ligne par ligne à la sortie du programme. Ce jour-là, Chrome sans fenêtre et la capture par DevTools ont tous deux dépassé leur délai sur cette machine.
  • Puis dans la CI : le run 37142282699 du workflow C# for beginners examples, sur le commit 9229f15 de la PR #126, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok fretboard svg ; les jobs comptent 122 lignes ok sous Linux et macOS et 121 sous Windows, pour la même raison qu’à la leçon 6.
  • La leçon 10 est écrite en trois langues : File, Path, le dossier courant, les sauts de ligne, un fichier CSV, les nombres et les cultures, les flux et using, les encodages, et ce que Guitar Alchemist fait de ses fichiers. Elle a 11 exemples, 4 exercices et 3 extraits refusés, chacun comparé à expected/ par check.sh. La leçon est un fichier .mdx, pour les onglets qui montrent un chemin sur chaque OS.
  • data/ga-projects.csv est nouveau : les douze projets de la leçon 4 avec leurs nombres de fichiers .cs et .fs au commit 5c3a52a de GA, comptés avec git ls-tree dans le dossier de chaque projet. Il réserve deux surprises. GA.Business.Config, un projet F#, contient 8 fichiers C#, les services rencontrés à la leçon 9. GA.Presentation n’a aucun fichier source, seulement son .csproj. .gitattributes donne à *.csv des fins de ligne LF, pour que le fichier soit le même sur chaque OS.
  • Huit hypothèses ont été écrites avant de mesurer, et les huit sont confirmées (tableau des expériences). Deux n’étaient pas à l’aveugle : l’écriture de tools/fretboard.cs, plus tôt le même jour, avait montré EntryPointFileDirectoryPath, et $"{2.5}" avec fr-FR. L’hypothèse E14 vient de la lecture de GA par un sous-agent, avant que je lise ScaleVideoUrlById moi-même.
  • check.sh exécute l10_where.cs une seconde fois depuis la racine du dépôt et compare sa sortie à expected/l10_where_root.txt : la CI vérifie ainsi sur trois OS que le même programme manque son fichier depuis un autre dossier. La racine n’a pas de global.json : là, sur ma machine, dotnet choisit le SDK 11.0.100 en préversion au lieu du 10.0.112, un autre choix fait par le dossier courant. La sortie est la même.
  • TextFieldParser a compilé avec using Microsoft.VisualBasic.FileIO; et sans package : il est fourni avec .NET.
  • La sonde files.cs exécute les chargeurs de GA trois fois, comme le décrit son README : telle que compilée, après suppression des fichiers YAML à côté d’elle, et depuis la racine du code de GA extrait. Sa compilation affiche IL3000 sur son propre usage de Assembly.Location, l’appel dont dépend le repli de ScaleVideoUrlById : dans une application en fichier unique, il renvoie une chaîne vide.
  • Six nouvelles lignes QA viennent de la sonde et de la lecture du code de GA qui gère ses fichiers. Un sous-agent a listé les candidats ; j’ai relu les lignes de chacun au commit 5c3a52a avant de l’écrire. Deux de ces lignes sont lues dans le code seulement, pas exécutées.
  • L’échec de chargement des YAML de la leçon 9 est maintenant GuitarAlchemist/ga#797, déposée le 2026-10-03 avec des liens épinglés sur le main de GA, ba3b9ac, où les trois lignes sont inchangées.
  • Sous Windows avec le SDK 10.0.112, check.sh se termine avec le code 0 et 140 lignes ok, dont les 19 de la leçon 10. Ce n’est pas encore une exécution de la CI sur trois OS.
  • Puis dans la CI : le run 37145323198 du workflow C# for beginners examples, sur le commit 8b9fc03 de la PR #129, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 19 vérifications l10_*, dont l’exécution depuis la racine du dépôt ; les jobs comptent 141 lignes ok sous Linux et macOS et 140 sous Windows, pour la même raison qu’à la leçon 6.
  • La leçon 11 est écrite en trois langues : un test à la main, une bibliothèque et un projet de test, [Fact] et dotnet test, [Theory], quatre tests qui échouent exprès, un objet par test, la surface publique, et les tests de Guitar Alchemist. Le code est dans l11-tests : la bibliothèque Fretboard, avec des méthodes des leçons 4, 8 et 10 inchangées ; Fretboard.Tests, 26 tests avec les solutions des exercices ; Pitfalls.Tests, 6 tests dont 4 échouent exprès. examples/l11_by_hand.cs et compile_fail/l11_private.cs tournent avec les autres fichiers.
  • Les projets viennent de dotnet new classlib, dotnet new xunit et dotnet add reference, lancés dans le dossier du cours ; la leçon cite leurs messages. Le modèle crée un projet xUnit v2, alors que le site de xUnit dit que la v2 est en maintenance et encourage la v3.
  • check.sh lance dotnet test sur les deux projets de test après dotnet clean, pour que les avertissements de la compilation s’affichent à chaque fois, et garde un résumé : les avertissements, chaque test en échec et son message, la dernière ligne. Il retire les chemins, les durées et les piles d’appels, et trie les tests en échec par nom : xUnit a signalé les quatre échecs dans un autre ordre que celui du fichier. Les flèches ↓ et ↑ d’un échec sur une collection ont traversé Git Bash sous Windows ; la CI dira si c’est le cas sur les trois OS.
  • Sept hypothèses ont été écrites avant de mesurer, et les sept sont confirmées (tableau des expériences). Les deux dernières ont été écrites après lecture du fichier de test de GA et de son bootstrap, avant de les exécuter.
  • Observé sans hypothèse : depuis un projet de test, un membre de bibliothèque marqué internal donne CS0117, « does not contain a definition », et non CS0122, dans une paire de projets d’essai ; l’analyseur du package xunit signale les arguments inversés (xUnit2000) avant que les tests tournent.
  • Le premier échec de la leçon est une vérification à la main : FretFrequency(246.94, 1) donne 261.62, et une table donne 261.63 pour le do central, parce que la corde de si à vide est déjà arrondie. Le programme se termine quand même avec le code 0, ce qui motive un framework.
  • La sonde knowledge-tests est un projet NUnit qui compile sans changement quatre fichiers de GA.Business.Core.Tests extraits du code : MusicalKnowledgeServiceTests.cs, TestEnvironment.cs, GlobalUsings.cs et AssemblyInfo.cs, avec les versions de packages de ce projet. ProbeTests.cs est son seul fichier propre. Sa compilation n’affiche que IDE0028 dans ScaleMetadataRegistry.cs de GA. Son exécution : 9 tests, 6 réussis (les 5 de GA et celui de la sonde), 3 ignorés.
  • Cela répond à la question ouverte du journal : aucun test de GA n’échoue quand un fichier YAML de GA.Business.Config cesse de se charger. La question est retirée de la liste plus bas.
  • Sous Windows avec le SDK 10.0.112, check.sh se termine avec le code 0 et 144 lignes ok, dont les 4 de la leçon 11. Ce n’est pas encore une exécution de la CI sur trois OS.
  • Puis dans la CI : le run 37170066128 du workflow C# for beginners examples, sur le commit c3427a9 de la PR #135, a réussi sous ubuntu-latest, windows-latest et macos-latest. Chaque job affiche ok pour les 4 vérifications l11_* ; les jobs comptent 145 lignes ok sous Linux et macOS et 144 sous Windows, pour la même raison qu’à la leçon 6. l11_Pitfalls.Tests réussit aussi sous Windows : les flèches ↓ et ↑ de l’échec sur une collection traversent Git Bash sans changement.
  • La leçon 12 est écrite en trois langues : une solution avec la bibliothèque de la leçon 11, une application console et un projet de test ; les codes de sortie ; un package NuGet dans une application basée sur un fichier et dans la bibliothèque ; un package qui a une vulnérabilité connue ; dotnet pack ; un premier regard sur async ; et la solution, les versions de packages, l’outil en ligne de commande et les attentes bloquantes de Guitar Alchemist. Le code est dans l12-project : Fretboard, la bibliothèque de la leçon 11 avec Guitar inchangé, Project sans Parse, et ProjectFile, qui lit le fichier CSV avec CsvHelper 33.1.0 ; Fretboard.App, avec trois commandes ; Fretboard.Tests, 6 tests. Quatre exemples, deux extraits refusés et quatre solutions d’exercices sont des applications basées sur un fichier.
  • check.sh empaquette la bibliothèque avant la boucle, puisque exercises/l12_ex_package.cs lit le package, et supprime d’abord l’ancien package : sans changement, dotnet pack le garde et n’affiche aucune de ses deux lignes. Il lance dotnet test sur la solution après dotnet clean et vérifie que l’application console n’est toujours pas compilée, puis dotnet build, dont il réduit la sortie aux noms des projets, triés, et aux totaux. Il lance l’application avec six commandes et affiche chaque code de sortie. Une nouvelle règle sed retire le dossier des messages NuGet d’une ligne #:package, file.csproj : warning NUxxxx.
  • Douze hypothèses sur le SDK et neuf sur GA ont été écrites avant de mesurer, certaines après avoir lu le code qu’elles testent (tableau des expériences) : 16 confirmées, 1 partiellement réfutée, 2 réfutées. Les réfutées portent sur les attentes de GA : 11 attentes bloquantes compilées, pas 20 ou plus, et aucun async void.
  • Observé sans hypothèse : dotnet test sur une solution ne compile pas l’application console, ce qui a mené à l’hypothèse sur une erreur de compilation ; l’avertissement de l’audit NuGet sort deux fois dans une application basée sur un fichier ; le .nuspec enregistre le commit de HEAD ; 12 des 28 commandes de GaCLI lèvent NotImplementedException, et le run 37167745677 de Quality Check de GA est vert avec une étape terminée par le code de sortie 134.
  • Les sondes sont dans ga-probes : lesson12_counts.py lit le commit avec git seulement, et lesson12-results.txt garde sa sortie ; la compilation de GA.Business.Core.Graphiti.Tests et sa restauration, avec une copie témoin dont la ligne Update est dans Directory.Build.targets ; GaCLI, compilé à partir des 18 projets de ses références en 28 s avec 21 avertissements, et lancé depuis deux dossiers.
  • Cela règle le point à vérifier sur GaCLI lancé depuis un autre dossier : il tourne, et à ce commit appsettings.yaml ne change rien. Le point est retiré de la liste ci-dessous, et la ligne du tableau QA est mise à jour.
  • Sous Windows avec le SDK 10.0.112, check.sh se termine avec le code 0 et 158 lignes ok, dont les 14 de la leçon 12. Ce n’est pas encore une exécution de la CI sur trois OS.
  • La première exécution de la CI de la PR, 37174610693, a échoué sur l12_audit et l12_package_no_version sur les trois OS, et a passé les 12 autres vérifications de la leçon 12. La CI utilise le SDK 10.0.401, que global.json autorise (rollForward: latestFeature), et ce SDK nomme le projet caché derrière une application basée sur un fichier l12_audit.cs.csproj, là où le 10.0.112 dit l12_audit.csproj. La règle sed n’attendait pas de point dans le nom et laissait le dossier. check.sh écrit maintenant file.csproj pour les deux, et la leçon donne les deux noms.
  • Puis dans la CI : le run 37175088215 du workflow C# for beginners examples, sur le commit 87dbafd de la PR #137, a réussi sous ubuntu-latest, windows-latest et macos-latest, tous trois avec le SDK 10.0.401. Chaque job affiche ok pour les 14 vérifications l12_* ; les jobs comptent 159 lignes ok sous Linux et macOS et 158 sous Windows, pour la même raison qu’à la leçon 6. L’avertissement de l’audit NuGet de l12_audit.cs sort sur les trois machines, et la sortie triée de la compilation est la même.

2026-10-04 — Correctifs proposés à Guitar Alchemist

Section intitulée « 2026-10-04 — Correctifs proposés à Guitar Alchemist »

Treize lignes du tableau QA sont reparties vers GA sous forme de cinq pull requests, ouvertes depuis un clone séparé à main 75959835. Chacune mesure le défaut avant et après le correctif, et la CI de GA passe sur les cinq. Aucune n’est encore fusionnée : GA laisse la relecture et la fusion à quelqu’un d’autre que l’auteur.

PR Lignes QA Changement Avant → après
n° 808 Les trois fichiers YAML (issue n° 797) ; les tests qui ne voient pas une valeur de repli Les trois fichiers se chargent ; les trois tests [Ignore] s’exécutent, et un nouveau test échoue quand un fichier retombe sur son élément par défaut Avec les anciens fichiers, 5 des 9 tests échouent, le nouveau avec Expected: greater than 1 But was: 1 pour chaque fichier cassé
n° 811 ReloadConfiguration, GetArtistBreakdown, la culture du CSV, son nombre de lignes, sync-mongodb Un nouveau Lazy au lieu de FieldInfo.SetValue ; le tri avant Take(20) ; FormattableString.Invariant ; l’en-tête hors du compte ; Environment.Exit(1) FieldAccessException → les quatre fichiers se rechargent ; < 6, 8, 8, … > valeurs par ligne sous fr-FR → 6 ; 3 lignes comptées pour un en-tête et deux lignes → 2 ; sync-mongodb sort avec 0 → 1
n° 812 Les projets de test hors de AllProjects.slnx, les éléments PackageReference Update, le scan de sécurité Les éléments passent dans Directory.Build.targets ; trois projets de test rejoignent la solution ; le scan échoue sur un avis qu’une ligne de base ne liste pas Rétrogradations NU1605 masquées par NoWarn : 80 → 40 ; 56 tests de plus en CI ; 21 avis dans la ligne de base
n° 816 Le ToString d’un modèle d’accord, le [SetUpFixture] ChordTemplate.ToString devient sealed ; TestEnvironment passe dans l’espace de noms racine du projet Un vidage de 442 ou 475 caractères → le nom ; GA_TEST_MODE absent → 1, avec la racine du dépôt comme répertoire courant
n° 817 ScaleVideoUrl GA.Domain.Core.csproj incorpore scale_video_urls.json, et ScaleVideoUrlById le lit sous son nom par défaut Ensembles avec une URL de vidéo : 0 → 1 490 sur 4 096
  • Avec le YAML de la n° 808, GA compte 63 artistes au lieu de 16 : la coupe de GetArtistBreakdown avant le tri se voit, et le test de la n° 811 sur cette répartition échoue sur ces données avec l’ancien code.
  • Déplacer seuls les éléments Update faisait échouer la restauration, error NU1107: Version conflict detected for Microsoft.Extensions.Logging.Abstractions dans GA.Infrastructure, car cinq packages Microsoft.Extensions seulement étaient alignés. La n° 812 met les 19 qui sont livrés avec .NET et ASP.NET Core à une seule version, 10.0.6, la plus haute que demande un projet ou un package.
  • Trois des six projets de test hors de la solution, qu’aucun workflow ne nomme, y entrent : les tests Graphiti, avec la référence de projet qui leur manquait, ceux de ProbabilisticGrammar et ceux de TabConversion.Api. GaChatbot.Tests et Tests/GaApi.Tests ne compilent pas non plus sur main, et FloorManager.Tests.Playwright a besoin d’un FloorManager lancé.
  • Le contrôle risk-report de GA échoue sur la n° 812 par construction : sa politique interdit à un agent de modifier .github/workflows et classe Directory.Build.*, *.csproj et *.slnx parmi les décisions irréversibles ; un mainteneur doit donc la relire.
  • La n° 817 modifie un .csproj, que la politique de risque de GA compte comme une porte à sens unique : c’est donc une pull request à part.
  • La première exécution de la CI de la n° 816 a échoué sur un test d’un autre projet, qui laisse 10 secondes à PowerShell ; le même test avait échoué sur une autre branche le 2026-05-23, et la relance est passée.

2026-10-06 — Les PR 816 et 817 de GA, revérifiées

Section intitulée « 2026-10-06 — Les PR 816 et 817 de GA, revérifiées »

Les PR n° 816 et n° 817 de GA sont toujours ouvertes, sans revue ni commentaire. Leur CI passe, 16 vérifications dont 5 ignorées, et GitHub les dit toutes deux fusionnables sans conflit, bien que le main de GA ait avancé de 14 commits depuis leur base, jusqu’à 40d33747. Lus par l’API GitHub, les trois défauts n’ont pas changé à ce commit :

  • Le ToString() => Name de ChordTemplate n’est toujours pas sealed (ChordTemplate.cs:84).
  • ScaleVideoUrlById lit toujours la ressource GA.Domain.Core.Scales.Data.scale_video_urls.json et le chemin de repli Scales/Data/ (ScaleVideoUrlById.cs:30, :38), et GA.Domain.Core.csproj ne nomme toujours pas le fichier.
  • TestEnvironment est toujours seul dans GA.Business.Core.Tests.TestBootstrap (TestEnvironment.cs:1).

La leçon 7 montre maintenant le piège de ToString à côté de ChordTemplate, avec examples/l07_record_tostring.cs. Un record dérivé affiche Seventh { Name = Major 7th, Quality = Major 7th } ; le même record sous une base dont le ToString est sealed affiche Major 7th. Les deux sorties viennent du SDK 10.0.112, comparées à expected/ comme le fait check.sh. L’exemple existant l07_inheritance.cs, lancé de la même façon, correspond toujours à sa sortie attendue. Les sondes GA n’ont pas été relancées : elles demandent une copie de GA hors du code du cours. check.sh non plus n’a pas été lancé en entier.

  • Le nombre d’artistes que GA compterait une fois ses trois fichiers YAML chargés. Un script PyYAML sur les fichiers bruts, hors du code du cours, l’estime à 63. Selon la même estimation, Take(20) écarterait 8 artistes qui ont plus d’entrées que le plus petit nombre gardé, dont The Beatles (3 entrées).
  • Les commandes d’installation pour Linux et macOS : seul le setup-dotnet de la CI a tourné sur ces OS.
  • La démonstration du débogueur de la leçon 3 dans VS Code, Visual Studio et Rider, pour une application basée sur un fichier et pour un projet. VS Code 1.118 et Rider sont installés sur ma machine ; Visual Studio ne l’est pas.
  • Les conditions de licence des trois éditeurs, au moment de la lecture.
  • Les autres projets de GA, au-delà de GA.Domain.Core et GA.Core, ont-ils des avertissements de nullabilité que les deux listes NoWarn cachent ? Seuls ces deux-là ont été mesurés (2026-10-02).