20 Commits

Author SHA1 Message Date
9a7f4f7c67 0.0.5-rel.001.fix.1 2026-09-25 18:27:36 +02:00
436680207f 0.0.5-rel.001 2026-09-25 18:24:10 +02:00
390dd63e21 0.0.5-rc.1 2026-09-25 18:13:53 +02:00
c97ca5f21a 0.0.5-beta.1 2026-09-25 18:07:50 +02:00
5a83968813 0.0.5-alpha.9 2026-09-25 18:00:24 +02:00
9f596f30b2 0.0.5-alpha.9 2026-09-25 17:59:25 +02:00
c331da5114 0.0.5-alpha.8 2026-09-25 17:41:26 +02:00
ecd9de4d4e 0.0.5-alpha.7.fix.1 2026-09-25 17:22:10 +02:00
ac03b78708 0.0.5-alpha.7 2026-09-25 11:45:10 +02:00
4e1c1cd6f0 0.0.5-alpha.6.fix.3 2026-09-25 10:47:38 +02:00
b7e65d9100 0.0.5-alpha.6.fix.2 2026-09-25 10:35:20 +02:00
33bf835b7f 0.0.5-alpha.6.fix.1 2026-09-25 10:19:35 +02:00
0f6a2e21eb 0.0.5-alpha.6 2026-09-25 09:16:06 +02:00
81c7a720f5 0.0.5-alpha.5.fix.1 2026-09-25 08:14:57 +02:00
f03f8640d4 0.0.5-alpha.5 2026-09-25 00:36:24 +02:00
defc9ca4b5 0.0.5-alpha.4 2026-09-25 00:03:51 +02:00
a954d7f90a 0.0.5-alpha.3.fix.1 2026-09-24 23:40:02 +02:00
1b1a7092e5 0.0.5-alpha.3 2026-09-24 22:23:59 +02:00
d7282dc33b 0.0.5-alpha.2 2026-09-24 21:21:44 +02:00
3ca2518c5c 0.0.5-alpha.1 2026-09-24 20:43:57 +02:00
76 changed files with 8877 additions and 98 deletions

View File

@@ -1,5 +1,5 @@
<!-- file: CHANGELOG.md -->
<!-- version: 7 -->
<!-- version: 9 -->
# Changelog Project Governance Rules
@@ -7,6 +7,15 @@ Ce fichier décrit les changements utiles aux utilisateurs ou intégrateurs des
Les tranches `alpha`, `beta`, `rc`, fixes, validations, audits et détails de développement appartiennent aux plans, deltas et historiques. Ils ne sont pas recopiés ici par défaut.
## 0.0.5
- consolide le lifecycle inter-release de PGR : prompts de reprise versionnés, contrats locaux de fichiers/familles, responsabilités de fermeture et revue d'horizon sans création artificielle de releases futures ;
- renforce le profil Rust/Cargo des nouveaux projets contrôlés avec Rust 2024, chemins explicites, façade crate-root, contrats de suffixes de crates, séparation bibliothèque/exécutable, erreurs explicites et ownership du tracing ;
- ajoute et sépare les validations Rust structurelles utiles : complétude des réexports/chemins `crate::Item`, espacement des items, ordre des modules, blocs d'imports de traits et blocs de réexports, tout en conservant les contrôles à ordre sémantique comme opt-in ;
- active les profils framework et Tauri 2, avec host Rust direct, frontière frontend explicite, TS-RS pour les DTO partagés, plugins/capabilities gouvernés, versions produit classifiées et exception Tauri encadrée au principe de séparation `lib`/`bin` ;
- enregistre le skeleton Tauri contrôlé comme trajectoire `DEFER` jusqu'à matérialisation d'owners Web suffisants, sans créer de skeleton décoratif ni réserver une release ;
- réconcilie les décisions avec les corpus KSP/Games, conserve leurs architectures métier locales hors du noyau PGR et prépare le prompt de reprise `006` vers la première baseline réutilisable `0.1.0`.
## 0.0.4
- définit un contrat transversal de skeleton specifications pour sélectionner et composer règles, profils, templates, décisions destinataire, overrides et validation sans dupliquer les propriétaires normatifs ;

View File

@@ -1,5 +1,5 @@
<!-- file: README.md -->
<!-- version: 11 -->
<!-- version: 14 -->
# Project Governance Rules
@@ -13,7 +13,7 @@ Le dépôt officiel est `https://git.sasedev.com/Sasedev/pgr`.
PGR est actuellement en phase de fondation `0.0.x`.
La version courante est définie par [`VERSION`](VERSION). La release `0.0.1` établit la gouvernance normative et documentaire de PGR lui-même. La release `0.0.2` ajoute les premiers profils réutilisables Rust/Cargo, Java/Maven/Gradle et Android, ainsi que deux audits Markdown. La release `0.0.3` fournit les premiers modèles Rust/Cargo, deux audits de style Rust facultatifs, l'audit des headers Markdown et la spécification du message manuel de reprise. La release `0.0.4` définit le contrat transversal des skeleton specifications, ajoute une fondation composable de gouvernance locale et une première spécialisation Rust/Cargo workspace fondée sur les profils `500 + 510` et les cinq templates existants. Les autres spécialisations technologiques restent différées tant que leurs pièces requises manquent.
La version courante est définie par [`VERSION`](VERSION). La release `0.0.1` établit la gouvernance normative et documentaire de PGR lui-même. La release `0.0.2` ajoute les premiers profils réutilisables Rust/Cargo, Java/Maven/Gradle et Android, ainsi que deux audits Markdown. La release `0.0.3` fournit les premiers modèles Rust/Cargo, deux audits de style Rust facultatifs, l'audit des headers Markdown et la spécification du message manuel de reprise. La release `0.0.4` définit le contrat transversal des skeleton specifications, ajoute une fondation composable de gouvernance locale et une première spécialisation Rust/Cargo workspace fondée sur les profils `500 + 510` et les cinq templates existants. La release `0.0.5` réconcilie PGR avec les baselines réelles de `khadhroony-solana-project` et `sasedev-games` : prompts versionnés, contrats documentaires locaux, lifecycle inter-release, profil Rust strict et audits monotâches, premier profil Tauri 2 et décision explicite de différer son skeleton complet jusqu'à matérialisation d'owners Web suffisants.
## Entrées principales
@@ -28,6 +28,8 @@ La version courante est définie par [`VERSION`](VERSION). La release `0.0.1` é
- [`docs/plans/002-V0_0_2_RULE_PROFILES_PLAN.md`](docs/plans/002-V0_0_2_RULE_PROFILES_PLAN.md) — plan historique des premiers profils `0.0.2` ;
- [`docs/plans/003-V0_0_3_TEMPLATES_AUDITS_PLAN.md`](docs/plans/003-V0_0_3_TEMPLATES_AUDITS_PLAN.md) — plan historique des templates et audits `0.0.3` ;
- [`docs/plans/004-V0_0_4_SKELETONS_PLAN.md`](docs/plans/004-V0_0_4_SKELETONS_PLAN.md) — plan historique des skeleton specifications `0.0.4` ;
- [`docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md`](docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md) — plan historique de réconciliation `0.0.5` ;
- [`docs/studies/001-KSP_GAMES_RECONCILIATION.md`](docs/studies/001-KSP_GAMES_RECONCILIATION.md) — étude de réconciliation et classification des écarts entre PGR, KSP et Games ;
- [`docs/skeletons/000-README.md`](docs/skeletons/000-README.md) — index des skeleton specifications et de leur contrat de composition ;
- [`docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md`](docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md) — fondation composable de gouvernance locale pour un projet adoptant PGR ;
- [`docs/skeletons/002-RUST_CARGO_WORKSPACE.md`](docs/skeletons/002-RUST_CARGO_WORKSPACE.md) — specialization composable pour un workspace Rust/Cargo gouverné ;

View File

@@ -1,9 +1,9 @@
<!-- file: ROADMAP.md -->
<!-- version: 5 -->
<!-- version: 8 -->
# Roadmap Project Governance Rules
Le roadmap décrit le découpage macro des releases PGR. Il ne remplace ni les objectifs globaux du projet ni les plans détaillés de version.
Le roadmap décrit le découpage macro engagé des releases PGR. Il ne remplace ni les objectifs globaux du projet ni les plans détaillés de version et ne sert pas à inventer des versions futures uniquement pour prolonger l'horizon.
Statuts : `( )` planifié, `(x)` terminé, `(d)` différé, `(c)` annulé.
@@ -13,10 +13,22 @@ Statuts : `( )` planifié, `(x)` terminé, `(d)` différé, `(c)` annulé.
- `(x) 0.0.2` — premiers profils de règles réutilisables : Rust/Cargo, Java/Maven/Gradle et Android, avec modèle de composition et audits Markdown initiaux ;
- `(x) 0.0.3` — templates techniques initiaux et premiers patterns d'audit réutilisables issus de besoins réels ;
- `(x) 0.0.4` — spécifications Markdown de skeletons et règles de composition des profils/templates ;
- `( ) 0.0.5` — réconciliation réelle de PGR avec `khadhroony-solana-project` et `sasedev-games`.
- `(x) 0.0.5` — réconciliation réelle de PGR avec `khadhroony-solana-project` et `sasedev-games`, incluant la consolidation du lifecycle inter-release et de la revue d'horizon.
## Première baseline réutilisable
- `( ) 0.1.0` — première baseline PGR destinée à initialiser de nouveaux projets à partir de règles, templates et skeleton specifications éprouvés.
## Horizon après `0.1.0`
L'horizon est volontairement ouvert. PGR ne réserve pas encore `0.1.1`, `0.2.0` ou une autre release sans objectif suffisamment décidé.
### Trajectoire explicitement différée
- `(d) skeleton Tauri pour projets contrôlés` — la décision `0.0.5-alpha.9` conserve le candidat, mais sa matérialisation attend des owners Web suffisants pour TypeScript, Vite, Sass/CSS et leur validation. Aucun numéro de release n'est réservé pour ce travail et `0.1.0` ne doit pas le considérer comme obligatoire par défaut.
Cette entrée conserve une décision de report réelle ; elle ne transforme pas les autres candidats d'horizon en engagements. Les profils techniques supplémentaires, les projets documentaires/spécifications et l'audit de généricité hors logiciel restent chez leurs owners actuels tant qu'une revue d'horizon n'a pas décidé leur entrée dans la trajectoire engagée.
Le plan de `0.1.0` devra programmer une revue d'horizon avant sa clôture, à un moment où la première baseline réutilisable sera assez établie pour juger la suite. Cette revue pourra se tenir dans une session stratégique séparée sans modifier `VERSION`. Elle décidera soit d'étendre la présente roadmap, soit de maintenir explicitement l'horizon ouvert avec un nouveau déclencheur de revue, soit de requalifier/différer/mettre en pause une trajectoire concernée.
Le contenu détaillé et le nombre de tranches de chaque release sont définis par son plan actif, créé ou consolidé pendant `alpha.1`.

View File

@@ -1,5 +1,5 @@
<!-- file: RULES.md -->
<!-- version: 10 -->
<!-- version: 11 -->
# Index normatif Project Governance Rules
@@ -84,6 +84,8 @@ Familles actuellement actives :
- [`docs/rules/520-JAVA_MAVEN_GRADLE.md`](docs/rules/520-JAVA_MAVEN_GRADLE.md) — profil Java et spécialisations Maven/Gradle : toolchain LTS, structure, dépendances, tests et validation de build.
- [`docs/rules/600-PLATFORMS.md`](docs/rules/600-PLATFORMS.md) — contrat commun des profils plateforme et de leur compatibilité de target/runtime.
- [`docs/rules/610-ANDROID.md`](docs/rules/610-ANDROID.md) — profil Android : structure, toolchain/SDK, compatibilité, packaging, configuration locale et validation plateforme.
- [`docs/rules/700-FRAMEWORKS.md`](docs/rules/700-FRAMEWORKS.md) — contrat commun des profils framework et de leur orchestration avec langages, plateformes et outils de build.
- [`docs/rules/710-TAURI.md`](docs/rules/710-TAURI.md) — profil Tauri 2 : host Rust, configuration/capabilities, hooks frontend, version produit, artefacts générés et validation d'intégration.
Le modèle de composition et d'ownership des profils est défini dans [`docs/architecture/003-RULE_PROFILE_MODEL.md`](docs/architecture/003-RULE_PROFILE_MODEL.md).

View File

@@ -1 +1 @@
0.0.4
0.0.5

193
deltas/0.0.5/alpha.1.md Normal file
View File

@@ -0,0 +1,193 @@
<!-- file: deltas/0.0.5/alpha.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.1 — réconciliation des baselines et cadrage
## Baseline et sources
Baseline PGR : archive source fournie `pgr-v0.0.4.zip`, déclarée téléchargée depuis le tag Gitea `v0.0.4`, accompagnée du rapport externe `pgr-0.0.4-publish.001-report.md` avec statut `PASS`.
SHA-256 vérifié :
```text
pgr-v0.0.4.zip 7137bb084f017362323a2043d13ba71490be15b8786adc8d645981bf455b80ae
khadhroony-solana-project-v0.3.16.zip db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0
games-v0.3.5.zip 5f5fdee1a271386766d059657d6eaa831acdc263b3e722fed738808335c0a105
```
Les deux archives externes sont des corpus autoritaires de réconciliation pour cette session, pas des projets déjà gouvernés par PGR. Elles ne sont ni modifiées ni incorporées à l'archive delta.
Version avant application :
```text
0.0.4
```
Version après application :
```text
0.0.5-alpha.1
```
## Objet
Exécuter la gate de cadrage imposée par `prompts/005-V0_0_5_START_PROMPT.md` : vérifier indépendamment la stable PGR et sa preuve post-tag, contrôler les deux snapshots externes fournis, inventorier leurs responsabilités et audits, puis classer les écarts avant toute modification normative importante.
L'analyse détaillée vit dans `docs/studies/001-KSP_GAMES_RECONCILIATION.md`. Elle confirme que KSP `v0.3.16` et Games `v0.3.5` n'ont pas encore adopté PGR et ne doivent donc pas être jugés comme des projets non conformes. La plupart des mécanismes de gouvernance, documentation, validation et Rust/Cargo observés sont déjà correctement possédés par PGR.
Quatre axes communs restent suffisamment étayés pour le plan `0.0.5` : contrat générique des prompts versionnés, pattern facultatif de contrats de fichiers/familles, audit Rust de façade/export completeness, et Tauri comme premier candidat réel à la famille framework `700`.
Aucun de ces axes n'est matérialisé normativement dans `alpha.1`. Web/Vite/WASM, JNI, les audits composites et les règles métier ou de distribution spécifiques restent différés ou locaux.
## Vérifications d'intake déjà exécutées
Le ZIP PGR est lisible, possède une racine unique `pgr/`, `139` fichiers et `28` répertoires, sans doublon, collision de casse, chemin dangereux, lien symbolique ni `.git`. Son hash concorde avec le rapport post-tag.
Les deux corpus externes passent les contrôles ZIP équivalents. Leurs audits statiques fournis ont également été exécutés avec succès : audits Rust généraux/export/workspace et tableaux Markdown pour KSP ; audits Rust généraux/export/workspace, tableaux Markdown et distribution layout pour Games.
Sur la stable PGR extraite, les `15` tests Python passent, les trois audits Markdown sont propres et les cinq TOML Rust/Cargo se parsèrent. La toolchain `cargo` n'est pas disponible dans l'environnement de préparation ; aucune commande Cargo externe n'est donc présentée comme exécutée.
## Scope
REPLACE :
```text
VERSION
README.md
docs/000-README.md
```
ADD :
```text
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/studies/000-README.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
deltas/0.0.5/alpha.1.md
```
DELETE : aucun.
L'archive delta contient uniquement ces sept fichiers complets avec leurs chemins relatifs. Aucun history, règle normative, script, template technique, profil framework, skeleton ou modification KSP/Games n'appartient à cette tranche.
`ROADMAP.md` annonce déjà `0.0.5` comme planifiée et `CHANGELOG.md` reste limité aux releases publiées ; aucun changement cérémoniel de ces deux fichiers n'est requis ici.
## Validation opérateur
Après application sur le commit stable `0.0.4`, depuis la racine du dépôt :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.1'
test -f docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
test -f docs/studies/000-README.md
test -f docs/studies/001-KSP_GAMES_RECONCILIATION.md
test -f deltas/0.0.5/alpha.1.md
test ! -e history/0.0.5/alpha.1.md
grep -Fx '<!-- version: 12 -->' README.md
grep -Fx '<!-- version: 10 -->' docs/000-README.md
grep -F '005-V0_0_5_RECONCILIATION_PLAN.md' README.md docs/000-README.md
grep -F '001-KSP_GAMES_RECONCILIATION.md' README.md docs/000-README.md docs/studies/000-README.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
import tomllib
expected_hashes = {
'pgr-v0.0.4.zip': '7137bb084f017362323a2043d13ba71490be15b8786adc8d645981bf455b80ae',
'khadhroony-solana-project-v0.3.16.zip': 'db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0',
'games-v0.3.5.zip': '5f5fdee1a271386766d059657d6eaa831acdc263b3e722fed738808335c0a105',
}
study = Path('docs/studies/001-KSP_GAMES_RECONCILIATION.md').read_text(encoding='utf-8')
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
for name, digest in expected_hashes.items():
assert name in study or name in plan, name
assert digest in study or digest in plan, digest
for path in (
Path('templates/rust-cargo/rustfmt.toml'),
Path('templates/rust-cargo/clippy.toml'),
Path('templates/rust-cargo/.cargo/config.toml'),
Path('templates/rust-cargo/Cargo.toml'),
Path('templates/rust-cargo/crates/project-core-lib/Cargo.toml'),
):
with path.open('rb') as stream:
tomllib.load(stream)
expected_counts = {'0.0.1': 12, '0.0.2': 12, '0.0.3': 10, '0.0.4': 9}
for release, expected in expected_counts.items():
deltas = {path.name for path in Path('deltas', release).glob('*.md')}
histories = {path.name for path in Path('history', release).glob('*.md')}
assert len(deltas) == expected, (release, len(deltas))
assert deltas == histories, (release, sorted(deltas ^ histories))
statuses_004 = {}
for path in Path('history/0.0.4').glob('*.md'):
text = path.read_text(encoding='utf-8')
match = re.search(r'## Statut\s+(PASS|FAIL|PARTIAL|BLOCKED)\s', text)
assert match is not None, path
statuses_004[path.name] = match.group(1)
assert statuses_004['alpha.4.md'] == 'FAIL'
assert statuses_004['alpha.4.fix.1.md'] == 'PASS'
assert statuses_004['beta.1.md'] == 'FAIL'
assert statuses_004['beta.1.fix.1.md'] == 'PASS'
assert statuses_004['rc.1.md'] == 'PASS'
assert statuses_004['rel.001.md'] == 'PASS'
assert 'ne sont pas encore gouvernés par PGR' in plan
assert 'aucune migration de KSP ou Games' in plan
assert 'Tauri' in plan
assert 'audit_rust_export_completeness.py' in study
print('0.0.5 alpha.1 structural audit: clean (43 prior pairs, 5 TOML parsed, reconciliation sources recorded)')
PY
git diff --check
git add VERSION README.md docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/studies/000-README.md docs/studies/001-KSP_GAMES_RECONCILIATION.md \
deltas/0.0.5/alpha.1.md
git diff --cached --check
git status --short
```
Le scope indexé attendu avant ajout du history est :
```text
M README.md
M VERSION
A deltas/0.0.5/alpha.1.md
M docs/000-README.md
A docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
A docs/studies/000-README.md
A docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
La revue opérateur doit en outre confirmer que :
- les trois archives/hashes consignés correspondent bien aux sources autoritaires fournies pour cette reprise ;
- l'étude traite KSP et Games comme corpus non encore migrés vers PGR ;
- le plan n'atteste aucun profil Tauri, audit de façade ou template avant sa tranche dédiée ;
- les conventions métier et plateforme locales ne sont pas promues en règles PGR par simple similarité ;
- aucune modification des deux projets externes n'est cachée dans le delta.
## Transition
### PASS
Rédiger et livrer séparément `history/0.0.5/alpha.1.md` avec les résultats réellement observés ; l'appliquer et committer la tranche. Préparer seulement ensuite `0.0.5-alpha.2` pour le contrat générique des prompts versionnés.
### FAIL
Consigner le résultat réel dans le history de `alpha.1`, puis corriger le cadrage dans `0.0.5-alpha.1.fix.1` ou produire un REPLAN motivé. Aucun contenu de `alpha.2` ne doit être absorbé silencieusement dans un fix.

179
deltas/0.0.5/alpha.2.md Normal file
View File

@@ -0,0 +1,179 @@
<!-- file: deltas/0.0.5/alpha.2.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.2 — contrat générique des prompts de démarrage
## Baseline et version
Baseline attendue : commit opérateur de `0.0.5-alpha.1`, réalisé après application de `history/0.0.5/alpha.1.md` avec statut `PASS`.
Le hash de ce commit n'est volontairement pas anticipé : le history `alpha.1` et le présent delta peuvent être livrés dans le même échange, mais restent deux archives séparées et le delta `alpha.2` MUST NOT être appliqué avant le commit de `alpha.1`.
Version avant application :
```text
0.0.5-alpha.1
```
Version après application :
```text
0.0.5-alpha.2
```
## Objet
Exécuter la deuxième tranche du plan `0.0.5` en renforçant l'owner `docs/rules/210-WORKFLOW.md` pour les prompts de démarrage versionnés.
Les prompts PGR récents et les baselines KSP/Games montrent qu'un bon prompt de reprise ne se limite pas à nommer une baseline et une mission : il doit permettre de reconstruire explicitement l'état utile, les sources à lire, les décisions déjà acquises, les limites de scope, la première gate et la trajectoire de validation sans réimporter toute la documentation du projet.
La tranche généralise ce contrat sémantique sans copier la structure exhaustive des prompts KSP/Games. Elle ne crée ni nouveau template manuel ni nouveau type d'artefact.
## Scope
REPLACE :
```text
VERSION
docs/rules/000-README.md
docs/rules/210-WORKFLOW.md
```
ADD :
```text
deltas/0.0.5/alpha.2.md
```
DELETE : aucun.
L'archive delta contient uniquement ces quatre fichiers complets avec leurs chemins relatifs. Elle ne contient pas `history/0.0.5/alpha.1.md`, livré séparément pour clôturer la tranche précédente, ni `history/0.0.5/alpha.2.md`, qui sera produit après la gate opérateur.
Le plan `docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md`, l'étude `docs/studies/001-KSP_GAMES_RECONCILIATION.md` et `docs/templates/003-NEW_SESSION_PROMPT.md` restent inchangés : cette tranche exécute leur trajectoire et préserve explicitement la séparation entre prompt versionné et message manuel de nouvelle session.
## Contrat matérialisé
`docs/rules/210-WORKFLOW.md` porte désormais un contrat réutilisable des prompts de démarrage versionnés couvrant :
- baseline autoritaire, cible et première tranche ;
- ordre de lecture nécessaire à une reprise fiable ;
- état déjà acquis et traçabilité des décisions conservées ;
- mission et résultat attendu ;
- scope et hors-scope ;
- décisions gelées et questions ouvertes ;
- première gate et travaux éventuellement interdits avant sa fermeture ;
- trajectoire prévisionnelle explicitement non contraignante ;
- validation et responsabilité de la preuve ;
- comportement fail-closed lorsqu'une source externe requise manque.
Le contrat reste sémantique : il n'impose pas neuf titres littéraux ni une longueur uniforme. Les projets importants peuvent détailler leur ordre de lecture et leurs questions de cadrage ; les projets simples conservent une forme courte dès lors que les mêmes responsabilités sont couvertes.
Les règles locales de langage, plateforme, framework, architecture ou métier ne deviennent pas des règles PGR parce qu'elles apparaissent dans un prompt. Le prompt doit renvoyer vers les owners existants au lieu de les dupliquer.
`docs/templates/003-NEW_SESSION_PROMPT.md` conserve son rôle distinct : produire la courte phrase manuelle de reprise après une stable publiée. Il ne remplace pas le prompt versionné et n'est pas dupliqué par cette tranche.
## Validation opérateur
Après avoir appliqué `history/0.0.5/alpha.1.md`, committé `0.0.5-alpha.1`, puis appliqué le présent delta, depuis la racine du dépôt :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.2'
test -f history/0.0.5/alpha.1.md
test -f deltas/0.0.5/alpha.2.md
test ! -e history/0.0.5/alpha.2.md
grep -Fx '<!-- version: 12 -->' docs/rules/000-README.md
grep -Fx '<!-- version: 4 -->' docs/rules/210-WORKFLOW.md
grep -F 'prompts versionnés' docs/rules/000-README.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.1.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+PASS\s', history)
workflow = Path('docs/rules/210-WORKFLOW.md').read_text(encoding='utf-8')
required = (
'## Prompt de démarrage versionné',
'### Contrat minimal',
'baseline autoritaire et cible',
'ordre de lecture',
'état déjà acquis',
'mission et résultat attendu',
'scope et hors-scope',
'décisions gelées et questions ouvertes',
'première gate',
'trajectoire prévisionnelle',
'validation et responsabilité',
'### Sources externes et données manquantes',
'### Décisions acquises et détails locaux',
'### Création et clôture',
'003-NEW_SESSION_PROMPT.md',
)
for phrase in required:
assert phrase in workflow, phrase
for forbidden in (
'prompts/036-V0_3_17_START_PROMPT.md',
'prompts/007-V0_3_6_START_PROMPT.md',
'tokio-postgres',
'WebTransport',
'Uroburas',
):
assert forbidden not in workflow, forbidden
assert 'MUST NOT les recopier exhaustivement' in workflow
assert 'Le prompt n\'a pas à reproduire littéralement cette numérotation.' in workflow
assert 'MUST NOT autoriser à reconstituer silencieusement' in workflow
assert 'Les contraintes locales de langage, plateforme, framework, architecture ou métier restent chez leurs owners respectifs.' in workflow
manual = Path('docs/templates/003-NEW_SESSION_PROMPT.md').read_text(encoding='utf-8')
assert '<!-- version: 1 -->' in manual
assert 'une seule phrase' in manual
print('0.0.5 alpha.2 prompt contract audit: clean (versioned prompt strengthened, manual restart template unchanged)')
PY
git diff --check
git add VERSION docs/rules/000-README.md docs/rules/210-WORKFLOW.md \
deltas/0.0.5/alpha.2.md
git diff --cached --check
git status --short
```
Le scope indexé attendu avant ajout du history est :
```text
M VERSION
A deltas/0.0.5/alpha.2.md
M docs/rules/000-README.md
M docs/rules/210-WORKFLOW.md
```
La revue opérateur doit en outre confirmer que :
- le nouveau contrat décrit des responsabilités de reprise et non une liste obligatoire de titres ;
- la baseline et les sources manquantes restent fail-closed : aucun état externe n'est reconstruit depuis un souvenir ou un workspace non fourni ;
- les décisions acquises sont résumées seulement lorsqu'elles empêchent une régression utile et restent traçables vers leur owner ;
- les contraintes KSP/Games spécifiques ne sont pas aspirées dans la règle générale ;
- le template `003-NEW_SESSION_PROMPT.md` reste la forme courte post-publication et n'est ni remplacé ni dupliqué.
## Transition
### PASS
Rédiger et livrer séparément `history/0.0.5/alpha.2.md` avec les résultats réellement observés ; l'appliquer et committer la tranche. Préparer seulement ensuite `0.0.5-alpha.3` pour le pattern facultatif de contrats de fichiers/familles.
### FAIL
Consigner le résultat réel dans le history de `alpha.2`, puis corriger le contrat dans `0.0.5-alpha.2.fix.1` ou produire un REPLAN motivé. Aucun contenu de `alpha.3` ne doit être absorbé silencieusement dans un fix.

View File

@@ -0,0 +1,164 @@
<!-- file: deltas/0.0.5/alpha.3.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.3.fix.1 — couverture des tables dans les exemples Markdown fenced
## Baseline et version
Baseline attendue : commit opérateur de `0.0.5-alpha.3`, réalisé après application de `history/0.0.5/alpha.3.md` avec statut `FAIL`.
Le hash de ce commit n'est pas anticipé.
Version avant application :
```text
0.0.5-alpha.3
```
Version après application :
```text
0.0.5-alpha.3.fix.1
```
## Objet
Corriger le faux négatif découvert pendant la gate opérateur de `alpha.3` : `audit_markdown_tables.py` ignorait toutes les tables placées dans des blocs fenced, y compris les exemples explicitement `markdown` du template `004-FILE_FAMILY_CONTRACTS.md`.
Le fix étend uniquement le contrat de l'audit de tables. Il ne transforme pas l'audit en analyseur général des blocs de code et n'introduit aucun contenu de la tranche Rust `alpha.4`.
## Scope
REPLACE :
```text
VERSION
scripts/audit_markdown_tables.py
docs/templates/004-FILE_FAMILY_CONTRACTS.md
docs/validation/001-VALIDATION_MATRIX.md
```
ADD :
```text
tests/test_markdown_tables.py
deltas/0.0.5/alpha.3.fix.1.md
```
DELETE : aucun.
Le history `history/0.0.5/alpha.3.md` appartient au commit baseline du FAIL et n'est pas inclus dans l'archive du fix.
## Correction
`scripts/audit_markdown_tables.py` passe en révision interne `2`.
L'audit continue de contrôler les tables Markdown ordinaires. Dans un bloc fenced :
- une fence explicitement `markdown` est auditée ;
- une fence explicitement `md` est auditée ;
- une fence d'un autre langage ou sans langage reste ignorée.
Cette frontière évite les faux positifs sur les sorties terminal, extraits de code ou tableaux ASCII qui utilisent éventuellement des pipes sans être du Markdown à gouverner.
`tests/test_markdown_tables.py` verrouille quatre cas :
1. table ordinaire et table fenced Markdown valides toutes deux comptées ;
2. table mal alignée dans une fence `markdown` rejetée ;
3. alias `md` audité ;
4. fence non Markdown conservée hors scope.
`docs/templates/004-FILE_FAMILY_CONTRACTS.md` passe en révision `2` et réaligne les deux tables fenced révélées par le nouvel audit.
`docs/validation/001-VALIDATION_MATRIX.md` passe en révision `10` et explicite la frontière de `PGR-MD-TABLE-001`.
## Validation opérateur
Après application du history `FAIL`, commit de `alpha.3`, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.3.fix.1'
test -f history/0.0.5/alpha.3.md
test -f deltas/0.0.5/alpha.3.md
test -f deltas/0.0.5/alpha.3.fix.1.md
test -f tests/test_markdown_tables.py
test ! -e history/0.0.5/alpha.3.fix.1.md
grep -A2 '^## Statut$' history/0.0.5/alpha.3.md | grep -Fx 'FAIL'
grep -Fx '# version: 2' scripts/audit_markdown_tables.py
grep -Fx '<!-- version: 2 -->' docs/templates/004-FILE_FAMILY_CONTRACTS.md
grep -Fx '<!-- version: 10 -->' docs/validation/001-VALIDATION_MATRIX.md
grep -Fx '# version: 1' tests/test_markdown_tables.py
python3 -B -m unittest discover -s tests -p 'test_markdown_tables.py' -v
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py docs/templates/004-FILE_FAMILY_CONTRACTS.md
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.3.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+FAIL\s', history)
script = Path('scripts/audit_markdown_tables.py').read_text(encoding='utf-8')
assert '_MARKDOWN_FENCE_LANGUAGES = frozenset({"markdown", "md"})' in script
assert 'audit_fenced_markdown' in script
template = Path('docs/templates/004-FILE_FAMILY_CONTRACTS.md').read_text(encoding='utf-8')
assert template.count('```markdown') == 2
matrix = Path('docs/validation/001-VALIDATION_MATRIX.md').read_text(encoding='utf-8')
assert 'fences explicitement `markdown` ou `md`' in matrix
assert 'sorties terminal' in matrix
print('0.0.5 alpha.3.fix.1 fenced Markdown table audit: clean (regression covered, two template examples governed)')
PY
git diff --check
git add VERSION scripts/audit_markdown_tables.py tests/test_markdown_tables.py \
docs/templates/004-FILE_FAMILY_CONTRACTS.md \
docs/validation/001-VALIDATION_MATRIX.md \
deltas/0.0.5/alpha.3.fix.1.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history du fix est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.3.fix.1.md
M docs/templates/004-FILE_FAMILY_CONTRACTS.md
M docs/validation/001-VALIDATION_MATRIX.md
M scripts/audit_markdown_tables.py
A tests/test_markdown_tables.py
```
Le contrôle ciblé du template doit désormais compter ses deux exemples :
```text
Markdown table audit: clean (2 table(s), 1 file(s))
```
La suite Python doit compter `19` tests si aucun autre test n'est ajouté localement avant exécution.
## Transition
### PASS
Créer et appliquer séparément `history/0.0.5/alpha.3.fix.1.md`, committer et pousser le fix, puis reprendre le plan `0.0.5` à `alpha.4`.
### FAIL
Rester dans `0.0.5-alpha.3.fix.M`. Le scope MUST rester limité au contrat de `PGR-MD-TABLE-001` et aux tables révélées par ce contrôle tant qu'aucun défaut plus large ne justifie un REPLAN.

173
deltas/0.0.5/alpha.3.md Normal file
View File

@@ -0,0 +1,173 @@
<!-- file: deltas/0.0.5/alpha.3.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.3 — pattern facultatif de contrats de fichiers ou familles
## Baseline et version
Baseline attendue : commit opérateur de `0.0.5-alpha.2`, réalisé après application de `history/0.0.5/alpha.2.md` avec statut `PASS`.
Le hash de ce commit n'est pas anticipé. Le history `alpha.2` et le présent delta peuvent être livrés dans le même échange, mais restent deux archives séparées et le delta `alpha.3` MUST NOT être appliqué avant le commit de `alpha.2`.
Version avant application :
```text
0.0.5-alpha.2
```
Version après application :
```text
0.0.5-alpha.3
```
## Objet
Exécuter la troisième tranche du plan `0.0.5` en généralisant un besoin observé indépendamment dans les deux corpus externes : expliciter, lorsque la complexité le justifie, la responsabilité et la règle de modification de certaines familles de fichiers locales.
La tranche normalise le pattern et non les catalogues KSP/Games. Elle n'impose ni le nom `FILE_CONTRACTS.md`, ni une table exhaustive, ni la reprise de leurs chemins, règles métier ou conventions de release.
## Scope
REPLACE :
```text
VERSION
docs/000-README.md
docs/rules/300-DOCUMENTATION.md
docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
```
ADD :
```text
docs/templates/004-FILE_FAMILY_CONTRACTS.md
deltas/0.0.5/alpha.3.md
```
DELETE : aucun.
L'archive delta contient uniquement ces six fichiers complets avec leurs chemins relatifs. Elle ne contient pas `history/0.0.5/alpha.2.md`, livré séparément pour clôturer la tranche précédente, ni `history/0.0.5/alpha.3.md`, qui sera produit après la gate opérateur.
## Contrat matérialisé
`docs/rules/300-DOCUMENTATION.md` possède désormais le pattern générique :
- un registre local est facultatif et ne doit exister que pour lever une ambiguïté réelle ;
- il décrit au minimum fichier/famille, responsabilité durable et règle de modification ;
- source de vérité, statut généré/versionné, owner ou validation restent des champs optionnels ;
- le registre ne doit ni recopier les owners existants ni devenir un miroir exhaustif de l'arborescence ;
- son nom, son emplacement et sa forme appartiennent au projet destinataire ;
- l'ajout d'un fichier déjà couvert par une famille ne force pas une entrée supplémentaire.
`docs/templates/004-FILE_FAMILY_CONTRACTS.md` fournit une forme d'adaptation courte avec critères d'usage, modèle minimal, règles d'instanciation et exemple abstrait.
La fondation `001-PROJECT_GOVERNANCE_FOUNDATION.md` référence ce pattern uniquement comme possibilité pour un projet dont plusieurs familles durables deviennent ambiguës. Elle confirme explicitement qu'un projet simple n'a rien à matérialiser.
## Validation opérateur
Après avoir appliqué `history/0.0.5/alpha.2.md`, committé `0.0.5-alpha.2`, puis appliqué le présent delta, depuis la racine du dépôt :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.3'
test -f history/0.0.5/alpha.2.md
test -f deltas/0.0.5/alpha.3.md
test -f docs/templates/004-FILE_FAMILY_CONTRACTS.md
test ! -e history/0.0.5/alpha.3.md
grep -Fx '<!-- version: 11 -->' docs/000-README.md
grep -Fx '<!-- version: 3 -->' docs/rules/300-DOCUMENTATION.md
grep -Fx '<!-- version: 2 -->' docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
grep -Fx '<!-- version: 1 -->' docs/templates/004-FILE_FAMILY_CONTRACTS.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.2.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+PASS\s', history)
doc = Path('docs/rules/300-DOCUMENTATION.md').read_text(encoding='utf-8')
template = Path('docs/templates/004-FILE_FAMILY_CONTRACTS.md').read_text(encoding='utf-8')
foundation = Path('docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md').read_text(encoding='utf-8')
index = Path('docs/000-README.md').read_text(encoding='utf-8')
required_doc = (
'## Registre local de contrats de fichiers ou familles',
'outil de désambiguïsation, pas un inventaire exhaustif',
'le fichier, motif de chemin ou famille concernée',
'sa responsabilité durable',
'la règle ou le déclencheur pertinent de modification',
'PGR n\'impose ni `FILE_CONTRACTS.md`',
'l\'ajout d\'un fichier conforme à une famille déjà décrite ne force pas une nouvelle entrée',
)
for phrase in required_doc:
assert phrase in doc, phrase
required_template = (
'# Pattern — contrats locaux de fichiers ou familles',
'Quand l\'utiliser',
'Forme minimale',
'Règles d\'adaptation',
'Critère de sortie',
'`docs/rules/FILE_CONTRACTS.md` est un nom possible, pas une convention PGR obligatoire',
)
for phrase in required_template:
assert phrase in template, phrase
assert '004-FILE_FAMILY_CONTRACTS.md' in foundation
assert 'Ce registre reste facultatif' in foundation
assert '004-FILE_FAMILY_CONTRACTS.md' in index
for text in (doc, template, foundation):
for forbidden in ('tokio-postgres', 'WebTransport', 'Uroburas'):
assert forbidden not in text, forbidden
print('0.0.5 alpha.3 file-family contract audit: clean (optional registry, short reusable pattern, no project-specific contract imported)')
PY
git diff --check
git add VERSION docs/000-README.md docs/rules/300-DOCUMENTATION.md \
docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md \
docs/templates/004-FILE_FAMILY_CONTRACTS.md deltas/0.0.5/alpha.3.md
git diff --cached --check
git status --short
```
Le scope indexé attendu avant ajout du history est :
```text
M VERSION
A deltas/0.0.5/alpha.3.md
M docs/000-README.md
M docs/rules/300-DOCUMENTATION.md
M docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
A docs/templates/004-FILE_FAMILY_CONTRACTS.md
```
La revue opérateur doit en outre confirmer que :
- le pattern réduit une ambiguïté réelle au lieu d'imposer une nouvelle cérémonie ;
- `FILE_CONTRACTS.md` reste un exemple de nom, jamais une obligation ;
- le registre ne devient pas un catalogue exhaustif de chaque fichier du dépôt ;
- une règle détaillée déjà possédée ailleurs est référencée plutôt que dupliquée ;
- la fondation n'impose aucun nouveau document aux petits projets ou aux projets dont les responsabilités sont déjà claires.
## Transition
### PASS
Rédiger et livrer séparément `history/0.0.5/alpha.3.md` avec les résultats réellement observés ; l'appliquer et committer la tranche. Préparer seulement ensuite `0.0.5-alpha.4` pour l'audit Rust facultatif de façade/export completeness.
### FAIL
Consigner le résultat réel dans le history de `alpha.3`, puis corriger le pattern dans `0.0.5-alpha.3.fix.1` ou produire un REPLAN motivé. Aucun contenu de l'audit Rust `alpha.4` ne doit être absorbé silencieusement dans un fix.

221
deltas/0.0.5/alpha.4.md Normal file
View File

@@ -0,0 +1,221 @@
<!-- file: deltas/0.0.5/alpha.4.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.4 — REPLAN du lifecycle inter-release et de l'horizon
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.3.fix.1`, réalisé après application de `history/0.0.5/alpha.3.fix.1.md` avec statut `PASS`.
Le hash de ce futur commit n'est pas anticipé.
Version avant application :
```text
0.0.5-alpha.3.fix.1
```
Version après application :
```text
0.0.5-alpha.4
```
## Rôle de la tranche
`alpha.4` est un `REPLAN` déclenché après la gate réussie du fix précédent. Il traite un besoin de gouvernance général découvert en relisant le plan `0.0.5`, `ROADMAP.md` et les historiques de conception PGR `0.0.1` à `0.0.4` : les responsabilités de fermeture sont connues séparément mais ne sont pas encore reliées par un contrat explicite de plan, et l'horizon des versions futures ne possède pas de mécanisme de revue.
La tranche ne remet pas en cause `alpha.3.fix.1`, ne commence pas le chantier Rust de façade et ne matérialise aucun profil Tauri.
## Scope
REPLACE :
```text
VERSION
ROADMAP.md
docs/000-README.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/200-GOVERNANCE.md
docs/rules/210-WORKFLOW.md
docs/rules/300-DOCUMENTATION.md
docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
docs/templates/003-NEW_SESSION_PROMPT.md
```
ADD :
```text
docs/templates/005-RELEASE_PLAN.md
deltas/0.0.5/alpha.4.md
```
DELETE : aucun.
Le history `alpha.3.fix.1` appartient au commit baseline et n'est pas inclus dans l'archive du présent delta.
## Décisions matérialisées
### Plan de release
Un plan complexe doit désormais expliciter sa **clôture et transition** : réconciliation documentaire, `CHANGELOG`, `ROADMAP`, prompt/handoff suivant, fermeture du plan, stable et publication.
Il doit aussi indiquer si une **revue d'horizon** est nécessaire. Une cible future inconnue reste inconnue : PGR interdit d'inventer une version ou une mission uniquement pour remplir le plan.
### Revue d'horizon
`ROADMAP.md` devient explicitement une trajectoire macro engagée à horizon fini. Une revue d'horizon est déclenchée lorsque la dernière release connue approche, qu'un jalon change la trajectoire ou que les ideas/studies/décisions accumulées justifient de rediscuter la suite.
La revue peut vivre dans une session stratégique séparée sans changement de `VERSION`.
Pour PGR, l'après-`0.1.0` reste volontairement ouvert. Le plan `0.1.0` devra programmer sa propre revue avant fermeture au lieu de spéculer dès `0.0.5`.
### Continuité et productivité
Lorsque la gate courante est `PASS` et que la suite est déjà déterminée, le générateur peut remettre `history` courant et `delta` suivant dans la même réponse. Les archives restent séparées et le delta suivant reste interdit d'application avant le commit de la tranche courante.
Le message manuel après publication devient spontané lorsque la prochaine cible est connue. S'il existe une preuve post-tag externe requise, la consigne finale indique explicitement qu'elle doit être fournie avec l'archive taggée.
### Pattern de plan
`docs/templates/005-RELEASE_PLAN.md` fournit une forme courte réutilisable pour KSP, Games et les futurs projets : mission, état acquis/ouvert, scope, validation, forecast, déclencheurs de REPLAN, clôture/transition et revue d'horizon.
Le pattern ne crée pas un journal de revisions obligatoire ; les deltas/histories restent la trace détaillée.
## Replan de 0.0.5
L'ancien forecast est décalé :
```text
alpha.4 REPLAN lifecycle inter-release / horizon
alpha.5 audit Rust façade/export completeness
alpha.6 profil Tauri
alpha.7 décision skeleton Tauri
beta.1 réconciliation intégrée
rc.1 candidate gelée / CHANGELOG / ROADMAP / prompt 006
stable 0.0.5 + publish.001 séparé
```
Aucun ancien objectif n'est abandonné par ce décalage.
## Validation opérateur
Après application du history `PASS` de `alpha.3.fix.1`, commit/push de ce fix, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.4'
test -f history/0.0.5/alpha.3.fix.1.md
test -f deltas/0.0.5/alpha.4.md
test -f docs/templates/005-RELEASE_PLAN.md
test ! -e history/0.0.5/alpha.4.md
grep -A2 '^## Statut$' history/0.0.5/alpha.3.fix.1.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 2 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '<!-- version: 3 -->' docs/rules/200-GOVERNANCE.md
grep -Fx '<!-- version: 5 -->' docs/rules/210-WORKFLOW.md
grep -Fx '<!-- version: 4 -->' docs/rules/300-DOCUMENTATION.md
grep -Fx '<!-- version: 6 -->' ROADMAP.md
grep -Fx '<!-- version: 2 -->' docs/templates/003-NEW_SESSION_PROMPT.md
grep -Fx '<!-- version: 1 -->' docs/templates/005-RELEASE_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.3.fix.1.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+PASS\s', history)
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
for phrase in (
'## REPLAN alpha.4 — lifecycle inter-release et horizon',
'## Clôture et transition',
'## Revue d\'horizon',
'### alpha.5 — audit Rust de façade/export completeness',
'### alpha.6 — premier profil framework : Tauri',
'### alpha.7 — décision de skeleton Tauri',
):
assert phrase in plan, phrase
workflow = Path('docs/rules/210-WORKFLOW.md').read_text(encoding='utf-8')
for phrase in (
'clôture et transition',
'revue d\'horizon',
'archive history de la tranche courante',
'archive delta de la tranche suivante',
'MUST remettre spontanément',
'preuve future extérieure au tag',
):
assert phrase in workflow, phrase
doc = Path('docs/rules/300-DOCUMENTATION.md').read_text(encoding='utf-8')
for phrase in (
'### Revue d\'horizon',
'Une roadmap à horizon fini est valide.',
'roadmap ou plan lorsqu\'un engagement de réalisation existe',
):
assert phrase in doc, phrase
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
assert '## Horizon après `0.1.0`' in roadmap
assert 'L\'horizon est volontairement ouvert.' in roadmap
assert '0.2.0' in roadmap # cité uniquement comme version à ne pas réserver arbitrairement
template = Path('docs/templates/005-RELEASE_PLAN.md').read_text(encoding='utf-8')
for phrase in ('## Contrat minimal', '## Clôture et transition', '## Revue d\'horizon', '## Critère de sortie'):
assert phrase in template, phrase
manual = Path('docs/templates/003-NEW_SESSION_PROMPT.md').read_text(encoding='utf-8')
assert 'doit être remis spontanément' in manual
assert 'horizon reste ouvert' in manual
print('0.0.5 alpha.4 lifecycle audit: clean (closure assigned, horizon review explicit, deterministic handoff optimized)')
PY
git diff --check
git add VERSION ROADMAP.md docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/200-GOVERNANCE.md docs/rules/210-WORKFLOW.md \
docs/rules/300-DOCUMENTATION.md \
docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md \
docs/templates/003-NEW_SESSION_PROMPT.md docs/templates/005-RELEASE_PLAN.md \
deltas/0.0.5/alpha.4.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history `alpha.4` est exactement :
```text
M ROADMAP.md
M VERSION
A deltas/0.0.5/alpha.4.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/200-GOVERNANCE.md
M docs/rules/210-WORKFLOW.md
M docs/rules/300-DOCUMENTATION.md
M docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
M docs/templates/003-NEW_SESSION_PROMPT.md
A docs/templates/005-RELEASE_PLAN.md
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.4.md`, committer/pousser `0.0.5-alpha.4`, puis poursuivre le forecast révisé à `0.0.5-alpha.5` pour l'audit Rust de façade/export completeness.
### FAIL
Rester dans `0.0.5-alpha.4.fix.M` tant que le défaut concerne le lifecycle inter-release ou la documentation livrée par cette tranche. Si la revue révèle qu'un autre chantier substantiel doit être absorbé avant l'audit Rust, refaire explicitement le REPLAN plutôt que l'introduire dans un fix.

View File

@@ -0,0 +1,217 @@
<!-- file: deltas/0.0.5/alpha.5.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.5.fix.1 — canonicalisation réelle des références crate-root
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.5`, réalisé après application de `history/0.0.5/alpha.5.md` avec statut `FAIL`.
Version avant application :
```text
0.0.5-alpha.5
```
Version après application :
```text
0.0.5-alpha.5.fix.1
```
## Cause du fix
La gate mécanique de `alpha.5` est passée, mais la revue fonctionnelle opérateur a identifié un blind spot dans `RUST-IMPORT-201` : la `version: 1` détecte `crate::module::Item` lorsque `crate::Item` existe, mais ne détecte pas un usage nu ou autrement non canonique de ce même item dans son module propriétaire ou un autre module.
Or le contrat recherché est bien :
```text
item partagé pub/pub(crate)
↓
façade crate-root
↓
toutes les références intra-crate détectées utilisent crate::Item
```
Le compilateur et Clippy ne possèdent pas cette convention. Une seule référence correcte à `crate::Item` peut rendre le réexport utilisé tout en laissant subsister d'autres `Item`, `self::Item`, `super::Item` ou chemins internes.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/510-RUST_CARGO.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
docs/validation/003-RUST_EXPORT_COMPLETENESS.md
scripts/audit_rust_export_completeness.py
tests/test_rust_export_completeness.py
```
ADD :
```text
deltas/0.0.5/alpha.5.fix.1.md
```
DELETE : aucun.
Le history `alpha.5` appartient au commit baseline et n'est pas inclus dans l'archive du présent delta.
## Décisions matérialisées
### RUST-IMPORT-201 couvre toute référence non canonique détectable
Pour un item possédant une identité crate-root, la seule forme intra-crate canonique est :
```text
crate::Item
```
Le diagnostic couvre désormais les formes suivantes lorsqu'elles désignent cet item partagé :
```text
Item
self::Item
super::Item
module::Item
crate::module::Item
```
La règle s'applique au module propriétaire lui-même : signatures, `impl`, constructeurs, appels et références de types doivent employer la façade crate-root lorsqu'ils portent l'item partagé.
Les déclarations qui créent l'item et les lignes de réexport qui construisent la façade sont exemptées. Les éléments strictement privés restent hors de cette règle ; leurs tests attachés peuvent utiliser `super::PrivateItem`.
### Targets Cargo distinctes
Le fix évite également de confondre bibliothèque et binaire d'un même package. `src/lib.rs`, `src/main.rs`, les bins standard et les targets explicites sont traités comme des crates compilées distinctes. Le graphe de modules de chaque target est suivi via `mod name;` et `#[path = "..."]`.
Cette correction est nécessaire pour ne pas qualifier à tort `owner_crate::Item` dans un binaire comme référence intra-crate de la bibliothèque du même package.
### Analyse lexicale bornée
L'audit reste read-only et standard library. Il ne prétend pas remplacer un parseur Rust :
- les commentaires et littéraux sont masqués ;
- les chemins qualifiés par une crate externe ne sont pas assimilés à l'item local homonyme ;
- un appel de méthode `.name()` n'est pas assimilé à une fonction libre locale de même nom ;
- les fonctions libres sont contrôlées dans les contextes d'appel directs ; leur usage comme valeur de premier ordre peut nécessiter une revue humaine.
### Réconciliation corrigée
En `--report-only --summary-only`, la `version: 2` observe sur les baselines de `alpha.1` :
```text
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
```
Ces nombres sont des lignes candidates à revoir, pas des déclarations distinctes. Ils ne constituent pas une non-conformité rétroactive : KSP et Games ne sont pas encore gouvernés par ce PGR. Ils montrent pourquoi une future adoption doit commencer par un inventaire `--report-only` et une migration proportionnée.
## Validation opérateur
Après application du history `FAIL` de `alpha.5`, commit/push de cette tranche, puis application du présent fix :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.5.fix.1'
test -f history/0.0.5/alpha.5.md
test -f deltas/0.0.5/alpha.5.fix.1.md
test ! -e history/0.0.5/alpha.5.fix.1.md
grep -A2 '^## Statut$' history/0.0.5/alpha.5.md | grep -Fx 'FAIL'
grep -Fx '# version: 2' scripts/audit_rust_export_completeness.py
grep -Fx '# version: 2' tests/test_rust_export_completeness.py
grep -Fx '<!-- version: 2 -->' docs/validation/003-RUST_EXPORT_COMPLETENESS.md
grep -Fx '<!-- version: 4 -->' docs/rules/510-RUST_CARGO.md
python3 -B -m unittest discover -s tests -p 'test_rust_export_completeness.py' -v
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.5.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+FAIL\s', history)
script = Path('scripts/audit_rust_export_completeness.py').read_text(encoding='utf-8')
for phrase in (
'def target_roots(',
'def target_modules(',
'def _canonical_reference_candidates(',
'RUST-API-201',
'RUST-IMPORT-201',
'non-canonical same-crate reference',
):
assert phrase in script, phrase
validation = Path('docs/validation/003-RUST_EXPORT_COMPLETENESS.md').read_text(encoding='utf-8')
for phrase in (
'Item\nself::Item\nsuper::Item\nmodule::Item\ncrate::module::Item',
'dans le module qui déclare l\'item',
'1525 candidate(s)',
'181 candidate(s)',
'pas des déclarations distinctes',
):
assert phrase in validation, phrase
rules = Path('docs/rules/510-RUST_CARGO.md').read_text(encoding='utf-8')
assert 'toute référence dans la même crate MUST utiliser `crate::Item`' in rules
assert 'y compris depuis son module de déclaration' in rules
print('0.0.5 alpha.5.fix.1 canonical Rust path audit: clean (owner module, cross-module, attached tests, distinct Cargo targets)')
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/510-RUST_CARGO.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
docs/validation/003-RUST_EXPORT_COMPLETENESS.md \
scripts/audit_rust_export_completeness.py \
tests/test_rust_export_completeness.py \
deltas/0.0.5/alpha.5.fix.1.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history du fix est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.5.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/validation/003-RUST_EXPORT_COMPLETENESS.md
M scripts/audit_rust_export_completeness.py
M tests/test_rust_export_completeness.py
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.5.fix.1.md`, committer/pousser `0.0.5-alpha.5.fix.1`, puis poursuivre à `0.0.5-alpha.6` pour le profil Tauri prévu par le plan.
### FAIL
Rester dans `0.0.5-alpha.5.fix.M`. Aucun travail Tauri ne doit être absorbé tant que le contrat de façade Rust n'est pas correctement fermé.

205
deltas/0.0.5/alpha.5.md Normal file
View File

@@ -0,0 +1,205 @@
<!-- file: deltas/0.0.5/alpha.5.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.5 — audit Rust de façade/export completeness
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.4`, réalisé après application de `history/0.0.5/alpha.4.md` avec statut `PASS`.
Le hash de ce futur commit n'est pas anticipé.
Version avant application :
```text
0.0.5-alpha.4
```
Version après application :
```text
0.0.5-alpha.5
```
## Rôle de la tranche
`alpha.5` extrait un audit Rust facultatif réellement réutilisable à partir du corpus KSP/Games, mais corrige avant généralisation une limite découverte dans le script historique : Games organise ses crates sous plusieurs niveaux et n'était pas réellement couvert par le glob `crates/*/Cargo.toml`.
La tranche reste limitée à la façade crate-root et aux chemins same-crate canoniques. Elle ne copie pas `audit_rust_general_rules.py`, ne commence pas Tauri et ne transforme pas PGR en workspace Rust.
## Scope
REPLACE :
```text
VERSION
docs/000-README.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/510-RUST_CARGO.md
docs/skeletons/002-RUST_CARGO_WORKSPACE.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
docs/validation/001-VALIDATION_MATRIX.md
```
ADD :
```text
docs/validation/003-RUST_EXPORT_COMPLETENESS.md
scripts/audit_rust_export_completeness.py
tests/test_rust_export_completeness.py
deltas/0.0.5/alpha.5.md
```
DELETE : aucun.
Le history `alpha.4` appartient au commit baseline et n'est pas inclus dans l'archive du présent delta.
## Décisions matérialisées
### Audit monotâche
Le nouveau script possède exactement deux responsabilités :
```text
RUST-API-201
façade crate-root absente pour un élément public, ou crate-public clairement partagé
RUST-IMPORT-201
chemin `crate::module::Item` alors qu'un réexport explicite fournit déjà `crate::Item`
```
Les conventions des tests unitaires séparés du script historique ne sont pas reprises. Le contrôle d'un symbole `crate::X` inexistant n'est pas repris non plus, car le compilateur Rust possède déjà la résolution de noms.
### Découverte des crates
La découverte des `Cargo.toml` devient récursive et accepte les crates imbriquées. Les arbres générés usuels (`target`, `build`, `builds`, `dist`, `node_modules`, etc.) restent ignorés.
Le module fichier `foo/mod.rs` est interprété comme `foo`, et les réexports crate-root simples, groupés ou aliasés couverts par les tests sont reconnus.
### Adoption facultative
Le contrat général Rust reste un `SHOULD`. Un projet existant peut commencer avec `--report-only`, traiter les écarts ou exceptions locales, puis rendre l'audit bloquant seulement après décision explicite.
L'audit est documenté dans `docs/validation/003-RUST_EXPORT_COMPLETENESS.md` et référencé par le skeleton Rust/Cargo sans devenir obligatoire.
### Mesure de réconciliation
La prévalidation générateur sur les baselines autoritaires de `alpha.1` observe :
```text
KSP 0.3.16 22 crate(s) 0 candidate(s)
Games 0.3.5 23 crate(s) 2 candidate(s)
```
Le résultat Games antérieur `0 candidate(s)` provenait d'une découverte de crates incomplète. Les deux candidats actuels ne sont pas traités comme des défauts historiques, car Games n'est pas encore gouverné par PGR.
## Validation opérateur
Après application du history `PASS` de `alpha.4`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.5'
test -f history/0.0.5/alpha.4.md
test -f deltas/0.0.5/alpha.5.md
test -f scripts/audit_rust_export_completeness.py
test -f tests/test_rust_export_completeness.py
test -f docs/validation/003-RUST_EXPORT_COMPLETENESS.md
test ! -e history/0.0.5/alpha.5.md
grep -A2 '^## Statut$' history/0.0.5/alpha.4.md | grep -Fx 'PASS'
grep -Fx '# version: 1' scripts/audit_rust_export_completeness.py
grep -Fx '# version: 1' tests/test_rust_export_completeness.py
grep -Fx '<!-- version: 1 -->' docs/validation/003-RUST_EXPORT_COMPLETENESS.md
grep -Fx '<!-- version: 3 -->' docs/rules/510-RUST_CARGO.md
grep -Fx '<!-- version: 2 -->' docs/skeletons/002-RUST_CARGO_WORKSPACE.md
grep -Fx '<!-- version: 11 -->' docs/validation/001-VALIDATION_MATRIX.md
python3 -B -m unittest discover -s tests -p 'test_rust_export_completeness.py' -v
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.4.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+PASS\s', history)
script = Path('scripts/audit_rust_export_completeness.py').read_text(encoding='utf-8')
for phrase in (
'def crate_roots(',
'root.rglob("Cargo.toml")',
'RUST-API-201',
'RUST-IMPORT-201',
'--report-only',
'--summary-only',
):
assert phrase in script, phrase
for forbidden in ('RUST-IMPORT-202', 'RUST-IMPORT-203', 'RUST-IMPORT-204', 'RUST-IMPORT-205'):
assert forbidden not in script, forbidden
validation = Path('docs/validation/003-RUST_EXPORT_COMPLETENESS.md').read_text(encoding='utf-8')
for phrase in (
'22` crates KSP',
'23` crates Games',
'0 candidate(s)',
'2 candidate(s)',
'ne sont pas qualifiés de défauts rétroactifs',
):
assert phrase in validation, phrase
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
assert 'KSP = 0 candidate(s) / 22 crates' in plan
assert 'Games = 2 candidate(s) / 23 crates' in plan
print('0.0.5 alpha.5 Rust façade audit: clean (recursive crate discovery, narrow ownership, regression coverage)')
PY
git diff --check
git add VERSION docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/510-RUST_CARGO.md \
docs/skeletons/002-RUST_CARGO_WORKSPACE.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
docs/validation/001-VALIDATION_MATRIX.md \
docs/validation/003-RUST_EXPORT_COMPLETENESS.md \
scripts/audit_rust_export_completeness.py \
tests/test_rust_export_completeness.py \
deltas/0.0.5/alpha.5.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history `alpha.5` est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.5.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/validation/001-VALIDATION_MATRIX.md
A docs/validation/003-RUST_EXPORT_COMPLETENESS.md
A scripts/audit_rust_export_completeness.py
A tests/test_rust_export_completeness.py
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.5.md`, committer/pousser `0.0.5-alpha.5`, puis poursuivre à `0.0.5-alpha.6` pour le premier profil framework Tauri.
### FAIL
Rester dans `0.0.5-alpha.5.fix.M` si le défaut concerne l'audit, ses tests, sa documentation ou l'intégration Rust/Cargo. Ne pas absorber le profil Tauri dans un fix.

View File

@@ -0,0 +1,178 @@
<!-- file: deltas/0.0.5/alpha.6.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.6.fix.1 — composition Tauri/Rust/frontend et gate corrigé
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.6`, réalisé après application de `history/0.0.5/alpha.6.md` avec statut `FAIL`.
Version avant application :
```text
0.0.5-alpha.6
```
Version après application :
```text
0.0.5-alpha.6.fix.1
```
## Cause du fix
Deux défauts appartiennent au scope exact de `alpha.6` :
1. le heredoc Python de la gate `alpha.6` contient une chaîne multilignes entre quotes simples et échoue avec `SyntaxError: unterminated string literal` ;
2. le profil livré ne distingue pas encore assez explicitement le contrat Tauri universel des conventions de structure/stack retenues pour les projets Tauri que l'organisation contrôle.
La clarification opérateur confirme aussi un écart plus large dans le profil Rust strict PGR. Ce troisième sujet ne doit pas être absorbé par le présent fix : il est replanifié en `alpha.7`, puis la décision de skeleton Tauri passe à `alpha.8`.
## Scope
REPLACE :
```text
VERSION
docs/architecture/003-RULE_PROFILE_MODEL.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/700-FRAMEWORKS.md
docs/rules/710-TAURI.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
ADD :
```text
deltas/0.0.5/alpha.6.fix.1.md
```
DELETE : aucun.
## Décisions corrigées
Le fix précise que :
- un host Tauri est un projet Rust/Cargo et n'obtient aucune exception implicite aux règles Rust sélectionnées ;
- un projet externe/imposé peut conserver des règles différentes via le niveau project-specific ou des overrides explicites ;
- le profil Tauri universel ne possède pas le choix `frontend/`, Vite, TypeScript, Sass, Bootstrap ou du package manager ;
- ces choix forment un candidat de skeleton pour les futurs projets contrôlés ;
- WASM n'est pas une composante implicite de Tauri et reste normalement une unité séparée avec ses propres owners ;
- `cargo tauri add` est une commodité, pas une obligation : l'ajout explicite des dépendances et de l'initialisation à partir de la documentation officielle des plugins est valide et peut être préféré pour garder le contrôle des manifests ;
- un POC mobile non concluant pour Games ne permet pas de rejeter Tauri Android/iOS pour d'autres produits.
## REPLAN borné
La clarification révèle qu'une décision Rust déjà retenue historiquement n'est pas complètement matérialisée dans `510-RUST_CARGO.md`. `alpha.7` devient donc une tranche normale de complétude du profil Rust strict, puis l'ancienne décision de skeleton Tauri devient `alpha.8`.
Le présent fix ne modifie pas `510-RUST_CARGO.md` et n'ajoute aucun nouvel audit Rust : cela constituerait un nouveau scope fonctionnel.
## Validation opérateur
Après application du history `FAIL` de `alpha.6`, commit/push de cette tranche, puis application du présent fix :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.6.fix.1'
test -f history/0.0.5/alpha.6.md
test -f deltas/0.0.5/alpha.6.fix.1.md
test ! -e history/0.0.5/alpha.6.fix.1.md
grep -A2 '^## Statut$' history/0.0.5/alpha.6.md | grep -Fx 'FAIL'
grep -Fx '<!-- version: 2 -->' docs/rules/700-FRAMEWORKS.md
grep -Fx '<!-- version: 2 -->' docs/rules/710-TAURI.md
grep -Fx '<!-- version: 5 -->' docs/architecture/003-RULE_PROFILE_MODEL.md
grep -Fx '<!-- version: 5 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 6 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.6.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+FAIL\s', history)
frameworks = Path('docs/rules/700-FRAMEWORKS.md').read_text(encoding='utf-8')
for phrase in (
"Il n'annule pas silencieusement les conventions des owners qu'il compose.",
'Un projet existant ou imposé de l'extérieur MAY conserver des contraintes différentes.',
'## Profil framework et convention d'instanciation',
):
assert phrase in frameworks, phrase
tauri = Path('docs/rules/710-TAURI.md').read_text(encoding='utf-8')
for phrase in (
'## Héritage Rust du host Tauri',
'Tauri ne crée aucune exception implicite aux règles Rust sélectionnées par le projet.',
'PGR MUST NOT imposer universellement Vite, TypeScript, Sass, Bootstrap, Tailwind CSS',
'WASM n'est pas une composante implicite d'un projet Tauri.',
'PGR ne définit aucun catalogue de plugins Tauri obligatoires et ne rend pas `cargo tauri add` obligatoire.',
'Un POC Tauri mobile non concluant pour un produit particulier ne constitue pas une preuve',
):
assert phrase in tauri, phrase
version_chain = '\n'.join((
'VERSION',
'↓',
'workspace.package.version',
'↓',
'package Cargo du host',
'↓',
'version applicative Tauri',
))
assert version_chain in tauri
study = Path('docs/studies/001-KSP_GAMES_RECONCILIATION.md').read_text(encoding='utf-8')
assert 'candidat de skeleton pour projets contrôlés' in study
assert 'WASM n'est pas une dépendance implicite de Tauri' in study
assert 'écart antérieur dans le profil Rust PGR' in study
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
assert '### alpha.7 — complétude du profil Rust strict' in plan
assert '### alpha.8 — décision de skeleton Tauri pour projets contrôlés' in plan
print('0.0.5 alpha.6.fix.1 Tauri composition audit: clean (Rust inherited, frontend/skeleton separated, WASM independent, gate valid)')
PY
git diff --check
git add VERSION \
docs/architecture/003-RULE_PROFILE_MODEL.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/700-FRAMEWORKS.md docs/rules/710-TAURI.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
deltas/0.0.5/alpha.6.fix.1.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.1.md
M docs/architecture/003-RULE_PROFILE_MODEL.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/700-FRAMEWORKS.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.6.fix.1.md`, committer/pousser le fix, puis poursuivre à `0.0.5-alpha.7` pour réconcilier les conventions Rust strictes déjà décidées avant toute matérialisation d'un skeleton Tauri.
### FAIL
Rester dans `0.0.5-alpha.6.fix.M`. Aucun changement du profil Rust ni skeleton Tauri ne doit être absorbé tant que la frontière Tauri/Rust/frontend n'est pas correctement fermée.

View File

@@ -0,0 +1,167 @@
<!-- file: deltas/0.0.5/alpha.6.fix.2.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.6.fix.2 — convention Tauri contrôlée et gate robuste
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.6.fix.1`, réalisé après application de `history/0.0.5/alpha.6.fix.1.md` avec statut `FAIL`.
Version avant application :
```text
0.0.5-alpha.6.fix.1
```
Version après application :
```text
0.0.5-alpha.6.fix.2
```
## Cause du fix
`alpha.6.fix.1` conserve une gate Python invalide : une phrase contenant `l'extérieur` est placée dans une chaîne simple et son apostrophe provoque encore `SyntaxError: unterminated string literal`.
La revue opérateur demande en parallèle de confronter le profil non seulement aux configurations Tauri mais aux règles complètes KSP/Games. Cette seconde passe montre qu'une convention de projet contrôlé plus riche est déjà éprouvée et doit être distinguée du contrat Tauri universel.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/710-TAURI.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
ADD :
```text
deltas/0.0.5/alpha.6.fix.2.md
```
DELETE : aucun.
## Décisions
Le fix établit pour les nouveaux projets Tauri contrôlés une convention forte mais adaptable :
- host Cargo direct, sans `src-tauri/` ajouté par réflexe ;
- fichiers Tauri/frontend principaux à la racine de la crate et sources Rust sous `src/` ;
- frontend versionné sous `frontend/` ;
- `lib.rs` comme façade, `main.rs` comme launcher mince et `tauri.rs` comme owner de l'assemblage et des wrappers `#[tauri::command]` ;
- modules cohérents par fenêtre/responsabilité et modules communs pour les helpers réellement partagés ;
- TS-RS obligatoire dans le profil strict lorsque des DTO/enum/struct Rust possédés par le host sont partagés avec un frontend TypeScript, sauf incompatibilité documentée ;
- bindings générés reconstruisibles et non modifiés manuellement ;
- intégration explicite des plugins préférée à une commande de commodité opaque ;
- WASM séparé du host Tauri par défaut.
Vite, TypeScript, Sass et Bootstrap restent le candidat de stack/skeleton pour les projets contrôlés ; ils ne deviennent pas des invariants universels du framework Tauri tant que leurs owners Web ne sont pas matérialisés.
## Replanification
La seconde lecture des règles KSP/Games montre que `alpha.7` serait trop large si elle mélangeait conventions sémantiques, nomenclature et tous les audits de formatage. Le forecast devient :
```text
alpha.7 profil Rust strict + chemins + nomenclature des crates
alpha.8 formatage Rust structurel + audits monotâches
alpha.9 décision de skeleton Tauri pour projets contrôlés
beta.1 réconciliation intégrée
```
KSP est utilisé comme référence stricte ; Games sert à identifier ce qui doit rester `SHOULD`, sélectionnable ou project-specific.
## Validation opérateur
Après application du history `FAIL` de `alpha.6.fix.1`, commit/push de cette tranche, puis application du présent fix :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.6.fix.2'
test -f history/0.0.5/alpha.6.fix.1.md
test -f deltas/0.0.5/alpha.6.fix.2.md
test ! -e history/0.0.5/alpha.6.fix.2.md
grep -A2 '^## Statut$' history/0.0.5/alpha.6.fix.1.md | grep -Fx 'FAIL'
grep -Fx '<!-- version: 3 -->' docs/rules/710-TAURI.md
grep -Fx '<!-- version: 6 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 7 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.6.fix.1.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+FAIL\s", history)
tauri = Path("docs/rules/710-TAURI.md").read_text(encoding="utf-8")
for phrase in (
"## Convention renforcée pour les projets contrôlés",
"sans couche `src-tauri/` ajoutée uniquement pour reproduire `create-tauri-app`",
"`src/tauri.rs` SHOULD posséder l'assemblage Tauri et les wrappers `#[tauri::command]`",
"partagés avec le frontend MUST utiliser TS-RS comme source de génération",
"WASM reste normalement une unité séparée de la crate Tauri",
):
assert phrase in tauri, phrase
study = Path("docs/studies/001-KSP_GAMES_RECONCILIATION.md").read_text(encoding="utf-8")
for phrase in (
"Commun aux `8` hosts Tauri observés",
"aucun ne possède `src-tauri/`",
"KSP plus strict mais réutilisable",
"Nomenclature des crates",
):
assert phrase in study, phrase
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
for phrase in (
"### alpha.7 — profil Rust strict, chemins et nomenclature explicite",
"### alpha.8 — formatage Rust structurel et audits monotâches",
"### alpha.9 — décision de skeleton Tauri pour projets contrôlés",
):
assert phrase in plan, phrase
print("0.0.5 alpha.6.fix.2 Tauri convention audit: clean (8 hosts reconciled, strict controlled layout separated from universal framework)")
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/710-TAURI.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
deltas/0.0.5/alpha.6.fix.2.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.2.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.6.fix.2.md`, committer/pousser le fix, puis poursuivre avec `alpha.7`.
### FAIL
Rester dans `0.0.5-alpha.6.fix.M`. Ne pas commencer la réconciliation Rust tant que la convention Tauri et sa gate ne sont pas fermées proprement.

View File

@@ -0,0 +1,140 @@
<!-- file: deltas/0.0.5/alpha.6.fix.3.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.6.fix.3 — politique de versions Tauri/frontend
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.6.fix.2`, réalisé après application de `history/0.0.5/alpha.6.fix.2.md` avec statut `FAIL`.
Version après application :
```text
0.0.5-alpha.6.fix.3
```
## Motif
La gate mécanique de `alpha.6.fix.2` est propre, mais la revue fonctionnelle identifie une convention mal généralisée : KSP et Games ont tous deux utilisé l'omission de `tauri.conf.json > version` et une `package.json > version` rarement synchronisée comme moyen de réduire le churn des prereleases. PGR ne doit pas transformer cette solution historique en obligation.
L'objectif réel est de conserver `VERSION` comme identité exacte de travail tout en évitant de modifier les miroirs produit qui n'ont pas besoin de suivre chaque `alpha`, `.fix`, beta ou RC.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/200-GOVERNANCE.md
docs/rules/710-TAURI.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
ADD :
```text
deltas/0.0.5/alpha.6.fix.3.md
```
DELETE : aucun.
## Décisions
Le fix rétablit une classification explicite des valeurs de version :
- `CANONICAL` pour `VERSION` ;
- `MIRROR-EXACT` pour les miroirs techniques qui doivent suivre chaque prerelease ;
- `MIRROR-STABLE` pour les champs qui portent seulement la cible `X.Y.Z` ;
- catégories indépendantes pour compteurs, schémas et révisions documentaires.
Pour une application Tauri contrôlée appartenant au même produit, `tauri.conf.json > version` et `package.json > version`, lorsqu'ils sont conservés, SHOULD être `MIRROR-STABLE` par défaut et porter `X.Y.Z` pendant tout le développement de cette cible. Ils ne sont pas modifiés uniquement pour passer de `alpha.N` à un autre alpha/fix/beta/RC.
Si des bundles prerelease sont réellement distribués et doivent être identifiables séparément, le projet MAY sélectionner `MIRROR-EXACT` ou une politique de jalons de packaging documentée. L'omission d'un champ reste possible lorsqu'il est réellement inutile, mais elle n'est plus la stratégie PGR recommandée uniquement pour éviter le churn.
La revue obligatoire déclenchée par `.fix.3` conclut `CONTINUE` : `alpha.6` reste bornée au profil Tauri ; `alpha.7`, `alpha.8` et `alpha.9` conservent leurs scopes distincts.
## Hors scope
Ce fix ne modifie ni les règles Rust strictes prévues en `alpha.7`, ni les audits de formatage de `alpha.8`, ni le skeleton Tauri de `alpha.9`. Il ne modifie pas non plus la politique Cargo existante de `510-RUST_CARGO.md`.
## Validation opérateur
Après application du history `FAIL` de `alpha.6.fix.2`, commit/push de cette tranche, puis application du présent fix :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.6.fix.3'
test -f history/0.0.5/alpha.6.fix.2.md
test -f deltas/0.0.5/alpha.6.fix.3.md
test ! -e history/0.0.5/alpha.6.fix.3.md
grep -A2 '^## Statut$' history/0.0.5/alpha.6.fix.2.md | grep -Fx 'FAIL'
grep -Fx '<!-- version: 4 -->' docs/rules/200-GOVERNANCE.md
grep -Fx '<!-- version: 4 -->' docs/rules/710-TAURI.md
grep -Fx '<!-- version: 7 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 8 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.6.fix.2.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+FAIL\s", history)
governance = Path("docs/rules/200-GOVERNANCE.md").read_text(encoding="utf-8")
for phrase in (
"MIRROR-EXACT",
"MIRROR-STABLE",
"Seul un `MIRROR-EXACT` est normalement modifié à chaque changement de `VERSION`.",
"PGR MUST NOT imposer la suppression d'un champ utile uniquement pour éviter de le modifier à chaque prerelease",
):
assert phrase in governance, phrase
tauri = Path("docs/rules/710-TAURI.md").read_text(encoding="utf-8")
for phrase in (
"PGR MUST NOT imposer l'une de ces deux formes uniquement pour réduire le nombre de fichiers modifiés.",
"la politique recommandée lorsque les prereleases ne sont pas distribuées comme produits installables distincts est `MIRROR-STABLE`",
"tauri.conf.json version = X.Y.Z",
"package.json version = X.Y.Z",
"remplace explicitement les conventions historiques KSP/Games",
):
assert phrase in tauri, phrase
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
assert "`alpha.6.fix.3` restaure la classification générale" in plan
assert "résultat `CONTINUE`" in plan
print("0.0.5 alpha.6.fix.3 version policy audit: clean (canonical/exact/stable mirrors classified, Tauri/frontend churn bounded)")
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/200-GOVERNANCE.md docs/rules/710-TAURI.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
deltas/0.0.5/alpha.6.fix.3.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.3.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/200-GOVERNANCE.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```

189
deltas/0.0.5/alpha.6.md Normal file
View File

@@ -0,0 +1,189 @@
<!-- file: deltas/0.0.5/alpha.6.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.6 — premier profil framework : Tauri 2
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.5.fix.1`, réalisé après application de `history/0.0.5/alpha.5.fix.1.md` avec statut `PASS`.
Version avant application :
```text
0.0.5-alpha.5.fix.1
```
Version après application :
```text
0.0.5-alpha.6
```
## Scope
REPLACE :
```text
VERSION
RULES.md
docs/000-README.md
docs/architecture/003-RULE_PROFILE_MODEL.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/000-README.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
ADD :
```text
docs/rules/700-FRAMEWORKS.md
docs/rules/710-TAURI.md
deltas/0.0.5/alpha.6.md
```
DELETE : aucun.
Le history `alpha.5.fix.1` appartient au commit baseline et n'est pas inclus dans l'archive du présent delta.
## Preuve de généralisation
Les baselines autoritaires étudiées en `alpha.1` contiennent :
```text
KSP 0.3.16 6 applications Tauri
Games 0.3.5 2 applications Tauri
```
Les huit applications utilisent Tauri 2 avec un host Rust/Cargo, `tauri-build`, `tauri.conf.json`, des capability files, des hooks frontend et un `frontendDist`. Elles divergent en revanche sur fenêtres, ports, CSP, bundling, plugins, ressources et contenu réel des hooks.
Le profil normalise donc les responsabilités et l'orchestration communes, pas ces valeurs locales.
## Décisions matérialisées
### Famille `700 — frameworks`
`700-FRAMEWORKS.md` devient l'owner des règles communes aux frameworks : composition avec les owners langage/plateforme, ownership des commandes d'intégration, classification des artefacts générés, cohérence de version produit et validation proportionnée.
La famille interdit notamment de supposer qu'un framework absorbe automatiquement son frontend, sa plateforme ou son package manager.
### Profil `710 — Tauri 2`
Le profil Tauri :
- se compose au-dessus de Rust/Cargo et des plateformes réellement ciblées ;
- ne rend pas Vite, WASM, TypeScript ou npm obligatoires ;
- ne rend pas `src-tauri/` obligatoire lorsque la toolchain supporte clairement un autre layout ;
- possède l'orchestration via le CLI Tauri lorsque les hooks/config/capabilities/bundles sont concernés ;
- distingue un check Rust, un build frontend et une preuve d'intégration Tauri ;
- préfère une chaîne de version unique `VERSION → workspace Cargo → package host → Tauri` lorsque l'application partage le release train et que la configuration Tauri peut omettre sa propre version ;
- traite les capability files comme source de sécurité intentionnelle ;
- ne généralise ni plugins, CSP, fenêtres, bundles, ports ni ressources des corpus ;
- ne généralise pas l'ignorance de tout `gen/`, car un projet mobile généré peut recevoir des personnalisations intentionnelles ;
- sélectionne `tauri dev/build` ou leur équivalent seulement lorsque la propriété à prouver le justifie.
### Pas de skeleton dans cette tranche
`alpha.6` ne crée aucun `tauri.conf.json`, package frontend, template Tauri ou skeleton. La décision de matérialiser une specialization Tauri appartient explicitement à `alpha.7`.
## Validation opérateur
Après application du history `PASS` de `alpha.5.fix.1`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.6'
test -f history/0.0.5/alpha.5.fix.1.md
test -f deltas/0.0.5/alpha.6.md
test -f docs/rules/700-FRAMEWORKS.md
test -f docs/rules/710-TAURI.md
test ! -e history/0.0.5/alpha.6.md
grep -A2 '^## Statut$' history/0.0.5/alpha.5.fix.1.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 11 -->' RULES.md
grep -Fx '<!-- version: 13 -->' docs/rules/000-README.md
grep -Fx '<!-- version: 4 -->' docs/architecture/003-RULE_PROFILE_MODEL.md
grep -Fx '<!-- version: 1 -->' docs/rules/700-FRAMEWORKS.md
grep -Fx '<!-- version: 1 -->' docs/rules/710-TAURI.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path('history/0.0.5/alpha.5.fix.1.md').read_text(encoding='utf-8')
assert re.search(r'## Statut\s+PASS\s', history)
frameworks = Path('docs/rules/700-FRAMEWORKS.md').read_text(encoding='utf-8')
for phrase in (
'# Règles de profils framework',
"Ownership des commandes d'intégration",
'PGR MUST NOT appliquer une règle globale « tout `gen/` est ignoré »',
'Les profils spécifiques utilisent la plage `710` à `799`.',
):
assert phrase in frameworks, phrase
tauri = Path('docs/rules/710-TAURI.md').read_text(encoding='utf-8')
for phrase in (
'# Profil Tauri 2',
'PGR MUST NOT imposer Vite, TypeScript, npm, pnpm, yarn, Bun, WASM',
"le CLI Tauri est l'owner de l'orchestration",
'VERSION
↓
workspace.package.version
↓
package Cargo du host
↓
version applicative Tauri',
'PGR MUST NOT appliquer en revanche une exclusion universelle de tout répertoire `gen/`',
'## Validation proportionnée',
'de `tauri.conf.json` universel',
):
assert phrase in tauri, phrase
study = Path('docs/studies/001-KSP_GAMES_RECONCILIATION.md').read_text(encoding='utf-8')
assert 'KSP contient `6` applications Desk Tauri et Games `2` hôtes Tauri' in study
assert 'Décision `alpha.6` : classer Tauri `COMMON`' in study
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
assert '`6` applications Tauri KSP et `2` applications Tauri Games' in plan
assert 'La tranche active `700-FRAMEWORKS.md` et `710-TAURI.md`.' in plan
print('0.0.5 alpha.6 Tauri profile audit: clean (700 activated, Tauri 2 responsibilities bounded, no frontend/layout overreach)')
PY
git diff --check
git add VERSION RULES.md docs/000-README.md docs/architecture/003-RULE_PROFILE_MODEL.md docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md docs/rules/000-README.md docs/rules/700-FRAMEWORKS.md docs/rules/710-TAURI.md docs/studies/001-KSP_GAMES_RECONCILIATION.md deltas/0.0.5/alpha.6.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history `alpha.6` est exactement :
```text
M RULES.md
M VERSION
A deltas/0.0.5/alpha.6.md
M docs/000-README.md
M docs/architecture/003-RULE_PROFILE_MODEL.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/000-README.md
A docs/rules/700-FRAMEWORKS.md
A docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Transition
### PASS
Créer/appliquer `history/0.0.5/alpha.6.md`, committer/pousser `0.0.5-alpha.6`, puis poursuivre à `0.0.5-alpha.7` pour décider si une skeleton specification Tauri apporte réellement une valeur de composition au-dessus des profils existants.
### FAIL
Rester dans `0.0.5-alpha.6.fix.M` ou REPLAN si le profil exige encore une valeur applicative arbitraire. Aucun skeleton Tauri ne doit être créé tant que le profil framework n'est pas correctement fermé.

View File

@@ -0,0 +1,186 @@
<!-- file: deltas/0.0.5/alpha.7.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.7.fix.1 — contrats de suffixes, packaging Rust et tracing
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.7`, réalisé après application de `history/0.0.5/alpha.7.md` avec statut `FAIL`.
Version après application :
```text
0.0.5-alpha.7.fix.1
```
## Motif
La gate mécanique de `alpha.7` est propre, mais la revue fonctionnelle opérateur révèle que la nomenclature livrée reste trop descriptive. Dans les projets contrôlés, les suffixes doivent également porter un contrat architectural : `-api` désigne une frontière de contrats réellement justifiée, `-lib` une implémentation/fonctionnalité réutilisable, tandis que les exécutables restent normalement des packages distincts et minces.
La relecture ciblée des corpus confirme plusieurs éléments réutilisables :
- KSP distingue explicitement `ksp-<domain>-api` et `ksp-<role>-lib`, interdit une crate `*-api` créée uniquement par symétrie et garde les APIs purement déclaratives sans logging runtime ;
- Games sépare explicitement la crate lib de jeu et ses launchers ;
- KSP et Games convergent sur `tracing`, la composition du subscriber et une crate transverse de logging ;
- les six hosts Tauri KSP et les deux hosts Tauri Games observés utilisent tous un package mixte lib/bin avec des noms de targets distincts.
Une ancienne règle KSP préférait un package worker mixte lib/bin. Cette préférence n'est pas généralisée : la décision PGR actuelle est la séparation par défaut, Tauri restant l'exception framework explicitement reconnue.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/510-RUST_CARGO.md
docs/rules/710-TAURI.md
docs/skeletons/002-RUST_CARGO_WORKSPACE.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
docs/templates/002-CARGO_MANIFESTS.md
```
ADD :
```text
deltas/0.0.5/alpha.7.fix.1.md
```
DELETE : aucun.
## Décisions
Pour une nouvelle baseline Rust contrôlée :
- `-api` signifie contrat extensible/passif ; une crate `-api` n'est créée que si une séparation réelle est justifiée par extension, backend, lifecycle, implémentations multiples ou implémentation externe ;
- une crate `-api` purement déclarative reste normalement sans logging/tracing runtime ;
- lorsqu'une seule implémentation est utile et qu'aucune frontière séparée n'apporte de valeur, une seule crate `-lib` est préférée ;
- `-lib` signifie implémentation ou fonctionnalité réutilisable et ne contient normalement pas de cible binaire ;
- hors exception explicite de framework/plateforme, une bibliothèque réutilisable et son exécutable sont des packages Cargo distincts ;
- l'exécutable porte un rôle lisible lorsqu'il existe (`cli`, `desk`, `worker`, `job`, `server`, etc.), dépend des libs nécessaires et conserve `main.rs` comme couche de bootstrap/composition mince ;
- Tauri reste l'exception framework actuelle au package séparé lib/bin ; son profil impose alors des noms de targets réellement distincts, y compris après normalisation Cargo `-` -> `_`.
Pour les diagnostics :
- `tracing` est le mécanisme recommandé pour les nouveaux projets Rust contrôlés ;
- `log` reste acceptable comme bridge de compatibilité mais n'est pas la façade par défaut sans contrainte externe ;
- lorsqu'un workspace possède plusieurs runtimes partageant subscriber, filtres, sinks ou lifecycle, une crate transverse de logging/tracing SHOULD centraliser cette responsabilité ;
- `tracing-subscriber` et `tracing-appender` appartiennent normalement à cet owner de composition ;
- une bibliothèque métier/technique peut émettre des événements mais ne configure pas silencieusement le subscriber global.
## Hors scope
Ce correctif ne traite toujours pas l'ordre des blocs, le regroupement par visibilité, l'espacement interne, l'ordre alphabétique des items ou les audits de forme : ces sujets restent réservés à `alpha.8`.
Il ne crée aucun nouvel audit Python et ne transforme pas le profil Rust en catalogue exhaustif de suffixes. Les vocabulaires métier exacts restent locaux.
## Validation opérateur
Après application du history `FAIL` de `alpha.7`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.7.fix.1'
test -f history/0.0.5/alpha.7.md
test -f deltas/0.0.5/alpha.7.fix.1.md
test ! -e history/0.0.5/alpha.7.fix.1.md
grep -A2 '^## Statut$' history/0.0.5/alpha.7.md | grep -Fx 'FAIL'
grep -Fx '<!-- version: 6 -->' docs/rules/510-RUST_CARGO.md
grep -Fx '<!-- version: 5 -->' docs/rules/710-TAURI.md
grep -Fx '<!-- version: 4 -->' docs/skeletons/002-RUST_CARGO_WORKSPACE.md
grep -Fx '<!-- version: 3 -->' docs/templates/002-CARGO_MANIFESTS.md
grep -Fx '<!-- version: 9 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 10 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.7.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+FAIL\s", history)
rules = Path("docs/rules/510-RUST_CARGO.md").read_text(encoding="utf-8")
for phrase in (
"### Les suffixes portent un contrat architectural",
"Une crate `-api` MUST NOT être créée uniquement pour obtenir une paire de noms symétrique.",
"Lorsqu'une seule implémentation est réellement prévue",
"### Séparation bibliothèques / exécutables",
"Tauri constitue l'exception framework actuellement reconnue par PGR",
"## Diagnostics structurés et ownership du logging",
"`tracing` est le mécanisme recommandé",
"SHOULD NOT installer silencieusement un subscriber global",
):
assert phrase in rules, phrase
tauri = Path("docs/rules/710-TAURI.md").read_text(encoding="utf-8")
for phrase in (
"le package mixte `lib + bin` constitue l'exception framework explicite",
"leurs noms MUST être distincts même après la normalisation Cargo usuelle des tirets en underscores",
"project_feature_desk_lib",
"Un plugin Tauri de tracing sert alors d'adapter",
):
assert phrase in tauri, phrase
skeleton = Path("docs/skeletons/002-RUST_CARGO_WORKSPACE.md").read_text(encoding="utf-8")
for phrase in (
"les frontières `-api` / `-lib` réellement justifiées",
"Les exécutables sont des packages distincts des bibliothèques",
"crate transverse de logging/tracing",
):
assert phrase in skeleton, phrase
template = Path("docs/templates/002-CARGO_MANIFESTS.md").read_text(encoding="utf-8")
assert "une bibliothèque réutilisable et son exécutable sont normalement deux packages Cargo distincts" in template
assert "Tauri est une exception framework" in template
study = Path("docs/studies/001-KSP_GAMES_RECONCILIATION.md").read_text(encoding="utf-8")
for phrase in (
"#### Correction `alpha.7.fix.1` — suffixes, packaging et tracing",
"KSP-WORKER-014",
"les `6` hosts Tauri KSP `0.3.16` et les `2` hosts Tauri Games `0.3.5`",
"PGR retient donc `tracing` par défaut",
):
assert phrase in study, phrase
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
assert "`alpha.7.fix.1` complète donc la tranche" in plan
assert "### alpha.8 — formatage Rust structurel et audits monotâches" in plan
print("0.0.5 alpha.7.fix.1 crate contract audit: clean (api/lib semantics, binary split, tracing owner, Tauri exception)")
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/510-RUST_CARGO.md docs/rules/710-TAURI.md \
docs/skeletons/002-RUST_CARGO_WORKSPACE.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
docs/templates/002-CARGO_MANIFESTS.md \
deltas/0.0.5/alpha.7.fix.1.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.7.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/rules/710-TAURI.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/templates/002-CARGO_MANIFESTS.md
```

175
deltas/0.0.5/alpha.7.md Normal file
View File

@@ -0,0 +1,175 @@
<!-- file: deltas/0.0.5/alpha.7.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.7 — profil Rust strict, chemins et nomenclature explicite
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.6.fix.3`, réalisé après application de `history/0.0.5/alpha.6.fix.3.md` avec statut `PASS`.
Version après application :
```text
0.0.5-alpha.7
```
## Motif
La réconciliation Tauri a confirmé qu'un host contrôlé hérite du profil Rust au lieu de redéfinir ses conventions. La relecture des règles autoritaires KSP/Games révèle alors un écart PGR plus ancien : la conception initiale avait retenu un profil de forte explicité, mais `510-RUST_CARGO.md` matérialisé conservait encore une politique permissive sur les `use` non-traits et ne formalisait pas la nomenclature des crates.
KSP `RULES_RUST.md` version `10` fournit la référence stricte : Rust 2024, `use` réservé aux traits, chemins externes publics explicites et courts, réexports internes `self::`, façade crate-root, `crate::Item`, absence d'alias/globs et contrôle de flux explicite. Games `RULES_RUST.md` version `7` confirme le noyau Rust 2024/façade/erreurs mais reste plus léger sur certains imports et règles de forme. `alpha.7` retient donc le profil strict comme défaut des **nouvelles baselines contrôlées** tout en conservant des overrides explicites pour les projets externes ou historiques.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/510-RUST_CARGO.md
docs/skeletons/002-RUST_CARGO_WORKSPACE.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
docs/templates/002-CARGO_MANIFESTS.md
```
ADD :
```text
deltas/0.0.5/alpha.7.md
```
DELETE : aucun.
## Décisions
Pour une nouvelle baseline Rust contrôlée :
- Rust 2024 devient la baseline normative actuelle ; une contrainte externe doit être documentée explicitement ;
- `use` est réservé aux traits qui doivent réellement être mis en scope ; les autres symboles sont appelés par chemins explicites ;
- un symbole externe utilise le chemin public le plus court exposé par sa crate ;
- les réexports internes utilisent `self::`, restent explicites ligne par ligne et ne masquent pas les collisions par alias ;
- les glob imports, regroupements par accolades de façade et imports non-traits de commodité sont interdits ;
- les modules d'implémentation restent privés ; les items partagés passent par la façade crate-root et `crate::Item`, y compris dans leur module propriétaire ;
- `pub(super)`/`pub(in ...)` ne remplacent pas cette façade dans une baseline contrôlée ;
- tout `pub`/`pub(crate)` et tout réexport crate-root porte une rustdoc utile ;
- `unwrap`, `expect` et `?` sont interdits en production dans le profil strict ; la propagation d'erreur et les retours restent explicites.
Le marqueur historique KSP `rust-rules: trait-import` n'est pas généralisé : il appartient à l'enforcement local, pas à la règle sémantique.
Pour la nomenclature des crates d'un nouveau projet contrôlé :
```text
<project>-<responsibility>-<role-or-target>
```
est la forme recommandée. Le préfixe projet/abréviation est obligatoire ; le classificateur (`lib`, `api`, `worker`, `job`, `cli`, `desk`, `desktop`, `tauri`, `wasm`, `server`, etc.) est utilisé lorsqu'il apporte une information réelle. Le vocabulaire exact reste local, aucun suffixe n'est ajouté par symétrie et aucun projet historique n'est renommé rétroactivement uniquement pour adopter cette grammaire.
Le skeleton Rust/Cargo et le guide des manifests adoptent ces choix pour les futures baselines contrôlées. L'audit de façade déjà livré devient une sélection recommandée du skeleton strict ; l'adoption d'un projet existant commence toujours en `--report-only`.
## Hors scope
Cette tranche ne modifie pas les règles de blocs homogènes, ordre alphabétique, espacement, regroupement par visibilité ou organisation structurelle des items : elles restent réservées à `alpha.8` avec leurs audits monotâches.
Elle n'importe pas `audit_rust_general_rules.py` depuis KSP/Games, ne crée aucun parser Rust général et ne modifie pas le skeleton Tauri prévu en `alpha.9`.
## Validation opérateur
Après application du history `PASS` de `alpha.6.fix.3`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.7'
test -f history/0.0.5/alpha.6.fix.3.md
test -f deltas/0.0.5/alpha.7.md
test ! -e history/0.0.5/alpha.7.md
grep -A2 '^## Statut$' history/0.0.5/alpha.6.fix.3.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 5 -->' docs/rules/510-RUST_CARGO.md
grep -Fx '<!-- version: 3 -->' docs/skeletons/002-RUST_CARGO_WORKSPACE.md
grep -Fx '<!-- version: 2 -->' docs/templates/002-CARGO_MANIFESTS.md
grep -Fx '<!-- version: 8 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 9 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.6.fix.3.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+PASS\s", history)
rules = Path("docs/rules/510-RUST_CARGO.md").read_text(encoding="utf-8")
for phrase in (
"nouveau projet Rust contrôlé MUST utiliser l'édition Rust 2024",
"### Nomenclature des crates dans un projet contrôlé",
"<project>-<responsibility>-<role-or-target>",
"### `use` réservé aux traits",
"le code utilise directement le **chemin public le plus court**",
"Les réexports d'éléments internes commencent par `self::`",
"toute référence dans la même crate MUST utiliser `crate::Item`",
"`unwrap()` et `expect()` MUST NOT être utilisés",
"l'opérateur `?` MUST NOT être utilisé",
):
assert phrase in rules, phrase
for forbidden in (
"PGR ne généralise pas l'interdiction KSP plus stricte de tous les `use` non-traits",
"le profil de façade crate-root est seulement facultatif pour une nouvelle baseline contrôlée",
):
assert forbidden not in rules, forbidden
skeleton = Path("docs/skeletons/002-RUST_CARGO_WORKSPACE.md").read_text(encoding="utf-8")
for phrase in (
"nom court/abréviation canonique du projet",
"édition Rust 2024",
"façade crate-root et les chemins explicites sont sélectionnés par défaut",
"audit_rust_export_completeness.py",
):
assert phrase in skeleton, phrase
study = Path("docs/studies/001-KSP_GAMES_RECONCILIATION.md").read_text(encoding="utf-8")
assert "#### Décision `alpha.7` — profil Rust strict et nomenclature" in study
assert "KSP `RULES_RUST.md` version `10`" in study
assert "Games `RULES_RUST.md` version `7`" in study
assert "Les règles de blocs homogènes" in study
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
assert "La réconciliation des règles versionnées KSP/Games retient KSP comme référence stricte" in plan
assert "### alpha.8 — formatage Rust structurel et audits monotâches" in plan
assert "### alpha.9 — décision de skeleton Tauri pour projets contrôlés" in plan
print("0.0.5 alpha.7 strict Rust profile audit: clean (explicit paths, crate-root facade, controlled naming, alpha.8 formatting deferred)")
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/510-RUST_CARGO.md \
docs/skeletons/002-RUST_CARGO_WORKSPACE.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
docs/templates/002-CARGO_MANIFESTS.md \
deltas/0.0.5/alpha.7.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.7.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/templates/002-CARGO_MANIFESTS.md
```

248
deltas/0.0.5/alpha.8.md Normal file
View File

@@ -0,0 +1,248 @@
<!-- file: deltas/0.0.5/alpha.8.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.8 — formatage Rust structurel et audits monotâches
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.7.fix.1`, réalisé après application de `history/0.0.5/alpha.7.fix.1.md` avec statut `PASS`.
Version après application :
```text
0.0.5-alpha.8
```
## Mission
Fermer le chantier de réconciliation du formatage Rust structurel sans importer les anciens `audit_rust_general_rules.py` composites de KSP/Games.
La tranche attribue chaque propriété à un owner identifiable : Rustfmt lorsqu'il sait réellement réécrire la forme, audits PGR monotâches lorsqu'une structure stable n'est pas possédée par l'outil officiel, et revue humaine lorsque l'ordre peut être métier/protocolaire.
## Scope
REPLACE :
```text
VERSION
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/rules/510-RUST_CARGO.md
docs/skeletons/002-RUST_CARGO_WORKSPACE.md
docs/studies/001-KSP_GAMES_RECONCILIATION.md
docs/validation/001-VALIDATION_MATRIX.md
docs/validation/002-RUST_STYLE_AUDITS.md
tests/test_rust_audits.py
```
ADD :
```text
deltas/0.0.5/alpha.8.md
scripts/audit_rust_item_spacing.py
scripts/audit_rust_module_order.py
scripts/audit_rust_reexport_blocks.py
scripts/audit_rust_trait_import_blocks.py
```
DELETE : aucun.
## Décisions de formatage
Pour une nouvelle baseline Rust contrôlée sélectionnant le profil strict :
- les lignes vides consécutives et les lignes vides dans fonctions/méthodes/`struct`/`enum` sont interdites ;
- deux items Rust distincts voisins d'un même scope sont séparés par exactement une ligne vide lorsque rien d'intermédiaire ne possède lui-même l'espacement ;
- les blocs compacts `const` restent sous un owner séparé et ne sont pas interprétés par l'audit d'espacement général ;
- les modules de production précèdent les modules de tests et chaque groupe est alphabétique lorsque cet ordre est sans sémantique ;
- les `use` non exportés autorisés par le profil strict restent dans un bloc module-level compact avant les déclarations métier ;
- les `pub use` précèdent les `pub(crate) use`, sans ligne vide interne et avec exactement une ligne vide entre les deux visibilités lorsqu'elles coexistent ;
- Rustfmt reste l'owner du tri intra-bloc des `use`, réexports et modules qu'il sait réécrire ; les audits PGR ne créent pas un tri concurrent.
PGR conserve comme fortes conventions de lisibilité le rapprochement d'un `struct` et de ses `impl`, ainsi que la préférence des éléments visibles avant les helpers privés lorsque cela clarifie le fichier. Aucun audit lexical n'impose cependant un ordre global `enum → struct → impl → fn` ou un tri visible/privé susceptible d'écraser une organisation métier.
## Audits ajoutés
Quatre contrôles read-only monotâches sont extraits :
```text
audit_rust_item_spacing.py
séparation exacte des items distincts voisins
audit_rust_module_order.py
ordre production/tests et ordre alphabétique des modules
audit_rust_trait_import_blocks.py
placement/contiguïté des use non exportés
audit_rust_reexport_blocks.py
ordre de visibilité et espacement des blocs de réexports
```
Ils utilisent `_rust_audit_common.py` pour la sélection et le masquage lexical. Ils ne tentent pas de remplacer un parser Rust ni de déterminer la nature sémantique d'un import.
Les audits déjà existants gardent leurs responsabilités :
```text
audit_rust_blank_lines.py
blancs consécutifs / blancs dans certains corps
audit_rust_const_blocks.py
consts sélectionnées : visibilité, ordre, espacement
audit_rust_export_completeness.py
façade crate-root et chemins canoniques
```
## Mesure croisée
Les quatre nouveaux audits ont été exécutés pendant la préparation sur tous les fichiers Rust sous `crates/` des baselines autoritaires :
```text
KSP 0.3.16 603 file(s) item/module/trait-import/reexport: clean
Games 0.3.5 87 file(s) item/module/trait-import/reexport: clean
```
`audit_rust_blank_lines.py` est également propre sur ces `603` et `87` fichiers.
En revanche, `audit_rust_const_blocks.py` exécuté aveuglément sur l'ensemble produit `18` signaux KSP et `0` Games. Certains signaux correspondent à des tables/schémas à ordre intentionnel. Il reste donc un audit à sélection explicite par fichier et ne devient pas une gate globale.
Ces mesures servent à qualifier la réutilisabilité des règles PGR ; elles ne constituent pas une validation opérateur de KSP/Games.
## Horizon documenté sans engagement
La revue d'horizon du plan conserve `0.1.0` comme seule prochaine release engagée, mais mémorise trois candidats sans numéro ni engagement :
1. profils techniques composables par langage/framework/target/plateforme et préoccupation transverse, notamment observability/logging ;
2. profil documentaire/spécification, avec `saselang-bible` comme corpus pilote possible ;
3. audit de généricité de PGR pour des projets hors développement logiciel.
Ces candidats ne deviennent ni `Study`, ni `ROADMAP`, ni release plan tant qu'une décision explicite ne les promeut pas.
## Hors scope
Cette tranche ne :
- crée pas de parser Rust général ;
- réimporte pas `audit_rust_general_rules.py` ;
- impose pas un ordre alphabétique à une séquence sémantique ;
- crée pas d'audit dédié `static/type` uniquement pour la symétrie ;
- modifie pas les contrats de suffixes/packaging/tracing fermés par `alpha.7.fix.1` ;
- commence pas le skeleton Tauri réservé à `alpha.9` ;
- transforme pas les candidats d'horizon en versions promises.
## Validation opérateur
Après application du history `PASS` de `alpha.7.fix.1`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.8'
test -f history/0.0.5/alpha.7.fix.1.md
test -f deltas/0.0.5/alpha.8.md
test -f scripts/audit_rust_item_spacing.py
test -f scripts/audit_rust_module_order.py
test -f scripts/audit_rust_reexport_blocks.py
test -f scripts/audit_rust_trait_import_blocks.py
test ! -e scripts/audit_rust_general_rules.py
test ! -e history/0.0.5/alpha.8.md
grep -A2 '^## Statut$' history/0.0.5/alpha.7.fix.1.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 7 -->' docs/rules/510-RUST_CARGO.md
grep -Fx '<!-- version: 5 -->' docs/skeletons/002-RUST_CARGO_WORKSPACE.md
grep -Fx '<!-- version: 10 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 12 -->' docs/validation/001-VALIDATION_MATRIX.md
grep -Fx '<!-- version: 2 -->' docs/validation/002-RUST_STYLE_AUDITS.md
grep -Fx '<!-- version: 11 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '# version: 2' tests/test_rust_audits.py
grep -Fx '# version: 1' scripts/audit_rust_item_spacing.py
grep -Fx '# version: 1' scripts/audit_rust_module_order.py
grep -Fx '# version: 1' scripts/audit_rust_reexport_blocks.py
grep -Fx '# version: 1' scripts/audit_rust_trait_import_blocks.py
python3 -B -m unittest discover -s tests -p 'test_rust_audits.py' -v
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.7.fix.1.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+PASS\s", history)
rules = Path("docs/rules/510-RUST_CARGO.md").read_text(encoding="utf-8")
for phrase in (
"Le profil structurel strict fait partie du défaut des nouvelles baselines Rust contrôlées.",
"audit_rust_item_spacing.py",
"audit_rust_module_order.py",
"audit_rust_trait_import_blocks.py",
"audit_rust_reexport_blocks.py",
"PGR n'impose toutefois pas un ordre global automatique `enum → struct → impl → fn`",
):
assert phrase in rules, phrase
validation = Path("docs/validation/002-RUST_STYLE_AUDITS.md").read_text(encoding="utf-8")
for phrase in (
"603` fichiers KSP `0.3.16`",
"87` fichiers Games `0.3.5`",
"18` signaux KSP",
"0` Games",
"Rustfmt reste l'owner de l'ordre intra-bloc",
):
assert phrase in validation, phrase
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
for phrase in (
"quatre extractions monotâches complètent le profil",
"maturation de profils techniques composables",
"`saselang-bible` comme corpus pilote possible",
"audit du noyau PGR hors développement logiciel",
"### alpha.9 — décision de skeleton Tauri pour projets contrôlés",
):
assert phrase in plan, phrase
print("0.0.5 alpha.8 Rust structure audit: clean (owners split, four monotask audits, semantic order protected, horizon candidates recorded)")
PY
git diff --check
git add VERSION \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/rules/510-RUST_CARGO.md \
docs/skeletons/002-RUST_CARGO_WORKSPACE.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
docs/validation/001-VALIDATION_MATRIX.md \
docs/validation/002-RUST_STYLE_AUDITS.md \
scripts/audit_rust_item_spacing.py \
scripts/audit_rust_module_order.py \
scripts/audit_rust_reexport_blocks.py \
scripts/audit_rust_trait_import_blocks.py \
tests/test_rust_audits.py \
deltas/0.0.5/alpha.8.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.8.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/validation/001-VALIDATION_MATRIX.md
M docs/validation/002-RUST_STYLE_AUDITS.md
A scripts/audit_rust_item_spacing.py
A scripts/audit_rust_module_order.py
A scripts/audit_rust_reexport_blocks.py
A scripts/audit_rust_trait_import_blocks.py
M tests/test_rust_audits.py
```

178
deltas/0.0.5/alpha.9.md Normal file
View File

@@ -0,0 +1,178 @@
<!-- file: deltas/0.0.5/alpha.9.md -->
<!-- version: 1 -->
# Delta 0.0.5-alpha.9 — décision de skeleton Tauri contrôlé
## Baseline et version
Baseline attendue : commit opérateur complet de `0.0.5-alpha.8`, réalisé après application de `history/0.0.5/alpha.8.md` avec statut `PASS`.
Version après application :
```text
0.0.5-alpha.9
```
## Mission
Décider si les owners PGR réellement matérialisés permettent de créer un skeleton Tauri actionnable pour les projets contrôlés sans transformer ce skeleton en propriétaire implicite de règles Web encore absentes.
## Scope
REPLACE :
```text
VERSION
docs/000-README.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
docs/skeletons/000-README.md
docs/studies/000-README.md
```
ADD :
```text
deltas/0.0.5/alpha.9.md
docs/studies/002-TAURI_SKELETON_DECISION.md
```
DELETE : aucun.
Aucun fichier `docs/skeletons/003-TAURI*.md` n'est créé.
## Décision
Résultat :
```text
DEFER
```
Le host Tauri est suffisamment cadré par les owners existants : Rust/Cargo, Tauri, plateformes applicables et règles générales de skeleton. La partie frontend candidate ne l'est pas encore : PGR ne possède aucun profil Web/Vite/TypeScript/Sass.
Créer maintenant le skeleton obligerait donc sa specification à posséder de fait la structure et la validation Web, alors qu'un skeleton doit composer des owners et non compenser leur absence.
## Candidat conservé
La décision ne rejette pas le skeleton organisationnel. Elle conserve comme hypothèse à réévaluer lorsque les owners Web existeront :
```text
host Cargo direct
+ frontend/
+ Vite
+ TypeScript
+ Sass
+ Bootstrap lorsque pertinent
+ TS-RS pour les DTO Rust partagés
+ tauri.rs comme owner Tauri
+ plugins/capabilities explicitement intégrés
+ WASM séparé par défaut
```
Aucune de ces lignes ne crée dans `0.0.5` un owner Web qui n'existe pas encore.
## Sources et vérification
La décision repose d'abord sur les baselines KSP `0.3.16` et Games `0.3.5` déjà autoritaires pour la réconciliation. La préparation a également vérifié la documentation Tauri 2 courante concernant la flexibilité du frontend, le scaffold `src-tauri/`, l'initialisation manuelle et l'installation explicite des plugins/capabilities.
L'étude détaillée est `docs/studies/002-TAURI_SKELETON_DECISION.md`.
## Hors scope
Cette tranche ne :
- crée pas un skeleton décoratif ;
- matérialise pas les futurs profils Web ;
- modifie pas `710-TAURI.md` ;
- choisit pas un package manager universel ;
- modifie pas les règles Rust fermées par `alpha.7/alpha.8` ;
- réserve pas une release pour le futur skeleton.
## Validation opérateur
Après application du history `PASS` de `alpha.8`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-alpha.9'
test -f history/0.0.5/alpha.8.md
test -f deltas/0.0.5/alpha.9.md
test -f docs/studies/002-TAURI_SKELETON_DECISION.md
test ! -e docs/skeletons/003-TAURI.md
test ! -e docs/skeletons/003-TAURI_CONTROLLED_PROJECT.md
test ! -e history/0.0.5/alpha.9.md
grep -A2 '^## Statut$' history/0.0.5/alpha.8.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 15 -->' docs/000-README.md
grep -Fx '<!-- version: 12 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '<!-- version: 4 -->' docs/skeletons/000-README.md
grep -Fx '<!-- version: 2 -->' docs/studies/000-README.md
grep -Fx '<!-- version: 1 -->' docs/studies/002-TAURI_SKELETON_DECISION.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.8.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+PASS\s", history)
study = Path("docs/studies/002-TAURI_SKELETON_DECISION.md").read_text(encoding="utf-8")
for phrase in (
"Décision `alpha.9` :",
"DEFER",
"Aucun des `8` hosts n'utilise `src-tauri/`.",
"PGR `0.0.5` **ne crée pas** de skeleton Tauri `003`.",
"PGR `0.0.5` ne possède aucun profil Web dédié",
"001-PROJECT_GOVERNANCE_FOUNDATION",
"002-RUST_CARGO_WORKSPACE",
"Bootstrap comme baseline de présentation lorsqu'un framework CSS généraliste est utile",
):
assert phrase in study, phrase
index = Path("docs/skeletons/000-README.md").read_text(encoding="utf-8")
assert "Aucun `003-TAURI...` n'est donc créé dans `0.0.5`." in index
assert "profils Web réellement matérialisés" in index
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
assert "### alpha.9 — décision de skeleton Tauri pour projets contrôlés — DEFER" in plan
assert "aucun skeleton Tauri `003` n'est créé dans `0.0.5`" in plan
assert "### beta.1 — réconciliation intégrée" in plan
assert not any(Path("docs/skeletons").glob("003-TAURI*.md"))
print("0.0.5 alpha.9 Tauri skeleton decision: clean (host ready, Web owners missing, decorative skeleton rejected, exit conditions recorded)")
PY
git diff --check
git add VERSION \
docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/skeletons/000-README.md \
docs/studies/000-README.md \
docs/studies/002-TAURI_SKELETON_DECISION.md \
deltas/0.0.5/alpha.9.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.9.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/skeletons/000-README.md
M docs/studies/000-README.md
A docs/studies/002-TAURI_SKELETON_DECISION.md
```

208
deltas/0.0.5/beta.1.md Normal file
View File

@@ -0,0 +1,208 @@
<!-- file: deltas/0.0.5/beta.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-beta.1 — réconciliation intégrée et transition vers 0.1.0
## Baseline
Version attendue avant application :
```text
0.0.5-alpha.9
```
Le history `history/0.0.5/alpha.9.md` avec statut `PASS` MUST avoir été appliqué, committé et poussé avant ce delta.
Version après application :
```text
0.0.5-beta.1
```
## Objet
Fermer la phase alpha de `0.0.5` par une réconciliation intégrée des décisions réellement matérialisées, puis préparer la transition vers la première baseline réutilisable `0.1.0` sans inventer d'owners, de skeletons ou de releases futures.
Cette tranche :
- rejoue la classification KSP/Games sur l'état PGR obtenu ;
- vérifie que chaque responsabilité nouvelle possède un owner identifiable et que les conventions locales non généralisées restent hors du noyau ;
- inscrit durablement le skeleton Tauri contrôlé comme trajectoire `DEFER` dans `ROADMAP.md`, sans numéro de release réservé ;
- maintient les trois autres axes discutés pendant `alpha.8` au stade de candidats d'horizon, hors roadmap engagée ;
- réconcilie les points d'entrée durables ;
- prépare `prompts/006-V0_1_0_START_PROMPT.md` puisque la mission `0.1.0` est suffisamment connue.
## Décision de roadmap
La décision `alpha.9` a déjà établi qu'un skeleton Tauri complet ne peut pas être matérialisé proprement avant l'existence d'owners Web suffisants. Ce report est plus qu'une idée : il résulte d'une étude et possède des conditions de sortie explicites.
`ROADMAP.md` enregistre donc :
```text
(d) skeleton Tauri pour projets contrôlés
```
sans associer cette trajectoire à `0.1.1`, `0.2.0` ou une autre release hypothétique.
Les axes suivants restent en revanche hors roadmap tant qu'une revue d'horizon ne les engage pas :
- maturation plus large des profils techniques composables ;
- projet documentaire/spécification avec `saselang-bible` comme corpus pilote possible ;
- audit de généricité de PGR hors développement logiciel.
Cette distinction applique directement `300-DOCUMENTATION.md` : une décision de report réelle peut être conservée comme trajectoire différée, tandis qu'un candidat d'horizon ne devient pas automatiquement un engagement macro.
## Réconciliation intégrée
L'étude `001-KSP_GAMES_RECONCILIATION.md` reçoit une matrice de sortie `beta.1` qui confirme notamment :
```text
prompt versionné OWNED
contrats de fichiers/familles OWNED
lifecycle inter-release / horizon OWNED
profil Rust contrôlé OWNED
audits Rust monotâches OWNED
profil Tauri 2 OWNED
skeleton Tauri complet DEFER
Web/Vite/TypeScript/Sass DEFER
Android/JNI spécifique LOCAL/DEFER
architectures métier KSP/Games LOCAL
```
La présence d'un owner PGR n'impose pas sa sélection à tous les projets. Applicabilité, composition et overrides restent gouvernés par le modèle de profils.
## Prompt 006
`prompts/006-V0_1_0_START_PROMPT.md` prépare la reprise après publication réelle de `v0.0.5`.
Il fixe uniquement ce qui est déjà décidé :
- baseline autoritaire `pgr-v0.0.5.zip` et rapport post-tag séparé lorsqu'il existe et est `PASS` ;
- cible `0.1.0`, première tranche `0.1.0-alpha.1` ;
- audit complet de la stable avant extension ;
- mission de première baseline réellement réutilisable ;
- nécessité de produire/consolider le plan `0.1.0` pendant `alpha.1` ;
- revue d'horizon obligatoire avant fermeture ;
- skeleton Tauri conservé `DEFER` tant que ses préconditions ne sont pas réellement satisfaites.
Le prompt MUST NOT être interprété comme un plan détaillé de `0.1.0` ni comme une promesse de matérialiser tous les candidats d'horizon.
## Documentation durable
`README.md` résume désormais les capacités réellement apportées par `0.0.5` sans recopier le plan de release.
`docs/000-README.md` expose les deux contrats de validation Rust et le prompt de reprise candidat `006`.
Le plan `005` enregistre le résultat de la réconciliation intégrée et conserve les responsabilités `rc.1`, stable et `publish.001` déjà attribuées.
## Hors scope
Cette tranche ne :
- crée pas de skeleton Tauri `003` ;
- crée pas de profil Web placeholder ;
- attribue pas de release au travail Tauri différé ;
- ajoute pas les trois candidats d'horizon à la roadmap engagée ;
- modifie pas `CHANGELOG.md`, réservé à la préparation RC puis à la stable ;
- migre pas KSP ou Games ;
- change pas les règles Rust/Tauri fermées pendant les alphas.
## Validation opérateur
Après application du history `PASS` de `alpha.9`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-beta.1'
test -f history/0.0.5/alpha.9.md
test -f deltas/0.0.5/beta.1.md
test -f prompts/006-V0_1_0_START_PROMPT.md
test ! -e history/0.0.5/beta.1.md
grep -A2 '^## Statut$' history/0.0.5/alpha.9.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 13 -->' README.md
grep -Fx '<!-- version: 7 -->' ROADMAP.md
grep -Fx '<!-- version: 16 -->' docs/000-README.md
grep -Fx '<!-- version: 13 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '<!-- version: 11 -->' docs/studies/001-KSP_GAMES_RECONCILIATION.md
grep -Fx '<!-- version: 1 -->' prompts/006-V0_1_0_START_PROMPT.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PY'
from pathlib import Path
import re
history = Path("history/0.0.5/alpha.9.md").read_text(encoding="utf-8")
assert re.search(r"## Statut\s+PASS\s", history)
roadmap = Path("ROADMAP.md").read_text(encoding="utf-8")
assert "(d) skeleton Tauri pour projets contrôlés" in roadmap
assert "Aucun numéro de release n'est réservé pour ce travail" in roadmap
assert "saselang-bible" not in roadmap
study = Path("docs/studies/001-KSP_GAMES_RECONCILIATION.md").read_text(encoding="utf-8")
for phrase in (
"## Réconciliation intégrée `beta.1`",
"skeleton Tauri contrôlé complet",
"`DEFER`",
"repris dans `ROADMAP.md` sans numéro de release",
"Les trois axes plus larges identifiés pendant `alpha.8` restent des **candidats d'horizon**",
):
assert phrase in study, phrase
plan = Path("docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md").read_text(encoding="utf-8")
for phrase in (
"Le `DEFER` du skeleton Tauri contrôlé est suffisamment décidé",
"`prompts/006-V0_1_0_START_PROMPT.md` est préparé",
"il ne transforme pas le skeleton Tauri différé en obligation de `0.1.0`",
):
assert phrase in plan, phrase
prompt = Path("prompts/006-V0_1_0_START_PROMPT.md").read_text(encoding="utf-8")
for phrase in (
"# Prompt de démarrage 0.1.0 — première baseline réutilisable",
"pgr-v0.0.5.zip",
"pgr-0.0.5-publish.001-report.md",
"Version cible : `0.1.0`. Première tranche attendue : `0.1.0-alpha.1`.",
"Le skeleton Tauri contrôlé reste `DEFER`",
"Le plan `0.1.0` MUST programmer une revue d'horizon avant la clôture stable.",
):
assert phrase in prompt, phrase
assert not any(Path("docs/skeletons").glob("003-TAURI*.md"))
print("0.0.5 beta.1 integrated reconciliation: clean (owners reconciled, Tauri defer roadmapped, horizon candidates remain uncommitted, prompt 006 prepared)")
PY
git diff --check
git add VERSION README.md ROADMAP.md \
docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
docs/studies/001-KSP_GAMES_RECONCILIATION.md \
prompts/006-V0_1_0_START_PROMPT.md \
deltas/0.0.5/beta.1.md
git diff --cached --check
git status --short
```
La sortie Git attendue avant ajout du history est exactement :
```text
M README.md
M ROADMAP.md
M VERSION
A deltas/0.0.5/beta.1.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
A prompts/006-V0_1_0_START_PROMPT.md
```

190
deltas/0.0.5/rc.1.md Normal file
View File

@@ -0,0 +1,190 @@
<!-- file: deltas/0.0.5/rc.1.md -->
<!-- version: 1 -->
# Delta 0.0.5-rc.1 — gel du scope et préparation de release
## Baseline et version
Baseline : `0.0.5-beta.1`, avec `history/0.0.5/beta.1.md` appliqué et committé. Ce delta MUST NOT être appliqué avant ce commit.
Le commit exact de cette baseline sera celui produit par l'opérateur après application du history beta ; il n'est pas anticipé dans cette livraison simultanée du history et du delta suivant.
Version avant application :
```text
0.0.5-beta.1
```
Version après application :
```text
0.0.5-rc.1
```
## Objet et gel
La réconciliation `beta.1` est `PASS`. La RC gèle le scope fonctionnel de `0.0.5` sur :
- les prompts versionnés, contrats documentaires facultatifs et responsabilités de lifecycle/revue d'horizon ;
- le profil Rust strict des projets contrôlés, ses contrats de nomenclature/packaging/tracing et ses audits monotâches ;
- l'audit de complétude de façade et l'usage canonique `crate::Item` ;
- les profils framework et Tauri 2, leurs frontières Rust/frontend, TS-RS, plugins/capabilities, politique de versions produit et exception encadrée `lib + bin` ;
- la décision explicite `DEFER` du skeleton Tauri contrôlé jusqu'à matérialisation d'owners Web suffisants ;
- la réconciliation KSP/Games et le prompt candidat `006` vers `0.1.0`.
Le changelog reçoit une entrée candidate `0.0.5` orientée utilisateurs. `ROADMAP.md` est vérifié mais reste inchangé : `0.0.5` n'est marqué terminé qu'à la promotion stable ; `0.1.0` reste la prochaine release engagée ; le skeleton Tauri reste différé sans numéro réservé. Le prompt `006` est vérifié sans être réécrit si son contrat est déjà correct.
Aucun nouvel owner, profil Web, skeleton, template, audit, règle fonctionnelle ou version postérieure à `0.1.0` n'entre en RC.
## Scope
REPLACE :
```text
VERSION
CHANGELOG.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
ADD :
```text
deltas/0.0.5/rc.1.md
```
DELETE : aucun.
L'archive contient exactement ces fichiers complets. Le history `beta.1` est livré séparément ; le history `rc.1` sera produit après la gate opérateur.
## Gate opérateur de release candidate
Après application du history `PASS` de `beta.1`, commit/push de cette tranche, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.5-rc.1'
git cat-file -e HEAD:history/0.0.5/beta.1.md
test -f history/0.0.5/beta.1.md
test -f deltas/0.0.5/rc.1.md
test -f prompts/006-V0_1_0_START_PROMPT.md
test ! -e history/0.0.5/rc.1.md
grep -A2 '^## Statut$' history/0.0.5/beta.1.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 8 -->' CHANGELOG.md
grep -Fx '## 0.0.5 (candidate)' CHANGELOG.md
grep -Fx '<!-- version: 14 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '<!-- version: 7 -->' ROADMAP.md
grep -Fx '<!-- version: 1 -->' prompts/006-V0_1_0_START_PROMPT.md
grep -F 'La gate opérateur de `beta.1` est `PASS`' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -F '(d) skeleton Tauri pour projets contrôlés' ROADMAP.md
grep -F 'Le skeleton Tauri contrôlé reste `DEFER`' prompts/006-V0_1_0_START_PROMPT.md
python3 -B -m unittest discover -s tests -v
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B - <<'PYGATE'
from pathlib import Path
import re
expected = {
'alpha.1.md': 'PASS',
'alpha.2.md': 'PASS',
'alpha.3.md': 'FAIL',
'alpha.3.fix.1.md': 'PASS',
'alpha.4.md': 'PASS',
'alpha.5.md': 'FAIL',
'alpha.5.fix.1.md': 'PASS',
'alpha.6.md': 'FAIL',
'alpha.6.fix.1.md': 'FAIL',
'alpha.6.fix.2.md': 'FAIL',
'alpha.6.fix.3.md': 'PASS',
'alpha.7.md': 'FAIL',
'alpha.7.fix.1.md': 'PASS',
'alpha.8.md': 'PASS',
'alpha.9.md': 'PASS',
'beta.1.md': 'PASS',
}
histories = {path.name for path in Path('history/0.0.5').glob('*.md')}
assert histories == set(expected), histories
for name, status in expected.items():
text = Path('history/0.0.5', name).read_text(encoding='utf-8')
assert re.search(rf'## Statut\s+{status}\s', text), (name, status)
deltas = {path.name for path in Path('deltas/0.0.5').glob('*.md')}
assert deltas == set(expected) | {'rc.1.md'}, deltas
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
assert '## 0.0.5 (candidate)' in changelog
for forbidden in ('alpha.6.fix.3', 'alpha.7.fix.1', 'beta.1'):
assert forbidden not in changelog, forbidden
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
assert '- `( ) 0.0.5`' in roadmap
assert '- `( ) 0.1.0`' in roadmap
assert '(d) skeleton Tauri pour projets contrôlés' in roadmap
assert 'saselang-bible' not in roadmap
prompt = Path('prompts/006-V0_1_0_START_PROMPT.md').read_text(encoding='utf-8')
for value in (
'pgr-v0.0.5.zip',
'pgr-0.0.5-publish.001-report.md',
'0.1.0-alpha.1',
'Le skeleton Tauri contrôlé reste `DEFER`',
"revue d'horizon avant la clôture stable",
):
assert value in prompt, value
for path in (
'scripts/audit_rust_item_spacing.py',
'scripts/audit_rust_module_order.py',
'scripts/audit_rust_reexport_blocks.py',
'scripts/audit_rust_trait_import_blocks.py',
'scripts/audit_rust_export_completeness.py',
):
assert Path(path).is_file(), path
assert Path('docs/rules/710-TAURI.md').is_file()
assert not any(Path('docs/skeletons').glob('003-TAURI*.md'))
print('0.0.5 rc.1 integration audit: clean (16 prior histories, frozen scope, candidate changelog, roadmap/prompt 006 verified)')
PYGATE
git diff --check
git add VERSION CHANGELOG.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
deltas/0.0.5/rc.1.md
git diff --cached --check
git status --short
```
Le scope attendu avant ajout du history est exactement :
```text
M CHANGELOG.md
M VERSION
A deltas/0.0.5/rc.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
## Revue opérateur complémentaire
Confirmer que le changelog décrit les capacités de la release plutôt que ses tranches internes, que le `DEFER` Tauri reste visible sans numéro de release, que les trois autres candidats d'horizon ne sont pas promus en roadmap, que le prompt `006` ne présume aucune publication post-tag et que la RC n'étend pas le périmètre fonctionnel.
Une gate RC `PASS` n'est ni la promotion stable, ni le tag `v0.0.5`, ni la vérification de l'archive publiée après le tag.
## Transition
### PASS
Produire séparément `history/0.0.5/rc.1.md`, l'appliquer puis committer/pousser la RC avant le delta stable `rel.001`.
### FAIL
Consigner l'échec réel dans `history/0.0.5/rc.1.md` et corriger uniquement le scope gelé dans `rc.1.fix.1`, sauf REPLAN explicite si la candidate révèle un défaut fonctionnel plus large.

View File

@@ -0,0 +1,170 @@
<!-- file: deltas/0.0.5/rel.001.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.5 rel.001.fix.1 — correction de la gate stable pré-tag
## Baseline et version
Baseline : état stable `0.0.5` issu de `rel.001`, avec `history/0.0.5/rel.001.md` appliqué et committé en `FAIL`.
`VERSION` reste :
```text
0.0.5
```
Ce correctif est strictement documentaire et opérationnel. Il ne change ni la version produit, ni le scope fonctionnel validé par `rc.1`, ni les fichiers de présentation stable déjà promus par `rel.001`.
## Diagnostic corrigé
La gate `rel.001` devait confirmer la présence exacte du heading stable `## 0.0.5` dans `CHANGELOG.md`. Le delta livré a généré une chaîne Python simple contenant un retour à la ligne physique :
```python
assert '## 0.0.5
' in changelog
```
Le heredoc est donc invalide et produit `SyntaxError: unterminated string literal` avant la preuve attendue.
`rel.001.fix.1` remplace cette forme fragile par une vérification ligne par ligne :
```python
assert '## 0.0.5' in changelog.splitlines()
```
Le delta historique `rel.001.md` reste immuable.
## Scope
REPLACE :
```text
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
ADD :
```text
deltas/0.0.5/rel.001.fix.1.md
```
DELETE : aucun.
Aucun fichier `VERSION`, `README`, `ROADMAP`, `CHANGELOG`, règle, script, test, template ou skeleton n'est modifié par ce correctif.
## Validation opérateur pré-tag
Après application sur le commit qui contient le history `FAIL` de `rel.001` :
```bash
test "$(cat VERSION)" = '0.0.5'
git cat-file -e HEAD:history/0.0.5/rel.001.md
test -f history/0.0.5/rel.001.md
test -f deltas/0.0.5/rel.001.fix.1.md
test -f prompts/006-V0_1_0_START_PROMPT.md
test ! -e history/0.0.5/rel.001.fix.1.md
grep -A2 '^## Statut$' history/0.0.5/rel.001.md | grep -Fx 'FAIL'
grep -Fx '<!-- version: 16 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -Fx '<!-- version: 14 -->' README.md
grep -Fx '<!-- version: 8 -->' ROADMAP.md
grep -Fx '<!-- version: 9 -->' CHANGELOG.md
grep -Fx '## 0.0.5' CHANGELOG.md
grep -F -- '- `(x) 0.0.5`' ROADMAP.md
grep -F 'Le skeleton Tauri contrôlé reste `DEFER`' prompts/006-V0_1_0_START_PROMPT.md
python3 -B - <<'PYGATE'
from pathlib import Path
import re
expected = {
'alpha.1.md': 'PASS',
'alpha.2.md': 'PASS',
'alpha.3.md': 'FAIL',
'alpha.3.fix.1.md': 'PASS',
'alpha.4.md': 'PASS',
'alpha.5.md': 'FAIL',
'alpha.5.fix.1.md': 'PASS',
'alpha.6.md': 'FAIL',
'alpha.6.fix.1.md': 'FAIL',
'alpha.6.fix.2.md': 'FAIL',
'alpha.6.fix.3.md': 'PASS',
'alpha.7.md': 'FAIL',
'alpha.7.fix.1.md': 'PASS',
'alpha.8.md': 'PASS',
'alpha.9.md': 'PASS',
'beta.1.md': 'PASS',
'rc.1.md': 'PASS',
'rel.001.md': 'FAIL',
}
for name, status in expected.items():
delta = Path('deltas/0.0.5', name)
history = Path('history/0.0.5', name)
assert delta.is_file(), name
assert history.is_file(), name
text = history.read_text(encoding='utf-8')
assert re.search(rf'## Statut\s+{status}\s', text), (name, status)
pending = {p.name for p in Path('deltas/0.0.5').glob('*.md')} - \
{p.name for p in Path('history/0.0.5').glob('*.md')}
assert pending == {'rel.001.fix.1.md'}, pending
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
assert '- `(x) 0.0.5`' in roadmap
assert '- `( ) 0.1.0`' in roadmap
assert '(d) skeleton Tauri pour projets contrôlés' in roadmap
assert 'saselang-bible' not in roadmap
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
assert '## 0.0.5' in changelog.splitlines()
assert '## 0.0.5 (candidate)' not in changelog
prompt = Path('prompts/006-V0_1_0_START_PROMPT.md').read_text(encoding='utf-8')
assert 'pgr-v0.0.5.zip' in prompt
assert 'pgr-0.0.5-publish.001-report.md' in prompt
assert '0.1.0-alpha.1' in prompt
assert 'Le skeleton Tauri contrôlé reste `DEFER`' in prompt
plan = Path('docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md').read_text(encoding='utf-8')
assert "La gate opérateur de `rel.001` est toutefois `FAIL`" in plan
assert '`rel.001.fix.1` corrige uniquement cette preuve de clôture' in plan
print('0.0.5 rel.001.fix.1 stable history audit: clean (18 prior histories, failed rel preserved, corrected pre-tag proof)')
PYGATE
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
git diff --check
git add docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md \
deltas/0.0.5/rel.001.fix.1.md
git diff --cached --check
git status --short
```
Le scope attendu avant ajout du history du fix est exactement :
```text
A deltas/0.0.5/rel.001.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
## Revue opérateur complémentaire
Confirmer que `VERSION` reste `0.0.5`, que la présentation stable de `rel.001` reste inchangée, que le `FAIL` de `rel.001` est conservé historiquement, que le fix ne modifie aucun owner fonctionnel et qu'aucun tag n'est créé avant le history PASS de ce correctif.
## Transition
### PASS
Produire séparément `history/0.0.5/rel.001.fix.1.md`, l'appliquer puis committer/pousser la clôture stable corrigée. Ensuite seulement créer et publier le tag canonique `v0.0.5`, télécharger son archive source depuis Gitea et exécuter la vérification externe `publish.001`.
### FAIL
Consigner le diagnostic réel dans `history/0.0.5/rel.001.fix.1.md`, ne pas tagger et poursuivre avec un nouveau `.fix.N` ou un REPLAN si le problème dépasse la gate de clôture.

179
deltas/0.0.5/rel.001.md Normal file
View File

@@ -0,0 +1,179 @@
<!-- file: deltas/0.0.5/rel.001.md -->
<!-- version: 1 -->
# Delta 0.0.5 rel.001 — promotion stable pré-tag
## Baseline et version
Baseline : `0.0.5-rc.1`, avec `history/0.0.5/rc.1.md` appliqué et committé. Ce delta MUST NOT être appliqué avant ce commit.
Le commit exact de cette baseline sera celui produit par l'opérateur après application du history RC ; il n'est pas anticipé dans cette livraison simultanée du history et du delta stable.
Version avant application :
```text
0.0.5-rc.1
```
Version après application :
```text
0.0.5
```
`rel.001` identifie la clôture opérationnelle ; il ne fait pas partie de la version SemVer du produit.
## Objet
Promouvoir la RC validée vers la stable `0.0.5`. La tranche finalise uniquement la présentation de la release : version stable, roadmap terminée, changelog sans marqueur candidate, index documentaires et plan historique.
Le scope fonctionnel reste celui validé en RC : lifecycle inter-release, prompts versionnés, contrats documentaires locaux, profil Rust strict et audits monotâches, contrats architecturaux des suffixes de crates, ownership du tracing, profil Tauri 2, classification des versions produit, réconciliation KSP/Games et décision `DEFER` du skeleton Tauri contrôlé jusqu'à matérialisation d'owners Web suffisants.
La validation de cette tranche est **pré-tag**. Ni le tag `v0.0.5`, ni l'archive `pgr-v0.0.5.zip` téléchargée depuis Gitea, ni un rapport `pgr-0.0.5-publish.001-report.md` ne sont déclarés existants par ce delta. Ils suivent le history PASS, le commit stable et la publication réelle du tag.
## Scope
REPLACE :
```text
VERSION
README.md
ROADMAP.md
CHANGELOG.md
docs/000-README.md
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
ADD :
```text
deltas/0.0.5/rel.001.md
```
DELETE : aucun.
L'archive contient uniquement ces fichiers complets. `history/0.0.5/rc.1.md` est livré séparément ; le history de `rel.001` sera produit après sa gate. Le rapport post-tag restera extérieur à l'archive source taggée.
## Validation opérateur pré-tag
Après application sur le commit `rc.1`, depuis la racine du dépôt :
```bash
test "$(cat VERSION)" = '0.0.5'
git cat-file -e HEAD:history/0.0.5/rc.1.md
test -f history/0.0.5/rc.1.md
test -f deltas/0.0.5/rel.001.md
test -f prompts/006-V0_1_0_START_PROMPT.md
test ! -e history/0.0.5/rel.001.md
grep -Fx '<!-- version: 14 -->' README.md
grep -Fx '<!-- version: 8 -->' ROADMAP.md
grep -Fx '<!-- version: 9 -->' CHANGELOG.md
grep -Fx '<!-- version: 17 -->' docs/000-README.md
grep -Fx '<!-- version: 15 -->' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -F 'plan historique de réconciliation `0.0.5`' README.md
grep -F -- '- `(x) 0.0.5`' ROADMAP.md
grep -Fx '## 0.0.5' CHANGELOG.md
grep -F 'périmètre et décisions de la release `0.0.5`' docs/000-README.md
grep -F 'La gate opérateur de `rc.1` est `PASS`' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -F 'pgr-0.0.5-publish.001-report.md' docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
grep -F 'Le skeleton Tauri contrôlé reste `DEFER`' prompts/006-V0_1_0_START_PROMPT.md
python3 -B - <<'PYGATE'
from pathlib import Path
import re
expected = {
'alpha.1.md': 'PASS',
'alpha.2.md': 'PASS',
'alpha.3.md': 'FAIL',
'alpha.3.fix.1.md': 'PASS',
'alpha.4.md': 'PASS',
'alpha.5.md': 'FAIL',
'alpha.5.fix.1.md': 'PASS',
'alpha.6.md': 'FAIL',
'alpha.6.fix.1.md': 'FAIL',
'alpha.6.fix.2.md': 'FAIL',
'alpha.6.fix.3.md': 'PASS',
'alpha.7.md': 'FAIL',
'alpha.7.fix.1.md': 'PASS',
'alpha.8.md': 'PASS',
'alpha.9.md': 'PASS',
'beta.1.md': 'PASS',
'rc.1.md': 'PASS',
}
for name, status in expected.items():
delta = Path('deltas/0.0.5', name)
history = Path('history/0.0.5', name)
assert delta.is_file(), name
assert history.is_file(), name
text = history.read_text(encoding='utf-8')
assert re.search(rf'## Statut\s+{status}\s', text), (name, status)
pending = {p.name for p in Path('deltas/0.0.5').glob('*.md')} - \
{p.name for p in Path('history/0.0.5').glob('*.md')}
assert pending == {'rel.001.md'}, pending
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
assert '- `(x) 0.0.5`' in roadmap
assert '- `( ) 0.1.0`' in roadmap
assert '(d) skeleton Tauri pour projets contrôlés' in roadmap
assert 'saselang-bible' not in roadmap
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
assert '## 0.0.5
' in changelog
assert '## 0.0.5 (candidate)' not in changelog
prompt = Path('prompts/006-V0_1_0_START_PROMPT.md').read_text(encoding='utf-8')
assert 'pgr-v0.0.5.zip' in prompt
assert 'pgr-0.0.5-publish.001-report.md' in prompt
assert '0.1.0-alpha.1' in prompt
assert 'Le skeleton Tauri contrôlé reste `DEFER`' in prompt
print('0.0.5 stable history audit: clean (17 prior histories with expected statuses, rel.001 pending)')
PYGATE
python3 -B scripts/audit_markdown_headers.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_tables.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
python3 -B scripts/audit_markdown_links.py \
README.md RULES.md ROADMAP.md CHANGELOG.md docs deltas history prompts
git diff --check
git add VERSION README.md ROADMAP.md CHANGELOG.md docs/000-README.md \
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md deltas/0.0.5/rel.001.md
git diff --cached --check
git status --short
```
Le scope attendu avant ajout du history est exactement :
```text
M CHANGELOG.md
M README.md
M ROADMAP.md
M VERSION
A deltas/0.0.5/rel.001.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
Les `48` tests Python et les validations structurelles validés en RC restent des preuves applicables parce qu'aucun code, script, test, règle, template ou skeleton technique n'est modifié pendant `rel.001`. La gate stable se concentre donc sur l'intégrité de la chaîne historique, la présentation stable et les audits documentaires affectés.
## Revue opérateur complémentaire
Confirmer que le scope fonctionnel de la RC ne change pas, que README/roadmap/changelog/index/plan décrivent la même stable, que les `FAIL` historiques restent conservés avec leurs fixes `PASS`, que le skeleton Tauri reste différé sans release réservée, que le prompt `006` reste conditionnel à une vraie publication et qu'aucune preuve post-tag n'est anticipée.
## Transition
### PASS
Produire séparément `history/0.0.5/rel.001.md`, l'appliquer puis committer la stable. Ensuite seulement créer et publier le tag canonique `v0.0.5`, télécharger son archive source depuis Gitea, vérifier cette archive et conserver séparément la preuve post-tag `pgr-0.0.5-publish.001-report.md`.
Le message manuel de reprise de `prompts/006-V0_1_0_START_PROMPT.md` n'est remis à l'opérateur qu'après vérification effective de l'archive et d'un rapport post-tag PASS réel.
### FAIL
Consigner le diagnostic réel dans `history/0.0.5/rel.001.md`, ne pas tagger et corriger la clôture dans une tranche explicite avant publication.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/000-README.md -->
<!-- version: 9 -->
<!-- version: 17 -->
# Documentation PGR
@@ -10,6 +10,7 @@ Ce fichier est l'index d'entrée de la documentation structurée de Project Gove
- [`objectives/`](objectives/) — mission, objectifs durables, non-objectifs et contraintes globales ;
- [`architecture/`](architecture/) — état architectural courant de PGR et profils applicables ;
- [`plans/`](plans/) — plans vivants des releases en cours, puis références historiques après fermeture ;
- [`studies/`](studies/) — études bornées utilisées pour comparer des corpus, instruire une décision ou conserver une analyse avant normalisation ;
- [`rules/`](rules/) — règles normatives détaillées, organisées progressivement par scope ;
- [`skeletons/`](skeletons/) — spécifications Markdown de composition et d'initialisation de projets ;
- [`templates/`](templates/) — instructions d'adaptation des templates techniques et spécification du message manuel de reprise ;
@@ -19,6 +20,17 @@ D'autres collections telles que `ideas/`, `studies/`, `reference/` ou `formats/`
Les templates techniques copiables sous `templates/` restent séparés des instructions documentaires sous `docs/templates/`. Les répertoires workflow de racine (`deltas/`, `history/`, `handoffs/`, `manifests/`, `prompts/`, `scripts/`) restent séparés de `docs/` parce qu'ils portent le processus de développement et de livraison lui-même.
## Points d'entrée de 0.0.5
- [`plans/005-V0_0_5_RECONCILIATION_PLAN.md`](plans/005-V0_0_5_RECONCILIATION_PLAN.md) — périmètre et décisions de la release `0.0.5` ;
- [`studies/001-KSP_GAMES_RECONCILIATION.md`](studies/001-KSP_GAMES_RECONCILIATION.md) — inventaire comparatif des baselines KSP/Games et décisions de généralisation, maintien local ou différé.
- [`studies/002-TAURI_SKELETON_DECISION.md`](studies/002-TAURI_SKELETON_DECISION.md) — décision `alpha.9` sur la maturité du skeleton Tauri contrôlé et ses prérequis Web encore manquants.
- [`templates/004-FILE_FAMILY_CONTRACTS.md`](templates/004-FILE_FAMILY_CONTRACTS.md) — pattern facultatif pour expliciter les responsabilités et règles de modification de familles de fichiers locales lorsque la complexité le justifie.
- [`templates/005-RELEASE_PLAN.md`](templates/005-RELEASE_PLAN.md) — pattern de plan de release avec clôture/transition explicites et revue d'horizon proportionnée.
- [`validation/002-RUST_STYLE_AUDITS.md`](validation/002-RUST_STYLE_AUDITS.md) et [`validation/003-RUST_EXPORT_COMPLETENESS.md`](validation/003-RUST_EXPORT_COMPLETENESS.md) — contrats et limites des audits Rust monotâches réconciliés depuis les corpus réels.
- [`rules/700-FRAMEWORKS.md`](rules/700-FRAMEWORKS.md) et [`rules/710-TAURI.md`](rules/710-TAURI.md) — activation de la famille framework et premier profil Tauri 2 issu de la réconciliation KSP/Games.
- [`../prompts/006-V0_1_0_START_PROMPT.md`](../prompts/006-V0_1_0_START_PROMPT.md) — prompt de reprise préparé pour la première baseline réutilisable `0.1.0`, à utiliser seulement après publication et vérification post-tag de `0.0.5`.
## Points d'entrée de 0.0.4
- [`plans/003-V0_0_3_TEMPLATES_AUDITS_PLAN.md`](plans/003-V0_0_3_TEMPLATES_AUDITS_PLAN.md) — périmètre et décisions de la release `0.0.3` ;
@@ -28,4 +40,4 @@ Les templates techniques copiables sous `templates/` restent séparés des instr
- [`skeletons/002-RUST_CARGO_WORKSPACE.md`](skeletons/002-RUST_CARGO_WORKSPACE.md) — specialization Rust/Cargo workspace composable avec la fondation et les cinq templates existants ;
- [`templates/001-RUST_CARGO_TOOLING.md`](templates/001-RUST_CARGO_TOOLING.md) et [`templates/002-CARGO_MANIFESTS.md`](templates/002-CARGO_MANIFESTS.md) — adaptation des cinq fichiers Rust/Cargo copiables ;
- [`templates/003-NEW_SESSION_PROMPT.md`](templates/003-NEW_SESSION_PROMPT.md) — spécification du message manuel après une stable publiée ;
- [`validation/001-VALIDATION_MATRIX.md`](validation/001-VALIDATION_MATRIX.md) et [`validation/002-RUST_STYLE_AUDITS.md`](validation/002-RUST_STYLE_AUDITS.md) — gates PGR et limites des audits de style Rust facultatifs.
- [`validation/001-VALIDATION_MATRIX.md`](validation/001-VALIDATION_MATRIX.md), [`validation/002-RUST_STYLE_AUDITS.md`](validation/002-RUST_STYLE_AUDITS.md) et [`validation/003-RUST_EXPORT_COMPLETENESS.md`](validation/003-RUST_EXPORT_COMPLETENESS.md) — gates PGR et limites des audits Rust facultatifs.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/architecture/003-RULE_PROFILE_MODEL.md -->
<!-- version: 3 -->
<!-- version: 5 -->
# Modèle de profils de règles PGR
@@ -117,6 +117,16 @@ Cette relation interne ne modifie pas la hiérarchie globale. Une contrainte de
Lorsque langage et build tool sont pratiquement indissociables pour le scope visé, PGR peut les documenter dans un même profil si cette fusion évite une séparation artificielle. Le choix du découpage doit suivre les responsabilités réelles, pas une taxonomie rigide.
## Baseline contrôlée et projet imposé
La même combinaison de technologies peut être gouvernée avec des degrés de liberté différents.
Pour une baseline créée et contrôlée par le projet, les profils sélectionnés s'appliquent intégralement puis une skeleton specification MAY fixer des choix réutilisables supplémentaires : layout, stack frontend, conventions de manifests ou organisation des fichiers.
Pour un projet existant ou imposé par un tiers, PGR doit d'abord préserver les contraintes externes réellement obligatoires. Toute divergence avec le profil préféré devient une règle project-specific ou une exception explicite et bornée ; elle ne modifie pas automatiquement le profil général.
Cette distinction évite deux erreurs opposées : forcer le skeleton interne d'une organisation sur un dépôt qu'elle ne contrôle pas, ou affaiblir ses propres conventions sous prétexte qu'un framework accepte plusieurs formes.
## Profils composites
PGR ne crée pas automatiquement un profil composite pour chaque combinaison possible.
@@ -194,6 +204,14 @@ Aucune contradiction non résolue de même niveau n'a été identifiée entre le
La revue beta n'a pas justifié l'activation d'une famille `700 — frameworks` ou `800 — architecture`. Les règles réellement transversales rencontrées restent déjà possédées par `RULES.md`, les familles existantes ou le présent modèle architectural.
## Activation de la famille framework en `0.0.5`
La réconciliation indépendante de KSP et Games apporte le premier framework suffisamment répété pour activer la famille `700` : Tauri 2. Le profil `710-TAURI.md` se compose au-dessus de Rust/Cargo et, selon les targets, des profils plateforme applicables.
Cette activation ne change pas la hiérarchie : Tauri ne devient propriétaire ni des règles Rust générales, ni d'Android, ni du choix du frontend. Il possède uniquement l'orchestration Tauri, sa configuration/capabilities, ses hooks, sa version applicative et les gates d'intégration propres au framework.
La famille `800 — architecture` reste réservée : aucune règle nouvelle n'en justifie encore la matérialisation.
La réconciliation a également confirmé qu'une règle générale ne doit pas être recopiée comme si le profil spécifique en devenait propriétaire. Un profil peut rappeler une conséquence spécifique au contexte, mais doit référencer l'owner général lorsque le contrat lui-même n'est pas spécialisé.
## Instanciation par skeleton

View File

@@ -0,0 +1,222 @@
<!-- file: docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md -->
<!-- version: 16 -->
# Plan 0.0.5 — réconciliation réelle avec KSP et sasedev-games
## Statut et sources
Plan historique créé pendant `0.0.5-alpha.1` conformément à `prompts/005-V0_0_5_START_PROMPT.md`. La release a confronté PGR à deux baselines autoritaires fournies sans présumer qu'elles avaient déjà adopté PGR.
Sources contrôlées :
```text
pgr-v0.0.4.zip 7137bb084f017362323a2043d13ba71490be15b8786adc8d645981bf455b80ae
khadhroony-solana-project-v0.3.16.zip db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0
games-v0.3.5.zip 5f5fdee1a271386766d059657d6eaa831acdc263b3e722fed738808335c0a105
```
Le rapport externe `pgr-0.0.4-publish.001-report.md` est `PASS` et annonce le même hash pour la stable PGR. L'étude détaillée et les limites de l'environnement sont consignées dans [`../studies/001-KSP_GAMES_RECONCILIATION.md`](../studies/001-KSP_GAMES_RECONCILIATION.md).
## Mission et garde-fous
`0.0.5` doit améliorer PGR à partir des besoins réellement répétés dans KSP et Games sans transformer leurs conventions historiques en règles universelles.
Les deux projets ne sont pas encore gouvernés par PGR. Par conséquent :
- aucune divergence de leurs archives n'est qualifiée de défaut PGR par défaut ;
- aucune migration de KSP ou Games n'appartient au scope de cette release ;
- aucune ancienne tranche, nomenclature ou chaîne history n'est reconstruite rétroactivement ;
- tout nouvel owner PGR doit correspondre à un besoin réutilisable et non à un simple copier-coller de projet ;
- les règles métier, architecture, protocoles, distribution et infrastructure spécifiques restent locales.
## Résultat du cadrage alpha.1
L'audit classe la majorité des mécanismes de gouvernance, documentation, validation et Rust/Cargo comme déjà `OWNED` par PGR. Les améliorations candidates réellement communes sont bornées à quatre axes :
1. **prompt de démarrage de projet** : le contrat actuel de `210-WORKFLOW.md` est plus minimal que les prompts opératoires éprouvés dans les deux corpus ;
2. **contrats documentaires locaux** : les deux projets utilisent un registre de responsabilités de fichiers/familles qui peut devenir un pattern facultatif de la fondation ;
3. **façade Rust et export completeness** : les deux projets partagent pratiquement le même audit spécialisé, alors que PGR n'en fournit pas encore de version réutilisable ;
4. **Tauri** : le framework est utilisé indépendamment et de manière répétée dans les deux corpus, ce qui fournit enfin une base réelle pour activer la famille framework `700` sans généraliser Vite/Web/JNI au-delà des preuves disponibles.
Les audits composites, le Web autonome, WASM, JNI, règles serveur, distribution Games et frontières métier KSP restent différés ou locaux.
## REPLAN alpha.4 — lifecycle inter-release et horizon
La gate `alpha.3.fix.1` a validé son correctif local. Une revue séparée du plan, de `ROADMAP.md` et des historiques de conception PGR `0.0.1` à `0.0.4` a ensuite confirmé un besoin général non couvert explicitement par le forecast initial : PGR sait piloter une release active, mais n'assigne pas encore assez strictement **où/quand sa fermeture et sa transition sont préparées**, ni **quand l'horizon des versions futures doit être rediscuté**.
Les historiques montrent que plusieurs briques avaient déjà été décidées séparément — moment du `CHANGELOG`, `ROADMAP`, prompt suivant, `REPLAN`, abandon/supersession, continuation inter-session, handoff et distinction `rel.001`/`publish.001`. Le problème n'est donc pas d'inventer un second workflow, mais de les relier dans le contrat du plan et d'éviter qu'elles restent implicites.
Ce constat déclenche le point de décision `REPLAN` prévu par `alpha.1`. Aucun travail antérieur n'est invalidé. Les anciennes tranches `alpha.4` à `alpha.6` sont décalées d'un numéro et restent dans le scope.
## Trajectoire prévisionnelle
### alpha.1 — audit indépendant, étude et plan
Vérifier la stable PGR, son rapport post-tag et les deux archives externes ; exécuter les audits statiques disponibles ; classifier les écarts ; créer l'étude et le présent plan. Aucun changement normatif, script réutilisable, profil framework ou skeleton nouveau dans cette tranche.
### alpha.2 — contrat générique des prompts de démarrage
Renforcer l'owner `210-WORKFLOW.md` pour les prompts versionnés sans recopier les règles locales KSP/Games. Fournir une spécification réutilisable couvrant au minimum : baseline autoritaire, ordre de lecture, état déjà validé, mission/résultat, scope/hors-scope, décisions gelées/ouvertes, première gate, trajectoire prévisionnelle et validations.
Le template manuel `003-NEW_SESSION_PROMPT.md` reste distinct : il sert au court message remis après publication, alors que cette tranche porte le prompt versionné du projet.
### alpha.3 — pattern de contrats de fichiers/familles
Étendre `300-DOCUMENTATION.md` et la fondation `001` uniquement si nécessaire pour décrire un registre local facultatif des fichiers ou familles dont le rôle et les règles de modification méritent d'être explicites. Fournir une spécification/template documentaire courte plutôt qu'un catalogue universel.
Le pattern ne doit pas imposer `FILE_CONTRACTS.md` à un petit projet, ni recopier les rôles déjà possédés par PGR. Le correctif `alpha.3.fix.1` a en outre renforcé l'audit des tables Markdown fenced sans modifier ce scope fonctionnel.
### alpha.4 — REPLAN : clôture, transition et revue d'horizon
Formaliser le lifecycle inter-release qui manquait au plan initial :
- imposer à un plan de release suffisamment complexe d'attribuer noir sur blanc les responsabilités de clôture/transition ;
- définir la revue d'horizon sans imposer un nombre artificiel de versions futures ;
- expliciter le passage `idea/study → décision durable → roadmap/plan` ;
- confirmer qu'une discussion stratégique peut vivre dans une session séparée sans changer `VERSION` ;
- rendre spontané le message manuel de reprise lorsque la prochaine cible est connue ;
- permettre la livraison `history courant + delta suivant` dans une même réponse lorsque la suite est déterministe, tout en maintenant deux archives et l'ordre commit/apply ;
- fournir un pattern réutilisable de plan de release pour l'adoption future dans KSP, Games et d'autres projets.
Cette tranche ne commence pas l'audit Rust ni le profil Tauri.
### alpha.5 — audit Rust de façade/export completeness
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` 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.
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.
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
La preuve commune est confirmée : `6` applications Tauri KSP et `2` applications Tauri Games partagent Tauri 2, un host Rust/Cargo, `tauri-build`, `tauri.conf.json`, des capability files, des hooks frontend et un `frontendDist`. Les valeurs applicatives divergent suffisamment pour exclure toute copie de ports, fenêtres, CSP, bundles, plugins ou commandes frontend particulières.
La première livraison active `700-FRAMEWORKS.md` et `710-TAURI.md`, mais la gate opérateur est `FAIL` pour deux raisons : le contrôle Python ciblé du delta contient une chaîne multilignes invalide et la revue fonctionnelle exige de mieux distinguer le profil Tauri universel des conventions d'instanciation des projets contrôlés. `alpha.6.fix.1` corrige donc le gate et explicite que le host Tauri hérite intégralement des règles Rust sélectionnées, que WASM reste normalement une unité séparée et que l'ajout manuel de plugins à partir de leur documentation officielle est une stratégie valide.
Les préférences récurrentes de l'organisation — frontend sous `frontend/`, Vite, TypeScript, Sass, Bootstrap et contrôle explicite des dépendances — sont consignées comme **candidat de skeleton**, pas comme invariants de `710-TAURI.md`. Un projet imposé de l'extérieur peut conserver une structure différente via ses règles project-specific.
La politique `gen/` n'est pas généralisée : les sorties purement reproductibles restent générées, mais les projets mobiles modifiés intentionnellement doivent être classifiés selon leur rôle réel. L'échec du POC Tauri mobile Games ne constitue pas un rejet général de Tauri Android/iOS pour les autres produits.
La gate `alpha.6.fix.1` révèle un second défaut de quoting Python (`l'extérieur` dans une chaîne simple) et la revue opérateur demande une seconde passe sur les règles réelles KSP/Games. `alpha.6.fix.2` corrige le gate et renforce la convention des projets contrôlés avec le layout direct sans `src-tauri/`, `lib.rs`/`main.rs`/`tauri.rs`, TS-RS lorsque le frontend TypeScript partage des DTO Rust, modules de fenêtres/responsabilités et préférence pour l'intégration explicite des plugins. Les règles Rust générales et la nomenclature des crates restent volontairement dans `alpha.7/alpha.8`.
La gate mécanique de `alpha.6.fix.2` est propre, mais la revue fonctionnelle révèle une troisième divergence : KSP et Games avaient tous deux converti la réduction du churn de version en préférence pour l'absence de `tauri.conf.json > version` et la stagnation de `package.json`. Cette solution ne doit pas devenir une règle PGR. `alpha.6.fix.3` restaure la classification générale `CANONICAL` / `MIRROR-EXACT` / `MIRROR-STABLE` et définit pour Tauri/frontend une synchronisation de jalon `X.Y.Z` par défaut, sans bump à chaque tranche.
Comme `fix.3` atteint le seuil de revue obligatoire des correctifs, le plan est réexaminé : résultat `CONTINUE`. Le scope Tauri reste cohérent et borné après cette correction ; les chantiers Rust `alpha.7`/`alpha.8` et la décision de skeleton `alpha.9` restent séparés et ne sont pas absorbés par le fix.
### alpha.7 — profil Rust strict, chemins et nomenclature explicite
La réconciliation des règles versionnées KSP/Games retient KSP comme référence stricte et Games comme contrôle de généralisation. Pour une nouvelle baseline contrôlée, `510-RUST_CARGO.md` impose désormais Rust 2024, réserve `use` aux traits réellement nécessaires, exige les chemins publics explicites les plus courts pour les autres symboles, les réexports internes `self::`, la façade crate-root et `crate::Item`, et conserve le contrôle de flux explicite sans `unwrap`, `expect` ni `?` en production.
La nomenclature des crates devient également explicite : préfixe projet/abréviation obligatoire ; responsabilité puis rôle/cible lisibles lorsque ce classificateur apporte une information réelle. La forme recommandée est `<project>-<responsibility>-<role-or-target>` sans figer les vocabulaires KSP/Games ni imposer de renommage rétroactif. Les projets externes ou historiquement contraints peuvent documenter un override borné.
Le skeleton Rust/Cargo et le guide de manifest reflètent ces choix. Aucun gros audit composite n'est importé ; le marqueur KSP spécifique des trait imports n'est pas généralisé et les contrôles déjà possédés par Clippy, rustc ou l'audit de façade restent chez leur owner.
La gate mécanique `alpha.7` est propre, mais la revue fonctionnelle post-gate montre que cette première matérialisation ne donnait pas encore de contrat architectural aux suffixes. `alpha.7.fix.1` complète donc la tranche sans la replanifier : `-api` devient un vrai contrat extensible/passif et n'est jamais créé par symétrie ; `-lib` identifie l'implémentation réutilisable ; les exécutables sont des packages distincts et minces par défaut ; `tracing` et un owner de logging commun sont fortement recommandés dans les workspaces multi-runtime. L'ancienne préférence KSP pour un worker mixte lib/bin n'est pas généralisée. Tauri reste l'exception framework actuelle et impose des noms de targets lib/bin réellement distincts.
### alpha.8 — formatage Rust structurel et audits monotâches
La réconciliation des anciens audits composites KSP/Games attribue désormais chaque propriété à un owner précis. Rustfmt reste propriétaire du formatage et du tri qu'il sait réellement réécrire dans un bloc contigu. Les audits PGR existants conservent `blank_lines` et `const_blocks`; quatre extractions monotâches complètent le profil : `item_spacing`, `module_order`, `trait_import_blocks` et `reexport_blocks`.
La mesure sur l'ensemble des sources `crates/**/*.rs` est propre pour les quatre nouveaux audits dans KSP `0.3.16` (`603` fichiers) et Games `0.3.5` (`87` fichiers). `blank_lines` est également propre sur ces corpus. `const_blocks` reste explicitement opt-in : son exécution globale remonte `18` signaux KSP, dont des tables/schémas à ordre intentionnel, contre `0` Games.
Le profil strict des nouvelles baselines contrôlées retient donc les règles mécaniques de lignes vides, séparation exacte des items distincts, modules production/tests, bloc de traits et blocs de réexports. PGR n'automatise pas un ordre global `enum → struct → impl → fn`, ni un tri visible/privé lorsque la structure métier peut être plus informative. Aucun parser Rust général ni audit composite n'est introduit.
### alpha.9 — décision de skeleton Tauri pour projets contrôlés — DEFER
La revue de readiness confirme que le host est suffisamment cadré : `510-RUST_CARGO.md` possède les règles Rust et le packaging, `710-TAURI.md` possède le layout contrôlé, l'exception `lib + bin`, `tauri.rs`, TS-RS à la frontière host/frontend, les plugins/capabilities, les versions produit et les gates d'orchestration Tauri. Les `8` hosts observés confirment également le layout direct sans `src-tauri/`, `frontend/`, Vite et TypeScript.
En revanche, PGR ne possède encore aucun profil Web/Vite/TypeScript/Sass. Le candidat organisationnel Vite + TypeScript + Sass + Bootstrap ne peut donc pas être instancié sans que le skeleton devienne de fait propriétaire de règles frontend, ce qui contredirait `220-SKELETONS.md` et le modèle de profils. La décision `alpha.9` est donc `DEFER` : aucun skeleton Tauri `003` n'est créé dans `0.0.5`.
Le futur skeleton reste un candidat explicite et devra au minimum composer la fondation de gouvernance, le skeleton Rust/Cargo, le profil Tauri et les owners Web alors disponibles. Il pourra ensuite sélectionner le layout direct, `frontend/`, Vite + TypeScript + Sass, Bootstrap comme baseline de présentation lorsque pertinente, TS-RS pour les DTO Rust partagés, intégration explicite des plugins/capabilities et séparation de WASM par défaut. Ces choix ne deviennent pas des règles Web dans la présente release.
La condition de sortie du `DEFER` est la matérialisation d'owners Web suffisants pour posséder la structure des sources frontend, la chaîne Vite/TypeScript, Sass/CSS, les artefacts de build et leur validation sans duplication normative dans le skeleton. La décision détaillée est enregistrée dans `docs/studies/002-TAURI_SKELETON_DECISION.md`.
### beta.1 — réconciliation intégrée
Rejouer la matrice de l'étude sur l'état PGR obtenu. Vérifier : owner unique, navigation, absence de règles KSP/Games aspirées par accident, caractère facultatif des patterns/audits concernés, cohérence des skeletons et domaines encore différés.
La réconciliation intégrée confirme que les owners créés ou renforcés par `0.0.5` restent bornés à leur responsabilité : prompt versionné, contrats documentaires facultatifs, lifecycle inter-release, profil/audits Rust stricts et profil Tauri. Les architectures métier KSP/Games, Android/JNI spécifique, Web autonome et distribution restent hors du noyau central tant qu'un owner réutilisable n'est pas justifié.
Le `DEFER` du skeleton Tauri contrôlé est suffisamment décidé pour devenir une trajectoire explicitement différée dans `ROADMAP.md`, sans numéro de release réservé. Les trois candidats plus larges de revue d'horizon restent volontairement hors de la roadmap engagée tant qu'une décision macro n'existe pas.
La mission `0.1.0` reste suffisamment connue : `prompts/006-V0_1_0_START_PROMPT.md` est préparé dans cette tranche comme prompt candidat de reprise. Il exige l'audit complet de la stable `0.0.5`, la consolidation d'un plan `0.1.0-alpha.1` proportionné et une revue d'horizon avant fermeture ; il ne transforme pas le skeleton Tauri différé en obligation de `0.1.0`.
Cette tranche réconcilie ainsi la documentation durable et confirme que le présent plan possède un contrat de fermeture exécutable avant `rc.1`.
La gate opérateur de `beta.1` est `PASS` : `48/48` tests Python, les trois audits Markdown, le contrôle d'intégration ciblé et les contrôles Git sont propres. La phase beta est donc fermée sans correctif.
### rc.1 — scope gelé et gate candidate
La RC gèle le scope fonctionnel de `0.0.5` sur les capacités déjà validées : contrats de reprise/lifecycle, profil Rust strict et ses audits monotâches, profil Tauri 2, réconciliation KSP/Games et décision `DEFER` du skeleton Tauri contrôlé. Aucun nouvel owner, skeleton, template, script d'audit ou domaine technique n'entre dans la candidate.
`CHANGELOG.md` reçoit l'entrée candidate `0.0.5` orientée utilisateurs. `ROADMAP.md` reste inchangé : `0.0.5` demeure en cours jusqu'à la promotion stable, `0.1.0` reste la prochaine release engagée et le skeleton Tauri reste `(d)` sans numéro de release. `prompts/006-V0_1_0_START_PROMPT.md` est vérifié comme prompt candidat de reprise et n'est pas modifié si son contrat est déjà correct.
La gate RC rejoue la suite Python complète, les audits Markdown et un contrôle d'intégrité historique de toutes les tranches `0.0.5`. Elle vérifie également la présence des owners/audits effectivement livrés et l'absence de skeleton Tauri `003`. Les validations lexicales déjà couvertes par la suite de tests ne sont pas dupliquées par un nouvel audit composite.
Aucune version postérieure à `0.1.0` n'est inventée à cette étape.
La gate opérateur de `rc.1` est `PASS` : `48/48` tests Python, les trois audits Markdown, le contrôle d'intégrité des `16` histories antérieurs et les contrôles Git sont propres. Le scope fonctionnel est donc gelé pour la promotion stable.
### stable — 0.0.5
Promouvoir mécaniquement la RC validée : synchroniser `VERSION`, README/index, `ROADMAP.md`, `CHANGELOG.md` et statut du présent plan sans ajouter de nouveau besoin commun. Après history PASS, commit et tag `v0.0.5`, vérifier séparément l'archive publiée.
La vérification post-tag doit produire, si le mécanisme reste retenu, `pgr-0.0.5-publish.001-report.md` extérieur au tag. Le message manuel final doit alors indiquer spontanément à l'opérateur de fournir l'archive taggée et ce rapport avec le prompt `006` lors de la session `0.1.0`.
La promotion `rel.001` clôt le présent plan sans modifier le scope fonctionnel : `VERSION = 0.0.5`, `ROADMAP.md` marque `0.0.5` terminé, le changelog perd son marqueur `candidate`, les index décrivent le plan comme historique et le prompt `006` reste conditionnel à la publication effective du tag et à son rapport post-tag PASS.
La gate opérateur de `rel.001` est toutefois `FAIL` pour un défaut strictement local à sa commande de validation : l'assertion Python qui cherchait le heading `## 0.0.5` a été livrée avec une chaîne simple coupée par un retour à la ligne et provoque `SyntaxError: unterminated string literal`. Les audits Markdown et les contrôles Git exécutés ensuite sont propres ; aucun défaut du contenu stable n'est établi. `rel.001.fix.1` corrige uniquement cette preuve de clôture, conserve `VERSION = 0.0.5` et ne rouvre pas le scope fonctionnel.
## Clôture et transition
Le présent plan attribue dès maintenant les responsabilités de fermeture :
- la documentation directement liée à chaque capacité évolue dans sa tranche ;
- `beta.1` réalise la réconciliation durable globale et prépare le prompt suivant si sa mission est connue ;
- `rc.1` porte le scope freeze, l'entrée `CHANGELOG` candidate, la vérification `ROADMAP` et le prompt suivant candidat ;
- `rel.001`/stable finalise `CHANGELOG`, marque `0.0.5` terminée dans `ROADMAP`, ferme le présent plan et effectue le promotion gate pré-tag ;
- `publish.001` vérifie l'archive réellement produite après le tag et fournit la preuve externe requise par la reprise suivante ;
- aucune de ces opérations ne doit être repoussée implicitement à la session `0.1.0`.
## Revue d'horizon
Pour PGR, la prochaine release engagée est `0.1.0 — première baseline réutilisable`. La trajectoire au-delà n'est pas suffisamment décidée et reste volontairement ouverte ; ce n'est pas un défaut de roadmap.
`0.0.5` ne doit donc pas inventer `0.1.1`, `0.2.0` ou un catalogue de versions futures. Le plan de `0.1.0-alpha.1` devra en revanche identifier un moment de revue d'horizon avant sa fermeture, une fois la baseline réutilisable suffisamment concrète pour juger la suite. Cette revue pourra se tenir dans une session stratégique séparée sans modifier `VERSION`.
Ses sorties autorisées seront : étendre la roadmap avec des objectifs réellement décidés, maintenir l'horizon ouvert avec un prochain déclencheur, différer/mettre en pause la trajectoire ou constater qu'un objectif macro est accompli.
Trois **candidats d'horizon**, sans numéro de release réservé ni engagement de réalisation, sont maintenant identifiés pour cette revue :
1. maturation de profils techniques composables par langage, framework, target/plateforme et préoccupation transverse — par exemple observability/logging, Web/TypeScript/Vite/Sass, Android, WASM, networking ou persistence — toujours à partir de corpus réels avant normalisation ;
2. profil de projet documentaire/spécification, avec `saselang-bible` comme corpus pilote possible pour tester gouvernance normative, terminologie, décisions, exemples, compatibilité et traduction lorsque la documentation est elle-même le produit ;
3. audit du noyau PGR hors développement logiciel afin d'identifier ce qui est réellement universel et ce qui doit rester dans un profil `software`, sans inventer les règles métier de domaines spécialisés.
Ces sujets restent au stade `horizon candidat`. Ils ne deviennent `Study`, `ROADMAP` ou plan de release qu'après décision explicite selon `300-DOCUMENTATION.md`.
## Points de décision
Un REPLAN est requis si :
- le contrat de prompt devient une copie exhaustive des règles plutôt qu'un artefact de reprise ;
- le pattern de contrats de fichiers ajoute plus de cérémonie qu'il n'en retire ;
- le lifecycle de clôture impose des documents ou versions futures sans valeur opérationnelle ;
- la revue d'horizon devient un quota de versions à prévoir plutôt qu'un mécanisme de décision ;
- l'audit de façade Rust ne peut pas être séparé proprement des conventions strictes KSP/Games ;
- le profil Tauri universel absorbe des choix de skeleton/frontend qui devraient rester project-specific ou appartenir à un owner Web ;
- la réconciliation Rust stricte ne peut pas être séparée en owners fiables et audits monotâches ;
- le skeleton Tauri n'apporte pas de décisions réutilisables au-delà du profil framework et des conventions explicites de projets contrôlés ;
- une autre divergence des corpus apparaît comme un besoin commun critique non couvert par ce plan.
## Stratégie de validation
Les tranches documentaires rejouent les trois audits Markdown et la suite Python PGR. Toute modification d'un script ajoute ou adapte des tests ciblés avant la suite globale. Les templates/configurations structurées sont parsés ou exercés avec l'outil le plus fiable disponible.
PGR ne compile pas KSP ou Games comme gate de sa propre release : ces projets servent de corpus de pression. Une validation croisée MAY exécuter leurs audits statiques sur leurs archives fournies lorsque cela vérifie une propriété précise, mais ne remplace jamais leurs propres gates opérateur.
La toolchain Cargo n'étant pas disponible dans l'environnement de préparation de `alpha.1`, aucune preuve Cargo externe n'est revendiquée pour cette tranche. La gate PGR reste documentaire et structurelle.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/000-README.md -->
<!-- version: 11 -->
<!-- version: 13 -->
# Index des règles PGR
@@ -12,7 +12,7 @@ Les règles générales actuellement nécessaires restent dans `RULES.md`. La fa
## Familles actives
- [`200-GOVERNANCE.md`](200-GOVERNANCE.md) — versioning, lifecycle des releases, fixes, cadrage, replan et abandon/supersession.
- [`210-WORKFLOW.md`](210-WORKFLOW.md) — contrats des artefacts workflow, ordre delta/history/commit, reprise et baselines de release.
- [`210-WORKFLOW.md`](210-WORKFLOW.md) — contrats des artefacts workflow, ordre delta/history/commit, prompts versionnés, reprise et baselines de release.
- [`220-SKELETONS.md`](220-SKELETONS.md) — contrat commun des skeleton specifications, sélection des profils/templates, décisions, overrides et validation.
- [`300-DOCUMENTATION.md`](300-DOCUMENTATION.md) — rôles des documents, taxonomie, révisions internes, documents vivants et snapshots historiques.
- [`400-VALIDATION.md`](400-VALIDATION.md) — matrice de validation, preuves proportionnelles, tests/audits/diagnostics, doubles de test et autorité du gate opérateur.
@@ -21,6 +21,8 @@ Les règles générales actuellement nécessaires restent dans `RULES.md`. La fa
- [`520-JAVA_MAVEN_GRADLE.md`](520-JAVA_MAVEN_GRADLE.md) — profil Java et spécialisations de build Maven/Gradle.
- [`600-PLATFORMS.md`](600-PLATFORMS.md) — contrat commun des profils plateforme.
- [`610-ANDROID.md`](610-ANDROID.md) — profil plateforme Android et règles réutilisables de compatibilité, packaging et validation.
- [`700-FRAMEWORKS.md`](700-FRAMEWORKS.md) — contrat commun des profils framework et de leur composition avec les owners déjà applicables.
- [`710-TAURI.md`](710-TAURI.md) — profil Tauri 2 et règles réutilisables d'orchestration native/frontend, sécurité et validation.
Le modèle transversal d'ownership, d'applicabilité et de composition est décrit dans [`../architecture/003-RULE_PROFILE_MODEL.md`](../architecture/003-RULE_PROFILE_MODEL.md).

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/200-GOVERNANCE.md -->
<!-- version: 2 -->
<!-- version: 4 -->
# Governance rules
@@ -38,9 +38,20 @@ Lorsqu'un dépôt utilise le fichier racine `VERSION`, ce fichier :
Un manifeste technique MAY refléter exactement `VERSION` lorsque son propre contrat l'exige. Un autre mécanisme de versioning MAY rester indépendant lorsqu'il possède une sémantique différente, par exemple un `versionCode` Android, une révision de document ou une version de protocole.
PGR MUST NOT imposer une égalité globale entre tous les champs contenant un numéro de version.
PGR MUST NOT imposer une égalité globale entre tous les champs contenant un numéro de version. Pour éviter à la fois les divergences silencieuses et le churn mécanique, chaque valeur de version durable SHOULD être classée selon son rôle réel. Les catégories réutilisables sont notamment :
Un champ de version qui n'est ni requis par l'écosystème ni utile au projet SHOULD être omis plutôt que maintenu comme duplication décorative.
- `CANONICAL` — identité SemVer exacte de l'unité de release, normalement portée par `VERSION` ;
- `MIRROR-EXACT` — miroir technique qui suit exactement `VERSION`, prereleases et fixes compris ;
- `MIRROR-STABLE` — miroir du jalon produit `X.Y.Z` ciblé, stable pendant `X.Y.Z-alpha.*`, `beta.*`, `rc.*` et leurs fixes ;
- `INDEPENDENT-COUNTER` — compteur technique monotone possédant sa propre sémantique ;
- `INDEPENDENT-SCHEMA` — version de protocole, format ou schéma indépendante du produit ;
- `DOCUMENT-REVISION` — révision interne d'un document ou artefact documentaire.
Seul un `MIRROR-EXACT` est normalement modifié à chaque changement de `VERSION`. Un `MIRROR-STABLE` SHOULD être fixé ou vérifié lorsque la cible `X.Y.Z` est ouverte, puis ne pas être réécrit uniquement parce qu'une tranche passe de `alpha.N` à `alpha.N+1`, à un `.fix.N`, à une beta ou à une RC. La promotion stable vérifie qu'il représente toujours la release publiée.
Lorsqu'une prerelease est réellement distribuée comme artefact installable distinct et que son écosystème exige une identité de package propre, le champ concerné peut devenir `MIRROR-EXACT` ou suivre des jalons explicitement définis. Cette décision appartient au plan de versioning/packaging du projet ; elle ne doit pas résulter d'une égalité globale implicite.
Un champ de version qui n'est ni requis par l'écosystème ni utile au projet SHOULD être omis plutôt que maintenu comme duplication décorative. Réciproquement, PGR MUST NOT imposer la suppression d'un champ utile uniquement pour éviter de le modifier à chaque prerelease : il faut d'abord lui attribuer la bonne classe de synchronisation.
## Schéma SemVer PGR
@@ -89,7 +100,9 @@ Cette convention pré-1.0 complète SemVer sans modifier sa syntaxe.
- les études ou décisions encore ouvertes ;
- les principaux risques et contraintes ;
- la stratégie de validation et les smokes nécessaires ;
- le dimensionnement des tranches.
- le dimensionnement des tranches ;
- lorsque la release possède un plan dédié, les responsabilités de clôture et de transition vers la suite ;
- l'état de l'horizon de roadmap et, lorsqu'il devient court ou incertain, le moment prévu pour le réexaminer.
Une tranche cible généralement un volume de travail cohérent et vérifiable. Une durée de l'ordre de 15 à 30 minutes MAY servir d'heuristique de découpage, mais MUST NOT être traitée comme une deadline ou un quota.
@@ -145,6 +158,8 @@ ABANDON
Une interruption de conversation ou de session MUST NOT créer artificiellement une nouvelle version. Le même état de version et de tranche continue tant que le scope reste valable.
Une revue stratégique de versions futures MAY également se tenir dans une session séparée. Cette frontière de conversation n'est ni une release, ni une phase SemVer, ni une raison de modifier `VERSION`. Si la discussion produit une décision durable à matérialiser dans le dépôt, cette modification passe ensuite par une tranche gouvernée appropriée.
## Abandon et supersession
Une version ou prerelease commencée n'est pas forcée vers stable uniquement pour préserver une séquence esthétique.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/210-WORKFLOW.md -->
<!-- version: 3 -->
<!-- version: 5 -->
# Workflow artifact rules
@@ -49,13 +49,25 @@ docs/plans/
Un plan :
- SHOULD être créé pendant le cadrage lorsque plusieurs tranches ou décisions doivent être coordonnées ;
- MAY évoluer lorsque le plan change réellement ;
- SHOULD être créé ou consolidé pendant `alpha.1` lorsque plusieurs tranches ou décisions doivent être coordonnées ;
- MAY évoluer lorsque la réalité invalide le forecast ;
- SHOULD NOT être modifié à chaque tranche uniquement pour recopier l'avancement ;
- MUST distinguer prévision et état réellement observé ;
- MUST NOT remplacer ROADMAP, delta ou history.
Un changement substantiel du plan MAY être porté par une tranche REPLAN conformément aux règles de gouvernance.
Lorsqu'un plan existe pour une release gouvernée par le workflow PGR, il MUST aussi rendre explicites, de manière proportionnée :
- la mission, le scope et le hors-scope actuellement applicables ;
- la stratégie de validation et les gates qui conditionnent réellement la fermeture ;
- la trajectoire prévisionnelle des responsabilités, sans transformer le forecast en promesse immuable ;
- une section ou un bloc **clôture et transition** indiquant où seront traités la réconciliation documentaire durable, le `CHANGELOG`, la mise à jour de `ROADMAP`, le prompt ou handoff de reprise, la fermeture du plan et la promotion/publication ;
- l'état de la **revue d'horizon** : soit elle n'est pas nécessaire parce que la roadmap possède déjà un horizon suffisant, soit son déclencheur et le moment où elle sera instruite sont identifiés.
Le plan MUST NOT inventer une version future uniquement pour remplir la rubrique de transition. Lorsque la cible suivante n'est pas suffisamment décidée, il l'indique explicitement et planifie une revue d'horizon ou un handoff approprié plutôt que de fabriquer un numéro, une mission ou un prompt.
Un changement substantiel du plan MAY être porté par une tranche REPLAN conformément aux règles de gouvernance. Le corps du plan reste l'état de pilotage actuellement applicable ; les deltas et histories conservent le détail historique des changements. PGR n'impose pas un journal `Revisions` alimenté à chaque tranche.
À la clôture `completed`, `abandoned` ou `superseded`, le plan devient principalement historique. Il ne doit plus être réécrit pour refléter silencieusement la connaissance acquise dans des releases ultérieures.
## Delta
@@ -137,6 +149,18 @@ L'archive history contient uniquement le ou les fichiers history à appliquer po
Delta et history MUST rester des livraisons séparées afin que la preuve observée ne soit pas confondue avec l'intention prospective.
Lorsque la gate courante est `PASS` et que la tranche suivante est déjà déterminée par le plan sans décision ouverte, le générateur SHOULD préparer et remettre dans la même réponse :
```text
archive history de la tranche courante
+
archive delta de la tranche suivante
```
Les deux archives restent séparées. Cette optimisation de flux n'autorise jamais l'application du delta suivant avant l'application du history courant et le commit complet de la tranche.
Le delta suivant SHOULD NOT être anticipé lorsqu'une décision opérateur, un REPLAN non résolu, un abandon/supersession, une preuve externe post-tag ou toute autre dépendance peut encore modifier sa baseline ou son scope.
## Commit de tranche
Le commit d'une tranche doit représenter l'état complet de cette tranche, y compris son history.
@@ -173,9 +197,9 @@ S'il existe :
Un manifest ne doit pas exister vide uniquement pour satisfaire une structure théorique.
## Prompt de démarrage
## Prompt de démarrage versionné
Un prompt de démarrage transmet la mission et le contexte nécessaires à une nouvelle version ou à une reprise explicitement prévue.
Un prompt de démarrage versionné transmet le contrat de reprise nécessaire pour ouvrir une nouvelle version depuis une baseline autoritaire sans reconstruire implicitement l'état du projet depuis une conversation antérieure, un workspace non fourni ou une mémoire informelle.
Le chemin canonique est :
@@ -185,19 +209,55 @@ prompts/NNN-VX_Y_Z_START_PROMPT.md
`NNN` est monotone à l'échelle du dépôt.
Le prompt :
Le prompt MUST rester un artefact de reprise et de cadrage. Il référence les règles, plans, histories, handoffs ou documents techniques qui possèdent déjà le détail ; il MUST NOT les recopier exhaustivement ni devenir une seconde source normative concurrente.
- doit transmettre la mission, la baseline et les règles de reprise utiles ;
- MUST NOT devenir une copie exhaustive du plan ou de l'historique ;
- SHOULD être finalisé à la clôture stable de la version précédente lorsqu'il prépare la version suivante.
### Contrat minimal
Une interruption au milieu d'une version MUST NOT produire artificiellement le prompt de la version suivante.
Le prompt MUST couvrir, sous des titres adaptés au projet plutôt que selon une forme imposée, les éléments suivants lorsqu'ils sont applicables :
Lors d'une nouvelle session ouverte depuis une stable publiée, une courte consigne manuelle MAY identifier l'archive du tag, le rapport post-tag lorsqu'il existe réellement, le prompt versionné à lire et la première gate. [`../templates/003-NEW_SESSION_PROMPT.md`](../templates/003-NEW_SESSION_PROMPT.md) décrit une forme réutilisable ; elle ne remplace ni le prompt versionné ni la preuve de publication.
1. **baseline autoritaire et cible** — identifier la source exacte depuis laquelle la version doit être ouverte, la version cible et la première tranche attendue ; préciser les limites matérielles de cette source, par exemple l'absence normale de `.git` dans une archive de tag ;
2. **ordre de lecture** — nommer les points d'entrée normatifs, plans, histories, handoffs ou documents de subsystem qui doivent être lus avant de modifier le projet lorsqu'un ordre est nécessaire pour reconstruire correctement le contexte ;
3. **état déjà acquis** — résumer uniquement les résultats stables nécessaires à la reprise et les décisions qui ne doivent pas être redébattues sans preuve nouvelle ; cet état MUST rester traçable à la baseline ou aux artefacts cités ;
4. **mission et résultat attendu** — décrire ce que la version doit rendre vrai ou livrer, sans substituer un catalogue de tâches à l'objectif ;
5. **scope et hors-scope** — borner les changements autorisés et les sujets explicitement différés afin d'éviter qu'une reprise étende silencieusement la release ;
6. **décisions gelées et questions ouvertes** — distinguer les choix déjà établis des décisions que la première tranche doit encore instruire ; une valeur non prouvée MUST rester ouverte plutôt qu'être figée arbitrairement ;
7. **première gate** — définir l'objectif de la première tranche, les lectures/audits ou preuves préalables attendus et, lorsque nécessaire, les travaux interdits avant fermeture de ce gate ;
8. **trajectoire prévisionnelle** — fournir uniquement le forecast nécessaire pour dimensionner la release et ordonner les responsabilités ; il reste non contraignant et peut être scindé, fusionné ou replanifié selon les preuves ;
9. **validation et responsabilité** — rappeler les familles de gates pertinentes, les validations déjà imposées par les règles applicables et toute séparation importante entre préparation, exécution opérateur et preuve observée, sans déclarer réussie une commande qui n'a pas été exécutée.
Un projet MAY regrouper plusieurs de ces éléments dans une même section lorsque le résultat reste explicite et non ambigu. Le prompt n'a pas à reproduire littéralement cette numérotation.
### Sources externes et données manquantes
Lorsqu'une release dépend de snapshots, corpus, rapports ou autres sources externes, le prompt MUST identifier celles qui sont réellement autoritaires pour la reprise et définir le comportement lorsque l'une d'elles manque.
L'absence d'une source attendue MUST être signalée. Le travail doit alors être borné à ce que les preuves disponibles permettent réellement de conclure ; le prompt MUST NOT autoriser à reconstituer silencieusement une version, un commit, un contenu ou un résultat depuis une conversation antérieure ou un workspace non fourni.
Une preuve externe complète la baseline seulement pour les propriétés qu'elle établit. Elle ne remplace pas l'audit de la source effectivement fournie à la nouvelle session.
### Décisions acquises et détails locaux
Le prompt SHOULD citer les décisions acquises qui empêchent une régression ou une réouverture inutile, mais il SHOULD pointer vers leur owner documentaire lorsque leur explication détaillée existe déjà.
Les contraintes locales de langage, plateforme, framework, architecture ou métier restent chez leurs owners respectifs. Leur présence dans un prompt de projet ne les élève pas au rang de règle générale PGR.
Le prompt MAY imposer un ordre de lecture détaillé ou une matrice de questions lorsque la taille du projet le justifie. Un petit projet SHOULD conserver une forme plus courte si elle couvre le même contrat de reprise.
### Création et clôture
Lorsque la version suivante est suffisamment connue, son prompt SHOULD être amorcé pendant la consolidation finale, vérifié pendant la RC et finalisé pendant la promotion stable. Il MUST être présent dans la stable taggée qui prépare cette reprise.
Lorsque la cible suivante n'est pas suffisamment connue, PGR MUST NOT fabriquer un prompt de version, un numéro ou une mission pour respecter une cérémonie. Le plan et la roadmap indiquent alors que l'horizon reste ouvert et un handoff `NEXT` ou une revue d'horizon porte la continuité nécessaire.
Une interruption au milieu d'une version MUST NOT produire artificiellement le prompt de la version suivante. La reprise de la tranche courante utilise son état committé et, si nécessaire, un handoff conforme au présent document.
Lorsqu'une stable publiée possède une cible suivante connue, le générateur MUST remettre spontanément à l'opérateur, après la vérification post-tag applicable, une courte consigne manuelle identifiant l'archive du tag, le rapport post-tag lorsqu'il existe réellement, le prompt versionné à lire et la première gate. [`../templates/003-NEW_SESSION_PROMPT.md`](../templates/003-NEW_SESSION_PROMPT.md) décrit cette forme courte ; elle reste distincte du prompt versionné et ne remplace ni son contrat détaillé ni la preuve de publication.
Si une preuve post-tag externe est requise pour la prochaine reprise, le prompt versionné MAY annoncer à l'avance son nom attendu mais MUST la décrire comme une preuve future extérieure au tag. La consigne manuelle finale ne la présente comme `PASS` qu'après sa production et sa vérification réelles.
## Handoff
Un handoff est un snapshot de continuité utilisé lorsqu'un travail doit être repris sans changer artificiellement de version ou de tranche.
Un handoff est un snapshot de continuité utilisé lorsqu'un travail doit être repris à une frontière de session ou de responsabilité sans changer artificiellement de version ou de tranche. Un REPLAN traité entièrement dans la même session n'a pas besoin d'un handoff uniquement pour laisser une trace : delta, history et plan possèdent déjà cette trace.
Le chemin canonique est :
@@ -217,9 +277,21 @@ SUPERSEDE
NEXT
```
Le handoff SHOULD enregistrer uniquement les informations nécessaires à une reprise fiable : état de version/tranche, résultat déjà acquis, travail restant, risques ou décisions ouvertes et commandes pertinentes.
Selon le besoin réel, un handoff SHOULD permettre de retrouver sans mémoire conversationnelle :
Une fois livré comme snapshot de reprise, il SHOULD être traité comme historique plutôt que réécrit continuellement.
- son type, la version source et une cible éventuelle ;
- la baseline autoritaire et le dernier état opérateur validé ;
- le dernier delta validé et tout delta livré mais encore en attente ;
- l'état du plan, le travail terminé et le travail restant ;
- les décisions acquises, les questions ouvertes et les validations restantes ;
- le prompt applicable lorsqu'il existe ;
- l'instruction exacte de reprise et, lorsque cela aide l'opérateur, un court texte copiable vers la nouvelle session.
Un petit handoff n'a pas à répéter les champs sans valeur pour son cas. Il référence les owners existants plutôt que recopier leurs règles.
Un `NEXT` après stable peut transmettre une version suivante connue. Si l'horizon est volontairement ouvert, il peut au contraire transmettre explicitement le besoin de revue d'horizon sans inventer une release.
Une fois livré comme snapshot de reprise, un handoff SHOULD être traité comme historique plutôt que réécrit continuellement.
Un changement de conversation ou de session n'est jamais, à lui seul, une raison de changer `VERSION`.
@@ -257,6 +329,10 @@ post-tag verification
Un identifiant opérationnel tel que `rel.001` ou `publish.001` MAY être utilisé pour distinguer une clôture ou une vérification de publication sans devenir une nouvelle version SemVer produit.
`rel.001` prouve l'état stable avant création du tag. Une preuve `publish.001`, lorsqu'elle existe, porte sur l'artefact réellement publié après le tag et reste donc extérieure au tag qu'elle vérifie. Elle ne revalide pas fonctionnellement la release.
Lorsqu'un rapport `publish.001` est un prérequis de la session suivante, la clôture post-tag MUST indiquer explicitement à l'opérateur qu'il doit fournir ce rapport avec l'archive taggée. Le démarrage suivant effectue toujours son propre audit d'intake de la baseline ; il ne traite pas pour autant la stable précédente comme une release encore en attente de validation.
## Conservation et immutabilité
Deltas, histories, handoffs, plans historiques, études et autres preuves utiles SHOULD être conservés lorsqu'ils expliquent une décision ou permettent une reprise fiable.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/300-DOCUMENTATION.md -->
<!-- version: 2 -->
<!-- version: 4 -->
# Documentation rules
@@ -47,9 +47,9 @@ Il SHOULD NOT devenir un fichier monolithique contenant toutes les règles spéc
### `ROADMAP.md`
`ROADMAP.md` décrit la trajectoire macro du projet : objectifs de versions, grandes étapes et ordre fonctionnel attendu.
`ROADMAP.md` décrit la trajectoire macro **engagée** du projet : objectifs de versions, grandes étapes et ordre fonctionnel attendu. Elle n'est ni un catalogue de toutes les possibilités futures, ni une obligation de prévoir arbitrairement plusieurs versions à l'avance.
Il MUST NOT devenir un journal des prereleases ou recopier les histories de tranche.
Il MUST NOT devenir un journal des prereleases ou recopier les histories de tranche. Une idée, une étude ou une possibilité encore spéculative reste chez son owner tant qu'une décision suffisante ne justifie pas son entrée dans la trajectoire engagée.
Lorsqu'un projet utilise des statuts de roadmap, la convention PGR est :
@@ -62,6 +62,30 @@ Lorsqu'un projet utilise des statuts de roadmap, la convention PGR est :
Une autre convention MAY être adoptée par un projet si elle est explicitement définie et cohérente.
Une roadmap à horizon fini est valide. Le projet MUST NOT inventer un numéro de version, un objectif ou un engagement uniquement pour éviter que la roadmap se termine.
### Revue d'horizon
Une revue d'horizon est une activité de gouvernance qui décide si la trajectoire macro connue doit être étendue, maintenue volontairement ouverte, mise en pause ou considérée accomplie. Elle n'est pas une nouvelle phase SemVer.
Une revue SHOULD être planifiée lorsque l'un des signaux suivants devient pertinent :
- la release courante ou la suivante est la dernière entrée engagée de la roadmap alors que le projet doit continuer ;
- un jalon important change ce qu'il est raisonnable de planifier ensuite ;
- plusieurs ideas/studies ou décisions récentes peuvent modifier la trajectoire ;
- les hypothèses qui fondaient les versions futures ont été invalidées par le travail réel.
La revue MAY avoir lieu dans une tranche de la release courante ou dans une session stratégique séparée. Une session séparée ne change pas `VERSION` par elle-même.
Une revue d'horizon produit l'un des résultats utiles suivants :
- **étendre** `ROADMAP.md` avec des objectifs suffisamment décidés ;
- **laisser l'horizon ouvert** et enregistrer le déclencheur ou le moment de la prochaine revue ;
- **différer/mettre en pause** une trajectoire sans fabriquer de remplaçant ;
- **constater l'accomplissement** de l'objectif macro si aucune suite n'est nécessaire.
Le plan actif SHOULD indiquer si une telle revue est nécessaire avant sa fermeture et où elle aura lieu.
### `CHANGELOG.md`
`CHANGELOG.md` décrit les changements utiles aux utilisateurs ou intégrateurs d'une release.
@@ -76,6 +100,26 @@ Un document sous `docs/objectives/` décrit la mission durable, les objectifs, c
Il MUST NOT être remplacé par `ROADMAP.md` : les objectifs expliquent pourquoi le projet existe et ce qu'il cherche à garantir ; la roadmap décrit comment sa trajectoire est découpée.
## Registre local de contrats de fichiers ou familles
Un projet MAY maintenir un registre local lorsque plusieurs fichiers ou familles durables ont des responsabilités, sources de vérité ou règles de modification qui ne sont pas suffisamment évidentes depuis leur nom, leur écosystème ou leurs owners existants.
Ce registre est un outil de désambiguïsation, pas un inventaire exhaustif de l'arborescence. Il MUST NOT être créé uniquement pour recopier les rôles déjà définis par PGR, un manifeste, une toolchain ou une documentation locale plus appropriée. Un petit projet dont les responsabilités sont déjà claires SHOULD ne pas créer ce document.
Chaque entrée utile SHOULD identifier au minimum :
- le fichier, motif de chemin ou famille concernée ;
- sa responsabilité durable ;
- la règle ou le déclencheur pertinent de modification.
Selon le besoin, une entrée MAY aussi préciser une source de vérité, le caractère généré/versionné, un owner local ou une validation associée. Ces champs supplémentaires ne doivent être ajoutés que s'ils réduisent réellement une ambiguïté.
Le nom, l'emplacement et la forme du registre appartiennent au projet destinataire. PGR n'impose ni `FILE_CONTRACTS.md`, ni une table Markdown, ni un catalogue de chaque fichier. Une liste courte, une table ou une autre forme lisible sont équivalentes si le contrat reste explicite.
Lorsqu'un owner existant possède déjà la règle détaillée, le registre SHOULD le référencer plutôt que dupliquer son contenu. Il est mis à jour lorsque le contrat d'une famille change réellement ; l'ajout d'un fichier conforme à une famille déjà décrite ne force pas une nouvelle entrée.
[`../templates/004-FILE_FAMILY_CONTRACTS.md`](../templates/004-FILE_FAMILY_CONTRACTS.md) fournit un pattern documentaire court pour les projets qui choisissent d'adopter ce registre.
## Taxonomie documentaire
La taxonomie doit rester sémantique et proportionnée. Les familles communes comprennent notamment :
@@ -111,6 +155,22 @@ Si une décision est déjà imposée par une contrainte explicite et qu'aucune a
Les décisions durables doivent ensuite être reflétées dans l'architecture, les règles, objectifs ou autres documents vivants qui possèdent réellement ce contrat.
La maturation typique MAY suivre :
```text
idea
↓
study
↓
decision durable / architecture / règle
↓
roadmap ou plan lorsqu'un engagement de réalisation existe
↓
implementation
```
Toutes les possibilités n'ont pas à parcourir cette chaîne. Une idée ou une étude MAY être rejetée, différée, superseded ou rester inconclusive. Une conclusion `retained` ne crée pas automatiquement une promesse de roadmap : le passage vers `ROADMAP.md` exige encore une décision d'engagement macro explicite.
## Documents vivants et snapshots historiques
PGR distingue :

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/510-RUST_CARGO.md -->
<!-- version: 2 -->
<!-- version: 7 -->
# Profil Rust / Cargo
@@ -11,9 +11,13 @@ Il spécialise [`500-LANGUAGES.md`](500-LANGUAGES.md) pour les responsabilités
Une règle plus spécifique, par exemple Android, WASM, Tauri ou une frontière FFI, MAY raffiner ce profil dans son scope sans modifier la politique Rust générale.
Pour un **nouveau projet contrôlé** par l'organisation, PGR sélectionne par défaut le profil strict décrit ci-dessous : provenance des symboles visible, façade crate-root explicite, contrôle de flux explicite et noms de crates lisibles. Un projet existant ou imposé par un tiers MAY conserver des contraintes différentes via des règles project-specific ou des exceptions bornées ; cette adaptation ne rend pas le profil strict moins canonique pour les nouvelles baselines contrôlées.
## Toolchain et édition
Un nouveau projet Rust SHOULD utiliser une toolchain stable récente compatible avec ses dépendances et ses cibles. Une toolchain nightly, beta ou autre prerelease nécessite une justification explicite.
Un nouveau projet Rust contrôlé MUST utiliser l'édition Rust 2024 avec une toolchain stable compatible avec ses dépendances et ses cibles, sauf contrainte externe explicitement documentée. Une toolchain nightly, beta ou autre prerelease nécessite une justification explicite.
`2024` est la baseline normative actuelle des projets contrôlés ; PGR ne bascule pas automatiquement vers une future édition au seul motif qu'elle existe. Une évolution de cette baseline demande une décision PGR explicite et une validation de compatibilité.
PGR MUST NOT imposer un pin exact de toolchain uniquement parce qu'un projet source historique utilisait cette version. Un fichier `rust-toolchain.toml` ou équivalent SHOULD être ajouté uniquement lorsqu'un besoin réel de reproductibilité, de composant ou de compatibilité l'exige.
@@ -34,6 +38,62 @@ Un monorepo contenant plusieurs unités de release indépendantes MUST NOT fabri
Le répertoire `crates/` est le placement PGR recommandé pour les packages Rust d'un workspace structuré, sauf layout imposé par un framework, une plateforme ou un projet existant.
### Nomenclature des crates dans un projet contrôlé
Une crate appartenant à un projet contrôlé MUST commencer par le nom court canonique du projet ou une abréviation documentée et stable. Le reste du nom SHOULD rendre immédiatement lisibles sa responsabilité principale puis, lorsqu'il apporte une information réelle, son rôle ou sa cible.
La forme recommandée est :
```text
<project>-<responsibility>-<role-or-target>
```
Exemples de classificateurs utiles selon le projet :
```text
lib
api
worker
job
cli
desk
desktop
tauri
wasm
server
```
Cette liste n'est ni exhaustive ni un vocabulaire imposé. Un suffixe ou classificateur ne doit pas être ajouté par symétrie : `api` suppose un vrai contrat/extensibility boundary, `worker` un service continu identifiable, `cli` une interface ligne de commande, etc. À l'inverse, un rôle important ne doit pas être masqué dans un nom générique comme `common`, `core` ou `utils` sans responsabilité suffisamment précise.
Un projet historique MAY conserver un ordre local déjà stable, par exemple un rôle placé avant la responsabilité, si sa nomenclature est documentée et cohérente. PGR n'impose aucune campagne de renommage rétroactive uniquement pour adopter la grammaire recommandée.
### Les suffixes portent un contrat architectural
Dans un projet contrôlé, les classificateurs de nom ne sont pas décoratifs. Lorsqu'ils sont utilisés, ils décrivent une responsabilité que l'architecture et le graphe de dépendances doivent respecter.
Une crate terminée par `-api` porte principalement des contrats réutilisables : traits, types, DTOs, identifiants et primitives nécessaires à des implémentations séparées. Elle SHOULD rester passive et sans runtime. Une crate `-api` purement déclarative MUST NOT dépendre d'une crate de logging/tracing uniquement pour satisfaire une convention transversale. Une instrumentation n'y est ajoutée que si la crate possède un comportement réel à observer.
Une crate `-api` MUST NOT être créée uniquement pour obtenir une paire de noms symétrique. Elle est justifiée lorsqu'au moins une frontière réelle existe : implémentations multiples ou remplaçables, implémentation externe, backend injectable, lifecycle partagé ou contrat public dont l'implémentation doit pouvoir vivre dans une autre crate. Lorsqu'une seule implémentation est réellement prévue et qu'aucune frontière de contrat séparée n'apporte de valeur, une seule crate `-lib` est préférée.
Une crate terminée par `-lib` porte une implémentation ou fonctionnalité réutilisable. Par défaut, elle est une crate bibliothèque sans cible binaire. Des familles comme `worker`, `job`, `store`, `transport` ou `scenario` MAY apparaître dans la responsabilité avant le suffixe `-lib` lorsqu'elles décrivent ce que la bibliothèque implémente ; le suffixe terminal `-lib` continue alors d'indiquer qu'il s'agit d'une unité réutilisable et non d'un launcher.
### Séparation bibliothèques / exécutables
Hors exception de framework ou plateforme explicitement documentée, un package contrôlé MUST NOT combiner une bibliothèque réutilisable et une cible binaire uniquement par commodité. La logique réutilisable appartient à une ou plusieurs crates `-lib`; l'exécutable appartient à un package distinct qui les consomme.
Le nom d'un package exécutable SHOULD rendre visible son rôle de processus ou d'interface, par exemple `-cli`, `-desk`, `-worker`, `-job` ou `-server` lorsque ces termes décrivent réellement le launcher. Le binaire SHOULD rester une couche de composition mince : initialisation du processus, verrou/single-instance lorsque nécessaire, arguments CLI, wiring des bibliothèques, lifecycle et arrêt. La logique métier ou technique réutilisable ne doit pas être dupliquée dans `main.rs`.
Tauri constitue l'exception framework actuellement reconnue par PGR à cette séparation par défaut : son profil peut demander un package mixte bibliothèque + binaire pour servir simultanément les entrées desktop/mobile. Cette exception est possédée par [`710-TAURI.md`](710-TAURI.md) et ne justifie pas de généraliser les packages mixtes aux workers, jobs ou autres exécutables. Toute autre exception mixte demande une règle locale et une raison technique identifiable.
## Diagnostics structurés et ownership du logging
Pour les nouveaux projets Rust contrôlés, `tracing` est le mécanisme recommandé de diagnostics structurés. La façade `log` MAY être conservée comme bridge de compatibilité lorsqu'une dépendance l'impose, mais elle SHOULD NOT devenir le mécanisme principal d'un nouveau projet lorsqu'aucune contrainte externe ne le requiert.
Lorsqu'un workspace possède plusieurs crates runtime partageant les mêmes règles de diagnostics, il SHOULD centraliser la configuration de logging/tracing dans une crate transverse dédiée, typiquement `<project>-logging-lib` ou un nom équivalent conforme à la nomenclature locale. Dès que plusieurs exécutables ou bibliothèques ont besoin du même subscriber, des mêmes filtres/sinks ou du même lifecycle de guard, cet owner commun SHOULD remplacer les initialisations dupliquées. Un petit projet à exécutable unique n'est pas obligé de créer une crate dédiée sans valeur de réutilisation.
Cette crate commune possède normalement la composition de `tracing-subscriber`, l'usage de `tracing-appender` lorsque nécessaire, les filtres/sinks/formats partagés et le lifecycle d'initialisation. Une bibliothèque métier ou technique MAY émettre des événements structurés, mais SHOULD NOT installer silencieusement un subscriber global. Les crates `-api` purement déclaratives restent normalement sans dépendance de logging.
## Manifests et dépendances Cargo
Dans un workspace, une dépendance tierce réellement partagée par plusieurs crates SHOULD posséder sa contrainte de version canonique sous `[workspace.dependencies]`.
@@ -84,26 +144,57 @@ Lorsqu'une FFI ou une plateforme impose réellement une opération `unsafe`, l'e
Une exception FFI locale n'autorise donc pas à remplacer globalement `unsafe_code = "forbid"` par une politique permissive pour toutes les crates.
## API, visibilité et rustdoc
## API, visibilité, imports et chemins
Tout élément `pub` ou `pub(crate)` SHOULD posséder une rustdoc utile au point de déclaration lorsque son contrat dépasse une implémentation triviale locale.
Dans le profil strict d'un projet contrôlé, tout élément `pub` ou `pub(crate)` MUST posséder une rustdoc utile au point de déclaration. Toute réexportation `pub use` ou `pub(crate) use` visible au crate-root MUST également porter une rustdoc adjacente utile. Une rustdoc qui répète seulement le nom du symbole ne satisfait pas ce contrat.
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.
Les modules d'implémentation restent privés. `pub mod` MUST NOT être utilisé pour exposer paresseusement une arborescence interne ; `pub(super)` et `pub(in ...)` MUST NOT remplacer une façade crate-root explicite dans une baseline contrôlée.
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.
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.
Les réexports d'éléments internes commencent par `self::` :
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.
```rust
pub use self::foo::Foo;
pub(crate) use self::bar::InternalBar;
```
Un réexport de crate externe utilise son chemin public canonique. Les réexports groupés par accolades, les glob imports et les alias `as` servant à raccourcir ou masquer un nom MUST NOT être utilisés. Une collision durable se résout en donnant au symbole son nom canonique dans son module propriétaire.
### `use` réservé aux traits
Dans le profil strict, `use` MUST NOT importer une constante, fonction, structure, énumération, union, alias de type, module ou macro uniquement pour raccourcir son chemin. Les déclarations `use` sont réservées aux traits lorsqu'une résolution de méthode ou une contrainte du langage exige réellement leur mise en scope.
Un import de trait reste au niveau du module et ne mélange pas de symboles non-traits. Un projet MAY ajouter un marqueur de commentaire spécifique à son audit local, mais PGR n'impose pas un commentaire tel que `rust-rules: trait-import` : ce marqueur est un mécanisme d'enforcement, pas la règle sémantique elle-même.
Pour un élément fourni par une crate externe, le code utilise directement le **chemin public le plus court** exposé par cette crate, par exemple :
```rust
std::path::Path
serde_json::Value
other_crate::Item
```
et non un `use` non-trait destiné à raccourcir ce chemin. Un chemin interne plus long n'est pas préféré lorsque la crate externe expose déjà une façade publique plus courte.
### Façade crate-root et références intra-crate
Dès qu'un item partagé possède une 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 et n'est pas réexporté. Dans un fichier de tests unitaires rattaché au module parent, un élément privé du parent peut être référencé via `super::PrivateItem`; un élément `pub` ou `pub(crate)` partagé continue d'être référencé via `crate::Item`.
Les autres crates consomment l'API publique du crate propriétaire via `owner_crate::Item`, jamais via ses modules internes. La visibilité d'un item MUST NOT être élargie uniquement pour permettre son test.
Ces conventions sont plus strictes que le Rust idiomatique minimal, mais elles sont retenues pour les projets contrôlés afin de rendre immédiatement visibles l'ownership et la provenance des symboles. Un projet externe contraint peut définir un override explicite ; l'exception ne devient pas la nouvelle règle PGR.
## Contrôle de flux et erreurs
Dans le code de production du profil strict PGR :
- `unwrap()` et `expect()` MUST NOT servir à traiter un chemin d'erreur récupérable ;
- `unwrap()` et `expect()` MUST NOT être utilisés ;
- `panic!` MUST NOT représenter une erreur métier récupérable ;
- les retours explicites sont la convention par défaut ;
- l'opérateur `?` est désactivé par le profil Clippy strict afin de conserver des chemins d'erreur explicites.
- les retours explicites sont obligatoires lorsque la fonction possède une valeur de retour ;
- l'opérateur `?` MUST NOT être utilisé ; les chemins `Ok`/`Err` et conversions d'erreur restent explicites.
Une règle plus spécifique MAY définir une politique d'erreur différente lorsqu'elle possède une justification architecturale explicite. PGR ne généralise pas l'interdiction d'une bibliothèque d'erreur particulière à partir d'un seul projet source.
@@ -129,12 +220,23 @@ L'édition de ce fichier MUST rester cohérente avec l'édition réellement util
Après une modification Rust, `cargo fmt --all` applique le formatage canonique puis `cargo fmt --all -- --check` vérifie l'état livré. Une modification produite uniquement par le formatter officiel ne crée pas artificiellement une nouvelle révision documentaire d'un fichier si aucun contenu réel n'a changé.
Le profil de style strict supplémentaire peut être adopté par un projet qui souhaite appliquer les conventions KSP/Games. Dans ce scope choisi, les lignes vides consécutives SHOULD être supprimées, ainsi que les lignes vides à l'intérieur des fonctions/méthodes et des définitions `struct`/`enum`. Ces propriétés ne sont pas entièrement possédées par Rustfmt. L'audit facultatif `scripts/audit_rust_blank_lines.py` contrôle ces deux formes d'espacement sur les seuls fichiers Rust sélectionnés.
Le profil structurel strict fait partie du défaut des nouvelles baselines Rust contrôlées. Un projet externe, historique ou imposé peut documenter un override borné ; cet override ne réduit pas le profil canonique des nouveaux projets. L'owner reste partagé entre `rustfmt` pour ce qu'il sait réécrire de manière fiable et des audits read-only monotâches pour les propriétés de structure qu'il ne possède pas.
Les constantes de module qui constituent un bloc homogène sans ordre sémantique SHOULD être regroupées dans l'ordre `pub`, `pub(crate)`, privé, puis par nom dans chaque visibilité : aucune ligne vide entre constantes d'une même visibilité et exactement une entre niveaux différents. `scripts/audit_rust_const_blocks.py` vérifie cette convention uniquement sur les constantes au niveau racine des fichiers choisis. Son exécution requiert une sélection explicite des fichiers pour lesquels cet ordre est réellement souhaité. Une table de protocole, une séquence de migrations ou tout autre ordre contractuel conserve son ordre propre ; le lecteur ne doit pas réordonner une telle séquence pour faire passer l'audit.
Les lignes vides consécutives MUST être supprimées. Aucune ligne vide n'est conservée à l'intérieur du corps d'une fonction ou méthode, ni à l'intérieur d'une définition `struct` ou `enum`. `scripts/audit_rust_blank_lines.py` contrôle ces deux propriétés sur les fichiers Rust sélectionnés.
L'ordre des `use`, réexports et déclarations `mod` que Rustfmt sait réordonner dans ses groupes appartient au formatteur ; les modules de test après les modules de production et l'organisation d'une façade relèvent de leur contrat local, avec revue ou audit dédié seulement si celui-ci apporte une preuve fiable. Les règles rustdoc et d'API conservent leurs propriétaires ci-dessus. Les scripts PGR ne prétendent pas parser toutes les macros ou les items Rust : une décision sémantique reste une revue.
Deux items Rust distincts et voisins d'un même scope — fonctions/méthodes, `struct`, `enum`, `union`, `trait`, blocs `impl` ou changements de catégorie/visibilité — MUST être séparés par exactement une ligne vide lorsqu'aucune syntaxe intermédiaire ne possède elle-même l'espacement. `scripts/audit_rust_item_spacing.py` contrôle cette séparation sans tenter d'interpréter les macros ou de posséder les blocs compacts `const`/`static`/`type`.
Les déclarations compactes d'un même type et d'une même visibilité SHOULD former un bloc sans ligne vide lorsque l'ordre n'a pas de signification sémantique. Pour les constantes de module sélectionnées, l'ordre canonique est `pub`, `pub(crate)`, privé, puis le nom dans chaque visibilité ; exactement une ligne vide sépare deux niveaux de visibilité. `scripts/audit_rust_const_blocks.py` possède uniquement ce contrat `const` au niveau racine. Il MUST NOT être appliqué aveuglément à une table de protocole, une séquence de migrations, un schéma, une table de dispatch ou toute autre série dont l'ordre est contractuel. Les blocs `static`/`type` peuvent suivre la même présentation par convention locale, mais PGR ne leur ajoute pas encore un audit dédié uniquement pour atteindre une symétrie théorique.
Les déclarations `mod` de production d'un même fichier MUST précéder les modules de tests conditionnels ; les deux groupes sont ordonnés alphabétiquement lorsque l'ordre ne porte aucune sémantique locale. `scripts/audit_rust_module_order.py` contrôle cette propriété. `rustfmt` reste propriétaire de l'ordre qu'il réécrit déjà à l'intérieur d'un bloc contigu.
Les `use` non exportés autorisés par le profil strict — donc normalement des imports de traits — restent au niveau du module, avant les déclarations Rust métier et dans un bloc contigu sans ligne vide interne. `scripts/audit_rust_trait_import_blocks.py` contrôle uniquement cette disposition ; il ne prétend pas déterminer lexicalement si le symbole importé est effectivement un trait. L'ordre intra-bloc reste celui de `cargo fmt --all`.
Au crate-root, les réexports `pub use` constituent un bloc avant les `pub(crate) use`. Aucune ligne vide n'est admise à l'intérieur d'un bloc de même visibilité et exactement une ligne vide sépare les deux niveaux lorsqu'ils existent tous deux. `scripts/audit_rust_reexport_blocks.py` contrôle cette structure sans imposer un second tri : l'ordre intra-bloc reste celui produit par Rustfmt. Les rustdocs adjacentes restent possédées par les règles d'API, pas par cet audit de forme.
Lorsqu'un `struct` et ses `impl` forment une unité locale, les `impl` SHOULD suivre la structure ; de même, les éléments visibles SHOULD généralement précéder les helpers privés lorsque cela rend l'API plus lisible. PGR n'impose toutefois pas un ordre global automatique `enum → struct → impl → fn` : un ordre métier, protocolaire ou pédagogique peut être plus important. Cette propriété reste une revue tant qu'un audit fiable ne peut pas distinguer la structure informative d'un ordre sémantique.
Les scripts PGR restent des analyseurs lexicaux ciblés, pas un parser Rust général. Une macro complexe, un item généré ou une syntaxe nouvelle peut nécessiter une revue humaine ou l'owner Rust officiel. Une règle déjà possédée par `rustfmt`, rustc ou Clippy MUST NOT être réimplémentée uniquement pour multiplier les gates.
## Configuration Cargo et artefacts générés
`.cargo/config.toml` est le propriétaire naturel d'une configuration Cargo partagée par le projet lorsqu'une telle configuration est nécessaire.
@@ -192,4 +294,8 @@ Lorsqu'une règle Rust devient objectivement automatisable dans un projet ou ske
Un éventuel wrapper d'orchestration MAY appeler plusieurs audits, mais chaque audit propriétaire doit conserver une responsabilité identifiable et rester read-only.
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é ; le dépôt PGR lui-même ne contient pas de workspace Rust sur lequel une telle gate fournirait une preuve réelle.
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 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 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

@@ -0,0 +1,125 @@
<!-- file: docs/rules/700-FRAMEWORKS.md -->
<!-- version: 2 -->
# Règles de profils framework
## Scope
Cette famille définit le contrat commun des profils liés à un framework réellement utilisé par le projet.
Un framework peut combiner plusieurs owners déjà applicables — langage/écosystème, plateforme, frontend, packaging ou runtime — sans devenir propriétaire de leurs règles générales. Son profil possède uniquement les contraintes et l'orchestration ajoutées par le framework lui-même.
## Responsabilités de la famille
Un profil framework MAY posséder les règles spécifiques concernant notamment :
- les fichiers de configuration réellement possédés par le framework ;
- la relation entre plusieurs toolchains que le framework orchestre ;
- les commandes de développement, build, packaging ou génération qui passent par son CLI ;
- les hooks exécutés par ce CLI et leur ordre de responsabilité ;
- les permissions, capabilities, manifests ou métadonnées propres au framework ;
- les artefacts générés par le framework et leur classification source/généré ;
- la cohérence de version lorsque le framework possède une version applicative distincte ou dérivée ;
- les gates d'intégration que les outils sous-jacents ne prouvent pas isolément.
Un profil framework MUST NOT recopier les règles d'un langage, d'une plateforme ou d'un build tool lorsqu'elles restent valides sans spécialisation.
## Composition
Un framework s'ajoute aux profils réellement applicables au projet. Il n'annule pas silencieusement les conventions des owners qu'il compose.
Par exemple :
```text
GENERAL
+ Rust / Cargo
+ Tauri
```
ou, pour une cible Android :
```text
GENERAL
+ Rust / Cargo
+ Android
+ Tauri
```
Lorsque PGR gouverne la baseline du projet, les règles des profils hérités restent applicables dans leur scope complet tant qu'un override plus spécifique n'est pas documenté. Un framework ne constitue pas à lui seul une justification pour affaiblir une convention de langage, de plateforme ou de sécurité.
Un projet existant ou imposé de l'extérieur MAY conserver des contraintes différentes. Ces écarts appartiennent alors au niveau project-specific ou à une exception explicite et bornée ; ils ne doivent pas être remontés comme nouvelle règle générale du framework.
La présence d'un frontend WebView n'active pas automatiquement un futur profil Web/Vite/WASM. Ces technologies restent sélectionnées selon les faits du projet et leurs propres owners.
## Profil framework et convention d'instanciation
Le profil framework décrit ce qui reste vrai pour toute adoption conforme du framework. Une skeleton specification ou une baseline locale MAY choisir en plus un layout, un bundler, un langage frontend, un package manager ou des bibliothèques préférées pour les projets que l'organisation contrôle.
Ces valeurs d'instanciation ne deviennent pas des invariants du framework :
```text
profil framework
→ responsabilités et contraintes propres au framework
skeleton / projet local
→ choix réutilisables de structure et de stack
```
Cette séparation permet d'appliquer PGR à un projet imposé sans lui forcer le skeleton préféré des nouveaux projets.
## Ownership des commandes d'intégration
Lorsqu'un framework possède un CLI qui orchestre plusieurs étapes, la validation de l'intégration SHOULD utiliser ce CLI plutôt que rejouer manuellement ses sous-commandes comme si elles prouvaient le même contrat.
Une commande sous-jacente reste utile pour une preuve ciblée : compiler le backend ne prouve pas les hooks frontend ; construire le frontend seul ne prouve pas l'intégration native ; un build framework complet n'est pas requis pour une modification purement documentaire qui n'invalide aucune de ces propriétés.
La gate sélectionnée reste proportionnée au scope et à la maturité conformément à [`400-VALIDATION.md`](400-VALIDATION.md).
## Sources, générés et configuration locale
Un framework MAY produire des répertoires générés, projets de plateforme, schémas, bindings ou artefacts de build. Leur simple origine framework ne suffit pas à décider leur politique Git.
Le profil spécifique doit distinguer :
```text
source intentionnelle
configuration durable
artefact reproductible
généré modifié intentionnellement
généré purement transitoire
secret / configuration locale
```
PGR MUST NOT appliquer une règle globale « tout `gen/` est ignoré » ou « tout généré est versionné » sans examiner le rôle réel de l'artefact.
Les secrets et valeurs locales restent possédés par les règles générales et les profils plateforme applicables.
## Version du framework et version produit
La version du framework/de ses dépendances et la version du produit sont deux contrats distincts.
Un projet MAY utiliser une plage compatible, un pin ou une politique de mise à jour adaptée au framework ; PGR ne copie pas une version observée dans un projet source comme règle générale.
Lorsqu'un framework expose une version applicative qui représente la même unité de release que `VERSION`, le profil spécifique doit définir une source de vérité ou une synchronisation explicite afin d'éviter plusieurs valeurs indépendantes.
## Validation spécialisée
Un profil framework doit expliquer ce que prouvent ses commandes propres et ce qu'elles ne prouvent pas.
Selon le scope, la preuve peut être :
- un check du langage/backend ;
- un build/test frontend ;
- un lancement framework avec smoke ciblé ;
- un build/package framework ;
- une validation de plateforme réelle lorsque le framework cible Android, iOS ou une autre plateforme.
Aucune de ces catégories n'est obligatoire à chaque tranche par simple présence du framework.
## Nomenclature documentaire
Le document `700-FRAMEWORKS.md` possède le contrat commun de la famille.
Les profils spécifiques utilisent la plage `710` à `799`. Leur numéro sert à la navigation et ne représente ni priorité normative, ni version du framework.
La résolution avec les autres profils suit [`../architecture/003-RULE_PROFILE_MODEL.md`](../architecture/003-RULE_PROFILE_MODEL.md).

213
docs/rules/710-TAURI.md Normal file
View File

@@ -0,0 +1,213 @@
<!-- file: docs/rules/710-TAURI.md -->
<!-- version: 5 -->
# Profil Tauri 2
## Rôle et applicabilité
Ce document possède les règles réutilisables propres à une application utilisant Tauri 2 comme host natif et orchestrateur d'intégration entre un host Rust et un frontend WebView.
Il complète [`700-FRAMEWORKS.md`](700-FRAMEWORKS.md) et [`510-RUST_CARGO.md`](510-RUST_CARGO.md). Les profils plateforme réellement ciblés, par exemple [`610-ANDROID.md`](610-ANDROID.md), restent applicables et possèdent leurs contraintes de SDK, packaging et smoke de plateforme.
Le profil est volontairement borné à Tauri 2. Une future génération incompatible du framework doit être réévaluée avant d'hériter automatiquement de ces règles.
## Héritage Rust du host Tauri
Un host Tauri est un projet Rust/Cargo. Tauri ne crée aucune exception implicite aux règles Rust sélectionnées par le projet.
Pour un projet dont PGR gouverne la baseline, le host Tauri MUST appliquer le profil Rust/Cargo et les conventions Rust locales retenues exactement comme une autre crate ou application Rust du même projet : édition, lints, safety, erreurs, formatage, façade, chemins canoniques, imports, organisation des sources et audits applicables restent possédés par leur owner Rust.
Un exemple généré par `create-tauri-app`, une documentation de plugin ou une macro Tauri MAY employer un style différent ; il doit être adapté aux règles Rust du projet lorsqu'il est intégré. La présence de Tauri ne justifie donc pas à elle seule `unwrap`, `expect`, `?`, un `use` non autorisé, un chemin interne non canonique ou un affaiblissement des lints si le profil Rust sélectionné les interdit.
Un projet Tauri existant ou imposé de l'extérieur MAY conserver d'autres conventions Rust. Ces différences doivent être traitées comme règles project-specific ou overrides explicites, pas comme un relâchement du profil Tauri général.
## Composition avec le frontend
Tauri ne détermine pas à lui seul le framework frontend, le bundler, le langage de style ou le package manager du projet.
PGR MUST NOT imposer universellement Vite, TypeScript, Sass, Bootstrap, Tailwind CSS, npm, pnpm, yarn, Bun, WASM ou une arborescence frontend particulière uniquement parce que Tauri est utilisé.
Le frontend possède ses propres règles et peut être sélectionné par une skeleton specification ou par la baseline locale du projet. Le profil Tauri possède seulement le contrat d'intégration entre ce frontend et le host natif.
Pour les projets que l'organisation contrôle entièrement, une skeleton MAY choisir une stack frontend par défaut et un layout stable. Cette décision appartient au skeleton et aux futurs profils Web applicables, pas au présent owner Tauri.
## WASM
WASM n'est pas une composante implicite d'un projet Tauri. Un package ou projet WASM SHOULD être traité comme une unité distincte appliquant ses propres profils Rust/Web/WASM, même s'il partage le même dépôt ou certaines crates avec une application Tauri.
Une application Tauri MAY consommer explicitement un artefact WASM lorsqu'un besoin architectural réel le justifie. Cette composition doit alors être documentée comme telle ; elle ne transforme pas les règles WASM en règles Tauri et ne justifie pas de placer systématiquement une chaîne WASM dans les hooks Tauri.
## Layout et fichiers possédés par Tauri
PGR MUST NOT imposer universellement un répertoire `src-tauri/` ni recopier le layout de `create-tauri-app`. Le projet peut utiliser le layout standard de Tauri ou une intégration différente lorsque la toolchain la supporte clairement.
Les éléments suivants appartiennent néanmoins au scope Tauri lorsqu'ils existent :
- le manifeste Cargo du host natif ;
- l'intégration build-time Tauri, généralement via `tauri-build` ;
- la configuration Tauri supportée par la version du framework, par exemple `tauri.conf.json` ;
- les capability files et permissions Tauri ;
- les icônes, ressources ou métadonnées de bundle référencées par la configuration ;
- les projets ou fichiers de plateforme produits/consommés par la toolchain Tauri.
Leur emplacement exact reste une décision du projet ou du skeleton retenu tant qu'il est supporté par la toolchain.
## Convention renforcée pour les projets contrôlés
Le contrat universel ci-dessus reste compatible avec des projets Tauri imposés de l'extérieur. Pour un **nouveau projet Tauri contrôlé par l'organisation** qui sélectionne le profil strict PGR, les conventions suivantes forment en revanche la baseline recommandée et ne doivent pas être abandonnées sans raison documentée.
Le host SHOULD être une crate Cargo mixte directement lisible, sans couche `src-tauri/` ajoutée uniquement pour reproduire `create-tauri-app`. Le layout recommandé est :
```text
<tauri-crate>/
├── Cargo.toml
├── build.rs
├── package.json
├── tsconfig.json
├── tauri.conf.json
├── capabilities/
├── src/
└── frontend/
```
Un fichier comme `vite.config.ts` appartient également à la racine lorsque Vite est le bundler sélectionné. Un projet externe MAY conserver `src-tauri/` ou une autre organisation lorsque cette structure est imposée ou qu'une migration n'apporte pas de valeur suffisante.
Dans le host Rust, le package mixte `lib + bin` constitue l'exception framework explicite à la séparation générale définie par `510-RUST_CARGO.md`. Pour une nouvelle baseline Tauri contrôlée :
- le package SHOULD déclarer explicitement une cible bibliothèque et une cible binaire ;
- leurs noms MUST être distincts même après la normalisation Cargo usuelle des tirets en underscores ; un modèle valide est package/bin `project-feature-desk` et lib `project_feature_desk_lib` ;
- `src/lib.rs` SHOULD rester une façade de modules/réexports et porter l'accès à la fonctionnalité applicative réutilisable ;
- `src/main.rs` SHOULD rester un launcher mince vers une fonction publique de la bibliothèque applicative ; il MAY posséder le verrou/single-instance, les arguments strictement process et le bootstrap exécutable qui ne peut pas appartenir à la bibliothèque ;
- `src/tauri.rs` SHOULD posséder l'assemblage Tauri et les wrappers `#[tauri::command]`, qui délèguent aux modules propriétaires plutôt que porter la logique métier ;
- une fenêtre ou responsabilité UI non triviale SHOULD posséder un module cohérent, avec sous-modules si nécessaire ; les helpers réellement partagés entre plusieurs fenêtres/responsabilités SHOULD être déplacés dans des modules communs dédiés ;
- un préfixe local comme `tw_` MAY être utilisé pour identifier les modules de fenêtres, mais PGR n'impose pas ce préfixe à tous les projets.
Lorsque le workspace possède une crate de logging/tracing commune, le host Tauri l'utilise pour initialiser le runtime de diagnostics. Un plugin Tauri de tracing sert alors d'adapter à la frontière desktop/mobile ; il ne remplace pas l'owner Rust de `tracing-subscriber`, des sinks ou du lifecycle de logging.
Lorsque le frontend sélectionné est TypeScript, les structures, énumérations et DTO Rust **possédés par le host** et partagés avec le frontend MUST utiliser TS-RS comme source de génération dans le profil strict, sauf incompatibilité technique explicitement documentée. Le contrat Rust reste la source de vérité ; les bindings générés ne sont pas modifiés manuellement et SHOULD rester hors Git lorsqu'ils sont entièrement reproductibles. Le skeleton MAY normaliser leur sortie sous `frontend/ts/bindings/`.
Pour les projets contrôlés, `frontend/` est le répertoire recommandé pour les sources Web versionnées. Vite + TypeScript + Sass constitue la stack candidate par défaut et Bootstrap est la baseline de présentation privilégiée lorsque le projet a besoin d'un framework CSS généraliste. Tailwind CSS n'est pas interdit par le profil Tauri, mais il n'est pas le choix PGR par défaut et son adoption demande une décision explicite du projet. Ces choix restent possédés par le futur skeleton et les profils Web correspondants.
L'intégration d'un plugin SHOULD préférer les étapes explicites décrites par sa documentation — dépendance Cargo, initialisation Rust, package frontend et capabilities nécessaires — afin que chaque modification reste visible et revue. `cargo tauri add` MAY être utilisé lorsque ce raccourci apporte une vraie valeur, mais il n'est pas la voie canonique imposée aux projets contrôlés.
WASM reste normalement une unité séparée de la crate Tauri. Une adaptation WASM réutilisable SHOULD donc posséder sa propre crate/package et ses propres profils ; elle n'est intégrée au host Tauri que lorsqu'une composition architecturale explicite l'exige.
## Ownership du build et hooks frontend
Lorsqu'une validation concerne l'intégration Tauri complète, le CLI Tauri est l'owner de l'orchestration.
Si `beforeDevCommand`, `beforeBuildCommand` ou un hook Tauri équivalent est configuré, la gate d'intégration SHOULD invoquer le CLI Tauri afin que ce hook soit exécuté dans le contexte prévu.
Ainsi :
```text
cargo check / cargo clippy
→ preuve Rust ciblée
frontend build/test direct
→ preuve frontend ciblée
Tauri CLI dev/build
→ preuve d'orchestration Tauri + hooks correspondants
```
Un projet MUST NOT considérer `npm run build`, `cargo build` ou une autre sous-commande isolée comme équivalente à un build Tauri lorsque la propriété à valider dépend des hooks, de `frontendDist`, des capabilities, du contexte Tauri ou du bundling.
Le CLI MAY être invoqué via `cargo tauri`, un script du package manager frontend ou une autre forme officiellement supportée. PGR n'impose pas le package manager qui sert de lanceur.
## `devUrl`, `frontendDist` et sorties frontend
`devUrl` et `frontendDist` doivent pointer vers les entrées/sorties réellement possédées par la chaîne frontend du projet.
PGR ne fixe ni port, ni nom de répertoire de distribution, ni profondeur relative universelle.
Le répertoire produit par le frontend est un artefact de build lorsqu'il est reproductible ; il SHOULD rester hors du source control sauf besoin explicite contraire. La configuration Tauri qui le référence reste, elle, source durable.
## Version produit
Lorsque l'application Tauri appartient au même release train que le projet, `VERSION` reste la version canonique exacte de cette unité de release. Les champs de version de Tauri et du frontend ne sont toutefois pas obligés de suivre chaque prerelease ou fix : ils sont classés selon [`200-GOVERNANCE.md`](200-GOVERNANCE.md).
Tauri 2 accepte une `version` applicative explicite dans `tauri.conf.json` et l'utilise pour les métadonnées/bundles. Si ce champ est absent, Tauri peut utiliser la version du package Cargo du host. PGR MUST NOT imposer l'une de ces deux formes uniquement pour réduire le nombre de fichiers modifiés.
Pour un projet contrôlé qui conserve une `version` explicite dans `tauri.conf.json`, la politique recommandée lorsque les prereleases ne sont pas distribuées comme produits installables distincts est `MIRROR-STABLE` : le champ porte la cible `X.Y.Z` de la version en cours de développement, est fixé ou vérifié à l'ouverture de cette cible et reste stable pendant ses `alpha.*`, `beta.*`, `rc.*` et fixes. La promotion stable vérifie la cohérence sans provoquer de réécriture cérémonielle si la valeur est déjà correcte.
Si le projet distribue réellement des bundles prerelease qui doivent être identifiables séparément, `tauri.conf.json > version` MAY devenir `MIRROR-EXACT` ou suivre des jalons de packaging explicitement documentés. Le choix dépend du contrat de distribution, pas d'une préférence PGR pour l'omission du champ.
Pour un `package.json` frontend privé appartenant à la même application, une propriété `version` conservée SHOULD de même être classée `MIRROR-STABLE` par défaut : elle peut porter `X.Y.Z` pendant tout le développement de cette release sans suivre chaque tranche. Si le package frontend est publié ou versionné indépendamment, il possède alors sa propre unité/version et cette règle ne s'applique plus.
La chaîne de version d'un projet contrôlé peut donc être, par exemple :
```text
VERSION = X.Y.Z-alpha.N.fix.M # CANONICAL
workspace.package.version = X.Y.Z-alpha.N.fix.M # MIRROR-EXACT si le workspace l'a choisi
tauri.conf.json version = X.Y.Z # MIRROR-STABLE
package.json version = X.Y.Z # MIRROR-STABLE si conservée
```
Cette classification évite de réécrire `package.json` et `tauri.conf.json` à chaque delta tout en conservant des champs de version utiles et lisibles. Elle remplace explicitement les conventions historiques KSP/Games qui préféraient omettre la version Tauri comme solution générale au churn.
Les numéros propres aux plateformes, par exemple Android `versionCode`, restent possédés par le profil plateforme concerné et ne sont pas assimilés au SemVer produit.
## Capabilities, permissions et frontière WebView/native
Les capability files et permissions Tauri sont du source de sécurité et SHOULD être versionnés lorsqu'ils définissent intentionnellement la surface exposée au frontend.
Le projet SHOULD accorder uniquement les permissions nécessaires aux fenêtres/webviews concernées. Ajouter un plugin ou une commande native ne justifie pas automatiquement une capability globale plus large.
Les schémas générés référencés par les capability files servent à l'outillage et à la validation de configuration ; ils ne deviennent pas pour autant la source normative de la politique de permissions.
Toute permission distante, origine distante ou extension de scope sensible doit être explicitement justifiée et validée comme frontière de sécurité.
## Plugins
PGR ne définit aucun catalogue de plugins Tauri obligatoires et ne rend pas `cargo tauri add` obligatoire.
Les pages officielles des plugins peuvent décrire à la fois une commande d'ajout automatique et les étapes explicites correspondantes côté Cargo, initialisation Rust et bindings frontend. Un projet MAY préférer appliquer manuellement ces étapes afin que les modifications de `Cargo.toml`, du manifeste frontend et du code d'initialisation restent directement contrôlées et revues.
Si une commande de commodité telle que `tauri add` est utilisée, les fichiers qu'elle modifie MUST être relus comme toute autre génération avant validation. Le projet ne doit installer que les côtés réellement nécessaires du plugin : dépendance Rust, bindings frontend, capabilities/permissions ou configuration associée selon le cas.
## CSP, fenêtres et bundles
CSP, nombre de fenêtres, labels, tailles, ports, `bundle.active`, targets de bundle, icônes et ressources restent des décisions du produit ou de la plateforme tant qu'aucun invariant plus général ne les impose.
Une règle locale MAY renforcer ces choix. Elle ne doit pas être remontée dans le profil Tauri uniquement parce qu'elle apparaît dans plusieurs applications d'un même projet.
## Artefacts générés et projets mobiles
Les dépendances frontend téléchargées et sorties de build reproductibles, telles que `node_modules/` ou le `frontendDist`, SHOULD rester hors Git.
PGR MUST NOT appliquer en revanche une exclusion universelle de tout répertoire `gen/`. Tauri peut générer des projets de plateforme qui deviennent ensuite le lieu de personnalisations Android/iOS intentionnelles. Le projet doit classifier chaque sous-arbre : reproductible/transitoire, configuration durable ou généré modifié volontairement.
Un POC Tauri mobile non concluant pour un produit particulier ne constitue pas une preuve que Tauri Android/iOS est inadapté à tous les autres projets. Le support mobile doit être jugé selon le besoin, les ABI, plugins, intégrations natives et gates plateforme du projet concerné.
Les secrets de signature, keystores, credentials et fichiers locaux restent hors Git conformément aux règles générales et aux profils plateforme applicables.
## Validation proportionnée
| Scope modifié | Preuve Tauri normalement pertinente | Ce que cette preuve ne remplace pas |
|-------------------------------------------------|------------------------------------------------------------------------|------------------------------------------------------|
| backend Rust uniquement | gates Rust/Cargo sélectionnées | frontend, hooks, WebView ou packaging |
| frontend uniquement | build/tests/smoke du frontend selon son owner | host natif ou packaging Tauri |
| hooks, config, capabilities, IPC ou intégration | `tauri dev` ou équivalent + smoke ciblé | package release de plateforme |
| bundling, ressources ou version packagée | `tauri build` ou équivalent + inspection/smoke de l'artefact | exigences d'un store ou d'une plateforme non exercée |
| Android/iOS | commande mobile Tauri applicable + gates du profil plateforme concerné | autres plateformes |
La syntaxe exacte de la commande dépend du lanceur choisi (`cargo`, npm, pnpm, etc.) et de la plateforme. Le delta/plan du projet doit fournir la commande concrète.
Un `cargo check` direct MAY rester utile pour une modification Rust ciblée, mais il ne constitue pas un smoke Tauri complet. Inversement, un full `tauri build` n'est pas requis lorsqu'aucune propriété d'intégration ou de packaging n'a été invalidée.
## Ce que le profil n'impose pas
Ce profil ne crée pas :
- de `tauri.conf.json` universel ;
- de port de développement par défaut PGR ;
- de frontend Vite/TypeScript/Sass/Bootstrap obligatoire ;
- de frontend WASM implicite ;
- de plugin obligatoire ;
- de nombre ou taille de fenêtres ;
- de politique de bundle unique ;
- de layout universel imposé aux projets externes ; la convention stricte des projets contrôlés est définie séparément ci-dessus ;
- de version exacte de Tauri, `tauri-build` ou du CLI.
Ces décisions appartiennent au projet, à un profil plus spécifique ou à une skeleton specification seulement lorsqu'une valeur réutilisable est réellement démontrée.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/skeletons/000-README.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Index des skeleton specifications PGR
@@ -20,6 +20,10 @@ Une specification peut se composer avec une fondation plus générale, mais la c
- [`001-PROJECT_GOVERNANCE_FOUNDATION.md`](001-PROJECT_GOVERNANCE_FOUNDATION.md) — fondation composable pour établir la gouvernance locale, la version, la documentation minimale, le workflow retenu et sa validation initiale sans imposer d'architecture applicative.
- [`002-RUST_CARGO_WORKSPACE.md`](002-RUST_CARGO_WORKSPACE.md) — specialization composable d'un workspace Rust/Cargo fondée sur `001`, les profils `500 + 510`, les cinq templates Rust/Cargo et une validation réelle de la toolchain.
Les familles Java/Android, Web, JNI, Tauri et autres domaines sans pièces suffisantes restent différées selon le plan [`../plans/004-V0_0_4_SKELETONS_PLAN.md`](../plans/004-V0_0_4_SKELETONS_PLAN.md).
Les familles Java/Android, Web, JNI et autres domaines sans pièces suffisantes restent différées tant que leurs owners normatifs et artefacts d'instanciation ne permettent pas une baseline réellement actionnable.
Le skeleton Tauri a fait l'objet d'une décision explicite en `0.0.5-alpha.9` : le host Rust/Tauri est suffisamment cadré par `510` et `710`, mais PGR ne possède pas encore les owners Web/Vite/TypeScript/Sass nécessaires pour matérialiser proprement la stack frontend candidate des projets contrôlés. Aucun `003-TAURI...` n'est donc créé dans `0.0.5`. La décision et ses conditions de sortie sont enregistrées dans [`../studies/002-TAURI_SKELETON_DECISION.md`](../studies/002-TAURI_SKELETON_DECISION.md).
Un futur skeleton Tauri devra composer les owners existants avec les profils Web réellement matérialisés ; il ne devra pas faire du skeleton lui-même le propriétaire des règles Vite, TypeScript, Sass, Bootstrap ou du package manager.
Aucun fichier placeholder ne doit être ajouté pour réserver un skeleton futur.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md -->
<!-- version: 1 -->
<!-- version: 3 -->
# Skeleton — Project governance foundation
@@ -92,6 +92,10 @@ Selon la réalité du projet :
- `docs/architecture/` est utile lorsqu'un état architectural ou un profil local doit être possédé durablement ;
- `docs/validation/` est utile lorsqu'une matrice ou un contrat de preuve mérite un document séparé.
Pour un projet où plusieurs fichiers ou familles durables ont des responsabilités ou règles de modification difficiles à déduire, la fondation MAY aussi instancier un registre local selon [`../templates/004-FILE_FAMILY_CONTRACTS.md`](../templates/004-FILE_FAMILY_CONTRACTS.md). Ce registre reste facultatif : un projet simple ou déjà explicite n'a rien à matérialiser.
Lorsqu'une release possède plusieurs tranches ou responsabilités de fermeture, le projet SHOULD utiliser un plan explicite conforme à [`../templates/005-RELEASE_PLAN.md`](../templates/005-RELEASE_PLAN.md) ou une forme locale équivalente. Le plan doit notamment attribuer la clôture/transition et éviter qu'une roadmap arrivée à son horizon soit étendue par spéculation.
Ne pas créer ces fichiers uniquement pour reproduire l'arborescence du dépôt PGR.
### Workflow retenu

View File

@@ -1,5 +1,5 @@
<!-- file: docs/skeletons/002-RUST_CARGO_WORKSPACE.md -->
<!-- version: 1 -->
<!-- version: 5 -->
# Skeleton — Rust/Cargo workspace
@@ -57,23 +57,26 @@ templates/rust-cargo/Cargo.toml
templates/rust-cargo/crates/project-core-lib/Cargo.toml
```
[`../validation/002-RUST_STYLE_AUDITS.md`](../validation/002-RUST_STYLE_AUDITS.md) est seulement une option supplémentaire. Les scripts `audit_rust_blank_lines.py`, `audit_rust_const_blocks.py` et `_rust_audit_common.py` ne sont copiés que si le projet choisit explicitement les conventions correspondantes et sait quels fichiers Rust doivent être audités.
[`../validation/002-RUST_STYLE_AUDITS.md`](../validation/002-RUST_STYLE_AUDITS.md) et [`../validation/003-RUST_EXPORT_COMPLETENESS.md`](../validation/003-RUST_EXPORT_COMPLETENESS.md) sont seulement des options supplémentaires. Les scripts Rust PGR ne sont copiés que si le projet choisit explicitement les conventions correspondantes. Le helper `_rust_audit_common.py` peut être partagé entre ces audits.
## Décisions destinataire avant copie
Avant de considérer la specialization comme instanciée, résoudre explicitement au minimum :
1. le release unit gouverné et la relation entre son `VERSION` et `[workspace.package].version` ;
2. la liste réelle des packages membres, leurs noms, leurs rôles et leurs chemins ;
3. l'édition Rust, le resolver Cargo et la toolchain stable compatible ;
2. le nom court/abréviation canonique du projet, la liste réelle des packages membres, leurs responsabilités, rôles et chemins, puis la conformité de leurs noms au contrat de `510-RUST_CARGO.md` ;
3. l'édition Rust 2024, le resolver Cargo et la toolchain stable compatible, ou l'exception externe explicitement documentée ;
4. la politique de publication et les métadonnées réelles communes (`license`, `repository`, `authors`) lorsqu'elles existent ;
5. les dépendances réellement partagées, leurs contraintes de version et les features qui doivent rester locales à chaque membre ;
6. la politique de versionnement de `Cargo.lock` adaptée au type de produit ;
7. les niveaux de lints hérités, les éventuelles exceptions locales et tout besoin réel d'`unsafe` dans un scope étroit ;
8. les paramètres `rustfmt.toml` et `clippy.toml` à conserver ou adapter ;
9. l'emplacement des artefacts Cargo et l'identifiant unique utilisé dans `.cargo/config.toml` si cette politique externe au dépôt est retenue ;
10. les audits de style Rust facultatifs réellement adoptés et leur corpus exact ;
11. les validations Cargo réellement nécessaires au code et aux manifests créés.
10. les frontières `-api` / `-lib` réellement justifiées, les packages exécutables distincts de leurs bibliothèques et toute exception framework à cette séparation ;
11. l'ownership du logging/tracing : simple exécutable local ou crate transverse commune lorsque plusieurs runtimes partagent subscriber, sinks ou policy ;
12. les exceptions éventuelles au profil Rust strict ; pour une nouvelle baseline contrôlée, la façade crate-root, les chemins explicites et les conventions structurelles sans ordre métier sont sélectionnés par défaut ;
13. les fichiers pour lesquels un ordre mécanique des blocs `const` est réellement applicable, sans englober les tables/protocoles/schémas ordonnés ;
14. les validations Cargo réellement nécessaires au code et aux manifests créés.
Les valeurs `0.1.0`, `2024`, `project-name`, `project-core-lib`, les seuils Clippy et les chemins de build présents dans les templates sont des exemples ou baselines à revoir. Ils ne deviennent jamais des valeurs universelles par simple copie.
@@ -103,7 +106,11 @@ Le projet ne doit pas remplir les champs manquants avec des identités fictives
Pour chaque crate réellement nécessaire, utiliser `templates/rust-cargo/crates/project-core-lib/Cargo.toml` comme exemple de manifest membre puis adapter son chemin et son nom.
`project-core-lib` n'est pas un nom de crate imposé. Une crate bibliothèque, binaire ou spécialisée doit conserver le layout et les héritages qui correspondent à son rôle réel.
`project-core-lib` n'est pas un nom de crate imposé. Dans une nouvelle baseline contrôlée, remplacer `project` par le nom court/abréviation du projet et choisir une responsabilité puis un rôle/cible lisibles conformément à `510-RUST_CARGO.md`. Une crate bibliothèque, binaire ou spécialisée doit conserver le layout et les héritages qui correspondent à son rôle réel ; un suffixe n'est pas ajouté uniquement pour obtenir une symétrie artificielle.
Avant de créer une paire `*-api` / `*-lib`, démontrer la frontière contractuelle qui justifie deux packages. Une implémentation unique sans extension/backend/lifecycle séparé reste normalement une seule crate `-lib`. À l'inverse, une vraie crate `-api` reste principalement déclarative et ne reçoit pas le runtime de logging du projet par automatisme.
Les exécutables sont des packages distincts des bibliothèques qu'ils composent, sauf exception de framework sélectionnée. Leur `main.rs` reste mince. Si plusieurs crates runtime partagent la même configuration de diagnostics, prévoir une crate transverse de logging/tracing ; ne pas répliquer l'initialisation du subscriber dans chaque binaire ou bibliothèque.
Lorsque le membre hérite de la politique commune, `version.workspace = true`, `edition.workspace = true`, `publish.workspace = true` et `[lints] workspace = true` restent cohérents. Une exception locale, notamment de lint ou de safety, doit suivre le contrat de `510-RUST_CARGO.md` et ne pas affaiblir silencieusement les autres membres.
@@ -127,13 +134,15 @@ L'édition de `rustfmt.toml` doit rester cohérente avec le workspace. Les seuil
Décider explicitement si `Cargo.lock` est versionné selon le type de produit, le mode de publication et le besoin de reproductibilité. La présence ou l'absence du lockfile dans un projet source historique ne constitue pas une règle suffisante pour le nouveau workspace.
### Audits de style facultatifs
### Audits Rust ciblés
Si le projet adopte les conventions documentées dans [`../validation/002-RUST_STYLE_AUDITS.md`](../validation/002-RUST_STYLE_AUDITS.md), copier les trois scripts concernés et adapter leurs headers à leurs chemins locaux.
Pour une nouvelle baseline contrôlée qui adopte le profil strict, [`../validation/002-RUST_STYLE_AUDITS.md`](../validation/002-RUST_STYLE_AUDITS.md) fournit des contrôles monotâches pour les propriétés structurelles que Rustfmt ne possède pas complètement : lignes vides, séparation des items, ordre des modules, bloc d'imports de traits et blocs de réexports. Copier seulement les scripts réellement retenus et adapter leurs headers aux chemins locaux.
La sélection des fichiers doit rester explicite. En particulier, l'audit des blocs de constantes ne doit jamais réordonner une table de protocole, une séquence de migrations ou toute autre structure dont l'ordre possède une sémantique propre.
L'audit `audit_rust_const_blocks.py` reste à sélection explicite par fichier : il ne doit jamais réordonner une table de protocole, une séquence de migrations, un schéma ou toute structure dont l'ordre possède une sémantique propre. Les blocs `static`/`type` peuvent reprendre localement la même présentation sans qu'un audit supplémentaire soit créé par symétrie.
Ne pas copier ces scripts uniquement parce qu'ils existent dans PGR.
Le contrat de façade décrit dans [`../validation/003-RUST_EXPORT_COMPLETENESS.md`](../validation/003-RUST_EXPORT_COMPLETENESS.md) appartient également au profil strict ; `audit_rust_export_completeness.py` avec `_rust_audit_common.py` SHOULD être adopté afin de contrôler les propriétés que `rustc` et Clippy ne possèdent pas. Une adoption d'un projet existant SHOULD commencer en `--report-only`, résoudre les écarts ou exceptions locales, puis seulement rendre l'audit bloquant.
Aucun script n'est copié uniquement parce qu'il existe dans PGR. Si un autre owner fiable possède déjà la propriété ou si l'ordre est sémantique, la revue ou l'outil officiel reste préférable.
## Validation initiale
@@ -150,7 +159,7 @@ cargo clippy --workspace --all-targets --all-features -- -D warnings
Ajouter des tests ciblés pour les crates qui possèdent déjà un comportement à vérifier. Un `cargo build` n'est nécessaire que lorsqu'un artefact de build doit réellement être produit.
Si les audits de style facultatifs ont été sélectionnés, les exécuter après le formatage sur les seuls chemins concernés.
Si des audits Rust facultatifs ont été sélectionnés, exécuter les audits de style après formatage sur les seuls chemins concernés et l'audit de façade sur la racine du projet une fois son contrat explicitement adopté.
La validation doit aussi confirmer :
@@ -158,7 +167,9 @@ La validation doit aussi confirmer :
CHECK VERSION et workspace.package.version cohérents pour un train de release unique
CHECK aucun placeholder project-name/project-core-lib non résolu dans les fichiers adoptés
CHECK édition Rust cohérente entre Cargo.toml et rustfmt.toml
CHECK membres du workspace existants et correctement nommés
CHECK membres du workspace existants, préfixés et nommés selon leur responsabilité/rôle
CHECK édition Rust 2024 ou exception externe explicitement documentée
CHECK profil strict de chemins/façade appliqué ou override borné documenté
CHECK politique Cargo.lock explicitement décidée
CHECK artefacts Cargo hors des livraisons source selon la politique locale
REVIEW dépendances/features, lints et métadonnées réellement nécessaires

View File

@@ -0,0 +1,13 @@
<!-- file: docs/studies/000-README.md -->
<!-- version: 2 -->
# Index des études PGR
Les études PGR enregistrent une analyse bornée nécessaire à une décision, une réconciliation ou une future normalisation sans transformer prématurément leurs conclusions en règles.
Une étude peut comparer des projets sources, des technologies ou plusieurs options. Elle doit distinguer les faits observés, les conclusions déjà suffisamment étayées, les décisions encore ouvertes et les éléments qui doivent rester spécifiques à leur contexte.
## Études actives
- [`001-KSP_GAMES_RECONCILIATION.md`](001-KSP_GAMES_RECONCILIATION.md) — première réconciliation explicite de PGR avec les baselines autoritaires fournies de `khadhroony-solana-project` et `sasedev-games`.
- [`002-TAURI_SKELETON_DECISION.md`](002-TAURI_SKELETON_DECISION.md) — étude de readiness `alpha.9` déterminant si les owners existants suffisent à matérialiser un skeleton Tauri contrôlé sans transférer au skeleton la propriété normative du frontend.

View File

@@ -0,0 +1,317 @@
<!-- file: docs/studies/001-KSP_GAMES_RECONCILIATION.md -->
<!-- version: 11 -->
# Étude de réconciliation PGR / KSP / sasedev-games
## Objet et statut des corpus
Cette étude exécute l'inventaire demandé par `prompts/005-V0_0_5_START_PROMPT.md` avant toute modification normative importante de `0.0.5`.
`khadhroony-solana-project` et `sasedev-games` sont ici des **corpus de conception et de réconciliation**. Ils n'ont pas encore adopté PGR comme référentiel de gouvernance. Une divergence observée dans leurs baselines ne constitue donc pas une non-conformité PGR et ne doit pas être « corrigée » rétroactivement dans ces projets pendant cette release.
La question utile est différente : quelles responsabilités déjà éprouvées doivent rester locales, lesquelles sont déjà correctement possédées par PGR, et lesquelles révèlent un besoin générique suffisamment réutilisable pour améliorer PGR avant une future adoption ?
## Sources autoritaires contrôlées
### PGR
Baseline : archive source `pgr-v0.0.4.zip`, annoncée comme téléchargement du tag Gitea `v0.0.4`, accompagnée du rapport externe `pgr-0.0.4-publish.001-report.md` avec statut `PASS`.
```text
SHA-256 7137bb084f017362323a2043d13ba71490be15b8786adc8d645981bf455b80ae
entrées 167
fichiers 139
répertoires 28
racine pgr/
CRC clean
.git absent
```
Aucun doublon ZIP, collision de casse, chemin dangereux ou lien symbolique n'a été détecté. Les `15` tests Python PGR passent ; les audits Markdown affichent `123` fichiers contrôlés, `3` tableaux propres et `116` liens locaux propres. Les cinq fichiers TOML du template Rust/Cargo se parsèrent correctement.
Le rapport post-tag donne le même hash et sépare explicitement la preuve de publication de l'état contenu dans l'archive.
### khadhroony-solana-project
Baseline autoritaire fournie : `khadhroony-solana-project-v0.3.16.zip`.
```text
SHA-256 db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0
entrées 2229
fichiers 2017
répertoires 212
racine khadhroony-solana-project/
CRC clean
.git absent
```
Aucun doublon ZIP, collision de casse, chemin dangereux ou lien symbolique n'a été détecté. Les audits Python fournis par la baseline passent :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
Markdown table audit: clean (352 table(s), 962 file(s))
```
### sasedev-games
Baseline autoritaire fournie : `games-v0.3.5.zip`.
```text
SHA-256 5f5fdee1a271386766d059657d6eaa831acdc263b3e722fed738808335c0a105
entrées 664
fichiers 508
répertoires 156
racine games/
CRC clean
.git absent
```
Aucun doublon ZIP, collision de casse, chemin dangereux ou lien symbolique n'a été détecté. Les audits Python fournis par la baseline passent :
```text
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
games.sasedev workspace audit: clean
Markdown table audit: clean (5 table(s), 304 file(s))
Distribution layout audit: clean (66 required path(s), 8 forbidden path(s) absent)
```
La toolchain `cargo` n'est pas disponible dans l'environnement utilisé pour cette étude ; aucun `cargo metadata`, build, test ou Clippy externe n'est donc présenté comme exécuté ici. Cette limite n'empêche pas l'inventaire documentaire et statique demandé à `alpha.1` et ne transforme pas les audits Python en preuve de compilation.
## Méthode de classification
Chaque propriété observée est classée selon l'un des états suivants :
```text
OWNED déjà correctement possédée par PGR ; l'usage local ne crée pas de nouvelle règle
COMMON besoin générique réel insuffisamment matérialisé dans PGR ; candidat de 0.0.5
LOCAL besoin spécifique au projet, à son architecture, son domaine ou sa plateforme
LEGACY divergence historique ou duplication locale à considérer lors d'une future adoption, pas à corriger ici
DEFER preuve insuffisante pour créer maintenant un owner PGR durable
```
La classification porte sur les responsabilités et contrats, pas sur la simple ressemblance des noms de fichiers.
## Réconciliation de la gouvernance et de la documentation
| Sujet observé | KSP / Games | Classification | Décision PGR |
|-----------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|----------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| hiérarchie normative et exceptions bornées | présente dans les deux corpus | `OWNED` | `RULES.md` et le modèle de profils possèdent déjà ce contrat ; ne pas recopier les règles locales |
| langue code/chemins anglais, documentation interne française | commune | `OWNED` | déjà possédée par `RULES.md` et `300-DOCUMENTATION.md` |
| rôles de `README`, `RULES`, `ROADMAP`, `CHANGELOG`, plans et documents durables | largement communs | `OWNED` | `300-DOCUMENTATION.md` couvre déjà les rôles génériques |
| headers internes `file` / `version` | communs | `OWNED` | déjà possédés et audités par PGR ; les règles locales plus strictes restent locales tant qu'elles ajoutent une sémantique propre |
| `FILE_CONTRACTS.md` local décrivant responsabilité et règle de modification des familles importantes | présent dans les deux corpus | `COMMON` | fournir un pattern/template documentaire facultatif et l'intégrer à la fondation seulement lorsque la complexité du projet le justifie |
| prompt de démarrage autonome avec baseline, ordre de lecture, état validé, mission, hors-scope, première gate et validation | présent dans les deux corpus | `COMMON` | renforcer le contrat générique des prompts PGR et fournir une spécification réutilisable ; ne pas copier les commandes KSP/Games |
| règle « une version complète par session » | explicitement forte dans Games, structurante dans certaines pratiques locales | `LOCAL` | PGR conserve sa règle actuelle : une session ne force pas artificiellement une version ; un projet peut ajouter une contrainte locale plus forte |
| couloirs exacts de fermeture et numérotation de prereleases | plus détaillés dans KSP/Games | `DEFER` | conserver le principe PGR de consolidation/RC/stable ; ne pas généraliser des numéros ou un nombre fixe de tranches |
| absence de `history/` dans KSP `v0.3.16` et historique partiel dans Games | divergence de workflow antérieure à l'adoption de PGR | `LEGACY` | ne pas créer de dette rétroactive ; une future adoption décide son point de départ et ne réécrit pas l'historique |
## Réconciliation de la validation
PGR possède déjà le besoin générique le plus important : sélectionner les preuves à partir du scope, des risques invalidés et de la maturité, avec une matrice canonique facultative pour les projets suffisamment structurés.
Les matrices et documents de validation de Games et KSP confirment plusieurs pratiques déjà couvertes : tests ciblés pendant le développement, gates plus larges aux frontières de maturité, contrôles de dépendances seulement lorsqu'ils apportent une preuve, et distinction entre audit statique, build, smoke et diagnostic. Ces éléments restent `OWNED`.
Les audits spécialisés restent locaux :
- `audit_ksp_workspace_rules.py` contrôle des frontières, dépendances et conventions propres à KSP ;
- `audit_distribution_layout.py` contrôle le layout et plusieurs intégrations Web/Android/Gradle propres à Games ;
- ces scripts ne doivent pas être copiés dans PGR ni transformés en audits universels.
## Réconciliation Rust/Cargo
Les travaux `0.0.3` et `0.0.4` ont déjà absorbé les parties génériques évidentes des deux corpus : workspace Cargo, héritage des métadonnées et lints, templates `rustfmt.toml`/`clippy.toml`/`.cargo/config.toml`, manifests, safety/FFI bornée et audits de style ciblés. Ces propriétés restent `OWNED`.
Un besoin commun reste cependant suffisamment net pour être repris : les deux projets contiennent `audit_rust_export_completeness.py` avec le même contrat et le même corps fonctionnel à l'exception de la révision de fichier. Il contrôle la complétude de façade crate-root et les chemins canoniques associés, propriété que Rustfmt et Clippy ne possèdent pas. Le profil `510` décrit déjà cette architecture comme convention recommandée.
Décision : classer ce contrôle `COMMON`, mais ne pas copier le script tel quel en `alpha.1`. Une tranche dédiée doit d'abord :
1. séparer ce qui dépend du profil Rust général de ce qui dépend d'un style KSP/Games plus strict ;
2. conserver l'audit facultatif tant que la façade crate-root n'est qu'un `SHOULD` du profil ;
3. fournir des tests positifs/négatifs ciblés et documenter les limites du parsing lexical ;
4. éviter de réintroduire les règles déjà possédées par Rustfmt, Clippy ou les deux audits de style PGR existants.
#### Mesure alpha.5
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 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.
L'orchestrateur `audit_rust_workspace_rules.py` est un pattern d'ergonomie utile mais ne possède aucune règle. PGR peut documenter comment composer des audits sélectionnés dans un projet destinataire sans fournir un lanceur universel qui supposerait les mêmes scripts partout.
## Réconciliation des frameworks et plateformes
### Tauri
Tauri est réellement présent dans les deux corpus et n'est plus un exemple isolé : KSP contient `6` applications Desk Tauri et Games `2` hôtes Tauri. Les `8` applications observées utilisent Tauri 2, un package Rust avec `tauri-build`, une configuration `tauri.conf.json`, des capability files, des hooks frontend et un `frontendDist` dédié.
Les différences sont tout aussi importantes que les points communs : KSP possède deux fenêtres par Desk, active le bundling et utilise des hooks npm simples ; Games utilise une fenêtre, désactive le bundling dans les POC observés et l'un de ses hooks construit d'abord un frontend WASM via un script Python. Les CSP, ports, identifiants, ressources et plugins ne sont pas uniformes.
Toutes les configurations observées omettent une version Tauri explicite tandis que leur package Cargo hérite de la version workspace. La relecture des règles montre que ce n'est pas un simple accident : KSP `VER-ID-015/016` et Games `VER-TAURI-001..004` ont tous deux utilisé l'omission de `tauri.conf.json > version` et la non-synchronisation systématique de `package.json` pour réduire le churn. La clarification opérateur de `alpha.6.fix.2` rejette toutefois la généralisation de cette solution : l'objectif durable est de **classifier la fréquence de synchronisation**, pas de supprimer les champs. PGR conserve donc la possibilité d'une version Tauri/frontend explicite et privilégie `MIRROR-STABLE` lorsque ces valeurs doivent seulement porter le jalon `X.Y.Z` cible.
Les deux corpus versionnent leurs capability files et ignorent les dépendances/sorties frontend reproductibles. Ils ignorent également `gen/`, mais ce dernier point n'est pas généralisé : les projets mobiles Tauri peuvent contenir sous leur projet généré des personnalisations de plateforme intentionnelles qui doivent être classifiées avant toute politique Git.
Décision `alpha.6` : classer Tauri `COMMON`, activer `700-FRAMEWORKS.md` et créer `710-TAURI.md`. Le profil possède l'orchestration Tauri : host Rust, configuration/capabilities, hooks frontend, version produit et gates d'intégration. Il ne possède pas à lui seul Vite, TypeScript, Sass, Bootstrap, WASM, le package manager, les ports, fenêtres, CSP, bundles ou plugins particuliers.
La revue opérateur de `alpha.6` apporte deux clarifications importantes. Premièrement, dans les projets Tauri que l'organisation contrôle, le host est traité comme n'importe quel projet Rust du même ensemble et doit donc hériter sans exception implicite du profil Rust sélectionné ; un projet imposé de l'extérieur peut conserver d'autres conventions via des overrides project-specific. Deuxièmement, les choix récurrents `frontend/`, Vite, TypeScript, Sass, Bootstrap et ajout explicite des dépendances de plugins décrivent un **candidat de skeleton pour projets contrôlés**, pas le contrat universel de Tauri.
Le POC Tauri mobile de Games n'a pas été concluant pour les besoins Games observés, notamment l'objectif Android/ABI et les contraintes de monétisation envisagées. Cette expérience ne permet pas de rejeter Tauri mobile pour d'autres produits : la capacité reste à évaluer par projet avec les profils plateforme applicables.
La règle de validation commune reste confirmée : lorsqu'une propriété dépend de `beforeDevCommand`, `beforeBuildCommand`, `frontendDist`, des capabilities ou du bundling, une sous-commande directe telle que `npm run build` ou `cargo build` ne prouve pas l'intégration Tauri complète. La gate doit passer par le CLI Tauri ou une forme officiellement équivalente ; les sous-commandes restent pertinentes pour leurs preuves ciblées.
Le CLI Tauri fournit une commande `add`, mais les documentations officielles des plugins détaillent également l'ajout explicite de la dépendance Cargo, l'initialisation Rust et, lorsqu'ils existent, les bindings frontend. PGR ne doit donc pas imposer la commande de commodité : un projet qui privilégie le contrôle manuel des manifests peut suivre explicitement ces étapes et revoir chaque modification.
#### Seconde passe de réconciliation des conventions Tauri/Rust
La clarification opérateur de `alpha.6.fix.1` déclenche une seconde lecture des règles versionnées plutôt qu'une simple extrapolation depuis les fichiers de configuration. Cette passe confirme plusieurs niveaux de preuve.
**Commun aux `8` hosts Tauri observés** : les `6` Desk KSP et les `2` hosts Games placent directement à la racine de leur crate `Cargo.toml`, `build.rs`, `package.json`, `tsconfig.json` et `tauri.conf.json`; tous possèdent `src/lib.rs`, `src/main.rs`, `src/tauri.rs` et `frontend/`; aucun ne possède `src-tauri/`. Cette convergence justifie une forte recommandation PGR pour les nouveaux projets contrôlés sans imposer une migration mécanique aux projets externes.
**Commun et déjà normatif dans les corpus** : KSP `KSP-APP-010..013` et Games `GAME-PLATFORM-010` convergent vers une bibliothèque applicative avec façade `lib.rs`, un launcher `main.rs` mince et un `tauri.rs` qui assemble Tauri et expose les wrappers de commandes. Games `GAME-PLATFORM-009` confirme aussi que l'adaptation WASM réutilisable reste distincte de la crate Tauri.
**KSP plus strict mais réutilisable** : `KSP-APP-023` place les sources Web sous `frontend/`, TypeScript sous `frontend/ts/`, Sass sous `frontend/sass/` et les bindings TS-RS sous `frontend/ts/bindings/`, les DTO Rust restant la source de vérité. La baseline KSP `0.3.16` contient `41` fichiers Rust applicatifs utilisant TS-RS dans les apps Desk. Games n'a pas encore ce contrat TS-RS : PGR le classe donc comme convention stricte des projets contrôlés, pas comme invariant universel de Tauri.
**Fortement recommandé mais pas universel** : le préfixe KSP `tw_`, le splashscreen commun, les paires de ports fixes, le plugin tracing précis et les détails de shell sont locaux. En revanche, un module cohérent par fenêtre/responsabilité, des helpers communs séparés et l'ajout explicite/revu des plugins sont des patterns réutilisables.
**Rust au-delà de Tauri** : `RULES_RUST.md` KSP contient un profil beaucoup plus complet que Games : `use` non-traits interdits, chemins externes explicites les plus courts, réexports `self::`, alias interdits, blocs de visibilité/ordre, contrôle de flux explicite et façade stricte. Games confirme le noyau façade/chemins/erreurs mais pas toutes les contraintes de style. Ces écarts doivent être traités en tranches Rust dédiées, avec KSP comme référence stricte et Games comme pression pour classifier correctement `MUST`/`SHOULD`/override.
**Nomenclature des crates** : KSP encode explicitement le rôle (`ksp-<role>-lib`, `ksp-<domain>-api`, `ksp-app-<role>-<interface>`, workers/jobs), tandis que Games conserve lui aussi des noms révélant rôle et cible (`game-logging-lib`, `*-desktop`, `*-tauri`, `*-wasm`, `engine-v1-*`). PGR doit donc étudier une convention générale « préfixe projet + responsabilité + suffixe de rôle/cible » sans figer les vocabulaires propres à KSP ou Games.
#### Décision `alpha.7` — profil Rust strict et nomenclature
La seconde passe sur les règles autoritaires confirme que KSP `RULES_RUST.md` version `10` matérialise le profil d'explicité le plus complet : Rust 2024, `use` réservé aux traits réellement nécessaires, chemins externes publics les plus courts, réexports internes `self::`, absence de glob/groupement/alias, façade crate-root, `crate::Item` intra-crate et contrôle de flux explicite. Games `RULES_RUST.md` version `7` confirme Rust 2024, façade crate-root, `crate::Item`, interdiction des globs, erreurs explicites et lints communs, tout en étant moins strict sur les imports non-traits et certaines formes de visibilité.
La décision PGR est de retenir ce niveau strict comme **défaut des nouvelles baselines Rust contrôlées**. Un projet externe ou historiquement contraint peut conserver une convention différente via un override project-specific borné ; cette exception ne réduit pas le profil canonique des nouveaux projets.
Les règles sémantiques retenues dans `alpha.7` sont :
- édition Rust 2024 comme baseline actuelle des projets contrôlés ;
- `use` réservé aux traits qui doivent réellement être mis en scope ;
- chemins explicites pour les autres symboles, avec le chemin public externe le plus court ;
- réexports internes explicites via `self::`, sans glob, regroupement par accolades ni alias de commodité ;
- façade crate-root pour les items partagés et `crate::Item` dans la crate, y compris dans le module propriétaire ;
- pas de visibilité élargie uniquement pour les tests ;
- `unwrap`, `expect` et `?` interdits en production dans le profil strict, avec retours et propagation d'erreurs explicites.
Le marqueur KSP `rust-rules: trait-import` n'est pas généralisé : il sert l'audit historique KSP et n'est pas la règle sémantique. De même, `alpha.7` n'importe aucun script composite KSP/Games ; l'enforcement complémentaire reste découpé entre les owners PGR existants et la tranche `alpha.8`.
La nomenclature converge sur une propriété utile sans imposer les vocabulaires locaux : KSP préfixe toutes ses crates avec `ksp-` et encode explicitement bibliothèque/API/application/worker/job ; Games emploie également des noms où responsabilité et cible restent visibles (`game-logging-lib`, `*-desktop`, `*-tauri`, `*-wasm`, familles `engine-v1-*`). PGR retient donc pour les nouveaux projets contrôlés un préfixe projet/abréviation obligatoire et recommande la forme `<project>-<responsibility>-<role-or-target>` lorsque le classificateur apporte une information réelle. Les ordres historiques déjà cohérents ne sont pas renommés rétroactivement.
Les règles de blocs homogènes, visibilité, espacement et ordre alphabétique restent volontairement hors de cette décision et appartiennent à `alpha.8`.
#### Correction `alpha.7.fix.1` — suffixes, packaging et tracing
La gate mécanique de `alpha.7` passe, mais la revue fonctionnelle opérateur révèle que la première matérialisation traitait encore les suffixes comme de simples indications de lisibilité. Le corpus KSP contient pourtant un contrat plus fort : `KSP-NAME-001..003` distingue implémentation réutilisable `-lib` et contrat extensible `-api`, `KSP-API-007` interdit de créer une API par symétrie, et `002-LAYERS_AND_DEPENDENCIES.md` réserve le logging aux crates ayant réellement du comportement.
La règle PGR devient donc sémantique : `-api` signifie frontière de contrat réellement justifiée et principalement passive ; `-lib` signifie implémentation/fonctionnalité réutilisable. Une implémentation unique sans besoin d'extension/backend/lifecycle ne crée pas artificiellement une paire `-api` / `-lib`. Les rôles `worker`, `job`, `store`, etc. peuvent faire partie du nom d'une bibliothèque, mais `-lib` continue d'indiquer qu'elle n'est pas le launcher.
Sur le packaging, les deux corpus ne doivent pas être fusionnés mécaniquement. Games `GAME-WS-007` sépare explicitement la lib de jeu et son runner Desktop. KSP contient encore `KSP-WORKER-014`, qui préférait à terme un package worker mixte bibliothèque + binaire. Cette dernière préférence n'est **pas généralisée** : la clarification opérateur retient désormais la séparation package bibliothèque / package exécutable par défaut, les exécutables restant des couches de composition minces. Tauri est l'exception framework actuelle.
Cette exception est elle-même fortement observée : les `6` hosts Tauri KSP `0.3.16` et les `2` hosts Tauri Games `0.3.5` déclarent une cible `lib` et une cible `bin` distinctes ; les libs utilisent un nom terminé par `_lib` tandis que le binaire conserve le nom applicatif. PGR exige donc, dans une nouvelle baseline Tauri contrôlée, des noms de targets distincts même après normalisation `-` → `_`. Le motif cross-platform exact à l'origine de cette convention n'est pas utilisé comme preuve normative ici ; le contrat observable et reproductible suffit.
Le logging converge également fortement. KSP possède `ksp-logging-lib` comme façade/runtime commun et garde ses `*-api` déclaratives sans logging ; Games définit `tracing` comme mécanisme canonique, `tracing-subscriber` comme owner de composition, `tracing-appender` lorsque nécessaire et `game-logging-lib` comme crate transverse. PGR retient donc `tracing` par défaut et recommande une crate commune de logging dès que plusieurs runtimes partagent subscriber, sinks, filtres ou lifecycle ; la façade `log` reste seulement une compatibilité possible, pas le default d'une nouvelle baseline contrôlée.
#### Politique de version Tauri/frontend réconciliée
La baseline KSP `0.3.16` illustre le défaut de l'ancienne convention : les six `tauri.conf.json` n'ont pas de champ `version`, tandis que leurs `package.json` portent des versions historiques différentes (`0.2.6`, `0.2.12-pre.9`, `0.3.7`, `0.3.8` ou `0.3.15`) sans représenter uniformément la release courante. Games `0.3.5` suit la même famille de règles. Cette situation réduit les modifications mécaniques mais affaiblit la lisibilité des manifests.
La décision PGR est donc :
- `VERSION` = `CANONICAL` exact de la tranche ;
- version Cargo = `MIRROR-EXACT` lorsque le profil Rust du projet la synchronise ;
- `tauri.conf.json > version` et `package.json > version`, lorsqu'ils sont conservés pour le même produit, SHOULD normalement être `MIRROR-STABLE` et porter la cible `X.Y.Z` pendant tout le développement ;
- une distribution réelle de prereleases peut sélectionner une politique plus fine (`MIRROR-EXACT` ou jalons de packaging) ;
- l'absence du champ reste autorisée lorsque le projet n'en tire aucune valeur, mais n'est plus la recommandation PGR destinée à éviter le churn.
Cette décision est cohérente avec le principe PGR historique qui distingue déjà version canonique, miroirs techniques, compteurs et versions de schéma au lieu d'exiger `tous les champs == VERSION`.
#### Décision `alpha.8` — formatage structurel et audits monotâches
La comparaison des `audit_rust_general_rules.py` KSP/Games confirme que leur grand script mélangeait plusieurs owners qui peuvent être séparés proprement. PGR possédait déjà `blank_lines` et `const_blocks`; la tranche extrait quatre propriétés supplémentaires sans copier les contrôles de headers, FFI, rustdoc, façade ou imports déjà possédés ailleurs.
Les nouveaux audits ciblés sont :
- `item_spacing` : exactement une ligne vide entre items distincts voisins d'un même scope ; les blocs compacts `const/static/type` homogènes sont délégués afin d'éviter les faux positifs sémantiques ;
- `module_order` : modules de production avant les modules de tests, ordre alphabétique de chaque groupe ;
- `trait_import_blocks` : `use` non exportés au niveau module, avant les déclarations et sans ligne vide interne ; la nature réelle du trait n'est pas inférée lexicalement ;
- `reexport_blocks` : bloc `pub use` avant `pub(crate) use`, aucun blanc interne et exactement un blanc entre niveaux ; Rustfmt reste owner du tri intra-bloc.
La mesure croisée sur tous les fichiers `crates/**/*.rs` donne `603` fichiers KSP et `87` Games. Les quatre nouveaux audits sont propres sur les deux corpus, de même que `blank_lines`. L'audit `const_blocks` produit en revanche `18` signaux KSP lorsqu'il est appliqué aveuglément et `0` Games ; certains signaux KSP correspondent à des tables ou schémas dont l'ordre est intentionnel. Cette différence confirme que `const_blocks` doit rester opt-in sur des fichiers explicitement sélectionnés.
PGR ne reprend pas automatiquement `RUST-FMT-110` du script KSP (item après `return` comme suspicion d'accolade déplacée), ni un ordre global `enum → struct → impl → fn`, ni l'idée que tous les éléments visibles doivent mécaniquement précéder les privés. Ces propriétés peuvent dépendre de la structure métier ; un audit lexical général créerait plus de faux positifs qu'il n'apporterait de preuve. La convention `struct` suivi de ses `impl` reste une forte préférence de lisibilité, pas un tri universel automatisé.
Cette extraction ferme le besoin identifié pour `alpha.8` sans créer de parser Rust général.
### Web / Vite / WASM
Les corpus et la clarification opérateur permettent désormais de séparer trois choses :
- le frontend Tauri est un owner distinct du host Rust et du framework Tauri ;
- Vite + TypeScript + Sass + Bootstrap constitue un candidat de stack par défaut pour les futurs skeletons de projets contrôlés, mais manque encore de profils Web dédiés dans PGR ;
- WASM n'est pas une dépendance implicite de Tauri et doit généralement rester un projet/package séparé appliquant ses propres profils, sauf composition architecturale explicitement voulue.
La création de profils Web/Vite/TypeScript/Sass reste `DEFER` pour `0.0.5` : cette release peut cadrer la future composition sans matérialiser précipitamment toute la famille Web.
La même clarification révèle un écart antérieur dans le profil Rust PGR : la conception initiale avait retenu le profil strict sans `use` non-trait, chemins explicites et conventions d'organisation, alors que le `510-RUST_CARGO.md` matérialisé conserve encore une formulation plus permissive. Ce sujet ne doit pas être absorbé par un fix Tauri ; il devient une tranche Rust normale dédiée avant toute décision de skeleton Tauri.
### Android / JNI
Android possède déjà un owner PGR `610`. Le JNI/Rust natif réellement observé est surtout propre à Games dans les baselines présentes. Les exceptions `unsafe`, ABI, exports JNI et génération Gradle doivent rester locales ou différées jusqu'à une étude composite Rust + Android/JNI suffisante. Classification : `DEFER`.
## Éléments qui doivent rester spécifiques
Les responsabilités suivantes sont utiles dans leurs projets mais ne fournissent pas un owner PGR générique :
- firewall de dépendances, protocoles Solana, transports, Store, workers/jobs, tracing targets et materialization de KSP ;
- générations de moteur, assets de jeux, Uroburas, serveurs temps réel, distribution Android/Web et politiques de hosting de Games ;
- noms métier exacts des crates, répertoires de config, schémas, routes, plugins et ressources propres à chaque application ; la convention générique de noms exprimant préfixe projet, responsabilité et rôle reste à réconcilier en `alpha.7` ;
- seuils Clippy, identifiants courts autorisés, ports de développement et paramètres de fenêtres lorsqu'ils n'expriment aucun invariant réutilisable.
Ces éléments servent à tester que PGR permet des règles locales spécifiques ; ils ne doivent pas être aspirés dans le référentiel central.
## Conséquences pour une future adoption de PGR
La future application de PGR à KSP et Games doit être une migration explicitement planifiée dans chacun des projets, après stabilisation de la release PGR retenue. Elle ne doit pas reconstruire artificiellement leurs anciens deltas/histories ni renommer rétroactivement toutes les conventions historiques.
Le point de départ attendu sera plutôt :
```text
prendre la stable réelle du projet
+ sélectionner les owners PGR applicables
+ comparer les règles locales actuelles
+ conserver les contraintes projet spécifiques
+ remplacer seulement les duplications devenues inutiles
+ introduire les nouveaux audits/templates de manière proportionnée
+ valider la migration comme une release du projet lui-même
```
`0.0.5` améliore donc le référentiel et ses points d'adoption ; il ne migre ni KSP ni Games.
## Réconciliation intégrée `beta.1`
Après les tranches `alpha.1` à `alpha.9`, la classification initiale peut être rejouée sur l'état PGR obtenu sans aspirer les architectures métier des corpus.
| Domaine | Owner / artefact PGR après réconciliation | Statut intégré | Décision durable |
|---------------------------------------------------------------|---------------------------------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------------------------|
| prompt de démarrage versionné | `210-WORKFLOW.md` + prompts de release | `OWNED` | contrat générique renforcé ; commandes et baselines propres au projet restent locales |
| contrats de fichiers/familles | `300-DOCUMENTATION.md` + `004-FILE_FAMILY_CONTRACTS.md` | `OWNED` | pattern facultatif, jamais inventaire obligatoire |
| lifecycle inter-release / horizon | `200-GOVERNANCE.md`, `210-WORKFLOW.md`, `300-DOCUMENTATION.md`, `005-RELEASE_PLAN.md` | `OWNED` | fermeture, transition et revue d'horizon ont désormais des owners explicites |
| profil Rust contrôlé | `510-RUST_CARGO.md` + skeleton `002` | `OWNED` | explicité, crate-root, suffixes architecturaux, séparation lib/bin et tracing transverse bornés |
| audits Rust structurels | `002-RUST_STYLE_AUDITS.md`, `003-RUST_EXPORT_COMPLETENESS.md`, scripts monotâches | `OWNED` | pas d'audit composite ; `const_blocks` reste sélectionné lorsque l'ordre est sémantique |
| Tauri 2 | `700-FRAMEWORKS.md` + `710-TAURI.md` | `OWNED` | host/layout contrôlé, capabilities/plugins, TS-RS, versions et gate framework possédés |
| skeleton Tauri contrôlé complet | étude `002-TAURI_SKELETON_DECISION.md` | `DEFER` | report durable jusqu'à matérialisation d'owners Web ; repris dans `ROADMAP.md` sans numéro de release |
| Web / Vite / TypeScript / Sass | aucun owner dédié en `0.0.5` | `DEFER` | ne pas créer de profils placeholders pour satisfaire le skeleton Tauri |
| Android/JNI Rust, distribution Games, architecture métier KSP | owners locaux ou preuves insuffisantes | `LOCAL/DEFER` | aucune aspiration dans PGR central |
La navigation durable pointe désormais vers les owners correspondant réellement aux décisions livrées. Les skeletons `001` et `002` restent composables ; aucun `003-TAURI...` n'est créé par anticipation. Les audits réutilisables restent opt-in ou sélectionnés par profil selon leur propriété, et aucune divergence historique des corpus n'est transformée rétroactivement en dette PGR.
Le `DEFER` Tauri est le seul travail différé de cette réconciliation suffisamment décidé pour être reporté explicitement dans la roadmap. Les trois axes plus larges identifiés pendant `alpha.8` restent des **candidats d'horizon**, pas des engagements : maturation de profils techniques composables, projet documentaire/spécification avec `saselang-bible` comme corpus pilote possible, et audit de généricité de PGR hors développement logiciel.
Cette réconciliation confirme que la mission de `0.0.5` est suffisamment fermée pour entrer en RC après la gate `beta.1`, sous réserve qu'aucune incohérence documentaire ou de navigation ne soit révélée par l'opérateur.

View File

@@ -0,0 +1,141 @@
<!-- file: docs/studies/002-TAURI_SKELETON_DECISION.md -->
<!-- version: 1 -->
# Décision de skeleton Tauri pour projets contrôlés
## Objet
Cette étude enregistre la décision `0.0.5-alpha.9` sur la matérialisation d'un skeleton Tauri pour les nouveaux projets que l'organisation contrôle.
Elle ne redéfinit pas les règles Tauri. Leur owner normatif reste [`../rules/710-TAURI.md`](../rules/710-TAURI.md). Le contrat des skeletons reste possédé par [`../rules/220-SKELETONS.md`](../rules/220-SKELETONS.md).
## Corpus et état observé
Le corpus de réconciliation reste celui de `0.0.5` :
```text
KSP 0.3.16 6 hosts Tauri
Games 0.3.5 2 hosts Tauri
```
Les `8` hosts possèdent le même socle structurel :
```text
Cargo.toml
build.rs
package.json
tsconfig.json
tauri.conf.json
vite.config.ts
capabilities/
src/lib.rs
src/main.rs
src/tauri.rs
frontend/
```
Aucun des `8` hosts n'utilise `src-tauri/`. Tous utilisent Vite et TypeScript. Les `6` hosts KSP utilisent en plus Bootstrap et `sass-embedded`, tandis que les `2` POC Games ne constituent pas une preuve suffisante pour imposer ces deux choix au profil Tauri universel.
KSP apporte également l'usage éprouvé de TS-RS pour les structures Rust partagées avec le frontend. Cette frontière est déjà cadrée par `710-TAURI.md` pour les projets contrôlés.
Ces observations qualifient un **candidat de skeleton organisationnel** ; elles ne suffisent pas à créer des règles Web générales.
## Owners déjà suffisants
Les responsabilités suivantes ont un owner PGR matérialisé :
- gouvernance et composition des skeletons : `200`, `210`, `220` ;
- Rust/Cargo et skeleton Rust : `510` + `002-RUST_CARGO_WORKSPACE.md` ;
- plateformes déjà matérialisées lorsqu'elles s'appliquent, notamment Android : `610` ;
- host Tauri, capabilities, plugins, hooks, version produit et frontière Rust/frontend : `710`.
La partie native du candidat n'a donc pas besoin d'un nouveau propriétaire caché dans le skeleton.
## Owner manquant
PGR `0.0.5` ne possède aucun profil Web dédié pour :
- organisation des sources frontend ;
- TypeScript comme langage frontend ;
- Vite comme bundler/dev server ;
- Sass/CSS et ownership des sorties générées ;
- framework CSS généraliste tel que Bootstrap ;
- validation Web proportionnée et artefacts de build correspondants.
Créer immédiatement un skeleton qui configure ces éléments ferait du skeleton le propriétaire normatif implicite de la stack Web. Ce serait contraire au principe suivant : un skeleton sélectionne et compose des owners ; il ne remplace pas les owners manquants.
## Vérification externe Tauri 2
La documentation Tauri 2 courante confirme deux points utiles mais ne comble pas le manque d'owner Web :
- `create-tauri-app` fournit plusieurs choix frontend et Tauri peut aussi être ajouté manuellement à un frontend existant ; le layout standard `src-tauri/` est donc un scaffold supporté, pas une obligation conceptuelle pour les projets PGR contrôlés ;
- les documentations de plugins décrivent aussi l'installation explicite des dépendances Cargo, l'initialisation Rust, les bindings JavaScript lorsque nécessaires et les permissions/capabilities, ce qui reste compatible avec la convention PGR d'intégration explicite.
Références vérifiées le `2026-09-25` :
- <https://v2.tauri.app/start/create-project/>
- <https://v2.tauri.app/start/project-structure/>
- <https://v2.tauri.app/plugin/file-system/>
- <https://v2.tauri.app/learn/security/using-plugin-permissions/>
## Décision
Décision `alpha.9` :
```text
DEFER
```
PGR `0.0.5` **ne crée pas** de skeleton Tauri `003`.
Le report est intentionnel : le profil Tauri est suffisamment mûr, mais le skeleton complet ne peut pas encore être instancié sans absorber prématurément les responsabilités Web.
Ce `DEFER` ne remet pas en cause les conventions Tauri déjà normalisées dans `710-TAURI.md` et ne réouvre pas les décisions `alpha.6` à `alpha.8`.
## Candidat conservé
Lorsque les owners Web seront suffisants, le skeleton candidat pour une nouvelle baseline contrôlée devra réévaluer puis, si toujours justifié, composer :
```text
001-PROJECT_GOVERNANCE_FOUNDATION
+ 002-RUST_CARGO_WORKSPACE
+ profil Tauri 710
+ profils Web réellement applicables
```
Le candidat organisationnel conserve les choix suivants sans les promouvoir aujourd'hui en règles Web :
- host Cargo direct sans `src-tauri/` ajouté uniquement pour reproduire le scaffold standard ;
- `frontend/` pour les sources Web ;
- Vite + TypeScript ;
- Sass pour la chaîne de styles ;
- Bootstrap comme baseline de présentation lorsqu'un framework CSS généraliste est utile ;
- TS-RS pour les DTO Rust possédés par le host et partagés avec TypeScript ;
- `tauri.rs` comme owner de l'assemblage et des wrappers `#[tauri::command]` ;
- ajout explicite et revu des plugins, dépendances frontend et capabilities minimales ;
- WASM dans une unité séparée par défaut ;
- politique de versions classifiée `CANONICAL` / `MIRROR-EXACT` / `MIRROR-STABLE` selon les owners concernés.
## Conditions de sortie du report
Un futur plan MAY promouvoir ce candidat lorsqu'il existe au minimum des owners suffisants pour :
1. TypeScript et conventions Web contrôlées ;
2. Vite et ownership des commandes/dev server/build outputs ;
3. Sass/CSS et statut des artefacts générés ;
4. validation frontend indépendante de la validation Tauri ;
5. choix Bootstrap ou alternative clairement classifié comme décision de skeleton plutôt que règle Tauri.
Le futur travail devra alors réexaminer le corpus courant au lieu de supposer que les versions ou outils observés dans KSP/Games restent les bons choix.
## Hors scope
Cette décision ne :
- crée pas de profil Web placeholder ;
- réserve pas un numéro de release futur ;
- transforme pas Bootstrap en dépendance Tauri ;
- impose pas npm plutôt qu'un autre package manager ;
- intègre pas WASM dans le host ;
- migre KSP ou Games vers PGR ;
- modifie pas les règles Tauri déjà stabilisées.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/templates/002-CARGO_MANIFESTS.md -->
<!-- version: 1 -->
<!-- version: 3 -->
# Modèles de manifests Cargo workspace et crate membre
@@ -23,11 +23,17 @@ Les trois lints Rust et les `18` niveaux Clippy reprennent la politique partagé
## Adopter le manifest membre
Copier le membre vers `crates/<nom>/Cargo.toml`, adapter `name`, son chemin dans `members` et le header `# file:`. `version.workspace = true`, `edition.workspace = true` et `publish.workspace = true` rendent visibles les héritages choisis ; ajouter séparément `license.workspace = true`, `repository.workspace = true` ou `authors.workspace = true` si les valeurs existent à la racine et concernent cette crate. `[lints] workspace = true` est requis pour reprendre les niveaux du workspace : leur présence à la racine ne suffit pas à les appliquer automatiquement aux membres.
Copier le membre vers `crates/<nom>/Cargo.toml`, adapter `name`, son chemin dans `members` et le header `# file:`. Pour un nouveau projet contrôlé, `<nom>` commence par le nom court ou l'abréviation canonique du projet et rend lisibles la responsabilité puis le rôle/cible utiles, conformément à [`510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Ne pas conserver `project-core-lib` comme nom réel et ne pas ajouter un suffixe uniquement par symétrie. `version.workspace = true`, `edition.workspace = true` et `publish.workspace = true` rendent visibles les héritages choisis ; ajouter séparément `license.workspace = true`, `repository.workspace = true` ou `authors.workspace = true` si les valeurs existent à la racine et concernent cette crate. `[lints] workspace = true` est requis pour reprendre les niveaux du workspace : leur présence à la racine ne suffit pas à les appliquer automatiquement aux membres.
Le bloc `[dependencies]` vide se remplit selon les besoins de la crate. Pour une dépendance déjà déclarée sous `[workspace.dependencies]`, utiliser localement `nom = { workspace = true }`, avec `features = [...]` au besoin. Pour une dépendance strictement locale, choisir sa contrainte directement dans ce membre ; aucune liste tierce arbitraire n'est copiée par ce modèle.
Une application binaire peut reprendre les mêmes héritages après adaptation du nom, du layout et de sa cible `src/main.rs` ; ne pas fabriquer un deuxième manifest quasi identique. Une crate à version ou politique de publication réellement indépendante doit déclarer ses valeurs locales et ne plus hériter ces champs par automatisme.
Pour une nouvelle baseline contrôlée, une bibliothèque réutilisable et son exécutable sont normalement deux packages Cargo distincts. Le package binaire peut reprendre les mêmes héritages de workspace, mais son manifest est légitime parce qu'il exprime une frontière de packaging et de dépendances différente : il dépend des crates `-lib`, conserve un `main.rs` mince et porte un nom révélant son rôle (`-cli`, `-desk`, `-worker`, `-job`, `-server`, etc.) lorsque ce classificateur est réellement applicable. Ne pas fusionner `lib` + `bin` dans un seul package uniquement pour éviter un manifest membre supplémentaire.
Une paire `*-api` / `*-lib` n'est créée que si un vrai contrat séparé le justifie. Une crate `-api` purement déclarative ne reçoit normalement ni subscriber global ni dépendance vers la crate de logging. Une crate `-lib` unique suffit lorsqu'il n'existe qu'une implémentation utile et aucune frontière d'extension/backend/lifecycle.
Tauri est une exception framework documentée par [`710-TAURI.md`](../rules/710-TAURI.md) : son host peut être un package mixte `lib + bin`, mais les deux targets doivent alors posséder des noms distincts selon le contrat Tauri.
Une crate à version ou politique de publication réellement indépendante doit déclarer ses valeurs locales et ne plus hériter ces champs par automatisme.
## Exception locale de lints

View File

@@ -1,24 +1,25 @@
<!-- file: docs/templates/003-NEW_SESSION_PROMPT.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Spécification du prompt manuel de nouvelle session
## Usage
Cette spécification sert à préparer un court message de reprise après une stable publiée. Le prompt de démarrage propre à la version suivante reste un fichier `prompts/NNN-VX_Y_Z_START_PROMPT.md` finalisé selon [`210-WORKFLOW.md`](../rules/210-WORKFLOW.md). Le message manuel indique où commencer et quelles preuves fournir ; il ne copie ni ce prompt, ni le plan, ni le history de la release.
Cette spécification sert à préparer le court message opérateur remis après une stable publiée. Le prompt de démarrage propre à la version suivante reste un fichier `prompts/NNN-VX_Y_Z_START_PROMPT.md` finalisé selon [`210-WORKFLOW.md`](../rules/210-WORKFLOW.md). Le message manuel indique où commencer et quelles preuves fournir ; il ne copie ni ce prompt, ni le plan, ni le history de la release.
La comparaison des prompts PGR `002` et `003` montre une structure récurrente (baseline taggée, cible, première tranche et mission). `003` ajoute le rapport de vérification post-tag et un texte transmis explicitement à l'opérateur. Une forme courte commune réduit les oublis lors de la clôture suivante, sans rendre obligatoires des champs propres à PGR dans les autres projets.
Lorsque la cible suivante est connue et que la vérification post-tag applicable est terminée, ce message doit être remis spontanément à l'opérateur sans attendre qu'il le réclame. Lorsque la cible suivante n'est pas suffisamment décidée, PGR ne fabrique pas de version ou de prompt : le message de clôture indique que l'horizon reste ouvert et pointe vers le handoff ou la revue d'horizon applicable.
## Champs à renseigner
1. **Baseline** : identifier le tag stable, le nom réel de l'archive source téléchargée depuis ce tag et la forge d'origine. Une archive sans `.git` demande une vérification interne ; elle ne prouve pas à elle seule le commit du tag.
2. **Preuve post-tag** : si un rapport de publication/vérification distinct existe et est réellement `PASS`, indiquer son chemin exact et le joindre. Sinon, signaler explicitement qu'il est absent ou que son état n'est pas `PASS` ; ne jamais fabriquer cette preuve ni la confondre avec le tag.
1. **Baseline** : identifier le tag stable, le nom réel de l'archive source téléchargée depuis ce tag et la forge d'origine. Une archive sans `.git` est normale pour une archive de tag et se vérifie par sa cohérence interne ; elle ne porte pas elle-même les métadonnées Git du dépôt.
2. **Preuve post-tag** : si un rapport de publication/vérification distinct existe et est réellement `PASS`, indiquer son chemin exact et demander qu'il soit fourni avec l'archive. Sinon, signaler explicitement son état réel ; ne jamais fabriquer cette preuve ni la confondre avec le tag.
3. **Contrat de reprise** : indiquer le chemin du prompt `prompts/NNN-VX_Y_Z_START_PROMPT.md` existant dans la baseline, la version cible et la première tranche attendue. Le prompt versionné possède la mission détaillée et le scope de la release.
4. **Intake** : demander la vérification de l'archive, des règles applicables, de la gouvernance, de la structure documentaire et des deltas/histories avant de préparer la première tranche. Adapter la liste aux artefacts effectivement présents et aux risques du projet.
5. **Horizon ouvert** : si aucune version suivante n'est encore suffisamment décidée, remplacer le contrat de reprise de version par l'instruction exacte de revue/handoff applicable ; ne pas inventer un `NEXT` artificiel.
## Forme à remplir et à remettre
Après vérification des chemins et de l'état réel du rapport, écrire **une seule phrase** adaptée au projet. Si le rapport est `PASS` et fourni :
Après vérification des chemins et de l'état réel du rapport, écrire **une seule phrase** adaptée au projet lorsque la cible suivante est connue. Si le rapport est `PASS` et fourni :
```text
Partir de l'archive <nom exact du ZIP issu du tag vX.Y.Z> et du rapport post-tag <chemin exact du rapport PASS>. Lire et exécuter <chemin du prompt de démarrage dans la baseline>. Vérifier l'archive, les règles, la gouvernance, la structure documentaire, les deltas/histories et la cohérence globale avant de préparer <version cible-alpha.1>.
@@ -26,4 +27,6 @@ Partir de l'archive <nom exact du ZIP issu du tag vX.Y.Z> et du rapport post-tag
Si le rapport est absent ou non validé, retirer « et du rapport post-tag ... » de la phrase et préciser séparément son état réel à la nouvelle session. Conserver les noms exacts de la forge et des fichiers du projet : la forme `<nom exact du ZIP>` n'est qu'un champ de rédaction.
Rédiger le texte manuel à la clôture après le contrôle de l'archive publiée et le remettre à l'opérateur. Une interruption au milieu d'une prerelease suit le mécanisme de reprise de la tranche courante prévu dans `210-WORKFLOW.md` ; elle ne crée pas artificiellement un nouveau prompt de version.
Si l'horizon est ouvert, remettre plutôt une consigne courte qui identifie la stable publiée, les preuves disponibles et le handoff/revue d'horizon à reprendre. Cette consigne ne doit contenir ni numéro de version hypothétique, ni faux prompt de démarrage.
Une interruption au milieu d'une prerelease suit le mécanisme de reprise de la tranche courante prévu dans `210-WORKFLOW.md` ; elle ne crée pas artificiellement un nouveau prompt de version.

View File

@@ -0,0 +1,80 @@
<!-- file: docs/templates/004-FILE_FAMILY_CONTRACTS.md -->
<!-- version: 2 -->
# Pattern — contrats locaux de fichiers ou familles
## Objectif
Ce pattern aide un projet à expliciter quelques responsabilités de fichiers ou familles durables lorsqu'elles deviennent difficiles à déduire de l'arborescence, de la toolchain ou des règles déjà existantes.
Il complète le contrat documentaire de [`../rules/300-DOCUMENTATION.md`](../rules/300-DOCUMENTATION.md). Il n'impose aucun fichier particulier au projet destinataire.
## Quand l'utiliser
Créer un registre local seulement lorsqu'il réduit une ambiguïté réelle, par exemple lorsque :
- plusieurs familles voisines ont des rôles différents qui provoquent régulièrement des erreurs de placement ;
- un fichier est une source de vérité tandis qu'un autre est généré ou dérivé ;
- une famille possède une règle de modification ou de synchronisation non évidente ;
- des contributeurs doivent savoir quel owner modifier sans relire plusieurs documents spécialisés.
Ne pas l'utiliser pour inventorier chaque fichier d'un petit dépôt, recopier les rôles standards déjà décrits par PGR ou dupliquer un manifeste/build system qui possède déjà l'information.
## Forme minimale
Le nom et l'emplacement appartiennent au projet destinataire. `docs/rules/FILE_CONTRACTS.md` est un nom possible, pas une convention PGR obligatoire.
Une entrée utile contient au minimum :
```text
file or family
responsibility
modification rule
```
Lorsque ces informations sont utiles, le projet MAY ajouter :
```text
source of truth / generated status
local owner or reference
validation or synchronization requirement
```
Une table Markdown convient aux registres structurés :
```markdown
| Fichier ou famille | Responsabilité | Règle de modification |
|---------------------|------------------|----------------------------|
| `<path-or-pattern>` | `<rôle durable>` | `<quand/comment modifier>` |
```
Une liste courte est préférable lorsqu'elle reste plus lisible. La forme n'est pas normative ; la clarté de la responsabilité l'est pour le projet qui choisit d'adopter ce contrat.
## Règles d'adaptation
Lors de l'instanciation :
1. sélectionner uniquement les fichiers ou familles dont le contrat local apporte une information non évidente ;
2. référencer l'owner détaillé existant au lieu de recopier ses règles ;
3. regrouper les chemins qui partagent réellement le même contrat plutôt que créer une entrée par fichier ;
4. distinguer explicitement source de vérité et artefact généré lorsque cette distinction existe ;
5. supprimer ou fusionner une entrée devenue redondante lorsque son rôle est désormais évident ailleurs.
Le registre SHOULD évoluer lorsqu'une responsabilité ou règle de modification change réellement. Il SHOULD NOT être modifié uniquement parce qu'un nouveau fichier respecte déjà une famille décrite.
## Exemple abstrait
```markdown
| Fichier ou famille | Responsabilité | Règle de modification |
|--------------------|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| `config/*.example` | Décrire les exemples versionnés de configuration locale. | Mettre à jour lorsqu'une option utilisateur versionnée est ajoutée ou retirée ; ne jamais y placer de secret réel. |
| `generated/**` | Contenir les sorties reconstruisibles produites par la toolchain choisie. | Ne pas modifier manuellement ; régénérer depuis la source de vérité documentée. |
```
Cet exemple illustre la forme uniquement. Il ne crée aucune règle universelle sur les chemins `config/` ou `generated/`.
## Critère de sortie
Le pattern est utile si un nouveau contributeur peut déterminer plus vite où une responsabilité vit et quand modifier une famille sans devoir maintenir un catalogue parallèle du dépôt.
S'il ajoute davantage de cérémonie que d'information, ne pas l'instancier ou le retirer.

85
docs/templates/005-RELEASE_PLAN.md vendored Normal file
View File

@@ -0,0 +1,85 @@
<!-- file: docs/templates/005-RELEASE_PLAN.md -->
<!-- version: 1 -->
# Pattern — plan de release
## Usage
Ce pattern aide un projet gouverné par PGR à construire un plan de release suffisamment précis pour piloter plusieurs tranches sans transformer le plan en journal d'activité. Il complète `alpha.1` et [`210-WORKFLOW.md`](../rules/210-WORKFLOW.md) ; il n'impose ni le titre exact des sections, ni un nombre fixe de prereleases.
Un petit changement qui ne nécessite pas de plan dédié n'a pas à instancier ce pattern.
## Contrat minimal
Un plan utile doit permettre de comprendre :
- d'où part la release et quelle mission elle doit rendre vraie ;
- ce qui est acquis, ouvert, inclus et explicitement hors-scope ;
- comment les risques, validations et smokes importants seront traités ;
- comment le travail est découpé actuellement et quels signaux imposeraient un REPLAN ;
- **où et quand la release sera clôturée** : documentation durable, `CHANGELOG`, `ROADMAP`, prompt/handoff suivant, plan et publication ;
- si l'horizon de roadmap doit être revu avant la fermeture et, si oui, à quel moment ou selon quel déclencheur.
## Forme candidate
```markdown
# Plan X.Y.Z — <mission courte>
## Statut et sources
Baseline, prompt, rapports ou corpus réellement autoritaires.
## Mission
Résultat attendu de la release.
## État acquis et questions ouvertes
Décisions déjà établies et points qui doivent encore être instruits.
## Scope et hors-scope
Frontières fonctionnelles, techniques et documentaires.
## Stratégie de validation
Gates, smokes et preuves importantes ; renvois vers la matrice plutôt que copie exhaustive.
## Trajectoire prévisionnelle
Responsabilité attendue de chaque tranche. Le forecast reste révisable.
## Déclencheurs de REPLAN
Signaux qui obligeraient à recalculer le travail restant.
## Clôture et transition
- réconciliation documentaire durable : <tranche/moment> ;
- entrée CHANGELOG candidate/finale : <tranche/moment> ;
- revue/mise à jour ROADMAP : <tranche/moment> ;
- prompt ou handoff de reprise : <tranche/moment ou cible encore ouverte> ;
- fermeture du plan : <tranche/moment> ;
- promotion stable et vérification post-tag : <mécanisme>.
## Revue d'horizon
- état : <non nécessaire / requise / horizon volontairement ouvert> ;
- raison : <pourquoi> ;
- moment ou déclencheur : <quand> ;
- sorties autorisées : <étendre / laisser ouvert / différer-pause / objectif accompli>.
```
## Règles d'adaptation
Le plan doit rester proportionné. Une responsabilité déjà possédée par une règle, une matrice ou une étude est référencée plutôt que recopiée. Les résultats détaillés des gates restent dans `history/`.
La rubrique `Clôture et transition` est un contrat d'attribution des responsabilités, pas l'obligation d'exécuter tous les travaux en fin de release. Une documentation directement liée à une fonctionnalité continue d'évoluer dans la tranche qui la modifie.
La rubrique `Revue d'horizon` peut simplement indiquer qu'aucune revue n'est nécessaire parce que la roadmap s'étend déjà suffisamment. Si la prochaine version n'est pas décidée, cette rubrique doit l'assumer explicitement plutôt que fabriquer une roadmap.
PGR n'impose pas un journal de révisions systématique dans le plan. Un REPLAN met à jour les parties devenues fausses et le delta/history correspondant conserve la preuve du changement.
## Critère de sortie
Le plan est prêt lorsqu'il autorise le développement sans laisser ambiguës les responsabilités de fermeture, les validations majeures ni le devenir de l'horizon macro. Après stable, abandon ou supersession, il devient une référence historique de la release.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
<!-- version: 9 -->
<!-- version: 12 -->
# Matrice de validation PGR
@@ -27,7 +27,7 @@ Les catégories et règles de sélection sont définies dans [`../rules/400-VALI
Un contrôle manuel peut rester dans la matrice lorsqu'aucun audit automatisé fiable n'existe encore. PGR SHOULD automatiser uniquement les propriétés stables et objectivement contrôlables.
`PGR-MD-TABLE-001` reste volontairement monotâche : il contrôle uniquement la structure et l'alignement source des tableaux Markdown.
`PGR-MD-TABLE-001` reste volontairement monotâche : il contrôle uniquement la structure et l'alignement source des tableaux Markdown. Il couvre les tableaux Markdown ordinaires ainsi que les exemples placés dans des fences explicitement `markdown` ou `md`. Les autres blocs fenced restent ignorés afin de ne pas interpréter comme Markdown des sorties terminal, extraits de code ou tableaux ASCII d'un autre langage.
`PGR-MD-HEADER-001` contrôle seulement les deux premières lignes des fichiers Markdown sélectionnés : chemin relatif depuis la racine et révision interne positive. La revue `PGR-DOC-001` reste responsable du rôle documentaire et de la pertinence réelle d'un incrément de révision.
@@ -37,9 +37,11 @@ PGR préfère plusieurs audits simples avec une responsabilité identifiable à
## Contrôles Rust réutilisables, hors gate du dépôt PGR
[`002-RUST_STYLE_AUDITS.md`](002-RUST_STYLE_AUDITS.md) décrit deux audits facultatifs : `audit_rust_blank_lines.py` contrôle l'espacement des lignes vides, `audit_rust_const_blocks.py` contrôle l'ordre et l'espacement des blocs de constantes au niveau racine. Ils acceptent des chemins Rust sélectionnés et partagent un petit support lexical ; leur validation ciblée dans PGR est `python3 -B -m unittest discover -s tests -p test_rust_audits.py`.
[`002-RUST_STYLE_AUDITS.md`](002-RUST_STYLE_AUDITS.md) décrit les audits monotâches de structure Rust : lignes vides, blocs `const`, séparation des items, ordre des modules, disposition des imports de traits et blocs de réexports. Ils acceptent des chemins Rust sélectionnés et partagent un petit support lexical ; leur validation ciblée dans PGR est `python3 -B -m unittest discover -s tests -p test_rust_audits.py -v`.
Leur disponibilité ne transforme pas PGR en workspace Rust et ne rend pas automatiquement l'ordre alphabétique applicable à chaque fichier d'un projet. Lors d'une adoption, le projet propriétaire sélectionne ses chemins après `cargo fmt` et applique sa propre gate ; Rustfmt et Clippy conservent les contrôles qu'ils possèdent.
Pour une nouvelle baseline contrôlée, les propriétés sans ordre métier peuvent faire partie du profil strict. L'audit `const_blocks` reste explicitement sélectionné, car une table de protocole, un schéma ou une séquence normative ne doit jamais être réordonné pour satisfaire un tri mécanique. Leur disponibilité ne transforme pas PGR en workspace Rust ; le projet propriétaire exécute ces audits après `cargo fmt`, et Rustfmt/Clippy/rustc conservent les contrôles qu'ils possèdent.
[`003-RUST_EXPORT_COMPLETENESS.md`](003-RUST_EXPORT_COMPLETENESS.md) décrit séparément `audit_rust_export_completeness.py`, audit facultatif de façade crate-root. Sa validation ciblée dans PGR est `python3 -B -m unittest discover -s tests -p test_rust_export_completeness.py -v`. L'audit est destiné aux projets Rust qui choisissent explicitement ce contrat ; PGR n'exécute pas ce script sur son propre dépôt comme gate, car il ne constitue pas lui-même un workspace Rust.
## Politique Eclipse actuelle

View File

@@ -1,34 +1,79 @@
<!-- file: docs/validation/002-RUST_STYLE_AUDITS.md -->
<!-- version: 1 -->
<!-- version: 2 -->
# Audits facultatifs de style Rust
# Audits monotâches de structure Rust
## Responsabilité et provenance
Les sources KSP `0.3.16` et Games `0.3.5` possèdent chacune un `audit_rust_general_rules.py` qui mêle plusieurs règles. Le découpage PGR fournit deux contrôles ciblés sans copier l'orchestrateur ni les règles propres aux deux projets. Le contrat de style facultatif est dans [`510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md), et la sélection des preuves dans [`400-VALIDATION.md`](../rules/400-VALIDATION.md).
Les sources KSP `0.3.16` et Games `0.3.5` possèdent chacune un `audit_rust_general_rules.py` composite. `0.0.3` avait déjà extrait deux propriétés ciblées ; `0.0.5-alpha.8` termine la réconciliation des propriétés de forme communes sans importer cet orchestrateur monolithique ni ses règles locales.
`scripts/audit_rust_blank_lines.py` contrôle les lignes vides consécutives et les lignes vides dans un corps de fonction/méthode, `struct` ou `enum`. `scripts/audit_rust_const_blocks.py` contrôle l'ordre `pub`, `pub(crate)`, privé, le tri naturel des noms dans chaque visibilité et l'espacement entre constantes consécutives au niveau racine du fichier. `scripts/_rust_audit_common.py` partage la sélection des chemins et le masquage des commentaires/littéraux : il ne définit aucune nouvelle politique.
Le contrat normatif est dans [`510-RUST_CARGO.md`](../rules/510-RUST_CARGO.md). Les scripts disponibles sont read-only et chacun possède une seule famille de preuve :
```text
audit_rust_blank_lines.py
lignes vides consécutives et lignes vides dans fn/méthode/struct/enum
audit_rust_const_blocks.py
ordre visibilité/nom et espacement des blocs const explicitement sélectionnés
audit_rust_item_spacing.py
exactement une ligne vide entre items Rust distincts voisins d'un même scope
audit_rust_module_order.py
modules de production avant tests, ordre alphabétique de chaque groupe
audit_rust_trait_import_blocks.py
bloc de use non exportés au module, avant les déclarations et sans ligne vide interne
audit_rust_reexport_blocks.py
bloc pub use avant pub(crate) use, espacement exact entre/à l'intérieur des blocs
```
`scripts/_rust_audit_common.py` partage seulement la sélection de fichiers et le masquage lexical des commentaires/littéraux. Il ne définit aucune politique. `audit_rust_export_completeness.py` reste séparé dans [`003-RUST_EXPORT_COMPLETENESS.md`](003-RUST_EXPORT_COMPLETENESS.md), car il possède la façade et les chemins canoniques plutôt que le formatage.
## Adoption et commandes
Dans un projet Rust qui choisit ces conventions, copier les trois fichiers `scripts/*.py` concernés en gardant le helper à côté des deux audits. Adapter les headers `file` au chemin du projet et maintenir une révision de fichier propre à cette copie.
Choisir explicitement les fichiers concernés par chaque contrat. Après formatage :
Pour une nouvelle baseline Rust contrôlée qui sélectionne le profil strict, les audits sans interprétation métier peuvent être exécutés sur les sources concernées après Rustfmt :
```bash
cargo fmt --all -- --check
python3 -B scripts/audit_rust_blank_lines.py crates/example/src
python3 -B scripts/audit_rust_blank_lines.py crates
python3 -B scripts/audit_rust_item_spacing.py crates
python3 -B scripts/audit_rust_module_order.py crates
python3 -B scripts/audit_rust_trait_import_blocks.py crates
python3 -B scripts/audit_rust_reexport_blocks.py crates
```
L'audit des constantes reste différent : sélectionner seulement les fichiers dont les blocs `const` doivent réellement suivre l'ordre mécanique PGR. Exemple :
```bash
python3 -B scripts/audit_rust_const_blocks.py crates/example/src/constants.rs
```
Une invocation sans chemin est une erreur, de même qu'un chemin absent ou aucun fichier Rust sélectionné. Un répertoire sélectionne récursivement les fichiers `.rs` hors répertoires générés habituels. Les audits ne modifient ni les fichiers inspectés ni l'arborescence de build. Les sorties utilisent le chemin et la ligne ; le code de sortie est `0` si propre, `1` pour violation, `2` pour sélection/lecture impossible.
Une invocation sans chemin est une erreur, de même qu'un chemin absent ou aucun fichier Rust sélectionné. Un répertoire sélectionne récursivement les fichiers `.rs` hors répertoires générés habituels. Les audits ne modifient ni les sources ni l'arborescence de build. Le code de sortie est `0` si propre, `1` pour violation et `2` pour sélection/lecture impossible.
Ne sélectionner pour l'audit des constantes que les fichiers dont les blocs racine sans sémantique d'ordre doivent effectivement suivre ce contrat. Un tri de table de protocole, des migrations, des champs de wire format ou de toute séquence métier peut casser le programme : sa revue appartient au projet. Ne pas utiliser la sortie du script comme justification pour les réordonner.
`audit_rust_trait_import_blocks.py` ne prouve pas qu'un `use` importe réellement un trait ; cette sémantique appartient au profil et à la revue/compilation. `audit_rust_reexport_blocks.py` ne trie pas les symboles exportés. Dans les deux cas, Rustfmt reste l'owner de l'ordre intra-bloc lorsque les lignes sont contiguës.
## Limites mesurées
`audit_rust_item_spacing.py` ne possède pas l'espacement des blocs compacts homogènes `const`/`static`/`type` : il les délègue afin de ne pas transformer une séparation sémantique entre deux grandes tables en faux positif. L'audit spécialisé `const_blocks` reste opt-in sur des fichiers explicitement choisis.
La prévalidation des sources réels a porté sur `300` fichiers KSP et `59` Games sous `crates/**/src/`. L'audit des lignes vides n'y détecte aucun écart. L'audit de constantes détecte `17` signaux KSP et aucun Games lorsque tous ces fichiers lui sont soumis ; des fichiers KSP contiennent des séquences ordonnées selon le protocole ou le schéma, ce qui rend une gate globale du tri inappropriée. Ces observations ne sont pas une gate de validation opérateur PGR.
## Mesure croisée KSP / Games
L'analyse lexicale masque littéraux et commentaires courants, y compris commentaires imbriqués et chaînes Rust brutes. Elle n'est pas un parseur Rust : les macros élaborées, syntaxe nouvelle, attributs complexes, items associés dans `impl` et constantes dans modules inline n'entrent pas dans le contrat garanti de l'audit des blocs racine. Les lignes vides à l'intérieur d'un littéral multilignes sont exclues. Les cas qui demandent une interprétation sémantique nécessitent la revue humaine ou les outils Rust du projet.
La prévalidation `alpha.8` a exécuté les quatre nouveaux audits sur l'ensemble des fichiers Rust sous `crates/` des archives fournies : `603` fichiers KSP `0.3.16` et `87` fichiers Games `0.3.5`. Les quatre contrôles sont propres sur les deux corpus :
Rustfmt reste propriétaire de l'ordre qu'il réécrit déjà dans les groupes de `use`/`mod`. Clippy et le compilateur restent responsables de leurs diagnostics. La suite `tests/test_rust_audits.py` vérifie les propriétés ciblées avec des cas propres, des violations et des littéraux/commentaires qui contiennent des accolades.
```text
KSP 0.3.16 Games 0.3.5
item spacing clean / 603 clean / 87
module order clean / 603 clean / 87
trait import blocks clean / 603 clean / 87
re-export blocks clean / 603 clean / 87
```
L'audit des lignes vides est également propre sur les `603` et `87` fichiers. En revanche, l'audit `const_blocks` appliqué aveuglément à tout le corpus produit `18` signaux KSP et `0` Games. Certains signaux KSP concernent des tables/schémas dont l'ordre est intentionnel, ce qui confirme qu'il ne doit pas devenir une gate globale par défaut. Ces mesures sont des preuves de réconciliation PGR, pas une validation opérateur de KSP/Games.
## Limites et owners
Les analyses masquent les commentaires et littéraux courants, y compris chaînes brutes et commentaires imbriqués, mais ne constituent pas un parseur Rust. Elles n'essaient pas de comprendre les expansions de macros, l'ordre métier, les protocoles ou les dépendances sémantiques entre items.
PGR ne crée pas d'audit automatique pour imposer un ordre global des catégories `enum`, `struct`, `impl`, fonctions ou helpers privés. Le rapprochement `struct` puis `impl`, et la préférence des éléments visibles avant les helpers privés, restent des conventions de lisibilité soumises à la sémantique locale.
Rustfmt possède le formatage et les tris qu'il sait réellement réécrire ; rustc et Clippy conservent leurs diagnostics. Les tests PGR de ces scripts vivent dans `tests/test_rust_audits.py`.

View File

@@ -0,0 +1,145 @@
<!-- file: docs/validation/003-RUST_EXPORT_COMPLETENESS.md -->
<!-- version: 2 -->
# 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 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 ; il matérialise uniquement le contrat strict lorsqu'un projet choisit de l'adopter.
L'audit émet deux familles de diagnostics :
```text
RUST-API-201
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
référence intra-crate non canonique à un item possédant une identité crate-root ;
la forme attendue est `crate::Item`
```
`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` historiquement identique à l'exception de sa révision. Cette convergence justifie l'extraction du concept, mais pas une copie littérale.
Deux limites historiques ont été identifiées :
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 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 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
```
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.
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
Pour adopter cette convention dans un projet Rust, copier :
```text
scripts/_rust_audit_common.py
scripts/audit_rust_export_completeness.py
```
puis adapter les headers de fichiers au dépôt destinataire.
Commande canonique :
```bash
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 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 d'un projet existant devrait commencer par :
```bash
python3 -B scripts/audit_rust_export_completeness.py --root . --report-only --summary-only
```
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 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.
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.
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.

115
history/0.0.5/alpha.1.md Normal file
View File

@@ -0,0 +1,115 @@
<!-- file: history/0.0.5/alpha.1.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.1 — réconciliation des baselines et cadrage
## Statut
PASS
La gate de `0.0.5-alpha.1` exécutée par l'opérateur le `2026-09-24` valide le cadrage de la réconciliation PGR avec les baselines autoritaires fournies de `khadhroony-solana-project` et `sasedev-games`. Les contrôles mécaniques demandés sont propres, les sources de réconciliation sont enregistrées et le scope Git observé correspond exactement au delta.
## Baseline et version
La tranche part de la stable PGR `v0.0.4`. Le rapport post-tag fourni établit la publication stable sur le commit :
```text
bd9aadf0cf26c89af6acecbb885b9d9335da651f
```
Les trois archives autoritaires utilisées par le cadrage sont enregistrées avec les SHA-256 suivants :
```text
pgr-v0.0.4.zip 7137bb084f017362323a2043d13ba71490be15b8786adc8d645981bf455b80ae
khadhroony-solana-project-v0.3.16.zip db3cc1b3fc3fb07ee864b02e2a7346c673d4cf63b33e8e872aca6c6458856ca0
games-v0.3.5.zip 5f5fdee1a271386766d059657d6eaa831acdc263b3e722fed738808335c0a105
```
La version contrôlée après application du delta est :
```text
0.0.5-alpha.1
```
Le plan `docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md`, l'index `docs/studies/000-README.md`, l'étude `docs/studies/001-KSP_GAMES_RECONCILIATION.md` et `deltas/0.0.5/alpha.1.md` sont présents. `history/0.0.5/alpha.1.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-24` depuis `~/Projects/pgr`, à `20:38:34`–`20:38:52`.
Les contrôles de présence et de version ont réussi sans diagnostic :
```text
VERSION 0.0.5-alpha.1
README.md version 12
docs/000-README.md version 10
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md présent
docs/studies/000-README.md présent
docs/studies/001-KSP_GAMES_RECONCILIATION.md présent
deltas/0.0.5/alpha.1.md présent
history/0.0.5/alpha.1.md absent pendant la gate
```
Les références attendues vers le plan et l'étude sont présentes dans les index contrôlés.
La suite Python globale a exécuté `15` tests, tous réussis :
```text
Ran 15 tests in 0.220s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (127 file(s))
Markdown table audit: clean (4 table(s), 127 file(s))
Markdown link audit: clean (123 local link(s), 127 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.1 structural audit: clean (43 prior pairs, 5 TOML parsed, reconciliation sources recorded)
```
Il confirme notamment :
- les trois noms d'archives et leurs SHA-256 dans l'étude ou le plan ;
- le parsing des cinq TOML du template Rust/Cargo ;
- l'appariement exact des deltas/histories des releases `0.0.1` à `0.0.4`, soit `43` paires antérieures ;
- la conservation des `FAIL` historiques `0.0.4-alpha.4` et `0.0.4-beta.1` suivis de leurs correctifs `PASS` ;
- le statut `PASS` de `0.0.4-rc.1` et `0.0.4-rel.001` ;
- l'affirmation explicite que KSP et Games ne sont pas encore gouvernés par PGR et qu'aucune migration de ces projets n'appartient au plan `0.0.5` ;
- la présence de Tauri dans la trajectoire de réconciliation et de `audit_rust_export_completeness.py` dans l'étude.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic. Après ajout des fichiers du delta, le statut indexé transmis est exactement :
```text
M README.md
M VERSION
A deltas/0.0.5/alpha.1.md
M docs/000-README.md
A docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
A docs/studies/000-README.md
A docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
La transcription du terminal intercale quelques fragments du heredoc du contrôle structurel avec la sortie de `unittest`. Les quinze tests individuels, le résumé `OK`, les trois audits Markdown, le contrôle structurel final et le scope Git restent néanmoins identifiables et ne signalent aucun échec.
## Résultat de la réconciliation initiale
La gate confirme le rôle correct des deux projets externes : KSP `v0.3.16` et Games `v0.3.5` sont des corpus réels utilisés pour éprouver et améliorer PGR avant leur future adoption ; ils ne sont pas évalués comme des projets déjà soumis aux règles PGR.
Le cadrage retient quatre besoins réutilisables à traiter séparément : contrat générique des prompts versionnés, pattern facultatif de contrats de fichiers/familles, audit Rust de façade/export completeness et premier profil framework Tauri. Aucun de ces axes n'a été matérialisé prématurément dans `alpha.1`.
Aucun fix ni REPLAN n'est requis.
## Transition
Appliquer le présent history puis committer `0.0.5-alpha.1` avant d'appliquer le delta `0.0.5-alpha.2`.
Le history `alpha.1` et le delta `alpha.2` peuvent être livrés dans le même échange sous deux archives distinctes. Cette livraison simultanée ne change pas l'ordre d'application : le delta suivant MUST rester absent du dépôt jusqu'au commit de la tranche `alpha.1`.
La tranche suivante est `0.0.5-alpha.2` et doit rester limitée au contrat générique des prompts de démarrage versionnés dans `docs/rules/210-WORKFLOW.md`, sans créer un second template de reprise manuelle ni recopier les règles locales KSP/Games.

100
history/0.0.5/alpha.2.md Normal file
View File

@@ -0,0 +1,100 @@
<!-- file: history/0.0.5/alpha.2.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.2 — contrat générique des prompts de démarrage
## Statut
PASS
La gate de `0.0.5-alpha.2` exécutée par l'opérateur le `2026-09-24` valide le renforcement du contrat générique des prompts de démarrage versionnés. Les tests, audits Markdown, contrôle structurel ciblé et contrôles Git demandés sont propres ; le scope indexé correspond exactement au delta.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.1` créé après ajout de son history `PASS`. La sortie Git fournie identifie ce commit sous la forme abrégée :
```text
3ca2518 0.0.5-alpha.1
```
Le push vers `origin/master` a réussi avant l'application de `alpha.2`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.2
```
`history/0.0.5/alpha.1.md` et `deltas/0.0.5/alpha.2.md` sont présents. `history/0.0.5/alpha.2.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-24` depuis `~/Projects/pgr`, à `20:44:04`–`20:44:48`.
Les contrôles de présence et de version ont réussi sans diagnostic :
```text
VERSION 0.0.5-alpha.2
history/0.0.5/alpha.1.md présent
deltas/0.0.5/alpha.2.md présent
history/0.0.5/alpha.2.md absent pendant la gate
docs/rules/000-README.md version 12
docs/rules/210-WORKFLOW.md version 4
```
L'index des règles contient bien la mention des `prompts versionnés` pour `210-WORKFLOW.md`.
La suite Python globale a exécuté `15` tests, tous réussis :
```text
Ran 15 tests in 0.239s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (129 file(s))
Markdown table audit: clean (4 table(s), 129 file(s))
Markdown link audit: clean (123 local link(s), 129 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.2 prompt contract audit: clean (versioned prompt strengthened, manual restart template unchanged)
```
Il confirme notamment :
- le statut `PASS` du history `alpha.1` ;
- la présence, dans `210-WORKFLOW.md`, du contrat minimal couvrant baseline autoritaire et cible, ordre de lecture, état acquis, mission/résultat, scope/hors-scope, décisions gelées/ouvertes, première gate, trajectoire prévisionnelle et responsabilité de validation ;
- le comportement fail-closed lorsqu'une source externe requise manque ;
- l'absence de détails locaux KSP/Games tels que `tokio-postgres`, `WebTransport` ou `Uroburas` dans la règle générale ;
- le maintien de `docs/templates/003-NEW_SESSION_PROMPT.md` en version `1` et de son rôle distinct de message manuel court.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic. Après ajout des fichiers du delta, le statut indexé transmis est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.2.md
M docs/rules/000-README.md
M docs/rules/210-WORKFLOW.md
```
La transcription du terminal intercale quelques fragments du heredoc de contrôle avec la sortie de `unittest`. Les quinze tests individuels, le résumé `OK`, les trois audits Markdown, le contrôle structurel final et le scope Git restent identifiables et ne signalent aucun échec.
## Résultat
Le contrat des prompts versionnés est renforcé sans imposer une liste rigide de titres, sans dupliquer le template manuel de reprise et sans aspirer les règles propres aux deux corpus externes.
La baseline et les sources externes manquantes restent fail-closed. Les décisions acquises peuvent être résumées pour empêcher une régression utile, mais restent reliées à leurs owners au lieu d'être recopiées exhaustivement dans le prompt.
Aucun fix ni REPLAN n'est requis.
## Transition
Appliquer le présent history puis committer `0.0.5-alpha.2` avant d'appliquer le delta `0.0.5-alpha.3`.
La tranche suivante est `0.0.5-alpha.3` et doit rester limitée au pattern documentaire facultatif de contrats de fichiers ou familles : owner `docs/rules/300-DOCUMENTATION.md`, guide court réutilisable et intégration minimale à la fondation `001` uniquement lorsque la complexité du projet justifie ce registre.

View File

@@ -0,0 +1,128 @@
<!-- file: history/0.0.5/alpha.3.fix.1.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.3.fix.1 — couverture des tables dans les exemples Markdown fenced
## Statut
PASS
La gate opérateur de `0.0.5-alpha.3.fix.1` exécutée le `2026-09-24` valide la correction de `PGR-MD-TABLE-001`. Le faux négatif constaté pendant `alpha.3` est couvert par des tests de régression, les deux tables fenced du template `004` sont désormais gouvernées par l'audit et les contrôles documentaires/Git sont propres.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.3`, enregistré après application du history `FAIL`. La sortie Git fournie identifie ce commit sous la forme abrégée :
```text
1b1a709 0.0.5-alpha.3
```
Le push vers `origin/master` a réussi avant l'application de `alpha.3.fix.1`.
La version contrôlée après application du fix est :
```text
0.0.5-alpha.3.fix.1
```
Les artefacts suivants sont présents pendant la gate :
```text
history/0.0.5/alpha.3.md
deltas/0.0.5/alpha.3.md
deltas/0.0.5/alpha.3.fix.1.md
tests/test_markdown_tables.py
```
`history/0.0.5/alpha.3.fix.1.md` est absent pendant la gate, conformément au workflow.
## Validation opérateur observée
Les contrôles de statut et de révision ont réussi :
```text
history/0.0.5/alpha.3.md FAIL
scripts/audit_markdown_tables.py version 2
docs/templates/004-FILE_FAMILY_CONTRACTS.md version 2
docs/validation/001-VALIDATION_MATRIX.md version 10
tests/test_markdown_tables.py version 1
```
La suite ciblée du nouvel audit a exécuté `4` tests, tous réussis :
```text
Ran 4 tests in 0.093s
OK
```
Les cas couverts vérifient :
- le comptage conjoint d'une table ordinaire et d'une table fenced Markdown ;
- le rejet d'une table mal alignée dans une fence `markdown` ;
- l'audit de l'alias `md` ;
- le maintien hors scope des fences non Markdown.
La suite Python globale a exécuté `19` tests, tous réussis :
```text
Ran 19 tests in 0.311s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (134 file(s))
Markdown table audit: clean (6 table(s), 134 file(s))
Markdown link audit: clean (127 local link(s), 134 file(s))
```
Le contrôle ciblé du template affiche désormais :
```text
Markdown table audit: clean (2 table(s), 1 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.3.fix.1 fenced Markdown table audit: clean (regression covered, two template examples governed)
```
Il confirme notamment que l'audit reconnaît uniquement les fences explicites `markdown` ou `md`, que les deux exemples du template sont présents et que la matrice de validation documente l'exclusion volontaire des sorties terminal/non-Markdown.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.3.fix.1.md
M docs/templates/004-FILE_FAMILY_CONTRACTS.md
M docs/validation/001-VALIDATION_MATRIX.md
M scripts/audit_markdown_tables.py
A tests/test_markdown_tables.py
```
## Résultat
Le défaut de `alpha.3` est corrigé sans élargir l'audit à des blocs de code qui ne sont pas explicitement du Markdown. `PGR-MD-TABLE-001` couvre maintenant les tables normales ainsi que les exemples fenced Markdown, tout en continuant d'ignorer les fences d'autres langages.
Aucun nouveau fix de `alpha.3` n'est requis.
Après la gate, une revue distincte du plan et des historiques de conception PGR a révélé un besoin de gouvernance plus large : le contrat des plans n'assigne pas encore explicitement les responsabilités de clôture/transition et PGR ne définit pas encore de revue d'horizon de roadmap. Ce besoin ne remet pas en cause le présent fix ; il justifie un REPLAN dans la tranche normale suivante.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.3.fix.1`.
La tranche suivante devient :
```text
0.0.5-alpha.4
```
avec rôle `REPLAN`. Elle doit traiter uniquement le lifecycle inter-release, le contrat de clôture/transition des plans, la revue d'horizon et les optimisations workflow directement liées. Les anciens travaux prévus à partir de `alpha.4` sont décalés sans être abandonnés.

114
history/0.0.5/alpha.3.md Normal file
View File

@@ -0,0 +1,114 @@
<!-- file: history/0.0.5/alpha.3.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.3 — pattern facultatif de contrats de fichiers ou familles
## Statut
FAIL
La tranche `0.0.5-alpha.3` n'est pas enregistrée `PASS` telle quelle.
Le contenu documentaire prévu est présent et les commandes automatisées de la gate ont toutes retourné un succès, mais la revue opérateur a identifié un défaut objectif dans `docs/templates/004-FILE_FAMILY_CONTRACTS.md` : la table d'exemple commençant à la ligne 46 n'était pas alignée alors que `scripts/audit_markdown_tables.py` annonçait un état `clean`.
Le diagnostic montre que l'audit ignorait tous les blocs fenced. Les tables placées dans des fences explicitement `markdown` n'étaient donc ni comptées ni validées. La preuve `PGR-MD-TABLE-001` était incomplète pour cette tranche.
## Baseline
La tranche précédente `0.0.5-alpha.2` a été committée et poussée sous le commit observé :
```text
d7282dc
```
Version contrôlée après application de `alpha.3` :
```text
0.0.5-alpha.3
```
Le scope Git observé avant le présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.3.md
M docs/000-README.md
M docs/rules/300-DOCUMENTATION.md
M docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
A docs/templates/004-FILE_FAMILY_CONTRACTS.md
```
## Contrôles automatisés observés
Les `15` tests Python ont tous réussi :
```text
Ran 15 tests in 0.219s
OK
```
Les audits Markdown ont annoncé :
```text
Markdown header audit: clean (132 file(s))
Markdown table audit: clean (4 table(s), 132 file(s))
Markdown link audit: clean (127 local link(s), 132 file(s))
```
L'audit structurel spécifique a également réussi :
```text
0.0.5 alpha.3 file-family contract audit: clean (optional registry, short reusable pattern, no project-specific contract imported)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
## Défaut exact
`docs/templates/004-FILE_FAMILY_CONTRACTS.md` contient des exemples de tables sous la forme :
````text
```markdown
| ... |
| ... |
```
````
La fonction `_audit_file` de `scripts/audit_markdown_tables.py` entrait dans un état `fence` dès l'ouverture du bloc puis ignorait toutes les lignes jusqu'à sa fermeture. Une table Markdown d'exemple pouvait donc être mal alignée sans être vue par l'audit.
La table signalée par l'opérateur à partir de la ligne 46 présente effectivement des largeurs brutes différentes entre header, séparateur et ligne de données. Le diagnostic du fix révèle en outre qu'une seconde table fenced du même template, dans la section `Exemple abstrait`, était masquée par le même défaut.
Ce comportement est hérité des audits observés dans les corpus KSP/Games, mais PGR doit corriger son propre contrat au lieu de conserver ce faux négatif.
## Décision
Aucun REPLAN n'est requis.
Le pattern documentaire de `alpha.3` reste pertinent. Le défaut est local à la qualité de son exemple et à la couverture de `PGR-MD-TABLE-001`.
La correction reste dans :
```text
0.0.5-alpha.3.fix.1
```
Le fix doit :
- auditer les tables Markdown ordinaires ;
- auditer aussi les tables présentes dans des fences explicitement `markdown` ou `md` ;
- continuer d'ignorer les autres fences afin d'éviter de traiter comme Markdown des sorties terminal ou tableaux ASCII d'un autre langage ;
- ajouter des tests de régression positifs et négatifs ;
- réaligner toutes les tables fenced du template `004` révélées par ce contrôle ;
- préciser ce contrat dans la matrice de validation.
Le delta `alpha.3` reste un snapshot historique et n'est pas réécrit.
## Résultat
`0.0.5-alpha.3` est enregistré `FAIL`.
La transition autorisée est :
```text
0.0.5-alpha.3.fix.1
```

117
history/0.0.5/alpha.4.md Normal file
View File

@@ -0,0 +1,117 @@
<!-- file: history/0.0.5/alpha.4.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.4 — REPLAN du lifecycle inter-release et de l'horizon
## Statut
PASS
La gate opérateur de `0.0.5-alpha.4` exécutée le `2026-09-24` valide le REPLAN consacré à la clôture/transition des releases et à la revue d'horizon. Les tests, audits Markdown, contrôle structurel ciblé et contrôles Git sont propres ; le scope indexé correspond exactement au delta.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.3.fix.1` créé après ajout de son history `PASS`. La sortie Git fournie identifie ce commit sous la forme abrégée :
```text
a954d7f 0.0.5-alpha.3.fix.1
```
Le push vers `origin/master` a réussi avant l'application de `alpha.4`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.4
```
`history/0.0.5/alpha.3.fix.1.md`, `deltas/0.0.5/alpha.4.md` et `docs/templates/005-RELEASE_PLAN.md` sont présents. `history/0.0.5/alpha.4.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-24` depuis `~/Projects/pgr`, à `23:40:08`–`23:47:42`.
Les contrôles de présence, statut et révision ont réussi :
```text
history/0.0.5/alpha.3.fix.1.md PASS
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 2
docs/rules/200-GOVERNANCE.md version 3
docs/rules/210-WORKFLOW.md version 5
docs/rules/300-DOCUMENTATION.md version 4
ROADMAP.md version 6
docs/templates/003-NEW_SESSION_PROMPT.md version 2
docs/templates/005-RELEASE_PLAN.md version 1
```
La suite Python globale a exécuté `19` tests, tous réussis :
```text
Ran 19 tests in 0.338s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (137 file(s))
Markdown table audit: clean (6 table(s), 137 file(s))
Markdown link audit: clean (130 local link(s), 137 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.4 lifecycle audit: clean (closure assigned, horizon review explicit, deterministic handoff optimized)
```
Il confirme notamment :
- la présence du REPLAN `alpha.4` dans le plan actif ;
- l'attribution explicite des responsabilités de clôture et transition ;
- l'existence d'une revue d'horizon sans réservation artificielle de versions futures ;
- le passage proportionné `idea/study → décision durable → roadmap/plan` ;
- la possibilité de livrer history courant et delta suivant dans une même réponse tout en conservant deux archives et l'ordre commit/apply ;
- le message manuel spontané après publication et la mention d'une preuve externe post-tag lorsqu'elle est requise ;
- le pattern réutilisable `docs/templates/005-RELEASE_PLAN.md`.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M ROADMAP.md
M VERSION
A deltas/0.0.5/alpha.4.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/200-GOVERNANCE.md
M docs/rules/210-WORKFLOW.md
M docs/rules/300-DOCUMENTATION.md
M docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md
M docs/templates/003-NEW_SESSION_PROMPT.md
A docs/templates/005-RELEASE_PLAN.md
```
La transcription du terminal intercale quelques fragments du heredoc de contrôle avec la sortie de `unittest`. Les dix-neuf tests individuels, le résumé `OK`, les trois audits Markdown, le contrôle structurel final et le scope Git restent identifiables et ne signalent aucun échec.
## Résultat
PGR possède désormais un contrat explicite de fermeture/transition et de revue d'horizon, sans imposer un nombre arbitraire de versions futures ni transformer une session stratégique en changement de version.
Le forecast `0.0.5` replanifié est confirmé : l'audit Rust de façade/export completeness devient `alpha.5`, le profil Tauri `alpha.6` et la décision de skeleton Tauri `alpha.7`.
Aucun fix de `alpha.4` n'est requis.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.4`.
La tranche suivante est :
```text
0.0.5-alpha.5
```
Elle doit rester limitée à l'extraction d'un audit Rust réutilisable de façade/export completeness, à ses tests, sa documentation d'adoption et la réconciliation des limites réellement observées dans KSP/Games. Elle ne commence ni le profil Tauri ni un audit Rust général composite.

View File

@@ -0,0 +1,110 @@
<!-- file: history/0.0.5/alpha.5.fix.1.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.5.fix.1 — canonicalisation réelle des références crate-root
## Statut
PASS
La gate opérateur de `0.0.5-alpha.5.fix.1` exécutée le `2026-09-25` valide la correction du contrat Rust de façade : un item partagé exposé au crate-root doit être référencé via `crate::Item` dans toute la crate, y compris depuis son module de déclaration. Les tests ciblés/globaux, audits Markdown, contrôle structurel et contrôles Git sont propres ; le scope indexé correspond exactement au fix.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.5` :
```text
f03f864 0.0.5-alpha.5
```
Le push vers `origin/master` a réussi avant l'application de `alpha.5.fix.1`.
La version contrôlée après application du fix est :
```text
0.0.5-alpha.5.fix.1
```
`history/0.0.5/alpha.5.md` est présent avec statut `FAIL`, `deltas/0.0.5/alpha.5.fix.1.md` est présent et `history/0.0.5/alpha.5.fix.1.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `00:36:32`–`00:37:30`.
Les contrôles de révision ont confirmé :
```text
scripts/audit_rust_export_completeness.py version 2
tests/test_rust_export_completeness.py version 2
docs/validation/003-RUST_EXPORT_COMPLETENESS.md version 2
docs/rules/510-RUST_CARGO.md version 4
```
La suite ciblée a exécuté `18` tests, tous réussis :
```text
Ran 18 tests in 0.161s
OK
```
La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.514s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (142 file(s))
Markdown table audit: clean (6 table(s), 142 file(s))
Markdown link audit: clean (137 local link(s), 142 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.5.fix.1 canonical Rust path audit: clean (owner module, cross-module, attached tests, distinct Cargo targets)
```
Il confirme notamment que l'audit possède les cibles Cargo distinctes, suit les modules associés et couvre `RUST-API-201` ainsi que `RUST-IMPORT-201` pour les références intra-crate non canoniques. La documentation normative impose `crate::Item` y compris depuis le module propriétaire.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.5.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/validation/003-RUST_EXPORT_COMPLETENESS.md
M scripts/audit_rust_export_completeness.py
M tests/test_rust_export_completeness.py
```
La transcription du terminal intercale quelques fragments du heredoc de contrôle avec la sortie de `unittest`. Les dix-huit tests ciblés, les trente-sept tests globaux, les trois audits Markdown, le contrôle structurel final et le scope Git restent identifiables et ne signalent aucun échec.
## Résultat
Le blind spot fonctionnel de `alpha.5` est fermé. L'audit PGR distingue les targets Cargo et impose la façade crate-root sur les références intra-crate détectables sans prétendre remplacer un parseur Rust.
Les mesures KSP/Games consignées par le fix restent des inventaires d'adoption future en `--report-only`, pas des non-conformités rétroactives.
Aucun nouveau fix n'est requis.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.5.fix.1`.
La tranche suivante est :
```text
0.0.5-alpha.6
```
Elle doit rester limitée à l'activation de la famille `700 — frameworks` et au premier profil Tauri réellement commun aux baselines KSP/Games. Elle ne doit pas créer prématurément un skeleton Tauri ni imposer Vite, WASM, npm, des ports, fenêtres, plugins ou layouts applicatifs particuliers.

105
history/0.0.5/alpha.5.md Normal file
View File

@@ -0,0 +1,105 @@
<!-- file: history/0.0.5/alpha.5.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.5 — audit Rust de façade/export completeness
## Statut
FAIL
La gate mécanique de `0.0.5-alpha.5` exécutée le `2026-09-25` est propre, mais la revue fonctionnelle qui suit révèle que le contrat livré est incomplet par rapport à la convention Rust que PGR doit pouvoir porter : l'audit détecte `crate::module::Item`, mais ne détecte pas les références non qualifiées ou autrement non canoniques à un item partagé déjà exposé au crate-root, notamment dans son propre module de déclaration.
Le statut global reste donc `FAIL`. Les résultats mécaniques ne sont pas annulés ; ils prouvent que l'implémentation livrée fonctionne selon son contrat écrit, tandis que la revue révèle que ce contrat est trop faible.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.4` :
```text
defc9ca 0.0.5-alpha.4
```
Le push vers `origin/master` a réussi avant l'application de `alpha.5`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.5
```
`history/0.0.5/alpha.4.md`, `deltas/0.0.5/alpha.5.md`, `scripts/audit_rust_export_completeness.py`, `tests/test_rust_export_completeness.py` et `docs/validation/003-RUST_EXPORT_COMPLETENESS.md` sont présents. `history/0.0.5/alpha.5.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `00:03:49`–`00:08:36`.
Les contrôles de présence, statut et révision ont réussi. La suite ciblée a exécuté `9` tests, tous réussis :
```text
Ran 9 tests in 0.161s
OK
```
La suite Python globale a exécuté `28` tests, tous réussis :
```text
Ran 28 tests in 0.468s
OK
```
Les audits documentaires affichent :
```text
Markdown header audit: clean (140 file(s))
Markdown table audit: clean (6 table(s), 140 file(s))
Markdown link audit: clean (137 local link(s), 140 file(s))
```
Le contrôle structurel de la tranche affiche :
```text
0.0.5 alpha.5 Rust façade audit: clean (recursive crate discovery, narrow ownership, regression coverage)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic. Le scope Git observé est exactement celui du delta `alpha.5`.
## Défaut fonctionnel découvert après la gate
La convention recherchée est plus stricte que le contrat implémenté : lorsqu'un item `pub` ou `pub(crate)` est partagé et exposé par la façade crate-root, ses références intra-crate doivent utiliser le chemin canonique `crate::Item`, y compris depuis le module propriétaire.
Le script `version: 1` détecte seulement le sous-cas :
```text
crate::module::Item
```
lorsque `crate::Item` existe déjà. Il laisse notamment passer :
```text
Item
self::Item
super::Item
module::Item
```
ainsi que les noms nus dans des signatures, `impl`, constructeurs ou appels situés dans le module de déclaration. Le compilateur et Clippy ne possèdent pas cette convention de style/architecture : une seule consommation correcte par `crate::Item` peut suffire à rendre le réexport utilisé sans révéler les autres accès non canoniques.
Ce manque est dans le scope exact de `alpha.5`; il doit donc être corrigé par `alpha.5.fix.1`, sans commencer Tauri.
## Résultat
`0.0.5-alpha.5` est enregistré `FAIL` malgré une gate mécanique verte, car l'acceptation fonctionnelle de l'audit est incomplète.
## Transition
Appliquer le présent history puis committer/pousser `0.0.5-alpha.5`.
La correction suivante est :
```text
0.0.5-alpha.5.fix.1
```
Elle doit étendre l'audit de façade pour imposer réellement la forme canonique intra-crate `crate::Item` aux items partagés, ajouter les régressions correspondantes et corriger la documentation/mesure de réconciliation. Aucun scope Tauri ne doit être absorbé.

View File

@@ -0,0 +1,102 @@
<!-- file: history/0.0.5/alpha.6.fix.1.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.6.fix.1 — composition Tauri/Rust/frontend
## Statut
FAIL
La gate opérateur de `0.0.5-alpha.6.fix.1` exécutée le `2026-09-25` valide la suite Python globale, les trois audits Markdown et le scope Git attendu, mais le contrôle structurel ciblé du fix échoue encore avant ses assertions : une phrase contenant l'apostrophe de `l'extérieur` est placée dans une chaîne Python délimitée elle-même par des quotes simples, ce qui produit `SyntaxError: unterminated string literal`.
La revue opérateur associée apporte en outre une clarification plus complète du modèle Tauri/Rust attendu. Les règles réellement versionnées dans KSP et Games montrent que plusieurs conventions avaient été sous-estimées : layout Tauri direct sans `src-tauri/`, `frontend/` explicite, `lib.rs` façade, `main.rs` launcher mince, `tauri.rs` centralisant l'assemblage et les wrappers `#[tauri::command]`, séparation du WASM et, dans KSP, contrats frontend TS-RS générés depuis les DTO Rust.
Le statut global est donc `FAIL`. Le fix doit être corrigé sans absorber encore la réconciliation Rust générale ni la future matérialisation d'un skeleton Tauri complet.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.6` :
```text
0f6a2e2 0.0.5-alpha.6
```
Le push vers `origin/master` a réussi avant l'application de `alpha.6.fix.1`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.6.fix.1
```
`history/0.0.5/alpha.6.md` est présent avec statut `FAIL`; `deltas/0.0.5/alpha.6.fix.1.md` est présent et `history/0.0.5/alpha.6.fix.1.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `09:16:02`–`10:08:50`.
Les contrôles de présence, statut et révision ont réussi. La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.504s
OK
```
Les audits documentaires affichent :
```text
Markdown header audit: clean (148 file(s))
Markdown table audit: clean (7 table(s), 148 file(s))
Markdown link audit: clean (148 local link(s), 148 file(s))
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.1.md
M docs/architecture/003-RULE_PROFILE_MODEL.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/700-FRAMEWORKS.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Échec du contrôle ciblé
Le heredoc contient notamment une chaîne Python simple avec le texte `l'extérieur`. L'apostrophe ferme prématurément la chaîne et produit :
```text
SyntaxError: unterminated string literal (detected at line 10)
```
Le message de succès attendu du contrôle ciblé n'a donc pas été produit. Une gate obligatoire du fix reste en échec.
## Clarification issue de la revue KSP/Games
La relecture des règles et layouts réels confirme :
- les `6` applications Desk KSP et les `2` applications Tauri Games possèdent toutes directement à la racine de leur crate `Cargo.toml`, `build.rs`, `package.json`, `tsconfig.json` et `tauri.conf.json`, ainsi que `src/` et `frontend/`; aucune n'utilise `src-tauri/` ;
- KSP formalise `lib.rs` comme façade, `main.rs` comme launcher mince et `tauri.rs` comme owner des wrappers `#[tauri::command]`; Games reprend la même séparation `lib.rs`/`tauri.rs` ;
- KSP impose pour ses apps Desk les bindings TS-RS générés depuis les DTO Rust comme source de vérité frontend ; Games n'a pas encore ce contrat, ce qui en fait un candidat fort pour la convention stricte des projets contrôlés plutôt qu'un invariant universel de Tauri ;
- Games formalise explicitement que WASM reste une crate distincte de la crate Tauri ;
- les règles de noms de crates et la discipline Rust stricte sont plus larges que Tauri et doivent être traitées dans les tranches Rust suivantes.
## Résultat
`0.0.5-alpha.6.fix.1` est enregistré `FAIL`.
## Transition
Appliquer le présent history puis committer/pousser `0.0.5-alpha.6.fix.1`.
La correction suivante est :
```text
0.0.5-alpha.6.fix.2
```
Elle doit rendre la gate ciblée robuste et compléter le profil Tauri avec les conventions fortement éprouvées qui appartiennent réellement à ce scope, tout en replanifiant séparément les conventions Rust/nomenclature et les audits de style restants.

View File

@@ -0,0 +1,110 @@
<!-- file: history/0.0.5/alpha.6.fix.2.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.6.fix.2 — convention Tauri contrôlée et gate robuste
## Statut
FAIL
La gate opérateur de `0.0.5-alpha.6.fix.2` exécutée le `2026-09-25` est mécaniquement propre : les `37` tests Python réussissent, les trois audits Markdown passent, le contrôle ciblé du profil Tauri s'exécute sans erreur et le scope Git correspond exactement au delta.
La revue fonctionnelle immédiatement postérieure révèle toutefois que la politique de version Tauri/frontend reste incorrectement généralisée. Le profil conserve une préférence pour l'omission de `tauri.conf.json > version` afin d'hériter de Cargo et traite trop facilement `package.json > version` comme une donnée non synchronisée. L'objectif réel n'est pas de supprimer ces champs, mais d'éviter leur modification à chaque `alpha`, `.fix`, beta ou RC lorsqu'ils ne portent qu'un jalon produit `X.Y.Z`.
Le statut global est donc `FAIL` malgré une gate mécanique verte. Le correctif suivant doit rétablir une classification explicite des valeurs de version et corriger uniquement cette politique sans rouvrir les autres décisions Tauri déjà stabilisées.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.6.fix.1` :
```text
33bf835 0.0.5-alpha.6.fix.1
```
Le push vers `origin/master` a réussi avant l'application de `alpha.6.fix.2`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.6.fix.2
```
`history/0.0.5/alpha.6.fix.1.md` est présent avec statut `FAIL`; `deltas/0.0.5/alpha.6.fix.2.md` est présent et `history/0.0.5/alpha.6.fix.2.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `10:19:33`–`10:22:30`.
Les contrôles de présence, statut et révision ont réussi. La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.518s
OK
```
Les audits documentaires affichent :
```text
Markdown header audit: clean (150 file(s))
Markdown table audit: clean (7 table(s), 150 file(s))
Markdown link audit: clean (148 local link(s), 150 file(s))
```
Le contrôle ciblé affiche :
```text
0.0.5 alpha.6.fix.2 Tauri convention audit: clean (8 hosts reconciled, strict controlled layout separated from universal framework)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.2.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Défaut fonctionnel découvert après la gate
La relecture des archives autoritaires montre que la convention historique existe dans **les deux corpus** :
- KSP `VER-ID-015/016` fait de Cargo la source de vérité du bundle, impose l'absence de `tauri.conf.json > version` et ne synchronise pas `package.json` avec chaque version workspace ;
- Games `VER-TAURI-001..004` reprend la version Cargo comme source produit, omet `tauri.conf.json > version` et ne met `package.json > version` à jour qu'à certains jalons.
Cette convergence historique explique la règle matérialisée dans PGR, mais ne suffit pas à la rendre correcte comme politique générale. Elle confond deux objectifs différents :
```text
réduire le churn des prereleases
≠
supprimer les champs de version utiles
```
Le contrat souhaité est plutôt :
- `VERSION` porte l'identité exacte de la tranche active ;
- les miroirs techniques qui ont besoin de cette granularité peuvent suivre exactement `VERSION` ;
- `tauri.conf.json > version` et `package.json > version`, lorsqu'ils sont utiles au même produit, peuvent porter le jalon stable cible `X.Y.Z` et rester inchangés pendant les prereleases/fixes ;
- les prereleases réellement distribuées peuvent adopter une politique plus fine lorsque leur packaging l'exige.
Cette distinction correspond à la conception PGR antérieure qui classait déjà les versions selon leur rôle plutôt que d'exiger une égalité globale.
## Résultat
`0.0.5-alpha.6.fix.2` est enregistré `FAIL` malgré une gate mécanique verte, car la politique de version Tauri/frontend reste fonctionnellement incorrecte.
## Transition
Appliquer le présent history puis committer/pousser `0.0.5-alpha.6.fix.2`.
La correction suivante est :
```text
0.0.5-alpha.6.fix.3
```
Comme ce troisième fix atteint le seuil de revue obligatoire, le plan doit être réexaminé. Le correctif doit rester limité à la classification/synchronisation des versions et confirmer explicitement si `alpha.6` peut ensuite être clôturée sans absorber les tranches Rust ou skeleton suivantes.

View File

@@ -0,0 +1,100 @@
<!-- file: history/0.0.5/alpha.6.fix.3.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.6.fix.3 — politique de versions Tauri/frontend réconciliée
## Statut
PASS
La gate opérateur de `0.0.5-alpha.6.fix.3` exécutée le `2026-09-25` valide la correction de la politique de versions produit : `VERSION` reste l'identité exacte de la tranche, tandis que les autres valeurs sont classifiées selon leur rôle (`MIRROR-EXACT`, `MIRROR-STABLE` ou version indépendante) au lieu d'être supprimées ou synchronisées mécaniquement. Les tests, audits Markdown, contrôle structurel ciblé et contrôles Git sont propres ; le scope indexé correspond exactement au delta.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.6.fix.2` :
```text
b7e65d9 0.0.5-alpha.6.fix.2
```
Le push vers `origin/master` a réussi avant l'application de `alpha.6.fix.3`.
La version contrôlée après application du correctif est :
```text
0.0.5-alpha.6.fix.3
```
`history/0.0.5/alpha.6.fix.2.md` est présent avec statut `FAIL`, `deltas/0.0.5/alpha.6.fix.3.md` est présent et `history/0.0.5/alpha.6.fix.3.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `10:35:30`–`10:37:41`.
Les contrôles de révision ont confirmé :
```text
docs/rules/200-GOVERNANCE.md version 4
docs/rules/710-TAURI.md version 4
docs/studies/001-KSP_GAMES_RECONCILIATION.md version 7
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 8
```
La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.487s
OK
```
Les trois audits documentaires affichent :
```text
Markdown header audit: clean (152 file(s))
Markdown table audit: clean (7 table(s), 152 file(s))
Markdown link audit: clean (149 local link(s), 152 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.6.fix.3 version policy audit: clean (canonical/exact/stable mirrors classified, Tauri/frontend churn bounded)
```
Il confirme notamment que seul un `MIRROR-EXACT` suit normalement chaque changement de `VERSION`, qu'un `MIRROR-STABLE` peut rester sur la cible `X.Y.Z`, et que PGR n'impose plus la suppression d'un champ de version utile uniquement pour éviter le churn des prereleases.
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.6.fix.3.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/200-GOVERNANCE.md
M docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
La transcription du terminal intercale quelques fragments du heredoc de contrôle avec la sortie de `unittest`. Les trente-sept tests, les audits Markdown, le contrôle structurel final et le scope Git restent identifiables et ne signalent aucun échec.
## Résultat
Le troisième correctif ferme la divergence de politique de version identifiée pendant `alpha.6`. La revue obligatoire liée à `.fix.3` conserve le résultat `CONTINUE` : aucun nouveau découpage de `alpha.6` n'est nécessaire.
Le profil Tauri peut maintenant être considéré comme suffisamment borné pour reprendre la trajectoire normale. Les règles Rust strictes, le formatage structurel et la décision de skeleton Tauri restent volontairement séparés dans les tranches suivantes.
Aucun nouveau fix `alpha.6` n'est requis.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.6.fix.3`.
La tranche suivante est :
```text
0.0.5-alpha.7
```
Elle doit rester limitée au profil Rust strict, aux chemins/imports/réexports et à une nomenclature générique explicite des crates. Le formatage structurel et ses audits restent réservés à `alpha.8`; le skeleton Tauri reste réservé à `alpha.9`.

133
history/0.0.5/alpha.6.md Normal file
View File

@@ -0,0 +1,133 @@
<!-- file: history/0.0.5/alpha.6.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.6 — premier profil framework Tauri 2
## Statut
FAIL
La gate opérateur de `0.0.5-alpha.6` exécutée le `2026-09-25` valide la présence du profil Tauri, la suite Python globale, les trois audits Markdown et le scope Git, mais le contrôle structurel ciblé fourni par le delta ne s'exécute pas : son heredoc contient une chaîne multilignes entre quotes simples et Python termine avec `SyntaxError: unterminated string literal`.
La revue fonctionnelle immédiatement associée apporte en plus une clarification de scope nécessaire : pour les projets Tauri que l'organisation contrôle, le host doit hériter intégralement des règles Rust sélectionnées tandis que le frontend et ses choix de stack appartiennent à un owner/skeleton distinct. Le profil livré ne sépare pas encore assez explicitement cette convention d'instanciation du contrat Tauri universel.
Le statut global est donc `FAIL`. Les preuves mécaniques réussies restent valides, mais la gate complète n'est pas passée et le contrat doit être précisé avant de continuer.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.5.fix.1` :
```text
81c7a72 0.0.5-alpha.5.fix.1
```
Le push vers `origin/master` a réussi avant l'application de `alpha.6`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.6
```
`history/0.0.5/alpha.5.fix.1.md`, `deltas/0.0.5/alpha.6.md`, `docs/rules/700-FRAMEWORKS.md` et `docs/rules/710-TAURI.md` sont présents. `history/0.0.5/alpha.6.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `08:15:06`–`08:42:54`.
Les contrôles de présence, statut et révision ont réussi :
```text
history/0.0.5/alpha.5.fix.1.md PASS
RULES.md version 11
docs/rules/000-README.md version 13
docs/architecture/003-RULE_PROFILE_MODEL.md version 4
docs/rules/700-FRAMEWORKS.md version 1
docs/rules/710-TAURI.md version 1
```
La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.505s
OK
```
Les audits documentaires affichent :
```text
Markdown header audit: clean (146 file(s))
Markdown table audit: clean (7 table(s), 146 file(s))
Markdown link audit: clean (148 local link(s), 146 file(s))
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé est exactement :
```text
M RULES.md
M VERSION
A deltas/0.0.5/alpha.6.md
M docs/000-README.md
M docs/architecture/003-RULE_PROFILE_MODEL.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/000-README.md
A docs/rules/700-FRAMEWORKS.md
A docs/rules/710-TAURI.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
```
## Échec du contrôle ciblé
Le heredoc de validation contient cette valeur sous forme de chaîne Python délimitée par des quotes simples sur plusieurs lignes :
```text
VERSION
↓
workspace.package.version
↓
package Cargo du host
↓
version applicative Tauri
```
Python l'interprète comme une chaîne non terminée et produit :
```text
SyntaxError: unterminated string literal (detected at line 21)
```
Le message de succès `0.0.5 alpha.6 Tauri profile audit: clean (...)` n'a donc pas été produit. Une gate obligatoire du delta n'est pas passée.
## Clarification fonctionnelle
La revue opérateur précise le modèle attendu pour les projets contrôlés par l'organisation :
- une application Tauri est d'abord un host Rust/Cargo et doit hériter des mêmes conventions Rust que les autres crates/applications du projet ;
- les exemples générés par Tauri ne justifient pas d'exceptions implicites aux règles Rust locales ;
- le frontend est un scope distinct ; pour les projets contrôlés, le layout `frontend/`, Vite, TypeScript, Sass et Bootstrap constituent des préférences de skeleton, pas des invariants universels de Tauri ;
- `cargo tauri add` n'est pas la voie obligatoire d'intégration des plugins : l'ajout explicite des dépendances Cargo/frontend et du code d'initialisation à partir de la documentation du plugin est une pratique volontaire ;
- WASM doit normalement rester une unité séparée et n'hérite pas des règles Tauri simplement parce qu'il partage éventuellement le même dépôt ;
- le POC Tauri mobile Games non concluant ne permet pas de conclure que Tauri Android/iOS est inadapté à d'autres projets.
Cette clarification révèle en parallèle un écart plus ancien du profil Rust PGR : plusieurs conventions strictes déjà retenues dans la conception initiale ne sont pas encore toutes matérialisées dans `510-RUST_CARGO.md`. Ce sujet dépasse le fix Tauri et doit devenir une tranche normale distincte.
## Résultat
`0.0.5-alpha.6` est enregistré `FAIL`.
Le profil framework/Tauri est une base utile et les preuves mécaniques générales sont propres, mais le gate ciblé doit être corrigé et la frontière entre profil Tauri universel, héritage Rust et conventions de skeleton doit être explicitée.
## Transition
Appliquer le présent history puis committer/pousser `0.0.5-alpha.6`.
La correction suivante est :
```text
0.0.5-alpha.6.fix.1
```
Elle doit corriger le heredoc de validation et la composition Tauri/Rust/frontend sans modifier encore `510-RUST_CARGO.md` ni créer de skeleton. Le plan doit ensuite réserver une tranche normale à la complétude du profil Rust strict avant la décision de skeleton Tauri.

View File

@@ -0,0 +1,102 @@
<!-- file: history/0.0.5/alpha.7.fix.1.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.7.fix.1 — contrats de crates, packaging et tracing
## Statut
PASS
La gate opérateur de `0.0.5-alpha.7.fix.1` exécutée le `2026-09-25` valide la correction fonctionnelle de `alpha.7` : les suffixes de crates portent désormais un contrat architectural explicite, les bibliothèques et exécutables sont séparés par défaut, l'ownership du logging/tracing est borné et Tauri reste l'exception framework documentée pour un package mixte `lib + bin`.
Les tests, audits Markdown, contrôle structurel ciblé et contrôles Git sont propres. Aucun nouveau correctif `alpha.7` n'est requis.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.7` :
```text
ac03b78 0.0.5-alpha.7
```
Le push vers `origin/master` a réussi avant l'application de `alpha.7.fix.1`.
La version contrôlée après application du correctif est :
```text
0.0.5-alpha.7.fix.1
```
`history/0.0.5/alpha.7.md` est présent avec statut `FAIL`, `deltas/0.0.5/alpha.7.fix.1.md` est présent et `history/0.0.5/alpha.7.fix.1.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `15:07:31`–`15:08:03`.
Les contrôles de révision ont confirmé :
```text
docs/rules/510-RUST_CARGO.md version 6
docs/rules/710-TAURI.md version 5
docs/skeletons/002-RUST_CARGO_WORKSPACE.md version 4
docs/templates/002-CARGO_MANIFESTS.md version 3
docs/studies/001-KSP_GAMES_RECONCILIATION.md version 9
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 10
```
La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.480s
OK
```
Les audits documentaires sont propres :
```text
Markdown header audit: clean (156 file(s))
Markdown table audit: clean (7 table(s), 156 file(s))
Markdown link audit: clean (152 local link(s), 156 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.7.fix.1 crate contract audit: clean (api/lib semantics, binary split, tracing owner, Tauri exception)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.7.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/rules/710-TAURI.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/templates/002-CARGO_MANIFESTS.md
```
## Résultat
Le profil Rust strict possède maintenant une nomenclature qui décrit réellement les frontières d'architecture : `-api` n'est pas créé par symétrie, `-lib` désigne une unité réutilisable, les launchers sont normalement des packages distincts, et les diagnostics structurés ont un owner transverse lorsque plusieurs runtimes partagent leur configuration.
L'ancienne préférence KSP pour certains packages worker mixtes n'est pas généralisée. Tauri reste l'exception framework actuelle, avec des noms de targets `lib`/`bin` réellement distincts.
Les règles de forme Rust restent volontairement hors de ce correctif et peuvent maintenant être traitées dans `alpha.8` sans rouvrir les contrats de crates.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.7.fix.1`.
La tranche suivante est :
```text
0.0.5-alpha.8
```
Elle doit réconcilier le formatage Rust structurel KSP/Games, attribuer chaque propriété à `rustfmt`, aux audits PGR déjà existants ou à un audit monotâche justifié, sans importer l'ancien audit composite et sans commencer le skeleton Tauri réservé à `alpha.9`.

108
history/0.0.5/alpha.7.md Normal file
View File

@@ -0,0 +1,108 @@
<!-- file: history/0.0.5/alpha.7.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.7 — profil Rust strict et nomenclature initiale
## Statut
FAIL
La gate mécanique de `0.0.5-alpha.7` exécutée le `2026-09-25` est propre : tests, audits Markdown, contrôle structurel ciblé et scope Git passent. La revue fonctionnelle opérateur qui suit révèle toutefois que la nomenclature matérialisée reste incomplète : les suffixes de crates ne portent pas encore explicitement leurs contrats architecturaux, la séparation package bibliothèque / package binaire n'est pas cadrée, et l'ownership du logging/tracing n'est pas généralisé alors que KSP et Games fournissent des preuves suffisamment convergentes.
La tranche est donc enregistrée `FAIL` malgré son gate mécanique vert. La correction appartient à `alpha.7.fix.1` ; aucun REPLAN de release n'est nécessaire.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.6.fix.3` :
```text
4e1c1cd 0.0.5-alpha.6.fix.3
```
Le push vers `origin/master` a réussi avant l'application de `alpha.7`.
La version contrôlée après application est :
```text
0.0.5-alpha.7
```
`history/0.0.5/alpha.6.fix.3.md` est présent avec statut `PASS`, `deltas/0.0.5/alpha.7.md` est présent et `history/0.0.5/alpha.7.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `10:47:34`–`11:15:53`.
Les révisions contrôlées sont :
```text
docs/rules/510-RUST_CARGO.md version 5
docs/skeletons/002-RUST_CARGO_WORKSPACE.md version 3
docs/templates/002-CARGO_MANIFESTS.md version 2
docs/studies/001-KSP_GAMES_RECONCILIATION.md version 8
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 9
```
La suite Python globale a exécuté `37` tests, tous réussis :
```text
Ran 37 tests in 0.492s
OK
```
Les audits documentaires sont propres :
```text
Markdown header audit: clean (154 file(s))
Markdown table audit: clean (7 table(s), 154 file(s))
Markdown link audit: clean (150 local link(s), 154 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.7 strict Rust profile audit: clean (explicit paths, crate-root facade, controlled naming, alpha.8 formatting deferred)
```
`git diff --check` et `git diff --cached --check` ne produisent aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.7.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/templates/002-CARGO_MANIFESTS.md
```
## Écart fonctionnel découvert après gate
La clarification opérateur impose un sens architectural aux noms de crates :
- une crate `-api` doit être un contrat réellement séparé, principalement passif ; elle n'est pas créée si une seule implémentation suffit ;
- une crate `-lib` porte une implémentation/fonctionnalité réutilisable ;
- les exécutables doivent normalement vivre dans des packages séparés et composer les libs au lieu de mélanger binaire et bibliothèque ;
- Tauri constitue l'exception actuelle et doit alors déclarer des targets lib/bin de noms distincts ;
- `tracing` est préféré à `log` pour les diagnostics structurés et un owner transverse de logging doit éviter la duplication de subscriber/sinks/lifecycle dans les workspaces multi-runtime.
La relecture des corpus confirme ce besoin. KSP encode déjà la séparation `-api` / `-lib`, interdit les APIs créées par symétrie et centralise le tracing dans `ksp-logging-lib`. Games sépare ses launchers de ses libs et centralise également la configuration tracing. Une ancienne préférence KSP pour un package worker mixte lib/bin est explicitement rejetée comme règle PGR générale au profit de la clarification actuelle.
## Résultat
`alpha.7` a correctement rétabli le profil Rust strict sur les chemins, imports, façade et contrôle de flux, mais son contrat de nomenclature/packaging reste incomplet. La correction est bornée et n'affecte pas la trajectoire `alpha.8` formatage puis `alpha.9` skeleton Tauri.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.7`.
La tranche suivante est :
```text
0.0.5-alpha.7.fix.1
```
Elle complète uniquement les contrats de suffixes, la séparation lib/bin, l'ownership du tracing et l'exception Tauri. Les règles de blocs/espacement/ordre restent hors scope jusqu'à `alpha.8`.

125
history/0.0.5/alpha.8.md Normal file
View File

@@ -0,0 +1,125 @@
<!-- file: history/0.0.5/alpha.8.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.8 — formatage Rust structurel et audits monotâches
## Statut
PASS
La gate opérateur de `0.0.5-alpha.8` exécutée le `2026-09-25` valide la fermeture du chantier de formatage Rust structurel : les quatre audits monotâches ajoutés sont couverts par leurs tests, la suite globale PGR est propre, les audits Markdown passent et le contrôle structurel confirme la séparation des owners ainsi que la conservation des ordres sémantiques hors automatisation.
Aucun correctif `alpha.8` n'est requis.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.7.fix.1` :
```text
ecd9de4 0.0.5-alpha.7.fix.1
```
Le push vers `origin/master` a réussi avant l'application de `alpha.8`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.8
```
`history/0.0.5/alpha.7.fix.1.md` est présent avec statut `PASS`, `deltas/0.0.5/alpha.8.md` est présent et `history/0.0.5/alpha.8.md` était absent pendant la gate, conformément au workflow.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `17:22:08`–`17:24:18`.
Les contrôles de révision ont confirmé :
```text
docs/rules/510-RUST_CARGO.md version 7
docs/skeletons/002-RUST_CARGO_WORKSPACE.md version 5
docs/studies/001-KSP_GAMES_RECONCILIATION.md version 10
docs/validation/001-VALIDATION_MATRIX.md version 12
docs/validation/002-RUST_STYLE_AUDITS.md version 2
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 11
tests/test_rust_audits.py version 2
```
Les quatre nouveaux audits sont présents en version `1` :
```text
scripts/audit_rust_item_spacing.py
scripts/audit_rust_module_order.py
scripts/audit_rust_reexport_blocks.py
scripts/audit_rust_trait_import_blocks.py
```
La suite ciblée Rust a exécuté `20` tests, tous réussis :
```text
Ran 20 tests in 0.287s
OK
```
La suite Python globale a exécuté `48` tests, tous réussis :
```text
Ran 48 tests in 0.710s
OK
```
Les audits documentaires sont propres :
```text
Markdown header audit: clean (158 file(s))
Markdown table audit: clean (7 table(s), 158 file(s))
Markdown link audit: clean (152 local link(s), 158 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.8 Rust structure audit: clean (owners split, four monotask audits, semantic order protected, horizon candidates recorded)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.8.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/rules/510-RUST_CARGO.md
M docs/skeletons/002-RUST_CARGO_WORKSPACE.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
M docs/validation/001-VALIDATION_MATRIX.md
M docs/validation/002-RUST_STYLE_AUDITS.md
A scripts/audit_rust_item_spacing.py
A scripts/audit_rust_module_order.py
A scripts/audit_rust_reexport_blocks.py
A scripts/audit_rust_trait_import_blocks.py
M tests/test_rust_audits.py
```
## Résultat
Le profil Rust strict possède maintenant des owners mécaniques séparés pour les blancs, blocs de constantes sélectionnés, espacement des items, ordre des modules, blocs de traits, réexports et façade crate-root. Aucun `audit_rust_general_rules.py` composite n'est réintroduit.
Les séquences métier ou protocolaires restent protégées contre un tri automatique injustifié. `audit_rust_const_blocks.py` demeure explicitement opt-in lorsque l'ordre des constantes peut être sémantique.
Les trois candidats d'horizon discutés pendant la tranche sont conservés sans numéro de release ni engagement : maturation des profils techniques composables, projets documentaires/spécifications et généricité de PGR hors développement logiciel.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.8`.
La tranche suivante est :
```text
0.0.5-alpha.9
```
Elle doit décider si un skeleton Tauri pour projets contrôlés peut être matérialisé sans faire du skeleton un propriétaire implicite des choix Web encore dépourvus de profils PGR. Si les owners Web restent insuffisants, la décision doit être explicitement différée plutôt que représentée par un skeleton décoratif.

100
history/0.0.5/alpha.9.md Normal file
View File

@@ -0,0 +1,100 @@
<!-- file: history/0.0.5/alpha.9.md -->
<!-- version: 1 -->
# History 0.0.5-alpha.9 — décision de skeleton Tauri contrôlé
## Statut
PASS
La gate opérateur de `0.0.5-alpha.9` exécutée le `2026-09-25` valide la décision de readiness du skeleton Tauri : le host Rust/Tauri est suffisamment cadré, mais les owners Web/Vite/TypeScript/Sass nécessaires à une baseline frontend complète ne sont pas encore matérialisés. La décision `DEFER` est donc acceptée et aucun skeleton Tauri `003` n'est créé dans cette tranche.
Les tests, audits Markdown, contrôle structurel ciblé et contrôles Git sont propres. Aucun correctif `alpha.9` n'est requis.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.8` :
```text
c331da5 0.0.5-alpha.8
```
Le push vers `origin/master` a réussi avant l'application de `alpha.9`.
La version contrôlée après application du delta est :
```text
0.0.5-alpha.9
```
`history/0.0.5/alpha.8.md` est présent avec statut `PASS`, `deltas/0.0.5/alpha.9.md` et `docs/studies/002-TAURI_SKELETON_DECISION.md` sont présents, aucun skeleton `003-TAURI...` n'existe et `history/0.0.5/alpha.9.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `17:41:25`–`17:44:38`.
Les contrôles de révision ont confirmé :
```text
docs/000-README.md version 15
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 12
docs/skeletons/000-README.md version 4
docs/studies/000-README.md version 2
docs/studies/002-TAURI_SKELETON_DECISION.md version 1
```
La suite Python globale a exécuté `48` tests, tous réussis :
```text
Ran 48 tests in 0.705s
OK
```
Les audits documentaires sont propres :
```text
Markdown header audit: clean (161 file(s))
Markdown table audit: clean (7 table(s), 161 file(s))
Markdown link audit: clean (156 local link(s), 161 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 alpha.9 Tauri skeleton decision: clean (host ready, Web owners missing, decorative skeleton rejected, exit conditions recorded)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M VERSION
A deltas/0.0.5/alpha.9.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/skeletons/000-README.md
M docs/studies/000-README.md
A docs/studies/002-TAURI_SKELETON_DECISION.md
```
## Résultat
Le profil Tauri n'est pas remis en cause : les responsabilités host, Rust, capabilities, plugins, versions, TS-RS et validation d'orchestration restent possédées par les owners existants.
Le skeleton Tauri contrôlé est explicitement différé jusqu'à matérialisation d'owners Web suffisants. Le candidat reste identifié sans devenir un skeleton décoratif ni réserver une release future.
Cette décision constitue désormais un élément durable à reprendre dans la réconciliation `beta.1` et dans la trajectoire macro appropriée, sans transformer les autres candidats d'horizon en engagements de roadmap.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-alpha.9`.
La tranche suivante est :
```text
0.0.5-beta.1
```
Elle doit rejouer la réconciliation intégrée, reporter durablement le `DEFER` Tauri dans la trajectoire sans lui attribuer une version, vérifier les owners/navigation/skeletons/audits et préparer le prompt `006` vers `0.1.0` si la mission reste suffisamment connue.

98
history/0.0.5/beta.1.md Normal file
View File

@@ -0,0 +1,98 @@
<!-- file: history/0.0.5/beta.1.md -->
<!-- version: 1 -->
# History 0.0.5-beta.1 — réconciliation intégrée
## Statut
PASS
La gate opérateur de `0.0.5-beta.1` exécutée le `2026-09-25` valide la réconciliation intégrée de la release : les owners restent bornés, le skeleton Tauri contrôlé est durablement enregistré comme trajectoire `DEFER` sans numéro de release, les trois axes plus larges restent des candidats d'horizon non engagés et le prompt `006` vers `0.1.0` est préparé sans transformer ces reports en obligations.
Les tests, audits Markdown, contrôle d'intégration ciblé et contrôles Git sont propres. Aucun correctif `beta.1` n'est requis.
## Baseline et version
La tranche part du commit opérateur de `0.0.5-alpha.9` poussé sur `origin/master` :
```text
5a83968 0.0.5-alpha.9
```
La version contrôlée après application du delta est :
```text
0.0.5-beta.1
```
`history/0.0.5/alpha.9.md` est présent avec statut `PASS`, `deltas/0.0.5/beta.1.md` et `prompts/006-V0_1_0_START_PROMPT.md` sont présents, et `history/0.0.5/beta.1.md` était absent pendant la gate.
## Validation opérateur observée
L'opérateur a fourni les sorties du `2026-09-25` depuis `~/Projects/pgr`, à `18:00:37`–`18:01:27`.
Les contrôles de révision ont confirmé :
```text
README.md version 13
ROADMAP.md version 7
docs/000-README.md version 16
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 13
docs/studies/001-KSP_GAMES_RECONCILIATION.md version 11
prompts/006-V0_1_0_START_PROMPT.md version 1
```
La suite Python globale a exécuté `48` tests, tous réussis :
```text
Ran 48 tests in 0.678s
OK
```
Les audits documentaires sont propres :
```text
Markdown header audit: clean (164 file(s))
Markdown table audit: clean (8 table(s), 164 file(s))
Markdown link audit: clean (161 local link(s), 164 file(s))
```
Le contrôle structurel spécifique affiche :
```text
0.0.5 beta.1 integrated reconciliation: clean (owners reconciled, Tauri defer roadmapped, horizon candidates remain uncommitted, prompt 006 prepared)
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le scope Git observé avant ajout du présent history est exactement :
```text
M README.md
M ROADMAP.md
M VERSION
A deltas/0.0.5/beta.1.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
M docs/studies/001-KSP_GAMES_RECONCILIATION.md
A prompts/006-V0_1_0_START_PROMPT.md
```
## Résultat
La phase beta confirme que `0.0.5` possède un contrat de fermeture exécutable. Le `DEFER` du skeleton Tauri est visible dans la roadmap sans fausse réservation de version ; les profils techniques supplémentaires, le corpus documentaire/spécification et l'audit hors logiciel restent des candidats d'horizon jusqu'à décision explicite.
Le prompt `006` est prêt comme artefact de reprise candidat vers `0.1.0`, sous réserve de la publication stable réelle de `v0.0.5` et de son contrôle post-tag.
## Transition
Appliquer le présent history puis committer et pousser `0.0.5-beta.1`.
La tranche suivante est :
```text
0.0.5-rc.1
```
Elle gèle le scope fonctionnel, prépare l'entrée candidate `0.0.5` du changelog, vérifie le `ROADMAP` et le prompt `006`, puis rejoue la gate de release candidate sans introduire de nouveau besoin commun.

70
history/0.0.5/rc.1.md Normal file
View File

@@ -0,0 +1,70 @@
<!-- file: history/0.0.5/rc.1.md -->
<!-- version: 1 -->
# History 0.0.5-rc.1 — gate de release candidate
## Statut
PASS
La gate opérateur de `0.0.5-rc.1` exécutée le `2026-09-25` valide le scope gelé de la release candidate, le changelog candidat, la roadmap déjà réconciliée et le prompt de reprise `006`. Cette preuve porte sur la RC avant son commit ; elle ne constitue ni la promotion stable, ni le tag `v0.0.5`, ni la vérification post-tag.
## Baseline et scope observés
La baseline opérateur est le commit `c97ca5f` de `0.0.5-beta.1`, poussé sur `master` avant application du delta RC. L'opérateur a confirmé `VERSION = 0.0.5-rc.1`, la présence dans `HEAD` de `history/0.0.5/beta.1.md`, la présence du delta `rc.1` et du prompt `006`, ainsi que l'absence du présent history avant sa livraison.
Les marqueurs attendus ont été retrouvés :
```text
CHANGELOG.md version 8
CHANGELOG 0.0.5 (candidate)
ROADMAP.md version 7
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 14
prompts/006-V0_1_0_START_PROMPT.md version 1
beta.1 PASS consigné dans le plan
skeleton Tauri contrôlé (d) / DEFER
```
`ROADMAP.md` conserve `0.0.5` en cours jusqu'à la promotion stable, `0.1.0` comme prochaine release engagée et le skeleton Tauri contrôlé comme trajectoire différée sans numéro de release. Le prompt `006` conserve ce `DEFER` comme condition à réexaminer, sans en faire une obligation de `0.1.0`.
## Tests et audits documentaires
La suite Python complète a réussi :
```text
Ran 48 tests in 0.725s
OK
```
Les trois audits Markdown ont affiché :
```text
Markdown header audit: clean (166 file(s))
Markdown table audit: clean (8 table(s), 166 file(s))
Markdown link audit: clean (161 local link(s), 166 file(s))
```
L'audit d'intégration RC a affiché :
```text
0.0.5 rc.1 integration audit: clean (16 prior histories, frozen scope, candidate changelog, roadmap/prompt 006 verified)
```
Il confirme les `16` histories antérieurs avec leurs statuts attendus, l'absence de history `rc.1` avant clôture, la présence des audits Rust réconciliés, le profil Tauri 2, l'absence de skeleton Tauri `003`, le changelog candidat et la préparation du prompt `006`.
## Scope Git
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic. Après ajout des fichiers RC, le statut indexé transmis est exactement :
```text
M CHANGELOG.md
M VERSION
A deltas/0.0.5/rc.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
La revue confirme qu'aucun owner, skeleton, template, script d'audit ou domaine technique nouveau n'entre dans la RC. `ROADMAP.md` et `prompts/006-V0_1_0_START_PROMPT.md` sont vérifiés mais non modifiés.
## Transition
Appliquer ce history puis committer/pousser `0.0.5-rc.1`. La tranche suivante est `rel.001`, promotion stable pré-tag : elle peut uniquement synchroniser la présentation stable et fermer le plan, sans rouvrir le scope fonctionnel.

View File

@@ -0,0 +1,88 @@
<!-- file: history/0.0.5/rel.001.fix.1.md -->
<!-- version: 1 -->
# History 0.0.5 rel.001.fix.1 — correction de la gate stable pré-tag
## Statut
PASS
La gate opérateur de `rel.001.fix.1` exécutée le `2026-09-25` est propre. Le correctif conserve `VERSION = 0.0.5`, préserve le `FAIL` historique de `rel.001`, exécute correctement la preuve Python de cohérence stable et ne modifie aucun fichier fonctionnel de la release.
La branche est donc prête pour le commit de cette clôture corrigée puis, uniquement après ce commit/push, pour la création du tag canonique `v0.0.5` et la vérification externe `publish.001`.
## Baseline et état observé
La baseline opérateur est le commit `4366802` de `0.0.5-rel.001`, poussé sur `master` avec `history/0.0.5/rel.001.md` en `FAIL`.
L'opérateur a confirmé :
```text
VERSION 0.0.5
history/0.0.5/rel.001.md présent dans HEAD
deltas/0.0.5/rel.001.fix.1.md présent
history/0.0.5/rel.001.fix.1.md absent avant clôture
history/0.0.5/rel.001.md FAIL
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 16
README.md version 14
ROADMAP.md version 8
CHANGELOG.md version 9
CHANGELOG heading stable ## 0.0.5
ROADMAP 0.0.5 terminé
prompt 006 skeleton Tauri toujours DEFER
```
## Preuve stable corrigée
Le contrôle Python inline corrigé s'est exécuté jusqu'à son terme et a produit :
```text
0.0.5 rel.001.fix.1 stable history audit: clean (18 prior histories, failed rel preserved, corrected pre-tag proof)
```
Il confirme notamment :
- les `18` histories antérieurs attendus existent avec leurs statuts réels ;
- `rel.001.md` reste enregistré `FAIL` ;
- seul `rel.001.fix.1.md` reste en attente de history ;
- `ROADMAP.md` conserve `0.0.5` terminé, `0.1.0` ouvert et le skeleton Tauri en `DEFER` ;
- `CHANGELOG.md` expose exactement le heading stable `## 0.0.5` et ne contient plus le marqueur candidate ;
- le prompt `006` référence l'archive taggée `pgr-v0.0.5.zip`, le rapport externe `pgr-0.0.5-publish.001-report.md`, la première tranche `0.1.0-alpha.1` et le `DEFER` Tauri ;
- le plan conserve le diagnostic `FAIL` de `rel.001` et décrit `rel.001.fix.1` comme correction exclusive de la preuve de clôture.
## Audits documentaires
Les audits Markdown ont produit :
```text
Markdown header audit: clean (170 file(s))
Markdown table audit: clean (8 table(s), 170 file(s))
Markdown link audit: clean (161 local link(s), 170 file(s))
```
## Contrôles Git
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic.
Le statut indexé transmis avant ajout du history est exactement :
```text
A deltas/0.0.5/rel.001.fix.1.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
Le correctif ne modifie donc ni `VERSION`, ni `README.md`, ni `ROADMAP.md`, ni `CHANGELOG.md`, ni règles, scripts, tests, templates ou skeletons.
## Conclusion pré-tag
`rel.001.fix.1` corrige exclusivement le défaut de quoting de la preuve `rel.001` sans rouvrir le scope stable. La promotion documentaire `0.0.5` reste inchangée et la preuve de clôture requise est désormais valide.
Aucun tag n'a été créé pendant cette gate.
## Transition
Appliquer ce history puis committer/pousser la tranche avec le message canonique `0.0.5-rel.001.fix.1`.
Après confirmation que `master` contient ce history et que l'arbre est propre, créer le tag canonique `v0.0.5` sur le commit de clôture, pousser ce tag, télécharger l'archive source taggée depuis Gitea puis exécuter la vérification externe `publish.001`.
Le rapport `pgr-0.0.5-publish.001-report.md` reste extérieur au tag stable et doit refléter les preuves réellement observées après publication.

85
history/0.0.5/rel.001.md Normal file
View File

@@ -0,0 +1,85 @@
<!-- file: history/0.0.5/rel.001.md -->
<!-- version: 1 -->
# History 0.0.5 rel.001 — promotion stable pré-tag
## Statut
FAIL
La gate opérateur de `rel.001` exécutée le `2026-09-25` n'a pas atteint un PASS complet. La promotion documentaire vers `VERSION = 0.0.5`, les audits Markdown et les contrôles Git observés sont propres, mais l'audit Python inline de cohérence historique s'est interrompu avec un `SyntaxError` avant de pouvoir produire sa preuve attendue.
Ce history constate uniquement la validation pré-tag réellement exécutée. Aucun tag `v0.0.5`, aucune archive source taggée et aucun rapport `publish.001` ne doivent être produits tant que le correctif de clôture n'a pas obtenu un PASS.
## Baseline et état observé
La baseline opérateur est le commit `390dd63` de `0.0.5-rc.1`, poussé sur `master` avant application de `rel.001`.
L'opérateur a confirmé :
```text
VERSION 0.0.5
history/0.0.5/rc.1.md présent dans HEAD
deltas/0.0.5/rel.001.md présent
history/0.0.5/rel.001.md absent avant clôture
README.md version 14
ROADMAP.md version 8
CHANGELOG.md version 9
docs/000-README.md version 17
docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md version 15
ROADMAP 0.0.5 terminé
CHANGELOG section stable 0.0.5
prompt 006 skeleton Tauri toujours DEFER
```
Les contrôles textuels de présentation stable ont tous réussi avant l'audit Python inline.
## Échec de la preuve ciblée
Le bloc Python inline de `rel.001` contenait l'assertion générée suivante :
```python
assert '## 0.0.5
' in changelog
```
La chaîne simple est coupée physiquement sur deux lignes dans le heredoc. Python a donc interrompu le contrôle avec :
```text
File "<stdin>", line 42
assert '## 0.0.5
^
SyntaxError: unterminated string literal (detected at line 42)
```
La preuve `0.0.5 stable history audit: clean (...)` n'a par conséquent jamais été produite. Conformément au contrat de validation PGR, une preuve requise qui échoue rend la tranche `FAIL`, même lorsque les contrôles exécutés ensuite sont propres.
## Audits documentaires et Git observés
Après cet échec, les audits Markdown ont affiché :
```text
Markdown header audit: clean (168 file(s))
Markdown table audit: clean (8 table(s), 168 file(s))
Markdown link audit: clean (161 local link(s), 168 file(s))
```
`git diff --check` et `git diff --cached --check` n'ont produit aucun diagnostic. Le statut indexé transmis est exactement :
```text
M CHANGELOG.md
M README.md
M ROADMAP.md
M VERSION
A deltas/0.0.5/rel.001.md
M docs/000-README.md
M docs/plans/005-V0_0_5_RECONCILIATION_PLAN.md
```
Aucun défaut fonctionnel de la stable n'est établi par cette gate. Le défaut constaté appartient à la commande de validation livrée par `rel.001`.
## Transition
Appliquer ce history puis committer/pousser la tranche `rel.001` avec son statut `FAIL`. Ne pas créer le tag `v0.0.5`.
Corriger ensuite uniquement la clôture dans `rel.001.fix.1`. Le correctif MUST conserver `VERSION = 0.0.5`, MUST NOT réécrire silencieusement `deltas/0.0.5/rel.001.md` et MUST rejouer une preuve historique syntaxiquement valide avant toute publication.

View File

@@ -0,0 +1,69 @@
<!-- file: prompts/006-V0_1_0_START_PROMPT.md -->
<!-- version: 1 -->
# Prompt de démarrage 0.1.0 — première baseline réutilisable
## Baseline et cible
Dépôt canonique : `https://git.sasedev.com/Sasedev/pgr`.
La baseline attendue au démarrage effectif de `0.1.0` est l'archive source téléchargée depuis le tag Gitea `v0.0.5`, dont le nom attendu est `pgr-v0.0.5.zip`. Une archive source peut légitimement ne pas contenir `.git` ; vérifier son contenu et ses artefacts de gouvernance sans lui attribuer une preuve Git qu'elle ne peut contenir.
Version cible : `0.1.0`. Première tranche attendue : `0.1.0-alpha.1`.
Si un rapport séparé `pgr-0.0.5-publish.001-report.md` a effectivement été produit et conclut `PASS`, le fournir avec l'archive. S'il est absent ou non validé, le signaler explicitement ; ne jamais présumer la publication ou un résultat PASS. Ce rapport ne fait pas partie du tag et ne remplace pas l'audit de la nouvelle session.
## Reprise manuelle après publication
Après vérification réelle de l'archive du tag et du rapport post-tag PASS, remettre spontanément à l'opérateur un message de reprise adapté selon [`../docs/templates/003-NEW_SESSION_PROMPT.md`](../docs/templates/003-NEW_SESSION_PROMPT.md). Si les noms attendus sont confirmés et le rapport est PASS, la base du texte est :
```text
Partir de l'archive pgr-v0.0.5.zip téléchargée depuis le tag Gitea v0.0.5 et du rapport post-tag pgr-0.0.5-publish.001-report.md. Lire et exécuter prompts/006-V0_1_0_START_PROMPT.md. Vérifier intégralement la stable PGR, ses règles, profils, templates, skeletons, audits, décisions différées et chaîne de gouvernance avant de préparer 0.1.0-alpha.1.
```
Si le rapport manque ou n'est pas PASS, retirer sa mention et transmettre son état réel séparément. Ce texte ne doit être remis qu'après clôture stable et vérification post-tag de `0.0.5`, jamais comme attestation anticipée au stade RC.
## Mission
Transformer les fondations `0.0.x` en première baseline PGR réellement réutilisable pour initialiser de nouveaux projets sans importer des choix locaux non applicables.
`alpha.1` MUST commencer par auditer indépendamment la stable `v0.0.5` : gouvernance, owners normatifs, templates, skeleton specifications, audits, documentation, roadmap, histories/deltas et rapport post-tag lorsqu'il est fourni. Elle doit ensuite produire ou consolider le plan `0.1.0` avant toute extension importante.
Le plan doit notamment déterminer :
- quelles règles, profils, templates et skeletons sont suffisamment cohérents pour constituer la baseline réutilisable ;
- quelles validations doivent être considérées comme gates minimales ou conditionnelles d'une nouvelle instanciation ;
- quelles décisions différées doivent seulement être conservées, réétudiées ou réellement engagées ;
- où aura lieu la revue d'horizon obligatoire avant fermeture de `0.1.0`.
## Décisions héritées à respecter
- Le skeleton Tauri contrôlé reste `DEFER` tant que les owners Web suffisants ne sont pas matérialisés. `0.1.0` MAY réexaminer cette trajectoire si son plan crée effectivement ces owners, mais MUST NOT créer le skeleton seulement pour fermer le report.
- Les profils Web/TypeScript/Vite/Sass, observability et autres domaines techniques ne sont pas présumés nécessaires : ils exigent un besoin réutilisable, un corpus réel et un owner clair avant normalisation.
- Les projets documentaires/spécifications, notamment `saselang-bible` comme corpus pilote possible, et l'usage de PGR hors logiciel restent des candidats d'horizon tant qu'une décision macro ne les engage pas.
- Aucune release postérieure à `0.1.0` ne doit être inventée pour remplir la roadmap.
## Principes de baseline
- Un owner normatif unique par responsabilité ; les skeletons sélectionnent/composent les owners et ne remplacent pas ceux qui manquent.
- Une règle n'existe que si elle apporte une valeur réelle de sûreté, cohérence, lisibilité, reprise ou automatisation.
- Compiler, outil officiel, manifeste/build system et audits read-only sont préférés aux tests textuels redondants.
- Les audits restent monotâches et leurs limites explicites ; un contrôle lexical ne prétend pas remplacer un parser ou le compilateur.
- Les profils stricts peuvent être le défaut des nouveaux projets contrôlés tout en permettant des overrides explicites lorsqu'un projet externe ou une contrainte technique impose autre chose.
- Les versions, décisions et documents restent classifiés par responsabilité ; `VERSION` ne force pas la réécriture de tous les miroirs techniques ou produit.
## Revue d'horizon obligatoire
Le plan `0.1.0` MUST programmer une revue d'horizon avant la clôture stable. Cette revue doit décider, à partir de la baseline réellement obtenue, s'il faut :
- étendre `ROADMAP.md` avec des objectifs suffisamment décidés ;
- conserver explicitement l'horizon ouvert avec un prochain déclencheur ;
- requalifier une trajectoire différée telle que le skeleton Tauri ;
- différer/mettre en pause d'autres axes ;
- ou constater qu'un objectif macro est accompli.
Elle ne doit pas réserver des numéros de version uniquement pour donner l'impression d'une trajectoire longue.
## Workflow
Suivre `RULES.md`, [`../docs/rules/210-WORKFLOW.md`](../docs/rules/210-WORKFLOW.md) et [`../docs/rules/400-VALIDATION.md`](../docs/rules/400-VALIDATION.md). La séquence reste delta → gate opérateur → history constaté → application du history → commit → delta suivant. Les corrections utilisent `.fix.N`; une session ou une discussion stratégique ne crée pas de version à elle seule.

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# file: scripts/audit_markdown_tables.py
# version: 1
# version: 2
"""Validate source formatting of Markdown tables in explicitly selected paths."""
@@ -13,6 +13,7 @@ import sys
_SEPARATOR = re.compile(r"^:?-{3,}:?$")
_IGNORED_DIRECTORIES = frozenset({".git", ".idea", ".venv", "__pycache__", "dist", "node_modules", "target"})
_MARKDOWN_FENCE_LANGUAGES = frozenset({"markdown", "md"})
def _selected_files(raw_paths: list[str]) -> tuple[list[pathlib.Path], list[str]]:
@@ -130,11 +131,19 @@ def _validate_table(path: pathlib.Path, start_line: int, rows: list[str]) -> lis
return errors
def _fence_language(stripped: str) -> str | None:
info = stripped[3:].strip()
if not info:
return None
return info.split(None, 1)[0].lower()
def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
lines = path.read_text(encoding="utf-8").splitlines()
errors: list[str] = []
tables = 0
fence: str | None = None
audit_fenced_markdown = False
index = 0
while index < len(lines):
@@ -143,12 +152,14 @@ def _audit_file(path: pathlib.Path) -> tuple[int, list[str]]:
marker = stripped[:3]
if fence is None:
fence = marker
audit_fenced_markdown = _fence_language(stripped) in _MARKDOWN_FENCE_LANGUAGES
elif fence == marker:
fence = None
audit_fenced_markdown = False
index += 1
continue
if fence is not None:
if fence is not None and not audit_fenced_markdown:
index += 1
continue

View File

@@ -0,0 +1,510 @@
#!/usr/bin/env python3
# file: scripts/audit_rust_export_completeness.py
# version: 2
"""Audit Rust crate-root façade completeness and canonical same-crate references."""
from __future__ import annotations
import argparse
import dataclasses
import pathlib
import re
import sys
import tomllib
from _rust_audit_common import line_depths, mask_source
_IGNORED_DIRECTORY_NAMES = frozenset({
".git",
".venv",
"__pycache__",
"build",
"builds",
"dist",
"node_modules",
"target",
})
@dataclasses.dataclass(frozen=True)
class Candidate:
"""One façade or canonical-reference violation candidate."""
code: str
path: str
line: int
message: str
@dataclasses.dataclass(frozen=True)
class Declaration:
"""One module-level public or crate-public declaration."""
module: str
name: str
visibility: str
kind: str
path: pathlib.Path
line: int
column: int
@dataclasses.dataclass(frozen=True)
class Target:
"""One Rust crate target and the module files reachable from its root."""
package: pathlib.Path
root: pathlib.Path
modules: tuple[tuple[pathlib.Path, str], ...]
@dataclasses.dataclass(frozen=True)
class CanonicalItem:
"""One visible item with its canonical crate-root name."""
declaration: Declaration
canonical_name: str
def _is_ignored(path: pathlib.Path, root: pathlib.Path) -> bool:
"""Return whether a path belongs to a generated or local-only tree."""
try:
relative = path.relative_to(root)
except ValueError:
return True
return bool(set(relative.parts) & _IGNORED_DIRECTORY_NAMES)
def crate_roots(root: pathlib.Path) -> list[pathlib.Path]:
"""Discover Rust package roots recursively below one project root."""
crates: set[pathlib.Path] = set()
for manifest in root.rglob("Cargo.toml"):
if _is_ignored(manifest, root):
continue
crate = manifest.parent
if (crate / "src/lib.rs").is_file() or (crate / "src/main.rs").is_file() or _explicit_target_paths(crate):
crates.add(crate)
return sorted(crates)
def _explicit_target_paths(crate: pathlib.Path) -> tuple[pathlib.Path, ...]:
"""Return existing explicit lib/bin target paths declared in Cargo.toml."""
manifest = crate / "Cargo.toml"
if not manifest.is_file():
return ()
try:
data = tomllib.loads(manifest.read_text(encoding="utf-8"))
except (OSError, tomllib.TOMLDecodeError):
return ()
paths: set[pathlib.Path] = set()
lib = data.get("lib")
if isinstance(lib, dict) and isinstance(lib.get("path"), str):
candidate = crate / lib["path"]
if candidate.is_file():
paths.add(candidate)
bins = data.get("bin", [])
if isinstance(bins, list):
for entry in bins:
if isinstance(entry, dict) and isinstance(entry.get("path"), str):
candidate = crate / entry["path"]
if candidate.is_file():
paths.add(candidate)
return tuple(sorted(paths))
def target_roots(crate: pathlib.Path) -> list[pathlib.Path]:
"""Return standard and explicitly configured Rust crate target roots."""
roots: set[pathlib.Path] = set(_explicit_target_paths(crate))
manifest = crate / "Cargo.toml"
package: dict[str, object] = {}
if manifest.is_file():
try:
parsed = tomllib.loads(manifest.read_text(encoding="utf-8"))
raw_package = parsed.get("package", {})
if isinstance(raw_package, dict):
package = raw_package
except (OSError, tomllib.TOMLDecodeError):
package = {}
library = crate / "src/lib.rs"
if library.is_file():
roots.add(library)
if package.get("autobins", True) is not False:
main = crate / "src/main.rs"
if main.is_file():
roots.add(main)
bin_dir = crate / "src/bin"
if bin_dir.is_dir():
roots.update(path for path in bin_dir.glob("*.rs") if path.is_file())
roots.update(path for path in bin_dir.glob("*/main.rs") if path.is_file())
return sorted(roots)
def _standard_child_module_path(current: pathlib.Path, target_root: pathlib.Path, name: str) -> pathlib.Path | None:
"""Resolve a standard `mod name;` file relative to one source module."""
if current == target_root or current.name == "mod.rs":
base = current.parent
else:
base = current.parent / current.stem
direct = base / f"{name}.rs"
nested = base / name / "mod.rs"
if direct.is_file():
return direct
if nested.is_file():
return nested
return None
def _module_children(path: pathlib.Path, target_root: pathlib.Path) -> list[tuple[pathlib.Path, str]]:
"""Return top-level external module declarations from one Rust source file."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
masked_lines = masked.splitlines()
depths = line_depths(masked)
module_pattern = re.compile(r"^\s*(?:(?:pub|pub\([^)]*\))\s+)?mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;")
path_pattern = re.compile(r'^\s*#\[path\s*=\s*"([^"]+)"\]\s*$')
found: list[tuple[pathlib.Path, str]] = []
for index, masked_line in enumerate(masked_lines):
if index >= len(depths) or depths[index] != 0:
continue
match = module_pattern.match(masked_line)
if match is None:
continue
module_name = match.group(1)
explicit: pathlib.Path | None = None
probe = index - 1
while probe >= 0 and lines[probe].lstrip().startswith("#["):
path_match = path_pattern.match(lines[probe])
if path_match is not None:
candidate = (path.parent / path_match.group(1)).resolve()
if candidate.is_file():
explicit = candidate
break
probe -= 1
child = explicit or _standard_child_module_path(path, target_root, module_name)
if child is not None:
found.append((child.resolve(), module_name))
return found
def target_modules(crate: pathlib.Path, target_root: pathlib.Path) -> dict[pathlib.Path, str]:
"""Return module files reachable from one target root and their logical paths."""
root = target_root.resolve()
modules: dict[pathlib.Path, str] = {root: ""}
pending: list[pathlib.Path] = [root]
while pending:
current = pending.pop()
parent_module = modules[current]
for child, name in _module_children(current, root):
if _is_ignored(child, crate) or child in modules:
continue
module = f"{parent_module}::{name}" if parent_module else name
modules[child] = module
pending.append(child)
return modules
def targets(crate: pathlib.Path) -> list[Target]:
"""Return independently compiled crate targets for one Cargo package."""
found: list[Target] = []
for root in target_roots(crate):
modules = target_modules(crate, root)
found.append(Target(crate, root.resolve(), tuple(sorted(modules.items(), key=lambda item: item[0].as_posix()))))
return found
def declaration_candidates(path: pathlib.Path, module: str) -> list[Declaration]:
"""Return top-level public or crate-public declarations from one module file."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
depths = line_depths(masked)
pattern = re.compile(
r"^\s*(pub(?:\(crate\))?)\s+"
r"(?:(?:async|unsafe|const)\s+)*"
r"(const|static|type|struct|enum|trait|union|fn)\s+"
r"([A-Za-z_][A-Za-z0-9_]*)"
)
found: list[Declaration] = []
for line_number, line in enumerate(lines, 1):
if line_number - 1 >= len(depths) or depths[line_number - 1] != 0:
continue
match = pattern.match(line)
if match is None:
continue
found.append(
Declaration(
module=module,
name=match.group(3),
visibility=match.group(1),
kind=match.group(2),
path=path,
line=line_number,
column=match.start(3),
)
)
return found
def _parse_export_item(module: str, item: str) -> tuple[tuple[str, str], str] | None:
"""Parse one simple explicit item from a crate-root re-export."""
stripped = item.strip()
if not stripped or stripped == "*" or "{" in stripped or "}" in stripped:
return None
alias_match = re.fullmatch(
r"([A-Za-z_][A-Za-z0-9_]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?",
stripped,
)
if alias_match is None:
return None
name = alias_match.group(1)
alias = alias_match.group(2) or name
return (module, name), alias
def root_exports(root_source: pathlib.Path) -> dict[tuple[str, str], str]:
"""Return explicit crate-root re-exports keyed by source module and symbol."""
exports: dict[tuple[str, str], str] = {}
text = root_source.read_text(encoding="utf-8")
pattern = re.compile(
r"^\s*pub(?:\(crate\))?\s+use\s+(?:self|crate)::(.*?);",
re.MULTILINE | re.DOTALL,
)
for match in pattern.finditer(text):
body = " ".join(part.strip() for part in match.group(1).splitlines())
if "{" in body and body.endswith("}"):
prefix, group = body.split("{", 1)
module = prefix.rstrip().rstrip(":")
for item in group[:-1].split(","):
parsed = _parse_export_item(module, item)
if parsed is not None:
key, alias = parsed
exports[key] = alias
continue
if "::" not in body:
continue
module, item = body.rsplit("::", 1)
parsed = _parse_export_item(module, item)
if parsed is not None:
key, alias = parsed
exports[key] = alias
return exports
def _reexport_lines(root_source: pathlib.Path) -> set[int]:
"""Return crate-root line numbers occupied by explicit re-export statements."""
text = root_source.read_text(encoding="utf-8")
pattern = re.compile(
r"^\s*pub(?:\(crate\))?\s+use\s+(?:self|crate)::.*?;",
re.MULTILINE | re.DOTALL,
)
lines: set[int] = set()
for match in pattern.finditer(text):
start = text.count("\n", 0, match.start()) + 1
end = text.count("\n", 0, match.end()) + 1
lines.update(range(start, end + 1))
return lines
def _target_declarations(target: Target) -> list[Declaration]:
"""Return visible module-level declarations for one crate target."""
return [
declaration
for path, module in target.modules
for declaration in declaration_candidates(path, module)
]
def _external_usage(target: Target, declaration: Declaration) -> bool:
"""Return whether a crate-public item is clearly referenced from another module file."""
if not declaration.module:
return True
direct = f"crate::{declaration.module}::{declaration.name}"
root_direct = f"crate::{declaration.name}"
super_direct = f"super::{declaration.name}"
for path, _ in target.modules:
if path.resolve() == declaration.path.resolve():
continue
masked, _ = mask_source(path.read_text(encoding="utf-8"))
if direct in masked or root_direct in masked or super_direct in masked:
return True
return False
def _declaration_lookup(declarations: list[Declaration]) -> dict[tuple[pathlib.Path, int, str], Declaration]:
"""Index declaration-name occurrences that must not be treated as references."""
return {(item.path.resolve(), item.line, item.name): item for item in declarations}
def _canonical_reference_candidates(
workspace: pathlib.Path,
target: Target,
declarations: list[Declaration],
exports: dict[tuple[str, str], str],
) -> list[Candidate]:
"""Return non-canonical references to items that have a crate-root identity."""
items: list[CanonicalItem] = []
for declaration in declarations:
if declaration.path.resolve() == target.root and not declaration.module:
items.append(CanonicalItem(declaration, declaration.name))
continue
alias = exports.get((declaration.module, declaration.name))
if alias is not None:
items.append(CanonicalItem(declaration, alias))
by_token: dict[str, list[CanonicalItem]] = {}
for item in items:
by_token.setdefault(item.declaration.name, []).append(item)
if item.canonical_name != item.declaration.name:
by_token.setdefault(item.canonical_name, []).append(item)
if not by_token:
return []
token_pattern = re.compile(r"\b(?:" + "|".join(re.escape(name) for name in sorted(by_token, key=len, reverse=True)) + r")\b")
declaration_lookup = _declaration_lookup(declarations)
root_reexport_lines = _reexport_lines(target.root)
internal_roots = {segment for _, module in target.modules if module for segment in module.split("::")}
candidates: list[Candidate] = []
for path, _ in target.modules:
relative = path.relative_to(workspace).as_posix()
masked, _ = mask_source(path.read_text(encoding="utf-8"))
for line_number, line in enumerate(masked.splitlines(), 1):
for match in token_pattern.finditer(line):
token = match.group(0)
for item in by_token[token]:
declaration = item.declaration
if declaration.kind == "fn" and re.match(r"\s*(?:\(|::<)", line[match.end():]) is None:
continue
prefix = line[:match.start()]
if re.search(r"(?:const|static|type|struct|enum|trait|union|fn)\s+$", prefix) is not None:
continue
if (path.resolve(), line_number, token) in declaration_lookup and token == declaration.name:
continue
if path.resolve() == target.root and line_number in root_reexport_lines:
continue
if token == item.canonical_name and re.search(r"\bcrate::\s*$", prefix) is not None:
continue
if match.start() > 0 and line[match.start() - 1] == ".":
continue
qualified = re.search(
r"([A-Za-z_][A-Za-z0-9_]*(?:::[A-Za-z_][A-Za-z0-9_]*)*)::\s*$",
prefix,
)
if qualified is not None:
first = qualified.group(1).split("::", 1)[0]
if first not in internal_roots and first not in {"crate", "self", "super"}:
continue
candidates.append(
Candidate(
code="RUST-IMPORT-201",
path=relative,
line=line_number,
message=(
f"non-canonical same-crate reference `{token}` for shared "
f"{declaration.visibility} `{declaration.name}`; use `crate::{item.canonical_name}`"
),
)
)
return candidates
def audit_target(workspace: pathlib.Path, target: Target) -> list[Candidate]:
"""Audit one independently compiled Rust crate target."""
declarations = _target_declarations(target)
exports = root_exports(target.root)
candidates: list[Candidate] = []
for declaration in declarations:
if declaration.path.resolve() == target.root and not declaration.module:
continue
required = declaration.visibility == "pub" or _external_usage(target, declaration)
if required and (declaration.module, declaration.name) not in exports:
reason = "public item" if declaration.visibility == "pub" else "crate-public item used outside its module"
candidates.append(
Candidate(
code="RUST-API-201",
path=declaration.path.relative_to(workspace).as_posix(),
line=declaration.line,
message=f"{reason} `{declaration.name}` requires an explicit crate-root re-export",
)
)
candidates.extend(_canonical_reference_candidates(workspace, target, declarations, exports))
return candidates
def audit_crate(workspace: pathlib.Path, crate: pathlib.Path) -> list[Candidate]:
"""Audit every independently compiled target in one Cargo package."""
return [candidate for target in targets(crate) for candidate in audit_target(workspace, target)]
def audit_workspace(root: pathlib.Path) -> tuple[list[pathlib.Path], list[Candidate]]:
"""Return discovered packages and all façade candidates below one project root."""
crates = crate_roots(root)
candidates = list({candidate for crate in crates for candidate in audit_crate(root, crate)})
candidates.sort(key=lambda candidate: (candidate.code, candidate.path, candidate.line, candidate.message))
return crates, candidates
def main() -> int:
"""Run the export-completeness audit."""
parser = argparse.ArgumentParser()
parser.add_argument("--root", default=".")
parser.add_argument("--report-only", action="store_true")
parser.add_argument("--summary-only", action="store_true")
arguments = parser.parse_args()
workspace = pathlib.Path(arguments.root).resolve()
if not workspace.is_dir():
sys.stderr.write(f"Rust export completeness audit: invalid root: {workspace}\n")
return 2
crates, candidates = audit_workspace(workspace)
if not crates:
sys.stderr.write(f"Rust export completeness audit: no Rust package found below {workspace}\n")
return 2
counts: dict[str, int] = {}
for candidate in candidates:
counts[candidate.code] = counts.get(candidate.code, 0) + 1
sys.stdout.write(f"Rust export completeness audit: {len(candidates)} candidate(s) across {len(crates)} package(s)\n")
for code in sorted(counts):
sys.stdout.write(f"{code}: {counts[code]}\n")
if not arguments.summary_only:
for candidate in candidates:
sys.stdout.write(f"{candidate.code} {candidate.path}:{candidate.line}: {candidate.message}\n")
return 0 if arguments.report_only or not candidates else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,168 @@
#!/usr/bin/env python3
# file: scripts/audit_rust_item_spacing.py
# version: 1
"""Audit exact spacing between mechanically detectable Rust items."""
from __future__ import annotations
import argparse
import dataclasses
import pathlib
import re
import sys
from _rust_audit_common import line_depths, mask_source, selected_files
@dataclasses.dataclass(frozen=True)
class RustItem:
"""One mechanically detected Rust item and its source span."""
kind: str
visibility: str
start: int
declaration: int
end: int
depth: int
braced: bool
def _visibility(stripped: str) -> str:
if stripped.startswith("pub(crate) "):
return "pub(crate)"
if stripped.startswith("pub "):
return "pub"
return "private"
def _kind(stripped: str) -> str | None:
prefix = r"(?:(?:pub|pub\(crate\))\s+)?"
qualifiers = r"(?:(?:async|const|unsafe)\s+)*"
if re.match(rf"^{prefix}{qualifiers}fn\b", stripped):
return "fn"
for kind in ("struct", "enum", "union", "trait", "impl", "const", "static", "type"):
if re.match(rf"^{prefix}{qualifiers}{kind}\b", stripped):
return kind
return None
def _leading_line(lines: list[str], declaration: int) -> int:
cursor = declaration - 2
while cursor >= 0:
stripped = lines[cursor].strip()
if stripped.startswith("///") or stripped.startswith("#["):
cursor -= 1
continue
break
return cursor + 2
def _end_line(masked_lines: list[str], depths: list[int], declaration: int, kind: str) -> tuple[int, bool]:
start_depth = depths[declaration - 1]
if kind in {"const", "static", "type"}:
brackets: list[str] = []
closing = {"}": "{", ")": "(", "]": "["}
for line_index in range(declaration - 1, len(masked_lines)):
for char in masked_lines[line_index]:
if char in "{([":
brackets.append(char)
elif char in "})]" and brackets and brackets[-1] == closing[char]:
brackets.pop()
elif char == ";" and not brackets:
return line_index + 1, False
return declaration, False
body_started = False
for line_index in range(declaration - 1, len(masked_lines)):
masked_line = masked_lines[line_index]
if not body_started and ";" in masked_line and "{" not in masked_line:
return line_index + 1, False
if "{" in masked_line:
body_started = True
if body_started:
end_depth = depths[line_index] + masked_line.count("{") - masked_line.count("}")
if end_depth == start_depth:
return line_index + 1, True
return declaration, body_started
def _items(lines: list[str], masked_lines: list[str], depths: list[int]) -> list[RustItem]:
result: list[RustItem] = []
for number, line in enumerate(lines, 1):
stripped = line.strip()
kind = _kind(stripped)
if kind is None:
continue
end, braced = _end_line(masked_lines, depths, number, kind)
result.append(RustItem(kind, _visibility(stripped), _leading_line(lines, number), number, end, depths[number - 1], braced))
return result
def _parent(item: RustItem, items: list[RustItem]) -> RustItem | None:
parents = [candidate for candidate in items if candidate.braced and candidate.declaration < item.declaration <= candidate.end and candidate.depth < item.depth]
if not parents:
return None
return max(parents, key=lambda candidate: candidate.depth)
def audit_file(path: pathlib.Path) -> list[str]:
"""Require exact separation between adjacent items in one Rust scope."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
masked_lines = masked.splitlines()
depths = line_depths(masked)
items = _items(lines, masked_lines, depths)
siblings: dict[tuple[int, int] | None, list[RustItem]] = {}
for item in items:
parent = _parent(item, items)
key = None if parent is None else (parent.declaration, parent.end)
siblings.setdefault(key, []).append(item)
errors: list[str] = []
for group in siblings.values():
group.sort(key=lambda item: item.declaration)
for previous, current in zip(group, group[1:]):
between = lines[previous.end:current.start - 1]
# Unknown syntax or a macro between items owns its own layout; do not infer across it.
if any(candidate.strip() for candidate in between):
continue
blank_count = sum(1 for candidate in between if not candidate.strip())
compact = previous.kind == current.kind and previous.kind in {"const", "static", "type"} and previous.visibility == current.visibility
if compact:
# Compact declaration blocks have a dedicated owner when a project selects one.
continue
if blank_count != 1:
errors.append(f"{path}:{current.start}: expected exactly one blank line between Rust items; found {blank_count}")
return errors
def main() -> int:
"""Run the read-only item-spacing audit on explicitly selected Rust files."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="+", help="Rust files or directories to inspect")
args = parser.parse_args()
files, errors = selected_files(args.paths)
if errors or not files:
for error in errors or ["no Rust files selected"]:
print(f"Rust item-spacing audit: {error}", file=sys.stderr)
return 2
for path in files:
try:
errors.extend(audit_file(path))
except (OSError, UnicodeError) as error:
print(f"Rust item-spacing audit: cannot read {path}: {error}", file=sys.stderr)
return 2
for error in errors:
print(error, file=sys.stderr)
if errors:
print(f"Rust item-spacing audit: {len(errors)} error(s) across {len(files)} file(s)", file=sys.stderr)
return 1
print(f"Rust item-spacing audit: clean ({len(files)} file(s))")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# file: scripts/audit_rust_module_order.py
# version: 1
"""Audit top-level Rust module ordering with test modules last."""
from __future__ import annotations
import argparse
import pathlib
import re
import sys
from _rust_audit_common import line_depths, mask_source, selected_files
def _natural_key(value: str) -> tuple[tuple[int, object], ...]:
parts = re.findall(r"[A-Za-z_]+|[0-9]+", value)
return tuple((1, int(part)) if part.isdigit() else (0, part) for part in parts)
def audit_file(path: pathlib.Path) -> list[str]:
"""Require production modules before test modules and alphabetic order within each group."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
depths = line_depths(masked)
modules: list[tuple[int, bool, str]] = []
for number, line in enumerate(lines, 1):
if depths[number - 1] != 0:
continue
match = re.match(r"^mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:;|\{)", line.strip())
if match is None:
continue
cursor = number - 2
is_test = False
while cursor >= 0 and lines[cursor].strip().startswith("#["):
if "cfg(test)" in lines[cursor].replace(" ", ""):
is_test = True
cursor -= 1
modules.append((number, is_test, match.group(1)))
production = [(number, name) for number, is_test, name in modules if not is_test]
tests = [(number, name) for number, is_test, name in modules if is_test]
errors: list[str] = []
if production and tests and min(number for number, _ in tests) < max(number for number, _ in production):
errors.append(f"{path}:{min(number for number, _ in tests)}: test modules must follow production modules")
for group, label in ((production, "production"), (tests, "test")):
names = [name for _, name in group]
expected = sorted(names, key=_natural_key)
if names != expected:
for (number, name), wanted in zip(group, expected, strict=True):
if name != wanted:
errors.append(f"{path}:{number}: {label} module declarations are not alphabetically ordered")
break
return errors
def main() -> int:
"""Run the read-only module-order audit on explicitly selected Rust files."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="+", help="Rust files or directories to inspect")
args = parser.parse_args()
files, errors = selected_files(args.paths)
if errors or not files:
for error in errors or ["no Rust files selected"]:
print(f"Rust module-order audit: {error}", file=sys.stderr)
return 2
for path in files:
try:
errors.extend(audit_file(path))
except (OSError, UnicodeError) as error:
print(f"Rust module-order audit: cannot read {path}: {error}", file=sys.stderr)
return 2
for error in errors:
print(error, file=sys.stderr)
if errors:
print(f"Rust module-order audit: {len(errors)} error(s) across {len(files)} file(s)", file=sys.stderr)
return 1
print(f"Rust module-order audit: clean ({len(files)} file(s))")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
# file: scripts/audit_rust_reexport_blocks.py
# version: 1
"""Audit crate-root Rust re-export block visibility and spacing."""
from __future__ import annotations
import argparse
import pathlib
import re
import sys
from _rust_audit_common import line_depths, mask_source, selected_files
_EXPORT = re.compile(r"^(pub(?:\(crate\))?)\s+use\s+")
def audit_file(path: pathlib.Path) -> list[str]:
"""Require public re-exports before crate-public re-exports and compact blocks."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
depths = line_depths(masked)
exports: list[tuple[int, str]] = []
for number, line in enumerate(lines, 1):
if depths[number - 1] != 0:
continue
match = _EXPORT.match(line.strip())
if match is None:
continue
visibility = "pub(crate)" if match.group(1) == "pub(crate)" else "pub"
exports.append((number, visibility))
errors: list[str] = []
crate_seen = False
previous_by_visibility: dict[str, int] = {}
for number, visibility in exports:
if visibility == "pub(crate)":
crate_seen = True
elif crate_seen:
errors.append(f"{path}:{number}: pub use block must precede pub(crate) use block")
previous = previous_by_visibility.get(visibility)
if previous is not None:
between = lines[previous:number - 1]
if any(not candidate.strip() for candidate in between):
errors.append(f"{path}:{number}: {visibility} use block contains an empty line")
previous_by_visibility[visibility] = number
public = [number for number, visibility in exports if visibility == "pub"]
crate_public = [number for number, visibility in exports if visibility == "pub(crate)"]
if public and crate_public:
transition = lines[public[-1]:crate_public[0] - 1]
blank_count = sum(1 for candidate in transition if not candidate.strip())
if blank_count != 1:
errors.append(f"{path}:{crate_public[0]}: pub use and pub(crate) use blocks require exactly one blank line; found {blank_count}")
return errors
def main() -> int:
"""Run the read-only re-export-block audit on explicitly selected Rust files."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="+", help="Rust crate-root files or directories to inspect")
args = parser.parse_args()
files, errors = selected_files(args.paths)
if errors or not files:
for error in errors or ["no Rust files selected"]:
print(f"Rust re-export-block audit: {error}", file=sys.stderr)
return 2
for path in files:
try:
errors.extend(audit_file(path))
except (OSError, UnicodeError) as error:
print(f"Rust re-export-block audit: cannot read {path}: {error}", file=sys.stderr)
return 2
for error in errors:
print(error, file=sys.stderr)
if errors:
print(f"Rust re-export-block audit: {len(errors)} error(s) across {len(files)} file(s)", file=sys.stderr)
return 1
print(f"Rust re-export-block audit: clean ({len(files)} file(s))")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# file: scripts/audit_rust_trait_import_blocks.py
# version: 1
"""Audit placement and contiguity of module-level Rust trait-import blocks."""
from __future__ import annotations
import argparse
import pathlib
import re
import sys
from _rust_audit_common import line_depths, mask_source, selected_files
_DECLARATION = re.compile(r"^(?:pub(?:\(crate\))?\s+)?(?:async\s+|const\s+|unsafe\s+)*(?:const|static|type|struct|enum|trait|union|fn|impl)\b")
_USE = re.compile(r"^use\s+")
def audit_file(path: pathlib.Path) -> list[str]:
"""Require ordinary module-level uses before declarations and in one compact block."""
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
masked, _ = mask_source(text)
depths = line_depths(masked)
seen_declaration = False
uses: list[int] = []
errors: list[str] = []
for number, line in enumerate(lines, 1):
if depths[number - 1] != 0:
continue
stripped = line.strip()
if _DECLARATION.match(stripped):
seen_declaration = True
if _USE.match(stripped):
uses.append(number)
if seen_declaration:
errors.append(f"{path}:{number}: module-level trait import must precede Rust declarations")
for previous, current in zip(uses, uses[1:]):
between = lines[previous:current - 1]
if any(not candidate.strip() for candidate in between):
errors.append(f"{path}:{current}: module-level trait import block contains an empty line")
return errors
def main() -> int:
"""Run the read-only trait-import-block audit on explicitly selected Rust files."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="+", help="Rust files or directories to inspect")
args = parser.parse_args()
files, errors = selected_files(args.paths)
if errors or not files:
for error in errors or ["no Rust files selected"]:
print(f"Rust trait-import-block audit: {error}", file=sys.stderr)
return 2
for path in files:
try:
errors.extend(audit_file(path))
except (OSError, UnicodeError) as error:
print(f"Rust trait-import-block audit: cannot read {path}: {error}", file=sys.stderr)
return 2
for error in errors:
print(error, file=sys.stderr)
if errors:
print(f"Rust trait-import-block audit: {len(errors)} error(s) across {len(files)} file(s)", file=sys.stderr)
return 1
print(f"Rust trait-import-block audit: clean ({len(files)} file(s))")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,90 @@
# file: tests/test_markdown_tables.py
# version: 1
"""Exercise Markdown table detection, including explicit Markdown examples."""
from __future__ import annotations
import pathlib
import subprocess
import sys
import tempfile
import unittest
_SCRIPT = pathlib.Path(__file__).resolve().parents[1] / "scripts/audit_markdown_tables.py"
class MarkdownTableAuditTests(unittest.TestCase):
def setUp(self) -> None:
temporary = tempfile.TemporaryDirectory(prefix="pgr-tables-")
self.addCleanup(temporary.cleanup)
self.root = pathlib.Path(temporary.name)
def source(self, contents: str) -> pathlib.Path:
path = self.root / "sample.md"
path.write_text(contents, encoding="utf-8")
return path
def run_audit(self) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, "-B", str(_SCRIPT), "sample.md"],
cwd=self.root, capture_output=True, text=True, check=False,
)
def test_regular_and_fenced_markdown_tables_are_counted(self) -> None:
self.source(
"| A | BB |\n"
"|---|----|\n"
"| x | y |\n"
"\n"
"```markdown\n"
"| C | DD |\n"
"|---|----|\n"
"| z | q |\n"
"```\n"
)
result = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("clean (2 table(s), 1 file(s))", result.stdout)
def test_misaligned_table_inside_markdown_fence_is_rejected(self) -> None:
self.source(
"```markdown\n"
"| Fichier ou famille | Responsabilité | Règle de modification |\n"
"|--------------------|----------------|------------------------|\n"
"| `<path-or-pattern>` | `<rôle durable>` | `<quand/comment modifier>` |\n"
"```\n"
)
result = self.run_audit()
self.assertEqual(result.returncode, 1)
self.assertIn("sample.md:2: column 1 is not vertically aligned", result.stderr)
self.assertIn("column 2 is not vertically aligned", result.stderr)
self.assertIn("column 3 is not vertically aligned", result.stderr)
def test_md_fence_is_audited(self) -> None:
self.source(
"```md\n"
"| A | BB |\n"
"|---|----|\n"
"| x | y |\n"
"```\n"
)
result = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("clean (1 table(s), 1 file(s))", result.stdout)
def test_non_markdown_fence_remains_ignored(self) -> None:
self.source(
"```text\n"
"| Fichier ou famille | Responsabilité |\n"
"|--------------------|----------------|\n"
"| x | y |\n"
"```\n"
)
result = self.run_audit()
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("clean (0 table(s), 1 file(s))", result.stdout)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,7 +1,7 @@
# file: tests/test_rust_audits.py
# version: 1
# version: 2
"""Targeted contracts for the two optional Rust style audits."""
"""Targeted contracts for the optional monotask Rust structure audits."""
from __future__ import annotations
@@ -15,6 +15,10 @@ sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "scripts"))
from audit_rust_blank_lines import audit_file as audit_blanks # noqa: E402
from audit_rust_const_blocks import audit_file as audit_consts # noqa: E402
from audit_rust_item_spacing import audit_file as audit_item_spacing # noqa: E402
from audit_rust_module_order import audit_file as audit_module_order # noqa: E402
from audit_rust_reexport_blocks import audit_file as audit_reexports # noqa: E402
from audit_rust_trait_import_blocks import audit_file as audit_trait_imports # noqa: E402
from _rust_audit_common import selected_files # noqa: E402
@@ -132,6 +136,125 @@ const ALPHA: u8 = 2;
self.assertEqual(run(path).returncode, 1)
self.assertEqual(run(self.root / "missing.rs").returncode, 2)
def test_item_spacing_between_distinct_items(self) -> None:
path = self.source("""struct Alpha {}
struct Beta {}
fn sample() {}
""")
issues = audit_item_spacing(path)
self.assertTrue(any("exactly one blank line" in issue for issue in issues))
def test_item_spacing_delegates_compact_declaration_blocks(self) -> None:
path = self.source("""const ALPHA: u8 = 1;
const BETA: u8 = 2;
static CACHE: u8 = 0;
static READY: u8 = 1;
type First = u8;
type Second = u16;
""")
self.assertEqual(audit_item_spacing(path), [])
def test_item_spacing_inside_impl(self) -> None:
path = self.source("""struct Sample {}
impl Sample {
fn alpha() {}
fn beta() {}
}
""")
self.assertTrue(any("exactly one blank line" in issue for issue in audit_item_spacing(path)))
def test_item_spacing_does_not_infer_across_macro(self) -> None:
path = self.source("""struct Alpha {}
macro_rules! generated { () => {}; }
struct Beta {}
""")
self.assertEqual(audit_item_spacing(path), [])
def test_module_order_production_and_tests(self) -> None:
path = self.source("""mod beta;
mod alpha;
#[cfg(test)]
mod tests;
""")
issues = audit_module_order(path)
self.assertTrue(any("production" in issue for issue in issues))
path.write_text("#[cfg(test)]\nmod tests;\nmod alpha;\n", encoding="utf-8")
self.assertTrue(any("test modules must follow" in issue for issue in audit_module_order(path)))
def test_module_order_clean_with_path_attribute(self) -> None:
path = self.source("""mod alpha;
mod beta;
#[cfg(test)]
#[path = "unit_tests/mod.rs"]
mod unit_tests;
""")
self.assertEqual(audit_module_order(path), [])
def test_reexport_blocks_visibility_and_spacing(self) -> None:
path = self.source("""pub(crate) use self::internal::Internal;
pub use self::public::Public;
""")
issues = audit_reexports(path)
self.assertTrue(any("must precede" in issue for issue in issues))
path.write_text("pub use self::public::Alpha;\n\npub use self::public::Beta;\n", encoding="utf-8")
self.assertTrue(any("contains an empty line" in issue for issue in audit_reexports(path)))
def test_reexport_blocks_clean_with_rustdocs(self) -> None:
path = self.source("""/// Alpha.
pub use self::public::Alpha;
/// Beta.
pub use self::public::Beta;
/// Internal.
pub(crate) use self::internal::Internal;
""")
self.assertEqual(audit_reexports(path), [])
def test_trait_import_block_layout(self) -> None:
path = self.source("""use alpha::AlphaTrait;
use beta::BetaTrait;
struct Sample {}
""")
self.assertTrue(any("contains an empty line" in issue for issue in audit_trait_imports(path)))
path.write_text("struct Sample {}\n\nuse alpha::AlphaTrait;\n", encoding="utf-8")
self.assertTrue(any("must precede" in issue for issue in audit_trait_imports(path)))
def test_trait_import_block_clean_and_exports_ignored(self) -> None:
path = self.source("""mod alpha;
use alpha::AlphaTrait;
use beta::BetaTrait;
struct Sample {}
pub use self::alpha::Alpha;
""")
self.assertEqual(audit_trait_imports(path), [])
def test_new_audit_cli_statuses(self) -> None:
path = self.source("mod alpha;\nmod beta;\n")
root = pathlib.Path(__file__).resolve().parents[1] / "scripts"
scripts = (
root / "audit_rust_item_spacing.py",
root / "audit_rust_module_order.py",
root / "audit_rust_reexport_blocks.py",
root / "audit_rust_trait_import_blocks.py",
)
for script in scripts:
result = subprocess.run([sys.executable, "-B", str(script), str(path)], capture_output=True, text=True, check=False)
self.assertEqual(result.returncode, 0, (script, result.stderr))
missing = subprocess.run([sys.executable, "-B", str(script), str(self.root / "missing.rs")], capture_output=True, text=True, check=False)
self.assertEqual(missing.returncode, 2, (script, missing.stderr))
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,259 @@
# file: tests/test_rust_export_completeness.py
# version: 2
"""Regression tests for the optional Rust crate-root façade audit."""
from __future__ import annotations
import pathlib
import subprocess
import sys
import tempfile
import unittest
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "scripts"))
from audit_rust_export_completeness import audit_workspace # noqa: E402
class RustExportCompletenessTests(unittest.TestCase):
def setUp(self) -> None:
self.temporary = tempfile.TemporaryDirectory()
self.addCleanup(self.temporary.cleanup)
self.root = pathlib.Path(self.temporary.name)
def crate(self, relative: str, root_source: str, files: dict[str, str]) -> pathlib.Path:
crate = self.root / relative
(crate / "src").mkdir(parents=True)
(crate / "Cargo.toml").write_text(
'[package]\nname = "fixture"\nversion = "0.1.0"\nedition = "2024"\n',
encoding="utf-8",
)
(crate / "src/lib.rs").write_text(root_source, encoding="utf-8")
for name, text in files.items():
path = crate / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding="utf-8")
return crate
def candidates(self) -> list[object]:
_, candidates = audit_workspace(self.root)
return candidates
def test_nested_crate_layout_is_discovered(self) -> None:
self.crate("crates/common/example-lib", "mod api;\npub use self::api::Thing;\n", {"src/api.rs": "pub struct Thing;\n"})
crates, candidates = audit_workspace(self.root)
self.assertEqual(len(crates), 1)
self.assertEqual(candidates, [])
def test_missing_public_reexport_is_rejected(self) -> None:
self.crate("crates/example-lib", "mod api;\n", {"src/api.rs": "pub struct Thing;\n"})
candidates = self.candidates()
self.assertEqual([candidate.code for candidate in candidates], ["RUST-API-201"])
def test_external_crate_public_item_requires_reexport(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\n",
{
"src/api.rs": "pub(crate) const VALUE: u8 = 1;\n",
"src/consumer.rs": "pub(crate) fn value() -> u8 { return crate::api::VALUE; }\n",
},
)
self.assertTrue(any(candidate.code == "RUST-API-201" for candidate in self.candidates()))
def test_unused_crate_public_item_does_not_force_reexport(self) -> None:
self.crate("crates/example-lib", "mod api;\n", {"src/api.rs": "pub(crate) const VALUE: u8 = 1;\n"})
self.assertEqual(self.candidates(), [])
def test_exported_item_requires_crate_root_path(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
{
"src/api.rs": "pub struct Thing;\n",
"src/consumer.rs": "pub(crate) fn make() -> crate::api::Thing { return crate::api::Thing; }\n",
},
)
candidates = self.candidates()
self.assertTrue(any(candidate.code == "RUST-IMPORT-201" for candidate in candidates))
def test_grouped_and_aliased_reexports_are_understood(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\npub use self::api::{First, Second as PublicSecond};\n",
{"src/api.rs": "pub struct First;\npub struct Second;\n"},
)
self.assertEqual(self.candidates(), [])
def test_mod_rs_maps_to_parent_module(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\npub use self::api::Thing;\n",
{"src/api/mod.rs": "pub struct Thing;\n"},
)
self.assertEqual(self.candidates(), [])
def test_comments_and_literals_do_not_create_long_path_candidates(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
{
"src/api.rs": "pub struct Thing;\n",
"src/consumer.rs": 'pub(crate) fn text() -> &\'static str { return "crate::api::Thing"; } // crate::api::Thing\n',
},
)
self.assertEqual(self.candidates(), [])
def test_bare_reference_in_declaration_module_is_rejected(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\npub use self::api::Thing;\n",
{
"src/api.rs": (
"pub struct Thing;\n"
"impl Thing {\n"
" pub fn make() -> Thing { return Thing; }\n"
"}\n"
),
},
)
candidates = [candidate for candidate in self.candidates() if candidate.code == "RUST-IMPORT-201"]
self.assertEqual(len(candidates), 2)
self.assertTrue(all("crate::Thing" in candidate.message for candidate in candidates))
def test_crate_root_reference_in_declaration_module_is_clean(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\npub use self::api::Thing;\n",
{
"src/api.rs": (
"pub struct Thing;\n"
"impl crate::Thing {\n"
" pub fn make() -> crate::Thing { return crate::Thing; }\n"
"}\n"
),
},
)
self.assertEqual(self.candidates(), [])
def test_import_then_bare_reference_is_rejected(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
{
"src/api.rs": "pub struct Thing;\n",
"src/consumer.rs": "use crate::Thing;\npub(crate) fn make() -> Thing { return Thing; }\n",
},
)
candidates = [candidate for candidate in self.candidates() if candidate.code == "RUST-IMPORT-201"]
self.assertEqual(len(candidates), 1)
def test_self_super_and_internal_module_paths_are_rejected(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::Thing;\n",
{
"src/api.rs": "pub struct Thing;\n",
"src/consumer.rs": (
"pub(crate) fn a() -> crate::api::Thing { return crate::api::Thing; }\n"
"pub(crate) fn b() -> api::Thing { todo!() }\n"
),
},
)
candidates = [candidate for candidate in self.candidates() if candidate.code == "RUST-IMPORT-201"]
self.assertEqual(len(candidates), 2)
def test_attached_unit_test_visible_item_uses_crate_root(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\npub use self::api::Thing;\n",
{
"src/api.rs": (
"pub struct Thing;\n"
"#[cfg(test)]\n"
"#[path = \"../unit_tests/api_tests.rs\"]\n"
"mod tests;\n"
),
"unit_tests/api_tests.rs": "fn make() -> super::Thing { return super::Thing; }\n",
},
)
candidates = [candidate for candidate in self.candidates() if candidate.code == "RUST-IMPORT-201"]
self.assertEqual(len(candidates), 1)
def test_private_parent_item_in_attached_test_is_not_owned_by_this_audit(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\n",
{
"src/api.rs": (
"fn helper() -> u8 { return 1; }\n"
"#[cfg(test)]\n"
"#[path = \"../unit_tests/api_tests.rs\"]\n"
"mod tests;\n"
),
"unit_tests/api_tests.rs": "fn value() -> u8 { return super::helper(); }\n",
},
)
self.assertEqual(self.candidates(), [])
def test_external_qualified_same_named_item_is_not_misclassified(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::Result;\n",
{
"src/api.rs": "pub struct Result;\n",
"src/consumer.rs": "pub(crate) fn value() -> external_crate::Result { todo!() }\n",
},
)
self.assertEqual(self.candidates(), [])
def test_method_with_same_name_as_public_function_is_not_misclassified(self) -> None:
self.crate(
"crates/example-lib",
"mod api;\nmod consumer;\npub use self::api::run;\n",
{
"src/api.rs": "pub fn run() -> u8 { return 1; }\n",
"src/consumer.rs": "pub(crate) fn value(service: &Service) -> u8 { return service.run(); }\nstruct Service;\nimpl Service { fn run(&self) -> u8 { return 2; } }\n",
},
)
self.assertEqual(self.candidates(), [])
def test_lib_and_main_targets_are_not_conflated(self) -> None:
crate = self.crate(
"crates/example-app",
"mod api;\npub use self::api::Thing;\n",
{"src/api.rs": "pub struct Thing;\n"},
)
(crate / "src/main.rs").write_text(
"fn main() { let _ = fixture::Thing; }\n",
encoding="utf-8",
)
self.assertEqual(self.candidates(), [])
def test_cli_status_for_clean_violation_report_only_and_missing_crates(self) -> None:
script = pathlib.Path(__file__).resolve().parents[1] / "scripts" / "audit_rust_export_completeness.py"
def run(root: pathlib.Path, *extra: str) -> subprocess.CompletedProcess[str]:
return subprocess.run(
[sys.executable, "-B", str(script), "--root", str(root), *extra],
capture_output=True,
text=True,
check=False,
)
empty = self.root / "empty"
empty.mkdir()
self.assertEqual(run(empty).returncode, 2)
self.crate("crates/example-lib", "mod api;\npub use self::api::Thing;\n", {"src/api.rs": "pub struct Thing;\n"})
self.assertEqual(run(self.root).returncode, 0)
(self.root / "crates/example-lib/src/lib.rs").write_text("mod api;\n", encoding="utf-8")
self.assertEqual(run(self.root).returncode, 1)
self.assertEqual(run(self.root, "--report-only").returncode, 0)
if __name__ == "__main__":
unittest.main()