# Delta 0.1.0-alpha.5 — baseline Rust/Cargo réutilisable et dry-run ## Baseline et version Baseline attendue : commit opérateur de `0.1.0-alpha.4`, réalisé après application de `history/0.1.0/alpha.4.md` avec statut `PASS`. Le hash de ce commit n’est pas anticipé. Version avant application : ```text 0.1.0-alpha.4 ``` Version après application : ```text 0.1.0-alpha.5 ``` ## Objet Prouver que la première baseline Rust/Cargo de PGR est réellement réutilisable dans deux situations distinctes : ```text NEW nouveau workspace gouverné depuis une baseline vide ADOPTION workspace Rust existant conservant ses décisions locales légitimes ``` La tranche réconcilie les skeletons `001` / `002`, les guides des templates Rust/Cargo et la validation. Elle ajoute un contrat durable de dry-run, mais aucun générateur de projet permanent et aucun nouveau profil technologique. Games et KSP ne servent pas de fixtures et ne sont pas modifiés. ## Scope REPLACE : ```text VERSION docs/000-README.md docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md docs/skeletons/000-README.md docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md docs/skeletons/002-RUST_CARGO_WORKSPACE.md docs/templates/001-RUST_CARGO_TOOLING.md docs/templates/002-CARGO_MANIFESTS.md docs/validation/001-VALIDATION_MATRIX.md ``` ADD : ```text deltas/0.1.0/alpha.5.md docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md ``` DELETE : aucun. Les templates techniques sous `templates/rust-cargo/`, les scripts d’audit, les profils Java/Android/Tauri, `ROADMAP.md` et `CHANGELOG.md` restent inchangés. ## Décisions ### Nouveau projet Le chemin d’instanciation reste `001 + 002`. Les cinq templates techniques sont adaptés dans un répertoire temporaire, les placeholders sont résolus et une crate source triviale est créée uniquement pour permettre à Cargo de vérifier la baseline. Le code temporaire du dry-run n’est pas ajouté aux templates PGR. ### Projet existant Le chemin d’adoption passe par `230-ADOPTION.md`. Les skeletons et templates servent de checklists de comparaison et ne sont jamais copiés aveuglément. Le dry-run doit inclure plusieurs décisions et prouver qu’un choix local classé `KEEP-LOCAL` ou `DEFER` reste inchangé. ### Autorité de la toolchain La syntaxe TOML et les assertions structurelles peuvent être prévalidées avec Python. En revanche, le mode `NEW` complet exige Cargo : une absence de toolchain ne peut pas être convertie en `PASS`. ## Validation opérateur Après application du history `PASS`, commit/push de `alpha.4`, puis application du présent delta : ```bash test "$(cat VERSION)" = '0.1.0-alpha.5' git cat-file -e HEAD:history/0.1.0/alpha.4.md test -f history/0.1.0/alpha.4.md test -f deltas/0.1.0/alpha.5.md test -f docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md test ! -e history/0.1.0/alpha.5.md grep -A2 '^## Statut$' history/0.1.0/alpha.4.md | grep -Fx 'PASS' grep -Fx '' docs/000-README.md grep -Fx '' docs/skeletons/000-README.md grep -Fx '' docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md grep -Fx '' docs/skeletons/002-RUST_CARGO_WORKSPACE.md grep -Fx '' docs/templates/001-RUST_CARGO_TOOLING.md grep -Fx '' docs/templates/002-CARGO_MANIFESTS.md grep -Fx '' docs/validation/001-VALIDATION_MATRIX.md grep -Fx '' docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md grep -Fx '' docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md ``` Contrôle structurel et prévalidation TOML : ```bash python3 -B - <<'PYGATE' from pathlib import Path import re import tomllib history = Path("history/0.1.0/alpha.4.md").read_text(encoding="utf-8") foundation = Path("docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md").read_text(encoding="utf-8") rust_skeleton = Path("docs/skeletons/002-RUST_CARGO_WORKSPACE.md").read_text(encoding="utf-8") tooling = Path("docs/templates/001-RUST_CARGO_TOOLING.md").read_text(encoding="utf-8") manifests = Path("docs/templates/002-CARGO_MANIFESTS.md").read_text(encoding="utf-8") dryrun = Path("docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md").read_text(encoding="utf-8") plan = Path("docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md").read_text(encoding="utf-8") roadmap = Path("ROADMAP.md").read_text(encoding="utf-8") assert re.search(r"## Statut\s+PASS\s", history) assert "../rules/230-ADOPTION.md" in foundation assert "../rules/230-ADOPTION.md" in rust_skeleton assert "004-RUST_CARGO_BASELINE_DRY_RUN.md" in rust_skeleton assert "KEEP-LOCAL" in tooling assert "ADOPT / KEEP-LOCAL / OVERRIDE / DEFER" in manifests for phrase in ( "NEW nouveau projet", "ADOPTION projet existant", "cargo metadata --format-version 1 --no-deps", "cargo clippy --workspace --all-targets --all-features -- -D warnings", "KEEP-LOCAL", "DEFER", "ne constitue pas un `PASS` de la toolchain", ): assert phrase in dryrun, phrase for path in ( Path("templates/rust-cargo/Cargo.toml"), Path("templates/rust-cargo/clippy.toml"), Path("templates/rust-cargo/rustfmt.toml"), Path("templates/rust-cargo/.cargo/config.toml"), Path("templates/rust-cargo/crates/project-core-lib/Cargo.toml"), ): with path.open("rb") as handle: tomllib.load(handle) assert "Statut opérateur : **PASS**." in plan assert "Statut opérateur : **à valider**." in plan assert "Games et KSP ne sont pas utilisés comme fixtures du dry-run." in plan assert "- `( ) 0.1.1`" not in roadmap print("0.1.0 alpha.5 structural/TOML audit: clean (NEW and ADOPTION paths explicit, templates parse, external corpora not required)") PYGATE ``` Dry-run `NEW` avec la toolchain officielle : ```bash python3 -B - <<'PYNEW' from pathlib import Path import shutil import subprocess import tempfile import tomllib cargo = shutil.which("cargo") if cargo is None: raise SystemExit("BLOCKED: cargo is required for the alpha.5 NEW dry-run") source = Path("templates/rust-cargo") with tempfile.TemporaryDirectory(prefix="pgr-alpha5-new-") as temporary: root = Path(temporary) (root / ".cargo").mkdir(parents=True) (root / "crates/pgr-dryrun-core-lib/src").mkdir(parents=True) shutil.copy2(source / "Cargo.toml", root / "Cargo.toml") shutil.copy2(source / "clippy.toml", root / "clippy.toml") shutil.copy2(source / "rustfmt.toml", root / "rustfmt.toml") shutil.copy2(source / ".cargo/config.toml", root / ".cargo/config.toml") shutil.copy2(source / "crates/project-core-lib/Cargo.toml", root / "crates/pgr-dryrun-core-lib/Cargo.toml") workspace = (root / "Cargo.toml").read_text(encoding="utf-8") workspace = workspace.replace("crates/project-core-lib", "crates/pgr-dryrun-core-lib") workspace = workspace.replace('version = "0.1.0"', 'version = "9.9.9"') (root / "Cargo.toml").write_text(workspace, encoding="utf-8") member = (root / "crates/pgr-dryrun-core-lib/Cargo.toml").read_text(encoding="utf-8") member = member.replace("project-core-lib", "pgr-dryrun-core-lib") (root / "crates/pgr-dryrun-core-lib/Cargo.toml").write_text(member, encoding="utf-8") config = (root / ".cargo/config.toml").read_text(encoding="utf-8") config = config.replace("project-name", "pgr-alpha5-new") (root / ".cargo/config.toml").write_text(config, encoding="utf-8") (root / "crates/pgr-dryrun-core-lib/src/lib.rs").write_text( "//! Minimal source used only by the PGR alpha.5 dry-run.\n\n" "/// Returns the dry-run marker.\n" "#[must_use]\n" "pub const fn dry_run_marker() -> u32 {\n" " return 1;\n" "}\n", encoding="utf-8", ) for path in ( root / "Cargo.toml", root / "clippy.toml", root / "rustfmt.toml", root / ".cargo/config.toml", root / "crates/pgr-dryrun-core-lib/Cargo.toml", ): with path.open("rb") as handle: tomllib.load(handle) text = path.read_text(encoding="utf-8") assert "project-name" not in text assert "project-core-lib" not in text commands = ( [cargo, "metadata", "--format-version", "1", "--no-deps"], [cargo, "fmt", "--all", "--", "--check"], [cargo, "check", "--workspace"], [cargo, "clippy", "--workspace", "--all-targets", "--all-features", "--", "-D", "warnings"], ) for command in commands: subprocess.run(command, cwd=root, check=True) print("0.1.0 alpha.5 NEW dry-run: clean (temporary templates accepted by Cargo/rustfmt/Clippy)") PYNEW ``` Dry-run `ADOPTION` non destructif : ```bash python3 -B - <<'PYADOPT' from pathlib import Path import hashlib import shutil import subprocess import tempfile cargo = shutil.which("cargo") if cargo is None: raise SystemExit("BLOCKED: cargo is required for the alpha.5 ADOPTION dry-run") valid_decisions = {"ADOPT", "KEEP-LOCAL", "OVERRIDE", "DEFER"} with tempfile.TemporaryDirectory(prefix="pgr-alpha5-adoption-") as temporary: root = Path(temporary) (root / "crates/legacy-core-lib/src").mkdir(parents=True) (root / "Cargo.toml").write_text( '[workspace]\nresolver = "3"\nmembers = ["crates/legacy-core-lib"]\n\n' '[workspace.package]\nversion = "2.3.4"\nedition = "2024"\npublish = false\n', encoding="utf-8", ) (root / "rustfmt.toml").write_text('edition = "2024"\nmax_width = 120\n', encoding="utf-8") (root / "crates/legacy-core-lib/Cargo.toml").write_text( '[package]\nname = "legacy-core-lib"\nversion.workspace = true\nedition.workspace = true\npublish.workspace = true\n', encoding="utf-8", ) (root / "crates/legacy-core-lib/src/lib.rs").write_text( "//! Existing synthetic library.\n\n" "/// Returns the existing marker.\n" "#[must_use]\n" "pub const fn existing_marker() -> u32 {\n" " 7\n" "}\n", encoding="utf-8", ) protected = (root / "Cargo.toml", root / "rustfmt.toml", root / "crates/legacy-core-lib/Cargo.toml") before = {path: hashlib.sha256(path.read_bytes()).hexdigest() for path in protected} decisions = { "510-RUST_CARGO": "ADOPT", "rustfmt.max_width=120": "KEEP-LOCAL", "PGR external artifact configuration": "DEFER", "strict crate-root export audit": "DEFER", } assert set(decisions.values()) <= valid_decisions assert "KEEP-LOCAL" in decisions.values() assert "DEFER" in decisions.values() for command in ( [cargo, "metadata", "--format-version", "1", "--no-deps"], [cargo, "fmt", "--all", "--", "--check"], [cargo, "check", "--workspace"], [cargo, "clippy", "--workspace", "--all-targets", "--all-features", "--", "-D", "warnings"], ): subprocess.run(command, cwd=root, check=True) after = {path: hashlib.sha256(path.read_bytes()).hexdigest() for path in protected} assert before == after assert "max_width = 120" in (root / "rustfmt.toml").read_text(encoding="utf-8") assert not (root / ".cargo/config.toml").exists() print("0.1.0 alpha.5 ADOPTION dry-run: clean (local KEEP-LOCAL/DEFER choices preserved, workspace remains valid)") PYADOPT ``` Puis rejouer les preuves PGR : ```bash 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 git diff --check git add VERSION docs/000-README.md \ docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md \ docs/skeletons/000-README.md \ docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md \ docs/skeletons/002-RUST_CARGO_WORKSPACE.md \ docs/templates/001-RUST_CARGO_TOOLING.md \ docs/templates/002-CARGO_MANIFESTS.md \ docs/validation/001-VALIDATION_MATRIX.md \ docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md \ deltas/0.1.0/alpha.5.md git diff --cached --check git status --short ``` Le scope indexé attendu avant ajout du history de `alpha.5` est exactement : ```text M VERSION A deltas/0.1.0/alpha.5.md M docs/000-README.md M docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md M docs/skeletons/000-README.md M docs/skeletons/001-PROJECT_GOVERNANCE_FOUNDATION.md M docs/skeletons/002-RUST_CARGO_WORKSPACE.md M docs/templates/001-RUST_CARGO_TOOLING.md M docs/templates/002-CARGO_MANIFESTS.md M docs/validation/001-VALIDATION_MATRIX.md A docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md ``` ## Revue opérateur complémentaire Confirmer que : - le mode `NEW` n’introduit aucun `src/lib.rs` universel dans PGR ; - le mode `ADOPTION` ne remplace pas les fichiers existants classés `KEEP-LOCAL` ou `DEFER` ; - les templates techniques eux-mêmes restent inchangés dans cette tranche ; - la réussite de la prévalidation Python n’est pas confondue avec la réussite Cargo ; - Games et KSP ne sont ni modifiés ni nécessaires à la preuve ; - aucun profil Java, Android, Tauri, Web, WASM ou serveur n’est matérialisé ; - aucun numéro futur `0.1.x` n’est réservé. ## Transition ### PASS Produire `history/0.1.0/alpha.5.md`, l’appliquer puis remettre automatiquement les commandes de commit/push. Passer ensuite à `0.1.0-beta.1` pour la réconciliation finale et la revue d’horizon obligatoire avant RC. ### FAIL / BLOCKED Consigner le résultat réel dans `history/0.1.0/alpha.5.md`. Un défaut des templates/skeletons reste dans `alpha.5.fix.N` sauf REPLAN. Une toolchain Cargo absente qui empêche les dry-runs requis doit être enregistrée `BLOCKED`, pas transformée en `PASS` par la seule prévalidation Python.