diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 392c21ef..d21f24e8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -306,13 +306,22 @@ This list lives here, in the repository, where a tool can check it. A scope
MUST NOT be a file name or a class name: those move; the zone they inhabit does
not. `fix(core):`, never `fix(ErrorCode.cs):`.
-The scope is optional here because the two published packages
-(`FirstClassErrors` and `FirstClassErrors.Testing`) share a single tag-driven
-version: the scope carries no versioning weight, only readability. What does not
-belong to a component takes **no** scope — repository infrastructure (the
+The scope is load-bearing for the release record. The tooling partitions commits
+into **release trains** by scope — `tools/trains.sh` is the single source of
+truth — and each train publishes independently: `lib` (scopes `core`, `analyzers`,
+`testing`, `binder` → `FirstClassErrors`, `FirstClassErrors.Testing` and
+`FirstClassErrors.RequestBinder`), `cli` (scopes `cli`, `gendoc` → the `fce` tool)
+and `dum` (scope `dummies` → `Dummies`). A commit's scope decides which train's
+release notes and changelog it lands in; see
+[Adding a release train](doc/handwritten/for-maintainers/AddingAReleaseTrain.en.md).
+
+Because of that, a user-facing change **must** carry a scope: an unscoped `feat:`
+or `fix:` matches no train and is **silently dropped from every release note and
+changelog**, so the change would vanish from the release record. Only what
+genuinely belongs to no component stays unscoped — repository infrastructure (the
solution, `Directory.Build.props`, the workflows, `.gitignore`, `CLAUDE.md`),
-repository-wide documentation, ADRs, and the `FirstClassErrors.Usage` samples:
-`ci: …`, `docs: …`.
+repository-wide documentation, ADRs, and the `FirstClassErrors.Usage` samples —
+and those use non-version-driving types anyway: `ci: …`, `docs: …`, `chore: …`.
When one atomic change crosses several components, the commit MUST carry all
their scopes, comma-separated with no space and ordered alphabetically. The
@@ -521,11 +530,11 @@ guide.
It applies from its adoption on, to every commit created after. Prior history is
not rewritten.
-Enforcement stands today at pull-request review, which refuses a non-conforming
-message and lets the author correct it before the merge. The repository will
-lock the rule in turn: a `commit-msg` hook that refuses the message at write
-time, doubled with a CI check, since a local hook can be bypassed. Merge
-commits, generated by GitHub, are exempt.
+Enforcement is layered. A `commit-msg` hook (versioned under `.githooks/`, enabled
+once per clone) refuses a non-conforming message at write time, and — because a
+local hook can be bypassed — a CI check re-runs the same validation on every pull
+request, so a bypassed hook is still caught before the merge. Merge commits,
+generated by GitHub, are exempt.
### Credits
diff --git a/doc/handwritten/for-users/ArbitraryTestValues.fr.md b/doc/handwritten/for-users/ArbitraryTestValues.fr.md
index f34fd2ea..e5b707a3 100644
--- a/doc/handwritten/for-users/ArbitraryTestValues.fr.md
+++ b/doc/handwritten/for-users/ArbitraryTestValues.fr.md
@@ -124,7 +124,7 @@ Avant de recourir à une valeur arbitraire, vérifiez que :
---
---
diff --git a/doc/handwritten/for-users/ArchitectureOfTheDocumentationPipeline.fr.md b/doc/handwritten/for-users/ArchitectureOfTheDocumentationPipeline.fr.md
index 65d939ec..ebc657c6 100644
--- a/doc/handwritten/for-users/ArchitectureOfTheDocumentationPipeline.fr.md
+++ b/doc/handwritten/for-users/ArchitectureOfTheDocumentationPipeline.fr.md
@@ -184,7 +184,7 @@ Le code reste la source de vérité ; le pipeline rend cette connaissance transp
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/BestPractices.fr.md b/doc/handwritten/for-users/BestPractices.fr.md
index 8eac3948..2f2bccd0 100644
--- a/doc/handwritten/for-users/BestPractices.fr.md
+++ b/doc/handwritten/for-users/BestPractices.fr.md
@@ -111,7 +111,7 @@ Avant de merger une modification liée aux erreurs, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/CONTRIBUTING.fr.md b/doc/handwritten/for-users/CONTRIBUTING.fr.md
index 815b62bc..694a778a 100644
--- a/doc/handwritten/for-users/CONTRIBUTING.fr.md
+++ b/doc/handwritten/for-users/CONTRIBUTING.fr.md
@@ -308,7 +308,9 @@ l’un des suivants :
|---|---|
| `core` | `FirstClassErrors` — la bibliothèque d’exécution (`Error`, `Outcome`, `ErrorCode`, `ErrorContextKey`, …) |
| `analyzers` | `FirstClassErrors.Analyzers` — les analyzers Roslyn et leurs diagnostics `FCExxx` |
+| `binder` | `FirstClassErrors.RequestBinder` — le request binder pour la frontière de l’adaptateur primaire |
| `cli` | `FirstClassErrors.Cli` — l’outil en ligne de commande |
+| `dummies` | `Dummies` — le générateur autonome de valeurs de test arbitraires |
| `gendoc` | `FirstClassErrors.GenDoc` et son worker — le générateur de documentation |
| `testing` | `FirstClassErrors.Testing` — le package de support aux tests |
@@ -316,13 +318,22 @@ Cette liste vit ici, dans le dépôt, là où un outil peut la vérifier. Un sco
être un nom de fichier ni un nom de classe : ceux-ci bougent ; la zone qu’ils habitent,
non. `fix(core):`, jamais `fix(ErrorCode.cs):`.
-Le scope est optionnel ici parce que les deux packages publiés (`FirstClassErrors` et
-`FirstClassErrors.Testing`) partagent une seule version pilotée par tag : le scope ne porte
-aucun poids de versioning, seulement de la lisibilité. Ce qui n’appartient pas à un
-composant ne prend **aucun** scope — l’infrastructure du dépôt (la solution,
-`Directory.Build.props`, les workflows, `.gitignore`, `CLAUDE.md`), la documentation à
-l’échelle du dépôt, les ADR, et les exemples de `FirstClassErrors.Usage` : `ci: …`,
-`docs: …`.
+Le scope porte le dossier de release. L’outillage répartit les commits en **trains de
+release** selon le scope — `tools/trains.sh` est la source de vérité unique — et chaque train
+publie indépendamment : `lib` (scopes `core`, `analyzers`, `testing`, `binder` →
+`FirstClassErrors`, `FirstClassErrors.Testing` et `FirstClassErrors.RequestBinder`), `cli`
+(scopes `cli`, `gendoc` → l’outil `fce`) et `dum` (scope `dummies` → `Dummies`). Le scope
+d’un commit décide dans les release notes et le changelog de quel train il atterrit ; voir
+[Ajouter un train de release](../for-maintainers/AddingAReleaseTrain.fr.md).
+
+À cause de cela, un changement visible par l’utilisateur **doit** porter un scope : un `feat:`
+ou `fix:` sans scope ne correspond à aucun train et est **silencieusement écarté de toutes les
+release notes et de tous les changelogs**, de sorte que le changement disparaîtrait du dossier
+de release. Seul ce qui n’appartient réellement à aucun composant reste sans scope —
+l’infrastructure du dépôt (la solution, `Directory.Build.props`, les workflows, `.gitignore`,
+`CLAUDE.md`), la documentation à l’échelle du dépôt, les ADR, et les exemples de
+`FirstClassErrors.Usage` — et ceux-ci emploient de toute façon des types qui ne pilotent pas
+la version : `ci: …`, `docs: …`, `chore: …`.
Lorsqu’un changement atomique traverse plusieurs composants, le commit DOIT porter tous
leurs scopes, séparés par des virgules sans espace et classés par ordre alphabétique.
@@ -524,11 +535,11 @@ un ADR sous `doc/handwritten/for-maintainers/adr/`, ou une mise à jour de ce gu
Il s’applique à partir de son adoption, à chaque commit créé après. L’historique antérieur
n’est pas réécrit.
-L’application repose aujourd’hui sur la relecture de pull request, qui refuse un message non
-conforme et laisse l’auteur le corriger avant le merge. Le dépôt verrouillera la règle à son
-tour : un hook `commit-msg` qui refuse le message à l’écriture, doublé d’une vérification CI,
-puisqu’un hook local peut être contourné. Les commits de merge, générés par GitHub, en sont
-exemptés.
+L’application est en couches. Un hook `commit-msg` (versionné sous `.githooks/`, activé une
+fois par clone) refuse un message non conforme à l’écriture, et — parce qu’un hook local peut
+être contourné — une vérification CI rejoue la même validation sur chaque pull request, de
+sorte qu’un hook contourné est tout de même rattrapé avant le merge. Les commits de merge,
+générés par GitHub, en sont exemptés.
### Crédits
diff --git a/doc/handwritten/for-users/CatalogVersioning.fr.md b/doc/handwritten/for-users/CatalogVersioning.fr.md
index 1442673c..5fb41862 100644
--- a/doc/handwritten/for-users/CatalogVersioning.fr.md
+++ b/doc/handwritten/for-users/CatalogVersioning.fr.md
@@ -163,7 +163,7 @@ Un renommage reste cassant, car les consommateurs connaissent l'ancien code. Lor
---
---
diff --git a/doc/handwritten/for-users/CatalogVersioningCI.fr.md b/doc/handwritten/for-users/CatalogVersioningCI.fr.md
index d8d4c1ae..80b33018 100644
--- a/doc/handwritten/for-users/CatalogVersioningCI.fr.md
+++ b/doc/handwritten/for-users/CatalogVersioningCI.fr.md
@@ -214,7 +214,7 @@ Cette forme peut servir à produire des notes de version du contrat d'erreurs ou
---
---
diff --git a/doc/handwritten/for-users/CatalogVersioningReference.fr.md b/doc/handwritten/for-users/CatalogVersioningReference.fr.md
index 2d979485..1e8f4ea9 100644
--- a/doc/handwritten/for-users/CatalogVersioningReference.fr.md
+++ b/doc/handwritten/for-users/CatalogVersioningReference.fr.md
@@ -147,7 +147,7 @@ Les commandes `catalog update` et `catalog diff` acceptent notamment :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/DeterministicTesting.en.md b/doc/handwritten/for-users/DeterministicTesting.en.md
index e7f68a5a..e96d2c8e 100644
--- a/doc/handwritten/for-users/DeterministicTesting.en.md
+++ b/doc/handwritten/for-users/DeterministicTesting.en.md
@@ -160,7 +160,7 @@ public void A_missing_order_error_is_fully_deterministic() {
## Arbitrary occurrence data
-When a test needs `OccurredAt` and `InstanceId` to be stable but does not assert their exact values, freeze them to an *arbitrary* value with `Clock.UseAny()` and `InstanceIds.UseAny()` rather than `UseFixed`; both take an optional seed for reproducibility. They belong to the broader `Any` factory for values a test does not assert on — see [Arbitrary Test Values](ArbitraryTestValues.en.md).
+When a test needs `OccurredAt` and `InstanceId` to be stable but does not assert their exact values, freeze them to an *arbitrary* value with `Clock.UseAny()` and `InstanceIds.UseAny()` rather than `UseFixed`; both are parameterless, and a test that needs those arbitrary values to be reproducible wraps its body in `Dummies.Any.Reproducibly(...)`. They belong to the broader `Any` factory for values a test does not assert on — see [Arbitrary Test Values](ArbitraryTestValues.en.md).
## Scope and parallel tests
diff --git a/doc/handwritten/for-users/DeterministicTesting.fr.md b/doc/handwritten/for-users/DeterministicTesting.fr.md
index c43f78bf..6db8ad6d 100644
--- a/doc/handwritten/for-users/DeterministicTesting.fr.md
+++ b/doc/handwritten/for-users/DeterministicTesting.fr.md
@@ -160,7 +160,7 @@ public void Une_erreur_de_commande_absente_est_totalement_deterministe() {
## Données d’occurrence arbitraires
-Lorsqu’un test a besoin que `OccurredAt` et `InstanceId` soient stables sans asserter leurs valeurs exactes, figez-les sur une valeur *arbitraire* avec `Clock.UseAny()` et `InstanceIds.UseAny()` plutôt que `UseFixed` ; les deux acceptent une graine optionnelle pour la reproductibilité. Ils appartiennent à la factory `Any`, plus large, dédiée aux valeurs qu’un test n’assertit pas — voir [Valeurs de test arbitraires](ArbitraryTestValues.fr.md).
+Lorsqu’un test a besoin que `OccurredAt` et `InstanceId` soient stables sans asserter leurs valeurs exactes, figez-les sur une valeur *arbitraire* avec `Clock.UseAny()` et `InstanceIds.UseAny()` plutôt que `UseFixed` ; les deux sont sans paramètre, et un test qui a besoin que ces valeurs arbitraires soient reproductibles enveloppe son corps dans `Dummies.Any.Reproducibly(...)`. Ils appartiennent à la factory `Any`, plus large, dédiée aux valeurs qu’un test n’assertit pas — voir [Valeurs de test arbitraires](ArbitraryTestValues.fr.md).
## Portée et tests parallèles
@@ -203,7 +203,7 @@ Avant d’approuver un test déterministe, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/DocumentationExtractionReference.fr.md b/doc/handwritten/for-users/DocumentationExtractionReference.fr.md
index d7690882..cf3e2353 100644
--- a/doc/handwritten/for-users/DocumentationExtractionReference.fr.md
+++ b/doc/handwritten/for-users/DocumentationExtractionReference.fr.md
@@ -280,7 +280,7 @@ Lorsque des erreurs attendues manquent, parcourez le pipeline dans l’ordre —
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/ErrorContext.fr.md b/doc/handwritten/for-users/ErrorContext.fr.md
index b4722490..67883276 100644
--- a/doc/handwritten/for-users/ErrorContext.fr.md
+++ b/doc/handwritten/for-users/ErrorContext.fr.md
@@ -173,7 +173,7 @@ Si la réponse est oui aux six questions, c’est une excellente candidate.
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/FAQ.fr.md b/doc/handwritten/for-users/FAQ.fr.md
index 1c5000c6..94cd0b26 100644
--- a/doc/handwritten/for-users/FAQ.fr.md
+++ b/doc/handwritten/for-users/FAQ.fr.md
@@ -130,7 +130,7 @@ Voir [Taxonomie et composition des erreurs](ErrorTaxonomy.fr.md).
---
---
diff --git a/doc/handwritten/for-users/Internationalisation.fr.md b/doc/handwritten/for-users/Internationalisation.fr.md
index 3ec7e429..775e6e17 100644
--- a/doc/handwritten/for-users/Internationalisation.fr.md
+++ b/doc/handwritten/for-users/Internationalisation.fr.md
@@ -250,7 +250,7 @@ Avant de publier des catalogues localisés, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/LoggingIntegration.fr.md b/doc/handwritten/for-users/LoggingIntegration.fr.md
index f9603397..89449aa9 100644
--- a/doc/handwritten/for-users/LoggingIntegration.fr.md
+++ b/doc/handwritten/for-users/LoggingIntegration.fr.md
@@ -265,7 +265,7 @@ Avant d’approuver une intégration de logging, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/OperationalIntegration.fr.md b/doc/handwritten/for-users/OperationalIntegration.fr.md
index 9457dae8..8c0fe001 100644
--- a/doc/handwritten/for-users/OperationalIntegration.fr.md
+++ b/doc/handwritten/for-users/OperationalIntegration.fr.md
@@ -239,7 +239,7 @@ Avant d’approuver un pipeline de livraison du catalogue, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/OutcomeGuide.fr.md b/doc/handwritten/for-users/OutcomeGuide.fr.md
index 4701452d..a358ccfd 100644
--- a/doc/handwritten/for-users/OutcomeGuide.fr.md
+++ b/doc/handwritten/for-users/OutcomeGuide.fr.md
@@ -293,7 +293,7 @@ Avant de valider un flux `Outcome`, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/Testing.fr.md b/doc/handwritten/for-users/Testing.fr.md
index 5e7a0f53..8c66056c 100644
--- a/doc/handwritten/for-users/Testing.fr.md
+++ b/doc/handwritten/for-users/Testing.fr.md
@@ -166,7 +166,7 @@ Avant d’approuver un test d’erreur, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/UsagePatterns.fr.md b/doc/handwritten/for-users/UsagePatterns.fr.md
index 7fe2133e..42e8f221 100644
--- a/doc/handwritten/for-users/UsagePatterns.fr.md
+++ b/doc/handwritten/for-users/UsagePatterns.fr.md
@@ -199,7 +199,7 @@ Choisissez le contrat à partir de la première liste, le type d’erreur à par
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/WhenNotToUseFirstClassErrors.fr.md b/doc/handwritten/for-users/WhenNotToUseFirstClassErrors.fr.md
index fa2419cd..86182e6e 100644
--- a/doc/handwritten/for-users/WhenNotToUseFirstClassErrors.fr.md
+++ b/doc/handwritten/for-users/WhenNotToUseFirstClassErrors.fr.md
@@ -73,7 +73,7 @@ L’objectif de cette bibliothèque n’est pas de rendre toutes les exceptions
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/WritingACustomRenderer.fr.md b/doc/handwritten/for-users/WritingACustomRenderer.fr.md
index bc40ecbb..3b008160 100644
--- a/doc/handwritten/for-users/WritingACustomRenderer.fr.md
+++ b/doc/handwritten/for-users/WritingACustomRenderer.fr.md
@@ -307,7 +307,7 @@ Avant de publier un renderer, vérifiez que :
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/WritingErrorMessages.fr.md b/doc/handwritten/for-users/WritingErrorMessages.fr.md
index 063b75a4..95c482af 100644
--- a/doc/handwritten/for-users/WritingErrorMessages.fr.md
+++ b/doc/handwritten/for-users/WritingErrorMessages.fr.md
@@ -212,7 +212,7 @@ Pour le titre, la description, la règle, les diagnostics et les exemples stable
---
---
\ No newline at end of file
diff --git a/doc/handwritten/for-users/WritingErrorsGuide.fr.md b/doc/handwritten/for-users/WritingErrorsGuide.fr.md
index 5821dbe7..4ccf675c 100644
--- a/doc/handwritten/for-users/WritingErrorsGuide.fr.md
+++ b/doc/handwritten/for-users/WritingErrorsGuide.fr.md
@@ -218,7 +218,7 @@ Pour choisir les textes publics et internes portés à l’exécution, continuez
---
---
\ No newline at end of file