0.0.2-alpha.2.fix.1

This commit is contained in:
2026-09-28 16:25:25 +02:00
parent bdbd6a51a4
commit ffb47214af
10 changed files with 528 additions and 81 deletions

View File

@@ -1,5 +1,5 @@
# file: Cargo.toml
# version: 19
# version: 20
[workspace]
resolver = "3"
@@ -15,7 +15,7 @@ members = [
]
[workspace.package]
version = "0.0.2-alpha.2"
version = "0.0.2-alpha.2.fix.1"
edition = "2024"
publish = false
repository = "https://git.sasedev.com/Sasedev/sasedev-knowledge"

View File

@@ -1 +1 @@
0.0.2-alpha.2
0.0.2-alpha.2.fix.1

View File

@@ -1,5 +1,5 @@
// file: crates/knowledge-git-lib/src/backend.rs
// version: 1
// version: 2
//! Physical source configuration, generated-state layout and controlled Git process runner.
@@ -17,10 +17,7 @@ impl crate::GitBackendConfig {
}
/// Build a backend configuration with an explicit Git executable path.
pub fn with_git_executable(
state_root: std::path::PathBuf,
git_executable: std::path::PathBuf,
) -> Result<crate::GitBackendConfig, crate::GitBackendError> {
pub fn with_git_executable(state_root: std::path::PathBuf, git_executable: std::path::PathBuf) -> Result<crate::GitBackendConfig, crate::GitBackendError> {
if state_root.as_os_str().is_empty() {
return Err(crate::GitBackendError::EmptyStateRoot);
}
@@ -75,18 +72,11 @@ pub struct GitSourceConfig {
impl crate::GitSourceConfig {
/// Build a local Git source configuration.
pub fn local(
source_id: knowledge_domain_lib::SourceId,
path: std::path::PathBuf,
) -> Result<crate::GitSourceConfig, crate::GitBackendError> {
pub fn local(source_id: knowledge_domain_lib::SourceId, path: std::path::PathBuf) -> Result<crate::GitSourceConfig, crate::GitBackendError> {
if path.as_os_str().is_empty() {
return Err(crate::GitBackendError::EmptySourceLocation);
}
return Ok(crate::GitSourceConfig {
location: path.into_os_string(),
source_id,
source_kind: crate::GitSourceKind::Local,
});
return Ok(crate::GitSourceConfig { location: path.into_os_string(), source_id, source_kind: crate::GitSourceKind::Local });
}
/// Build a remote Git source configuration without interpreting provider-specific syntax.
@@ -94,11 +84,7 @@ impl crate::GitSourceConfig {
if location.trim().is_empty() {
return Err(crate::GitBackendError::EmptySourceLocation);
}
return Ok(crate::GitSourceConfig {
location: std::ffi::OsString::from(location),
source_id,
source_kind: crate::GitSourceKind::Remote,
});
return Ok(crate::GitSourceConfig { location: std::ffi::OsString::from(location), source_id, source_kind: crate::GitSourceKind::Remote });
}
/// Return the logical source identifier associated with this physical source.
@@ -123,7 +109,7 @@ impl crate::GitSourceKey {
for byte in source_id.as_str().as_bytes() {
value.push_str(format!("{:02x}", *byte).as_str());
}
return crate::GitSourceKey(value);
return Self(value);
}
/// Return the path-safe key text.
@@ -204,16 +190,11 @@ impl GitCommandRunner for SystemGitRunner {
let output = match output_result {
Ok(value) => value,
Err(_) => {
return Err(crate::GitBackendError::GitUnavailable {
operation: specification.operation,
});
}
return Err(crate::GitBackendError::GitUnavailable { operation: specification.operation });
},
};
if !output.status.success() {
return Err(crate::GitBackendError::GitCommandFailed {
operation: specification.operation,
exit_code: output.status.code(),
});
return Err(crate::GitBackendError::GitCommandFailed { operation: specification.operation, exit_code: output.status.code() });
}
return Ok(GitCommandOutput { stdout: output.stdout });
}
@@ -228,10 +209,7 @@ pub struct GitBackend {
impl crate::GitBackend {
/// Create a backend using the configured real Git executable.
pub fn new(config: crate::GitBackendConfig) -> crate::GitBackend {
return crate::GitBackend {
config,
runner: Box::new(SystemGitRunner),
};
return crate::GitBackend { config, runner: Box::new(SystemGitRunner) };
}
#[cfg(test)]
@@ -260,12 +238,8 @@ impl crate::GitBackend {
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(Some(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Existing,
}));
}
return Ok(Some(crate::GitMirror { repository_path, source_key, status: crate::GitMirrorStatus::Existing }));
},
Err(error) => return Err(error),
}
}
@@ -283,12 +257,8 @@ impl crate::GitBackend {
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Existing,
});
}
return Ok(crate::GitMirror { repository_path, source_key, status: crate::GitMirrorStatus::Existing });
},
Err(error) => return Err(error),
}
}
@@ -300,12 +270,8 @@ impl crate::GitBackend {
let inspection_result = self.inspect_bare_repository(&repository_path);
match inspection_result {
Ok(()) => {
return Ok(crate::GitMirror {
repository_path,
source_key,
status: crate::GitMirrorStatus::Created,
});
}
return Ok(crate::GitMirror { repository_path, source_key, status: crate::GitMirrorStatus::Created });
},
Err(error) => return Err(error),
}
}
@@ -362,10 +328,8 @@ impl crate::GitBackend {
let stdout = match stdout_result {
Ok(value) => value.trim(),
Err(_) => {
return Err(crate::GitBackendError::UnexpectedGitOutput {
operation: crate::GitOperation::InspectMirror,
});
}
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectMirror });
},
};
if stdout != "true" {
return Err(crate::GitBackendError::InvalidMirror);

View File

@@ -1,5 +1,5 @@
// file: crates/knowledge-git-lib/src/error.rs
// version: 1
// version: 2
//! Recoverable errors for controlled Git process execution.
@@ -66,11 +66,11 @@ impl std::fmt::Display for crate::GitBackendError {
crate::GitBackendError::GitUnavailable { operation } => return write!(formatter, "git is unavailable while attempting to {operation}"),
crate::GitBackendError::GitCommandFailed { operation, exit_code } => {
return write!(formatter, "git failed while attempting to {operation}; exit code {exit_code:?}");
}
},
crate::GitBackendError::InvalidMirror => return formatter.write_str("controlled git repository is not a valid bare mirror"),
crate::GitBackendError::UnexpectedGitOutput { operation } => {
return write!(formatter, "git returned unexpected output while attempting to {operation}");
}
},
}
}
}

