0.0.2-alpha.3

This commit is contained in:
2026-09-28 19:23:12 +02:00
parent 38025266b9
commit 721e8a6d9c
18 changed files with 1103 additions and 38 deletions

298
deltas/0.0.2/alpha.3.md Normal file
View File

@@ -0,0 +1,298 @@
<!-- file: deltas/0.0.2/alpha.3.md -->
<!-- version: 1 -->
# 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 <mirror> 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 <mirror> rev-parse --verify --end-of-options <reference>^{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
<state-root>/snapshots/<source-key>/<resolved-revision>/
```
Un snapshot absent est créé par :
```text
git --git-dir <mirror> worktree add --detach -- <snapshot-path> <resolved-revision>
```
Un snapshot existant n'est réutilisé qu'après :
```text
git -C <snapshot> rev-parse --verify HEAD
git -C <snapshot> 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`.