# Delta 0.0.2-alpha.3 — refresh, résolution et snapshot Git réels ## Base requise Partir de `0.0.2-alpha.2.fix.2` après application, validation, history `PASS`, commit et push : ```text deltas/0.0.2/alpha.2.md présent et historique deltas/0.0.2/alpha.2.fix.1.md présent et historique deltas/0.0.2/alpha.2.fix.2.md présent et historique history/0.0.2/alpha.2.md FAIL history/0.0.2/alpha.2.fix.1.md FAIL history/0.0.2/alpha.2.fix.2.md PASS ``` Les deltas et histories antérieurs ne doivent pas être réécrits. La version devient : ```text 0.0.2-alpha.3 ``` ## Scope Cette tranche complète la matérialisation Git de lecture déjà prévue par le plan : - faire implémenter réellement `knowledge-git-api::GitRepository` par `knowledge-git-lib` ; - enregistrer explicitement les configurations physiques des sources dans `GitBackend` sans les faire remonter dans le domaine ou l'API provider-neutral ; - ajouter au contrat uniquement `GitSnapshot` et `prepare_snapshot`, maintenant nécessaires ; - initialiser ou rafraîchir le mirror avec pruning ; - résoudre une référence opaque vers un commit réel ; - matérialiser un worktree détaché adressé par la révision résolue ; - valider un snapshot existant avant réutilisation ; - prouver le chemin avec de vrais repositories Git temporaires locaux. Cette tranche ne matérialise pas encore le cas d'usage `Changed` / `Unchanged` d'`alpha.4`. ## Changements apportés ### Contrat provider-neutral `knowledge-git-api` ajoute `GitSnapshot`, contenant uniquement : ```text GitRevision PathBuf de lecture locale ``` `GitRepository` gagne : ```text prepare_snapshot(source_id, revision) ``` Aucune API de forge, notion de remote, commande Git ou configuration physique n'entre dans le contrat. ### Backend configuré `GitBackend` conserve sa configuration globale puis enregistre explicitement des `GitSourceConfig` par `SourceId`. Un doublon est refusé par une erreur récupérable. Une opération provider-neutral sur une source inconnue échoue également sans exposer son URL ou son chemin. ### Refresh Le refresh : 1. vérifie que la source logique possède une configuration physique enregistrée ; 2. initialise le mirror s'il n'existe pas encore ; 3. exécute ensuite : ```text git --git-dir remote update --prune ``` Aucune API de forge n'est nécessaire. ### Résolution La résolution exige un mirror existant et utilise : ```text git --git-dir rev-parse --verify --end-of-options ^{commit} ``` La sortie produite par le backend doit être non vide et hexadécimale avant de devenir `GitRevision`. Aucune longueur SHA particulière n'est figée. ### Snapshot Le chemin de snapshot reste : ```text /snapshots/// ``` Un snapshot absent est créé par : ```text git --git-dir worktree add --detach -- ``` Un snapshot existant n'est réutilisé qu'après : ```text git -C rev-parse --verify HEAD git -C status --porcelain=v1 --untracked-files=all ``` Le `HEAD` doit correspondre exactement à la révision demandée et le statut doit être vide. Une divergence ou un snapshot sale est rejeté sans suppression automatique. ### Tests réels `crates/knowledge-git-lib/tests/git_read_path.rs` crée un repository Git local temporaire réel et vérifie notamment : - initialisation du mirror via la voie `refresh` ; - résolution de `refs/heads/main` ; - création puis réutilisation idempotente d'un snapshot ; - ajout d'un second commit source, refresh et nouvelle révision ; - snapshot distinct pour la nouvelle révision ; - rejet d'une référence inexistante ; - rejet d'un snapshot existant rendu sale. Ces tests exercent la frontière `knowledge_git_api::GitRepository` et le client Git réel. Les mocks existants restent uniquement des tests unitaires des arguments/processus. ## Hors scope confirmé La tranche n'ajoute pas : - `knowledge-app-lib` ; - scheduler ; - persistance de dernière révision ; - PostgreSQL ou migrations ; - adapter Gitea/GitHub/GitLab ; - webhook ; - status/diff public, branch, commit, merge, rebase ou push ; - dépendance Git Rust secondaire ; - changement sous `Web/` ; - changement de rendu ou de route HTTP. ## Gate opérateur Exécuter depuis la racine du workspace après application du delta : ```bash test "$(cat VERSION)" = '0.0.2-alpha.3' grep -F 'version = "0.0.2-alpha.3"' Cargo.toml grep -A2 '^## Statut$' history/0.0.2/alpha.2.fix.2.md | grep -Fx 'PASS' git diff --check python3 - <<'PY' from pathlib import Path import tomllib root = tomllib.loads(Path("Cargo.toml").read_text(encoding="utf-8")) manifest = tomllib.loads(Path("crates/knowledge-git-lib/Cargo.toml").read_text(encoding="utf-8")) assert len(root["workspace"]["members"]) == 8 assert set(manifest["dependencies"]) == {"knowledge-domain-lib", "knowledge-git-api"} api = Path("crates/knowledge-git-api/src/lib.rs").read_text(encoding="utf-8") backend = Path("crates/knowledge-git-lib/src/backend.rs").read_text(encoding="utf-8") error = Path("crates/knowledge-git-lib/src/error.rs").read_text(encoding="utf-8") integration = Path("crates/knowledge-git-lib/tests/git_read_path.rs").read_text(encoding="utf-8") for phrase in ( "pub struct GitSnapshot", "fn prepare_snapshot(", ): assert phrase in api, phrase for phrase in ( "impl knowledge_git_api::GitRepository for crate::GitBackend", "pub fn register_source(", 'std::ffi::OsString::from("remote")', 'std::ffi::OsString::from("update")', 'std::ffi::OsString::from("--prune")', 'std::ffi::OsString::from("rev-parse")', 'std::ffi::OsString::from("--verify")', 'std::ffi::OsString::from("--end-of-options")', 'std::ffi::OsString::from("worktree")', 'std::ffi::OsString::from("add")', 'std::ffi::OsString::from("--detach")', 'std::ffi::OsString::from("status")', 'std::ffi::OsString::from("--porcelain=v1")', 'std::ffi::OsString::from("--untracked-files=all")', ): assert phrase in backend, phrase for phrase in ( "DuplicateSource", "SourceNotConfigured", "MirrorMissing", "InvalidRevision", "InvalidSnapshot", "RefreshMirror", "ResolveRevision", "CreateSnapshot", "InspectSnapshot", ): assert phrase in error, phrase for phrase in ( "real_local_repository_refreshes_resolves_and_materializes_revision_snapshots", "invalid_reference_and_dirty_existing_snapshot_are_rejected", "knowledge_git_api::GitRepository::refresh", "knowledge_git_api::GitRepository::resolve_revision", "knowledge_git_api::GitRepository::prepare_snapshot", ): assert phrase in integration, phrase for forbidden in ( 'Command::new("sh")', 'Command::new("bash")', 'Command::new("cmd")', 'Command::new("powershell")', "knowledge-app-lib", "knowledge-forge-gitea-lib", "knowledge-store-postgres-lib", ): assert forbidden not in backend + error, forbidden print("Knowledge 0.0.2-alpha.3 structural audit: clean") PY python3 -B scripts/audit_rust_test_layout.py crates 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 python3 -B scripts/audit_rust_export_completeness.py --root . --summary-only git --version cargo fmt --all -- --check cargo metadata --format-version 1 --no-deps > /tmp/sasedev-knowledge-0.0.2-alpha.3-metadata.json cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test -p knowledge-git-api --all-targets --all-features cargo test -p knowledge-git-lib --all-targets --all-features cargo test -p knowledge-git-lib --test git_read_path cargo test --workspace --all-targets --all-features python3 - <<'PY' import json from pathlib import Path metadata = json.loads(Path("/tmp/sasedev-knowledge-0.0.2-alpha.3-metadata.json").read_text(encoding="utf-8")) packages = {package["name"] for package in metadata["packages"]} expected = { "knowledge-domain-lib", "knowledge-git-api", "knowledge-git-lib", "knowledge-sync-api", "knowledge-web-lib", "knowledge-pgr-server", "knowledge-howtos-server", "knowledge-snippets-server", } assert packages == expected, packages assert len(metadata["workspace_members"]) == 8, metadata["workspace_members"] print("Knowledge 0.0.2-alpha.3 Cargo metadata audit: clean") PY test -f Cargo.lock git check-ignore -q Cargo.lock if git ls-files --error-unmatch Cargo.lock >/dev/null 2>&1; then echo 'Cargo.lock must remain untracked for Knowledge' >&2 exit 1 fi test -z "$(git diff --name-only -- Web)" test -z "$(git diff --name-only -- crates/knowledge-sync-api)" test ! -e crates/knowledge-app-lib test ! -e crates/knowledge-forge-gitea-lib test ! -e crates/knowledge-store-postgres-lib test ! -e history/0.0.2/alpha.3.md git add -A git diff --cached --check git status --short ``` ## Résultat attendu La tranche est `PASS` uniquement si : - le formatage, la compilation et Clippy strict sont propres ; - le test `knowledge-git-api` passe ; - les tests unitaires de `knowledge-git-lib` passent ; - les deux tests Git réels de `git_read_path` passent ; - les tests workspace passent ; - aucun changement hors scope n'est observé ; - aucun history `alpha.3` n'est créé avant réception des résultats opérateur. Un `FAIL`, `PARTIAL` ou `BLOCKED` doit être historisé avant tout fix. Un `PASS` autorise uniquement `0.0.2-alpha.4`.