View File

@@ -1,5 +1,5 @@
// file: crates/knowledge-git-lib/src/lib.rs
// version: 1
// version: 2
//! Controlled Git process backend for Knowledge content sources.
//!
@@ -17,10 +17,10 @@ pub use self::backend::GitBackendConfig;
pub use self::backend::GitMirror;
/// Result of ensuring that a controlled mirror exists.
pub use self::backend::GitMirrorStatus;
/// Path-safe storage key derived from a logical Knowledge source identifier.
pub use self::backend::GitSourceKey;
/// Physical Git source configuration associated with one logical source identifier.
pub use self::backend::GitSourceConfig;
/// Path-safe storage key derived from a logical Knowledge source identifier.
pub use self::backend::GitSourceKey;
/// Coarse physical source kind used by the backend.
pub use self::backend::GitSourceKind;
/// Recoverable error returned by the controlled Git backend.

View File

@@ -1,5 +1,5 @@
// file: crates/knowledge-git-lib/unit_tests/backend.rs
// version: 1
// version: 2
#[derive(Clone)]
struct RecordingRunner {
@@ -41,9 +41,7 @@ impl super::GitCommandRunner for RecordingRunner {
return Err(error);
}
if specification.operation == crate::GitOperation::InspectMirror {
return Ok(super::GitCommandOutput {
stdout: self.inspection_stdout.clone(),
});
return Ok(super::GitCommandOutput { stdout: self.inspection_stdout.clone() });
}
return Ok(super::GitCommandOutput { stdout: Vec::new() });
}
@@ -180,10 +178,7 @@ fn process_errors_do_not_expose_remote_location_or_credentials() {
Ok(value) => value,
Err(_) => return,
};
let runner = RecordingRunner::failing(crate::GitBackendError::GitCommandFailed {
operation: crate::GitOperation::CloneMirror,
exit_code: Some(128),
});
let runner = RecordingRunner::failing(crate::GitBackendError::GitCommandFailed { operation: crate::GitOperation::CloneMirror, exit_code: Some(128) });
let backend = crate::GitBackend::with_runner(config, Box::new(runner));
let source_id_result = source_id("snippets");
assert!(source_id_result.is_some());
@@ -206,13 +201,7 @@ fn process_errors_do_not_expose_remote_location_or_credentials() {
let display = error.to_string();
assert!(!display.contains("secret"));
assert!(!display.contains("example.invalid"));
assert_eq!(
error,
crate::GitBackendError::GitCommandFailed {
operation: crate::GitOperation::CloneMirror,
exit_code: Some(128),
}
);
assert_eq!(error, crate::GitBackendError::GitCommandFailed { operation: crate::GitOperation::CloneMirror, exit_code: Some(128) });
let _ = std::fs::remove_dir_all(root);
return;
}

