Files
pgr/deltas/0.1.0/rc.1.md
2026-09-26 23:26:23 +02:00

12 KiB
Raw Blame History

Delta 0.1.0-rc.1 — gel du scope et gate de release candidate

Baseline et version

Baseline attendue : commit opérateur de 0.1.0-beta.1, réalisé après application de history/0.1.0/beta.1.md avec statut PASS.

Le hash de ce commit n'est pas anticipé.

Version avant application :

0.1.0-beta.1

Version après application :

0.1.0-rc.1

Objet et gel

La RC gèle le scope fonctionnel de 0.1.0 sur :

  • la fondation de gouvernance et les skeleton specifications réutilisables ;
  • le socle Rust/Cargo, ses cinq templates techniques et les dry-runs NEW / ADOPTION ;
  • le contrat d'adoption de projets existants ADOPT / KEEP-LOCAL / OVERRIDE / DEFER ;
  • la convention de clôture stable rel.N sans zéro de padding et l'identité du premier commit stable X.Y.Z ;
  • le handoff automatique des commandes opérateur, du commit/push au tag et à la preuve publish.* ;
  • la revue d'horizon validée et le prompt 007 vers 0.1.1.

Le changelog reçoit une entrée candidate 0.1.0 orientée utilisateurs. ROADMAP.md, RULES.md, les owners, profils, templates techniques, skeletons et scripts d'audit sont vérifiés mais non modifiés.

Aucun scope Web, Java/Gradle, Android, Tauri, WASM, serveur Rust, Games/KSP ou deployment/operations n'entre fonctionnellement dans cette RC au-delà des décisions de trajectoire déjà validées.

Scope

REPLACE :

VERSION
CHANGELOG.md
docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md

ADD :

deltas/0.1.0/rc.1.md

DELETE : aucun.

Le history beta.1 est livré séparément et MUST être appliqué/committé avant ce delta.

Gate opérateur de release candidate

Après application du history PASS de beta.1, commit/push de beta.1, puis application du présent delta :

test "$(cat VERSION)" = '0.1.0-rc.1'
git cat-file -e HEAD:history/0.1.0/beta.1.md
test -f history/0.1.0/beta.1.md
test -f deltas/0.1.0/rc.1.md
test -f prompts/007-V0_1_1_START_PROMPT.md
test ! -e history/0.1.0/rc.1.md

grep -A2 '^## Statut$' history/0.1.0/beta.1.md | grep -Fx 'PASS'
grep -Fx '<!-- version: 10 -->' CHANGELOG.md
grep -Fx '## 0.1.0 (candidate)' CHANGELOG.md
grep -Fx '<!-- version: 9 -->' docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md
grep -Fx '<!-- version: 9 -->' ROADMAP.md
grep -Fx '<!-- version: 1 -->' prompts/007-V0_1_1_START_PROMPT.md

Contrôle d'intégration de la RC :

python3 -B - <<'PYGATE'
from pathlib import Path
import re

expected = {
    "alpha.1.md": "PASS",
    "alpha.2.md": "FAIL",
    "alpha.2.fix.1.md": "PASS",
    "alpha.3.md": "FAIL",
    "alpha.3.fix.1.md": "PASS",
    "alpha.4.md": "PASS",
    "alpha.5.md": "PASS",
    "beta.1.md": "PASS",
}

