Repository navigation
[1.0.0] - 2026-10-30
Changed
- Site vitrine - SYNE sans visuel de capture, schéma conceptuel à la place : SYNE est un moteur, sans interface à capturer ; les emplacements
.phde la page SYNE et la capture du Launcher affichée à sa place sur l'accueil sont remplacés parSyneSchema.astro(SVG en ligne, libellés localisés, adapté aux deux thèmes), légendée comme « illustration conceptuelle, pas une capture ». ECHOS et PRISM gardent leurs captures réelles. Les animations GIF inutiles (site et dépôt) sont supprimées et leurs mentions retirées : les animations éventuelles restent dans le dépôt et sont servies en WebP/MP4. - Site vitrine - refonte UX, SEO et accessibilité : en-tête et pied de page refondus (5 entrées de navigation, sous-menus cliquables, menu mobile accessible), recherche Pagefind, sommaires d'articles, fil d'Ariane et JSON-LD, page 404 bilingue, icônes PWA. Les chiffres publiés (versions des composants, état CI, tests, couverture) proviennent désormais de
site/src/data/au lieu d'être écrits en dur dans les pages ; la documentationdocs/website/est alignée sur l'implémentation réelle (revue du 21 octobre 2026, rapportdocs/website/REVIEW-2026-10-21.md). - Gouvernance - passage de la licence MIT à la GNU GPL v3.0 ou ultérieure : les composants SYNE, ECHOS, Launcher, syne-mock et site vitrine sont désormais distribués sous
GPL-3.0-or-later(copyleft). Le projet Unreal PRISM (prism/, dont PRISM-LDK) n'est pas couvert par cette licence et reste soumis aux conditions d'Epic Games.LICENSEcontient le texte GPLv3,NOTICEporte le copyright, le périmètre et la déclaration de licence, etTHIRD-PARTY-NOTICES.mdrecense les composants tiers (dont Unreal Engine, les fichiers modèles Epic Games et les polices OFL). Les métadonnées (CITATION.cff,package.json,pyproject.toml, projets .NET) et les pages du site sont alignées ; les contributions sont reçues sous la même licence et les fichiers source couverts portent un en-tête SPDXGPL-3.0-or-later. - ECHOS - schéma SQLite v7 (migration
decision_traces) :decision_traces.utilitydevient nullable (BL-09 / S-28 / D-03 / B-10) ; ajout des colonnesheld_utility,decision_state,decision_tick. Une absence (decisionState = none) ne porte plus de0.0substitué — un zéro observé et une absence de mesure ne se confondent plus. Migration par reconstruction de table, lignes legacy conservées avecdecision_state = NULL.SCHEMA_VERSIONbumpé"6"→"7". Parcours unitaire et batch (consume) alignés sur le même contrat nullable. - SYNE / Launcher / Console - refactor en classes partielles : découpage mécanique (aucun changement de comportement) de
CognitionPipeline,SimulationLoop,GroupSystem,ActionExecutor,KnowledgeBuilder,SimulationOptionsValidator,ControlServer,SimulationControlleretAnalysisWindowViewModelen fichiers partiels par responsabilité (méthodes préservées, comptes vérifiés). Aucune régression : 604 Core + 91 Console + 174 Launcher verts.
Changed
- Site vitrine - SYNE sans visuel de capture, schéma conceptuel à la place : SYNE est un moteur, sans interface à capturer ; les emplacements
.phde la page SYNE et la capture du Launcher affichée à sa place sur l'accueil sont remplacés parSyneSchema.astro(SVG en ligne, libellés localisés, adapté aux deux thèmes), légendée comme « illustration conceptuelle, pas une capture ». ECHOS et PRISM gardent leurs captures réelles. Les animations GIF inutiles (site et dépôt) sont supprimées et leurs mentions retirées : les animations éventuelles restent dans le dépôt et sont servies en WebP/MP4. - Site vitrine - refonte UX, SEO et accessibilité : en-tête et pied de page refondus (5 entrées de navigation, sous-menus cliquables, menu mobile accessible), recherche Pagefind, sommaires d'articles, fil d'Ariane et JSON-LD, page 404 bilingue, icônes PWA. Les chiffres publiés (versions des composants, état CI, tests, couverture) proviennent désormais de
site/src/data/au lieu d'être écrits en dur dans les pages ; la documentationdocs/website/est alignée sur l'implémentation réelle (revue du 21 octobre 2026, rapportdocs/website/REVIEW-2026-10-21.md). - Gouvernance - passage de la licence MIT à la GNU GPL v3.0 ou ultérieure : les composants SYNE, ECHOS, Launcher, syne-mock et site vitrine sont désormais distribués sous
GPL-3.0-or-later(copyleft). Le projet Unreal PRISM (prism/, dont PRISM-LDK) n'est pas couvert par cette licence et reste soumis aux conditions d'Epic Games.LICENSEcontient le texte GPLv3,NOTICEporte le copyright, le périmètre et la déclaration de licence, etTHIRD-PARTY-NOTICES.mdrecense les composants tiers (dont Unreal Engine, les fichiers modèles Epic Games et les polices OFL). Les métadonnées (CITATION.cff,package.json,pyproject.toml, projets .NET) et les pages du site sont alignées ; les contributions sont reçues sous la même licence et les fichiers source couverts portent un en-tête SPDXGPL-3.0-or-later. - ECHOS - schéma SQLite v7 (migration
decision_traces) :decision_traces.utilitydevient nullable (BL-09 / S-28 / D-03 / B-10) ; ajout des colonnesheld_utility,decision_state,decision_tick. Une absence (decisionState = none) ne porte plus de0.0substitué — un zéro observé et une absence de mesure ne se confondent plus. Migration par reconstruction de table, lignes legacy conservées avecdecision_state = NULL.SCHEMA_VERSIONbumpé"6"→"7". Parcours unitaire et batch (consume) alignés sur le même contrat nullable. - SYNE / Launcher / Console - refactor en classes partielles : découpage mécanique (aucun changement de comportement) de
CognitionPipeline,SimulationLoop,GroupSystem,ActionExecutor,KnowledgeBuilder,SimulationOptionsValidator,ControlServer,SimulationControlleretAnalysisWindowViewModelen fichiers partiels par responsabilité (méthodes préservées, comptes vérifiés). Aucune régression : 604 Core + 91 Console + 174 Launcher verts.
Added
- Site vitrine - recherche, pages d'état et CI performance : index de recherche Pagefind produit au build (
postbuild- 79 pages indexées, 2 langues) et recherche modale accessible (ouverture au clavierCtrl/Cmd+K, piège de focus,Échap, restitution du focus) ; nouvelles pagesgetting-started/(démarrage en 5 minutes),etat-du-projet/(relevé factuel daté),monographie/(portail des chapitres) eta-propos/auteur/, en français et en anglais ; sommaire automatique des articles ; workflow GitHub Actionslighthouse.ymlavecsite/lighthouserc.json(performance ≥ 0,95 en erreur, accessibilité/bonnes pratiques/SEO ≥ 0,95 en avertissement) et scriptnpm run check:links(liens internes du build). - Vérification
syne-mock↔ SYNE — harnais de parité de la description du monde :scripts/parity-mock-syne-world.jslance le moteur réel en supervisé (profilprism, surcoucheworld.obstacles/world.obstacleLayout) et le mock en mémoire dans la même géométrie, capture la trameworld_initializeddes deux sources et compare le jeu de clés, l'en-tête,cells[]champ par champ etobstacles[]; sortie en code 1 à la première divergence, et refus de conclure si le layout ne bloque aucune case. Toute affirmation d'alignement du mock devient vérifiable au lieu d'être relue :docs/prism/BLUEPRINT_ROADMAP.md§13 porte désormais la ligne correspondante, etsyne-mock/docs/CONTRACT_REFERENCE.mdla cite à la place de son ancien tableau de terrains. Résultat de référence (15/10/2026) : 4 900 cases identiques, 11 bloquées, 0 divergence. - SYNE/LAUNCHER — scénario de campagne
prismde bout en bout (complément ADR-017) :--simulationaccepteprismen plus dereference; le profil s'applique sous la surcouche du Launcher (Program.ResolveOptions, priorité §5) et le mode supervisé transporte la configuration résolue (lepreparerejoue désormais le profil complet, pas le seul fichier de surcouche) ; côté Launcher, le champ « Scénario » accepteprism(WellKnownSimulations.Prism, message de refus et validationCanCreateCampaignalignés). Parcours vérifié par un batch supervisé réel avec les arguments exacts du Launcher (world_initialized1.1 portantsimulatedSecondsPerTick: 5/metersPerUnit: 1+ snapshotstick × 5 s) et par un run CLI--simulation prism(provenanceresult.json : "simulation": "prism"). Suites : SYNE 634 verts (548 Core + 86 Console), Launcher 183 verts (163 unit + 20 intégration), 0 avertissement. - SYNE/PRISM — ADR-017, échelle temporelle configurable (spec « Profil gameplay PRISM ») : nouvelle clé
simulation.simulatedSecondsPerTick(entier [1, 3600], défaut 60 — 1 tick = 1 minute simulée, comportement historique bit-à-bit inchangé) + clé informativeworld.metersPerUnit(> 0, défaut 1,0) ; horloge instanciéeSimulationClock(remplace la constante statiqueSimulationTime.TicksPerSimulationMinute), conversion A/B/C appliquée une fois au démarrage (TemporalScale: taux ×dt, décroissances^ dt, périodes/ dt; cadences et effets par action inchangés) ; profilprism(SimulationProfiles.Prism()+configs/simulation/prism.json: monde 2 240 × 2 240, cellule 32, 6 TPS, 5 s/tick, R = 30). Contrats additifs (MINOR) : observabilité 0.3.0 → 0.4.0 (simulatedTimeSecondsdans le snapshot, minutes entières conservées), descriptionworld_initialized1.0 → 1.1 (simulatedSecondsPerTick,metersPerUnit),/api/control/status+simulatedSecondsPerTick. Inventaire complet des quantités par tick (classes A/B/C, preuves fichier:ligne) :docs/syne/TICK_QUANTITIES.md(PR 0) ; ADR-017, ADR-005 précisée, CONFIGURATION.md §6.12, API_CONTRACTS.md et TRANSPORT_API.md mis à jour.syne-mockaligné sur le contrat (tempstick × simulatedSecondsPerTick, plancher des minutes, version description 1.1, profil prism 4 900 cases). Non-régression : 626 tests SYNE verts dont les goldens dorés (543 Core + 83 Console), 48 tests mock, ECHOS inchangé (pytest exit 0). - LAUNCHER — deux tests de non-régression des correctifs Windows du Lot W :
AppHostLaunchTestslance réellement l'apphostlivex-launcher(.exe)publié avec une racineLIVEX_HOMEhermétique et exige les huit contrôles de--check(le défaut de manifeste restait invisible car aucune suite ne lançait l'apphost —dotnet testn'invoque que la DLL) ;SessionFileJournalTestsrelit le journal partagé (ReadSharedLines/ReadSharedTextAsync) pendant que l'écrivain le tient ouvert, et prouve qu'un lecteur naïf (File.ReadAllText) échoue bien sous Windows — c'est exactement le comportement qui motivait la lecture partagée. Suite Launcher portée à 214 tests (162 unit + 20 intégration + 32 E2E). - LAUNCHER/SYNE/ECHOS — Lot W : validation Windows de bout en bout (étapes 4 et 5) : machine
DESKTOP-MQQ150J(Windows 11 10.0.26200, dotnet 10.0.401 / SDK .NET 10.0.12, Node 25.9.0, Python 3.12.10, dépôtD:\LIVEX). Résultat : build--warnaserror0 avertissement / 0 erreur, Launcher 162 unit + 15 intégration + 31 E2E = 208 tests, 0 échec, aucun processus orphelin après les trois suites. E2E contre SYNE publiéwin-x64+ ECHOS réel (venv.venv\Scripts\python.exe,echos-launcher.cmd) : campagne → interruption → reprise → analyse → paquet.livexpscellé et relu. Validations ciblées :ManifestDetector(chemins Windows),ProcessTreeKiller(Job Object),ProcessRunExecutor, arrêt authentifié (24/24 tests filtrés),--check/EnvironmentChecker(8 contrôles, code 0 avec SYNE installé viaLIVEX_HOME, code 2 sans composant) et lancement natif delivex-launcher.exe. Constat CI fait le 08/10/2026 : PR #527 versdevelop, 13/13 jobs verts (Linux +windows-latest, run37843436072). Les 4 jobs Windows — jamais exécutés avant, le workflow ne se déclenchant que sur PR versdevelop/main— ont révélé et fait corriger quatre défauts : un glob shell non étendu par PowerShell (npm test), un saut dynamique xunit non converti en « ignoré », un verrou mortelCloseAsync/ReceiveAsyncdans le serveur d'observabilité SYNE et un plancher de débit mesuré pendant que les deux assemblées de tests tournaient en parallèle. - LAUNCHER/SYNE/ECHOS — support Windows, rédaction Linux (étape 5) : les trois manifestes déclarent désormais la clé
executable.windows—syne/component.jsonvers la publicationwin-x64(…/win-x64/publish/Simulation.Console.exe, produite en CI pardotnet publish -r win-x64),echos/component.jsonvers le nouveauechos/echos-launcher.cmd(symétrique duechos-launcherPOSIX : venv prioritaire,pythonen repli),syne-mock/component.jsonverssrc/cli.js. Validation :validate_manifests.pyimpose la formepathou les deux cléslinux+windows(job « Validation des manifests »). Portabilité Launcher :ProcessManagerexécute les.cmd/.batparcmd.exe /c(CreateProcess ne lance que des.exe),PublishedSyneInstallerlit la clé d'exécutable de la plateforme courante, le banc J3 accepte Windows (venv.venv\Scripts\python.exe, copies des deux lanceurs, liens de répertoire en junction), EarlyExit fabrique sonexit-now.cmd. CI : 4 nouveaux jobswindows-latest—syne-dotnet-windows(build + tests),echos-python-windows(lint + pytest),syne-mock-node-windows(npm),launcher-dotnet-windows(unit + intégration + E2E avec SYNE publié win-x64 et ECHOS réel). Vérifié sur Linux : manifestes validés, build--warnaserrorvert, Launcher 162 + 15 + 31 = 208 tests, 0 échec, contrat ECHOS vert, flake8 OK — les jobs Windows et le Lot W (comportements natifs) restent à constater (machine Windows §8). - SYNE/ECHOS — campagne V3 : cadence, backpressure, lag et reprise worker mesurés (étape 6) : outillage rejouable
scripts/v3_campaign.py(SYNE--serveréel + workerechos.dev_ingestréel, run de 30 s en 3 phases de 10 s — nominal /SIGSTOPdu worker /SIGCONT, échantillonnage 1 Hz du tick moteur et du tick max en base) ; deux passes à cadence demandée 100 puis 50 t/s, artefactsdocs/campaign-runs/v3/results-{100,50}tps.json(hors git). Résultats : cadence 76,4 t/s (0,76×) à 100 demandés et 44,4 t/s (0,89×) à 50 ; le gel du consommateur fige la base au tick exact de l'arrêt (plateaux 573 / 460) puis le rattrapage par lots mesure ≈ 155 t/s (retard final 19 ticks à 100 t/s) ; lag médian 2 ticks à 50 t/s nominal contre ≈ 100 (débit soutenu d'écriture ≈ 57 t/s) ; 1 trou à 100 t/s (client retiré) / 0 à 50 t/s — signalés, jamais masqués. Reprise du worker après mort violente du flux automatisée :test_worker_reconnects_after_violent_syne_death(3/3, constat 0,01 s, reconnexion 0,55–0,60 s, second run réingéré). Parcours UI (états vides, erreurs, verrouillages) : 66 tests ViewModellauncher/Launcher.Tests.Unit/Presentation/. Rapport complet :RAPPORT-ELEMENTS-OUVERTS.md§5.3 (document retiré du dépôt) ; étape 6 bouclée. - LAUNCHER — parcours J3 réel de bout en bout (étape 4) :
J3RealComponentsEndToEndTests(2 tests E2E) exécute une campagne contre SYNE publié et ECHOS réel (installation hermétique : manifeste etechos-launchercopiés, code et venv liés,ECHOS_ANALYTICS_DBdédié) — interruption après le run 1, paquetRecoverable, reprise sans rejouer RUN-0001 (entrée identique octet à octet, une seule occurrencerun_completed, journal « jamais rejoué(s) »), analyses individuelles produites par l'API réelle,/api/runsporte les 3 identités composites, rapport archivé octet à octet identique à une régénérationPOST /analysis/report, empreintes vérifiées et frontières du paquet contrôlées (aucune entrée horsruns/RUN-xxxx/). Second test : ECHOS arrêté entre deux runs → le second run termine, les données SYNE restent intactes, l'analyse est marquée « indisponible » et l'absence de rapport est consignée, jamais approximée. NouveauIAnalysisService.AnalysisComponentVersion: la version d'ECHOS qui a produit le rapport est consignée dans le manifeste du paquet (exposée parEchosAnalysisServicedepuis l'instance qui a répondu, enregistrée parCampaignRunner). CI Linux : le job Launcher crée le venv ECHOS (.github/workflows/ci.yml) pour que les tests J3 s'exécutent au lieu de sauter. Suite Launcher : 208 tests (162 unit + 15 intégration + 31 E2E), 0 échec. - SYNE — benchmarks V5 refaits sur la machine de référence V0.1 (étape 7) : machine figée (i7-8750H 6c/12t, 14 Gio, Ubuntu x86-64, .NET 10.0.401 — arbitrage A3), deux passes
--benchmarkreproductibles (checksums FNV-1a identiques, débits à ±3 %). Premières mesures honnêtes à charge complète (les chiffres ph9 mesuraient des populations mourantes) : 50 → 1354–2257 t/s (cible 30 ✓), 500 → 42,8–53,9 (cible 20 ✓), 1000 → 5,9–6,5 (cible 10 ✗, écart ~1,7×) — goulot : passe de perception, puis événements. Écart documenté dansPERFORMANCE.md§9.2, hors blocage V0.1 (scénarios ≤ 100 agents). - SYNE — campagne V2′ par le chemin
reset(étape 2) :POST /api/control/resetaccepte désormais une surcoucheconfig(JSON partiel, même règle questart/prepare, contratAPI_CONTRACTS.md§3 aligné), permettant d'enchaîner des runs de populations différentes par reset ; outillage rejouablescripts/reset_campaign.py(session--serveunique, flux WS segmenté parrunId, critères ADR-016). Résultat du 08/10/2026 : 6/6 ✓ (50 et 100 agents × 2500 ticks × 3 seeds enchaînésstart → reset ×5) — 0 extinction, 0 mort, |pente| ≤ 0,0037/tick, population = initiale, pentes identiques à celles du cheminprepare(ADR-016). Preuves :ControlServerWireTests.Reset_WithConfig_BuildsTheNewRunWithThatSurcouche,RAPPORT-ELEMENTS-OUVERTS.md§5.1 bis (document retiré du dépôt). - SYNE — calibration B1 des défauts intégrés pour la stabilité 2500 ticks (
ADR-016) : les défauts du moteur (ConfigLoader.LoadDefaults/SimulationOptions) re-calibrés pour que le chemin réel des runs (Launcher → overlayagents.initialCount+ défauts) tienne 2500 ticks — c'est le réglage par défaut qui devient viable, pas seulement un profil : réservesfood/water20 000 (régén 20/10) avecdegradationTicknull (l'ancienne dégradation à 100 ticks annulait la régénération en fin de période et figeait la réserve),moveEnergyCost0,05 → 0,03,restEnergyGain1,5 /restFatigueRecovery2,0,catalog.eat/drink.energyRecovery2,0/1,0, dérives socialessocialDriftRate0,001 → 0,0002 etcuriosityDriftRate0,002 → 0,0005 (besoins sans mécanisme de satisfaction : la pente d'énergie chutait de −0,037/tick), communication bornée (maxSendsPerTick/maxReceivesPerTick= 1,relayEnabledfalse) et les 4 coûts énergétiques de messages à 0 (l'envoi coûtait 4× le déplacement).SimulationProfiles.Reference()etconfigs/simulation/reference.jsonrejouent explicitement les mêmes valeurs — un seul jeu à maintenir.engineVersion0.14.0 → 0.15.0 (bump MINOR, trajectoire altérée) ; 4 checksums dorés re-pinnés (perception0xf4aaa2733e491935, état complet0x954b8e0e70c0af5f— DETERMINISM.md §7) ; 27 tests unitaires adaptés. Validation : campagne défauts purs 50 et 100 agents × 2500 ticks × 3 seeds (12345/424242/999) — 6/6 runs : 0 extinction, 0 mort, |pente| ≤ 0,0037/tick < 0,005, population finale = initiale (contre extinction à t303 avant) ; outil rejouablescripts/calibration_campaign.py. Docs : ADR-016, CONFIGURATION.md §B1, PERFORMANCE.md (note), INSTALLATION.md, VERSIONING.md. - LAUNCHER — réalisation des jalons G5 → G7 (contre le banc de stubs ; portes externes P1 – P5 ouvertes) : G5 session complète — service d'analyse HTTP conforme à
INTEGRATION_CONTRACT.md§10.1 (AnalyzeRun/AnalyzeExperiment/GenerateReportsur le port de contrôle, joignabilité instance → manifeste → 5000),Stub.Echosdoté des trois endpoints d'analyse déterministes et d'un journal de demandes prouvant qu'aucune interface web n'est ouverte pendant la campagne ; rapport archivé identique octet à octet à la réponse d'ECHOS puis affiché tel quel, et un analyste arrêté n'empêche ni la campagne suivante ni son scellement. G6--checkcomplet selonPACKAGING.md§6 : les huit vérifications du tableau, causes exactes, sortie textuelle stable — l'installation multi-plateformes et la cible 72 h restent des conditions physiques. G7 déverrouillage Immersion évalué : exigences §11.1 (mode porteur,snapshotStream,renderCadence, contrôle) lues au manifeste PRISM avec cause exacte et jalon affichés (carte PRISM, résolution de profil, vue Configuration) ; arguments de démarrage génériques (args communs §3.1 pour tous, args de lot SYNE réservés au moteur) ;Stub.Prismlivré (endpoints §6 + client WebSocket factice) ; profil sélectionnable dans Configuration (statut dynamique, démarrage dans l'ordre de résolution, arrêt en inverse strict). Supervision câblée : la boucle de sondes fait réellement transiter les états (Démarrage → Prêt, perte de contact après trois sondes,health.path/intervalMs/startupMsdu manifeste honorés) ; stubs en code 3 sur port occupé, ports libérés même en cas d'échec de run. Compatibilité auditée avec les composants réels codés (syne,echos,syne-mock) : écartscomponent.json, args communs et endpoints standard consignés endocs/launcher/ISSUES.md(O-38 à O-41). Bancs : 80 tests verts (63 unitaires, 7 intégration, 10 bout en bout). - LAUNCHER — réalisation des jalons G1 → G4 : première implémentation du Launcher, application .NET 10 / Avalonia dans
launcher/, conformément àdocs/launcheret aux six ADR. G1 : format de paquet.livexp(Launcher.Package) — ZIP64 vivant puis scellé, ordre d'écriture normatif §5.2, déterminisme octet pour octet vérifié par test, contre-mesures de sécurité (traversal, exécutables, zip-bomb, schéma inconnu refusé). G2 : orchestration sans interface (Launcher.Domain+Launcher.Infrastructure) — machine à états et règles d'agrégation, registre de services, résolution de profils avec verrou Immersion évalué, allocation de ports avec pré-vol et priorité au manifeste, gestion de processus (arrêt propre HTTP + jeton, confinement Job Object / groupe de processus Unix), détection par manifestes, journal de session JSON Lines. G3 : campagnes (Launcher.Application) — exécution séquentielle (ADR-005), graines dérivées, politiques d'échec stop/continue/retry/tolerate, reprise exacte parruns/index.json, annulation, scellement. G4 : interface squelette (Launcher.Presentation+Launcher.App) — navigation à trois modes de poids égal, colonne d'état permanente, dialogue du mode verrouillé, CLI--check, garde d'instance unique. Bancs de composants simulésStub.Syne/Stub.Echos(INTEGRATION_CONTRACT §13, pannes injectables). Trois bancs de tests verts : 50 unitaires (dont les 10 propriétés du format, l'analyse statique des frontières d'assembly et la navigation à sept entrées), 7 intégration (processus réels et surface HTTP du Launcher), 3 bout en bout (campagne complète contre le stub détecté par manifeste). Audit de conformité doc↔code bouclé : analyses individuelles par run, journaux d'échec archivés, corrélation argument+environnement,provenance.jsonà la création,run.jsonenrichi, versions des composants au manifeste (EXPERIMENTS.md§12) ; surface HTTP d'observabilité du Launcher (/health/live|ready|details,/info,/metrics,/registry—NETWORK.md§4.1,OBSERVABILITY.md§4) allouée dans la plage interne 5200–5399. Interface réalignée pixel sur la maquetteGUI.md: sidebar à sept entrées, hero débordant, cartes COMPOSANTS avec cycle de vie réel (démarrer/arrêter via/control/shutdown, jeton en variable d'environnement), tableau des expériences depuis les paquets réels, colonne d'état avec ressources échantillonnées (RAM, CPU, stockage) et journal de session ; les trois modes deviennent le sélecteur de type de session de la topbar (réconciliation USER_INTERFACE §2.2). Job CIBuild & tests (Launcher .NET). - Plan campagne-runs — axes A/B/C/D réalisés (J-C1 → J-C4) : réalisation complète du plan de correctifs campagne-runs du 29/09/2026 (décisions D1/D2/D3 ; document exécuté puis retiré du dépôt — résultats consolidés dans
RAPPORT-ELEMENTS-OUVERTS.md§5.1, également retiré du dépôt). Détail par axe ci-dessous (Added/Fixed) ; traces contractuelles dansdocs/syne/API_CONTRACTS.md,docs/echos/API_REST.md,EXPERIMENT_COMPARISON.md,CAUSAL_ANALYSIS.md,CONFIGURATION.mdetADR-015. - SYNE — calibration de survie du scénario de référence (D1,
ADR-015, J-C3) : profil de référence recalé —catalog.eat.energyRecovery = 2.0,catalog.drink.energyRecovery = 1.0(SimulationProfiles.Reference()+configs/simulation/reference.json) et bénéfice Eat/Drink déplafonné (min(need,100) × 0.6, monotone jusqu'à 60) dansUtilityEvaluator.BenefitOf. Motif : mort lente (énergie moyenne 69 → 49 entre t800 et t1200) et Eat/Drink écrasés par Socialize/Explore (utilité 13,9 vs 83,7).engineVersion0.12.0 → 0.13.0 (bump MINOR, altération volontaire de trajectoire) ; checksums dorés re-calés et assumés dans le même commit (perception0x46769cfb11c8b3a7, état complet0xe62395429b50b7c1— DETERMINISM.md §7). - ECHOS — signaux de viabilité observe-only (A3 + B2, J-C2) : rapport de calibration
schemaVersion 2—outcome(extinct/surviving) etextinctionTick(premier tick oùalive_count = 0) + blocviability(energySlopePerTicksur les 200 derniers ticks,actionSharesWhenHungryquandmean_hunger > 70,resourceRegimefood/water). Le résultat de population est aussi calculé en SQL (AnalyticsStore.population_outcome) et exposé par/api/runset/api/runs/{id}(outcome,extinction_tick) — l'observateur drappe « run fini avec écosystème mort » sans recalcul. Colonnestick_summaries.mean_food/mean_water(schéma SQLite v6, migration additive, régime vide sur lignes legacy). - ECHOS —
/api/compare?light=1(C3, J-C4) : résumé seul (seed/version/score/diffs), sansseriesni calcul d'empreinte bit-à-bit —bit_identical/is_reproduciblevalentnull(le verdict exige l'empreinte). L'UI l'utilise par défaut ; l'appel complet reste la référence pour la validation. - ECHOS — cadence du contexte
agents(C2, J-C4) : contexte persisté 1 tick surcontext_every(défaut 20, envECHOS_CONTEXT_EVERY, min 1 = comportement historique) plus le dernier tick du flux — ~208 → ~12 Ko/tick de contexte, dividende direct pour/compare. Les contextesgroups/phenomena/profilingsuiventanalysis_every(inchangé) ;latest_context/context_before//beliefs//relationshipslisent « le plus récent disponible ». - ECHOS — détection des trous de ticks (D3, J-C4) : le worker journalise tout discontinuité (dernier tick vu + 1 ≠ tick reçu — reconnexion, reset inter-run) via une ligne structurée et le compteur
ConsumeResult.gaps_detected, imprimé pardev_ingest. Le rattrapage (replay SYNE) reste hors périmètre V0.1 — signalé, pas masqué. - UI — badge d'extinction (D4, J-C4) : la liste des runs (
ControlScreen) consommeoutcome/extinction_ticket affiche « éteint à tN » (ou « vivant ») sans recalcul côté client. - SYNE — champ
seeddans le snapshot (A1, J-C1) : contrat d'observabilité 0.2.0 → 0.2.1 (champ additif) — ECHOS n'a plus à dériver le seed durun_id(dérivation qui produisaitseed: ""sur les 3 runs pilotés et invalidaitsame_seeddans/api/compare). Paritésyne-mock(le mock émetseeddans son snapshot). - ECHOS — application de bureau (Electron) : shell
echos/echos-desktop/(ADR-003) packagé en.deb(Linux) et NSIS.exe(Windows). Backend Python empaqueté par PyInstaller (onedir) lancé comme processus enfant, interfaceechos-uiservie par FastAPI sur la même origine (aucun CORS), attente de/healthavant ouverture de la fenêtre. Workflow CIechos-desktop.yml(matriceubuntu/windows, artefacts attachés sur tagechos-v*). Voirdocs/echos/adr/ADR-003-shell-electron-desktop.md. - Portail documentaire
docs/README.md: sépare les documents de référence courants des archives (prototype, Monographie), et pose la hiérarchie des sources de vérité — en cas de divergence, la documentation détaillée et l'implémentation testée du composant priment. - Jalon U8 — Constructions = obstacles statiques (SYNE-071) :
world.obstaclesréactivé (défautfalse) + layout initialworld.obstacleLayout[]({id, x, y, radius}) posé au build des mondes (CLI + serveur de contrôle) ;- mutation dynamique validée :
AddObstacleborné/id unique avec révision, constructions tracéesPlaceConstruction/RemoveConstruction(modification d'environnement) ; - grille A* re-rasterisable :
AStarPathfinder.Refresh()purge le cache LRU et re-rasterise si la révision du monde a changé (no-op sinon), câblé en tête du déplacement déterministe ; - traçabilité : événements
world.construction_placed/world.construction_removed+ champobstacles[]dans le snapshot (API_CONTRACTS §2) ; bornesworld.obstacleLayout[]validées ; engineVersion0.7.0 → 0.8.0 ; checksums dorés inchangés (0 tirage PRNG, Refresh no-op sur le scénario de référence) ;- tests : +20 (8 World, 4 A*, 2 ConfigLoader, 3 Validator, 3 ObservabilitySensor, 2 Console end-to-end) — 345 Core + 17 Console = 362 tests au total.
- Jalon U8 — Cycle des ressources (SYNE-070) :
- 4ᵉ type de réserve
Mineral(ResourceKind.Mineral, défaut 0) — initialisation enum-driven, exposé dans l'observabilitéresources[](4 types) et persisté SQLite (resources/resource_snapshots) ; - régénération & dégradation périodique :
ResourceStocks.ApplyLifecycle(tick, settings)appliquée en fin de tick (ordre causal strict, 0 tirage PRNG) —+ regenerationRate/tick, dégradation− regenerationRate × degradationTickà chaque période, clamp ≥ 0 ; - bornes
resources.*.{initial,regenerationRate,degradationTick}ajoutées à la validation (CONFIGURATION.md §6.7) ; engineVersion0.6.0 → 0.7.0 ; checksums dorés inchangés (pin contractuel, DETERMINISM.md §7) ;- tests : +7 Core (cycle, minéral, validation, persistance 4 réserves) — 327 Core + 15 Console = 342 tests au total.
- 4ᵉ type de réserve
- Jalon U8 — Persistance SQLite bit-à-bit (SYNE-110/111/112) :
SimulationSnapshot/SimulationSnapshotCodec: capture déterministe (JSONSystem.Text.Json) de l'état complet du monde + cognition + PRNG 4×64, avec hash stable ;SimulationSnapshotRestorer: reprise exacte au tick N sans re-jouage (kill states dérivés reconstruits déterministiquement, holdoverLastDecision+ réserves restaurées en place) ;SqlitePersistenceStore: schéma V2.0 des 11 tables (PERSISTENCE.md §3,PRAGMA user_version = 2), transactions atomiques, rotationmaxBackups(défaut 5), reprise au derniertick_states(SYNE-112 post-crash) ;- Hook d'autosave câblé dans
SimulationLoop.AdvanceOneTick(autoSaveEveryNTicks, défaut 1000) — purement en écriture, aucun tirage du PRNG ⇒ checksums épinglés inchangés ; - 5
PersistenceTests(315 → 320 tests Core, 329 au total avec Console : reprise bit-à-bit identique au run ininterrompu, état RNG, reprise post-crash, schéma 11 tables, rotation).
- Jalon U8 — Serveur de contrôle HTTP :5181 (SYNE-113) :
ControlServer(HttpListenerBCL, zéro dépendance ADR-002/003) +SimulationController(machine à étatsidle→running⇋paused→finished, gateManualResetEventSlim+CancellationTokenpar run) dansSimulation.Console/Control/, activés par--serve(--serve-port, défaut 5181) ;- routes
POST /api/control/start {seed?, config?}/pause/resume/reset {seed?, runId?}+GET /status(contrat API_CONTRACTS §3) — réponses{ok, action, runId, state, tick, aliveCount, seed}; - non-intrusif (SYNE-081/DETERMINISM §3) : 0 tirage PRNG ajouté ⇒ run piloté == run ininterrompu, vérifié bit-à-bit ; finalise l'interop ECHOS-085 (testé contre le vrai
ControlClientECHOS) ; - 6
ControlServerWireTests(320 Core + 15 Console = 335 tests au total).
- Job CI
Intégration mock → ECHOS(ci.yml) :echos/echos/tests/test_syne_mock_integration.pylancesyne-mock(Node) et vérifie le trajet completControlClient/WsClient→storage.consume(SQLite + Parquet) → API REST, sur toute PR touchantsyne-mock/ouechos/(gateLIVEX_MOCK_E2E=1). C'est le pendant du jobIntégration U8 (SYNE → ECHOS), qui reste réservé au moteur .NET : ECHOS devient donc testable sur chaque PR sans build .NET. - Job CI
Tests (SYNE-MOCK Node)(ci.yml) :syne-mock/rejoint la détection de composants modifiés (sortiesyne_mock) et la suitenpm tests'exécute sur le runner à chaque PR touchant le composant, avectimeout-minutes: 5en garde-fou contre une régression de type fuite de descripteurs.docs/CI_CD.md§2 annonçait déjà ce job. - ECHOS — provenance des métriques (
measured) :analysis.provenance(snapshot)distingue, par couple (moteur, métrique), une valeur réellement mesurée du repli neutre du moteur faute de fenêtre. Persisté par tick (tick_metrics.measured, schéma SQLite v5, migration additive) et publié parGET /api/runs/{id}/metricsetGET /api/runs/{id}. Sans ce signal, 7 métriques pouvaient rester à 0 sur tout run réel sans que l'interface puisse le voir — c'est ce qui avait permis leur passage inaperçu. Contrat dansdocs/echos/API_REST.md§3.2-§3.3 etMETRICS_SPEC.md(annexe). Évol adhésion MINOR : champ ajouté, aucun consommateur existant n'est cassé. echos/ECHOS-REVIEW.md: rapport de revue du composant (584 lignes) — bugs critiques, moteurs, analyse causale, API, robustesse, tests, interfaceechos-ui, et arbitrage explicite des points restés ouverts (CORS = décision de déploiement,StarletteDeprecationWarning= montée de FastAPI, normalisation/100= à documenter).
Changed
-
SYNE — la phrase qui laissait croire que les obstacles étaient générés à partir de la seed :
docs/syne/API_CONTRACTS.md(§2, description du monde) écrite « Le layout est déterministe pour un seed », qu'un lecteur comprend légitimement comme « le relief est généré ou varie avec la seed » — alors que SYNE-071 pose le layout déclaré sans un seul tirage de PRNG, queworld.obstacleLayout[]est la seule source des obstacles, et quesyne-mockrefuse même la cléworld.proceduralObstaclespour ne pas publier un monde que le moteur ne produit pas. Reformulé en « indépendant de la seed : deux seeds sur la même configuration donnent le même relief — la seed ne fait varier que les positions initiales des agents, les marqueurs de ressources et la dynamique », avec l'avertissement explicite qu'un changement de seed ne régénère pas les obstacles.docs/syne/CONFIGURATION.md§6.8 dit désormais la même chose au même endroit que les clés : layout déclaré, pas généré, et une variation par seed exigerait une clé de configuration et un générateur qui n'existent pas en V0.1 (donc une décision à prendre, pas un paramètre oublié). Aucun code touché : la doctrine du moteur est inchangée. -
ci.ymlse déclenche sur les PR visantmainautant quedevelop. Sans cela, aucune PR de release ou de hotfix n'aurait produit le moindre check, et la protection « branche à jour + CI verte » demainresterait bloquée indéfiniment en attendant un status qui n'arrive jamais. -
Dossier de documentation PRISM réécrit pour le projet Unreal final et son plugin PRISM-LDK :
world_initialized, snapshot global par tick, deltas/événements et cycle de contrôleprepare/ready/start/pause/resume/stop/resetdeviennent la référence du composant ; les instructions d'implémentation Godot sont remplacées par le périmètre réel du plugin, et les spécifications visuelles sont recadrées en objectifs de présentation (elles ne décrivent pas des fonctions déjà livrées). -
Décision d'architecture actée : PRISM est le projet Unreal final de LIVEX et intègre le plugin Unreal PRISM-LDK (LIVEX Development Kit, module technique
PrismLdk). L'ADR-002 formalise ce choix et supersède l'ADR-001 (prototype Godot), désormais conservée comme décision historique. SYNE reste le seul moteur décisionnel et l'autorité de l'état simulé ;prism/LDK/LDK.uprojectest un hôte technique de développement/build du plugin, pas un second produit. -
ADR-003 (API HTTP) et ADR-004 (WebSocket temps réel) précisent désormais que la liste des routes et des formats qu'elles portaient est celle de la proposition d'origine de la Monographie, et renvoient vers
docs/syne/API_CONTRACTS.mdetdocs/COMMUNICATION.mdpour le contrat courant (diffusion multi-consommateur, cycleprepare/ready/start/pause/resume/stop/reset). -
Documentation transverse réalignée sur PRISM, projet Unreal final de LIVEX :
README.md,docs/ARCHITECTURE.md(§2.4syne-mock, §6 arborescence),docs/presentation/VISION.md,docs/governance/ROADMAP.md,docs/presentation/FAQ.md,docs/INSTALLATION.md,docs/COMMUNICATION.md,SECURITY.md,docs/CI_CD.md,CONTRIBUTING.mdet la Monographie. Les références à Godot et àgodot-renderer/ne décrivent plus l'implémentation actuelle ;docs/ARCHITECTURE.mddevient la source de vérité pour les responsabilités, le plugin PRISM, LDK et les projets Unreal. -
Portée de
syne-mockcadrée explicitement comme outil de développement des contrats Unreal, pas un moteur de simulation :README.md,docs/ARCHITECTURE.md§2.4,CONTRIBUTING.md§4 (checklistnpm test --prefix syne-mock),docs/CI_CD.md§2 et §5,docs/presentation/ETHICS_AND_SCOPE.md§4.1 etdocs/prism. Ses résultats ne doivent pas être présentés comme ceux de SYNE. -
docs/echos/{ISSUES,ROADMAP,TESTING}.md: la validationECHOS-091ne couvre plus « le runtime PRISM qui commence après U8 » mais est explicitement restreinte à SYNE→ECHOS. La validation du plugin PRISM-LDK et du projet Unreal PRISM relève d'un chantier distinct. -
docs/docs_prototype/README.mdporte désormais un bandeau d'archive pointant versdocs-prism/etdocs/README.md: les références à Godot qu'il contient sont historiques. [répertoiredocs/docs_prototype/retiré depuis par la revue documentaire V0.1, PR #502] -
CONTRIBUTING.md: ajout de la validation de compilation du plugin PRISM-LDK dans l'hôte Unreal de développement et dans le projet PRISM final.
Fixed
-
Site vitrine - le partage e-mail des articles était retiré : le bouton « E-mail » n'ouvrait aucune adresse (il lançait seulement le client de messagerie du visiteur avec le lien) — il est supprimé ; la copie du lien, X/Twitter, LinkedIn et la feuille de partage native suffisent. Aucun
mailto:ne reste publié sur le site. -
Site vitrine - débordement horizontal du menu mobile et contact direct par formulaire : le bouton « Suivre » du menu mobile héritait de
width: 100%et des marges latérales, débordant de 40 px et créant un défilement horizontal dans le panneau ; il est désormais borné à la largeur du menu. Les pages auteur et mentions légales ne publient plus d'adresse e-mail : le contact passe par le formulaire du portfolio (donovan-dev-web.vercel.app/contact). -
Site vitrine - entités HTML affichées en clair et chaînes françaises en pages anglaises : les attributs Astro (
title,text,cta) ne décodent pas les entités HTML -éet→y étaient ré-échappés en&eacute;et s'affichaient littéralement (le bloc « Pourquoi LIVEX » de l'accueil affichait « Déterminisme » et « Voir les mesures → »). Toutes les entités hors<code>/<pre>sont désormais écrites en UTF-8 direct ; les pages anglaises utilisent les chaînes localisées (PROJECT_STATE_EN,RELEASE_EN) au lieu des chaînes françaises (pourcentages, état CI, support Windows, date de relevé) ; les trois pages anglaises créées récemment transmettentlang="en"àPageHeaderetCardLink(fil d'Ariane, libellés d'accessibilité) ; tirets, apostrophes et libellés harmonisés (tiret cadratin, « Portail des chapitres », « Guide de contribution »). -
Site vitrine - défauts de fond corrigés : la page 404 renvoyait un document sans
h1et une URL canonique hors dubase/LIVEX/; les images d'articles étaient servies en PNG brut, sans dimensions ni optimisation ; les tableaux Markdown ne portaient passcope="col"; les articles publiés uniquement en français déclaraient unhreflanganglais vers une page qui n'en est pas la traduction ; le JSON-LDWebSitepointait vers la racine du domaine au lieu dubase; le manifeste PWA figeait/LIVEX/(icônes en 404 sur un build à la racine) ; les liens « Discussions » menaient à des discussions GitHub désactivées (remplacés parSUPPORT.md). Validation : 80 pages construites,astro checksans erreur ni avertissement, 0 lien interne cassé, 207 ancres résolues, 128 URL externes valides, Lighthouse 100/100/100/100 sur six pages. -
SYNE - les marqueurs de ressources tombaient dans les obstacles que la description publiait pourtant comme bloqués :
WorldDescriptionBuilder.BuildResourceLocationspiochait la case du LCG local sans regarder la marchabilité, alors que la passe de couverture juste au-dessus remplissait déjàcells[].walkableetmovementCost: 100— PRISM instanciait une ressource dans un rocher et affichait un marqueur sur une case que le même message annonçait infranchissable. Mesuré contre le moteur réel et le mock, profilprismlivré, seed 1 :resource-food-2sur la case (8, 68), dans la côte sud (cote-sud-1, r = 150) — le moteur publiait 11 des 12 marqueurs identiques au mock, le douzième déplacé. Correctif : la couverture des obstacles est calculée une fois par case et partagée parcells[]et par le placement, qui avance jusqu'à la première case libre et minore le nombre d'emplacements par le nombre de cases marchables (0 case libre ⇒ aucun marqueur, au lieu d'une boucle). Aucun tirage de simulation n'est consommé (LCG local, déjà séparé du Xoshiro) : la trajectoire des ticks est inchangée. Preuves : 4 nouveaux testsWorldDescriptionTests, dont le rejeu du cas mesuré (profilprism, seeds 1 à 8 : la case (8, 68) reste bloquée et ne porte aucun marqueur) ; le harnaisscripts/parity-mock-syne-world.jscompare désormaisresources[]champ par champ au lieu de l'exclure — c'est cette exclusion qui laissait la divergence vivre — et accepte--seedpour rejouer une graine précise ; moteur et mock donnent 12/12 marqueurs identiques aux seeds 1 et 42, exit 0. -
syne-mock — un fichier de configuration introuvable était ignoré en silence :
readConfigrendait{}dès que le chemin n'existait pas, doncnpm start -- config.prism.json(point au lieu du souligné) démarrait le mock sur les défauts — 500 × 500, 50 agents,obstacles: 0— pendant que l'opérateur croyait charger le profilprism: le monde publié ne correspondait ni à sa configuration, ni au moteur, et le journal n'en disait rien (constaté surPREPARE complete (world=500x500 ... agents=50)alors que le fichier annonce 2 240 × 2 240/100 agents). Un chemin demandé est désormais une erreur : message nommant le chemin résolu, code de sortie 1, démarrage refusé ; seul un appel sans chemin garde les défauts. Le message propose en plus le fichier voisin correspondant quand il est unique (séparateurs et casse ignorés), donc l'erreur de frappe se répare d'elle-même —Vouliez-vous dire : …config_prism.json ?. Tests :readConfigd'un chemin absent lève et citeconfig_prism.json, un chemin sans voisin n'invente aucune suggestion,config_prism.jsonannonce bien le monde du moteur (2240/32/6 TPS/5 s/tick, 5 disques, 203 cases bloquées) — 51 tests, 49 verts, 0 échec, 2 skip. -
syne-mock/PRISM — le mock publiait un monde que SYNE ne sait pas produire (0 obstacle, plus de mer/rivières/obstacles procéduraux côté PRISM) : la
WorldDescriptiondu mock divergeait du moteur sur trois points mesurés —world.terrainactif par défaut (seaDepthRatio0,12 + une rivière : 300 casessea, 44river, etmovementCost: Infinitysérialisé ennull), des obstacles à formes (shape/cells) etproceduralObstaclesqueStaticObstacleSettings { Id, X, Y, Radius }n'a jamais connus. Le mock émet désormais exactement la règle deWorldDescriptionBuilder.Build:terrainTypetoujoursplains(le moteur n'a ni biome ni palette de terrains),walkablevrai si et seulement si la case ne porte aucun obstacle,movementCost1 ou 100, obstacles en disques{id, x, y, radius}.world.terrain,world.proceduralObstacleset un obstacle non-disque sont refusés au chargement avec un message explicite, au lieu d'être ignorés — c'est cette indifférence aux clés inconnues qui avait laissé la divergence vivre. Sur le transport,/api/world, la réponse deprepareet la trameworld_initializedpublient la même géométrie (la trame diffusée envoyait auparavant la représentation interne, avectype). Preuve : nouveauscripts/parity-mock-syne-world.js, qui lance le moteur réel (profilprism: 2 240 × 2 240, cellule 32, deux disquesr = 8etr = 40) et le mock sur la même géométrie, puis compare jeu de clés, en-tête,cells[]champ par champ etobstacles[]— 4 900 cases identiques, 11 bloquées de part et d'autre, 0 divergence ; le script sort en code 1 dès la moindre différence et refuse de conclure si le layout ne bloque aucune case (comparer deux mondes vides ne prouverait rien). Côté plugin,ParseWorldne peut plus arrêter le jeu sur une description incomplète :cells/obstacles/resources/regionssont lus enTryGetArrayField(l'ancienGetArrayFielddéclenche uncheckfmoteur quand la clé manque) et un terrain inconnu ou unmovementCostsans valeur émettent un avertissement nommant la première valeur fautive, au lieu d'unUnknownsilencieux ; l'absence d'obstacles, légitime quand le profil n'en déclare aucun (configs/simulation/prism.json:world.obstaclesfaux par défaut), n'est plus un avertissement mais une ligne d'information qui nomme la cause probable. Tests du mock : 47 pass / 0 fail / 2 skip (dont un test de la règle de description — aucunnullsérialisé, aucun terrain horsplains), les 4 tests de la mer et des rivières remplacés par leurs équivalents obstacles. -
LAUNCHER — un run réussi devenait Échoué sur son propre journal (défaut de CI n°5, échec de la reprise J3) :
ProcessRunExecutor.CollectLogFilesetCampaignRunner.CollectFailureLogsAsyncarchivaient les journaux d'un run avecFile.ReadAllBytes, c'est-à-dire pendant que la pompe détachée du moteur (ProcessManager.PumpAsync) peut encore écrirestdout.log/stderr.log. Sous Windows un lecteur ouvert avecFileShare.Readrefuse l'écrivain en cours : l'archivage d'un run pourtant terminé lançaitIOException, le run passait Échoué, puis la collecte d'échec retombait sur le même verrou — l'exception de lecture remplaçait la cause réelle et sortait de la campagne (joblauncher-dotnet-windows,J3RealComponentsEndToEndTestsà l'étape de reprise ; les passages CI précédents de la branche étaient verts, la course ne se joue que sous contention). C'est la règle déjà appliquée au Lot W pour le journal de session (SessionFileJournal.ReadShared*), étendue à l'archivage des journaux de run : nouveauRunLogArchive.Collect(lecture enFileShare.ReadWrite), réutilisé par les deux chemins. Un journal réellement illisible n'échoue plus la collecte : il est consigné danslogs-unreadable.txtavec sa cause — le diagnostic d'un échec ne peut plus être remplacé par l'échec du diagnostic. Preuves :RunLogArchiveTests(3 tests, dont 2 Windows — le lecteur naïf lève bienIOExceptionlà oùCollectréussit, et un journal verrouillé en exclusif est consigné au lieu de tuer la collecte) ; suite Launcher 214 tests verts (162 unit + 20 intégration + 32 E2E dont J3 contre SYNE publiéwin-x64et ECHOS réel), build--warnaserror0/0 ; PR #527 revalidée 13/13 (run37850315626). -
SYNE — verrou mortel du serveur d'observabilité sous Windows (2 tests sur 4 de
ObservabilityServerWireTestsen échec, testhost bloqué à l'infini) :ObservabilityServer.DisposeAsync— et le retrait d'un client après échec d'envoi — lançaitCloseAsyncpendant qu'uneReceiveAsyncdu client était en vol. L'implémentation Windows deSystem.Net.HttpListener(WebSocketBase.TakeLocks/Monitor.Enter) fait partager un verrou aux deux opérations : le fil de fermeture attendait celui de la réception, la réception annulée attendait celui de la fermeture — délai d'attente mutuelle, qu'aucun des deux ne pouvait lever (leCancellationTokenSourcede 2 s n'interrompt pas unMonitor.Enter). Le test restait donc bloqué après ses assertions, au niveau duawait usingdu serveur. Correctif : ordre strict annuler la réception (CTS par session) → attendre la boucleDrainAsync(bornée à 2 s) → fermer ;RemoveClientne ferme plus jamais pendant qu'une réception tourne. Preuves : classe rejouée 10/10 (≈ 2/10 auparavant), suite SYNE 593 tests verts (79 Console + 514 Core), build--warnaserror0/0. Note d'enquête : le « Plantage du processus hôte de test » annoncé par vstest était un artefact dutimeoutd'investigation (exit 143 = SIGTERM), la vraie cause étant le blocage. -
ECHOS/syne-mock — 12 tests qui n'échouaient que sous Windows : (1) ECHOS — 43 lectures/écritures de fichiers sans
encoding=lisaient les fixtures UTF-8 avec l'ANSI cp1252 → 6 comparaisons dorées et instanciations fausses ; désormaisencoding="utf-8"explicite partout (sans effet sur Linux, UTF-8 y étant déjà la locale). (2) ECHOS — 3 contrats d'adaptateur purement POSIX : bit d'exécution deechos-launcher(Windows n'a pas de mode de fichier) et exécution directe du script →WinError 193; l'adaptateur Windowsechos-launcher.cmdest désormais lancé viaCOMSPEC /c(chemin réel qu'emprunte le Launcher, cléexecutable.windows) avec nettoyage d'arbre, et le bit d'exécution n'est contrôlé que sur POSIX. (3) syne-mock — 2 tests de fermeture gracieuseSIGTERM/SIGINTinapplicables (Windows livre les signaux parTerminateProcess:code null, signal 'SIGTERM') → ajournés avec motif, le chemin POSIX restant couvert par Linux ; même garde plateforme sur le bit d'exécution desrc/cli.js. Preuves : ECHOS flake8 0 + pytest 0 échec (dont intégrations U8SYNE → ECHOSetmock → ECHOS), syne-mock 44 pass / 0 fail / 2 skip. -
LAUNCHER —
livex-launcher.exene démarrait jamais sous Windows (« la configuration côté-à-côté est incorrecte ») :launcher/Launcher.App/app.manifestdéclarait<assembly manifestType="dualIdentity">sansmanifestVersion— le chargeur Windows refuse le manifeste (journal SideBySide, événement 11 : « l'élément assembly du manifeste ne contient pas l'attribut manifestVersion correct », puis événement 64 : « l'attribut manifestType n'est pas autorisé dans ce contexte »). Conséquence : l'apphost s'arrêtait au démarrage, quel que soit l'argument, tandis quedotnet livex-launcher.dllfonctionnait — aucune suite ne lançait l'apphost, le défaut était donc invisible en CI. Manifeste réécrit au format canonique (manifestVersion="1.0") ; vérifié en PowerShell :livex-launcher.exe --checks'exécute nativement. -
LAUNCHER — l'export et l'affichage des journaux de session échouaient sous Windows :
OrchestrationFacade.ExportSessionLogsAsyncetRecentLogslisaient le fichier avecFile.ReadAllText/File.ReadLines, c'est-à-dire pendant queSessionFileJournall'a encore ouvert en écriture → violation de partage sur Windows : l'export des journaux tombait en erreur et le panneau « journaux récents » retournait silencieusement la dernière vue (jamais rafraîchie). Lecture partagée mutualisée (SessionFileJournal.ReadSharedLines/ReadSharedTextAsync,FileShare.ReadWrite), réutilisée par le banc J3. -
LAUNCHER — un arrêt forcé tuait tous les composants vivants et retirait le garde-fou anti-orphelin :
ProcessTreeKiller.KillTreeWindowsterminait le Job Object unique partagé par toute la session (TerminateJobObject) puis fermait son handle — le job contient tous les composants assignés (donc collateral), etKILL_ON_JOB_CLOSEne protégeait plus ensuite. Désormais : assignation au démarrage (MakeOwnProcessGroup, symétrique du groupe de processus Unix) et arrêt forcé borné à l'arbre du composant (Kill(entireProcessTree: true)), le job n'étant ni terminé ni fermé avant la sortie du Launcher. -
LAUNCHER — bancs E2E/intégration rendus valides sous Windows :
StubInstall.WaitForHealthyAsyncne interceptait queHttpRequestExceptionalors qu'un port sans écoute ne rendECONNREFUSEDqu'après ≈ 2,05 s sous Windows (retransmission du SYN — « slow TCP connect on Windows », daniel.haxx.se, 14/08/2024), soit après l'expiration des 2 s du client → le démarrage en cours était pris pour un échec ; le manifeste du bancCampaignEndToEndTestsomettait la cléexecutable.windows(« binaire absent ») et ses compositions n'étaient pas libérées (racine non supprimable sous Windows) ;PublishedSyneHarnessne libérait pas son journal ;Sonde_health_ready_repondsondait un stub dont l'horizon (200 ms) était plus court que la latence de refus Windows — horizon porté au-delà du délai de sondage, assertions inchangées. -
SYNE — un client WebSocket lent figeait le moteur de simulation :
SendTextAsynclaissait échapper leTimeoutExceptiondeWaitAsync(SendTimeout)(absent du filtre ducatch) — le client n'était donc jamais retiré et chaque trame rebloquait 2 s : découvert pendant la campagne V3, le moteur tombait à 1,5 t/s au lieu de ~77 dès qu'un consommateur cessait de lire (rétroaction consommateur→moteur, contraire au contrat d'observabilité best-effort).TimeoutExceptionajoutée au filtre ducatch→RemoveClient(fermeture du socket, client abandonné retiré, trame perdue comptée honnêtement comme trou). Test de non-régressionObservabilityServer_ClientThatStopsReading_IsRemovedAndBroadcastKeepsWorking(client qui ne lit plus : la diffusion continue, le client est retiré). Mesures avant/après de la campagne V3 : consommateur gelé 10 s → 71,2 t/s au lieu de 1,5 ; suite SYNE 593/593. -
SYNE — le chemin
resetapplique le profil de référence (étape 1) :POST /api/control/resetrelançait le run surConfigLoader.LoadDefaults()au lieu du profil de référence (ef878f21) — une campagne pilotée longue parresetpouvait redémarrer sur un autre profil queprepare(mesuré le 30/09 : extinction t300 parresetvs survie 1200/1200).PrepareCoreAsyncapplique désormaisSimulationProfiles.ReferenceJson()quand la surcouche est nulle (doctrine ADR-016 : le chemin HTTP ne dépend jamais d'un défaut qu'on pourrait oublier de recaler). Test de non-régressionReset_WithoutConfig_BuildsTheSameReferenceProfileAsPrepare; SYNE vert (590 tests), oracles inchangés,engineVersionconservée. -
LAUNCHER — impossible d'archiver une donnée de run au-delà de 2 Gio (« The file is too long ») : trois bornes
byte[]/MemoryStream(2 147 483 647 o) bloquaient la chaîne d'archivage d'un vraistream.jsonlde campagne (mesuré : 2 337 158 145 o pour 2500 ticks × 50 agents, soit ~4,7 Gio à 100 agents) — (1)ProcessRunExecutor.CollectFileslisait chaque fichier avecFile.ReadAllBytes→ le run échouait après exécution, le manifeste restait sansrunIdset ECHOS refusait le rapport (422) ; (2)LivexPackageWriter.CompleteRunbufferisait chaque entrée dans leMemoryStreamdu mode Update deZipArchive; (3)Sealreplaçait toutes les entrées dans unSortedDictionary<string, byte[]>. Correction de bout en bout : nouveau typeRunDataFile(source fichier ou octets, jamais de lecture en mémoire),CompleteRunréécrit l'archive en mode Create (compression directe dans le fichier, échange atomique via temporaire, ordre normatif §5.2 et déterminisme inchangés), hachage d'intégrité calculé en flux pendant la copie,Sealrelit en mode Read (flux) et réécrit en mode Create,VerifyRunIntegrityhache en flux,ReadEntrydocumenté réservé aux entrées courtes. Plafond d'entrée du lecteur relevé de 4 à 16 Gio (PACKAGE_FORMAT §9) — les runs 100 agents × 2500 ticks le dépasseraient, le ratio zip-bomb 100:1 reste. Rapport de campagne : le compteur viderunIdsest maintenant détecté côté Launcher (avertissement explicite au lieu d'un 422 opaque). Preuve : test de régression > 2 Gio (archive + scellement + vérification) + archivage du vraistream.jsonlde 2,34 Gio ; 206 tests Launcher verts (15 intégration, 162 unitaires, 29 bout en bout dont campagne SYNE publié réel). -
LAUNCHER — délai d'analyse ECHOS porté à 30 min pour les opérations lourdes : le client HTTP unique plafonnait à 30 s, alors que l'ingestion d'un flux réel de 2500 ticks × 50 agents (2,34 Gio) prend 5 min 30 s mesurées (~11 min à 100 agents) — l'ingestion était systématiquement coupée,
analysis/experimenttrouvait « run inconnu » et le rapport de campagne n'arrivait jamais. Le délai est désormais posé par requête : 30 s pour les appels ordinaires (santé, état, 5 s pour l'ingestion live), 30 min pouringest/run,analysis/run,analysis/experimentetanalysis/report(EXPERIMENTS.md§11). Décliné avec la mesure dansdocs/launcher/EXPERIMENTS.md§11. -
SYNE — rejet 413 : le client recevait « broken pipe » au lieu de la réponse : sur un
Content-Lengthdéclaré > 1 Mio,ControlServerrépondait 413 et fermait la socket pendant que le client écrivait encore son corps — la fermeture avec des octets non lus émet un RST et l'expéditeur (HttpRequestException) ne voyait jamais le 413 (course perdue sous charge parallèle, testOversizedBody_IsRejected_With413_AndNotBufferedéchouait en 2 runs sur 3). Vidage borné (16 Mio max, tampon jetable 8 Kio, corps jamais accumulé — la garantie « not buffered » tient) avant d'écrire la réponse ; le 413 part de toute façon si le vidage échoue. -
SYNE —
configs/simulation/reference.jsonn'appliquait pas la calibration D1 : le fichier portaitagents.actions.catalog.eat.energyRecoveryau lieu deagents.actions.catalog.entries.eat.energyRecovery—System.Text.Jsonignore les propriétés non mappées, donc toute exécution--config configs/simulation/reference.json(dontscripts/batch_analysis.py) tournait aveceat/drink.energyRecovery = 0(mesuré :energyDeltamoyen d'Eat = −0,2 au lieu de +1,8), sans aucun avertissement. Cheminentriescorrigé ; le fichier rejoue désormais explicitement les valeurs B1. -
SYNE —
TickBudgetSnapshot.ComputationShare()mélangeait des unités : elle sommaitMeanMs(phase)(temps par échantillon) alors que les phases intra-entité sont échantillonnées par entité × tick et les phases réseau/monde par tick — la part computationnelle affichée revenait à (communication + événements) / tick et sous-estimait les phases intra-entité d'un facteur ≈ population. Corrigé en temps par tick (_totalMs / PipelineSamples, ADR-016) ; le seuil ≥ 30 % (PERFORMANCE.md §9) reste tenu. -
Contrat SYNE↔ECHOS — le run est enregistré avant ses ticks (A2, J-C1) :
consume()ne posaitrecord_runqu'au premier snapshot du flux (run_knownjamais réinitialisé) — lors d'unresetSYNE, les ticks du nouveaurun_idlevaientFOREIGN KEY constraint failed(ticks 1–365 et 1–106 perdus en campagne, rapport de calibration du run interrompu jamais écrit). Le pipeline détecte désormais le changement derun_iden flux : nouveau run enregistré avant son premier tick, fenêtres glissantes (_RollingContext) réinitialisées (elles contaminaient les métriques du run suivant), série Parquet flushée, et rapport de calibration bâti à la fin de chaque run — plus seulement pour le dernierrun_idvu. -
Contrat SYNE↔ECHOS — format canonique du
run_idpiloté (A1, J-C1) :SimulationController.StartAsyncgénérait unrun_idaléatoire sans seed (les runs pilotés perdaient leur seed côté ECHOS) alors que le monde préparé utilisaitrun-<seed>(collisions entre runs successifs de même seed). Format uniquerun-<seed>-<12hex>: seed lisible (repli de dérivation ECHOS sur les flux ≤ 0.2.0), unicité garantie. -
SYNE —
PrintSummary: ligne « (tête) tick 1 » avec le compteur final (D1, J-C4) : le résumé de fin de run affichait le nombre d'entités final sur la ligne de tête (« tick 1 … 42 entités » après une extinction). Le compte initial est capturé avantloop.Runet passé àPrintSummary; la ligne de queue reste le compteur courant. -
SYNE — erreur de
startorientée après un runfinished(D2, J-C4) : unstartsur un run terminé naturellement répondait409 world_not_ready(orientant versprepareau lieu dereset). Nouveau code409 run_finishedavec le détail « run terminé — appelez /api/control/reset avant de redémarrer » (API_CONTRACTS.md§3 mis à jour, wire test). -
ECHOS — performance des routes de lecture (C1, J-C4) :
/api/runs/{id}et/api/emergent-phenomenachargeaient tous les contextes (contexts(), dontagents≈ 208 Ko/tick) pour ne consommer quephenomena— timeouts > 20 s et OOM-kills en campagne. Lecture cibléeobservations_for(run_id, "phenomena")(mesuré 0,28 s sur la campagne). -
ECHOS — revue et refactor du composant (
echos/ECHOS-REVIEW.md) : deux bugs qui rendaient des fonctions centrales inopérantes —/api/comparerenvoyait 500 sur tout run contenant un événement (la comparaison de reproductibilité n'était donc accessible sur aucun run réel), etAnalyticsStore.runs()plantait sur un run sans tick, ce qui rendait l'API entière inopérante sur une base fraîche ou un run en cours d'acquisition. Sept moteurs refondus : 7 métriques à 0 sur tout run réel (history/communityHistoryjamais produits par le pipeline), dénominateur des taux de groupe dépendant de la charge (facteur 50 entre un run calme et un tick chargé), singletons comptés comme communautés,DecisionDiversitycalculée sur les objectifs,SystemComplexitynon borné,_availability(100, None, 0)renvoyant 1e11, cycle d'importemergence. Robustesse : cadences nulles, fuite de descripteurs, reconnexion à 100 ms,pyarrowimporté mais non déclaré (pip install echosproduisait un paquet qui ne démarrait pas). Identifiants de groupe (members,leaderId) alignés sur les identifiants d'agent — entiers contre chaînes, donc jamais jointables. Tests : 233 → 283 (couverture 93 %), flake8 propre. -
ECHOS — interface
echos-ui: l'export « CSV » téléchargeait une enveloppe JSON illisible par tout tableur ;beliefs/relationships/phenomenaappelaient l'API sansrun_idet affichaient donc les données du run le plus récent sous le nom du run sélectionné, sans signal visible ; le cache de métriques ignorait la cadence dans sa clé, fusionnant des séries d'échantillonnages différents et produisant desNaNrendus comme des zéros ; le graphe social relançait une requête/api/relationshipspar membre en série à chaque tick ; la jauge de vitesse de diffusion alertait en rouge un monde qui propageait bien ;disconnect()se reconnectait 2 s plus tard.npm testlance désormaisvitest run(le mode interactif ne termine jamais et accrochait la CI). Tests : 18 → 61 sur 14 fichiers, eslint ettscpropres. -
syne-mock: ordre des trames WebSocket incompatible avec ECHOS. Le mock diffusait les événements du tick avant son snapshot, alors que SYNE (ObservabilityTickEmitter) etAPI_CONTRACTS.md§2 imposent le snapshot en tête. ECHOS rejette ce flux :TickAlignmentError: … événement decision_made reçu avant le premier snapshot(ingestion/stream.aligned_ticks).step()regroupe désormais les événements du tick, diffusesnapshotpuistick_summary, puis les événements de ce tick ; l'ordre des événements entre eux et le tampon de diagnostics sont inchangés,stepReplay()n'est pas touché. Contrat, checklist et README du mock alignés sur l'ordre réel, avec la procédure pour brancher ECHOS sur le mock (SYNE_OBSERVABILITY_URL,SYNE_CONTROL_URL) et ce que le mock ne couvre pas. -
docs/prism/adr/ADR-001-choix-godot.md: la coquille « qui formality ce choix » est corrigée en « qui formalise ce choix ». L'entréeCHANGELOG.mdqui annonce cette correction décrivait en réalité la coquille deCHANGELOG.mdlui-même ; le fichier ADR visé n'avait jamais été corrigé. Saut de ligne final ajouté à cet ADR et àdocs/prism/adr/README.md. -
CHANGELOG.md: la section## [Unreleased]cumulait des sous-sections dupliquées (### Changed×3,### Added×2) et une ligne blanche parasite coupant une liste en deux, ce qui cassait le rendu de la section et la comparaison d'un release. Sections regroupées et ordonnéesAdded→Changed→Fixed→Deprecated, avec correction de la coquille « L'ADR-002 formality ce choix » → « formalise ce choix ». -
Cadrage Jalon U8 (PR cadrage docs) : planche U8 du
ROADMAPracine recalée sur le backlog réel — plage ECHOS080…093fictive remplacée par080…085(ph8, livrés U7) +090…092(ph9) ; -
Ajout de la carte manquante SYNE-113 (serveur de contrôle HTTP :5181, cible réelle du relais
controlClientECHOS — absente du backlog) ; -
SYNE-110précisée : hooksautoSaveEveryNTicks(défaut 1000) /maxBackups(défaut 5) à brancher sur la boucle (aucun consommateur à ce jour) ; -
ECHOS-085 annotée : livrée côté UI, finalisation en U8 contre SYNE-113.
-
PERSISTENCE.md§3 recalé sur le modèle réel V0.1 :agent_snapshotssanshealthfictive (besoins =energy, hunger, thirst, fatigue), implémentation du snapshot bit-à-bit et de la rotation documentée. -
syne-mock:npm testne se terminait plus.createServer().close()arrêtait l'écoute sans libérer les ressources : les clients WebSocket restaient connectés et les connexions HTTP en keep-alive n'étaient pas fermées, ce qui laissait 2 serveurs TCP et 3 sockets ouverts et empêchaitnode --testde rendre la main.close()termine désormais les clients WebSocket, purge les connexions HTTP et chaîne les rappels de fermeture ; les tests libèrent leur serveur viat.after()et attendent la fermeture du client. -
syne-mock: les assertions d'obstacles attendaient un objet{id, x, y, radius}alors queWorldDescriptionpublie aussitype. Les attentes sont alignées sur le contrat réel. -
syne-mock: les agents initiaux pouvaient apparaître sur une case non franchissable.createInitialAgentsne testait que les obstacles et ignorait le relief, donc un agent pouvait se poser sur une cellulesea(walkable: false) ; il contrôlait aussi la position non arrondie alors que le monde publie la position arrondie. Le placement s'appuie désormais surcells[].walkable, l'indicateur queWorldDescriptionexpose lui-même, ce qui traite obstacles et mer par la même règle que celle publiée et supprime la divergence entre le test de placement (isPositionBlocked, point contre cercle) et la grille (isCellBlocked, centre de case). Sur 300 graines × 5 densités × 2 configurations d'obstacles (324 600 positions), 3 452 agents sur case non franchissable avant, 0 après ; la génération reste déterministe pour une graine donnée. Test de non-régression ajouté sur 5 graines. -
docs/CI_CD.md§2.1 : documente les rulesetsmainRulesetdevelopRulesdésormais actifs surmainetdevelop(PR obligatoire, squash seul, CI verte et branche à jour, pas de force-push, pas de suppression), les deux checks exigés et la raison de ce choix, ainsi que le retrait derequired_signatures. -
syne-mock: le déplacement ignorait la carte. Quatre défauts distincts, tous couverts partest/movement.test.js(+10 tests, 12 → 22) :MovementService.moveappelaitisPositionBlockedsanscellSize: la brancheshapecomparait des coordonnéesNaNet laissait traverser les sept formes d'obstacle, alors que la version circulaire bloquait ;canEnterne consultait que les obstacles et ignoraitcells[].walkable: les agents traversaient la mer, et pouvaient y être placés ;- le coût de terrain était ignoré : une rivière (
movementCost: 4) se traversait à la vitesse de la plaine. Le pas est désormais borné par le coût le plus élevé entre case de départ et case visée — entrer dans une rivière ralentit, en sortir aussi — mesuré à 3,83× plus lent qu'en plaine sur 4 318 déplacements en rivière contre 19 974 en plaine ; - un déplacement refusé était facturé :
agent.energybaissait demoveEnergyCostmême quand l'agent n'avait pas bougé, vidant les agents immobiles tout en déclarant une intention qu'ils ne suivaient pas.move()retourne{moved, blocked, destination}; seul un mouvement effectif est facturé, et un blocage est reporté viaoutcome: 'blocked'. cell-grid.jscentralise l'indexation decells[](y * cellCountX + x) partagée par le placement et le déplacement. L'inversionx * cellCountX + y, auto-cohérente sur une carte carrée, faisait chevaucher des clés sur une carte rectangulaire (20×30 :(0,21)et(1,1)donnent la même clé 21) ;- l'arrondi est appliqué avant le test : valider une position non arrondie puis publier sa version arrondie pouvait placer un agent dans une case que le test disait libre.
- Génération du monde inchangée : empreintes SHA-256 identiques avant/après sur 5 graines × 4 configurations, dont une carte rectangulaire 300×180 (
cellCountX38,cellCountY23).npm test: 22/22.
-
syne-mock: trois défauts de cycle de vie, tous couverts partest/lifecycle.test.js(+8 tests, 22 → 30) :start()laissait un run actif après un échec :loadReplaylève sur un fichier absent ou une ligne JSON malformée, et comme il était appelé aprèsstate = 'running'mais avantstartTimer(), le résultat étaitstate='running'avectimer=null— rien ne tickait, et tout appel ultérieur àstart()était rejeté au motif que le run était déjà actif. Le chargement du replay précède désormais l'entrée enrunning. Après un échec, le monde reste préparé et acquitté, et un nouvel appel se comporte exactement comme le premier (vérifié sur le fichier absent et sur une ligne malformée) ;this.eventsn'était pas borné : deux événements par agent et par tick étaient conservés pendant toute la durée du run. Sur 2 000 ticks × 50 agents, 202 200 événements restaient en mémoire (heap 66 Mo, RSS 168 Mo, processus isolé).diagnostics.maxEvents(défaut 2 000) borne désormais le tampon, qui ne retient que les derniers événements — ticks 1981 à 2000 pour le même run (2 000 événements, heap 18 Mo, RSS 84 Mo). La troncature vide jusqu'à mi-capacité plutôt que d'un seul élément, pour ne pas décaler tout le tampon à chaque émission. Le flux diffusé (WebSocket) reste la source faisant autorité, ce tampon est un diagnostic ;goals[].agevalait le tick absolu : la liste étant reconstruite à chaque tick,ageétait toujours égal au tick courant et ne disait rien de la durée d'un but.intentionSincesuit le dernier changement d'intention etagedevient le nombre de ticks écoulés depuis (15 à 24 au tick 2 000 d'un run de référence, au lieu de 2 000). Le marqueur reste interne : le snapshot publie une liste de champs explicite, doncintentionSincen'est pas exposé.
-
syne-mock/docs/CONTRACT_REFERENCE.md: deux affirmations du contrat étaient fausses ou incomplètes, et un consommateur pouvait s'y fier.- Le document annonçait « SYNE n'a pas de configuration de type de terrain : les cellules utilisent
terrainType: "plains"». C'est faux pour le mock, qui publie trois profils —plains(coût 1),river(coût 4) etsea(walkable: false, coût infini) — et dont le pas est borné par le coût de terrain. Le tableau des profils est ajouté, avec la règle d'entrée et de sortie de rivière et la frontière mer, en distinguant ce qui appartient au mock de ce qui appartient à SYNE. - La liste des actions à mapper en
UENUMn'en citait que 5 sur 9 :SeekFood,SeekWater,FleeetSocializemanquaient. Les 9 sont listées, avec pour chacune sa condition de candidature et son premier tick de production, mesuré (graine 42, 50 agents) et non déduit. Deux impossibilités sont désormais explicites :Fleeest structurellement inatteignable, carsafetyDriftRateest positif alors quesafetydémarre à sa borne haute et ne peut donc jamais passer sous 0.5 ;Socializen'est produite qu'au tick 943, donc hors dumaxTicks: 400par défaut. - Nouvelle section « Périmètre réel et simplifications assumées » : la mort, la perception, le contenu des communications, la décision de groupe, les livres, les territoires, les ressources locales, l'échelle temporelle et le pathfinding sont des éléments décoratifs, et
simulation.eventsun tampon de diagnostic borné. Ces éléments sont publiés dans le contrat comme s'ils portaient du sens ; la section dit désormais ce qu'un client ne doit pas encore interpréter. - Le comportement d'un
startrefusé est documenté dans la section HTTP : le monde restereadyet réutilisable, donc un client peut réessayer sans appelerstop.
- Le document annonçait « SYNE n'a pas de configuration de type de terrain : les cellules utilisent
-
syne-mock: les agents passaient 146 ticks à ne rien faire au démarrage. Les besoins de départ étaient codés en dur danscreateInitialAgentsetcuriositypartait de0: il fallait quecuriosityDriftRate(0.002) amène la valeur au seuil d'exploration de 0.3, soit 150 ticks, ou 15 s àticksPerSecond: 10. Couvert par 4 tests danstest/server.test.js(30 → 34) :agentSimulation.needs.initialrend les six besoins de départ configurables, avec les domaines qu'ils bornent déjà :hunger/thirst/fatigueen0..100,safety/social/curiosityen0..1. Une valeur absente retombe sur l'état historique du mock, une valeur hors domaine est refusée à la génération du monde avecagents.needs.initial.<besoin> must be a finite number between 0 and <max>, comme les autres clés de configuration. Le placement ne lit que la graine et la carte : les besoins de départ décalent la trajectoire d'un agent, pas l'endroit où il apparaît ;curiosity: 0.3par défaut, exactement le seuil d'exploration, contre0auparavant : les 50 agents de la population de référence se mettent en mouvement au tick 1 et plus aucunIdlen'est produit.Eat: 100,Drink: 72,Rest: 234etSocialize: 943sont inchangés ;curiosityDriftRatepasse de 0.002 à 0.001, ce qui est le coût de ce décalage : explorer dès le premier tick saturecuriositybien plus tôt, et un besoin saturé garde une utilité supérieure à celle deRest, qui devient alors plus jamais produit — mesuré sur 2 000 ticks,0.002supprimeRestdu flux. À 0.001 le répertoire complet d'actions est conservé. Le taux est documenté dans le contrat avec cette dépendance ;- génération du monde vérifiée byte-identique avant/après sur 5 graines × 2 tailles (dont
300×180) ; run déterministe à l'identique sur 2 processus distincts (200 ticks, 50 agents).
-
Documentation technique V0.1 complète du monorepo (phases 0 à 5 du Plan documentation) :
- générique racine :
VISION,ARCHITECTURE,COMMUNICATION,GLOSSARY,ROADMAP,FAQ,README; - gouvernance :
GITFLOW,CI_CD,VERSIONING,LICENSE,CONTRIBUTING,CODE_OF_CONDUCT,SECURITY; docs/governance/*(ISSUES, PULL_REQUESTS, KANBAN) et templates.github/(issues, PR, CIci.yml, releaserelease.yml) ;- SYNE : 16 docs + ADR-001/002/005–011 ;
- ECHOS : 14 docs + ADR-001/002 (stack FastAPI + électron, calcul causal) ;
- PRISM : 12 docs + ADR-001 (choix Godot) ;
- ADR transverses : ADR-003 (API HTTP REST 5181), ADR-004 (WebSocket 5180) +
0000-template. docs/presentation/ETHICS_AND_SCOPE.md.
- générique racine :
-
Jalon U0 — Socle & gouvernance :
syne/: solution .NET (Simulation.Core,Simulation.Console,Simulation.Core.Tests),global.json(SDK 10.0.400), modèle de configuration Annexe H + validation, flags CLI (--seed,--max-ticks,--world-size,--config,--headless) ;echos/: monorepoechos/(API FastAPI :5000, package analyse, clients d'ingestion,echos-uiReact/TS, tests pytest) ;.gitignore,.editorconfig, branches Git Flow (develop), milestones dédupliqués, labels normalisés, CI remaniée (jobs filtréssyne/**,echos/**) ;- ECHOS-1 : structure code ECHOS livrée — paquet
echos(API FastAPIcreate_app(), registre des 7 moteurs de métriques, placeholder ingestion),echos-ui(Vite + React + TS : lint, build, tests vitest/jsdom), 13 tests pytest couverture 100 %, jobs CIechos-python+echos-uiactifs ; - ECHOS-2 : contrats d'ingestion SYNE livrés — modèles pydantic
WorldSnapshot/ExternalEvent(JSON camelCase), clientsWsClient:5180 +ControlClient:5181 testés de façon déterministe sur golden files versionnés (41 tests pytest, couverture 97 %).
-
Divergence ECHOS annoncée et documentée (prototype C#/.NET + Django → FastAPI + web local React/Vite servie par FastAPI pour V0.1, SQLite/Parquet) ; le shell Electron est conservé (implémentation différée à un horizon ultérieur, correction 23/09/2026). [surclassé par ADR-007 (05/10/2026) : interface et shell retirés — voir
Removed] -
Correction Electron (23/09/2026) : reformulation « PAS de shell Electron » → « shell Electron conservé, implémentation différée post-V0.1 » dans ADR-001 ECHOS,
docs/ARCHITECTURE.md(ECHOS + racine),FRONTEND_VISION.md(retiré du dépôt),README.md,docs/governance/ROADMAP.md,ISSUES.md,CHANGELOG.md(ECHOS),TRANSPORT_API.md(PRISM). Monographie non modifiée (snapshot figé). [surclassé par ADR-007 (05/10/2026)] -
README et INSTALLATION reflètent l'état du socle U0 ; conteneurisation Docker d'abord reportée, puis retirée définitivement le 08/10/2026 (arbitrage A1, voir
Removed).
Removed
- Jalon T6 (
docker compose up< 30 s) et conteneurisation retirés (08/10/2026, arbitrage A1) : plus de Dockerfile, decompose.yml, d'image GHCR ni de TODO de publication d'images — l'installation et l'orchestration sont portées par le Launcher (artefacts natifslinux-x64/win-x64,--check). Concernedocs/governance/ROADMAP.md(T6),docs/CI_CD.md§2/§4/§5,docs/ARCHITECTURE.md,docs/syne/ARCHITECTURE.mdetdocs/launcher/OBSERVABILITY.md; les entrées « conteneurisation reportée » des changelogs antérieures restent historiques. - ECHOS — interface web et shell de bureau retirés (ADR-007, 05/10/2026) : suppression de
echos-ui/(React/Vite, 6 écrans), deechos-desktop/(shell Electron, backend PyInstaller, packaging.deb/NSIS) et du workflowechos-desktop.yml. ECHOS devient une API headless (/ingest/run,/analysis/run|experiment|report, schéma SQLite v6) et la présentation revient aux fenêtres natives du Launcher (docs/launcher/adr/ADR-007-consoles-et-fenetre-analyse-natives.md). Les entrées « Electron conservé, différé post-V0.1 » (23/09/2026) sont surclassées par cette décision. - Site de documentation DocFX et publication GitHub Pages :
docs/site/(source DocFX),docs/landing/(landing SaaS),docs/pages-dist/(artefact d'assemblage commité alors que.gitignorel'ignorait, origine des fichiersstyles/*.jsperpétuellement modifiés) et le workflow.github/workflows/docs-pages.yml. La branchegh-pagesest réinitialisée sur une page unique. Le nettoyage supprime 51 fichiers suivis ; l'entréedocs/pages-dist/devenue inutile est retirée de.gitignore. Les documents markdown écrits à la main (docs/syne,docs/echos,docs/prism,docs/adr/,docs/governance/) sont conservés : ce sont les sources, pas des artefacts.
Deprecated
- (aucun)