View File

@@ -0,0 +1,280 @@
<!-- file: deltas/0.0.2/alpha.2.fix.1.md -->
<!-- version: 1 -->
# Delta 0.0.2-alpha.2.fix.1 — correction de gate, formatage et construction interne
## Base requise
Partir de `0.0.2-alpha.2` après application et commit du history réel :
```text
deltas/0.0.2/alpha.2.md présent et historique
history/0.0.2/alpha.2.md FAIL
```
Le delta `alpha.2` historique ne doit pas être réécrit.
La version devient :
```text
0.0.2-alpha.2.fix.1
```
## Objet
Corriger uniquement les défauts révélés par la gate opérateur d'`alpha.2` :
- supprimer la ligne vide terminale détectée par `git diff --check` dans la matrice de validation ;
- empêcher l'audit Markdown embarqué de traiter `node_modules` ou d'autres arbres générés comme contenu gouverné ;
- appliquer le formatage attendu par `cargo fmt` aux quatre fichiers Rust signalés ;
- construire `GitSourceKey` depuis son module propriétaire au lieu de tenter d'utiliser son constructeur tuple privé via le réexport `crate::GitSourceKey` ;
- conserver le champ tuple privé et ne pas élargir l'API publique ;
- documenter le `FAIL` d'`alpha.2` et la portée strictement corrective de ce fix.
Le fix n'ajoute aucune capacité Git. `refresh`, résolution vers commit et snapshot/worktree restent réservés à `alpha.3`.
## Scope
REPLACE :
```text
VERSION
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
docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md
docs/validation/001-VALIDATION_MATRIX.md
```
ADD :
```text
deltas/0.0.2/alpha.2.fix.1.md
```
DELETE : aucun.
Le history `history/0.0.2/alpha.2.md` appartient à la base requise mais n'est pas inclus dans ce delta.
Aucun fichier sous `Web/`, `crates/knowledge-git-api/` ou `crates/knowledge-sync-api/` n'est modifié.
## Corrections attendues
### Hygiène documentaire
`docs/validation/001-VALIDATION_MATRIX.md` doit terminer par une seule fin de ligne, sans ligne vide additionnelle produisant un warning `git diff --check`.
La gate du fix contrôle les Markdown du workspace mais ignore explicitement les arbres générés/non gouvernés :
```text
.git
node_modules
target
builds
```
La présence d'un README provenant d'une dépendance npm ne doit donc plus faire échouer la gouvernance documentaire du projet.
### Rustfmt
Les fichiers signalés par la gate initiale sont remis dans la forme attendue par `rustfmt.toml` :
```text
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
```
Aucun changement fonctionnel autre que la correction de visibilité ci-dessous n'est introduit.
### Construction de `GitSourceKey`
`GitSourceKey` conserve son champ tuple privé :
```text
pub struct GitSourceKey(String);
```
Sa méthode `from_source_id`, définie dans le module `backend`, construit le type avec `Self(value)` depuis son propre `impl`. Le réexport public reste destiné à nommer le type, pas à contourner la visibilité de son champ interne.
## Gate opérateur
Après application du history `alpha.2`, commit/push de cet état, puis application du présent delta :
```bash
test "$(cat VERSION)" = '0.0.2-alpha.2.fix.1'
grep -F 'version = "0.0.2-alpha.2.fix.1"' Cargo.toml
grep -A2 '^## Statut$' history/0.0.2/alpha.2.md | grep -Fx 'FAIL'
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",
"deltas/0.0.2/alpha.2.fix.1.md",
"history/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")
assert "return Self(value);" in backend
assert "return crate::GitSourceKey(value);" not in backend
assert "return crate::backend::GitSourceKey(value);" not in backend
assert "pub struct GitSourceKey(String);" in backend
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>",
"refresh(",
"resolve_revision(",
"worktree add",
"knowledge-app-lib",
):
assert forbidden not in backend + error, 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)
ignored_parts = {".git", "node_modules", "target", "builds"}
checked_markdown = 0
for path in Path(".").rglob("*.md"):
if any(part in ignored_parts for part 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
checked_markdown += 1
assert checked_markdown > 0
print(f"Knowledge 0.0.2-alpha.2.fix.1 structural audit: clean ({checked_markdown} Markdown file(s))")
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.fix.1-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.fix.1-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.fix.1 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.fix.1 structural audit: clean (... Markdown file(s))
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.fix.1 Cargo metadata audit: clean
```
`cargo fmt --all -- --check` doit être silencieux et `cargo check`, Clippy et les deux suites de tests doivent tous terminer avec succès.
La suite ciblée `knowledge-git-lib` doit exécuter `7` tests unitaires `PASS`, sauf modification explicitement justifiée du nombre de tests dans un fix ultérieur.
## Revue opérateur complémentaire
Vérifier que :
- la correction de visibilité n'a pas rendu public l'état interne de `GitSourceKey` ;
- l'audit Markdown ne dépend plus de l'absence physique de `node_modules` ;
- aucun comportement de `refresh`, résolution ou snapshot n'est apparu dans le fix ;
- aucune nouvelle dépendance Cargo n'a été introduite ;
- le fix ne change pas l'architecture validée d'`alpha.2`.
## Transition
### PASS
Produire `history/0.0.2/alpha.2.fix.1.md`, appliquer ce history, committer/pousser le fix, 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.fix.1.md` et rester dans le scope `alpha.2` avec `alpha.2.fix.2` si nécessaire, sauf déclencheur de REPLAN explicitement satisfait.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# Plan 0.0.2 — Git générique et synchronisation de lecture
@@ -139,7 +139,9 @@ La gate opérateur de `alpha.1.fix.1` est **PASS**.
### alpha.2 — contrats minimaux et backend process
Statut opérateur : **à valider**.
Statut opérateur initial : **FAIL**.
`alpha.2.fix.1` corrige exclusivement l'hygiène de fin de fichier, la portée de la gate Markdown, le formatage rustfmt et la construction interne de `GitSourceKey`. Il n'ajoute aucune capacité Git et ne modifie ni les dépendances ni les frontières de la tranche.
Le delta matérialise le premier backend réel sans encore prétendre couvrir tout le cycle de synchronisation. Les choix concrets de la tranche sont : configuration publique `GitBackendConfig`/`GitSourceConfig`, runner process strictement interne, erreurs publiques redacted par construction et `source-key` hexadécimale déterministe dérivée du `SourceId`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
<!-- version: 20 -->
<!-- version: 21 -->
# Matrice de validation Knowledge
@@ -572,3 +572,22 @@ La première tranche fonctionnelle Git doit prouver :
Le `PASS` d'`alpha.2` autorise uniquement la préparation de `0.0.2-alpha.3`, consacrée au refresh réel, à la résolution et au snapshot sur repositories Git temporaires.
## Gate de `0.0.2-alpha.2.fix.1`
Le fix reste strictement dans le scope `alpha.2` et doit prouver :
- `VERSION = 0.0.2-alpha.2.fix.1` et `[workspace.package].version` identique ;
- history `history/0.0.2/alpha.2.md` présent avec statut réel `FAIL` et committé avant application du fix ;
- delta historique `deltas/0.0.2/alpha.2.md` inchangé ;
- suppression du défaut `git diff --check` sur la matrice de validation ;
- audit Markdown limité au contenu gouverné et ignorant notamment `.git`, `node_modules`, `target` et autres arbres générés ;
- correction de la construction interne de `GitSourceKey` sans rendre son champ tuple public ;
- formatage rustfmt propre des fichiers de `knowledge-git-lib` ;
- huit packages Cargo exactement et dépendances de `knowledge-git-lib` inchangées ;
- absence de `refresh`, résolution de révision, worktree/snapshot, forge, PostgreSQL, scheduler ou `knowledge-app-lib` ;
- audits Rust, `cargo metadata`, `cargo check`, Clippy strict, tests ciblés `knowledge-git-lib` et tests workspace propres ;
- `Web/`, `knowledge-git-api` et `knowledge-sync-api` sans diff ;
- `Cargo.lock` toujours ignoré/non suivi ;
- `git diff --cached --check` propre avant commit.
Le `PASS` du fix ferme `alpha.2` et autorise uniquement la préparation de `0.0.2-alpha.3`.

View File

@@ -0,0 +1,193 @@
<!-- file: history/0.0.2/alpha.2.fix.1.md -->
<!-- version: 1 -->
# History 0.0.2-alpha.2.fix.1 — correction de gate, formatage et construction interne
## Statut
FAIL
La gate opérateur de `0.0.2-alpha.2.fix.1` exécutée le `2026-09-28` corrige les défauts initiaux d'`alpha.2`, mais reste bloquée par une violation Clippy unique dans un test de `knowledge-git-lib`.
## Baseline opérateur
`alpha.2` `FAIL` a été historisé, committé et poussé avant application du fix :
```text
history/0.0.2/alpha.2.md FAIL
commit bdbd6a5
message 0.0.2-alpha.2
branch master
push origin/master PASS
workspace après push propre
```
Le push observé a avancé `origin/master` de `84c01eb` à `bdbd6a5`.
## Version et prérequis
Les contrôles passent :
```text
VERSION 0.0.2-alpha.2.fix.1
Cargo workspace version 0.0.2-alpha.2.fix.1
history alpha.2 FAIL
```
`git diff --check` est propre.
## Audit structurel et documentaire
L'audit corrigé passe :
```text
Knowledge 0.0.2-alpha.2.fix.1 structural audit: clean (61 Markdown file(s))
```
Il confirme notamment :
- la présence des artefacts attendus de `knowledge-git-lib` ;
- huit membres workspace avec dépendances minimales inchangées ;
- `GitSourceKey` construit via `Self(value)` tout en conservant son champ tuple privé ;
- le runner process sans shell, les protections d'environnement Git et les arguments `--mirror` / `--no-local` ;
- l'absence de `refresh`, `resolve_revision`, worktree/snapshot et `knowledge-app-lib` ;
- l'exclusion de `.git`, `node_modules`, `target` et `builds` de l'audit Markdown gouverné.
Les audits Rust structurels passent :
```text
Rust test-layout audit: clean (8 crate(s))
Rust blank-line audit: clean (22 file(s))
Rust item-spacing audit: clean (22 file(s))
Rust module-order audit: clean (22 file(s))
Rust trait-import-block audit: clean (22 file(s))
Rust re-export-block audit: clean (22 file(s))
Rust export completeness audit: 0 candidate(s) across 8 package(s)
```
## Formatage et compilation
Les contrôles suivants passent :
```text
cargo fmt --all -- --check PASS
cargo metadata --format-version 1 --no-deps PASS
cargo check --workspace PASS
```
La compilation du workspace est donc restaurée par rapport à `alpha.2`.
## Clippy
La gate reste `FAIL` sur :
```text
cargo clippy --workspace --all-targets --all-features -- -D warnings
```
Erreur observée :
```text
crates/knowledge-git-lib/unit_tests/backend.rs:85
missing `return` statement
clippy::implicit_return
```
La closure passée à `Iterator::all` retourne implicitement son expression booléenne :
```text
|character| character.is_ascii_alphanumeric() || character == '-'
```
La configuration workspace interdit explicitement `clippy::implicit_return`. Le correctif attendu est uniquement de rendre ce retour explicite dans la closure.
## Tests
Malgré l'échec Clippy, les tests ciblés passent :
```text
cargo test -p knowledge-git-lib --all-targets --all-features
7 passed; 0 failed
```
Tests observés :
```text
backend_configuration_requires_non_empty_paths PASS
local_source_clone_uses_mirror_no_local_and_argument_separator PASS
invalid_existing_mirror_is_rejected_without_deleting_it PASS
existing_mirror_is_inspected_without_clone PASS
source_key_never_interpolates_raw_source_identifier_as_a_path PASS
process_errors_do_not_expose_remote_location_or_credentials PASS
remote_source_clone_does_not_add_no_local PASS
```
Les tests workspace passent également :
```text
knowledge-domain-lib : 3 PASS
knowledge-git-api : 1 PASS
knowledge-git-lib : 7 PASS
knowledge-sync-api : 1 PASS
knowledge-web-lib : 4 PASS
launchers : 0 test chacun
```
## Métadonnées, lockfile et frontières
L'audit Cargo passe :
```text
Knowledge 0.0.2-alpha.2.fix.1 Cargo metadata audit: clean
```
Les contrôles de frontière passent :
```text
Web/ sans diff
crates/knowledge-git-api sans diff
crates/knowledge-sync-api sans diff
crates/knowledge-app-lib absent
crates/knowledge-forge-gitea-lib absent
crates/knowledge-store-postgres-lib absent
Cargo.lock présent localement, ignoré et non suivi
```
Après staging, `git diff --cached --check` passe. Le staging observé avant ajout du présent history est :
```text
M Cargo.toml
M VERSION
M crates/knowledge-git-lib/src/backend.rs
M crates/knowledge-git-lib/src/error.rs
M crates/knowledge-git-lib/src/lib.rs
M crates/knowledge-git-lib/unit_tests/backend.rs
A deltas/0.0.2/alpha.2.fix.1.md
M docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md
M docs/validation/001-VALIDATION_MATRIX.md
```
## Diagnostic
`alpha.2.fix.1` reste dans le scope prévu et restaure l'hygiène, le formatage et la compilation. Aucun déclencheur de REPLAN n'est atteint.
Le nouveau fix doit uniquement :
- rendre explicite le retour de la closure signalée par Clippy dans `unit_tests/backend.rs` ;
- conserver l'API, les dépendances et le comportement Git inchangés ;
- rejouer la gate complète d'`alpha.2` afin de confirmer qu'aucun autre warning Clippy n'est masqué.
## Décision
`0.0.2-alpha.2.fix.1` est `FAIL`.
La transition autorisée est :
```text
appliquer ce history
-> git add / diff --cached --check / status
-> commit exact "0.0.2-alpha.2.fix.1"
-> push origin master
-> vérifier le workspace propre
-> appliquer seulement ensuite 0.0.2-alpha.2.fix.2
```