Files
sasedev-knowledge/deltas/0.0.2/alpha.2.md
2026-09-28 16:16:55 +02:00

11 KiB

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 :

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 :

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 :

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 :

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 :

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 :

<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 :

git clone --mirror --no-local -- <source> <mirror>

Les sources remote utilisent :

git clone --mirror -- <source> <mirror>

Un mirror existant est inspecté avec :

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 :

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 :

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.