0.0.5-alpha.5.fix.1

This commit is contained in:
2026-09-25 08:14:57 +02:00
parent f03f8640d4
commit 81c7a720f5
9 changed files with 864 additions and 124 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Plan 0.0.5 — réconciliation réelle avec KSP et sasedev-games
@@ -84,11 +84,13 @@ Cette tranche ne commence pas l'audit Rust ni le profil Tauri.
Extraire le noyau réellement commun de `audit_rust_export_completeness.py`, l'aligner avec `510-RUST_CARGO.md`, documenter son caractère facultatif et ses limites, et ajouter des tests positifs/négatifs.
La réconciliation a révélé que le script historique ne découvrait que `crates/*/Cargo.toml` : il couvrait les `22` crates KSP mais aucune des `23` crates Games imbriquées sous des sous-familles. La variante PGR doit donc corriger la découverte récursive avant toute généralisation. Elle doit également abandonner les sous-règles historiques de tests séparés et la duplication de résolution de noms déjà possédée par le compilateur.
La réconciliation a révélé que le script historique ne découvrait que `crates/*/Cargo.toml` : il couvrait les `22` packages KSP mais aucun des `23` packages Games imbriqués sous des sous-familles. La variante PGR corrige donc la découverte récursive et distingue aussi les cibles `lib`/`bin` d'un même package pour ne pas confondre plusieurs crates Rust compilées séparément.
Mesure de référence avec le scope PGR retenu : `KSP = 0 candidate(s) / 22 crates`, `Games = 2 candidate(s) / 23 crates`. Les deux signaux Games sont une observation de réconciliation, pas une non-conformité rétroactive.
La première livraison `alpha.5` a ensuite révélé un second défaut de scope pendant la revue opérateur : elle ne détectait que les chemins internes longs tels que `crate::module::Item`, alors que le contrat attendu impose `crate::Item` pour **toute référence intra-crate à un item partagé**, y compris un nom nu dans son module propriétaire. La gate mécanique était verte mais l'acceptation fonctionnelle est donc `FAIL`; `alpha.5.fix.1` corrige ce blind spot sans commencer Tauri.
Cette tranche ne réimporte pas `audit_rust_general_rules.py` en bloc. Toute règle déjà possédée par Rustfmt, Clippy, le compilateur ou les audits PGR existants reste chez son owner actuel.
Mesure `alpha.5.fix.1` en `--report-only --summary-only` : KSP `0.3.16` = `1525` lignes candidates `RUST-IMPORT-201` sur `22` packages ; Games `0.3.5` = `181` lignes candidates sur `23` packages (`2 RUST-API-201`, `179 RUST-IMPORT-201`). Ces nombres forment un inventaire d'adoption future, pas une non-conformité rétroactive des baselines.
Cette tranche ne réimporte pas `audit_rust_general_rules.py` en bloc et ne réintroduit pas le diagnostic historique de résolution `crate::X` déjà possédé par le compilateur. Toute autre règle déjà possédée par Rustfmt, Clippy, le compilateur ou les audits PGR existants reste chez son owner actuel.
### alpha.6 — premier profil framework : Tauri

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/510-RUST_CARGO.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Profil Rust / Cargo
@@ -90,9 +90,11 @@ Tout élément `pub` ou `pub(crate)` SHOULD posséder une rustdoc utile au point
Une crate réutilisable SHOULD exposer son API durable depuis son crate-root par des réexports explicites. Les modules d'implémentation SHOULD rester privés ; `pub mod` ne doit pas être utilisé uniquement pour éviter de concevoir une façade stable.
Un élément partagé à l'échelle de sa propre crate SHOULD être consommable via `crate::Item` après réexport approprié. Les autres crates consomment l'API publique du crate propriétaire plutôt que ses chemins internes.
Lorsqu'un projet adopte le **contrat strict de façade crate-root**, un élément `pub` d'un module fichier MUST être réexporté explicitement au crate-root. Un élément `pub(crate)` consommé hors de son module MUST de même posséder un réexport `pub(crate)` au crate-root. Un helper réellement local SHOULD rester privé plutôt qu'élargir artificiellement sa visibilité.
Les glob imports SHOULD être évités. Un alias de réexport ne doit pas servir à masquer durablement une collision de noms dans l'API ; lorsqu'un nom fait partie du contrat public, le propriétaire SHOULD choisir un nom canonique non ambigu.
Dès qu'un item possède cette identité crate-root, toute référence dans la même crate MUST utiliser `crate::Item`, y compris depuis son module de déclaration et depuis ses tests unitaires attachés. Les formes `Item`, `self::Item`, `super::Item`, `module::Item` et `crate::module::Item` ne sont pas des chemins de consommation canoniques pour cet item partagé. Un élément strictement privé reste local au module ; un test unitaire attaché peut alors utiliser `super::PrivateItem`.
Les autres crates consomment l'API publique du crate propriétaire via `owner_crate::Item` plutôt que ses chemins internes. Les glob imports SHOULD être évités. Un alias de réexport ne doit pas servir à masquer durablement une collision de noms dans l'API ; lorsqu'un nom fait partie du contrat public, le propriétaire SHOULD choisir un nom canonique non ambigu.
PGR ne généralise pas l'interdiction KSP plus stricte de tous les `use` non-traits : cette convention appartient à un profil ou projet plus spécifique tant qu'elle n'est pas nécessaire au contrat Rust général.
@@ -194,6 +196,6 @@ Un éventuel wrapper d'orchestration MAY appeler plusieurs audits, mais chaque a
Les deux audits de style disponibles sont documentés dans [`../validation/002-RUST_STYLE_AUDITS.md`](../validation/002-RUST_STYLE_AUDITS.md). Ils restent optionnels et prennent un corpus Rust explicitement sélectionné.
[`../validation/003-RUST_EXPORT_COMPLETENESS.md`](../validation/003-RUST_EXPORT_COMPLETENESS.md) documente séparément un audit facultatif de façade crate-root. Lorsqu'un projet choisit ce contrat strict, les éléments `pub` des modules fichiers et les éléments `pub(crate)` clairement partagés hors de leur module SHOULD être réexportés explicitement au crate-root ; lorsqu'un tel réexport existe, les chemins internes SHOULD employer `crate::Item` plutôt que `crate::module::Item`.
[`../validation/003-RUST_EXPORT_COMPLETENESS.md`](../validation/003-RUST_EXPORT_COMPLETENESS.md) documente séparément l'audit facultatif qui matérialise ce contrat strict. `RUST-API-201` vérifie la présence de la façade pour les items partagés ; `RUST-IMPORT-201` vérifie que les références intra-crate détectées passent réellement par `crate::Item`, y compris dans le module propriétaire, et pas seulement qu'un chemin interne long a été évité.
Cet audit de façade ne rend pas la règle obligatoire pour tous les projets Rust et ne remplace pas le compilateur. Le dépôt PGR lui-même ne contient pas de workspace Rust sur lequel une telle gate fournirait une preuve runtime ou Cargo réelle.
Cet audit ne rend pas le contrat strict obligatoire pour tous les projets Rust et ne remplace pas le compilateur. Il contrôle au contraire une convention architecturale que `rustc` et Clippy ne possèdent pas. Le dépôt PGR lui-même ne contient pas de workspace Rust sur lequel une telle gate fournirait une preuve runtime ou Cargo réelle.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/studies/001-KSP_GAMES_RECONCILIATION.md -->
<!-- version: 2 -->
<!-- version: 3 -->
# Étude de réconciliation PGR / KSP / sasedev-games
@@ -137,7 +137,9 @@ Décision : classer ce contrôle `COMMON`, mais ne pas copier le script tel quel
La relecture du script historique révèle une limite importante : sa découverte `crates/*/Cargo.toml` couvre les `22` crates KSP placées directement sous `crates/`, mais aucune des `23` crates Games imbriquées sous `crates/apps`, `crates/common`, `crates/engines`, etc. Le code des deux scripts est bien commun, mais le résultat historique Games `0 candidate(s)` n'était pas une preuve d'audit des crates réelles.
La variante PGR corrige la découverte récursive et réduit le scope aux deux propriétés effectivement possédées par `510-RUST_CARGO.md` : complétude de la façade crate-root et canonicalisation d'un chemin interne lorsqu'un réexport explicite existe. Avec cette variante, KSP donne `0 candidate(s)` sur `22` crates et Games `2 candidate(s)` sur `23` crates. Ces deux signaux Games ne sont pas des non-conformités rétroactives ; ils deviennent des points de décision lors d'une future adoption PGR.
La variante PGR corrige d'abord la découverte récursive. La revue opérateur de `alpha.5` montre ensuite que limiter la canonicalisation à `crate::module::Item` est insuffisant : les corpus contiennent de nombreuses références nues à des items pourtant réexportés, notamment dans leur module propriétaire, et ni `rustc` ni Clippy ne possèdent la convention « référence partagée = `crate::Item` partout dans la crate ». `alpha.5` est donc conservé `FAIL` et `alpha.5.fix.1` étend `RUST-IMPORT-201` à ce contrat réel.
La mesure corrigée, avec découverte récursive et séparation des cibles Cargo, donne `1525` lignes candidates `RUST-IMPORT-201` dans KSP sur `22` packages et `181` lignes candidates dans Games sur `23` packages (`2 RUST-API-201`, `179 RUST-IMPORT-201`). Ces volumes ne sont pas interprétés comme des défauts rétroactifs : ils montrent précisément pourquoi une adoption PGR d'un projet existant doit commencer en `--report-only`, puis migrer progressivement la convention avant d'en faire une gate bloquante.
Le grand `audit_rust_general_rules.py` des deux corpus reste `DEFER` comme source d'extraction, pas comme script à importer. Il mélange plusieurs responsabilités : headers, rustdoc, imports, structure de façade, blancs, constantes et règles locales ; Games y ajoute une exception FFI Android étroite. PGR doit continuer à extraire des audits monotâches seulement lorsqu'une propriété commune apporte une preuve non redondante.

