# Delta 0.0.3-alpha.3 — worktree d'écriture et commit candidat ## Baseline requise Partir de `0.0.3-alpha.2` après application, commit et push de son history `PASS`. La version devient : ```text 0.0.3-alpha.3 ``` ## Objet Implémenter dans `knowledge-git-lib` la création locale réelle d'une révision candidate à partir d'une base Git explicite, sans mettre encore à jour de branche distante. La tranche : - ajoute un worktree d'écriture temporaire et déterministe sous la racine d'état du backend ; - réutilise le mirror contrôlé du `SourceId` et exige une `GitRevision` de base sûre ; - applique exactement les mutations `Write` / `Delete` demandées ; - refuse toute cible ou tout parent filesystem qui est un symlink ; - stage uniquement les chemins demandés puis vérifie que l'index contient exactement cet ensemble ; - neutralise la configuration Git système/globale ambiante pertinente et conserve `core.hooksPath` sous contrôle ; - crée le commit avec auteur et committer explicitement issus de `GitCommitIdentity`, `--no-verify` et `--no-gpg-sign` ; - vérifie que le candidat a exactement la base demandée comme premier parent ; - retient l'objet candidat sous `refs/knowledge/candidates/` avant suppression du worktree ; - retire le worktree possédé après succès et tente une suppression forcée après une erreur survenue dans ce worktree ; - refuse un worktree `candidate` préexistant au lieu de le supprimer silencieusement ; - ajoute des tests d'intégration Git réels pour le candidat et l'échappement par symlink. `alpha.3` ne met à jour aucun remote et n'implémente pas encore `GitRepositoryWriter` sur `GitBackend`, car ce trait exige également `push_candidate`. L'implémentation complète du trait et le compare-and-exchange distant restent réservés à `alpha.4`. ## Scope des fichiers REPLACE : ```text VERSION Cargo.toml README.md crates/knowledge-git-lib/src/backend.rs crates/knowledge-git-lib/src/error.rs crates/knowledge-git-lib/src/lib.rs crates/knowledge-git-lib/unit_tests/backend.rs docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md docs/validation/001-VALIDATION_MATRIX.md ``` ADD : ```text crates/knowledge-git-lib/src/write.rs crates/knowledge-git-lib/tests/git_write_candidate.rs deltas/0.0.3/alpha.3.md ``` DELETE : aucun. `knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` et `ROADMAP.md` ne sont pas modifiés. Aucun history `alpha.3` n'est inclus dans ce delta. ## Contrat backend matérialisé Le chemin local devient : ```text GitRevision de base -> mirror contrôlé -> write-worktrees//candidate -> mutations confinées -> git add limité aux chemins demandés -> commit avec identité explicite -> vérification HEAD / HEAD^1 -> refs/knowledge/candidates/ -> suppression du worktree temporaire -> GitRevision candidate ``` La racine d'état contrôlée contient désormais également : ```text empty-global.gitconfig write-worktrees/ ``` `empty-global.gitconfig` neutralise la configuration utilisateur globale pour les commandes possédées par le backend ; `GIT_CONFIG_NOSYSTEM=1` neutralise la configuration système. La configuration repository nécessaire au mirror reste disponible, tandis que `core.hooksPath` est forcé vers `hooks-disabled`. ## Confinement filesystem `GitMutationPath` fournit déjà le confinement lexical. `alpha.3` ajoute le contrôle physique : avant toute écriture ou suppression, chaque composant existant du chemin sous le worktree est inspecté avec `symlink_metadata`. Un symlink rencontré produit `UnsafeMutationTarget`. Une suppression d'une cible absente produit `MutationTargetMissing`. Les erreurs filesystem restent récupérables et ne contiennent pas le chemin ou le contenu sensible dans leur texte public. Un worktree préexistant à l'emplacement contrôlé produit `WriteWorktreeExists`. Il n'est pas supprimé automatiquement. ## Candidate et nettoyage Une candidate n'est valide que si : - les chemins réellement stagés correspondent exactement aux mutations demandées ; - le commit est créé avec l'identité explicite de la requête ; - `HEAD` est une révision Git sûre ; - `HEAD^1` est exactement la base demandée ; - une référence interne `refs/knowledge/candidates/` conserve l'objet avant retrait du worktree. Le retrait normal n'utilise pas `--force`. Si une erreur survient après création du worktree possédé par l'opération, le backend utilise `worktree remove --force` pour retirer uniquement cet état temporaire connu. Un échec de nettoyage devient `WriteCleanupFailed` afin qu'un état inattendu ne soit pas masqué. ## Gate opérateur Exécuter depuis la racine du dépôt après application du delta : ```bash test "$(cat VERSION)" = '0.0.3-alpha.3' grep -F 'version = "0.0.3-alpha.3"' Cargo.toml git diff --check python3 -B scripts/audit_markdown_headers.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history python3 -B scripts/audit_markdown_links.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history python3 -B scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history cargo fmt --all cargo fmt --all -- --check 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_crate_root_facades.py crates python3 -B scripts/audit_rust_export_completeness.py --root . --summary-only cargo metadata --format-version 1 --no-deps > /tmp/sasedev-knowledge-0.0.3-alpha.3-metadata.json cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test -p knowledge-git-lib --test git_read_path cargo test -p knowledge-git-lib --test git_write_candidate cargo test --workspace --all-targets --all-features python3 - <<'PYGATE' from pathlib import Path history = Path('history/0.0.3/alpha.2.md').read_text(encoding='utf-8') assert '\nPASS\n' in history write = Path('crates/knowledge-git-lib/src/write.rs').read_text(encoding='utf-8') backend = Path('crates/knowledge-git-lib/src/backend.rs').read_text(encoding='utf-8') errors = Path('crates/knowledge-git-lib/src/error.rs').read_text(encoding='utf-8') api = Path('crates/knowledge-git-api/src/write.rs').read_text(encoding='utf-8') plan = Path('docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md').read_text(encoding='utf-8') changelog = Path('CHANGELOG.md').read_text(encoding='utf-8') for required in ( 'pub fn create_candidate(', 'symlink_metadata', 'CandidateMutationMismatch', 'GIT_AUTHOR_NAME', 'GIT_COMMITTER_NAME', '--no-verify', '--no-gpg-sign', 'HEAD^1', 'refs/knowledge/candidates/', 'RemoveWriteWorktree', ): assert required in write, required assert 'GIT_CONFIG_GLOBAL' in backend assert 'GIT_CONFIG_NOSYSTEM' in backend assert 'core.hooksPath' in backend assert 'write_worktrees_path' in backend assert 'empty_global_config_path' in backend for required_error in ( 'WriteWorktreeExists', 'UnsafeMutationTarget', 'MutationTargetMissing', 'MutationFilesystemFailed', 'CandidateMutationMismatch', 'InvalidCandidate', 'WriteCleanupFailed', ): assert required_error in errors, required_error assert 'trait GitRepositoryWriter' in api assert 'fn create_candidate(' in api assert 'fn push_candidate(' in api assert 'impl knowledge_git_api::GitRepositoryWriter for crate::GitBackend' not in write assert 'GitPushRequest' not in write assert 'push_candidate' not in write assert '`alpha.2` a ensuite matérialisé le contrat d\'écriture provider-neutral et sa gate opérateur est `PASS`' in plan assert '## 0.0.3' not in changelog assert not Path('history/0.0.3/alpha.3.md').exists() assert not Path('crates/knowledge-forge-gitea-lib').exists() assert not Path('crates/knowledge-store-postgres-lib').exists() assert not Path('migrations').exists() print('Knowledge 0.0.3-alpha.3 candidate-worktree audit: clean') PYGATE python3 - <<'PYTEXT' from pathlib import Path roots = [Path('README.md'), Path('RULES.md'), Path('ROADMAP.md'), Path('CHANGELOG.md'), Path('docs'), Path('prompts'), Path('deltas'), Path('history')] paths = [] for root in roots: if root.is_file(): paths.append(root) elif root.is_dir(): paths.extend(path for path in root.rglob('*') if path.is_file()) for path in paths: data = path.read_bytes() assert b'\r' not in data, path assert data.endswith(b'\n'), path assert not data.endswith(b'\n\n'), path text = data.decode('utf-8') for number, line in enumerate(text.splitlines(), start=1): assert line == line.rstrip(' \t'), (path, number) print(f'Knowledge 0.0.3-alpha.3 text hygiene: clean ({len(paths)} file(s))') PYTEXT test -z "$(git diff --name-only -- crates/knowledge-git-api crates/knowledge-app-lib Web CHANGELOG.md ROADMAP.md)" test ! -e history/0.0.3/alpha.3.md 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 -f Web/knowledge-template-lab/package-lock.json git add -A git diff --cached --check git status --short ``` La gate ne rejoue pas npm/Vite/HTTP : aucun fichier `Web/`, launcher ou contrat runtime Web n'est modifié. ## Transition ### PASS Produire `history/0.0.3/alpha.3.md`, l'appliquer, commit/push `0.0.3-alpha.3`, puis préparer `0.0.3-alpha.4` pour l'observation distante, le compare-and-exchange et le push gardé avec conflits explicites. ### FAIL / PARTIAL / BLOCKED Conserver le résultat réel dans son history avant toute correction. Une correction bornée utilise `alpha.3.fix.N` ; une remise en cause du cycle de vie des worktrees, de la frontière provider-neutral ou de la stratégie process Git exige un `REPLAN` explicite.