0.0.2-alpha.2

This commit is contained in:
2026-09-28 16:16:55 +02:00
parent 84c01eb8e8
commit bdbd6a51a4
15 changed files with 1368 additions and 72 deletions

324
deltas/0.0.2/alpha.2.md Normal file
View File

@@ -0,0 +1,324 @@
<!-- file: deltas/0.0.2/alpha.2.md -->
<!-- version: 1 -->
# Delta 0.0.2-alpha.2 — contrats minimaux et backend process Git
## Base requise
Partir de `0.0.2-alpha.1.fix.1` après application de son history `PASS`, commit et push :
```text
history/0.0.2/alpha.1.md FAIL
history/0.0.2/alpha.1.fix.1.md PASS
```
Le workspace doit être propre avant application du présent delta.
La version devient :
```text
0.0.2-alpha.2
```
## Objet
Matérialiser la première responsabilité runtime Git réelle sans anticiper la tranche suivante :
- créer `knowledge-git-lib` comme crate d'implémentation réutilisable ;
- posséder une racine d'état explicitement fournie au backend ;
- posséder la configuration physique d'une source locale ou remote ;
- dériver une `source-key` sûre sans interpoler directement `SourceId` ou URL dans le filesystem ;
- encapsuler `std::process::Command` derrière un runner interne testable, sans shell ;
- préparer les répertoires `hooks-disabled/`, `repositories/` et `snapshots/` ;
- initialiser un mirror avec `git clone --mirror`, et `--no-local` pour une source locale ;
- inspecter un mirror existant comme repository bare avant réutilisation ;
- ne pas supprimer/recréer silencieusement un mirror invalide ;
- ne pas exposer URL, arguments, stderr ou credentials dans les erreurs publiques du runner.
`refresh`, résolution vers commit et snapshot/worktree fonctionnel restent réservés à `alpha.3`.
## Scope
ADD :
```text
crates/knowledge-git-lib/Cargo.toml
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
deltas/0.0.2/alpha.2.md
```
REPLACE :
```text
Cargo.toml
README.md
RULES.md
VERSION
docs/architecture/001-PROJECT_PROFILE.md
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md
docs/validation/001-VALIDATION_MATRIX.md
```
DELETE : aucun.
Le history `history/0.0.2/alpha.1.fix.1.md` appartient à la base requise mais n'est pas inclus dans ce delta.
Aucun fichier sous `Web/` n'est modifié.
## Contrat de `knowledge-git-lib`
La crate dépend uniquement de `knowledge-domain-lib` dans cette tranche.
Elle expose :
```text
GitBackend
GitBackendConfig
GitSourceConfig
GitSourceKind
GitSourceKey
GitMirror
GitMirrorStatus
GitBackendError
GitOperation
```
`knowledge-git-api` reste inchangée : son trait `GitRepository` possède déjà `refresh` et `resolve_revision`, qui ne doivent pas être implémentés artificiellement avant la tranche qui matérialise ces comportements.
La dépendance et l'implémentation explicite de ce trait seront introduites lorsque le backend possédera effectivement les opérations requises.
## Layout d'état
Une configuration valide possède obligatoirement une racine d'état non vide :
```text
<git-state-root>/
hooks-disabled/
repositories/
<source-key>.git/
snapshots/
```
Le répertoire `snapshots/` est réservé dès maintenant par le layout mais aucun snapshot n'est créé dans `alpha.2`.
La `source-key` est obtenue par encodage hexadécimal déterministe des octets UTF-8 du `SourceId`, préfixé par `source-`. Cette stratégie évite les collisions entre octets distincts et interdit qu'un séparateur de chemin, `..`, une URL ou un credential provenant de l'entrée logique devienne un composant de chemin brut.
## Runner Git contrôlé
Le runner système :
- utilise `std::process::Command` directement ;
- passe chaque argument séparément ;
- pose `GIT_TERMINAL_PROMPT=0` ;
- retire les variables `GIT_*` qui peuvent rediriger implicitement repository, worktree, index ou object database ;
- force `core.hooksPath` vers le répertoire vide contrôlé via l'environnement de configuration Git ;
- transforme un échec de spawn ou exit non nul en erreur récupérable ;
- ne copie jamais arguments, source, stdout/stderr ou credentials dans l'erreur publique.
Les sources locales utilisent :
```text
git clone --mirror --no-local -- <source> <mirror>
```
Les sources remote utilisent :
```text
git clone --mirror -- <source> <mirror>
```
Un mirror existant est inspecté avec :
```text
git --git-dir <mirror> rev-parse --is-bare-repository
```
Une réponse différente de `true` produit une erreur sans suppression destructive.
## Tests de la tranche
Les tests unitaires de `knowledge-git-lib` utilisent un runner enregistré injecté uniquement sous `cfg(test)` pour vérifier sans réseau :
- rejet des chemins de configuration vides ;
- dérivation sûre de `source-key` ;
- arguments exacts de clone local ;
- absence de `--no-local` pour un remote ;
- redaction des erreurs contenant potentiellement une URL avec credentials ;
- inspection d'un mirror existant sans clone ;
- refus d'un mirror invalide sans suppression.
Les tests réels sur repositories Git temporaires appartiennent à `alpha.3`, avec refresh, résolution et snapshot.
## Gate opérateur
Après application du history `alpha.1.fix.1`, commit/push de cet état, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.2-alpha.2'
grep -F 'version = "0.0.2-alpha.2"' Cargo.toml
grep -A2 '^## Statut$' history/0.0.2/alpha.1.fix.1.md | grep -Fx 'PASS'
git diff --check
python3 - <<'PY'
from pathlib import Path
import re
import tomllib
required = (
"crates/knowledge-git-lib/Cargo.toml",
"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",
"deltas/0.0.2/alpha.2.md",
)
for value in required:
assert Path(value).is_file(), value
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 "crates/knowledge-git-lib" in root["workspace"]["members"]
assert set(manifest["dependencies"]) == {"knowledge-domain-lib"}, manifest["dependencies"]
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")
for phrase in (
"std::process::Command::new",
'command.env("GIT_TERMINAL_PROMPT", "0")',
'command.env_remove("GIT_DIR")',
'command.env_remove("GIT_WORK_TREE")',
'command.env_remove("GIT_INDEX_FILE")',
'command.env_remove("GIT_OBJECT_DIRECTORY")',
'command.env_remove("GIT_ALTERNATE_OBJECT_DIRECTORIES")',
'command.env("GIT_CONFIG_KEY_0", "core.hooksPath")',
'std::ffi::OsString::from("--mirror")',
'std::ffi::OsString::from("--no-local")',
'std::ffi::OsString::from("--")',
'std::ffi::OsString::from("--is-bare-repository")',
):
assert phrase in backend, phrase
for forbidden in (
'Command::new("sh")',
'Command::new("bash")',
'Command::new("cmd")',
'Command::new("powershell")',
"stderr: String",
"arguments: Vec<String>",
):
assert forbidden not in backend + error, forbidden
for forbidden in ("refresh(", "resolve_revision(", "worktree add", "knowledge-app-lib"):
assert forbidden not in backend, forbidden
for path in Path("crates/knowledge-git-lib").rglob("*.rs"):
text = path.read_text(encoding="utf-8")
for forbidden in ("unwrap()", "expect(", "panic!("):
assert forbidden not in text, (path, forbidden)
for path in Path(".").rglob("*.md"):
if ".git" in path.parts:
continue
lines = path.read_text(encoding="utf-8").splitlines()
assert lines[0] == f"<!-- file: {path.as_posix()} -->", path
assert re.fullmatch(r"<!-- version: [1-9][0-9]* -->", lines[1]), path
print("Knowledge 0.0.2-alpha.2 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
cargo fmt --all -- --check
cargo metadata --format-version 1 --no-deps > /tmp/sasedev-knowledge-0.0.2-alpha.2-metadata.json
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p knowledge-git-lib --all-targets --all-features
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.2-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.2 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-git-api crates/knowledge-sync-api)"
test ! -e crates/knowledge-app-lib
test ! -e crates/knowledge-forge-gitea-lib
test ! -e crates/knowledge-store-postgres-lib
git add -A
git diff --cached --check
git status --short
```
## Résultats attendus
La gate doit notamment produire :
```text
Knowledge 0.0.2-alpha.2 structural audit: clean
Rust test-layout audit: clean (8 crate(s))
Rust export completeness audit: 0 candidate(s) across 8 package(s)
Knowledge 0.0.2-alpha.2 Cargo metadata audit: clean
```
La suite ciblée `knowledge-git-lib` doit exécuter `7` tests unitaires `PASS`.
`git diff --name-only -- Web` et le diff ciblant les deux crates `-api` doivent rester vides.
Le staging final doit contenir uniquement le scope annoncé par ce delta. Aucun `history/0.0.2/alpha.2.md` ne doit exister avant réception des résultats opérateur.
## Revue opérateur complémentaire
Vérifier que :
- `knowledge-git-lib` possède bien une responsabilité d'implémentation et n'est pas une crate placeholder ;
- le runner ne passe jamais par un shell ;
- les erreurs process restent exploitables sans recopier des informations sensibles ;
- le layout généré reste reconstructible et distinct du stockage métier ;
- `knowledge-git-api` n'est pas artificiellement étendue pour des opérations qui n'existent pas encore ;
- `alpha.3` reste propriétaire du refresh, de la résolution et du snapshot réel.
## Transition
### PASS
Produire `history/0.0.2/alpha.2.md`, appliquer ce history, committer/pousser `0.0.2-alpha.2`, puis seulement préparer `0.0.2-alpha.3`.
### FAIL / PARTIAL / BLOCKED
Conserver le résultat réel dans `history/0.0.2/alpha.2.md` et rester dans le scope `alpha.2` avec `alpha.2.fix.1` si nécessaire, sauf déclencheur de REPLAN explicitement satisfait.