View File

@@ -1,51 +1,111 @@
<!-- file: docs/validation/003-RUST_EXPORT_COMPLETENESS.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Audit facultatif de façade Rust et export completeness
# Audit facultatif de façade Rust et canonicalisation intra-crate
## Responsabilité
`scripts/audit_rust_export_completeness.py` contrôle un contrat Rust optionnel plus strict que les garanties du compilateur : les éléments publics durables d'un module fichier doivent être exposés par une façade explicite au crate-root, et le code de la même crate doit employer cette façade lorsqu'un réexport correspondant existe.
`scripts/audit_rust_export_completeness.py` contrôle un contrat Rust optionnel plus strict que les garanties du compilateur et de Clippy : une crate qui adopte cette convention possède une façade crate-root explicite pour ses items partagés, et toute consommation intra-crate d'un item possédant cette identité canonique passe par `crate::Item`.
Le propriétaire normatif reste [`../rules/510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Le script ne définit pas une architecture à lui seul et ne doit être adopté que par un projet qui choisit ce contrat de façade.
Le propriétaire normatif reste [`../rules/510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Le script ne définit pas une architecture à lui seul ; il matérialise uniquement le contrat strict lorsqu'un projet choisit de l'adopter.
L'audit émet seulement deux familles de diagnostics :
L'audit émet deux familles de diagnostics :
```text
RUST-API-201
élément `pub`, ou élément `pub(crate)` clairement consommé hors de son module fichier,
sans réexport explicite correspondant au crate-root
item `pub` d'un module fichier sans réexport explicite au crate-root,
ou item `pub(crate)` clairement consommé hors de son module sans réexport crate-root
RUST-IMPORT-201
chemin interne long `crate::module::Item` alors que le crate-root expose déjà `crate::Item`
référence intra-crate non canonique à un item possédant une identité crate-root ;
la forme attendue est `crate::Item`
```
Il ne vérifie pas les règles générales de style, les lignes vides, l'ordre des constantes, la rustdoc, les lints Cargo/Clippy, la sûreté FFI ou le contenu métier.
`RUST-IMPORT-201` ne se limite donc pas à `crate::module::Item`. Il couvre notamment :
```text
Item
self::Item
super::Item
module::Item
crate::module::Item
```
lorsque ces formes désignent un item partagé déjà exposé comme `crate::Item`.
La règle s'applique également **dans le module qui déclare l'item**. Par exemple :
```rust
pub struct Runtime;
impl Runtime {
pub fn new() -> Runtime {
return Runtime;
}
}
```
n'est pas canonique après réexport de `Runtime`. Le contrat strict attend :
```rust
pub struct Runtime;
impl crate::Runtime {
pub fn new() -> crate::Runtime {
return crate::Runtime;
}
}
```
Le même principe vaut pour une fonction partagée appelée depuis son module propriétaire : `crate::build()` est canonique, `build()` ne l'est pas.
Les lignes de déclaration de l'item et les lignes de réexport qui construisent la façade sont naturellement exemptées. Un item strictement privé n'appartient pas à ce contrat ; dans un test unitaire attaché à son module parent, `super::PrivateItem` reste une forme locale légitime.
## Pourquoi Cargo/Clippy ne suffisent pas
Le compilateur vérifie la résolution et la visibilité des noms ; il ne possède pas la convention « tout accès partagé passe par `crate::Item` ». Clippy peut signaler certains imports ou réexports inutilisés, mais une seule consommation correcte de `crate::Item` peut suffire à rendre le réexport utilisé tandis que d'autres références continuent d'employer un nom nu ou un chemin interne.
L'audit fournit donc une preuve distincte : **uniformité de la façade et des chemins intra-crate**, pas simple compilabilité.
## Cibles Cargo et graphes de modules
La `version: 2` ne confond plus automatiquement tous les fichiers d'un package Cargo dans une seule crate. Un package peut contenir simultanément une bibliothèque `src/lib.rs` et un binaire `src/main.rs` ; ces deux cibles sont compilées comme des crates distinctes et `crate::Item` n'y désigne pas la même racine.
L'audit découvre les packages récursivement puis traite séparément les cibles standard/explicites `lib` et `bin`. Pour chaque cible, il suit les modules fichiers déclarés par `mod name;` et les modules `#[path = "..."]`, ce qui couvre notamment les `unit_tests/` attachés utilisés dans le corpus KSP/Games sans englober les tests d'intégration Cargo sous `tests/`, qui sont des crates externes.
Cette distinction évite de considérer à tort `owner_crate::Item` utilisé dans un binaire comme une violation de la bibliothèque du même package.
## Provenance et correction du corpus
KSP `0.3.16` et Games `0.3.5` contiennent un `audit_rust_export_completeness.py` dont le corps est identique à l'exception de sa révision de fichier. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
KSP `0.3.16` et Games `0.3.5` contiennent un `audit_rust_export_completeness.py` historiquement identique à l'exception de sa révision. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
Le script historique découvre uniquement `crates/*/Cargo.toml`. Cette hypothèse couvre les `22` crates KSP situées directement sous `crates/`, mais elle ne couvre aucune des `23` crates Games organisées sous des sous-familles telles que `crates/apps/`, `crates/common/` ou `crates/engines/`. Un résultat historique `0 candidate(s)` dans Games ne constitue donc pas une preuve que ses crates avaient réellement été inspectées par cet audit.
Deux limites historiques ont été identifiées :
La variante PGR corrige ce point en découvrant récursivement les packages Cargo possédant `src/lib.rs` ou `src/main.rs`, tout en ignorant les arbres générés usuels. Elle gère également correctement un module fichier `mod.rs` et les réexports explicites simples, groupés ou aliasés.
1. la découverte `crates/*/Cargo.toml` couvrait les `22` packages KSP directement sous `crates/`, mais aucune des `23` crates Games imbriquées sous `crates/apps/`, `crates/common/`, `crates/engines/`, etc. ;
2. la canonicalisation détectait surtout `crate::module::Item` et quelques cas particuliers de tests, mais pas les références ordinaires par nom nu dans les modules de production, y compris le module propriétaire.
La réconciliation générateur réalisée pendant `0.0.5-alpha.5` donne avec la nouvelle découverte :
La première version PGR de `alpha.5` corrigeait seulement la découverte récursive et le chemin interne long. La revue opérateur après sa gate a explicitement révélé la seconde lacune ; `alpha.5` est donc conservé `FAIL` et `alpha.5.fix.1` étend `RUST-IMPORT-201` au contrat réellement attendu.
## Mesure de réconciliation après le fix
Avec la `version: 2`, en mode `--report-only --summary-only`, les baselines autoritaires de la réconciliation donnent :
```text
KSP 0.3.16 22 crate(s) 0 candidate(s)
Games 0.3.5 23 crate(s) 2 candidate(s)
KSP 0.3.16
22 package(s)
1525 candidate(s)
RUST-IMPORT-201: 1525
Games 0.3.5
23 package(s)
181 candidate(s)
RUST-API-201: 2
RUST-IMPORT-201: 179
```
Les deux candidats Games sont des constantes `pub(crate)` du smoke WebTransport navigateur consommées depuis d'autres modules sans façade crate-root. Games n'étant pas encore gouverné par PGR, ces signaux ne sont pas qualifiés de défauts rétroactifs ; ils montrent simplement ce qu'une adoption future de ce contrat devrait décider ou corriger.
Ces nombres représentent des **lignes candidates**, pas des déclarations distinctes. Ils sont beaucoup plus élevés que la première mesure `0 / 2` parce que le contrat corrige précisément le blind spot signalé : les références nues déjà répandues dans les modules propriétaires et consumers deviennent enfin visibles.
## Ce qui n'est pas repris du script historique
PGR n'importe pas les diagnostics historiques liés aux tests unitaires séparés (`super::...`, références non qualifiées) : leur validité dépend du layout et du contrat local des modules de test, et la baseline Games ne prouve pas une convergence opérationnelle sur ce point.
PGR n'importe pas non plus le contrôle d'un `crate::Symbol` non résolu. Le compilateur Rust est déjà propriétaire de la résolution de noms ; dupliquer cette erreur dans un audit Python n'apporte pas une preuve supplémentaire proportionnée.
L'audit PGR reste donc monotâche : façade crate-root et canonicalisation des chemins pour les seuls symboles qu'il sait relier explicitement.
KSP et Games n'étant pas encore gouvernés par cette version de PGR, ces résultats ne sont pas qualifiés de défauts rétroactifs et ne déclenchent aucune migration dans `0.0.5`. Ils constituent un inventaire d'adoption future. Pour un projet existant, l'usage initial en `--report-only` est donc particulièrement important.
## Adoption
@@ -64,20 +124,22 @@ Commande canonique :
python3 -B scripts/audit_rust_export_completeness.py --root .
```
Le code de sortie est `0` lorsque le corpus est propre, `1` lorsqu'au moins un candidat est trouvé et `2` lorsque la racine est invalide, illisible ou ne contient aucune crate Rust détectable. `--report-only` permet une phase d'inventaire sans bloquer la gate ; `--summary-only` réduit la sortie aux comptes.
Le code de sortie est `0` lorsque le corpus est propre, `1` lorsqu'au moins un candidat est trouvé et `2` lorsque la racine est invalide, illisible ou ne contient aucun package Rust détectable. `--report-only` permet un inventaire sans bloquer la gate ; `--summary-only` réduit la sortie aux comptes.
Une adoption progressive peut donc commencer par :
Une adoption progressive d'un projet existant devrait commencer par :
```bash
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only --summary-only
```
puis devenir bloquante une fois les exceptions ou écarts locaux explicitement résolus.
puis inventorier/corriger les familles d'écarts avant de rendre l'audit bloquant. Une nouvelle crate créée sous le contrat peut au contraire l'activer immédiatement.
## Limites
L'analyse est lexicale et non un parseur Rust complet. Elle vise les déclarations `pub`/`pub(crate)` au niveau racine de modules fichiers et les réexports explicites du crate-root. Les macros générant des items, attributs élaborés, modules inline complexes, `#[path]` non standard et formes de réexport imbriquées au-delà du contrat testé peuvent nécessiter une revue humaine ou les outils Rust du projet.
L'analyse reste lexicale et ne remplace pas un parseur Rust complet. Elle vise les déclarations module-level `pub`/`pub(crate)`, les réexports explicites et les références dans le graphe de modules d'une cible. Les macros générant des items, modules inline complexes, `include!`, formes de `#[path]` atypiques et réexports élaborés peuvent nécessiter une revue humaine.
Un élément `pub(crate)` n'est exigé à la façade que lorsqu'une consommation externe au fichier est clairement détectée par un chemin `crate::module::Item` ou `crate::Item`. L'absence de diagnostic ne prouve donc pas toute la sémantique de visibilité de la crate.
Pour réduire les faux positifs, une fonction partagée est reconnue comme référence lorsqu'elle apparaît sous une forme d'appel directe ; l'usage d'une fonction comme valeur de premier ordre peut donc échapper à cette version. Les chemins explicitement qualifiés par une crate externe et les appels de méthodes `.name()` ne sont pas assimilés à un item local de même nom.
L'audit ne remplace ni `cargo check`, ni Clippy, ni rustdoc. Il complète uniquement une convention architecturale explicitement choisie.
Un item `pub(crate)` qui n'est pas clairement consommé hors de son module n'est pas forcé au crate-root par `RUST-API-201`; sa visibilité peut nécessiter une revue séparée. Dès qu'un réexport crate-root existe, ses références détectées sont toutefois soumises à `RUST-IMPORT-201`, y compris dans son module de déclaration.
L'audit ne remplace ni `cargo check`, ni Clippy, ni rustdoc. Il complète ces outils avec une convention architecturale qu'ils ne possèdent pas.