histories = {path.name for path in Path("history/0.1.0").glob("*.md")}
assert histories == set(expected), histories
for name, status in expected.items():
    text = Path("history/0.1.0", 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.1.0").glob("*.md")}
assert deltas == set(expected) | {"rc.1.md"}, deltas

changelog = Path("CHANGELOG.md").read_text(encoding="utf-8")
assert "## 0.1.0 (candidate)" in changelog
section = changelog.split("## 0.1.0 (candidate)", 1)[1].split("## 0.0.5", 1)[0]
for phrase in (
    "première baseline PGR réellement réutilisable",
    "ADOPT / KEEP-LOCAL / OVERRIDE / DEFER",
    "rel.N",
    "publish.*",
    "frontend Web",
):
    assert phrase in section, phrase
for forbidden in ("alpha.2", "alpha.3", "beta.1", "rc.1"):
    assert forbidden not in section, forbidden

workflow = Path("docs/rules/210-WORKFLOW.md").read_text(encoding="utf-8")
for phrase in (
    "La famille `rel.N` utilise la représentation sans zéro de padding",
    "première de ces tranches qui obtient réellement `PASS` est incluse dans le commit stable `X.Y.Z`",
    "Le rapport `publish.*` est produit seulement à partir des artefacts et preuves réellement disponibles après publication.",
    "../templates/006-OPERATOR_COMMAND_HANDOFF.md",
):
    assert phrase in workflow, phrase

adoption = Path("docs/rules/230-ADOPTION.md").read_text(encoding="utf-8")
for phrase in ("ADOPT", "KEEP-LOCAL", "OVERRIDE", "DEFER", "Préservation de l’historique"):
    assert phrase in adoption, phrase

rust_dryrun = Path("docs/validation/004-RUST_CARGO_BASELINE_DRY_RUN.md").read_text(encoding="utf-8")
assert "NEW       nouveau projet" in rust_dryrun
assert "ADOPTION  projet existant" in rust_dryrun

roadmap = Path("ROADMAP.md").read_text(encoding="utf-8")
for version in ("0.1.1", "0.1.2", "0.1.3", "0.1.4", "0.1.5", "0.1.6", "0.1.7"):
    assert f"- `( ) {version}`" in roadmap, version
assert "- `( ) 0.1.8`" not in roadmap
assert "WASM" in roadmap
assert "serveur Web Rust / Actix Web ou alternative" in roadmap
assert "deployment & operations" in roadmap

prompt = Path("prompts/007-V0_1_1_START_PROMPT.md").read_text(encoding="utf-8")
for phrase in (
    "archive source téléchargée depuis le tag Gitea `v0.1.0`",
    "pgr-0.1.0-publish.001-report.md",
    "0.1.1-alpha.1",
    "games-v0.3.6.zip",
    "TypeScript",
    "Vite",
    "Sass/CSS",
    "Le skeleton Tauri reste `DEFER` pendant cette release.",
):
    assert phrase in prompt, phrase

plan = Path("docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md").read_text(encoding="utf-8")
assert "### beta.1 — réconciliation finale et revue d'horizon\n\nStatut opérateur : **PASS**." in plan
assert "### rc.1 — release candidate\n\nStatut opérateur : **à valider**." in plan
assert "État : **validée `PASS` par la gate opérateur de `beta.1`**." in plan

print("0.1.0 rc.1 integration audit: clean (8 prior histories, frozen reusable baseline, candidate changelog, horizon and prompt 007 verified)")
PYGATE

Rejouer également les deux chemins Rust/Cargo avec la toolchain réelle :

python3 -B - <<'PYRUST'
from pathlib import Path
import hashlib
import shutil
import subprocess
import tempfile
import tomllib

cargo = shutil.which("cargo")
if cargo is None:
    raise SystemExit("BLOCKED: cargo is required for the 0.1.0 RC Rust/Cargo release gate")

commands = (
    [cargo, "metadata", "--format-version", "1", "--no-deps"],
    [cargo, "fmt", "--all", "--", "--check"],
    [cargo, "check", "--workspace"],
    [cargo, "clippy", "--workspace", "--all-targets", "--all-features", "--", "-D", "warnings"],
)

source = Path("templates/rust-cargo")
with tempfile.TemporaryDirectory(prefix="pgr-rc1-new-") as temporary:
    root = Path(temporary)
    (root / ".cargo").mkdir(parents=True)
    (root / "crates/pgr-rc-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-rc-core-lib/Cargo.toml")

    workspace = (root / "Cargo.toml").read_text(encoding="utf-8")
    workspace = workspace.replace("crates/project-core-lib", "crates/pgr-rc-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-rc-core-lib/Cargo.toml").read_text(encoding="utf-8")
    member = member.replace("project-core-lib", "pgr-rc-core-lib")
    (root / "crates/pgr-rc-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-rc1-new")
    (root / ".cargo/config.toml").write_text(config, encoding="utf-8")

    (root / "crates/pgr-rc-core-lib/src/lib.rs").write_text(
        "//! Minimal source used only by the PGR RC dry-run.\n\n"
        "/// Returns the RC marker.\n"
        "#[must_use]\n"
        "pub const fn rc_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-rc-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

    for command in commands:
        subprocess.run(command, cwd=root, check=True)

with tempfile.TemporaryDirectory(prefix="pgr-rc1-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()) <= {"ADOPT", "KEEP-LOCAL", "OVERRIDE", "DEFER"}

    for command in commands:
        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")

print("0.1.0 rc.1 Rust/Cargo release dry-runs: clean (NEW and ADOPTION accepted by Cargo/rustfmt/Clippy)")
PYRUST

Puis la suite PGR complète :

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 CHANGELOG.md \
    docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md \
    deltas/0.1.0/rc.1.md

git diff --cached --check
git status --short

Le scope attendu avant ajout du history est exactement :

M  CHANGELOG.md
M  VERSION
A  deltas/0.1.0/rc.1.md
M  docs/plans/006-V0_1_0_REUSABLE_BASELINE_PLAN.md

Revue opérateur complémentaire

Confirmer que :

  • le changelog décrit les capacités de 0.1.0 et non ses tranches internes ;
  • aucune règle/profil/template/skeleton/audit fonctionnel nouveau n'entre dans la RC ;
  • ROADMAP.md conserve 0.1.0 en cours jusqu'à la promotion stable ;
  • 0.1.1..0.1.7 restent la trajectoire engagée et les domaines non numérotés restent réellement différés ;
  • le prompt 007 exige bien l'archive taggée v0.1.0 et le rapport publish.001 seulement lorsqu'il existe réellement avec statut PASS ;
  • la RC n'est ni le commit stable 0.1.0, ni le tag v0.1.0, ni la vérification post-tag.

Transition

PASS

Produire séparément history/0.1.0/rc.1.md, l'appliquer puis committer/pousser 0.1.0-rc.1 avant le delta stable rel.1.

FAIL

Consigner l'échec réel dans history/0.1.0/rc.1.md puis corriger uniquement le scope gelé dans rc.1.fix.1, sauf REPLAN si la candidate révèle un défaut fonctionnel plus large.