Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a15fff86b8 | |||
| 095ac37d08 | |||
| b55eb23dc0 | |||
| b382397244 | |||
| 9a1f73d57b | |||
| 0351aa045e | |||
| 038b91864a | |||
| 282d59cfb7 | |||
| a87f3ba4ab | |||
| aca00e6595 | |||
| c3a825657d | |||
| 88a63b06cc | |||
| 3004e4b581 | |||
| dd63e8578a | |||
| a74a19838f |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,10 +1,21 @@
|
|||||||
<!-- file: CHANGELOG.md -->
|
<!-- file: CHANGELOG.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 18 -->
|
||||||
|
|
||||||
# Changelog SaseDev Knowledge
|
# Changelog SaseDev Knowledge
|
||||||
|
|
||||||
Ce fichier décrit les changements utiles des releases publiées. Les détails de tranches et preuves opérateur appartiennent aux plans, deltas et histories.
|
Ce fichier décrit les changements utiles des releases publiées. Les détails de tranches et preuves opérateur appartiennent aux plans, deltas et histories.
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
|
||||||
|
- ajoute un contrat Git d'écriture provider-neutral distinct de `GitRepository`, afin que les consommateurs lecture seule n'acquièrent aucune obligation d'écriture ;
|
||||||
|
- valide les chemins de mutation, l'identité de commit, les branches `refs/heads/...` et l'état distant attendu avant toute opération backend ;
|
||||||
|
- matérialise un worktree d'écriture isolé, applique create/update/delete sous confinement et refuse les traversées par symlink vers l'extérieur ;
|
||||||
|
- crée un commit candidat avec auteur/committer explicites, hooks/configuration Git ambiante neutralisés et parent vérifié exactement égal à la révision de base ;
|
||||||
|
- conserve les candidats sous une référence backend interne puis nettoie les worktrees temporaires contrôlés ;
|
||||||
|
- pousse une branche distante uniquement sous garde compare-and-exchange `--force-with-lease` correspondant à une révision attendue ou à une absence attendue ;
|
||||||
|
- traduit une divergence distante, y compris concurrente entre observation et push, en conflit récupérable sans écrasement silencieux ;
|
||||||
|
- conserve la forge optionnelle et laisse hors de cette release PostgreSQL, rebase/merge automatique, auth/admin, routes Web d'édition et workflow complet de proposition/revue/publication.
|
||||||
|
|
||||||
## 0.0.2
|
## 0.0.2
|
||||||
|
|
||||||
- matérialise un backend Git générique de lecture derrière `knowledge-git-api`, sans dépendance obligatoire à une forge ;
|
- matérialise un backend Git générique de lecture derrière `knowledge-git-api`, sans dépendance obligatoire à une forge ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 30
|
# version: 41
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -13,10 +13,11 @@ members = [
|
|||||||
"crates/knowledge-pgr-server",
|
"crates/knowledge-pgr-server",
|
||||||
"crates/knowledge-howtos-server",
|
"crates/knowledge-howtos-server",
|
||||||
"crates/knowledge-snippets-server",
|
"crates/knowledge-snippets-server",
|
||||||
|
"crates/knowledge-store-postgres-lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.0.2"
|
version = "0.0.4-alpha.2.fix.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = false
|
publish = false
|
||||||
repository = "https://git.sasedev.com/Sasedev/sasedev-knowledge"
|
repository = "https://git.sasedev.com/Sasedev/sasedev-knowledge"
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: README.md -->
|
<!-- file: README.md -->
|
||||||
<!-- version: 18 -->
|
<!-- version: 27 -->
|
||||||
|
|
||||||
# SaseDev Knowledge
|
# SaseDev Knowledge
|
||||||
|
|
||||||
@@ -11,9 +11,13 @@ Le dépôt applicatif retenu est `sasedev-knowledge`. Le nom court du projet et
|
|||||||
|
|
||||||
La version courante est définie par [`VERSION`](VERSION).
|
La version courante est définie par [`VERSION`](VERSION).
|
||||||
|
|
||||||
`0.0.1` est la première stable de fondation de SaseDev Knowledge. `0.0.2` est la stable du chemin Git générique de lecture, de l'autonomie normative locale et du cas d'usage partagé de synchronisation `Changed` / `Unchanged`. La release conserve les intégrations forge, PostgreSQL, écriture Git, auth/admin complète et formats éditoriaux finaux hors scope tant qu'une mission ultérieure ne les engage pas explicitement.
|
`0.0.1` est la première stable de fondation de SaseDev Knowledge. `0.0.2` est la stable du chemin Git générique de lecture, de l'autonomie normative locale et du cas d'usage partagé de synchronisation `Changed` / `Unchanged`.
|
||||||
|
|
||||||
`knowledge-git-lib` implémente `knowledge-git-api::GitRepository` pour des sources physiques enregistrées : mirror contrôlé, refresh avec pruning, résolution d'une référence vers un commit et snapshot/worktree détaché adressé par révision. `alpha.4` ne change pas cette sémantique. Les règles nécessaires au développement courant sont désormais possédées localement sous `docs/rules/`, et les futurs prompts Knowledge ne chargent pas PGR sauf réévaluation explicitement engagée. `knowledge-app-lib` compose maintenant refresh, résolution, comparaison avec la révision précédente et préparation de snapshot en un résultat explicite `Changed` / `Unchanged`, sans persister la dernière révision ni matérialiser de scheduler.
|
`0.0.3` est la stable du chemin Git générique complet en lecture et écriture contrôlée : lecture/snapshot, création d'un commit candidat isolé, mutations confinées, identité explicite puis push compare-and-exchange protégé par `--force-with-lease` contre l'état distant attendu. Une branche peut être créée uniquement lorsque son absence est attendue ; toute divergence devient un conflit récupérable sans écrasement silencieux. Cette stable reste provider-neutral et n'introduit ni forge obligatoire, ni PostgreSQL, ni rebase/merge automatique, ni workflow produit complet de proposition/publication.
|
||||||
|
|
||||||
|
`0.0.4-alpha.2` matérialise la première fondation PostgreSQL partagée. `knowledge-store-postgres-lib` possède désormais une configuration explicite, un pool `deadpool-postgres` sur `tokio-postgres`, des migrations SQL embarquées via Refinery et une première migration créant `knowledge_sources` et `knowledge_source_sync_state`. Aucun ORM, fallback de stockage, accès implicite à l'environnement ou raccord launcher n'est introduit ; Git reste autoritaire pour tout contenu publié.
|
||||||
|
|
||||||
|
`knowledge-git-lib` implémente `knowledge-git-api::GitRepository` pour des sources physiques enregistrées : mirror contrôlé, refresh avec pruning, résolution d'une référence vers un commit et snapshot/worktree détaché adressé par révision. La création locale du candidat d'`alpha.3` reste séparée de cette sémantique de lecture. Les règles nécessaires au développement courant sont désormais possédées localement sous `docs/rules/`, et les futurs prompts Knowledge ne chargent pas PGR sauf réévaluation explicitement engagée. `knowledge-app-lib` compose maintenant refresh, résolution, comparaison avec la révision précédente et préparation de snapshot en un résultat explicite `Changed` / `Unchanged`, sans persister la dernière révision ni matérialiser de scheduler.
|
||||||
|
|
||||||
## Topologie produit de fondation
|
## Topologie produit de fondation
|
||||||
|
|
||||||
@@ -29,11 +33,11 @@ Ces trois launchers restent minces et réutilisent les mêmes crates communes lo
|
|||||||
|
|
||||||
Chaque famille possède au minimum sa propre source Git autoritaire : dépôt `pgr`, dépôt `howtos` et dépôt `snippets`. La configuration doit permettre d'ajouter d'autres sources sans figer le système à exactement trois repositories.
|
Chaque famille possède au minimum sa propre source Git autoritaire : dépôt `pgr`, dépôt `howtos` et dépôt `snippets`. La configuration doit permettre d'ajouter d'autres sources sans figer le système à exactement trois repositories.
|
||||||
|
|
||||||
PostgreSQL est mutualisable et constitue la baseline retenue pour l'état transversal de la plateforme : comptes, sessions, propositions, synchronisation, index et relations entre familles. Il ne remplace jamais les sources Git comme vérité du contenu publié.
|
PostgreSQL est mutualisable et constitue la baseline retenue pour l'état transversal de la plateforme : comptes, sessions, propositions, synchronisation, index et relations entre familles. `0.0.4-alpha.2` matérialise le premier owner partagé avec `tokio-postgres 0.7.18`, `deadpool-postgres 0.14.2`, Refinery `0.9.2` et une migration SQL versionnée. La bibliothèque de stockage reçoit sa configuration de connexion depuis la composition, ne lit ni ne journalise elle-même des secrets d'environnement et utilise actuellement `NoTls` sans ajouter de backend TLS tant qu'un déploiement ne l'exige pas. PostgreSQL ne remplace jamais les sources Git comme vérité du contenu publié.
|
||||||
|
|
||||||
Chaque application Web doit pouvoir exposer sa propre surface d'administration protégée pour son domaine. Les chemins HTTP exacts ne sont pas encore figés. Une éventuelle quatrième application d'administration pourra agréger des fonctions transversales, mais elle ne remplacera pas les contrôles d'autorisation propres aux trois serveurs.
|
Chaque application Web doit pouvoir exposer sa propre surface d'administration protégée pour son domaine. Les chemins HTTP exacts ne sont pas encore figés. Une éventuelle quatrième application d'administration pourra agréger des fonctions transversales, mais elle ne remplacera pas les contrôles d'autorisation propres aux trois serveurs.
|
||||||
|
|
||||||
Le workspace Rust matérialise neuf packages : les trois frontières de fondation `knowledge-domain-lib`, `knowledge-git-api` et `knowledge-sync-api`, le backend `knowledge-git-lib`, la couche application partagée `knowledge-app-lib`, la composition Web `knowledge-web-lib`, puis les trois launchers `knowledge-pgr-server`, `knowledge-howtos-server` et `knowledge-snippets-server`. Les deux crates `-api` restent passives et indépendantes d'Actix, Gitea et PostgreSQL.
|
Le workspace Rust matérialise dix packages : les trois frontières de fondation `knowledge-domain-lib`, `knowledge-git-api` et `knowledge-sync-api`, le backend `knowledge-git-lib`, la couche application partagée `knowledge-app-lib`, la composition Web `knowledge-web-lib`, le stockage `knowledge-store-postgres-lib`, puis les trois launchers `knowledge-pgr-server`, `knowledge-howtos-server` et `knowledge-snippets-server`. Les deux crates `-api` restent passives et indépendantes d'Actix, Gitea et PostgreSQL.
|
||||||
|
|
||||||
## Frontend de référence et rendu Maud
|
## Frontend de référence et rendu Maud
|
||||||
|
|
||||||
@@ -49,6 +53,8 @@ La stable `0.0.1` matérialise `knowledge-web-lib` et les trois launchers `knowl
|
|||||||
|
|
||||||
Le runtime exige un bind configuré par launcher et l'une des deux sources d'assets suivantes : `KNOWLEDGE_VITE_DEV_ORIGIN` en développement ou `KNOWLEDGE_VITE_MANIFEST_PATH` en production. `KNOWLEDGE_ASSET_BASE_PATH` peut remplacer le préfixe `/` de production. Aucune route admin/login n'est matérialisée à ce stade.
|
Le runtime exige un bind configuré par launcher et l'une des deux sources d'assets suivantes : `KNOWLEDGE_VITE_DEV_ORIGIN` en développement ou `KNOWLEDGE_VITE_MANIFEST_PATH` en production. `KNOWLEDGE_ASSET_BASE_PATH` peut remplacer le préfixe `/` de production. Aucune route admin/login n'est matérialisée à ce stade.
|
||||||
|
|
||||||
|
La revue d'horizon de `0.0.3-beta.1` sépare désormais explicitement la construction produit des trois façades : fondation PostgreSQL partagée, modèles/projections de contenu, templates publics (dont pages de garde) pour PGR/HOWTOs/snippets, templates d'administration, intégration Actix Web et adaptation Maud des templates validés, puis workflow de gestion/proposition/revue/publication. Les templates restent d'abord développés et validés dans le laboratoire Web avant adaptation runtime en Maud ; cette trajectoire n'implémente encore aucune de ces phases pendant `0.0.3`.
|
||||||
|
|
||||||
## Invariants de fondation
|
## Invariants de fondation
|
||||||
|
|
||||||
- Git est la source autoritaire du contenu versionné.
|
- Git est la source autoritaire du contenu versionné.
|
||||||
@@ -76,11 +82,18 @@ Le runtime exige un bind configuré par launcher et l'une des deux sources d'ass
|
|||||||
- [`docs/studies/002-ACTIX_MAUD_VITE_CORPUS_STUDY.md`](docs/studies/002-ACTIX_MAUD_VITE_CORPUS_STUDY.md) — étude ciblée du pattern Vite manifest / Actix / Maud à adapter pour Knowledge ;
|
- [`docs/studies/002-ACTIX_MAUD_VITE_CORPUS_STUDY.md`](docs/studies/002-ACTIX_MAUD_VITE_CORPUS_STUDY.md) — étude ciblée du pattern Vite manifest / Actix / Maud à adapter pour Knowledge ;
|
||||||
- [`docs/studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md`](docs/studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md) — réconciliation `beta.1`, état matérialisé, DEFER conservés et revue d'horizon ;
|
- [`docs/studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md`](docs/studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md) — réconciliation `beta.1`, état matérialisé, DEFER conservés et revue d'horizon ;
|
||||||
- [`docs/studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md`](docs/studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md) — comparaison processus Git / `gix` / `git2` et décision d'implémentation de lecture ;
|
- [`docs/studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md`](docs/studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md) — comparaison processus Git / `gix` / `git2` et décision d'implémentation de lecture ;
|
||||||
|
- [`docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md`](docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md) — choix de la mission, stratégie de worktree d'écriture et invariants de push gardé ;
|
||||||
|
- [`docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md`](docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md) — réconciliation beta du chemin Git d'écriture et revue d'horizon produit PGR/HOWTOs/snippets ;
|
||||||
|
- [`docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md`](docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md) — étude de démarrage PostgreSQL, comparaison des clients/ORM, ownership, premier schéma, migrations et tests ;
|
||||||
- [`docs/plans/001-V0_0_1_FOUNDATION_PLAN.md`](docs/plans/001-V0_0_1_FOUNDATION_PLAN.md) — plan historique de la fondation stable `0.0.1` ;
|
- [`docs/plans/001-V0_0_1_FOUNDATION_PLAN.md`](docs/plans/001-V0_0_1_FOUNDATION_PLAN.md) — plan historique de la fondation stable `0.0.1` ;
|
||||||
- [`docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md`](docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md) — plan historique de la stable `0.0.2`, gates et décisions de fermeture ;
|
- [`docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md`](docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md) — plan historique de la stable `0.0.2`, gates et décisions de fermeture ;
|
||||||
|
- [`docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md`](docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md) — plan historique de la stable `0.0.3`, écriture Git contrôlée et push gardé ;
|
||||||
|
- [`docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md`](docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md) — plan actif de la fondation PostgreSQL partagée ;
|
||||||
- [`docs/validation/001-VALIDATION_MATRIX.md`](docs/validation/001-VALIDATION_MATRIX.md) — matrice de validation ;
|
- [`docs/validation/001-VALIDATION_MATRIX.md`](docs/validation/001-VALIDATION_MATRIX.md) — matrice de validation ;
|
||||||
- [`prompts/001-V0_0_1_START_PROMPT.md`](prompts/001-V0_0_1_START_PROMPT.md) — prompt de démarrage autoritaire de la release.
|
- [`prompts/001-V0_0_1_START_PROMPT.md`](prompts/001-V0_0_1_START_PROMPT.md) — prompt de démarrage autoritaire de la release.
|
||||||
- [`prompts/002-V0_0_2_START_PROMPT.md`](prompts/002-V0_0_2_START_PROMPT.md) — handoff historique exécuté pour ouvrir `0.0.2` depuis la stable `v0.0.1` vérifiée.
|
- [`prompts/002-V0_0_2_START_PROMPT.md`](prompts/002-V0_0_2_START_PROMPT.md) — handoff historique exécuté pour ouvrir `0.0.2` depuis la stable `v0.0.1` vérifiée.
|
||||||
|
- [`prompts/003-V0_0_3_START_PROMPT.md`](prompts/003-V0_0_3_START_PROMPT.md) — prompt local de reprise de `0.0.3` depuis la stable `v0.0.2` publiée et vérifiée.
|
||||||
|
- [`prompts/004-V0_0_4_START_PROMPT.md`](prompts/004-V0_0_4_START_PROMPT.md) — handoff préparé pendant la stabilisation de `0.0.3` pour ouvrir la fondation PostgreSQL partagée après publication réelle de `v0.0.3`.
|
||||||
|
|
||||||
## Langue et nomenclature
|
## Langue et nomenclature
|
||||||
|
|
||||||
|
|||||||
48
ROADMAP.md
48
ROADMAP.md
@@ -1,31 +1,47 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 12 -->
|
||||||
|
|
||||||
# Roadmap SaseDev Knowledge
|
# Roadmap SaseDev Knowledge
|
||||||
|
|
||||||
Le roadmap ne réserve que les releases dont la mission est réellement engagée.
|
La roadmap montre la release effectivement en cours et un horizon borné des releases suivantes. Une release d'horizon reste prévisionnelle tant qu'elle n'est pas ouverte : son numéro, son ordre ou son scope peuvent encore être replanifiés par une revue d'horizon ultérieure.
|
||||||
|
|
||||||
Statuts : `( )` planifié, `(x)` terminé, `(d)` différé, `(c)` annulé.
|
Statuts : `( )` engagée/planifiée, `(x)` terminée, `(h)` horizon prévisionnel, `(d)` différée, `(c)` annulée.
|
||||||
|
|
||||||
## Fondation
|
## Fondation
|
||||||
|
|
||||||
- `(x) 0.0.1` — fondation Knowledge : gouvernance PGR, workspace à trois applications Actix Web, architecture Git/forge/PostgreSQL partagé, frontières PGR/HOWTO/snippet, contrat d'administration locale protégée sans UI/auth complète, workspace Rust, premiers contrats, baseline Web reproductible, rendu Maud et intégration réelle du manifest Vite, sans implémentation métier disproportionnée.
|
- `(x) 0.0.1` — fondation Knowledge : gouvernance locale, workspace à trois applications Actix Web, architecture Git/forge/PostgreSQL partagé, frontières PGR/HOWTO/snippet, baseline Web reproductible, rendu Maud et intégration réelle du manifest Vite, sans implémentation métier disproportionnée.
|
||||||
|
|
||||||
## Releases livrées
|
## Releases livrées
|
||||||
|
|
||||||
- `(x) 0.0.2` — implémentation Git générique et synchronisation de lecture : backend provider-neutral derrière `knowledge-git-api`, configuration de sources locales/remotes, matérialisation locale reproductible, refresh/résolution de révision, snapshot/worktree de lecture et convergence des déclencheurs explicite/périodique vers un même cas d'usage, sans API de forge obligatoire, écriture/publication Git, PostgreSQL ni format final HOWTO/snippet.
|
- `(x) 0.0.2` — Git générique et synchronisation de lecture : backend provider-neutral, sources locales/remotes, mirror contrôlé, refresh/résolution de révision, snapshot/worktree de lecture et cas d'usage partagé `Changed` / `Unchanged`, sans forge obligatoire ni persistance PostgreSQL.
|
||||||
|
- `(x) 0.0.3` — écriture Git contrôlée et push gardé : contrat d'écriture séparé de la lecture, worktree candidat isolé, mutations confinées, commit à identité explicite et mise à jour d'une branche `refs/heads/...` uniquement lorsque son état distant correspond à l'attente fournie ; divergence = conflit récupérable, sans rebase/merge automatique ni forge obligatoire.
|
||||||
|
|
||||||
## Domaines futurs non numérotés
|
## Release engagée
|
||||||
|
|
||||||
Les thèmes suivants sont candidats à des releases ultérieures mais ne reçoivent pas encore de numéro :
|
- `( ) 0.0.4` — fondation PostgreSQL partagée : premier schéma réel et migrations, configuration logique persistante des sources, dernière révision synchronisée/indexée et état transversal minimal reconstructible ; Git reste autoritaire pour le contenu publié.
|
||||||
|
|
||||||
- écriture Git contrôlée, branches/propositions/rebase/push et workflow de publication ;
|
## Horizon produit après 0.0.4
|
||||||
- état PostgreSQL partagé et migrations applicatives ;
|
|
||||||
- contribution/revue et administration ;
|
|
||||||
- publication/rendu des trois façades ;
|
|
||||||
- recherche et indexation avancées ;
|
|
||||||
- adapter Gitea puis autres forges selon besoin réel ;
|
|
||||||
- authentification/autorisation complète ;
|
|
||||||
- déploiement et opérations multi-domaines.
|
|
||||||
|
|
||||||
La revue d'horizon `0.0.2-beta.1` est réalisée et la stable `0.0.2` est fermée : aucune release postérieure à `0.0.2` n'est engagée ni numérotée. Les thèmes futurs restent volontairement non numérotés jusqu'à ce qu'un scope et un ordre réellement utiles soient décidés.
|
La revue `0.0.3-beta.1`, confirmée pendant la promotion stable, engage `0.0.4` comme prochaine mission. Les versions suivantes restent des horizons : elles explicitent la trajectoire des trois produits Web sans figer prématurément leurs détails.
|
||||||
|
|
||||||
|
- `(h) 0.0.5` — modèles de contenu et projections : corpus réels PGR/HOWTO/snippet, parsing/validation, métadonnées et relations utiles, projection locale lisible depuis Git + PostgreSQL et contrats de lecture nécessaires aux trois façades.
|
||||||
|
- `(h) 0.0.6` — templates publics des trois sites : développer et valider dans `Web/knowledge-template-lab` les pages de garde, navigation, listes, détails, recherche/filtrage visible et composants partagés pour PGR, HOWTOs et snippets, sans encore faire de ces templates le rendu runtime définitif.
|
||||||
|
- `(h) 0.0.7` — templates d'administration des trois sites : dashboards et surfaces de gestion propres à PGR, HOWTOs et snippets, formulaires/états de proposition et revue nécessaires au produit, toujours comme références Web validables avant adaptation serveur.
|
||||||
|
- `(h) 0.0.8` — lecture publique Actix Web / Maud : matérialiser les routes publiques réelles des trois serveurs, alimentées par Git/projections locales, et adapter en Maud les templates publics validés tout en conservant le pipeline Vite/assets commun.
|
||||||
|
- `(h) 0.0.9` — administration Actix Web / Maud et autorisation : matérialiser identité/session/capabilities réellement nécessaires, routes admin protégées par façade et adaptation Maud des templates d'administration validés.
|
||||||
|
- `(h) 0.0.10` — gestion, proposition, revue et publication : relier les interfaces d'administration aux cas d'usage d'édition, conserver la révision Git de base, gérer les conflits, produire/pousser la révision Git autoritaire puis réconcilier les projections PostgreSQL.
|
||||||
|
|
||||||
|
## Domaines ultérieurs à reclasser
|
||||||
|
|
||||||
|
Les thèmes suivants restent volontairement non numérotés ou peuvent être absorbés dans une release d'horizon seulement lorsqu'un besoin mesuré le justifie :
|
||||||
|
|
||||||
|
- recherche/indexation avancée au-delà des capacités PostgreSQL et des projections réellement nécessaires aux façades ;
|
||||||
|
- adapter Gitea puis autres forges et webhooks forge-only ;
|
||||||
|
- scheduler/jobs de synchronisation, indexation ou publication ;
|
||||||
|
- quatrième application d'administration transverse ;
|
||||||
|
- déploiement/opérations multi-domaines ;
|
||||||
|
- partage concurrent d'une même racine Git entre plusieurs processus ;
|
||||||
|
- support explicite Git LFS/submodules et politiques de rétention/cleanup avancées ;
|
||||||
|
- observabilité transversale ou service réseau supplémentaire sans ownership démontré.
|
||||||
|
|
||||||
|
Chaque revue d'horizon doit vérifier la prochaine mission et réévaluer au moins les deux releases qui la suivent. Elle peut conserver, réordonner, fusionner, scinder ou dénuméroter un horizon lorsque les preuves produit ou techniques le justifient.
|
||||||
|
|||||||
26
RULES.md
26
RULES.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: RULES.md -->
|
<!-- file: RULES.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 17 -->
|
||||||
|
|
||||||
# Règles SaseDev Knowledge
|
# Règles SaseDev Knowledge
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ Le prompt `prompts/002-V0_0_2_START_PROMPT.md` reste historique : sa vérificati
|
|||||||
|
|
||||||
Une future version de PGR n'a aucun effet automatique sur Knowledge. Elle n'est consultée que lorsqu'une réévaluation/adoption PGR est explicitement inscrite au scope d'une tranche ou d'une release. Cette réévaluation doit alors comparer la baseline locale Knowledge avec la version PGR choisie, classifier les décisions `ADOPT / KEEP-LOCAL / OVERRIDE / DEFER`, puis matérialiser localement les décisions retenues avant de refermer l'adoption.
|
Une future version de PGR n'a aucun effet automatique sur Knowledge. Elle n'est consultée que lorsqu'une réévaluation/adoption PGR est explicitement inscrite au scope d'une tranche ou d'une release. Cette réévaluation doit alors comparer la baseline locale Knowledge avec la version PGR choisie, classifier les décisions `ADOPT / KEEP-LOCAL / OVERRIDE / DEFER`, puis matérialiser localement les décisions retenues avant de refermer l'adoption.
|
||||||
|
|
||||||
`0.0.2-alpha.4` est la tranche de réconciliation qui doit terminer l'autonomie normative de l'adoption `v0.1.3` : inventorier les owners réellement retenus, vérifier où chaque contrat nécessaire est possédé localement, adapter les règles/templates/audits manquants sans recopier mécaniquement l'arborescence PGR et supprimer les références externes nécessaires à la compréhension courante du projet.
|
`0.0.2-alpha.4` a terminé l'autonomie normative de l'adoption `v0.1.3` : les owners nécessaires sont possédés localement et les futures releases normales repartent de Knowledge seul.
|
||||||
|
|
||||||
## Owners normatifs locaux
|
## Owners normatifs locaux
|
||||||
|
|
||||||
@@ -124,6 +124,16 @@ La configuration physique des sources Git et la matérialisation locale appartie
|
|||||||
|
|
||||||
La racine d'état Git n'est pas un stockage métier. En `0.0.2`, elle est possédée exclusivement par une instance de backend/processus ; un partage concurrent inter-processus reste différé tant qu'un verrouillage dédié n'est pas matérialisé.
|
La racine d'état Git n'est pas un stockage métier. En `0.0.2`, elle est possédée exclusivement par une instance de backend/processus ; un partage concurrent inter-processus reste différé tant qu'un verrouillage dédié n'est pas matérialisé.
|
||||||
|
|
||||||
|
### Écriture Git contrôlée
|
||||||
|
|
||||||
|
`0.0.3` engage une capacité d'écriture Git générique derrière un contrat distinct du contrat de lecture. Elle reste indépendante d'une forge et ne transforme pas une proposition externe en publication directe.
|
||||||
|
|
||||||
|
Toute écriture MUST partir d'une révision de base explicite et utiliser un worktree contrôlé distinct des snapshots de lecture. Les chemins de mutation MUST rester relatifs au worktree ; les chemins absolus, composants `..`, accès `.git` et échappements hors racine sont interdits.
|
||||||
|
|
||||||
|
Les commits créés par le backend MUST utiliser une identité auteur/committer explicitement fournie et MUST neutraliser les hooks et prompts interactifs possédés par l'opération. La configuration Git globale de la machine ne constitue pas une source d'identité autoritaire.
|
||||||
|
|
||||||
|
Une mise à jour distante de `0.0.3` MUST viser une branche `refs/heads/...` et MUST être gardée par l'état distant attendu. Une divergence produit un conflit récupérable ; le backend MUST NOT effectuer un `push --force` non gardé, un rebase automatique ou un merge automatique pour masquer cette divergence.
|
||||||
|
|
||||||
## PostgreSQL
|
## PostgreSQL
|
||||||
|
|
||||||
PostgreSQL est destiné à l'état applicatif : comptes, sessions, propositions, revues, audit applicatif, index, recherche, synchronisation et jobs.
|
PostgreSQL est destiné à l'état applicatif : comptes, sessions, propositions, revues, audit applicatif, index, recherche, synchronisation et jobs.
|
||||||
@@ -132,7 +142,17 @@ La baseline de fondation retient une base PostgreSQL logique partagée par les t
|
|||||||
|
|
||||||
Le texte et les métadonnées éditoriales qui constituent le contenu publié restent autoritaires dans Git. Une copie ou projection PostgreSQL de ces données est dérivée et reconstructible.
|
Le texte et les métadonnées éditoriales qui constituent le contenu publié restent autoritaires dans Git. Une copie ou projection PostgreSQL de ces données est dérivée et reconstructible.
|
||||||
|
|
||||||
Le choix du driver, de l'ORM ou de la stratégie de migration reste différé jusqu'à une tranche qui en a réellement besoin.
|
`0.0.4-alpha.1` sélectionne `tokio-postgres` comme driver PostgreSQL async natif, `deadpool-postgres` comme pool et Refinery comme mécanisme de migrations. `0.0.4-alpha.2` matérialise ces choix avec `tokio-postgres 0.7.18`, `deadpool-postgres 0.14.2` et Refinery `0.9.2`. Knowledge ne sélectionne pas d'ORM ni d'abstraction multi-SGBD : les requêtes restent SQL explicite derrière un owner de stockage partagé. Chaque dépendance doit être ajoutée avec les fonctionnalités Cargo minimales réellement nécessaires : `runtime` pour `tokio-postgres`, `rt_tokio_1` pour `deadpool-postgres` et `tokio-postgres` pour Refinery dans cette première matérialisation.
|
||||||
|
|
||||||
|
L'owner réel est `knowledge-store-postgres-lib`. Aucun `knowledge-store-api` séparé n'est créé tant qu'une seconde implémentation remplaçable ou une frontière passive indépendante n'est pas démontrée. Les types du driver, du pool et du migrator restent confinés à cette implémentation.
|
||||||
|
|
||||||
|
Les migrations sont des fichiers SQL versionnés possédés par cette crate, appliqués explicitement via Refinery et embarqués avec le binaire. Une migration déjà appliquée est suivie par le mécanisme de migration ; le runtime ne doit pas modifier silencieusement le schéma au fil des requêtes. La première implémentation utilise `tokio_postgres::NoTls` et n'ajoute aucun backend TLS tant qu'une cible de déploiement n'en démontre pas le besoin.
|
||||||
|
|
||||||
|
La bibliothèque de stockage reçoit sa configuration de connexion et de pool depuis sa composition. Elle MUST NOT lire directement une variable d'environnement secrète comme source implicite, MUST NOT journaliser une URL de connexion ou des credentials et MUST retourner une erreur récupérable si la connexion ou une migration échoue. Aucune indisponibilité PostgreSQL ne déclenche un fallback caché vers un fichier ou un stockage mémoire.
|
||||||
|
|
||||||
|
Le premier schéma de `0.0.4` reste minimal : `knowledge_sources` conserve l'identifiant logique, la famille et la référence Git logique ; `knowledge_source_sync_state` conserve uniquement la dernière révision synchronisée. Il ne stocke ni corps de contenu publié, ni credential Git, ni modèle auth/proposition final, ni projection de contenu anticipée.
|
||||||
|
|
||||||
|
À partir d'`0.0.4-alpha.2`, toute gate qui prétend valider les migrations PostgreSQL doit utiliser un PostgreSQL réel fourni explicitement via `KNOWLEDGE_POSTGRES_TEST_URL`. Le test doit isoler son état dans un schéma temporaire et le supprimer après validation ; l'absence du service requis rend la gate `BLOCKED`, jamais `PASS`.
|
||||||
|
|
||||||
## Frontières documentaires
|
## Frontières documentaires
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/knowledge-git-api/src/lib.rs
|
// file: crates/knowledge-git-api/src/lib.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! Provider-neutral contracts for Git-backed Knowledge sources.
|
//! Provider-neutral contracts for Git-backed Knowledge sources.
|
||||||
//!
|
//!
|
||||||
@@ -10,6 +10,7 @@ mod reference;
|
|||||||
mod repository;
|
mod repository;
|
||||||
mod revision;
|
mod revision;
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
|
mod write;
|
||||||
|
|
||||||
/// Opaque Git reference requested by a caller, such as a branch, tag or object expression.
|
/// Opaque Git reference requested by a caller, such as a branch, tag or object expression.
|
||||||
pub use self::reference::GitReference;
|
pub use self::reference::GitReference;
|
||||||
@@ -19,6 +20,11 @@ pub use self::repository::GitRepository;
|
|||||||
pub use self::revision::GitRevision;
|
pub use self::revision::GitRevision;
|
||||||
/// Read-only local materialization of one authoritative Git revision.
|
/// Read-only local materialization of one authoritative Git revision.
|
||||||
pub use self::snapshot::GitSnapshot;
|
pub use self::snapshot::GitSnapshot;
|
||||||
|
/// Provider-neutral types and operations for controlled Git writes.
|
||||||
|
pub use self::write::{
|
||||||
|
GitBranch, GitCommitIdentity, GitFileMutation, GitMutationPath, GitPushOutcome, GitPushRequest, GitRemoteState, GitRepositoryWriter, GitWriteInputError,
|
||||||
|
GitWriteRequest,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "../unit_tests/lib.rs"]
|
#[path = "../unit_tests/lib.rs"]
|
||||||
|
|||||||
319
crates/knowledge-git-api/src/write.rs
Normal file
319
crates/knowledge-git-api/src/write.rs
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
// file: crates/knowledge-git-api/src/write.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
/// Invalid value supplied to the provider-neutral Git write contract.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum GitWriteInputError {
|
||||||
|
/// A mutation path is empty or contains only whitespace.
|
||||||
|
EmptyMutationPath,
|
||||||
|
/// A mutation path is absolute or uses a platform-specific separator.
|
||||||
|
AbsoluteMutationPath,
|
||||||
|
/// A mutation path contains an empty, current-directory or parent-directory component.
|
||||||
|
UnnormalizedMutationPath,
|
||||||
|
/// A mutation path addresses Git's private `.git` area.
|
||||||
|
ProtectedGitPath,
|
||||||
|
/// A commit identity name is empty or contains a forbidden line break or NUL byte.
|
||||||
|
InvalidCommitIdentityName,
|
||||||
|
/// A commit identity email is empty or contains a forbidden line break or NUL byte.
|
||||||
|
InvalidCommitIdentityEmail,
|
||||||
|
/// A commit message is empty or contains a NUL byte.
|
||||||
|
InvalidCommitMessage,
|
||||||
|
/// A write request does not contain any file mutation.
|
||||||
|
NoMutations,
|
||||||
|
/// More than one mutation targets the same normalized path.
|
||||||
|
DuplicateMutationPath,
|
||||||
|
/// A target branch is not a valid full `refs/heads/...` Git reference.
|
||||||
|
InvalidBranch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for crate::GitWriteInputError {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
crate::GitWriteInputError::EmptyMutationPath => return formatter.write_str("Git mutation path must not be empty"),
|
||||||
|
crate::GitWriteInputError::AbsoluteMutationPath => return formatter.write_str("Git mutation path must be relative and use '/' separators"),
|
||||||
|
crate::GitWriteInputError::UnnormalizedMutationPath => return formatter.write_str("Git mutation path must be normalized"),
|
||||||
|
crate::GitWriteInputError::ProtectedGitPath => return formatter.write_str("Git mutation path must not address .git"),
|
||||||
|
crate::GitWriteInputError::InvalidCommitIdentityName => return formatter.write_str("Git commit identity name is invalid"),
|
||||||
|
crate::GitWriteInputError::InvalidCommitIdentityEmail => return formatter.write_str("Git commit identity email is invalid"),
|
||||||
|
crate::GitWriteInputError::InvalidCommitMessage => return formatter.write_str("Git commit message is invalid"),
|
||||||
|
crate::GitWriteInputError::NoMutations => return formatter.write_str("Git write request must contain at least one mutation"),
|
||||||
|
crate::GitWriteInputError::DuplicateMutationPath => return formatter.write_str("Git write request must not mutate one path more than once"),
|
||||||
|
crate::GitWriteInputError::InvalidBranch => return formatter.write_str("Git target branch must be a valid refs/heads reference"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for crate::GitWriteInputError {}
|
||||||
|
|
||||||
|
/// Normalized relative path that may be mutated inside an isolated Git write worktree.
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct GitMutationPath(String);
|
||||||
|
|
||||||
|
impl crate::GitMutationPath {
|
||||||
|
/// Validate and build a provider-neutral repository-relative mutation path.
|
||||||
|
pub fn new(value: &str) -> Result<crate::GitMutationPath, crate::GitWriteInputError> {
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
return Err(crate::GitWriteInputError::EmptyMutationPath);
|
||||||
|
}
|
||||||
|
if value.starts_with('/') || value.contains('\\') || crate::write::has_windows_drive_prefix(value) {
|
||||||
|
return Err(crate::GitWriteInputError::AbsoluteMutationPath);
|
||||||
|
}
|
||||||
|
if value.contains('\0') {
|
||||||
|
return Err(crate::GitWriteInputError::UnnormalizedMutationPath);
|
||||||
|
}
|
||||||
|
for component in value.split('/') {
|
||||||
|
if component.is_empty() || component == "." || component == ".." {
|
||||||
|
return Err(crate::GitWriteInputError::UnnormalizedMutationPath);
|
||||||
|
}
|
||||||
|
if component.eq_ignore_ascii_case(".git") {
|
||||||
|
return Err(crate::GitWriteInputError::ProtectedGitPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(Self(value.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the normalized repository-relative path.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
return self.0.as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One file mutation requested for a candidate Git commit.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum GitFileMutation {
|
||||||
|
/// Create or replace one file with the provided bytes.
|
||||||
|
Write {
|
||||||
|
/// Validated repository-relative file path.
|
||||||
|
path: crate::GitMutationPath,
|
||||||
|
/// Complete bytes that must become the file content.
|
||||||
|
content: Vec<u8>,
|
||||||
|
},
|
||||||
|
/// Delete one existing path from the candidate tree.
|
||||||
|
Delete {
|
||||||
|
/// Validated repository-relative file path.
|
||||||
|
path: crate::GitMutationPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::GitFileMutation {
|
||||||
|
/// Return the validated path targeted by this mutation.
|
||||||
|
pub fn path(&self) -> &crate::GitMutationPath {
|
||||||
|
match self {
|
||||||
|
crate::GitFileMutation::Write { path, .. } => return path,
|
||||||
|
crate::GitFileMutation::Delete { path } => return path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explicit author and committer identity used to create a candidate commit.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct GitCommitIdentity {
|
||||||
|
name: String,
|
||||||
|
email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::GitCommitIdentity {
|
||||||
|
/// Validate and build an explicit Git commit identity.
|
||||||
|
pub fn new(name: &str, email: &str) -> Result<crate::GitCommitIdentity, crate::GitWriteInputError> {
|
||||||
|
if crate::write::invalid_identity_component(name) {
|
||||||
|
return Err(crate::GitWriteInputError::InvalidCommitIdentityName);
|
||||||
|
}
|
||||||
|
if crate::write::invalid_identity_component(email) {
|
||||||
|
return Err(crate::GitWriteInputError::InvalidCommitIdentityEmail);
|
||||||
|
}
|
||||||
|
return Ok(Self { name: name.to_owned(), email: email.to_owned() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the explicit identity name.
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
return self.name.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the explicit identity email.
|
||||||
|
pub fn email(&self) -> &str {
|
||||||
|
return self.email.as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validated full Git branch reference accepted as a remote write target.
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub struct GitBranch(String);
|
||||||
|
|
||||||
|
impl crate::GitBranch {
|
||||||
|
/// Validate a full `refs/heads/...` branch reference.
|
||||||
|
pub fn new(value: &str) -> Result<crate::GitBranch, crate::GitWriteInputError> {
|
||||||
|
if !crate::write::valid_branch_reference(value) {
|
||||||
|
return Err(crate::GitWriteInputError::InvalidBranch);
|
||||||
|
}
|
||||||
|
return Ok(Self(value.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the validated full branch reference.
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
return self.0.as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expected or observed state of one remote branch.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum GitRemoteState {
|
||||||
|
/// The branch does not exist.
|
||||||
|
Absent,
|
||||||
|
/// The branch resolves to the provided revision.
|
||||||
|
Revision(crate::GitRevision),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request to build one candidate commit from an explicit base revision.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct GitWriteRequest {
|
||||||
|
base_revision: crate::GitRevision,
|
||||||
|
mutations: Vec<crate::GitFileMutation>,
|
||||||
|
identity: crate::GitCommitIdentity,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::GitWriteRequest {
|
||||||
|
/// Validate and build a candidate-commit request.
|
||||||
|
pub fn new(
|
||||||
|
base_revision: crate::GitRevision,
|
||||||
|
mutations: Vec<crate::GitFileMutation>,
|
||||||
|
identity: crate::GitCommitIdentity,
|
||||||
|
message: &str,
|
||||||
|
) -> Result<crate::GitWriteRequest, crate::GitWriteInputError> {
|
||||||
|
if mutations.is_empty() {
|
||||||
|
return Err(crate::GitWriteInputError::NoMutations);
|
||||||
|
}
|
||||||
|
if message.trim().is_empty() || message.contains('\0') {
|
||||||
|
return Err(crate::GitWriteInputError::InvalidCommitMessage);
|
||||||
|
}
|
||||||
|
let mut paths = std::collections::HashSet::new();
|
||||||
|
for mutation in &mutations {
|
||||||
|
if !paths.insert(mutation.path().as_str()) {
|
||||||
|
return Err(crate::GitWriteInputError::DuplicateMutationPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(Self { base_revision, mutations, identity, message: message.to_owned() });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the resolved base revision from which the candidate must be created.
|
||||||
|
pub fn base_revision(&self) -> &crate::GitRevision {
|
||||||
|
return &self.base_revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the ordered mutations to apply in the isolated write worktree.
|
||||||
|
pub fn mutations(&self) -> &[crate::GitFileMutation] {
|
||||||
|
return self.mutations.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the explicit author/committer identity.
|
||||||
|
pub fn identity(&self) -> &crate::GitCommitIdentity {
|
||||||
|
return &self.identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the candidate commit message.
|
||||||
|
pub fn message(&self) -> &str {
|
||||||
|
return self.message.as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request to compare-and-exchange one remote branch to a candidate revision.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct GitPushRequest {
|
||||||
|
target_branch: crate::GitBranch,
|
||||||
|
expected_remote: crate::GitRemoteState,
|
||||||
|
candidate_revision: crate::GitRevision,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::GitPushRequest {
|
||||||
|
/// Build a guarded remote update request from already validated values.
|
||||||
|
pub fn new(target_branch: crate::GitBranch, expected_remote: crate::GitRemoteState, candidate_revision: crate::GitRevision) -> crate::GitPushRequest {
|
||||||
|
return Self { target_branch, expected_remote, candidate_revision };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the target remote branch.
|
||||||
|
pub fn target_branch(&self) -> &crate::GitBranch {
|
||||||
|
return &self.target_branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the remote state that must still hold for the update to succeed.
|
||||||
|
pub fn expected_remote(&self) -> &crate::GitRemoteState {
|
||||||
|
return &self.expected_remote;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the candidate revision to publish.
|
||||||
|
pub fn candidate_revision(&self) -> &crate::GitRevision {
|
||||||
|
return &self.candidate_revision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of one guarded remote branch update.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum GitPushOutcome {
|
||||||
|
/// The target branch was updated to the candidate revision.
|
||||||
|
Updated(
|
||||||
|
/// Revision now expected at the updated target branch.
|
||||||
|
crate::GitRevision,
|
||||||
|
),
|
||||||
|
/// The target branch no longer matched the expected state.
|
||||||
|
Conflict {
|
||||||
|
/// Actual remote state observed when the guarded update was rejected.
|
||||||
|
actual: crate::GitRemoteState,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provider-neutral write operations required from a Git repository backend.
|
||||||
|
///
|
||||||
|
/// This capability is intentionally separate from [`crate::GitRepository`] so read-only consumers
|
||||||
|
/// and fakes do not need to implement write methods. A concrete backend may implement this trait
|
||||||
|
/// independently from the read contract.
|
||||||
|
pub trait GitRepositoryWriter {
|
||||||
|
/// Implementation-specific recoverable error.
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
/// Create a candidate commit without updating a remote branch.
|
||||||
|
fn create_candidate(&mut self, source_id: &knowledge_domain_lib::SourceId, request: &crate::GitWriteRequest) -> Result<crate::GitRevision, Self::Error>;
|
||||||
|
|
||||||
|
/// Attempt one guarded remote branch update for a previously created candidate revision.
|
||||||
|
fn push_candidate(&mut self, source_id: &knowledge_domain_lib::SourceId, request: &crate::GitPushRequest) -> Result<crate::GitPushOutcome, Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_windows_drive_prefix(value: &str) -> bool {
|
||||||
|
let bytes = value.as_bytes();
|
||||||
|
if bytes.len() < 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return bytes[0].is_ascii_alphabetic() && bytes[1] == b':';
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invalid_identity_component(value: &str) -> bool {
|
||||||
|
return value.trim().is_empty() || value.contains('\0') || value.contains('\n') || value.contains('\r');
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_branch_reference(value: &str) -> bool {
|
||||||
|
const PREFIX: &str = "refs/heads/";
|
||||||
|
let branch = match value.strip_prefix(PREFIX) {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if branch.is_empty()
|
||||||
|
|| branch.starts_with('/')
|
||||||
|
|| branch.ends_with('/')
|
||||||
|
|| branch.ends_with('.')
|
||||||
|
|| branch.contains("..")
|
||||||
|
|| branch.contains("@{")
|
||||||
|
|| branch.contains("//")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for byte in branch.bytes() {
|
||||||
|
if byte <= 0x20 || byte == 0x7f || matches!(byte, b'~' | b'^' | b':' | b'?' | b'*' | b'[' | b'\\') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for component in branch.split('/') {
|
||||||
|
if component.is_empty() || component.starts_with('.') || component.ends_with(".lock") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/knowledge-git-api/unit_tests/lib.rs
|
// file: crates/knowledge-git-api/unit_tests/lib.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
struct FakeGitError;
|
struct FakeGitError;
|
||||||
@@ -22,6 +22,20 @@ impl crate::GitRepository for FakeGitRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FakeGitWriter;
|
||||||
|
|
||||||
|
impl crate::GitRepositoryWriter for FakeGitWriter {
|
||||||
|
type Error = FakeGitError;
|
||||||
|
|
||||||
|
fn create_candidate(&mut self, _source_id: &knowledge_domain_lib::SourceId, request: &crate::GitWriteRequest) -> Result<crate::GitRevision, Self::Error> {
|
||||||
|
return Ok(crate::GitRevision::new(&format!("candidate:{}", request.base_revision().as_str())));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_candidate(&mut self, _source_id: &knowledge_domain_lib::SourceId, request: &crate::GitPushRequest) -> Result<crate::GitPushOutcome, Self::Error> {
|
||||||
|
return Ok(crate::GitPushOutcome::Updated(request.candidate_revision().clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generic_repository_contract_does_not_require_a_forge() {
|
fn generic_repository_contract_does_not_require_a_forge() {
|
||||||
let source_result = knowledge_domain_lib::SourceId::new("local-pgr");
|
let source_result = knowledge_domain_lib::SourceId::new("local-pgr");
|
||||||
@@ -43,3 +57,140 @@ fn generic_repository_contract_does_not_require_a_forge() {
|
|||||||
assert_eq!(snapshot_result, Ok(crate::GitSnapshot::new(revision, std::path::PathBuf::from("/controlled/snapshot"))));
|
assert_eq!(snapshot_result, Ok(crate::GitSnapshot::new(revision, std::path::PathBuf::from("/controlled/snapshot"))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mutation_paths_are_relative_normalized_and_protect_git_metadata() {
|
||||||
|
let valid = crate::GitMutationPath::new("docs/rules/200-GOVERNANCE.md");
|
||||||
|
assert_eq!(valid.as_ref().map(crate::GitMutationPath::as_str), Ok("docs/rules/200-GOVERNANCE.md"));
|
||||||
|
assert_eq!(crate::GitMutationPath::new(""), Err(crate::GitWriteInputError::EmptyMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("/etc/passwd"), Err(crate::GitWriteInputError::AbsoluteMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("C:/outside.txt"), Err(crate::GitWriteInputError::AbsoluteMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("docs\\rules.md"), Err(crate::GitWriteInputError::AbsoluteMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("docs/../RULES.md"), Err(crate::GitWriteInputError::UnnormalizedMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("docs/./RULES.md"), Err(crate::GitWriteInputError::UnnormalizedMutationPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new(".git/config"), Err(crate::GitWriteInputError::ProtectedGitPath));
|
||||||
|
assert_eq!(crate::GitMutationPath::new("nested/.GIT/config"), Err(crate::GitWriteInputError::ProtectedGitPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn commit_identity_is_explicit_and_rejects_line_breaks() {
|
||||||
|
let identity = crate::GitCommitIdentity::new("Knowledge Publisher", "knowledge@example.invalid");
|
||||||
|
assert_eq!(identity.as_ref().map(crate::GitCommitIdentity::name), Ok("Knowledge Publisher"));
|
||||||
|
assert_eq!(identity.as_ref().map(crate::GitCommitIdentity::email), Ok("knowledge@example.invalid"));
|
||||||
|
assert_eq!(crate::GitCommitIdentity::new(" ", "knowledge@example.invalid"), Err(crate::GitWriteInputError::InvalidCommitIdentityName));
|
||||||
|
assert_eq!(
|
||||||
|
crate::GitCommitIdentity::new("Knowledge\nPublisher", "knowledge@example.invalid"),
|
||||||
|
Err(crate::GitWriteInputError::InvalidCommitIdentityName)
|
||||||
|
);
|
||||||
|
assert_eq!(crate::GitCommitIdentity::new("Knowledge Publisher", " "), Err(crate::GitWriteInputError::InvalidCommitIdentityEmail));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn target_branch_is_limited_to_valid_heads_references() {
|
||||||
|
let branch = crate::GitBranch::new("refs/heads/knowledge/proposal-42");
|
||||||
|
assert_eq!(branch.as_ref().map(crate::GitBranch::as_str), Ok("refs/heads/knowledge/proposal-42"));
|
||||||
|
for invalid in [
|
||||||
|
"main",
|
||||||
|
"refs/tags/v1",
|
||||||
|
"refs/heads/",
|
||||||
|
"refs/heads/.hidden",
|
||||||
|
"refs/heads/topic..next",
|
||||||
|
"refs/heads/topic.lock",
|
||||||
|
"refs/heads/topic@{next",
|
||||||
|
"refs/heads/topic\\next",
|
||||||
|
"refs/heads/topic//next",
|
||||||
|
] {
|
||||||
|
assert_eq!(crate::GitBranch::new(invalid), Err(crate::GitWriteInputError::InvalidBranch), "{invalid}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_request_requires_mutations_unique_paths_and_message() {
|
||||||
|
let identity_result = crate::GitCommitIdentity::new("Knowledge Publisher", "knowledge@example.invalid");
|
||||||
|
let identity = match identity_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let empty = crate::GitWriteRequest::new(crate::GitRevision::new("base"), Vec::new(), identity.clone(), "candidate");
|
||||||
|
assert_eq!(empty, Err(crate::GitWriteInputError::NoMutations));
|
||||||
|
let path_result = crate::GitMutationPath::new("content.md");
|
||||||
|
let path = match path_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let duplicates =
|
||||||
|
vec![crate::GitFileMutation::Write { path: path.clone(), content: b"one".to_vec() }, crate::GitFileMutation::Delete { path: path.clone() }];
|
||||||
|
let duplicate_request = crate::GitWriteRequest::new(crate::GitRevision::new("base"), duplicates, identity.clone(), "candidate");
|
||||||
|
assert_eq!(duplicate_request, Err(crate::GitWriteInputError::DuplicateMutationPath));
|
||||||
|
let mutation = crate::GitFileMutation::Write { path, content: b"content".to_vec() };
|
||||||
|
let invalid_message = crate::GitWriteRequest::new(crate::GitRevision::new("base"), vec![mutation.clone()], identity.clone(), " ");
|
||||||
|
assert_eq!(invalid_message, Err(crate::GitWriteInputError::InvalidCommitMessage));
|
||||||
|
let request = crate::GitWriteRequest::new(crate::GitRevision::new("base"), vec![mutation], identity, "candidate");
|
||||||
|
assert!(request.is_ok());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn push_request_models_absent_or_revision_remote_state() {
|
||||||
|
let branch_result = crate::GitBranch::new("refs/heads/proposals/example");
|
||||||
|
let branch = match branch_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let candidate = crate::GitRevision::new("candidate");
|
||||||
|
let create = crate::GitPushRequest::new(branch.clone(), crate::GitRemoteState::Absent, candidate.clone());
|
||||||
|
assert_eq!(create.target_branch(), &branch);
|
||||||
|
assert_eq!(create.expected_remote(), &crate::GitRemoteState::Absent);
|
||||||
|
assert_eq!(create.candidate_revision(), &candidate);
|
||||||
|
let existing = crate::GitPushRequest::new(branch, crate::GitRemoteState::Revision(crate::GitRevision::new("old")), candidate);
|
||||||
|
assert_eq!(existing.expected_remote(), &crate::GitRemoteState::Revision(crate::GitRevision::new("old")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn write_contract_is_separate_from_read_contract_and_provider_neutral() {
|
||||||
|
let source_result = knowledge_domain_lib::SourceId::new("snippets-extra");
|
||||||
|
let source_id = match source_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let path_result = crate::GitMutationPath::new("snippets/example.md");
|
||||||
|
let path = match path_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let identity_result = crate::GitCommitIdentity::new("Knowledge Publisher", "knowledge@example.invalid");
|
||||||
|
let identity = match identity_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let write_result = crate::GitWriteRequest::new(
|
||||||
|
crate::GitRevision::new("base"),
|
||||||
|
vec![crate::GitFileMutation::Write { path, content: b"snippet".to_vec() }],
|
||||||
|
identity,
|
||||||
|
"Add snippet",
|
||||||
|
);
|
||||||
|
let write_request = match write_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let mut writer = FakeGitWriter;
|
||||||
|
let candidate_result = crate::GitRepositoryWriter::create_candidate(&mut writer, &source_id, &write_request);
|
||||||
|
assert_eq!(candidate_result, Ok(crate::GitRevision::new("candidate:base")));
|
||||||
|
let candidate = match candidate_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let branch_result = crate::GitBranch::new("refs/heads/proposals/snippet");
|
||||||
|
let branch = match branch_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let push_request = crate::GitPushRequest::new(branch, crate::GitRemoteState::Absent, candidate.clone());
|
||||||
|
let push_result = crate::GitRepositoryWriter::push_candidate(&mut writer, &source_id, &push_request);
|
||||||
|
assert_eq!(push_result, Ok(crate::GitPushOutcome::Updated(candidate)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/knowledge-git-lib/src/backend.rs
|
// file: crates/knowledge-git-lib/src/backend.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
//! Physical source configuration, generated-state layout and controlled Git process runner.
|
//! Physical source configuration, generated-state layout and controlled Git process runner.
|
||||||
|
|
||||||
@@ -51,6 +51,16 @@ impl crate::GitBackendConfig {
|
|||||||
pub fn snapshots_path(&self) -> std::path::PathBuf {
|
pub fn snapshots_path(&self) -> std::path::PathBuf {
|
||||||
return self.state_root.join("snapshots");
|
return self.state_root.join("snapshots");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the directory reserved for temporary isolated write worktrees.
|
||||||
|
pub fn write_worktrees_path(&self) -> std::path::PathBuf {
|
||||||
|
return self.state_root.join("write-worktrees");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the controlled empty global Git configuration path.
|
||||||
|
pub fn empty_global_config_path(&self) -> std::path::PathBuf {
|
||||||
|
return self.state_root.join("empty-global.gitconfig");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coarse physical source kind used by the backend.
|
/// Coarse physical source kind used by the backend.
|
||||||
@@ -155,7 +165,9 @@ impl crate::GitMirror {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
struct GitCommandSpec {
|
struct GitCommandSpec {
|
||||||
arguments: Vec<std::ffi::OsString>,
|
arguments: Vec<std::ffi::OsString>,
|
||||||
|
environment: Vec<(std::ffi::OsString, std::ffi::OsString)>,
|
||||||
executable: std::path::PathBuf,
|
executable: std::path::PathBuf,
|
||||||
|
global_config_path: std::path::PathBuf,
|
||||||
hooks_path: std::path::PathBuf,
|
hooks_path: std::path::PathBuf,
|
||||||
operation: crate::GitOperation,
|
operation: crate::GitOperation,
|
||||||
}
|
}
|
||||||
@@ -183,9 +195,20 @@ impl GitCommandRunner for SystemGitRunner {
|
|||||||
command.env_remove("GIT_INDEX_FILE");
|
command.env_remove("GIT_INDEX_FILE");
|
||||||
command.env_remove("GIT_OBJECT_DIRECTORY");
|
command.env_remove("GIT_OBJECT_DIRECTORY");
|
||||||
command.env_remove("GIT_WORK_TREE");
|
command.env_remove("GIT_WORK_TREE");
|
||||||
|
command.env_remove("GIT_AUTHOR_NAME");
|
||||||
|
command.env_remove("GIT_AUTHOR_EMAIL");
|
||||||
|
command.env_remove("GIT_COMMITTER_NAME");
|
||||||
|
command.env_remove("GIT_COMMITTER_EMAIL");
|
||||||
|
command.env_remove("GIT_CONFIG_GLOBAL");
|
||||||
|
command.env_remove("GIT_CONFIG_NOSYSTEM");
|
||||||
|
command.env("GIT_CONFIG_GLOBAL", &specification.global_config_path);
|
||||||
|
command.env("GIT_CONFIG_NOSYSTEM", "1");
|
||||||
command.env("GIT_CONFIG_COUNT", "1");
|
command.env("GIT_CONFIG_COUNT", "1");
|
||||||
command.env("GIT_CONFIG_KEY_0", "core.hooksPath");
|
command.env("GIT_CONFIG_KEY_0", "core.hooksPath");
|
||||||
command.env("GIT_CONFIG_VALUE_0", &specification.hooks_path);
|
command.env("GIT_CONFIG_VALUE_0", &specification.hooks_path);
|
||||||
|
for (name, value) in &specification.environment {
|
||||||
|
command.env(name, value);
|
||||||
|
}
|
||||||
let output_result = command.output();
|
let output_result = command.output();
|
||||||
let output = match output_result {
|
let output = match output_result {
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
@@ -246,7 +269,7 @@ impl crate::GitBackend {
|
|||||||
source_id: &knowledge_domain_lib::SourceId,
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
revision: &knowledge_git_api::GitRevision,
|
revision: &knowledge_git_api::GitRevision,
|
||||||
) -> Result<std::path::PathBuf, crate::GitBackendError> {
|
) -> Result<std::path::PathBuf, crate::GitBackendError> {
|
||||||
if !is_safe_revision(revision.as_str()) {
|
if !crate::is_safe_revision(revision.as_str()) {
|
||||||
return Err(crate::GitBackendError::InvalidRevision);
|
return Err(crate::GitBackendError::InvalidRevision);
|
||||||
}
|
}
|
||||||
let source_key = crate::GitSourceKey::from_source_id(source_id);
|
let source_key = crate::GitSourceKey::from_source_id(source_id);
|
||||||
@@ -301,7 +324,7 @@ impl crate::GitBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn configured_source(&self, source_id: &knowledge_domain_lib::SourceId) -> Result<&crate::GitSourceConfig, crate::GitBackendError> {
|
pub(crate) fn configured_source(&self, source_id: &knowledge_domain_lib::SourceId) -> Result<&crate::GitSourceConfig, crate::GitBackendError> {
|
||||||
for source in &self.sources {
|
for source in &self.sources {
|
||||||
if source.source_id() == source_id {
|
if source.source_id() == source_id {
|
||||||
return Ok(source);
|
return Ok(source);
|
||||||
@@ -310,16 +333,44 @@ impl crate::GitBackend {
|
|||||||
return Err(crate::GitBackendError::SourceNotConfigured);
|
return Err(crate::GitBackendError::SourceNotConfigured);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_state_directories(&self) -> Result<(), crate::GitBackendError> {
|
pub(crate) fn prepare_state_directories(&self) -> Result<(), crate::GitBackendError> {
|
||||||
for path in [self.config.hooks_path(), self.config.repositories_path(), self.config.snapshots_path()] {
|
for path in [self.config.hooks_path(), self.config.repositories_path(), self.config.snapshots_path(), self.config.write_worktrees_path()] {
|
||||||
let create_result = std::fs::create_dir_all(path);
|
let create_result = std::fs::create_dir_all(path);
|
||||||
if create_result.is_err() {
|
if create_result.is_err() {
|
||||||
return Err(crate::GitBackendError::StatePreparationFailed);
|
return Err(crate::GitBackendError::StatePreparationFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let config_path = self.config.empty_global_config_path();
|
||||||
|
if !config_path.exists() {
|
||||||
|
let write_result = std::fs::write(config_path, b"");
|
||||||
|
if write_result.is_err() {
|
||||||
|
return Err(crate::GitBackendError::StatePreparationFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run_controlled_git(
|
||||||
|
&self,
|
||||||
|
operation: crate::GitOperation,
|
||||||
|
arguments: Vec<std::ffi::OsString>,
|
||||||
|
environment: Vec<(std::ffi::OsString, std::ffi::OsString)>,
|
||||||
|
) -> Result<Vec<u8>, crate::GitBackendError> {
|
||||||
|
let specification = GitCommandSpec {
|
||||||
|
arguments,
|
||||||
|
environment,
|
||||||
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
|
hooks_path: self.config.hooks_path(),
|
||||||
|
operation,
|
||||||
|
};
|
||||||
|
let result = self.runner.run(&specification);
|
||||||
|
match result {
|
||||||
|
Ok(output) => return Ok(output.stdout),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn clone_mirror(&self, source: &crate::GitSourceConfig, repository_path: &std::path::Path) -> Result<(), crate::GitBackendError> {
|
fn clone_mirror(&self, source: &crate::GitSourceConfig, repository_path: &std::path::Path) -> Result<(), crate::GitBackendError> {
|
||||||
let mut arguments = vec![std::ffi::OsString::from("clone"), std::ffi::OsString::from("--mirror")];
|
let mut arguments = vec![std::ffi::OsString::from("clone"), std::ffi::OsString::from("--mirror")];
|
||||||
if source.source_kind == crate::GitSourceKind::Local {
|
if source.source_kind == crate::GitSourceKind::Local {
|
||||||
@@ -330,7 +381,9 @@ impl crate::GitBackend {
|
|||||||
arguments.push(repository_path.as_os_str().to_os_string());
|
arguments.push(repository_path.as_os_str().to_os_string());
|
||||||
let specification = GitCommandSpec {
|
let specification = GitCommandSpec {
|
||||||
arguments,
|
arguments,
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::CloneMirror,
|
operation: crate::GitOperation::CloneMirror,
|
||||||
};
|
};
|
||||||
@@ -349,7 +402,9 @@ impl crate::GitBackend {
|
|||||||
std::ffi::OsString::from("rev-parse"),
|
std::ffi::OsString::from("rev-parse"),
|
||||||
std::ffi::OsString::from("--is-bare-repository"),
|
std::ffi::OsString::from("--is-bare-repository"),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::InspectMirror,
|
operation: crate::GitOperation::InspectMirror,
|
||||||
};
|
};
|
||||||
@@ -390,7 +445,9 @@ impl crate::GitBackend {
|
|||||||
std::ffi::OsString::from("update"),
|
std::ffi::OsString::from("update"),
|
||||||
std::ffi::OsString::from("--prune"),
|
std::ffi::OsString::from("--prune"),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::RefreshMirror,
|
operation: crate::GitOperation::RefreshMirror,
|
||||||
};
|
};
|
||||||
@@ -427,7 +484,9 @@ impl crate::GitBackend {
|
|||||||
std::ffi::OsString::from("--end-of-options"),
|
std::ffi::OsString::from("--end-of-options"),
|
||||||
std::ffi::OsString::from(revision_expression),
|
std::ffi::OsString::from(revision_expression),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::ResolveRevision,
|
operation: crate::GitOperation::ResolveRevision,
|
||||||
};
|
};
|
||||||
@@ -443,7 +502,7 @@ impl crate::GitBackend {
|
|||||||
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::ResolveRevision });
|
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::ResolveRevision });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if !is_safe_revision(stdout) {
|
if !crate::is_safe_revision(stdout) {
|
||||||
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::ResolveRevision });
|
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::ResolveRevision });
|
||||||
}
|
}
|
||||||
return Ok(knowledge_git_api::GitRevision::new(stdout));
|
return Ok(knowledge_git_api::GitRevision::new(stdout));
|
||||||
@@ -459,7 +518,7 @@ impl crate::GitBackend {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
}
|
}
|
||||||
if !is_safe_revision(revision.as_str()) {
|
if !crate::is_safe_revision(revision.as_str()) {
|
||||||
return Err(crate::GitBackendError::InvalidRevision);
|
return Err(crate::GitBackendError::InvalidRevision);
|
||||||
}
|
}
|
||||||
let mirror_result = self.inspect_mirror(source_id);
|
let mirror_result = self.inspect_mirror(source_id);
|
||||||
@@ -499,7 +558,9 @@ impl crate::GitBackend {
|
|||||||
snapshot_path.as_os_str().to_os_string(),
|
snapshot_path.as_os_str().to_os_string(),
|
||||||
std::ffi::OsString::from(revision.as_str()),
|
std::ffi::OsString::from(revision.as_str()),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::CreateSnapshot,
|
operation: crate::GitOperation::CreateSnapshot,
|
||||||
};
|
};
|
||||||
@@ -527,7 +588,9 @@ impl crate::GitBackend {
|
|||||||
std::ffi::OsString::from("--verify"),
|
std::ffi::OsString::from("--verify"),
|
||||||
std::ffi::OsString::from("HEAD"),
|
std::ffi::OsString::from("HEAD"),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::InspectSnapshot,
|
operation: crate::GitOperation::InspectSnapshot,
|
||||||
};
|
};
|
||||||
@@ -552,7 +615,9 @@ impl crate::GitBackend {
|
|||||||
std::ffi::OsString::from("--porcelain=v1"),
|
std::ffi::OsString::from("--porcelain=v1"),
|
||||||
std::ffi::OsString::from("--untracked-files=all"),
|
std::ffi::OsString::from("--untracked-files=all"),
|
||||||
],
|
],
|
||||||
|
environment: Vec::new(),
|
||||||
executable: self.config.git_executable().to_path_buf(),
|
executable: self.config.git_executable().to_path_buf(),
|
||||||
|
global_config_path: self.config.empty_global_config_path(),
|
||||||
hooks_path: self.config.hooks_path(),
|
hooks_path: self.config.hooks_path(),
|
||||||
operation: crate::GitOperation::InspectSnapshot,
|
operation: crate::GitOperation::InspectSnapshot,
|
||||||
};
|
};
|
||||||
@@ -592,7 +657,7 @@ impl knowledge_git_api::GitRepository for crate::GitBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_safe_revision(value: &str) -> bool {
|
pub(crate) fn is_safe_revision(value: &str) -> bool {
|
||||||
if value.is_empty() {
|
if value.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/knowledge-git-lib/src/error.rs
|
// file: crates/knowledge-git-lib/src/error.rs
|
||||||
// version: 3
|
// version: 5
|
||||||
|
|
||||||
//! Recoverable errors for controlled Git process execution.
|
//! Recoverable errors for controlled Git process execution.
|
||||||
|
|
||||||
@@ -18,6 +18,22 @@ pub enum GitOperation {
|
|||||||
CreateSnapshot,
|
CreateSnapshot,
|
||||||
/// Validate an existing detached snapshot before reuse.
|
/// Validate an existing detached snapshot before reuse.
|
||||||
InspectSnapshot,
|
InspectSnapshot,
|
||||||
|
/// Create one isolated detached worktree for candidate construction.
|
||||||
|
CreateWriteWorktree,
|
||||||
|
/// Stage the requested candidate mutations.
|
||||||
|
StageCandidate,
|
||||||
|
/// Inspect the candidate index, commit or parent relationship.
|
||||||
|
InspectCandidate,
|
||||||
|
/// Create the candidate commit with explicit identity.
|
||||||
|
CommitCandidate,
|
||||||
|
/// Retain the detached candidate commit under an internal backend reference.
|
||||||
|
PreserveCandidate,
|
||||||
|
/// Remove one backend-owned candidate worktree.
|
||||||
|
RemoveWriteWorktree,
|
||||||
|
/// Observe the current state of one remote branch.
|
||||||
|
InspectRemote,
|
||||||
|
/// Push one candidate revision with an explicit compare-and-exchange guard.
|
||||||
|
PushCandidate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for crate::GitOperation {
|
impl std::fmt::Display for crate::GitOperation {
|
||||||
@@ -29,6 +45,14 @@ impl std::fmt::Display for crate::GitOperation {
|
|||||||
crate::GitOperation::ResolveRevision => return formatter.write_str("resolve revision"),
|
crate::GitOperation::ResolveRevision => return formatter.write_str("resolve revision"),
|
||||||
crate::GitOperation::CreateSnapshot => return formatter.write_str("create snapshot"),
|
crate::GitOperation::CreateSnapshot => return formatter.write_str("create snapshot"),
|
||||||
crate::GitOperation::InspectSnapshot => return formatter.write_str("inspect snapshot"),
|
crate::GitOperation::InspectSnapshot => return formatter.write_str("inspect snapshot"),
|
||||||
|
crate::GitOperation::CreateWriteWorktree => return formatter.write_str("create write worktree"),
|
||||||
|
crate::GitOperation::StageCandidate => return formatter.write_str("stage candidate"),
|
||||||
|
crate::GitOperation::InspectCandidate => return formatter.write_str("inspect candidate"),
|
||||||
|
crate::GitOperation::CommitCandidate => return formatter.write_str("commit candidate"),
|
||||||
|
crate::GitOperation::PreserveCandidate => return formatter.write_str("preserve candidate"),
|
||||||
|
crate::GitOperation::RemoveWriteWorktree => return formatter.write_str("remove write worktree"),
|
||||||
|
crate::GitOperation::InspectRemote => return formatter.write_str("inspect remote branch"),
|
||||||
|
crate::GitOperation::PushCandidate => return formatter.write_str("push guarded candidate"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,6 +95,20 @@ pub enum GitBackendError {
|
|||||||
InvalidRevision,
|
InvalidRevision,
|
||||||
/// An existing snapshot does not match the requested revision or is not clean.
|
/// An existing snapshot does not match the requested revision or is not clean.
|
||||||
InvalidSnapshot,
|
InvalidSnapshot,
|
||||||
|
/// The deterministic backend-owned write worktree path already exists unexpectedly.
|
||||||
|
WriteWorktreeExists,
|
||||||
|
/// A mutation would traverse or replace a symbolic link.
|
||||||
|
UnsafeMutationTarget,
|
||||||
|
/// A requested deletion target does not exist.
|
||||||
|
MutationTargetMissing,
|
||||||
|
/// A filesystem mutation or parent-directory preparation failed.
|
||||||
|
MutationFilesystemFailed,
|
||||||
|
/// The staged candidate paths do not exactly match the requested mutation set.
|
||||||
|
CandidateMutationMismatch,
|
||||||
|
/// The resulting candidate commit does not have the requested base as its sole first parent.
|
||||||
|
InvalidCandidate,
|
||||||
|
/// A backend-owned write worktree could not be removed deterministically.
|
||||||
|
WriteCleanupFailed,
|
||||||
/// Git returned output that does not satisfy the backend contract.
|
/// Git returned output that does not satisfy the backend contract.
|
||||||
UnexpectedGitOutput {
|
UnexpectedGitOutput {
|
||||||
/// Operation whose output could not be validated.
|
/// Operation whose output could not be validated.
|
||||||
@@ -95,6 +133,13 @@ impl std::fmt::Display for crate::GitBackendError {
|
|||||||
crate::GitBackendError::MirrorMissing => return formatter.write_str("controlled git mirror does not exist"),
|
crate::GitBackendError::MirrorMissing => return formatter.write_str("controlled git mirror does not exist"),
|
||||||
crate::GitBackendError::InvalidRevision => return formatter.write_str("git revision is not a safe object identity"),
|
crate::GitBackendError::InvalidRevision => return formatter.write_str("git revision is not a safe object identity"),
|
||||||
crate::GitBackendError::InvalidSnapshot => return formatter.write_str("controlled git snapshot is invalid or dirty"),
|
crate::GitBackendError::InvalidSnapshot => return formatter.write_str("controlled git snapshot is invalid or dirty"),
|
||||||
|
crate::GitBackendError::WriteWorktreeExists => return formatter.write_str("controlled git write worktree already exists"),
|
||||||
|
crate::GitBackendError::UnsafeMutationTarget => return formatter.write_str("git mutation target traverses a symbolic link"),
|
||||||
|
crate::GitBackendError::MutationTargetMissing => return formatter.write_str("git deletion target does not exist"),
|
||||||
|
crate::GitBackendError::MutationFilesystemFailed => return formatter.write_str("git candidate filesystem mutation failed"),
|
||||||
|
crate::GitBackendError::CandidateMutationMismatch => return formatter.write_str("git candidate staged paths do not match requested mutations"),
|
||||||
|
crate::GitBackendError::InvalidCandidate => return formatter.write_str("git candidate commit does not match the requested base"),
|
||||||
|
crate::GitBackendError::WriteCleanupFailed => return formatter.write_str("controlled git write worktree cleanup failed"),
|
||||||
crate::GitBackendError::UnexpectedGitOutput { operation } => {
|
crate::GitBackendError::UnexpectedGitOutput { operation } => {
|
||||||
return write!(formatter, "git returned unexpected output while attempting to {operation}");
|
return write!(formatter, "git returned unexpected output while attempting to {operation}");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
// file: crates/knowledge-git-lib/src/lib.rs
|
// file: crates/knowledge-git-lib/src/lib.rs
|
||||||
// version: 3
|
// version: 5
|
||||||
|
|
||||||
//! Controlled Git process backend for Knowledge content sources.
|
//! Controlled Git process backend for Knowledge content sources.
|
||||||
//!
|
//!
|
||||||
//! This crate owns physical Git source configuration, generated-state layout, provider-neutral read
|
//! This crate owns physical Git source configuration, generated-state layout, provider-neutral read
|
||||||
//! materialization and process execution. It deliberately contains no forge API, persistence,
|
//! materialization, isolated candidate construction, guarded remote branch updates and process execution.
|
||||||
//! scheduler or application orchestration.
|
//! It deliberately contains no forge API, persistence, scheduler or application orchestration.
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod write;
|
||||||
|
|
||||||
/// Controlled Git backend that owns configured sources and local read materialization.
|
/// Controlled Git backend that owns configured sources and local read materialization.
|
||||||
pub use self::backend::GitBackend;
|
pub use self::backend::GitBackend;
|
||||||
@@ -28,3 +29,5 @@ pub use self::backend::GitSourceKind;
|
|||||||
pub use self::error::GitBackendError;
|
pub use self::error::GitBackendError;
|
||||||
/// Git operation category exposed by recoverable process errors without leaking command arguments.
|
/// Git operation category exposed by recoverable process errors without leaking command arguments.
|
||||||
pub use self::error::GitOperation;
|
pub use self::error::GitOperation;
|
||||||
|
|
||||||
|
pub(crate) use self::backend::is_safe_revision;
|
||||||
|
|||||||
505
crates/knowledge-git-lib/src/write.rs
Normal file
505
crates/knowledge-git-lib/src/write.rs
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
// file: crates/knowledge-git-lib/src/write.rs
|
||||||
|
// version: 2
|
||||||
|
|
||||||
|
//! Isolated candidate-commit creation and guarded remote publication for controlled Git sources.
|
||||||
|
|
||||||
|
impl crate::GitBackend {
|
||||||
|
/// Create one detached candidate commit from an explicit resolved base revision.
|
||||||
|
///
|
||||||
|
/// This operation does not update any remote branch. The candidate is retained by an internal
|
||||||
|
/// backend reference so a later guarded push can publish it without depending on worktree
|
||||||
|
/// lifetime or reflog retention.
|
||||||
|
pub fn create_candidate(
|
||||||
|
&mut self,
|
||||||
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
|
request: &knowledge_git_api::GitWriteRequest,
|
||||||
|
) -> Result<knowledge_git_api::GitRevision, crate::GitBackendError> {
|
||||||
|
if !crate::is_safe_revision(request.base_revision().as_str()) {
|
||||||
|
return Err(crate::GitBackendError::InvalidRevision);
|
||||||
|
}
|
||||||
|
let source_result = self.configured_source(source_id);
|
||||||
|
let source = match source_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let preparation_result = self.prepare_state_directories();
|
||||||
|
match preparation_result {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
let mirror_result = self.ensure_mirror(source);
|
||||||
|
let mirror = match mirror_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let source_key = crate::GitSourceKey::from_source_id(source_id);
|
||||||
|
let source_write_root = self.config().write_worktrees_path().join(source_key.as_str());
|
||||||
|
let worktree_path = source_write_root.join("candidate");
|
||||||
|
if worktree_path.exists() {
|
||||||
|
return Err(crate::GitBackendError::WriteWorktreeExists);
|
||||||
|
}
|
||||||
|
let create_parent_result = std::fs::create_dir_all(&source_write_root);
|
||||||
|
if create_parent_result.is_err() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
let add_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::CreateWriteWorktree,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
mirror.repository_path().as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("worktree"),
|
||||||
|
std::ffi::OsString::from("add"),
|
||||||
|
std::ffi::OsString::from("--detach"),
|
||||||
|
std::ffi::OsString::from("--"),
|
||||||
|
worktree_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from(request.base_revision().as_str()),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
match add_result {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
let candidate_result = self.populate_candidate_worktree(mirror.repository_path(), &worktree_path, request);
|
||||||
|
let cleanup_result = self.remove_candidate_worktree(mirror.repository_path(), &worktree_path, candidate_result.is_err());
|
||||||
|
match cleanup_result {
|
||||||
|
Ok(()) => return candidate_result,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate_candidate_worktree(
|
||||||
|
&self,
|
||||||
|
repository_path: &std::path::Path,
|
||||||
|
worktree_path: &std::path::Path,
|
||||||
|
request: &knowledge_git_api::GitWriteRequest,
|
||||||
|
) -> Result<knowledge_git_api::GitRevision, crate::GitBackendError> {
|
||||||
|
for mutation in request.mutations() {
|
||||||
|
let mutation_result = apply_mutation(worktree_path, mutation);
|
||||||
|
match mutation_result {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut add_arguments = vec![
|
||||||
|
std::ffi::OsString::from("-C"),
|
||||||
|
worktree_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("add"),
|
||||||
|
std::ffi::OsString::from("--all"),
|
||||||
|
std::ffi::OsString::from("--"),
|
||||||
|
];
|
||||||
|
for mutation in request.mutations() {
|
||||||
|
add_arguments.push(std::ffi::OsString::from(mutation.path().as_str()));
|
||||||
|
}
|
||||||
|
let add_result = self.run_controlled_git(crate::GitOperation::StageCandidate, add_arguments, Vec::new());
|
||||||
|
match add_result {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
let staged_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::InspectCandidate,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("-C"),
|
||||||
|
worktree_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("diff"),
|
||||||
|
std::ffi::OsString::from("--cached"),
|
||||||
|
std::ffi::OsString::from("--name-only"),
|
||||||
|
std::ffi::OsString::from("-z"),
|
||||||
|
std::ffi::OsString::from("--"),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
let staged = match staged_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
if !staged_paths_match(&staged, request.mutations()) {
|
||||||
|
return Err(crate::GitBackendError::CandidateMutationMismatch);
|
||||||
|
}
|
||||||
|
let identity = request.identity();
|
||||||
|
let environment = vec![
|
||||||
|
(std::ffi::OsString::from("GIT_AUTHOR_NAME"), std::ffi::OsString::from(identity.name())),
|
||||||
|
(std::ffi::OsString::from("GIT_AUTHOR_EMAIL"), std::ffi::OsString::from(identity.email())),
|
||||||
|
(std::ffi::OsString::from("GIT_COMMITTER_NAME"), std::ffi::OsString::from(identity.name())),
|
||||||
|
(std::ffi::OsString::from("GIT_COMMITTER_EMAIL"), std::ffi::OsString::from(identity.email())),
|
||||||
|
];
|
||||||
|
let commit_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::CommitCandidate,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("-C"),
|
||||||
|
worktree_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("commit"),
|
||||||
|
std::ffi::OsString::from("--no-verify"),
|
||||||
|
std::ffi::OsString::from("--no-gpg-sign"),
|
||||||
|
std::ffi::OsString::from("--cleanup=verbatim"),
|
||||||
|
std::ffi::OsString::from("-m"),
|
||||||
|
std::ffi::OsString::from(request.message()),
|
||||||
|
],
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
match commit_result {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
let candidate_result = self.resolve_worktree_revision(worktree_path, "HEAD", crate::GitOperation::InspectCandidate);
|
||||||
|
let candidate = match candidate_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let parent_result = self.resolve_worktree_revision(worktree_path, "HEAD^1", crate::GitOperation::InspectCandidate);
|
||||||
|
let parent = match parent_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
if &parent != request.base_revision() || candidate == parent {
|
||||||
|
return Err(crate::GitBackendError::InvalidCandidate);
|
||||||
|
}
|
||||||
|
let candidate_reference = format!("refs/knowledge/candidates/{}", candidate.as_str());
|
||||||
|
let preserve_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::PreserveCandidate,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("update-ref"),
|
||||||
|
std::ffi::OsString::from(candidate_reference),
|
||||||
|
std::ffi::OsString::from(candidate.as_str()),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
match preserve_result {
|
||||||
|
Ok(_) => return Ok(candidate),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_worktree_revision(
|
||||||
|
&self,
|
||||||
|
worktree_path: &std::path::Path,
|
||||||
|
expression: &str,
|
||||||
|
operation: crate::GitOperation,
|
||||||
|
) -> Result<knowledge_git_api::GitRevision, crate::GitBackendError> {
|
||||||
|
let output_result = self.run_controlled_git(
|
||||||
|
operation,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("-C"),
|
||||||
|
worktree_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("rev-parse"),
|
||||||
|
std::ffi::OsString::from("--verify"),
|
||||||
|
std::ffi::OsString::from("--end-of-options"),
|
||||||
|
std::ffi::OsString::from(expression),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
let output = match output_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let text_result = std::str::from_utf8(&output);
|
||||||
|
let text = match text_result {
|
||||||
|
Ok(value) => value.trim(),
|
||||||
|
Err(_) => return Err(crate::GitBackendError::UnexpectedGitOutput { operation }),
|
||||||
|
};
|
||||||
|
if !crate::is_safe_revision(text) {
|
||||||
|
return Err(crate::GitBackendError::UnexpectedGitOutput { operation });
|
||||||
|
}
|
||||||
|
return Ok(knowledge_git_api::GitRevision::new(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_candidate_worktree(&self, repository_path: &std::path::Path, worktree_path: &std::path::Path, force: bool) -> Result<(), crate::GitBackendError> {
|
||||||
|
let mut arguments = vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("worktree"),
|
||||||
|
std::ffi::OsString::from("remove"),
|
||||||
|
];
|
||||||
|
if force {
|
||||||
|
arguments.push(std::ffi::OsString::from("--force"));
|
||||||
|
}
|
||||||
|
arguments.push(std::ffi::OsString::from("--"));
|
||||||
|
arguments.push(worktree_path.as_os_str().to_os_string());
|
||||||
|
let remove_result = self.run_controlled_git(crate::GitOperation::RemoveWriteWorktree, arguments, Vec::new());
|
||||||
|
match remove_result {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(crate::GitBackendError::WriteCleanupFailed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt one guarded remote branch update for a retained candidate revision.
|
||||||
|
///
|
||||||
|
/// The remote is first observed for an early conflict result, then Git's server-side
|
||||||
|
/// `--force-with-lease=<ref>:<expected>` guard performs the authoritative compare-and-exchange.
|
||||||
|
/// A failed guarded push is observed again so a concurrent update is returned as a recoverable
|
||||||
|
/// [`knowledge_git_api::GitPushOutcome::Conflict`] instead of being collapsed into a process error.
|
||||||
|
pub fn push_candidate(
|
||||||
|
&mut self,
|
||||||
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
|
request: &knowledge_git_api::GitPushRequest,
|
||||||
|
) -> Result<knowledge_git_api::GitPushOutcome, crate::GitBackendError> {
|
||||||
|
if !crate::is_safe_revision(request.candidate_revision().as_str()) {
|
||||||
|
return Err(crate::GitBackendError::InvalidRevision);
|
||||||
|
}
|
||||||
|
let source_result = self.configured_source(source_id);
|
||||||
|
let source = match source_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let mirror_result = self.ensure_mirror(source);
|
||||||
|
let mirror = match mirror_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
let candidate_result = self.validate_retained_candidate(mirror.repository_path(), request.candidate_revision());
|
||||||
|
match candidate_result {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
let observed_result = self.observe_remote_state(mirror.repository_path(), request.target_branch());
|
||||||
|
let observed = match observed_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
if &observed != request.expected_remote() {
|
||||||
|
return Ok(knowledge_git_api::GitPushOutcome::Conflict { actual: observed });
|
||||||
|
}
|
||||||
|
let lease = match request.expected_remote() {
|
||||||
|
knowledge_git_api::GitRemoteState::Absent => format!("--force-with-lease={}:", request.target_branch().as_str()),
|
||||||
|
knowledge_git_api::GitRemoteState::Revision(revision) => {
|
||||||
|
if !crate::is_safe_revision(revision.as_str()) {
|
||||||
|
return Err(crate::GitBackendError::InvalidRevision);
|
||||||
|
}
|
||||||
|
format!("--force-with-lease={}:{}", request.target_branch().as_str(), revision.as_str())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let refspec = format!("{}:{}", request.candidate_revision().as_str(), request.target_branch().as_str());
|
||||||
|
let push_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::PushCandidate,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
mirror.repository_path().as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("-c"),
|
||||||
|
std::ffi::OsString::from("remote.origin.mirror=false"),
|
||||||
|
std::ffi::OsString::from("push"),
|
||||||
|
std::ffi::OsString::from("--porcelain"),
|
||||||
|
std::ffi::OsString::from("--no-verify"),
|
||||||
|
std::ffi::OsString::from(lease),
|
||||||
|
std::ffi::OsString::from("origin"),
|
||||||
|
std::ffi::OsString::from(refspec),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
match push_result {
|
||||||
|
Ok(_) => return Ok(knowledge_git_api::GitPushOutcome::Updated(request.candidate_revision().clone())),
|
||||||
|
Err(push_error) => {
|
||||||
|
let after_result = self.observe_remote_state(mirror.repository_path(), request.target_branch());
|
||||||
|
let after = match after_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Err(push_error),
|
||||||
|
};
|
||||||
|
if &after != request.expected_remote() {
|
||||||
|
if after == knowledge_git_api::GitRemoteState::Revision(request.candidate_revision().clone()) {
|
||||||
|
return Ok(knowledge_git_api::GitPushOutcome::Updated(request.candidate_revision().clone()));
|
||||||
|
}
|
||||||
|
return Ok(knowledge_git_api::GitPushOutcome::Conflict { actual: after });
|
||||||
|
}
|
||||||
|
return Err(push_error);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_retained_candidate(&self, repository_path: &std::path::Path, candidate: &knowledge_git_api::GitRevision) -> Result<(), crate::GitBackendError> {
|
||||||
|
let candidate_reference = format!("refs/knowledge/candidates/{}^{{commit}}", candidate.as_str());
|
||||||
|
let output_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::InspectCandidate,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("rev-parse"),
|
||||||
|
std::ffi::OsString::from("--verify"),
|
||||||
|
std::ffi::OsString::from("--end-of-options"),
|
||||||
|
std::ffi::OsString::from(candidate_reference),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
let output = match output_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Err(crate::GitBackendError::InvalidCandidate),
|
||||||
|
};
|
||||||
|
let text_result = std::str::from_utf8(&output);
|
||||||
|
let text = match text_result {
|
||||||
|
Ok(value) => value.trim(),
|
||||||
|
Err(_) => return Err(crate::GitBackendError::InvalidCandidate),
|
||||||
|
};
|
||||||
|
if text != candidate.as_str() {
|
||||||
|
return Err(crate::GitBackendError::InvalidCandidate);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn observe_remote_state(
|
||||||
|
&self,
|
||||||
|
repository_path: &std::path::Path,
|
||||||
|
branch: &knowledge_git_api::GitBranch,
|
||||||
|
) -> Result<knowledge_git_api::GitRemoteState, crate::GitBackendError> {
|
||||||
|
let output_result = self.run_controlled_git(
|
||||||
|
crate::GitOperation::InspectRemote,
|
||||||
|
vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("ls-remote"),
|
||||||
|
std::ffi::OsString::from("--refs"),
|
||||||
|
std::ffi::OsString::from("origin"),
|
||||||
|
std::ffi::OsString::from(branch.as_str()),
|
||||||
|
],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
let output = match output_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
if output.is_empty() {
|
||||||
|
return Ok(knowledge_git_api::GitRemoteState::Absent);
|
||||||
|
}
|
||||||
|
let text_result = std::str::from_utf8(&output);
|
||||||
|
let text = match text_result {
|
||||||
|
Ok(value) => value.trim(),
|
||||||
|
Err(_) => return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectRemote }),
|
||||||
|
};
|
||||||
|
let mut lines = text.lines();
|
||||||
|
let line = match lines.next() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Ok(knowledge_git_api::GitRemoteState::Absent),
|
||||||
|
};
|
||||||
|
if lines.next().is_some() {
|
||||||
|
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectRemote });
|
||||||
|
}
|
||||||
|
let mut fields = line.split('\t');
|
||||||
|
let revision = match fields.next() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectRemote }),
|
||||||
|
};
|
||||||
|
let remote_reference = match fields.next() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectRemote }),
|
||||||
|
};
|
||||||
|
if fields.next().is_some() || remote_reference != branch.as_str() || !crate::is_safe_revision(revision) {
|
||||||
|
return Err(crate::GitBackendError::UnexpectedGitOutput { operation: crate::GitOperation::InspectRemote });
|
||||||
|
}
|
||||||
|
return Ok(knowledge_git_api::GitRemoteState::Revision(knowledge_git_api::GitRevision::new(revision)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl knowledge_git_api::GitRepositoryWriter for crate::GitBackend {
|
||||||
|
type Error = crate::GitBackendError;
|
||||||
|
|
||||||
|
fn create_candidate(
|
||||||
|
&mut self,
|
||||||
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
|
request: &knowledge_git_api::GitWriteRequest,
|
||||||
|
) -> Result<knowledge_git_api::GitRevision, Self::Error> {
|
||||||
|
return crate::GitBackend::create_candidate(self, source_id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_candidate(
|
||||||
|
&mut self,
|
||||||
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
|
request: &knowledge_git_api::GitPushRequest,
|
||||||
|
) -> Result<knowledge_git_api::GitPushOutcome, Self::Error> {
|
||||||
|
return crate::GitBackend::push_candidate(self, source_id, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_mutation(worktree_path: &std::path::Path, mutation: &knowledge_git_api::GitFileMutation) -> Result<(), crate::GitBackendError> {
|
||||||
|
let target_result = checked_target_path(worktree_path, mutation.path());
|
||||||
|
let target = match target_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
};
|
||||||
|
match mutation {
|
||||||
|
knowledge_git_api::GitFileMutation::Write { content, .. } => {
|
||||||
|
let parent = match target.parent() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Err(crate::GitBackendError::MutationFilesystemFailed),
|
||||||
|
};
|
||||||
|
let create_result = std::fs::create_dir_all(parent);
|
||||||
|
if create_result.is_err() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
let write_result = std::fs::write(target, content);
|
||||||
|
if write_result.is_err() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
knowledge_git_api::GitFileMutation::Delete { .. } => {
|
||||||
|
if !target.exists() {
|
||||||
|
return Err(crate::GitBackendError::MutationTargetMissing);
|
||||||
|
}
|
||||||
|
let remove_result = std::fs::remove_file(target);
|
||||||
|
if remove_result.is_err() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checked_target_path(
|
||||||
|
worktree_path: &std::path::Path,
|
||||||
|
mutation_path: &knowledge_git_api::GitMutationPath,
|
||||||
|
) -> Result<std::path::PathBuf, crate::GitBackendError> {
|
||||||
|
let components: Vec<&str> = mutation_path.as_str().split('/').collect();
|
||||||
|
let mut current = worktree_path.to_path_buf();
|
||||||
|
for (index, component) in components.iter().enumerate() {
|
||||||
|
current.push(*component);
|
||||||
|
let metadata_result = std::fs::symlink_metadata(¤t);
|
||||||
|
let metadata = match metadata_result {
|
||||||
|
Ok(value) => Some(value),
|
||||||
|
Err(error) => {
|
||||||
|
if error.kind() == std::io::ErrorKind::NotFound {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let metadata = match metadata {
|
||||||
|
Some(value) => value,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
if metadata.file_type().is_symlink() {
|
||||||
|
return Err(crate::GitBackendError::UnsafeMutationTarget);
|
||||||
|
}
|
||||||
|
let is_final = index + 1 == components.len();
|
||||||
|
if !is_final && !metadata.is_dir() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
if is_final && metadata.is_dir() {
|
||||||
|
return Err(crate::GitBackendError::MutationFilesystemFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Ok(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn staged_paths_match(staged: &[u8], mutations: &[knowledge_git_api::GitFileMutation]) -> bool {
|
||||||
|
let mut observed = std::collections::HashSet::new();
|
||||||
|
for bytes in staged.split(|byte| return *byte == 0) {
|
||||||
|
if bytes.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let path_result = std::str::from_utf8(bytes);
|
||||||
|
let path = match path_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
observed.insert(path);
|
||||||
|
}
|
||||||
|
if observed.len() != mutations.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for mutation in mutations {
|
||||||
|
if !observed.contains(mutation.path().as_str()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
312
crates/knowledge-git-lib/tests/git_write_candidate.rs
Normal file
312
crates/knowledge-git-lib/tests/git_write_candidate.rs
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
// file: crates/knowledge-git-lib/tests/git_write_candidate.rs
|
||||||
|
// version: 2
|
||||||
|
|
||||||
|
//! Integration tests for isolated real Git candidate creation using local temporary repositories.
|
||||||
|
|
||||||
|
fn test_root(label: &str) -> std::path::PathBuf {
|
||||||
|
let nanos_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH);
|
||||||
|
let nanos = match nanos_result {
|
||||||
|
Ok(value) => value.as_nanos(),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
let mut path = std::env::temp_dir();
|
||||||
|
path.push(format!("sasedev-knowledge-git-write-{label}-{}-{nanos}", std::process::id()));
|
||||||
|
let _ = std::fs::remove_dir_all(&path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_git(repository: &std::path::Path, arguments: &[&str]) -> bool {
|
||||||
|
let mut command = std::process::Command::new("git");
|
||||||
|
command.current_dir(repository);
|
||||||
|
command.args(arguments);
|
||||||
|
command.env("GIT_TERMINAL_PROMPT", "0");
|
||||||
|
let status_result = command.status();
|
||||||
|
match status_result {
|
||||||
|
Ok(status) => return status.success(),
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_git_output(arguments: Vec<std::ffi::OsString>) -> Option<Vec<u8>> {
|
||||||
|
let mut command = std::process::Command::new("git");
|
||||||
|
command.args(arguments);
|
||||||
|
command.env("GIT_TERMINAL_PROMPT", "0");
|
||||||
|
let output_result = command.output();
|
||||||
|
let output = match output_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
if !output.status.success() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(output.stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_source(root: &std::path::Path) -> Option<std::path::PathBuf> {
|
||||||
|
let source = root.join("source");
|
||||||
|
if std::fs::create_dir_all(&source).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["init", "-b", "main"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["config", "user.name", "Ambient Identity"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["config", "user.email", "ambient@example.invalid"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["config", "commit.gpgsign", "false"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if std::fs::write(source.join("content.txt"), "first\n").is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if std::fs::write(source.join("delete.txt"), "delete me\n").is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["add", "content.txt", "delete.txt"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&source, &["commit", "-m", "first"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured_backend(root: &std::path::Path, source: &std::path::Path) -> Option<(knowledge_git_lib::GitBackend, knowledge_domain_lib::SourceId)> {
|
||||||
|
let source_id_result = knowledge_domain_lib::SourceId::new("integration-write-source");
|
||||||
|
let source_id = match source_id_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let config_result = knowledge_git_lib::GitBackendConfig::new(root.join("state"));
|
||||||
|
let config = match config_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let source_result = knowledge_git_lib::GitSourceConfig::local(source_id.clone(), source.to_path_buf());
|
||||||
|
let source_config = match source_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let mut backend = knowledge_git_lib::GitBackend::new(config);
|
||||||
|
if backend.register_source(source_config).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some((backend, source_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolved_main(backend: &mut knowledge_git_lib::GitBackend, source_id: &knowledge_domain_lib::SourceId) -> Option<knowledge_git_api::GitRevision> {
|
||||||
|
if knowledge_git_api::GitRepository::refresh(backend, source_id).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let reference = knowledge_git_api::GitReference::new("refs/heads/main");
|
||||||
|
let revision_result = knowledge_git_api::GitRepository::resolve_revision(backend, source_id, &reference);
|
||||||
|
match revision_result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutation_path(value: &str) -> Option<knowledge_git_api::GitMutationPath> {
|
||||||
|
let result = knowledge_git_api::GitMutationPath::new(value);
|
||||||
|
match result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit_identity() -> Option<knowledge_git_api::GitCommitIdentity> {
|
||||||
|
let result = knowledge_git_api::GitCommitIdentity::new("Knowledge Writer", "writer@example.invalid");
|
||||||
|
match result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn candidate_commit_applies_exact_mutations_uses_explicit_identity_and_cleans_worktree() {
|
||||||
|
let root = test_root("candidate");
|
||||||
|
let source_result = prepare_source(&root);
|
||||||
|
assert!(source_result.is_some());
|
||||||
|
let source = match source_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let backend_result = configured_backend(&root, &source);
|
||||||
|
assert!(backend_result.is_some());
|
||||||
|
let (mut backend, source_id) = match backend_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let base_result = resolved_main(&mut backend, &source_id);
|
||||||
|
assert!(base_result.is_some());
|
||||||
|
let base = match base_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let snapshot_result = knowledge_git_api::GitRepository::prepare_snapshot(&backend, &source_id, &base);
|
||||||
|
assert!(snapshot_result.is_ok());
|
||||||
|
let snapshot = match snapshot_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let content_path = match mutation_path("content.txt") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let nested_path = match mutation_path("nested/new.txt") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let delete_path = match mutation_path("delete.txt") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let identity = match commit_identity() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let request_result = knowledge_git_api::GitWriteRequest::new(
|
||||||
|
base.clone(),
|
||||||
|
vec![
|
||||||
|
knowledge_git_api::GitFileMutation::Write { path: content_path, content: b"second\n".to_vec() },
|
||||||
|
knowledge_git_api::GitFileMutation::Write { path: nested_path, content: b"nested\n".to_vec() },
|
||||||
|
knowledge_git_api::GitFileMutation::Delete { path: delete_path },
|
||||||
|
],
|
||||||
|
identity,
|
||||||
|
"candidate commit",
|
||||||
|
);
|
||||||
|
assert!(request_result.is_ok());
|
||||||
|
let request = match request_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let candidate_result = backend.create_candidate(&source_id, &request);
|
||||||
|
assert!(candidate_result.is_ok());
|
||||||
|
let candidate = match candidate_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_ne!(candidate, base);
|
||||||
|
let repository_path = backend.repository_path(&source_id);
|
||||||
|
let parent_output = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("rev-parse"),
|
||||||
|
std::ffi::OsString::from(format!("{}^1", candidate.as_str())),
|
||||||
|
]);
|
||||||
|
assert!(parent_output.is_some());
|
||||||
|
let parent = match parent_output {
|
||||||
|
Some(value) => String::from_utf8_lossy(&value).trim().to_owned(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
assert_eq!(parent, base.as_str());
|
||||||
|
let content_output = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("show"),
|
||||||
|
std::ffi::OsString::from(format!("{}:content.txt", candidate.as_str())),
|
||||||
|
]);
|
||||||
|
assert_eq!(content_output, Some(b"second\n".to_vec()));
|
||||||
|
let nested_output = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("show"),
|
||||||
|
std::ffi::OsString::from(format!("{}:nested/new.txt", candidate.as_str())),
|
||||||
|
]);
|
||||||
|
assert_eq!(nested_output, Some(b"nested\n".to_vec()));
|
||||||
|
let deleted_output = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("show"),
|
||||||
|
std::ffi::OsString::from(format!("{}:delete.txt", candidate.as_str())),
|
||||||
|
]);
|
||||||
|
assert!(deleted_output.is_none());
|
||||||
|
let identity_output = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("show"),
|
||||||
|
std::ffi::OsString::from("-s"),
|
||||||
|
std::ffi::OsString::from("--format=%an%x00%ae%x00%cn%x00%ce"),
|
||||||
|
std::ffi::OsString::from(candidate.as_str()),
|
||||||
|
]);
|
||||||
|
assert!(identity_output.is_some());
|
||||||
|
let identity_text = match identity_output {
|
||||||
|
Some(value) => String::from_utf8_lossy(&value).trim().to_owned(),
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
assert_eq!(identity_text, "Knowledge Writer\0writer@example.invalid\0Knowledge Writer\0writer@example.invalid");
|
||||||
|
let candidate_ref = format!("refs/knowledge/candidates/{}", candidate.as_str());
|
||||||
|
let retained = run_git_output(vec![
|
||||||
|
std::ffi::OsString::from("--git-dir"),
|
||||||
|
repository_path.as_os_str().to_os_string(),
|
||||||
|
std::ffi::OsString::from("show-ref"),
|
||||||
|
std::ffi::OsString::from("--verify"),
|
||||||
|
std::ffi::OsString::from(candidate_ref),
|
||||||
|
]);
|
||||||
|
assert!(retained.is_some());
|
||||||
|
let source_key = knowledge_git_lib::GitSourceKey::from_source_id(&source_id);
|
||||||
|
assert!(!backend.config().write_worktrees_path().join(source_key.as_str()).join("candidate").exists());
|
||||||
|
let snapshot_content = std::fs::read_to_string(snapshot.path().join("content.txt"));
|
||||||
|
assert!(matches!(snapshot_content.as_deref(), Ok("first\n")));
|
||||||
|
assert!(snapshot.path().join("delete.txt").is_file());
|
||||||
|
let _ = std::fs::remove_dir_all(&root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn candidate_rejects_symlink_escape_and_removes_owned_failed_worktree() {
|
||||||
|
let root = test_root("symlink");
|
||||||
|
let source_result = prepare_source(&root);
|
||||||
|
assert!(source_result.is_some());
|
||||||
|
let source = match source_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let outside = root.join("outside");
|
||||||
|
assert!(std::fs::create_dir_all(&outside).is_ok());
|
||||||
|
assert!(std::os::unix::fs::symlink(&outside, source.join("escape")).is_ok());
|
||||||
|
assert!(run_git(&source, &["add", "escape"]));
|
||||||
|
assert!(run_git(&source, &["commit", "-m", "add escape symlink"]));
|
||||||
|
let backend_result = configured_backend(&root, &source);
|
||||||
|
assert!(backend_result.is_some());
|
||||||
|
let (mut backend, source_id) = match backend_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let base_result = resolved_main(&mut backend, &source_id);
|
||||||
|
assert!(base_result.is_some());
|
||||||
|
let base = match base_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let path = match mutation_path("escape/pwned.txt") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let identity = match commit_identity() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let request_result = knowledge_git_api::GitWriteRequest::new(
|
||||||
|
base,
|
||||||
|
vec![knowledge_git_api::GitFileMutation::Write { path, content: b"forbidden\n".to_vec() }],
|
||||||
|
identity,
|
||||||
|
"must fail",
|
||||||
|
);
|
||||||
|
assert!(request_result.is_ok());
|
||||||
|
let request = match request_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let result = backend.create_candidate(&source_id, &request);
|
||||||
|
assert_eq!(result, Err(knowledge_git_lib::GitBackendError::UnsafeMutationTarget));
|
||||||
|
assert!(!outside.join("pwned.txt").exists());
|
||||||
|
let source_key = knowledge_git_lib::GitSourceKey::from_source_id(&source_id);
|
||||||
|
assert!(!backend.config().write_worktrees_path().join(source_key.as_str()).join("candidate").exists());
|
||||||
|
let _ = std::fs::remove_dir_all(&root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
282
crates/knowledge-git-lib/tests/git_write_push.rs
Normal file
282
crates/knowledge-git-lib/tests/git_write_push.rs
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// file: crates/knowledge-git-lib/tests/git_write_push.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Integration tests for guarded real Git pushes using local bare remotes.
|
||||||
|
|
||||||
|
fn test_root(label: &str) -> std::path::PathBuf {
|
||||||
|
let nanos_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH);
|
||||||
|
let nanos = match nanos_result {
|
||||||
|
Ok(value) => value.as_nanos(),
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
let mut path = std::env::temp_dir();
|
||||||
|
path.push(format!("sasedev-knowledge-git-push-{label}-{}-{nanos}", std::process::id()));
|
||||||
|
let _ = std::fs::remove_dir_all(&path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_git(repository: &std::path::Path, arguments: &[&str]) -> bool {
|
||||||
|
let mut command = std::process::Command::new("git");
|
||||||
|
command.current_dir(repository);
|
||||||
|
command.args(arguments);
|
||||||
|
command.env("GIT_TERMINAL_PROMPT", "0");
|
||||||
|
let status_result = command.status();
|
||||||
|
match status_result {
|
||||||
|
Ok(status) => return status.success(),
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_git_with_path(repository: &std::path::Path, arguments: &[&str], path: &std::path::Path) -> bool {
|
||||||
|
let mut command = std::process::Command::new("git");
|
||||||
|
command.current_dir(repository);
|
||||||
|
command.args(arguments);
|
||||||
|
command.arg(path);
|
||||||
|
command.env("GIT_TERMINAL_PROMPT", "0");
|
||||||
|
let status_result = command.status();
|
||||||
|
match status_result {
|
||||||
|
Ok(status) => return status.success(),
|
||||||
|
Err(_) => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_git_dir_output(repository: &std::path::Path, reference: &str) -> Option<String> {
|
||||||
|
let mut command = std::process::Command::new("git");
|
||||||
|
command.arg("--git-dir");
|
||||||
|
command.arg(repository);
|
||||||
|
command.arg("rev-parse");
|
||||||
|
command.arg("--verify");
|
||||||
|
command.arg(reference);
|
||||||
|
command.env("GIT_TERMINAL_PROMPT", "0");
|
||||||
|
let output_result = command.output();
|
||||||
|
let output = match output_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
if !output.status.success() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let text_result = std::str::from_utf8(&output.stdout);
|
||||||
|
match text_result {
|
||||||
|
Ok(value) => return Some(value.trim().to_owned()),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_remote(root: &std::path::Path) -> Option<(std::path::PathBuf, std::path::PathBuf)> {
|
||||||
|
let remote = root.join("remote.git");
|
||||||
|
let seed = root.join("seed");
|
||||||
|
if std::fs::create_dir_all(root).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git_with_path(root, &["init", "--bare"], &remote) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git_with_path(root, &["init", "-b", "main"], &seed) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["config", "user.name", "Remote Writer"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["config", "user.email", "remote@example.invalid"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["config", "commit.gpgsign", "false"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if std::fs::write(seed.join("content.txt"), "first\n").is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["add", "content.txt"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["commit", "-m", "first"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git_with_path(&seed, &["remote", "add", "origin"], &remote) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !run_git(&seed, &["push", "origin", "main"]) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some((remote, seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured_backend(root: &std::path::Path, remote: &std::path::Path) -> Option<(knowledge_git_lib::GitBackend, knowledge_domain_lib::SourceId)> {
|
||||||
|
let source_id_result = knowledge_domain_lib::SourceId::new("integration-push-source");
|
||||||
|
let source_id = match source_id_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let config_result = knowledge_git_lib::GitBackendConfig::new(root.join("state"));
|
||||||
|
let config = match config_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let source_result = knowledge_git_lib::GitSourceConfig::local(source_id.clone(), remote.to_path_buf());
|
||||||
|
let source_config = match source_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let mut backend = knowledge_git_lib::GitBackend::new(config);
|
||||||
|
if backend.register_source(source_config).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return Some((backend, source_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolved_main(backend: &mut knowledge_git_lib::GitBackend, source_id: &knowledge_domain_lib::SourceId) -> Option<knowledge_git_api::GitRevision> {
|
||||||
|
if knowledge_git_api::GitRepository::refresh(backend, source_id).is_err() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let reference = knowledge_git_api::GitReference::new("refs/heads/main");
|
||||||
|
let revision_result = knowledge_git_api::GitRepository::resolve_revision(backend, source_id, &reference);
|
||||||
|
match revision_result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mutation_path(value: &str) -> Option<knowledge_git_api::GitMutationPath> {
|
||||||
|
let result = knowledge_git_api::GitMutationPath::new(value);
|
||||||
|
match result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit_identity() -> Option<knowledge_git_api::GitCommitIdentity> {
|
||||||
|
let result = knowledge_git_api::GitCommitIdentity::new("Knowledge Writer", "writer@example.invalid");
|
||||||
|
match result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch(value: &str) -> Option<knowledge_git_api::GitBranch> {
|
||||||
|
let result = knowledge_git_api::GitBranch::new(value);
|
||||||
|
match result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn candidate_for(
|
||||||
|
backend: &mut knowledge_git_lib::GitBackend,
|
||||||
|
source_id: &knowledge_domain_lib::SourceId,
|
||||||
|
base: knowledge_git_api::GitRevision,
|
||||||
|
path: &str,
|
||||||
|
content: &[u8],
|
||||||
|
message: &str,
|
||||||
|
) -> Option<knowledge_git_api::GitRevision> {
|
||||||
|
let mutation = match mutation_path(path) {
|
||||||
|
Some(value) => knowledge_git_api::GitFileMutation::Write { path: value, content: content.to_vec() },
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
let identity = match commit_identity() {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
let request_result = knowledge_git_api::GitWriteRequest::new(base, vec![mutation], identity, message);
|
||||||
|
let request = match request_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let candidate_result = knowledge_git_api::GitRepositoryWriter::create_candidate(backend, source_id, &request);
|
||||||
|
match candidate_result {
|
||||||
|
Ok(value) => return Some(value),
|
||||||
|
Err(_) => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guarded_push_updates_expected_branch_and_creates_only_expected_absent_branch() {
|
||||||
|
let root = test_root("success");
|
||||||
|
let remote_result = prepare_remote(&root);
|
||||||
|
assert!(remote_result.is_some());
|
||||||
|
let (remote, _) = match remote_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let backend_result = configured_backend(&root, &remote);
|
||||||
|
assert!(backend_result.is_some());
|
||||||
|
let (mut backend, source_id) = match backend_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let base = match resolved_main(&mut backend, &source_id) {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let candidate = match candidate_for(&mut backend, &source_id, base.clone(), "content.txt", b"second\n", "second") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let main = match branch("refs/heads/main") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let push = knowledge_git_api::GitPushRequest::new(main, knowledge_git_api::GitRemoteState::Revision(base), candidate.clone());
|
||||||
|
let outcome = knowledge_git_api::GitRepositoryWriter::push_candidate(&mut backend, &source_id, &push);
|
||||||
|
assert_eq!(outcome, Ok(knowledge_git_api::GitPushOutcome::Updated(candidate.clone())));
|
||||||
|
assert_eq!(run_git_dir_output(&remote, "refs/heads/main"), Some(candidate.as_str().to_owned()));
|
||||||
|
let proposal_candidate = match candidate_for(&mut backend, &source_id, candidate, "proposal.txt", b"proposal\n", "proposal") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let proposal = match branch("refs/heads/proposal") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let create = knowledge_git_api::GitPushRequest::new(proposal, knowledge_git_api::GitRemoteState::Absent, proposal_candidate.clone());
|
||||||
|
let create_outcome = knowledge_git_api::GitRepositoryWriter::push_candidate(&mut backend, &source_id, &create);
|
||||||
|
assert_eq!(create_outcome, Ok(knowledge_git_api::GitPushOutcome::Updated(proposal_candidate.clone())));
|
||||||
|
assert_eq!(run_git_dir_output(&remote, "refs/heads/proposal"), Some(proposal_candidate.as_str().to_owned()));
|
||||||
|
let _ = std::fs::remove_dir_all(&root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn guarded_push_returns_conflict_after_concurrent_remote_change_without_overwrite() {
|
||||||
|
let root = test_root("conflict");
|
||||||
|
let remote_result = prepare_remote(&root);
|
||||||
|
assert!(remote_result.is_some());
|
||||||
|
let (remote, seed) = match remote_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let backend_result = configured_backend(&root, &remote);
|
||||||
|
assert!(backend_result.is_some());
|
||||||
|
let (mut backend, source_id) = match backend_result {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let base = match resolved_main(&mut backend, &source_id) {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let candidate = match candidate_for(&mut backend, &source_id, base.clone(), "content.txt", b"candidate\n", "candidate") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
assert!(std::fs::write(seed.join("content.txt"), "concurrent\n").is_ok());
|
||||||
|
assert!(run_git(&seed, &["add", "content.txt"]));
|
||||||
|
assert!(run_git(&seed, &["commit", "-m", "concurrent"]));
|
||||||
|
assert!(run_git(&seed, &["push", "origin", "main"]));
|
||||||
|
let concurrent_text = match run_git_dir_output(&remote, "refs/heads/main") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let concurrent = knowledge_git_api::GitRevision::new(concurrent_text.as_str());
|
||||||
|
assert_ne!(concurrent, base);
|
||||||
|
let main = match branch("refs/heads/main") {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let request = knowledge_git_api::GitPushRequest::new(main, knowledge_git_api::GitRemoteState::Revision(base), candidate);
|
||||||
|
let outcome = knowledge_git_api::GitRepositoryWriter::push_candidate(&mut backend, &source_id, &request);
|
||||||
|
assert_eq!(outcome, Ok(knowledge_git_api::GitPushOutcome::Conflict { actual: knowledge_git_api::GitRemoteState::Revision(concurrent.clone()) }));
|
||||||
|
assert_eq!(run_git_dir_output(&remote, "refs/heads/main"), Some(concurrent.as_str().to_owned()));
|
||||||
|
let _ = std::fs::remove_dir_all(&root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/knowledge-git-lib/unit_tests/backend.rs
|
// file: crates/knowledge-git-lib/unit_tests/backend.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct RecordingRunner {
|
struct RecordingRunner {
|
||||||
@@ -159,6 +159,9 @@ fn local_source_clone_uses_mirror_no_local_and_argument_separator() {
|
|||||||
assert!(backend.config().hooks_path().is_dir());
|
assert!(backend.config().hooks_path().is_dir());
|
||||||
assert!(backend.config().repositories_path().is_dir());
|
assert!(backend.config().repositories_path().is_dir());
|
||||||
assert!(backend.config().snapshots_path().is_dir());
|
assert!(backend.config().snapshots_path().is_dir());
|
||||||
|
assert!(backend.config().write_worktrees_path().is_dir());
|
||||||
|
assert!(backend.config().empty_global_config_path().is_file());
|
||||||
|
assert_eq!(calls[0].global_config_path, backend.config().empty_global_config_path());
|
||||||
let _ = std::fs::remove_dir_all(root);
|
let _ = std::fs::remove_dir_all(root);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
20
crates/knowledge-store-postgres-lib/Cargo.toml
Normal file
20
crates/knowledge-store-postgres-lib/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# file: crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
# version: 1
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "knowledge-store-postgres-lib"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
publish.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
deadpool-postgres = { version = "0.14.2", default-features = false, features = ["rt_tokio_1"] }
|
||||||
|
refinery = { version = "0.9.2", default-features = false, features = ["tokio-postgres"] }
|
||||||
|
tokio-postgres = { version = "0.7.18", default-features = false, features = ["runtime"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1.53.1", default-features = false, features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- file: crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
-- version: 1
|
||||||
|
|
||||||
|
CREATE TABLE knowledge_sources (
|
||||||
|
source_id TEXT PRIMARY KEY,
|
||||||
|
family TEXT NOT NULL CHECK (family IN ('pgr', 'howto', 'snippet', 'project_local')),
|
||||||
|
git_reference TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE knowledge_source_sync_state (
|
||||||
|
source_id TEXT PRIMARY KEY REFERENCES knowledge_sources(source_id) ON DELETE CASCADE,
|
||||||
|
last_synchronized_revision TEXT NOT NULL
|
||||||
|
);
|
||||||
39
crates/knowledge-store-postgres-lib/src/config.rs
Normal file
39
crates/knowledge-store-postgres-lib/src/config.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/src/config.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Explicit PostgreSQL pool configuration supplied by application composition.
|
||||||
|
|
||||||
|
/// Configuration required to construct the shared PostgreSQL store.
|
||||||
|
///
|
||||||
|
/// The type intentionally does not implement `Debug` because the database URL may contain a
|
||||||
|
/// credential. Callers must not log or persist the URL as diagnostic output.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PostgresStoreConfig {
|
||||||
|
database_url: String,
|
||||||
|
max_pool_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::PostgresStoreConfig {
|
||||||
|
/// Build a PostgreSQL configuration from an explicit connection URL and maximum pool size.
|
||||||
|
pub fn new(database_url: &str, max_pool_size: usize) -> Result<crate::PostgresStoreConfig, crate::PostgresStoreError> {
|
||||||
|
if database_url.trim().is_empty() {
|
||||||
|
return Err(crate::PostgresStoreError::EmptyDatabaseUrl);
|
||||||
|
}
|
||||||
|
if max_pool_size == 0 {
|
||||||
|
return Err(crate::PostgresStoreError::InvalidPoolSize);
|
||||||
|
}
|
||||||
|
return Ok(Self { database_url: database_url.to_owned(), max_pool_size });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn database_url(&self) -> &str {
|
||||||
|
return self.database_url.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn max_pool_size(&self) -> usize {
|
||||||
|
return self.max_pool_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "../unit_tests/config.rs"]
|
||||||
|
mod tests;
|
||||||
36
crates/knowledge-store-postgres-lib/src/error.rs
Normal file
36
crates/knowledge-store-postgres-lib/src/error.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/src/error.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Recoverable errors owned by the PostgreSQL storage boundary.
|
||||||
|
|
||||||
|
/// Recoverable failure returned by the PostgreSQL storage foundation.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PostgresStoreError {
|
||||||
|
/// The explicit database URL is empty or whitespace only.
|
||||||
|
EmptyDatabaseUrl,
|
||||||
|
/// The configured pool size is zero.
|
||||||
|
InvalidPoolSize,
|
||||||
|
/// The database URL cannot be parsed as PostgreSQL connection configuration.
|
||||||
|
InvalidDatabaseConfiguration,
|
||||||
|
/// The connection pool cannot be constructed from the supplied configuration.
|
||||||
|
PoolBuild,
|
||||||
|
/// A PostgreSQL connection cannot be acquired from the pool.
|
||||||
|
PoolAcquisition,
|
||||||
|
/// The embedded migration set cannot be applied.
|
||||||
|
Migration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for crate::PostgresStoreError {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
crate::PostgresStoreError::EmptyDatabaseUrl => return formatter.write_str("PostgreSQL database URL must not be empty"),
|
||||||
|
crate::PostgresStoreError::InvalidPoolSize => return formatter.write_str("PostgreSQL pool size must be greater than zero"),
|
||||||
|
crate::PostgresStoreError::InvalidDatabaseConfiguration => return formatter.write_str("PostgreSQL database configuration is invalid"),
|
||||||
|
crate::PostgresStoreError::PoolBuild => return formatter.write_str("PostgreSQL connection pool could not be constructed"),
|
||||||
|
crate::PostgresStoreError::PoolAcquisition => return formatter.write_str("PostgreSQL connection could not be acquired"),
|
||||||
|
crate::PostgresStoreError::Migration => return formatter.write_str("PostgreSQL migrations could not be applied"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for crate::PostgresStoreError {}
|
||||||
16
crates/knowledge-store-postgres-lib/src/lib.rs
Normal file
16
crates/knowledge-store-postgres-lib/src/lib.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! PostgreSQL storage foundation shared by SaseDev Knowledge applications.
|
||||||
|
//!
|
||||||
|
//! The crate owns explicit PostgreSQL configuration, the connection pool and versioned migrations.
|
||||||
|
//! It does not read process environment variables, expose driver types through its public API or
|
||||||
|
//! replace Git as the authority for published content.
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod error;
|
||||||
|
mod store;
|
||||||
|
|
||||||
|
pub use crate::config::PostgresStoreConfig;
|
||||||
|
pub use crate::error::PostgresStoreError;
|
||||||
|
pub use crate::store::PostgresStore;
|
||||||
44
crates/knowledge-store-postgres-lib/src/store.rs
Normal file
44
crates/knowledge-store-postgres-lib/src/store.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Connection-pool ownership and explicit migration execution.
|
||||||
|
|
||||||
|
mod embedded {
|
||||||
|
refinery::embed_migrations!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shared PostgreSQL store backed by a bounded Deadpool connection pool.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PostgresStore {
|
||||||
|
pool: deadpool_postgres::Pool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::PostgresStore {
|
||||||
|
/// Construct the PostgreSQL store without reading process environment variables.
|
||||||
|
pub fn new(config: crate::PostgresStoreConfig) -> Result<crate::PostgresStore, crate::PostgresStoreError> {
|
||||||
|
let postgres_config = match config.database_url().parse::<tokio_postgres::Config>() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Err(crate::PostgresStoreError::InvalidDatabaseConfiguration),
|
||||||
|
};
|
||||||
|
let manager = deadpool_postgres::Manager::new(postgres_config, tokio_postgres::NoTls);
|
||||||
|
let pool = match deadpool_postgres::Pool::builder(manager).max_size(config.max_pool_size()).build() {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Err(crate::PostgresStoreError::PoolBuild),
|
||||||
|
};
|
||||||
|
return Ok(Self { pool });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply every embedded versioned migration that has not yet been recorded by Refinery.
|
||||||
|
pub async fn apply_migrations(&self) -> Result<(), crate::PostgresStoreError> {
|
||||||
|
let mut connection = match self.pool.get().await {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return Err(crate::PostgresStoreError::PoolAcquisition),
|
||||||
|
};
|
||||||
|
let wrapper = std::ops::DerefMut::deref_mut(&mut connection);
|
||||||
|
let client = std::ops::DerefMut::deref_mut(wrapper);
|
||||||
|
match embedded::migrations::runner().run_async(client).await {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(crate::PostgresStoreError::Migration),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
// version: 2
|
||||||
|
|
||||||
|
//! Integration test for the initial PostgreSQL migration in an isolated schema.
|
||||||
|
|
||||||
|
fn test_schema_name() -> String {
|
||||||
|
let elapsed = match std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return format!("knowledge_test_{}", std::process::id()),
|
||||||
|
};
|
||||||
|
return format!("knowledge_test_{}_{}", std::process::id(), elapsed.as_nanos());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn database_url_with_search_path(database_url: &str, schema: &str) -> String {
|
||||||
|
let separator = if database_url.contains('?') { '&' } else { '?' };
|
||||||
|
return format!("{database_url}{separator}options=-c%20search_path%3D{schema}");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect(database_url: &str) -> Option<tokio_postgres::Client> {
|
||||||
|
let connection_result = tokio_postgres::connect(database_url, tokio_postgres::NoTls).await;
|
||||||
|
assert!(connection_result.is_ok(), "test PostgreSQL connection failed");
|
||||||
|
let (client, connection) = match connection_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let _connection_task = tokio::spawn(async move {
|
||||||
|
let _connection_result = connection.await;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
return Some(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[ignore = "requires KNOWLEDGE_POSTGRES_TEST_URL"]
|
||||||
|
async fn initial_migration_runs_in_isolated_schema() {
|
||||||
|
let database_url_result = std::env::var("KNOWLEDGE_POSTGRES_TEST_URL");
|
||||||
|
assert!(database_url_result.is_ok(), "KNOWLEDGE_POSTGRES_TEST_URL is required");
|
||||||
|
let database_url = match database_url_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let schema = test_schema_name();
|
||||||
|
let admin = match connect(database_url.as_str()).await {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let create_schema = format!("CREATE SCHEMA {schema}");
|
||||||
|
let create_result = admin.batch_execute(create_schema.as_str()).await;
|
||||||
|
assert!(create_result.is_ok(), "isolated PostgreSQL schema creation failed");
|
||||||
|
if create_result.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let isolated_url = database_url_with_search_path(database_url.as_str(), schema.as_str());
|
||||||
|
let config_result = knowledge_store_postgres_lib::PostgresStoreConfig::new(isolated_url.as_str(), 2);
|
||||||
|
assert!(config_result.is_ok(), "PostgreSQL store configuration failed");
|
||||||
|
let config = match config_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let store_result = knowledge_store_postgres_lib::PostgresStore::new(config);
|
||||||
|
assert!(store_result.is_ok(), "PostgreSQL store construction failed");
|
||||||
|
let store = match store_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
let migration_result = store.apply_migrations().await;
|
||||||
|
assert!(migration_result.is_ok(), "initial PostgreSQL migration failed");
|
||||||
|
let table_result = admin
|
||||||
|
.query(
|
||||||
|
"SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_name IN ('knowledge_sources', 'knowledge_source_sync_state', 'refinery_schema_history') ORDER BY table_name",
|
||||||
|
&[&schema],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert!(table_result.is_ok(), "PostgreSQL migration verification query failed");
|
||||||
|
let table_rows = match table_result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
assert_eq!(table_rows.len(), 3);
|
||||||
|
if table_rows.len() == 3 {
|
||||||
|
assert_eq!(table_rows[0].get::<usize, &str>(0), "knowledge_source_sync_state");
|
||||||
|
assert_eq!(table_rows[1].get::<usize, &str>(0), "knowledge_sources");
|
||||||
|
assert_eq!(table_rows[2].get::<usize, &str>(0), "refinery_schema_history");
|
||||||
|
}
|
||||||
|
drop(store);
|
||||||
|
let drop_schema = format!("DROP SCHEMA {schema} CASCADE");
|
||||||
|
let drop_result = admin.batch_execute(drop_schema.as_str()).await;
|
||||||
|
assert!(drop_result.is_ok(), "isolated PostgreSQL schema cleanup failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
23
crates/knowledge-store-postgres-lib/unit_tests/config.rs
Normal file
23
crates/knowledge-store-postgres-lib/unit_tests/config.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// file: crates/knowledge-store-postgres-lib/unit_tests/config.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_rejects_empty_database_url() {
|
||||||
|
let result = crate::PostgresStoreConfig::new(" ", 4);
|
||||||
|
assert!(matches!(result, Err(crate::PostgresStoreError::EmptyDatabaseUrl)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_rejects_zero_pool_size() {
|
||||||
|
let result = crate::PostgresStoreConfig::new("postgresql://localhost/knowledge", 0);
|
||||||
|
assert!(matches!(result, Err(crate::PostgresStoreError::InvalidPoolSize)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_accepts_explicit_non_empty_values() {
|
||||||
|
let result = crate::PostgresStoreConfig::new("postgresql://localhost/knowledge", 4);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
return;
|
||||||
|
}
|
||||||
184
deltas/0.0.3/alpha.1.fix.1.md
Normal file
184
deltas/0.0.3/alpha.1.fix.1.md
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.1.fix.1 — cycle documentaire et horizon
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.1` après application et commit/push de son history `PARTIAL`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Corriger le cadrage documentaire signalé par l'opérateur sans modifier la mission Git ni le code produit :
|
||||||
|
|
||||||
|
- retirer l'entrée `0.0.3 (candidate)` écrite trop tôt dans `CHANGELOG.md` ;
|
||||||
|
- codifier que le changelog de release est préparé vers la stabilisation à partir des changements réellement acquis ;
|
||||||
|
- conserver `prompts/003-V0_0_3_START_PROMPT.md` comme rattrapage valide, mais codifier que le prompt de `N+1` est normalement produit pendant les dernières phases de `N` après revue d'horizon ;
|
||||||
|
- transformer `ROADMAP.md` en vue de la release courante plus un horizon prévisionnel borné et explicitement révisable ;
|
||||||
|
- établir l'horizon actuel `0.0.4` à `0.0.7` sans les considérer comme des missions engagées ;
|
||||||
|
- mettre à jour le plan `0.0.3` afin que `beta.1` réévalue cet horizon et prépare le prochain prompt avant la stable si la mission suivante est suffisamment définie.
|
||||||
|
|
||||||
|
Aucun fichier sous `crates/` ou `Web/` n'est modifié.
|
||||||
|
|
||||||
|
## Horizon introduit
|
||||||
|
|
||||||
|
L'horizon actuel est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4 PostgreSQL partagé et migrations
|
||||||
|
0.0.5 contenu structuré et lecture publique
|
||||||
|
0.0.6 identité / autorisation / administration locale
|
||||||
|
0.0.7 propositions / revue / publication
|
||||||
|
```
|
||||||
|
|
||||||
|
Le statut `(h)` signifie horizon prévisionnel : numéro, ordre et scope peuvent être révisés lors des revues d'horizon ultérieures. Seule `0.0.3` est engagée aujourd'hui.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
CHANGELOG.md
|
||||||
|
ROADMAP.md
|
||||||
|
docs/rules/210-WORKFLOW.md
|
||||||
|
docs/rules/300-DOCUMENTATION.md
|
||||||
|
docs/templates/001-NEW_SESSION_PROMPT.md
|
||||||
|
docs/templates/002-RELEASE_PLAN.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/alpha.1.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
`prompts/003-V0_0_3_START_PROMPT.md` reste inchangé : il constitue le rattrapage nécessaire à l'ouverture de `0.0.3`.
|
||||||
|
|
||||||
|
Aucun history `alpha.1.fix.1` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## 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.1.fix.1'
|
||||||
|
grep -F 'version = "0.0.3-alpha.1.fix.1"' 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.1.fix.1-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 --workspace --all-targets --all-features
|
||||||
|
|
||||||
|
python3 - <<'PYGATE'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
|
||||||
|
assert '## 0.0.3' not in changelog
|
||||||
|
assert '## 0.0.2' in changelog
|
||||||
|
|
||||||
|
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
|
||||||
|
assert '- `( ) 0.0.3`' in roadmap
|
||||||
|
for version in ('0.0.4', '0.0.5', '0.0.6', '0.0.7'):
|
||||||
|
assert f'- `(h) {version}`' in roadmap, version
|
||||||
|
assert 'horizon prévisionnel' in roadmap
|
||||||
|
|
||||||
|
workflow = Path('docs/rules/210-WORKFLOW.md').read_text(encoding='utf-8')
|
||||||
|
assert 'pendant les dernières phases de `N`' in workflow
|
||||||
|
assert 'rattrapage et non le cycle normal' in workflow
|
||||||
|
|
||||||
|
documentation = Path('docs/rules/300-DOCUMENTATION.md').read_text(encoding='utf-8')
|
||||||
|
assert "Il n'est normalement pas rédigé au démarrage" in documentation
|
||||||
|
assert 'au moins les deux releases d\'horizon qui la suivent' in documentation
|
||||||
|
|
||||||
|
prompt_template = Path('docs/templates/001-NEW_SESSION_PROMPT.md').read_text(encoding='utf-8')
|
||||||
|
assert 'pendant les dernières phases de la release `N`' in prompt_template
|
||||||
|
assert 'MUST NOT inventer ces valeurs' in prompt_template
|
||||||
|
|
||||||
|
assert Path('prompts/003-V0_0_3_START_PROMPT.md').is_file()
|
||||||
|
assert not Path('history/0.0.3/alpha.1.fix.1.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.1.fix.1 documentation lifecycle 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.1.fix.1 text hygiene: clean ({len(paths)} file(s))')
|
||||||
|
PYTEXT
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates Web)"
|
||||||
|
test ! -e history/0.0.3/alpha.1.fix.1.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 ni contrat runtime n'est modifié.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/alpha.1.fix.1.md`, l'appliquer, commit/push `0.0.3-alpha.1.fix.1`, puis préparer `0.0.3-alpha.2`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Une correction bornée utilise `alpha.1.fix.2`; un problème qui invalide la trajectoire appelle un REPLAN.
|
||||||
173
deltas/0.0.3/alpha.1.fix.2.md
Normal file
173
deltas/0.0.3/alpha.1.fix.2.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.1.fix.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.1.fix.2 — complétion de la preuve opérateur
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.1.fix.1` après application et commit/push de son history `PARTIAL`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1.fix.2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Fermer uniquement la preuve restée incomplète sur `alpha.1.fix.1`.
|
||||||
|
|
||||||
|
Aucune décision fonctionnelle n'est modifiée et aucune fonctionnalité Git n'est ajoutée. Le fix :
|
||||||
|
|
||||||
|
- conserve le résultat `PARTIAL` de `alpha.1.fix.1` comme artefact historique ;
|
||||||
|
- met à jour le plan actif pour refléter cette transition ;
|
||||||
|
- rejoue la non-régression nécessaire après le bump de version ;
|
||||||
|
- exécute explicitement les assertions documentaires/horizon, l'hygiène texte, l'invariance du code produit et la politique des lockfiles ;
|
||||||
|
- termine par le staging requis.
|
||||||
|
|
||||||
|
Le développement du contrat d'écriture provider-neutral reste réservé à `alpha.2` après `PASS` de ce fix.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/alpha.1.fix.2.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Aucun fichier sous `crates/` ou `Web/` n'est modifié.
|
||||||
|
|
||||||
|
Aucun history `alpha.1.fix.2` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## 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.1.fix.2'
|
||||||
|
grep -F 'version = "0.0.3-alpha.1.fix.2"' 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.1.fix.2-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 --workspace --all-targets --all-features
|
||||||
|
|
||||||
|
python3 - <<'PYGATE'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
history = Path('history/0.0.3/alpha.1.fix.1.md').read_text(encoding='utf-8')
|
||||||
|
assert '\nPARTIAL\n' in history
|
||||||
|
|
||||||
|
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
|
||||||
|
assert '## 0.0.3' not in changelog
|
||||||
|
assert '## 0.0.2' in changelog
|
||||||
|
|
||||||
|
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
|
||||||
|
assert '- `( ) 0.0.3`' in roadmap
|
||||||
|
for version in ('0.0.4', '0.0.5', '0.0.6', '0.0.7'):
|
||||||
|
assert f'- `(h) {version}`' in roadmap, version
|
||||||
|
assert 'horizon prévisionnel' in roadmap
|
||||||
|
|
||||||
|
workflow = Path('docs/rules/210-WORKFLOW.md').read_text(encoding='utf-8')
|
||||||
|
assert 'pendant les dernières phases de `N`' in workflow
|
||||||
|
assert 'rattrapage et non le cycle normal' in workflow
|
||||||
|
|
||||||
|
documentation = Path('docs/rules/300-DOCUMENTATION.md').read_text(encoding='utf-8')
|
||||||
|
assert "Il n'est normalement pas rédigé au démarrage" in documentation
|
||||||
|
assert "au moins les deux releases d'horizon qui la suivent" in documentation
|
||||||
|
|
||||||
|
prompt_template = Path('docs/templates/001-NEW_SESSION_PROMPT.md').read_text(encoding='utf-8')
|
||||||
|
assert 'pendant les dernières phases de la release `N`' in prompt_template
|
||||||
|
assert 'MUST NOT inventer ces valeurs' in prompt_template
|
||||||
|
|
||||||
|
plan = Path('docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md').read_text(encoding='utf-8')
|
||||||
|
assert '### alpha.1.fix.2 — complétion de la preuve opérateur' in plan
|
||||||
|
assert 'Un `PASS` complet de `alpha.1.fix.2` autorise `0.0.3-alpha.2`' in plan
|
||||||
|
|
||||||
|
assert Path('prompts/003-V0_0_3_START_PROMPT.md').is_file()
|
||||||
|
assert not Path('history/0.0.3/alpha.1.fix.2.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.1.fix.2 completion 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.1.fix.2 text hygiene: clean ({len(paths)} file(s))')
|
||||||
|
PYTEXT
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates Web)"
|
||||||
|
test ! -e history/0.0.3/alpha.1.fix.2.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 ni contrat runtime n'est modifié.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/alpha.1.fix.2.md`, l'appliquer, commit/push `0.0.3-alpha.1.fix.2`, puis préparer `0.0.3-alpha.2`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Une correction bornée utilise `alpha.1.fix.3`; `.fix.3` déclenche en outre la revue `REPLAN` prévue par le plan actif.
|
||||||
288
deltas/0.0.3/alpha.1.md
Normal file
288
deltas/0.0.3/alpha.1.md
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.1 — cadrage de l'écriture Git contrôlée
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de la stable autoritaire :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tag Knowledge v0.0.2
|
||||||
|
commit Knowledge 183d354a67bec6d2d512c4898f5189759a6120b0
|
||||||
|
archive SHA-256 0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce
|
||||||
|
publish report sasedev-knowledge-0.0.2-publish.001-report.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
La stable `0.0.2` ne contenait volontairement aucun prompt `0.0.3`, car sa revue d'horizon n'avait engagé aucune prochaine mission. La présente session a ouvert explicitement une nouvelle mission depuis cette baseline ; le prompt versionné correspondant est donc matérialisé par cette tranche :
|
||||||
|
|
||||||
|
```text
|
||||||
|
prompts/003-V0_0_3_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
PGR `v0.1.3` reste une provenance historique. Aucune version PGR ultérieure n'est consultée ou adoptée.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Fermer le cadrage de la prochaine release avant toute modification fonctionnelle :
|
||||||
|
|
||||||
|
- vérifier la stable `v0.0.2`, ses règles/owners, architecture, deltas/histories et `DEFER` ;
|
||||||
|
- sélectionner l'écriture Git générique contrôlée comme mission `0.0.3` ;
|
||||||
|
- conserver le processus `git` contrôlé déjà validé en `0.0.2` ;
|
||||||
|
- choisir un worktree d'écriture isolé pour créer une révision candidate ;
|
||||||
|
- décider que le contrat d'écriture restera distinct du contrat `GitRepository` de lecture ;
|
||||||
|
- borner la mise à jour distante aux branches `refs/heads/...` avec attente explicite sur leur état précédent ;
|
||||||
|
- garder une divergence comme conflit récupérable sans rebase/merge automatique ;
|
||||||
|
- établir l'étude, le plan, le prompt local et la gate de release.
|
||||||
|
|
||||||
|
Aucun code produit, aucune crate, aucune dépendance Cargo/npm et aucun fichier Web ne sont ajoutés ou modifiés dans cette tranche.
|
||||||
|
|
||||||
|
## Décision d'horizon
|
||||||
|
|
||||||
|
La prochaine release engagée est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3 — écriture Git contrôlée et push gardé
|
||||||
|
```
|
||||||
|
|
||||||
|
Les domaines PostgreSQL, proposition/revue, rendu/publication produit, recherche, Gitea, auth/admin et opérations restent non numérotés.
|
||||||
|
|
||||||
|
`0.0.3` ne ferme pas le workflow de proposition complet. Elle fournit la primitive Git générique dont ce workflow aura besoin : base explicite, mutations confinées, commit candidat et mise à jour distante gardée.
|
||||||
|
|
||||||
|
## Contrats retenus
|
||||||
|
|
||||||
|
L'étude :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md
|
||||||
|
```
|
||||||
|
|
||||||
|
retient :
|
||||||
|
|
||||||
|
- un worktree d'écriture isolé sous la racine d'état contrôlée ;
|
||||||
|
- des mutations de fichiers par chemins relatifs validés ;
|
||||||
|
- une identité auteur/committer explicite ;
|
||||||
|
- hooks et prompts neutralisés ;
|
||||||
|
- une nouvelle révision candidate avant tout push ;
|
||||||
|
- un push de branche avec comparaison de l'état distant attendu ;
|
||||||
|
- conflit explicite si la branche a divergé ;
|
||||||
|
- aucun `push --force` non gardé ;
|
||||||
|
- aucun rebase/merge automatique ;
|
||||||
|
- aucune forge obligatoire.
|
||||||
|
|
||||||
|
Le plan actif est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
avec trajectoire prévisionnelle `alpha.1` à `alpha.4`, puis `beta.1`, `rc.1` et `rel.1`.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/000-README.md
|
||||||
|
docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
prompts/003-V0_0_3_START_PROMPT.md
|
||||||
|
deltas/0.0.3/alpha.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Aucun fichier sous `crates/` ou `Web/` n'est modifié.
|
||||||
|
|
||||||
|
Aucun history `0.0.3-alpha.1` n'est inclus : il doit refléter la gate opérateur réelle.
|
||||||
|
|
||||||
|
## 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.1'
|
||||||
|
grep -F 'version = "0.0.3-alpha.1"' 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.1-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 --workspace --all-targets --all-features
|
||||||
|
|
||||||
|
python3 - <<'PYGATE'
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
assert Path('docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md').is_file()
|
||||||
|
assert Path('docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md').is_file()
|
||||||
|
assert Path('prompts/003-V0_0_3_START_PROMPT.md').is_file()
|
||||||
|
assert Path('deltas/0.0.3/alpha.1.md').is_file()
|
||||||
|
assert not Path('history/0.0.3/alpha.1.md').exists()
|
||||||
|
|
||||||
|
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
|
||||||
|
assert '- `( ) 0.0.3`' in roadmap
|
||||||
|
assert '0.0.4' not in roadmap
|
||||||
|
|
||||||
|
rules = Path('RULES.md').read_text(encoding='utf-8')
|
||||||
|
for phrase in (
|
||||||
|
'Écriture Git contrôlée',
|
||||||
|
'MUST viser une branche `refs/heads/...`',
|
||||||
|
'MUST NOT effectuer un `push --force` non gardé',
|
||||||
|
):
|
||||||
|
assert phrase in rules, phrase
|
||||||
|
|
||||||
|
study = Path('docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md').read_text(encoding='utf-8')
|
||||||
|
for phrase in (
|
||||||
|
'worktree d\'écriture isolé',
|
||||||
|
'comparaison-et-échange',
|
||||||
|
'aucun rebase ou merge automatique',
|
||||||
|
'PGR `v0.1.3` reste une provenance historique',
|
||||||
|
):
|
||||||
|
assert phrase in study, phrase
|
||||||
|
|
||||||
|
plan = Path('docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md').read_text(encoding='utf-8')
|
||||||
|
headings = (
|
||||||
|
'## Statut et sources',
|
||||||
|
'## Mission',
|
||||||
|
'## État acquis et questions ouvertes',
|
||||||
|
'## Scope',
|
||||||
|
'## Hors scope',
|
||||||
|
'## Stratégie de validation',
|
||||||
|
'## Trajectoire prévisionnelle',
|
||||||
|
'## Déclencheurs de REPLAN',
|
||||||
|
'## Clôture et transition',
|
||||||
|
'## Revue d\'horizon',
|
||||||
|
)
|
||||||
|
positions = [plan.index(value) for value in headings]
|
||||||
|
assert positions == sorted(positions)
|
||||||
|
for tranche in ('alpha.1', 'alpha.2', 'alpha.3', 'alpha.4', 'beta.1', 'rc.1', 'rel.1'):
|
||||||
|
assert f'### {tranche} —' in plan, tranche
|
||||||
|
|
||||||
|
prompt = Path('prompts/003-V0_0_3_START_PROMPT.md').read_text(encoding='utf-8')
|
||||||
|
assert 'Ne pas rechercher, charger ni adopter automatiquement une nouvelle version de PGR.' in prompt
|
||||||
|
assert '0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce' in prompt
|
||||||
|
|
||||||
|
metadata = json.loads(Path('/tmp/sasedev-knowledge-0.0.3-alpha.1-metadata.json').read_text(encoding='utf-8'))
|
||||||
|
expected = {
|
||||||
|
'knowledge-app-lib',
|
||||||
|
'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 {package['name'] for package in metadata['packages']} == expected
|
||||||
|
assert len(metadata['workspace_members']) == 9
|
||||||
|
|
||||||
|
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.1 framing 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.1 text hygiene: clean ({len(paths)} file(s))')
|
||||||
|
PYTEXT
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates Web)"
|
||||||
|
test ! -e history/0.0.3/alpha.1.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 dans `alpha.1` : aucun contrat Web ni fichier `Web/` n'est modifié, et la stable `0.0.2` a déjà acquis une gate Web/runtime complète. Cette omission est conforme à la validation proportionnée locale.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire séparément :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
avec les résultats réellement observés. Après application du history, commit/push `0.0.3-alpha.1`, puis seulement préparer `0.0.3-alpha.2`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Produire le history réel avec son statut, l'appliquer et le committer avant toute correction.
|
||||||
|
|
||||||
|
Une correction bornée à ce cadrage utilise :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Si le diagnostic invalide la mission, l'ownership ou la stratégie d'écriture, exécuter un REPLAN plutôt que d'absorber une nouvelle responsabilité dans un fix.
|
||||||
232
deltas/0.0.3/alpha.2.md
Normal file
232
deltas/0.0.3/alpha.2.md
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.2 — contrat d'écriture Git provider-neutral
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.1.fix.2` après application, commit et push de son history `PASS`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Matérialiser uniquement la frontière publique provider-neutral nécessaire aux futures opérations d'écriture Git, sans implémenter encore le worktree d'écriture ou le push distant réel.
|
||||||
|
|
||||||
|
La tranche :
|
||||||
|
|
||||||
|
- conserve `GitRepository` comme contrat de lecture inchangé ;
|
||||||
|
- ajoute `GitRepositoryWriter` comme capacité distincte afin de ne pas imposer l'écriture aux consommateurs/fakes lecture seule ;
|
||||||
|
- introduit `GitMutationPath` pour des chemins relatifs normalisés, sans `.` / `..`, séparateur plateforme, chemin absolu, drive Windows ou composant `.git` ;
|
||||||
|
- introduit `GitFileMutation` pour écrire/créer/remplacer ou supprimer un fichier ;
|
||||||
|
- introduit `GitCommitIdentity` et exige une identité explicite ;
|
||||||
|
- introduit `GitBranch` et borne la publication à une référence complète valide sous `refs/heads/...` ;
|
||||||
|
- introduit `GitRemoteState` pour représenter explicitement branche absente ou révision attendue ;
|
||||||
|
- introduit `GitWriteRequest`, `GitPushRequest` et `GitPushOutcome` pour séparer création de candidate, mise à jour distante gardée et conflit ;
|
||||||
|
- rejette une requête sans mutation, un message vide/NUL et plusieurs mutations visant le même chemin ;
|
||||||
|
- documente la matérialisation de la frontière sans déplacer les responsabilités backend prévues pour `alpha.3` / `alpha.4`.
|
||||||
|
|
||||||
|
Aucun processus Git d'écriture n'est lancé par cette tranche.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
crates/knowledge-git-api/src/lib.rs
|
||||||
|
crates/knowledge-git-api/unit_tests/lib.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-api/src/write.rs
|
||||||
|
deltas/0.0.3/alpha.2.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Aucun fichier sous `knowledge-git-lib`, `knowledge-app-lib` ou `Web/` n'est modifié.
|
||||||
|
|
||||||
|
Aucun history `alpha.2` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## Contrat public matérialisé
|
||||||
|
|
||||||
|
Le contrat expose les valeurs suivantes depuis la façade `knowledge_git_api` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
GitWriteInputError
|
||||||
|
GitMutationPath
|
||||||
|
GitFileMutation
|
||||||
|
GitCommitIdentity
|
||||||
|
GitBranch
|
||||||
|
GitRemoteState
|
||||||
|
GitWriteRequest
|
||||||
|
GitPushRequest
|
||||||
|
GitPushOutcome
|
||||||
|
GitRepositoryWriter
|
||||||
|
```
|
||||||
|
|
||||||
|
Le chemin conceptuel reste :
|
||||||
|
|
||||||
|
```text
|
||||||
|
GitRevision de base
|
||||||
|
+ mutations confinées
|
||||||
|
+ identité/message explicites
|
||||||
|
-> create_candidate
|
||||||
|
-> GitRevision candidate
|
||||||
|
|
||||||
|
GitBranch
|
||||||
|
+ GitRemoteState attendu
|
||||||
|
+ GitRevision candidate
|
||||||
|
-> push_candidate
|
||||||
|
-> Updated ou Conflict
|
||||||
|
```
|
||||||
|
|
||||||
|
`alpha.2` définit ce contrat mais ne prétend pas encore prouver le confinement filesystem face aux symlinks, la neutralisation des hooks, la création effective d'un commit ou le compare-and-exchange distant. Ces preuves appartiennent au backend `knowledge-git-lib` des tranches suivantes.
|
||||||
|
|
||||||
|
## 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.2'
|
||||||
|
grep -F 'version = "0.0.3-alpha.2"' 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.2-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 --workspace --all-targets --all-features
|
||||||
|
|
||||||
|
python3 - <<'PYGATE'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
api = Path('crates/knowledge-git-api/src/lib.rs').read_text(encoding='utf-8')
|
||||||
|
write = Path('crates/knowledge-git-api/src/write.rs').read_text(encoding='utf-8')
|
||||||
|
read_contract = Path('crates/knowledge-git-api/src/repository.rs').read_text(encoding='utf-8')
|
||||||
|
manifest = Path('crates/knowledge-git-api/Cargo.toml').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 item in (
|
||||||
|
'GitWriteInputError',
|
||||||
|
'GitMutationPath',
|
||||||
|
'GitFileMutation',
|
||||||
|
'GitCommitIdentity',
|
||||||
|
'GitBranch',
|
||||||
|
'GitRemoteState',
|
||||||
|
'GitWriteRequest',
|
||||||
|
'GitPushRequest',
|
||||||
|
'GitPushOutcome',
|
||||||
|
'GitRepositoryWriter',
|
||||||
|
):
|
||||||
|
assert item in api, item
|
||||||
|
assert item in write, item
|
||||||
|
|
||||||
|
assert 'fn refresh(' in read_contract
|
||||||
|
assert 'fn resolve_revision(' in read_contract
|
||||||
|
assert 'fn prepare_snapshot(' in read_contract
|
||||||
|
assert 'create_candidate' not in read_contract
|
||||||
|
assert 'push_candidate' not in read_contract
|
||||||
|
|
||||||
|
assert 'refs/heads/' in write
|
||||||
|
assert 'ProtectedGitPath' in write
|
||||||
|
assert 'DuplicateMutationPath' in write
|
||||||
|
assert 'GitRemoteState::Absent' not in write or 'GitRemoteState' in write
|
||||||
|
assert 'rebase' not in write.lower()
|
||||||
|
assert 'merge' not in write.lower()
|
||||||
|
|
||||||
|
for forbidden in ('actix', 'tokio', 'gitea', 'gitlab', 'github', 'postgres', 'sqlx', 'diesel'):
|
||||||
|
assert forbidden not in manifest.lower(), forbidden
|
||||||
|
|
||||||
|
assert '`alpha.1.fix.2` a complété la gate documentaire et technique en `PASS`' in plan
|
||||||
|
assert '### alpha.2 — contrat d\'écriture provider-neutral' in plan
|
||||||
|
assert '## 0.0.3' not in changelog
|
||||||
|
|
||||||
|
assert Path('history/0.0.3/alpha.1.fix.2.md').is_file()
|
||||||
|
assert not Path('history/0.0.3/alpha.2.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.2 write-contract 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.2 text hygiene: clean ({len(paths)} file(s))')
|
||||||
|
PYTEXT
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates/knowledge-git-lib crates/knowledge-app-lib Web)"
|
||||||
|
test ! -e history/0.0.3/alpha.2.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 : `Web/`, les launchers et leur contrat runtime ne sont pas modifiés.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/alpha.2.md`, l'appliquer, commit/push `0.0.3-alpha.2`, puis préparer `0.0.3-alpha.3` pour le worktree d'écriture isolé et le commit candidat réel dans `knowledge-git-lib`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Une correction bornée utilise `alpha.2.fix.N` ; une modification de la mission ou de la frontière d'ownership exige un `REPLAN` explicite plutôt qu'un fix absorbant une nouvelle fonctionnalité.
|
||||||
173
deltas/0.0.3/alpha.3.fix.1.md
Normal file
173
deltas/0.0.3/alpha.3.fix.1.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.3.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.3.fix.1 — correction du test de commit candidat
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.3` après application, commit et push de son history `FAIL`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.3.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Corriger uniquement le défaut de compilation observé dans `crates/knowledge-git-lib/tests/git_write_candidate.rs` pendant la gate `alpha.3`.
|
||||||
|
|
||||||
|
L'assertion fautive comparait directement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Result<String, std::io::Error>
|
||||||
|
```
|
||||||
|
|
||||||
|
à un `Ok(String)`. Cette forme exige `PartialEq` sur `std::io::Error`, qui n'est pas implémenté.
|
||||||
|
|
||||||
|
Le fix remplace cette comparaison par une vérification de motif sur `snapshot_content.as_deref()` afin de valider uniquement le cas `Ok("first\n")` sans comparer la variante d'erreur.
|
||||||
|
|
||||||
|
Aucune logique de `knowledge-git-lib` n'est modifiée. Le worktree, les mutations, le commit candidat, la rétention de référence, le nettoyage et les contrats publics restent byte-for-byte identiques à `alpha.3`.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
crates/knowledge-git-lib/tests/git_write_candidate.rs
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/alpha.3.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
`crates/knowledge-git-lib/src/`, `crates/knowledge-git-lib/unit_tests/`, `knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` et `ROADMAP.md` ne sont pas modifiés.
|
||||||
|
|
||||||
|
Aucun history `alpha.3.fix.1` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## 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.fix.1'
|
||||||
|
grep -F 'version = "0.0.3-alpha.3.fix.1"' 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.fix.1-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.3.md').read_text(encoding='utf-8')
|
||||||
|
assert '\nFAIL\n' in history
|
||||||
|
|
||||||
|
test_source = Path('crates/knowledge-git-lib/tests/git_write_candidate.rs').read_text(encoding='utf-8')
|
||||||
|
assert 'assert!(matches!(snapshot_content.as_deref(), Ok("first\\n")));' in test_source
|
||||||
|
assert 'assert_eq!(snapshot_content, Ok(String::from("first\\n")));' not in test_source
|
||||||
|
|
||||||
|
plan = Path('docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md').read_text(encoding='utf-8')
|
||||||
|
assert "### alpha.3.fix.1 — correction de l'assertion du test candidat" in plan
|
||||||
|
assert '`alpha.3` est conservée `FAIL`' in plan
|
||||||
|
assert 'Un `PASS` complet de ce fix autorise `alpha.4`' in plan
|
||||||
|
|
||||||
|
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
|
||||||
|
assert '## 0.0.3' not in changelog
|
||||||
|
|
||||||
|
assert not Path('history/0.0.3/alpha.3.fix.1.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.fix.1 compile-fix 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.fix.1 text hygiene: clean ({len(paths)} file(s))')
|
||||||
|
PYTEXT
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates/knowledge-git-lib/src crates/knowledge-git-lib/unit_tests crates/knowledge-git-api crates/knowledge-app-lib Web CHANGELOG.md ROADMAP.md)"
|
||||||
|
test ! -e history/0.0.3/alpha.3.fix.1.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 ni contrat runtime n'est modifié.
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/alpha.3.fix.1.md`, l'appliquer, commit/push `0.0.3-alpha.3.fix.1`, puis préparer `0.0.3-alpha.4`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Une nouvelle correction bornée utilise `alpha.3.fix.2`; si le diagnostic invalide les invariants du worktree ou du candidat, effectuer un REPLAN avant de poursuivre.
|
||||||
256
deltas/0.0.3/alpha.3.md
Normal file
256
deltas/0.0.3/alpha.3.md
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.3.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# 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/<revision>` 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/<source-key>/candidate
|
||||||
|
-> mutations confinées
|
||||||
|
-> git add limité aux chemins demandés
|
||||||
|
-> commit avec identité explicite
|
||||||
|
-> vérification HEAD / HEAD^1
|
||||||
|
-> refs/knowledge/candidates/<revision>
|
||||||
|
-> 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/<revision>` 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.
|
||||||
186
deltas/0.0.3/alpha.4.md
Normal file
186
deltas/0.0.3/alpha.4.md
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<!-- file: deltas/0.0.3/alpha.4.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-alpha.4 — push gardé et concurrence
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.3.fix.1` après application, commit et push de son history `PASS`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Fermer le scope fonctionnel alpha de `0.0.3` en implémentant le push Git provider-neutral gardé par l'état distant attendu.
|
||||||
|
|
||||||
|
La tranche :
|
||||||
|
|
||||||
|
- conserve la création locale de candidat acquise en `alpha.3.fix.1` ;
|
||||||
|
- exige que le candidat soit encore retenu sous `refs/knowledge/candidates/<revision>` ;
|
||||||
|
- observe la branche distante par `git ls-remote --refs origin <refs/heads/...>` ;
|
||||||
|
- compare l'état observé à `GitRemoteState::Absent` ou `GitRemoteState::Revision(...)` ;
|
||||||
|
- utilise `--force-with-lease=<branche>:<attendu>` comme compare-and-exchange autoritaire ;
|
||||||
|
- neutralise `remote.origin.mirror=true` uniquement pour la commande de push explicite du mirror ;
|
||||||
|
- crée une branche uniquement lorsque l'absence est explicitement attendue ;
|
||||||
|
- réobserve l'état distant après un rejet de push afin de produire un conflit récupérable en cas de divergence ;
|
||||||
|
- implémente `knowledge_git_api::GitRepositoryWriter` sur `GitBackend` ;
|
||||||
|
- ajoute des tests d'intégration avec remote bare local pour mise à jour, création et conflit sans écrasement.
|
||||||
|
|
||||||
|
Aucun rebase/merge automatique, aucun force-push non gardé, aucune API de forge, aucun PostgreSQL et aucune orchestration applicative de proposition ne sont introduits.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
crates/knowledge-git-lib/src/error.rs
|
||||||
|
crates/knowledge-git-lib/src/lib.rs
|
||||||
|
crates/knowledge-git-lib/src/write.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/tests/git_write_push.rs
|
||||||
|
deltas/0.0.3/alpha.4.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
`knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` et `ROADMAP.md` ne sont pas modifiés.
|
||||||
|
|
||||||
|
Aucun history `alpha.4` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## Compare-and-exchange distant
|
||||||
|
|
||||||
|
Le chemin d'écriture complet devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
base explicite
|
||||||
|
-> candidat local retenu
|
||||||
|
-> observation distante
|
||||||
|
-> comparaison à l'état attendu
|
||||||
|
-> push --force-with-lease exact
|
||||||
|
-> Updated(candidate)
|
||||||
|
ou Conflict { actual }
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour une branche existante :
|
||||||
|
|
||||||
|
```text
|
||||||
|
--force-with-lease=refs/heads/main:<revision-attendue>
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour une branche qui doit être absente :
|
||||||
|
|
||||||
|
```text
|
||||||
|
--force-with-lease=refs/heads/proposal:
|
||||||
|
```
|
||||||
|
|
||||||
|
L'observation préalable fournit un conflit rapide, mais elle n'est pas la garde de concurrence définitive. La lease est évaluée par Git au moment du push et protège la course entre observation et mise à jour distante.
|
||||||
|
|
||||||
|
Un mirror créé avec `git clone --mirror` possède `remote.origin.mirror=true`. Le push de `alpha.4` fournit donc explicitement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
-c remote.origin.mirror=false
|
||||||
|
```
|
||||||
|
|
||||||
|
pour cette commande uniquement, afin d'autoriser le refspec ciblé sans transformer le mirror ou pousser toutes ses références internes.
|
||||||
|
|
||||||
|
## 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.4'
|
||||||
|
grep -F 'version = "0.0.3-alpha.4"' 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.4-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 -p knowledge-git-lib --test git_write_push
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
Les contrôles spécifiques sont volontairement courts et indépendants afin d'éviter les problèmes de copier/coller de blocs heredoc :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -F 'PASS' history/0.0.3/alpha.3.fix.1.md
|
||||||
|
grep -F 'pub fn push_candidate(' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F -- '--force-with-lease=' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F 'remote.origin.mirror=false' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F 'GitOperation::InspectRemote' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F 'GitOperation::PushCandidate' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F 'impl knowledge_git_api::GitRepositoryWriter for crate::GitBackend' crates/knowledge-git-lib/src/write.rs
|
||||||
|
grep -F 'guarded_push_updates_expected_branch_and_creates_only_expected_absent_branch' crates/knowledge-git-lib/tests/git_write_push.rs
|
||||||
|
grep -F 'guarded_push_returns_conflict_after_concurrent_remote_change_without_overwrite' crates/knowledge-git-lib/tests/git_write_push.rs
|
||||||
|
! grep -F '## 0.0.3' CHANGELOG.md
|
||||||
|
test ! -e history/0.0.3/alpha.4.md
|
||||||
|
test ! -e crates/knowledge-forge-gitea-lib
|
||||||
|
test ! -e crates/knowledge-store-postgres-lib
|
||||||
|
test ! -e migrations
|
||||||
|
test -z "$(git diff --name-only -- crates/knowledge-git-api crates/knowledge-app-lib Web CHANGELOG.md ROADMAP.md)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Hygiène texte :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; print(f"Knowledge 0.0.3-alpha.4 text hygiene: clean ({len(paths)} file(s))") if not bad else (_ for _ in ()).throw(AssertionError(bad))'
|
||||||
|
```
|
||||||
|
|
||||||
|
Politique des lockfiles et staging :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.4.md`, l'appliquer, commit/push `0.0.3-alpha.4`, puis préparer `0.0.3-beta.1` pour réconciliation, revue d'horizon étendue et stabilisation.
|
||||||
|
|
||||||
|
La revue d'horizon beta doit notamment réintroduire explicitement la trajectoire produit complète des trois sites PGR / Snippets / Howtos : templates publics et admin, prototypes Web, Actix Web, adaptation Maud, PostgreSQL, consultation/gestion et workflow de proposition/publication.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Une correction bornée utilise `alpha.4.fix.N` ; une remise en cause de la garantie compare-and-exchange, de la frontière provider-neutral ou du modèle de remote exige un `REPLAN` explicite.
|
||||||
207
deltas/0.0.3/beta.1.md
Normal file
207
deltas/0.0.3/beta.1.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<!-- file: deltas/0.0.3/beta.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-beta.1 — réconciliation, changelog candidat et horizon produit
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.3-alpha.4` après application, commit et push de son history :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.4.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-beta.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nature de la tranche
|
||||||
|
|
||||||
|
`beta.1` est une tranche de stabilisation documentaire et de réconciliation. Elle n'ajoute aucun comportement Rust/Web, aucune crate, aucune dépendance et aucune migration.
|
||||||
|
|
||||||
|
Elle :
|
||||||
|
|
||||||
|
- réconcilie le chemin Git de lecture/écriture réellement obtenu ;
|
||||||
|
- prépare le changelog `0.0.3 (candidate)` à partir des capacités validées ;
|
||||||
|
- reclassifie la persistance PostgreSQL comme prochaine mission `0.0.4` ;
|
||||||
|
- étend l'horizon afin de représenter les trois produits PGR/HOWTOs/snippets et leurs phases de templates publics/admin puis d'intégration Actix/Maud ;
|
||||||
|
- prépare le prompt `0.0.4` pendant la fin de `0.0.3`, conformément au cycle normal ;
|
||||||
|
- conserve les autres `DEFER` sans implémentation anticipée.
|
||||||
|
|
||||||
|
L'étude durable est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/000-README.md
|
||||||
|
docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
deltas/0.0.3/beta.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
`crates/` et `Web/` ne sont pas modifiés. Aucun history `beta.1` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## Horizon retenu
|
||||||
|
|
||||||
|
La prochaine mission potentielle est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4 fondation PostgreSQL partagée et migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
L'horizon révisable devient ensuite :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.5 modèles de contenu et projections PGR/HOWTO/snippet
|
||||||
|
0.0.6 templates publics des trois sites, pages de garde incluses
|
||||||
|
0.0.7 templates d'administration des trois sites
|
||||||
|
0.0.8 routes publiques Actix Web + adaptation Maud
|
||||||
|
0.0.9 auth/autorisation + routes admin Actix Web + adaptation Maud
|
||||||
|
0.0.10 gestion/proposition/revue/publication Git + PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette numérotation donne une trajectoire produit lisible mais ne transforme pas `0.0.5+` en engagements irrévocables. Les revues d'horizon futures peuvent encore réordonner, scinder ou fusionner ces étapes.
|
||||||
|
|
||||||
|
## Handoff 0.0.4
|
||||||
|
|
||||||
|
`prompts/004-V0_0_4_START_PROMPT.md` est créé maintenant parce que la mission PostgreSQL est suffisamment décidée.
|
||||||
|
|
||||||
|
Le prompt ne fabrique aucune preuve postérieure à `0.0.3` : il ne contient ni commit stable réel de `v0.0.3`, ni SHA-256 d'archive taggée, ni statut post-tag supposé. La future session devra recevoir l'archive et le rapport réels après publication et les vérifier avant développement.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt après application du delta.
|
||||||
|
|
||||||
|
### Version, documentation et Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.3-beta.1'
|
||||||
|
grep -F 'version = "0.0.3-beta.1"' Cargo.toml
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.4.md | grep -Fx 'PASS'
|
||||||
|
|
||||||
|
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-beta.1-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 -p knowledge-git-lib --test git_write_push
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
### Baseline Web de stabilisation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(cd Web/knowledge-template-lab && npm ci)
|
||||||
|
(cd Web/knowledge-template-lab && npm run typecheck)
|
||||||
|
(cd Web/knowledge-template-lab && npm run build)
|
||||||
|
```
|
||||||
|
|
||||||
|
Audit du manifest, sous forme d'une commande indépendante :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'import json; from pathlib import Path; p=Path("../builds/sasedev-knowledge/web/template-lab/.vite/manifest.json"); m=json.loads(p.read_text(encoding="utf-8")); e=m["src/main.ts"]; assert e.get("file"); assert e.get("css"); print("Knowledge 0.0.3-beta.1 Vite manifest audit: clean")'
|
||||||
|
```
|
||||||
|
|
||||||
|
Les smokes HTTP complets des trois launchers seront obligatoires en `rc.1`. `beta.1` ne modifie aucun runtime Web et valide ici la baseline npm/typecheck/build/manifest en plus des tests Rust workspace.
|
||||||
|
|
||||||
|
### Contrôles de réconciliation et d'horizon
|
||||||
|
|
||||||
|
Ces commandes sont volontairement courtes et indépendantes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -F '## 0.0.3 (candidate)' CHANGELOG.md
|
||||||
|
|
||||||
|
grep -F -- '- `(h) 0.0.4` — fondation PostgreSQL partagée' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.6` — templates publics des trois sites' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.7` — templates d'administration des trois sites' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.8` — lecture publique Actix Web / Maud' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.9` — administration Actix Web / Maud et autorisation' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.10` — gestion, proposition, revue et publication' ROADMAP.md
|
||||||
|
|
||||||
|
test -f docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
test -f prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
|
||||||
|
grep -F '### 0.0.4 — PostgreSQL partagé' docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
grep -F '### 0.0.6 — templates publics' docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
grep -F "### 0.0.7 — templates d'administration" docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
grep -F '### 0.0.8 — Actix Web / Maud public' docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
grep -F 'pgr.sasedev.com affichage et gestion de PGR' docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
|
||||||
|
grep -F 'archive sasedev-knowledge-v0.0.3.zip téléchargée depuis le tag Gitea v0.0.3' prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
grep -F 'Ne pas rechercher, charger ni adopter automatiquement une nouvelle version de PGR.' prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
! grep -Eq '[0-9a-f]{40}|[0-9a-f]{64}' prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates Web)"
|
||||||
|
test ! -e crates/knowledge-store-postgres-lib
|
||||||
|
test ! -e crates/knowledge-forge-gitea-lib
|
||||||
|
test ! -e migrations
|
||||||
|
test ! -e history/0.0.3/beta.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hygiène texte et lockfiles
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; print(f"Knowledge 0.0.3-beta.1 text hygiene: clean ({len(paths)} file(s))") if not bad else (_ for _ in ()).throw(AssertionError(bad))'
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/beta.1.md`, l'appliquer, commit/push `0.0.3-beta.1`, puis préparer `0.0.3-rc.1` sans nouvelle fonctionnalité. La RC doit geler le scope et rejouer la gate complète, y compris npm/Vite et les trois smokes HTTP.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute correction. Un défaut borné utilise `beta.1.fix.N`. Toute découverte remettant en cause l'intégrité du chemin Git, la mission `0.0.4` ou la trajectoire produit exige un REPLAN explicite avant poursuite.
|
||||||
154
deltas/0.0.3/rc.1.fix.1.md
Normal file
154
deltas/0.0.3/rc.1.fix.1.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<!-- file: deltas/0.0.3/rc.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-rc.1.fix.1 — correction de la vérification des statuts historiques
|
||||||
|
|
||||||
|
## Base requise
|
||||||
|
|
||||||
|
État `0.0.3-rc.1` committé et poussé après application du history :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/rc.1.md = FAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-rc.1.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Corriger uniquement l'assertion de validation qui a fait échouer `rc.1` alors que le history `alpha.1` porte bien sémantiquement le statut `PARTIAL`.
|
||||||
|
|
||||||
|
Le fichier historique `history/0.0.3/alpha.1.md` n'est pas modifié. Sa représentation `` `PARTIAL` `` reste une preuve historique valide.
|
||||||
|
|
||||||
|
Aucune fonctionnalité Git, Web, PostgreSQL, forge, auth ou workflow produit n'est ajoutée.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
La tranche remplace uniquement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
et ajoute :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/rc.1.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun fichier n'est supprimé. `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/`, `prompts/` et tous les histories antérieurs sont gelés.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter directement dans le shell interactif.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.3-rc.1.fix.1'
|
||||||
|
grep -F 'version = "0.0.3-rc.1.fix.1"' Cargo.toml
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/rc.1.md | grep -Fx 'FAIL'
|
||||||
|
|
||||||
|
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-rc.1.fix.1-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 -p knowledge-git-lib --test git_write_push
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contrôles spécifiques courts
|
||||||
|
|
||||||
|
Chaque commande est indépendante.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.1.md | tr -d '`' | grep -Fx 'PARTIAL'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.1.fix.1.md | tr -d '`' | grep -Fx 'PARTIAL'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.1.fix.2.md | tr -d '`' | grep -Fx 'PASS'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.2.md | tr -d '`' | grep -Fx 'PASS'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.3.md | tr -d '`' | grep -Fx 'FAIL'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.3.fix.1.md | tr -d '`' | grep -Fx 'PASS'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/alpha.4.md | tr -d '`' | grep -Fx 'PASS'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/beta.1.md | tr -d '`' | grep -Fx 'PASS'
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/rc.1.md | tr -d '`' | grep -Fx 'FAIL'
|
||||||
|
|
||||||
|
test "$(find history/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 9
|
||||||
|
test "$(find deltas/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 10
|
||||||
|
test ! -e history/0.0.3/rc.1.fix.1.md
|
||||||
|
|
||||||
|
grep -F '## 0.0.3 (candidate)' CHANGELOG.md
|
||||||
|
grep -F -- '- `( ) 0.0.3` — écriture Git contrôlée et push gardé' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.4` — fondation PostgreSQL partagée' ROADMAP.md
|
||||||
|
test -f prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- crates Web CHANGELOG.md ROADMAP.md docs/architecture docs/studies docs/rules prompts history)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier l'hygiène texte :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; print(f"Knowledge 0.0.3-rc.1.fix.1 text hygiene: clean ({len(paths)} file(s))") if not bad else (_ for _ in ()).throw(AssertionError(bad))'
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging attendu est limité à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/rc.1.fix.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/rc.1.fix.1.md`, l'appliquer, commit/push `0.0.3-rc.1.fix.1`, puis préparer `0.0.3-rel.1` comme promotion mécanique stable pré-tag.
|
||||||
|
|
||||||
|
### FAIL
|
||||||
|
|
||||||
|
Produire le history exact puis un nouveau fix RC strictement borné au défaut observé. Ne pas promouvoir `rel.1` tant que la RC corrigée n'est pas `PASS`.
|
||||||
253
deltas/0.0.3/rc.1.md
Normal file
253
deltas/0.0.3/rc.1.md
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<!-- file: deltas/0.0.3/rc.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3-rc.1 — release candidate
|
||||||
|
|
||||||
|
## Base requise
|
||||||
|
|
||||||
|
État `0.0.3-beta.1` committé et poussé après application du history :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/beta.1.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-rc.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Geler le scope de `0.0.3` et exécuter sa gate complète de release candidate.
|
||||||
|
|
||||||
|
Aucune nouvelle fonctionnalité, dépendance, crate, route, comportement Git, décision PostgreSQL/Web ou modification de l'horizon produit n'est introduite.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
La RC remplace uniquement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
et ajoute :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/rc.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun fichier n'est supprimé. `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/` et `prompts/` sont gelés.
|
||||||
|
|
||||||
|
## Histories attendus avant RC
|
||||||
|
|
||||||
|
La gate doit retrouver exactement les huit histories antérieurs suivants :
|
||||||
|
|
||||||
|
```text
|
||||||
|
alpha.1 PARTIAL
|
||||||
|
alpha.1.fix.1 PARTIAL
|
||||||
|
alpha.1.fix.2 PASS
|
||||||
|
alpha.2 PASS
|
||||||
|
alpha.3 FAIL
|
||||||
|
alpha.3.fix.1 PASS
|
||||||
|
alpha.4 PASS
|
||||||
|
beta.1 PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Pendant la gate, `rc.1.md` doit être le seul delta `0.0.3` sans history correspondant.
|
||||||
|
|
||||||
|
## Présentation candidate
|
||||||
|
|
||||||
|
La RC conserve :
|
||||||
|
|
||||||
|
```text
|
||||||
|
CHANGELOG.md -> ## 0.0.3 (candidate)
|
||||||
|
ROADMAP.md -> 0.0.3 encore engagée + horizon 0.0.4..0.0.10
|
||||||
|
prompt 004 -> handoff 0.0.4 présent, sans commit/SHA futur inventé
|
||||||
|
```
|
||||||
|
|
||||||
|
La présentation stable appartient à `rel.1`, pas à la RC.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter directement dans le shell interactif. Ne pas activer `set -e`, `set -u` ou `set -o pipefail` globalement.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.3-rc.1'
|
||||||
|
grep -F 'version = "0.0.3-rc.1"' Cargo.toml
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/beta.1.md | grep -Fx 'PASS'
|
||||||
|
|
||||||
|
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-rc.1-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 -p knowledge-git-lib --test git_write_push
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
|
||||||
|
(cd Web/knowledge-template-lab && npm ci)
|
||||||
|
(cd Web/knowledge-template-lab && npm run typecheck)
|
||||||
|
(cd Web/knowledge-template-lab && npm run build)
|
||||||
|
|
||||||
|
python3 -c 'import json; from pathlib import Path; p=Path("../builds/sasedev-knowledge/web/template-lab/.vite/manifest.json"); m=json.loads(p.read_text(encoding="utf-8")); e=m["src/main.ts"]; assert e.get("file"); assert e.get("css"); print("Knowledge 0.0.3-rc.1 Vite manifest audit: clean")'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contrôles d'intégrité courts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.1.md | grep -Fx 'PARTIAL'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.1.fix.1.md | grep -Fx 'PARTIAL'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.1.fix.2.md | grep -Fx 'PASS'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.2.md | grep -Fx 'PASS'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.3.md | grep -Fx 'FAIL'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.3.fix.1.md | grep -Fx 'PASS'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.4.md | grep -Fx 'PASS'
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/beta.1.md | grep -Fx 'PASS'
|
||||||
|
|
||||||
|
test "$(find history/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 8
|
||||||
|
test "$(find deltas/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 9
|
||||||
|
|
||||||
|
test ! -e history/0.0.3/rc.1.md
|
||||||
|
|
||||||
|
grep -F '## 0.0.3 (candidate)' CHANGELOG.md
|
||||||
|
grep -F -- '- `( ) 0.0.3` — écriture Git contrôlée et push gardé' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.4` — fondation PostgreSQL partagée' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.10` — gestion, proposition, revue et publication' ROADMAP.md
|
||||||
|
|
||||||
|
test -f prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
! grep -Eq '[0-9a-f]{40}|[0-9a-f]{64}' prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
|
||||||
|
test ! -e crates/knowledge-store-postgres-lib
|
||||||
|
test ! -e crates/knowledge-forge-gitea-lib
|
||||||
|
test ! -e migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier le workspace Cargo sans bloc multi-lignes :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'import json; from pathlib import Path; m=json.loads(Path("/tmp/sasedev-knowledge-0.0.3-rc.1-metadata.json").read_text()); expected={"knowledge-app-lib","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 {p["name"] for p in m["packages"]} == expected; assert len(m["workspace_members"]) == 9; print("Knowledge 0.0.3-rc.1 workspace audit: clean")'
|
||||||
|
```
|
||||||
|
|
||||||
|
Vérifier l'hygiène texte :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; print(f"Knowledge 0.0.3-rc.1 text hygiene: clean ({len(paths)} file(s))") if not bad else (_ for _ in ()).throw(AssertionError(bad))'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Smokes HTTP de production
|
||||||
|
|
||||||
|
Construire les trois launchers :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build -p knowledge-pgr-server -p knowledge-howtos-server -p knowledge-snippets-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Préparer le chemin du manifest et la liste des assets :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export KNOWLEDGE_RC_MANIFEST="$(realpath ../builds/sasedev-knowledge/web/template-lab/.vite/manifest.json)"
|
||||||
|
python3 -c 'import json,os; m=json.load(open(os.environ["KNOWLEDGE_RC_MANIFEST"],encoding="utf-8")); e=m["src/main.ts"]; print("/"+e["file"].lstrip("/")); print(*["/"+x.lstrip("/") for x in e.get("css",[])],sep="\n")' > /tmp/sasedev-knowledge-0.0.3-rc.1-assets.txt
|
||||||
|
cat /tmp/sasedev-knowledge-0.0.3-rc.1-assets.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### PGR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
env -u KNOWLEDGE_VITE_DEV_ORIGIN KNOWLEDGE_VITE_MANIFEST_PATH="$KNOWLEDGE_RC_MANIFEST" KNOWLEDGE_PGR_BIND_ADDRESS=127.0.0.1:18181 ../builds/sasedev-knowledge/target/debug/knowledge-pgr-server > /tmp/knowledge-pgr-rc.1.log 2>&1 & echo $! > /tmp/knowledge-pgr-rc.1.pid
|
||||||
|
sleep 1
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18181/healthz",timeout=2); assert r.status==200 and r.read().decode()=="ok"; print("pgr healthz PASS")'
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18181/",timeout=2); assert r.status==200; open("/tmp/knowledge-pgr-rc.1.html","wb").write(r.read())'
|
||||||
|
while IFS= read -r asset; do grep -F "$asset" /tmp/knowledge-pgr-rc.1.html >/dev/null && python3 -c 'import sys,urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18181"+sys.argv[1],timeout=2); assert r.status==200' "$asset" || exit 1; done < /tmp/sasedev-knowledge-0.0.3-rc.1-assets.txt
|
||||||
|
kill "$(cat /tmp/knowledge-pgr-rc.1.pid)"
|
||||||
|
wait "$(cat /tmp/knowledge-pgr-rc.1.pid)" 2>/dev/null || true
|
||||||
|
echo 'knowledge-pgr-server smoke PASS'
|
||||||
|
```
|
||||||
|
|
||||||
|
### HOWTOs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
env -u KNOWLEDGE_VITE_DEV_ORIGIN KNOWLEDGE_VITE_MANIFEST_PATH="$KNOWLEDGE_RC_MANIFEST" KNOWLEDGE_HOWTOS_BIND_ADDRESS=127.0.0.1:18182 ../builds/sasedev-knowledge/target/debug/knowledge-howtos-server > /tmp/knowledge-howtos-rc.1.log 2>&1 & echo $! > /tmp/knowledge-howtos-rc.1.pid
|
||||||
|
sleep 1
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18182/healthz",timeout=2); assert r.status==200 and r.read().decode()=="ok"; print("howtos healthz PASS")'
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18182/",timeout=2); assert r.status==200; open("/tmp/knowledge-howtos-rc.1.html","wb").write(r.read())'
|
||||||
|
while IFS= read -r asset; do grep -F "$asset" /tmp/knowledge-howtos-rc.1.html >/dev/null && python3 -c 'import sys,urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18182"+sys.argv[1],timeout=2); assert r.status==200' "$asset" || exit 1; done < /tmp/sasedev-knowledge-0.0.3-rc.1-assets.txt
|
||||||
|
kill "$(cat /tmp/knowledge-howtos-rc.1.pid)"
|
||||||
|
wait "$(cat /tmp/knowledge-howtos-rc.1.pid)" 2>/dev/null || true
|
||||||
|
echo 'knowledge-howtos-server smoke PASS'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Snippets
|
||||||
|
|
||||||
|
```bash
|
||||||
|
env -u KNOWLEDGE_VITE_DEV_ORIGIN KNOWLEDGE_VITE_MANIFEST_PATH="$KNOWLEDGE_RC_MANIFEST" KNOWLEDGE_SNIPPETS_BIND_ADDRESS=127.0.0.1:18183 ../builds/sasedev-knowledge/target/debug/knowledge-snippets-server > /tmp/knowledge-snippets-rc.1.log 2>&1 & echo $! > /tmp/knowledge-snippets-rc.1.pid
|
||||||
|
sleep 1
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18183/healthz",timeout=2); assert r.status==200 and r.read().decode()=="ok"; print("snippets healthz PASS")'
|
||||||
|
python3 -c 'import urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18183/",timeout=2); assert r.status==200; open("/tmp/knowledge-snippets-rc.1.html","wb").write(r.read())'
|
||||||
|
while IFS= read -r asset; do grep -F "$asset" /tmp/knowledge-snippets-rc.1.html >/dev/null && python3 -c 'import sys,urllib.request; r=urllib.request.urlopen("http://127.0.0.1:18183"+sys.argv[1],timeout=2); assert r.status==200' "$asset" || exit 1; done < /tmp/sasedev-knowledge-0.0.3-rc.1-assets.txt
|
||||||
|
kill "$(cat /tmp/knowledge-snippets-rc.1.pid)"
|
||||||
|
wait "$(cat /tmp/knowledge-snippets-rc.1.pid)" 2>/dev/null || true
|
||||||
|
echo 'knowledge-snippets-server smoke PASS'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scope gelé et staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -z "$(git diff --name-only -- crates Web CHANGELOG.md ROADMAP.md docs/architecture docs/studies docs/rules prompts)"
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final attendu avant history est limité à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/rc.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/rc.1.md`, l'appliquer, commit/push `0.0.3-rc.1`, puis préparer `0.0.3-rel.1` comme promotion mécanique stable pré-tag.
|
||||||
|
|
||||||
|
### FAIL
|
||||||
|
|
||||||
|
Produire le history avec le résultat exact puis une tranche `rc.1.fix.1` strictement bornée à la régression observée. Ne pas promouvoir la stable tant que la RC n'est pas `PASS`.
|
||||||
112
deltas/0.0.3/rel.1.fix.1.md
Normal file
112
deltas/0.0.3/rel.1.fix.1.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<!-- file: deltas/0.0.3/rel.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3 rel.1.fix.1 — complétion de la preuve de clôture stable
|
||||||
|
|
||||||
|
## Baseline et version
|
||||||
|
|
||||||
|
Baseline : `0.0.3-rel.1` avec `history/0.0.3/rel.1.md` appliqué, committé et poussé avec le statut `PARTIAL`.
|
||||||
|
|
||||||
|
Ce delta MUST NOT être appliqué avant ce commit.
|
||||||
|
|
||||||
|
Version produit avant et après application :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
`rel.1.fix.1` est une tranche opérationnelle de clôture ; elle ne change pas la version SemVer stable visée.
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Compléter uniquement les deux preuves absentes du transcript `rel.1` :
|
||||||
|
|
||||||
|
- audit explicite de l'ensemble exact du workspace à partir de `cargo metadata` ;
|
||||||
|
- audit explicite d'hygiène texte.
|
||||||
|
|
||||||
|
Toutes les preuves produit/runtime de `rel.1` — Rust, Git, Web, Vite et trois smokes HTTP — sont déjà `PASS` et sont conservées dans son history. Ce fix ne modifie aucun fichier concerné par ces preuves.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/rel.1.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Aucun changement sous `VERSION`, `Cargo.toml`, `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/` ou `prompts/`.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.3'
|
||||||
|
grep -F 'version = "0.0.3"' Cargo.toml
|
||||||
|
sed -n '/^## Statut$/{n;n;p;q;}' history/0.0.3/rel.1.md | tr -d '`' | grep -Fx 'PARTIAL'
|
||||||
|
|
||||||
|
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 metadata --format-version 1 --no-deps > /tmp/sasedev-knowledge-0.0.3-rel.1.fix.1-metadata.json
|
||||||
|
|
||||||
|
python3 -c 'import json; from pathlib import Path; m=json.loads(Path("/tmp/sasedev-knowledge-0.0.3-rel.1.fix.1-metadata.json").read_text()); expected={"knowledge-app-lib","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 {p["name"] for p in m["packages"]} == expected; assert len(m["workspace_members"]) == 9; print("Knowledge 0.0.3-rel.1.fix.1 workspace audit: clean")'
|
||||||
|
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; print(f"Knowledge 0.0.3-rel.1.fix.1 text hygiene: clean ({len(paths)} file(s))") if not bad else (_ for _ in ()).throw(AssertionError(bad))'
|
||||||
|
|
||||||
|
test -z "$(git diff --name-only -- VERSION Cargo.toml crates Web CHANGELOG.md ROADMAP.md docs/architecture docs/studies docs/rules prompts)"
|
||||||
|
test ! -e history/0.0.3/rel.1.fix.1.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 show-ref --verify --quiet refs/tags/v0.0.3
|
||||||
|
test ! -e sasedev-knowledge-0.0.3-publish.001-report.md
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git diff --cached --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
## Staging attendu
|
||||||
|
|
||||||
|
Avant ajout du history de ce fix :
|
||||||
|
|
||||||
|
```text
|
||||||
|
A deltas/0.0.3/rel.1.fix.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.3/rel.1.fix.1.md`, l'appliquer, puis créer le premier commit stable avec le message exact :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
Après push de `master`, créer/pousser le tag annoté `v0.0.3`, vérifier tag/commit/archive réellement publiés puis produire séparément le rapport post-tag `sasedev-knowledge-0.0.3-publish.001-report.md`.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history et corriger uniquement la fermeture stable dans le fix suivant, sans créer le tag.
|
||||||
77
deltas/0.0.3/rel.1.md
Normal file
77
deltas/0.0.3/rel.1.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<!-- file: deltas/0.0.3/rel.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.3 rel.1 — promotion stable pré-tag
|
||||||
|
|
||||||
|
## Baseline et version
|
||||||
|
|
||||||
|
Baseline : `0.0.3-rc.1.fix.1`, avec `history/0.0.3/rc.1.fix.1.md` appliqué et committé.
|
||||||
|
|
||||||
|
Ce delta MUST NOT être appliqué avant ce commit.
|
||||||
|
|
||||||
|
Version avant application :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-rc.1.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Version après application :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
`rel.1` identifie la tranche de clôture opérationnelle ; il ne fait pas partie de la version SemVer du produit.
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Promouvoir mécaniquement la candidate corrigée vers la stable `0.0.3` sans rouvrir le scope.
|
||||||
|
|
||||||
|
La tranche finalise la version stable, la présentation README/roadmap/changelog, l'index documentaire, le plan historique `0.0.3` et la matrice de validation. Elle engage `0.0.4` comme prochaine mission conformément à la revue d'horizon déjà acquise et conserve le prompt `004` préparé avant clôture.
|
||||||
|
|
||||||
|
Aucun fichier Rust, frontend, architecture, étude, règle normative ou prompt n'est modifié.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/000-README.md
|
||||||
|
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.3/rel.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Le history `rc.1.fix.1` est livré séparément. Le history `rel.1` sera produit uniquement après la gate opérateur.
|
||||||
|
|
||||||
|
## Présentation stable attendue
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3
|
||||||
|
Cargo workspace version 0.0.3
|
||||||
|
ROADMAP (x) 0.0.3 ; ( ) 0.0.4
|
||||||
|
CHANGELOG ## 0.0.3
|
||||||
|
README 0.0.3 présentée comme stable
|
||||||
|
plan 003 plan historique de la stable 0.0.3
|
||||||
|
prompt 004 présent pour 0.0.4
|
||||||
|
```
|
||||||
|
|
||||||
|
Les versions `0.0.5` à `0.0.10` restent des horizons révisables.
|
||||||
|
|
||||||
|
## Gate pré-tag
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt les commandes fournies avec le delta. La gate doit couvrir : version/history précédent, audits Markdown, `cargo fmt --all` puis `--check`, audits Rust, metadata/check/Clippy, tests Git ciblés et workspace, npm/typecheck/build, manifest, cohérence stable/histories/deltas, hygiène texte, trois smokes HTTP, lockfiles et staging final.
|
||||||
|
|
||||||
|
Aucun tag `v0.0.3`, archive taggée ou rapport post-tag ne doit être déclaré comme existant pendant cette gate.
|
||||||
217
deltas/0.0.4/alpha.1.md
Normal file
217
deltas/0.0.4/alpha.1.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
<!-- file: deltas/0.0.4/alpha.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.4-alpha.1 — cadrage de la fondation PostgreSQL partagée
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de la stable autoritaire réellement publiée :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tag Knowledge v0.0.3
|
||||||
|
commit Knowledge b3823972449150b72905930b806d4d15471cddce
|
||||||
|
archive SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d
|
||||||
|
publish report sasedev-knowledge-0.0.3-publish.001-report.md = PASS
|
||||||
|
start prompt prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Le rapport post-tag fourni confirme la convergence `HEAD/tag distant/commentaire ZIP`, l'intégrité de l'archive, les douze deltas/histories `0.0.3` et `rel.1.fix.1 = PASS`.
|
||||||
|
|
||||||
|
PGR `v0.1.3` reste une provenance historique de l'adoption fermée en `0.0.2`. Aucune nouvelle version PGR n'est chargée ou adoptée.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4-alpha.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Fermer le cadrage PostgreSQL avant toute implémentation :
|
||||||
|
|
||||||
|
- vérifier la stable `v0.0.3`, ses owners, architecture, deltas/histories, `DEFER` et revue d'horizon ;
|
||||||
|
- comparer les clients/ORM/migrations Rust actuels ;
|
||||||
|
- sélectionner `tokio-postgres` comme driver, `deadpool-postgres` comme pool et Refinery comme migrator, sans ORM ;
|
||||||
|
- retenir `knowledge-store-postgres-lib` comme owner futur du pool, des migrations et de l'état PostgreSQL ;
|
||||||
|
- borner le premier schéma à `knowledge_sources` et `knowledge_source_sync_state` ;
|
||||||
|
- définir une configuration injectée sans fuite de secrets et sans fallback caché ;
|
||||||
|
- définir une stratégie de tests PostgreSQL réels par schéma éphémère ;
|
||||||
|
- préparer l'étude `008` et le plan `004` ;
|
||||||
|
- conserver l'horizon produit existant puisqu'aucune dépendance future n'est invalidée.
|
||||||
|
|
||||||
|
Aucun code produit, aucune nouvelle crate, aucune dépendance Cargo/npm, aucune migration SQL et aucun fichier Web ne sont ajoutés dans cette tranche.
|
||||||
|
|
||||||
|
`CHANGELOG.md` reste inchangé conformément au prompt de démarrage : l'entrée `0.0.4` ne sera préparée qu'à l'approche de la stabilisation.
|
||||||
|
|
||||||
|
## Décisions retenues
|
||||||
|
|
||||||
|
```text
|
||||||
|
owner PostgreSQL knowledge-store-postgres-lib (à créer en alpha.2)
|
||||||
|
driver tokio-postgres 0.7
|
||||||
|
ORM aucun
|
||||||
|
pool deadpool-postgres 0.14
|
||||||
|
migrations SQL versionné + Refinery 0.9
|
||||||
|
premier schéma source logique + dernière révision synchronisée
|
||||||
|
test PostgreSQL URL explicite + schéma éphémère isolé
|
||||||
|
autorité contenu Git
|
||||||
|
```
|
||||||
|
|
||||||
|
Les types de `tokio-postgres`, Deadpool et Refinery restent confinés à `knowledge-store-postgres-lib`. Un backend TLS sera choisi seulement si la connexion de déploiement réellement visée l'exige.
|
||||||
|
|
||||||
|
## Horizon
|
||||||
|
|
||||||
|
Aucun REPLAN d'horizon n'est nécessaire.
|
||||||
|
|
||||||
|
Restent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.5 modèles de contenu et projections
|
||||||
|
0.0.6 templates publics
|
||||||
|
0.0.7 templates d'administration
|
||||||
|
0.0.8 lecture publique Actix Web / Maud
|
||||||
|
0.0.9 administration Actix Web / Maud et autorisation
|
||||||
|
0.0.10 gestion, proposition, revue et publication
|
||||||
|
```
|
||||||
|
|
||||||
|
Gitea/forge, scheduler, recherche avancée, auth, modèles finaux de contenu, administration transverse et sujets Git avancés restent `DEFER`.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
docs/000-README.md
|
||||||
|
docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
deltas/0.0.4/alpha.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
`ROADMAP.md`, `CHANGELOG.md`, `prompts/`, `crates/` et `Web/` ne sont pas modifiés.
|
||||||
|
|
||||||
|
Aucun history `0.0.4-alpha.1` n'est inclus : il doit refléter uniquement la gate opérateur réelle.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt après application du delta.
|
||||||
|
|
||||||
|
### Version et baseline
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.4-alpha.1'
|
||||||
|
grep -F 'version = "0.0.4-alpha.1"' Cargo.toml
|
||||||
|
|
||||||
|
grep -F 'Knowledge stable tag v0.0.3' docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
grep -F 'Knowledge stable commit b3823972449150b72905930b806d4d15471cddce' docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
grep -F 'Knowledge archive SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d' docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
grep -F 'Knowledge publish report sasedev-knowledge-0.0.3-publish.001-report.md = PASS' docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
|
||||||
|
test "$(find deltas/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 12
|
||||||
|
test "$(find history/0.0.3 -maxdepth 1 -type f -name '*.md' | wc -l)" -eq 12
|
||||||
|
diff <(find deltas/0.0.3 -maxdepth 1 -type f -name '*.md' -printf '%f\n' | sort) <(find history/0.0.3 -maxdepth 1 -type f -name '*.md' -printf '%f\n' | sort)
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/rel.1.fix.1.md | grep -Fx 'PASS'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cadrage PostgreSQL et autonomie
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -f docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
test -f docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
test -f deltas/0.0.4/alpha.1.md
|
||||||
|
test ! -e history/0.0.4/alpha.1.md
|
||||||
|
|
||||||
|
grep -F '`tokio-postgres` comme driver PostgreSQL' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F '`deadpool-postgres` comme pool' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F '`refinery` comme owner technique des migrations SQL' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F 'knowledge-store-postgres-lib' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F 'knowledge_sources' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F 'knowledge_source_sync_state' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
grep -F 'KNOWLEDGE_POSTGRES_TEST_URL' docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
|
||||||
|
grep -F 'Ne pas rechercher, charger ni adopter automatiquement une nouvelle version de PGR.' prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
grep -F 'PGR `v0.1.3` reste une provenance historique' docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
|
||||||
|
test ! -e crates/knowledge-store-postgres-lib
|
||||||
|
test ! -e migrations
|
||||||
|
test ! -e history/0.0.4/alpha.1.md
|
||||||
|
test -z "$(git status --short -- crates Web CHANGELOG.md ROADMAP.md prompts)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Documentation puis formatage/audits Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.4-alpha.1-metadata.json
|
||||||
|
cargo check --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
`alpha.1` ne rejoue pas Clippy, tests workspace, npm/Vite ou smokes HTTP : aucun source Rust, dépendance, frontend ou runtime n'est modifié. `cargo check --workspace` vérifie néanmoins le manifest après promotion de version.
|
||||||
|
|
||||||
|
### Structure, horizon et hygiène
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'import json; from pathlib import Path; m=json.loads(Path("/tmp/sasedev-knowledge-0.0.4-alpha.1-metadata.json").read_text()); assert len(m["workspace_members"]) == 9; assert {p["name"] for p in m["packages"]} == {"knowledge-app-lib","knowledge-domain-lib","knowledge-git-api","knowledge-git-lib","knowledge-sync-api","knowledge-web-lib","knowledge-pgr-server","knowledge-howtos-server","knowledge-snippets-server"}; print("Knowledge 0.0.4-alpha.1 workspace audit: clean")'
|
||||||
|
|
||||||
|
grep -F -- '- `( ) 0.0.4` — fondation PostgreSQL partagée' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.5` — modèles de contenu et projections' ROADMAP.md
|
||||||
|
grep -F -- '- `(h) 0.0.10` — gestion, proposition, revue et publication' ROADMAP.md
|
||||||
|
|
||||||
|
python3 -c '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=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file()]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; assert not bad, bad; print(f"Knowledge 0.0.4-alpha.1 text hygiene: clean ({len(paths)} file(s))")'
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire séparément :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.4/alpha.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
avec les résultats réellement observés. Après application du history, commit/push `0.0.4-alpha.1`, puis seulement préparer `0.0.4-alpha.2` qui matérialisera la crate PostgreSQL, `tokio-postgres`, `deadpool-postgres`, Refinery et la première migration.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Produire le history réel, l'appliquer et le committer avant toute correction.
|
||||||
|
|
||||||
|
Une correction bornée au cadrage utilise `0.0.4-alpha.1.fix.1`. Si le diagnostic invalide le driver/pool/migrator retenu, l'ownership, le schéma ou la stratégie de test, exécuter un REPLAN plutôt que d'absorber une architecture différente dans un fix.
|
||||||
179
deltas/0.0.4/alpha.2.fix.1.md
Normal file
179
deltas/0.0.4/alpha.2.fix.1.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<!-- file: deltas/0.0.4/alpha.2.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.4-alpha.2.fix.1 — documentation de la crate de test PostgreSQL
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.4-alpha.2` après application, commit et push de son history `FAIL`.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4-alpha.2.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le history requis est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.4/alpha.2.md = FAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Corriger uniquement le défaut Clippy observé pendant la gate `alpha.2` : la target d'intégration `tests/postgres_migration.rs` ne possède pas de documentation de crate alors que `missing_docs` est activé et promu en erreur par `-D warnings`.
|
||||||
|
|
||||||
|
Le fix ajoute une documentation interne `//!` en tête de cette crate de test.
|
||||||
|
|
||||||
|
Aucun code produit, contrat public, dépendance, configuration de pool, migration SQL ou comportement PostgreSQL n'est modifié. La preuve PostgreSQL réelle de `alpha.2` a déjà passé après fourniture explicite de `KNOWLEDGE_POSTGRES_TEST_URL`, mais elle est rejouée pour fermer complètement le fix.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.0.4/alpha.2.fix.1.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Les fichiers suivants restent inchangés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-store-postgres-lib/src/
|
||||||
|
crates/knowledge-store-postgres-lib/unit_tests/
|
||||||
|
crates/knowledge-store-postgres-lib/migrations/
|
||||||
|
crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/architecture/
|
||||||
|
docs/studies/
|
||||||
|
docs/validation/
|
||||||
|
Web/
|
||||||
|
prompts/
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun history `alpha.2.fix.1` n'est inclus dans ce delta.
|
||||||
|
|
||||||
|
## Précondition PostgreSQL
|
||||||
|
|
||||||
|
La gate requiert toujours une base PostgreSQL de test permettant de créer et supprimer un schéma.
|
||||||
|
|
||||||
|
Avant la preuve PostgreSQL, définir explicitement dans le shell opérateur :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export KNOWLEDGE_POSTGRES_TEST_URL='postgresql://knowledge:knowledge@localhost/knowledge'
|
||||||
|
```
|
||||||
|
|
||||||
|
Adapter l'URL si nécessaire. Ne jamais versionner cette valeur.
|
||||||
|
|
||||||
|
Si PostgreSQL n'est pas disponible, classer la gate `BLOCKED` à cette étape ; ne pas convertir cette absence en `PASS`.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt après application du delta.
|
||||||
|
|
||||||
|
### Version, history précédent et correction bornée
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.4-alpha.2.fix.1'
|
||||||
|
grep -F 'version = "0.0.4-alpha.2.fix.1"' Cargo.toml
|
||||||
|
|
||||||
|
test -f history/0.0.4/alpha.2.md
|
||||||
|
grep -A2 '^## Statut$' history/0.0.4/alpha.2.md | grep -Fx 'FAIL'
|
||||||
|
git ls-files --error-unmatch history/0.0.4/alpha.2.md >/dev/null
|
||||||
|
test -z "$(git status --short -- history/0.0.4/alpha.2.md)"
|
||||||
|
|
||||||
|
grep -Fx '//! Integration test for the initial PostgreSQL migration in an isolated schema.' crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
|
||||||
|
grep -F '### alpha.2.fix.1 — documentation de la crate de test PostgreSQL' docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
grep -F '`alpha.2` historisée `FAIL`' docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
grep -F 'autoriser `alpha.3` uniquement après `PASS` complet du fix' docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
|
||||||
|
test ! -e history/0.0.4/alpha.2.fix.1.md
|
||||||
|
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scope négatif
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -z "$(git diff --name-only -- crates/knowledge-store-postgres-lib/src crates/knowledge-store-postgres-lib/unit_tests crates/knowledge-store-postgres-lib/migrations crates/knowledge-store-postgres-lib/Cargo.toml README.md RULES.md ROADMAP.md CHANGELOG.md docs/architecture docs/studies docs/validation Web prompts)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Markdown puis séquence Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.4-alpha.2.fix.1-metadata.json
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p knowledge-store-postgres-lib
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composition workspace
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'import json; from pathlib import Path; m=json.loads(Path("/tmp/sasedev-knowledge-0.0.4-alpha.2.fix.1-metadata.json").read_text()); expected={"knowledge-app-lib","knowledge-domain-lib","knowledge-git-api","knowledge-git-lib","knowledge-sync-api","knowledge-web-lib","knowledge-pgr-server","knowledge-howtos-server","knowledge-snippets-server","knowledge-store-postgres-lib"}; assert len(m["workspace_members"]) == 10; assert {p["name"] for p in m["packages"]} == expected; print("Knowledge 0.0.4-alpha.2.fix.1 workspace audit: clean")'
|
||||||
|
```
|
||||||
|
|
||||||
|
### PostgreSQL réel obligatoire
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -n "${KNOWLEDGE_POSTGRES_TEST_URL:-}"
|
||||||
|
cargo test -p knowledge-store-postgres-lib --test postgres_migration -- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hygiène, lockfiles et staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'from pathlib import Path; roots=[Path("README.md"),Path("RULES.md"),Path("ROADMAP.md"),Path("CHANGELOG.md"),Path("Cargo.toml"),Path("VERSION"),Path("docs"),Path("prompts"),Path("deltas"),Path("history"),Path("crates")]; allowed={".md",".rs",".toml",".sql"}; paths=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file() and (p.name == "VERSION" or p.suffix in allowed)]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; assert not bad, bad; print(f"Knowledge 0.0.4-alpha.2.fix.1 text hygiene: clean ({len(paths)} file(s))")'
|
||||||
|
|
||||||
|
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 diff --quiet -- Web/knowledge-template-lab/package-lock.json
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git diff --cached --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire `history/0.0.4/alpha.2.fix.1.md`, l'appliquer, commit/push `0.0.4-alpha.2.fix.1`, puis préparer `0.0.4-alpha.3` pour la persistance minimale de source et d'état de synchronisation.
|
||||||
|
|
||||||
|
### FAIL / PARTIAL / BLOCKED
|
||||||
|
|
||||||
|
Conserver le résultat réel dans son history avant toute nouvelle correction. Un défaut borné supplémentaire utilise `alpha.2.fix.2`. Si le diagnostic invalide l'ownership PostgreSQL, les dépendances retenues, le schéma ou la stratégie d'isolation, effectuer un REPLAN avant de poursuivre.
|
||||||
279
deltas/0.0.4/alpha.2.md
Normal file
279
deltas/0.0.4/alpha.2.md
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<!-- file: deltas/0.0.4/alpha.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta 0.0.4-alpha.2 — owner PostgreSQL, pool et première migration
|
||||||
|
|
||||||
|
## Baseline requise
|
||||||
|
|
||||||
|
Partir de `0.0.4-alpha.1` validée et historisée `PASS` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
version précédente 0.0.4-alpha.1
|
||||||
|
history requis history/0.0.4/alpha.1.md = PASS
|
||||||
|
stable autoritaire v0.0.3
|
||||||
|
owner décidé knowledge-store-postgres-lib
|
||||||
|
driver décidé tokio-postgres
|
||||||
|
pool décidé deadpool-postgres
|
||||||
|
migrator décidé Refinery
|
||||||
|
```
|
||||||
|
|
||||||
|
Le history `alpha.1` doit être committé et poussé avant application du présent delta.
|
||||||
|
|
||||||
|
La version devient :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4-alpha.2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Matérialiser la première frontière PostgreSQL réelle sans encore raccorder les launchers ou les cas d'usage applicatifs :
|
||||||
|
|
||||||
|
- créer `knowledge-store-postgres-lib` comme unique owner PostgreSQL ;
|
||||||
|
- ajouter `tokio-postgres 0.7.18` comme driver async natif avec uniquement la feature `runtime` ;
|
||||||
|
- ajouter `deadpool-postgres 0.14.2` comme pool avec uniquement `rt_tokio_1` ;
|
||||||
|
- ajouter Refinery `0.9.2` avec uniquement son support `tokio-postgres` ;
|
||||||
|
- utiliser Tokio `1.53.1` uniquement comme runtime de test ;
|
||||||
|
- recevoir URL PostgreSQL et taille du pool par configuration injectée ;
|
||||||
|
- ne jamais lire de secret depuis l'environnement dans la crate produit ;
|
||||||
|
- construire un pool borné et conserver `NoTls` comme choix initial explicite ;
|
||||||
|
- embarquer `V1__initial_sources.sql` via Refinery ;
|
||||||
|
- créer uniquement `knowledge_sources` et `knowledge_source_sync_state` ;
|
||||||
|
- exposer une application explicite des migrations ;
|
||||||
|
- prouver le chemin sur un PostgreSQL réel isolé par schéma temporaire.
|
||||||
|
|
||||||
|
Git reste l'autorité du contenu publié. Cette tranche ne stocke ni corps de contenu, ni credential Git, ni compte, proposition, job, index ou projection finale.
|
||||||
|
|
||||||
|
## Dépendances retenues
|
||||||
|
|
||||||
|
```text
|
||||||
|
tokio-postgres 0.7.18 default-features = false, features = ["runtime"]
|
||||||
|
deadpool-postgres 0.14.2 default-features = false, features = ["rt_tokio_1"]
|
||||||
|
refinery 0.9.2 default-features = false, features = ["tokio-postgres"]
|
||||||
|
tokio 1.53.1 dev only, features = ["macros", "rt-multi-thread"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun ORM, TLS backend supplémentaire ou abstraction multi-SGBD n'est introduit.
|
||||||
|
|
||||||
|
## Contrat public
|
||||||
|
|
||||||
|
La façade crate-root est limitée à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgresStoreConfig
|
||||||
|
PostgresStore
|
||||||
|
PostgresStoreError
|
||||||
|
```
|
||||||
|
|
||||||
|
Les types `tokio-postgres`, Deadpool et Refinery restent privés à l'implémentation.
|
||||||
|
|
||||||
|
`PostgresStoreConfig` ne fournit aucun getter public de l'URL et n'implémente volontairement pas `Debug`. L'URL peut contenir des credentials et ne doit pas être exposée dans les erreurs ou logs.
|
||||||
|
|
||||||
|
## Premier schéma
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge_sources
|
||||||
|
source_id TEXT PRIMARY KEY
|
||||||
|
family TEXT NOT NULL CHECK pgr/howto/snippet/project_local
|
||||||
|
git_reference TEXT NOT NULL
|
||||||
|
|
||||||
|
knowledge_source_sync_state
|
||||||
|
source_id TEXT PRIMARY KEY REFERENCES knowledge_sources ON DELETE CASCADE
|
||||||
|
last_synchronized_revision TEXT NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
Refinery possède sa table technique de suivi des migrations. Aucune autre table métier n'est créée.
|
||||||
|
|
||||||
|
## Scope des fichiers
|
||||||
|
|
||||||
|
REPLACE :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION
|
||||||
|
Cargo.toml
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
ADD :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
crates/knowledge-store-postgres-lib/src/config.rs
|
||||||
|
crates/knowledge-store-postgres-lib/src/error.rs
|
||||||
|
crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
crates/knowledge-store-postgres-lib/unit_tests/config.rs
|
||||||
|
crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
deltas/0.0.4/alpha.2.md
|
||||||
|
```
|
||||||
|
|
||||||
|
DELETE : aucun.
|
||||||
|
|
||||||
|
Aucun fichier sous `Web/`, les trois launchers, `knowledge-app-lib`, `knowledge-domain-lib`, `knowledge-git-api`, `knowledge-git-lib` ou `knowledge-sync-api` n'est modifié.
|
||||||
|
|
||||||
|
Aucun history `0.0.4-alpha.2` n'est inclus : il doit refléter uniquement la gate opérateur réelle.
|
||||||
|
|
||||||
|
## Précondition PostgreSQL de la gate
|
||||||
|
|
||||||
|
La gate réelle nécessite une base PostgreSQL de test accessible avec les droits de créer et supprimer un schéma. Fournir l'URL uniquement dans l'environnement opérateur :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export KNOWLEDGE_POSTGRES_TEST_URL='postgresql://...'
|
||||||
|
```
|
||||||
|
|
||||||
|
Ne jamais versionner cette valeur.
|
||||||
|
|
||||||
|
Si `KNOWLEDGE_POSTGRES_TEST_URL` est absent ou si PostgreSQL n'est pas joignable, arrêter la gate et classer la tranche `BLOCKED`. Ne pas convertir cette absence en `PASS` ou en test ignoré.
|
||||||
|
|
||||||
|
## Gate opérateur
|
||||||
|
|
||||||
|
Exécuter depuis la racine du dépôt après application du delta.
|
||||||
|
|
||||||
|
### Version, history précédent et scope
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test "$(cat VERSION)" = '0.0.4-alpha.2'
|
||||||
|
grep -F 'version = "0.0.4-alpha.2"' Cargo.toml
|
||||||
|
|
||||||
|
test -f history/0.0.4/alpha.1.md
|
||||||
|
grep -A2 '^## Statut$' history/0.0.4/alpha.1.md | grep -Fx 'PASS'
|
||||||
|
git ls-files --error-unmatch history/0.0.4/alpha.1.md >/dev/null
|
||||||
|
test -z "$(git status --short -- history/0.0.4/alpha.1.md)"
|
||||||
|
|
||||||
|
test -d crates/knowledge-store-postgres-lib
|
||||||
|
test -f crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
test -f deltas/0.0.4/alpha.2.md
|
||||||
|
test ! -e history/0.0.4/alpha.2.md
|
||||||
|
|
||||||
|
test -z "$(git status --short -- Web crates/knowledge-pgr-server crates/knowledge-howtos-server crates/knowledge-snippets-server crates/knowledge-app-lib crates/knowledge-domain-lib crates/knowledge-git-api crates/knowledge-git-lib crates/knowledge-sync-api)"
|
||||||
|
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dépendances, façade et configuration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -F 'tokio-postgres = { version = "0.7.18", default-features = false, features = ["runtime"] }' crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
grep -F 'deadpool-postgres = { version = "0.14.2", default-features = false, features = ["rt_tokio_1"] }' crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
grep -F 'refinery = { version = "0.9.2", default-features = false, features = ["tokio-postgres"] }' crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
grep -F 'tokio = { version = "1.53.1", default-features = false, features = ["macros", "rt-multi-thread"] }' crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
|
||||||
|
test "$(grep -c '^pub use crate::' crates/knowledge-store-postgres-lib/src/lib.rs)" -eq 3
|
||||||
|
grep -Fx 'pub use crate::config::PostgresStoreConfig;' crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
grep -Fx 'pub use crate::error::PostgresStoreError;' crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
grep -Fx 'pub use crate::store::PostgresStore;' crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
|
||||||
|
! grep -R -n -E 'std::env|KNOWLEDGE_POSTGRES_TEST_URL' crates/knowledge-store-postgres-lib/src
|
||||||
|
! grep -R -n -E 'println!|eprintln!|dbg!' crates/knowledge-store-postgres-lib/src
|
||||||
|
|
||||||
|
grep -F 'max_pool_size' crates/knowledge-store-postgres-lib/src/config.rs
|
||||||
|
grep -F 'deadpool_postgres::Manager::new' crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
grep -F 'deadpool_postgres::Pool::builder' crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
grep -F 'tokio_postgres::NoTls' crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
grep -F 'run_async(client)' crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration et test ciblé
|
||||||
|
|
||||||
|
```bash
|
||||||
|
grep -F 'CREATE TABLE knowledge_sources' crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
grep -F "CHECK (family IN ('pgr', 'howto', 'snippet', 'project_local'))" crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
grep -F 'CREATE TABLE knowledge_source_sync_state' crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
grep -F 'REFERENCES knowledge_sources(source_id) ON DELETE CASCADE' crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
grep -F 'last_synchronized_revision TEXT NOT NULL' crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
|
||||||
|
! grep -E -i '\b(content|body|credential|account|proposal|job)\b' crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
|
||||||
|
grep -F '#[ignore = "requires KNOWLEDGE_POSTGRES_TEST_URL"]' crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
grep -F 'CREATE SCHEMA' crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
grep -F 'DROP SCHEMA' crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Markdown puis séquence Rust
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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.4-alpha.2-metadata.json
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p knowledge-store-postgres-lib
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
### Composition workspace
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'import json; from pathlib import Path; m=json.loads(Path("/tmp/sasedev-knowledge-0.0.4-alpha.2-metadata.json").read_text()); expected={"knowledge-app-lib","knowledge-domain-lib","knowledge-git-api","knowledge-git-lib","knowledge-sync-api","knowledge-web-lib","knowledge-pgr-server","knowledge-howtos-server","knowledge-snippets-server","knowledge-store-postgres-lib"}; assert len(m["workspace_members"]) == 10; assert {p["name"] for p in m["packages"]} == expected; print("Knowledge 0.0.4-alpha.2 workspace audit: clean")'
|
||||||
|
```
|
||||||
|
|
||||||
|
### PostgreSQL réel obligatoire
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -n "${KNOWLEDGE_POSTGRES_TEST_URL:-}"
|
||||||
|
cargo test -p knowledge-store-postgres-lib --test postgres_migration -- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
Si la première commande échoue ou si le test ne peut pas joindre PostgreSQL, arrêter ici et reporter `BLOCKED` avec le transcript obtenu.
|
||||||
|
|
||||||
|
### Hygiène, lockfiles et staging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c 'from pathlib import Path; roots=[Path("README.md"),Path("RULES.md"),Path("ROADMAP.md"),Path("CHANGELOG.md"),Path("Cargo.toml"),Path("VERSION"),Path("docs"),Path("prompts"),Path("deltas"),Path("history"),Path("crates")]; allowed={".md",".rs",".toml",".sql"}; paths=[p for r in roots for p in ([r] if r.is_file() else r.rglob("*")) if p.is_file() and (p.name == "VERSION" or p.suffix in allowed)]; bad=[str(p) for p in paths if (lambda b: b"\r" in b or not b.endswith(b"\n") or b.endswith(b"\n\n") or any(line != line.rstrip(" \t") for line in b.decode("utf-8").splitlines()))(p.read_bytes())]; assert not bad, bad; print(f"Knowledge 0.0.4-alpha.2 text hygiene: clean ({len(paths)} file(s))")'
|
||||||
|
|
||||||
|
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 diff --quiet -- Web/knowledge-template-lab/package-lock.json
|
||||||
|
|
||||||
|
git add -A
|
||||||
|
git diff --cached --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transition
|
||||||
|
|
||||||
|
### PASS
|
||||||
|
|
||||||
|
Produire séparément :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.4/alpha.2.md
|
||||||
|
```
|
||||||
|
|
||||||
|
avec les résultats réellement observés. Après application du history, commit/push `0.0.4-alpha.2`, puis préparer `0.0.4-alpha.3` pour la persistance minimale de source et d'état de synchronisation.
|
||||||
|
|
||||||
|
### FAIL
|
||||||
|
|
||||||
|
Produire le history réel et le committer avant correction. Une erreur bornée de code, manifest, migration ou test utilise `0.0.4-alpha.2.fix.1`.
|
||||||
|
|
||||||
|
### BLOCKED
|
||||||
|
|
||||||
|
Si seul le PostgreSQL réel requis est indisponible, historiser `BLOCKED` sans prétendre que la gate a passé. Ne modifier ni le code ni l'architecture tant qu'aucun défaut n'est démontré ; reprendre la même preuve dès qu'un PostgreSQL de test est disponible.
|
||||||
|
|
||||||
|
### REPLAN
|
||||||
|
|
||||||
|
Replanifier si la toolchain stable réellement utilisée est incompatible avec les versions retenues, si l'isolation par schéma n'est pas fiable ou si l'ownership PostgreSQL doit être changé.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/000-README.md -->
|
<!-- file: docs/000-README.md -->
|
||||||
<!-- version: 9 -->
|
<!-- version: 13 -->
|
||||||
|
|
||||||
# Documentation SaseDev Knowledge
|
# Documentation SaseDev Knowledge
|
||||||
|
|
||||||
@@ -31,11 +31,16 @@
|
|||||||
- [`studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md`](studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md) — réconciliation beta de la fondation, DEFER conservés et revue d'horizon engageant uniquement `0.0.2`.
|
- [`studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md`](studies/003-V0_0_1_FOUNDATION_RECONCILIATION.md) — réconciliation beta de la fondation, DEFER conservés et revue d'horizon engageant uniquement `0.0.2`.
|
||||||
- [`studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md`](studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md) — étude comparative Git de démarrage `0.0.2`, décision process et ownership du backend.
|
- [`studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md`](studies/004-V0_0_2_GIT_IMPLEMENTATION_STUDY.md) — étude comparative Git de démarrage `0.0.2`, décision process et ownership du backend.
|
||||||
- [`studies/005-V0_0_2_GIT_SYNC_RECONCILIATION.md`](studies/005-V0_0_2_GIT_SYNC_RECONCILIATION.md) — réconciliation beta du chemin Git/synchronisation, `DEFER` conservés et revue d'horizon sans prochaine release réservée.
|
- [`studies/005-V0_0_2_GIT_SYNC_RECONCILIATION.md`](studies/005-V0_0_2_GIT_SYNC_RECONCILIATION.md) — réconciliation beta du chemin Git/synchronisation, `DEFER` conservés et revue d'horizon sans prochaine release réservée.
|
||||||
|
- [`studies/006-V0_0_3_GIT_WRITE_STUDY.md`](studies/006-V0_0_3_GIT_WRITE_STUDY.md) — décision d'engager l'écriture Git contrôlée, stratégie de worktree et invariants de concurrence.
|
||||||
|
- [`studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md`](studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md) — réconciliation beta du chemin Git d'écriture, DEFER reclassés et horizon produit des trois sites.
|
||||||
|
- [`studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md`](studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md) — comparaison PostgreSQL de démarrage `0.0.4`, décision `tokio-postgres`/Deadpool/Refinery, ownership, schéma minimal, migrations et tests éphémères.
|
||||||
|
|
||||||
## Plans
|
## Plans
|
||||||
|
|
||||||
- [`plans/001-V0_0_1_FOUNDATION_PLAN.md`](plans/001-V0_0_1_FOUNDATION_PLAN.md) — plan historique de la stable de fondation `0.0.1`.
|
- [`plans/001-V0_0_1_FOUNDATION_PLAN.md`](plans/001-V0_0_1_FOUNDATION_PLAN.md) — plan historique de la stable de fondation `0.0.1`.
|
||||||
- [`plans/002-V0_0_2_GIT_SYNC_PLAN.md`](plans/002-V0_0_2_GIT_SYNC_PLAN.md) — plan historique de la stable Git générique et synchronisation de lecture `0.0.2`.
|
- [`plans/002-V0_0_2_GIT_SYNC_PLAN.md`](plans/002-V0_0_2_GIT_SYNC_PLAN.md) — plan historique de la stable Git générique et synchronisation de lecture `0.0.2`.
|
||||||
|
- [`plans/003-V0_0_3_GIT_WRITE_PLAN.md`](plans/003-V0_0_3_GIT_WRITE_PLAN.md) — plan historique de la stable `0.0.3`, écriture Git contrôlée et push gardé.
|
||||||
|
- [`plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md`](plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md) — plan actif de la fondation PostgreSQL partagée `0.0.4`.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
@@ -46,3 +51,5 @@ Les deltas et histories sont des artefacts de workflow, pas des substituts à ce
|
|||||||
## Handoff
|
## Handoff
|
||||||
|
|
||||||
- [`../prompts/002-V0_0_2_START_PROMPT.md`](../prompts/002-V0_0_2_START_PROMPT.md) — handoff historique exécuté pour ouvrir `0.0.2` depuis `v0.0.1` publiée et vérifiée.
|
- [`../prompts/002-V0_0_2_START_PROMPT.md`](../prompts/002-V0_0_2_START_PROMPT.md) — handoff historique exécuté pour ouvrir `0.0.2` depuis `v0.0.1` publiée et vérifiée.
|
||||||
|
- [`../prompts/003-V0_0_3_START_PROMPT.md`](../prompts/003-V0_0_3_START_PROMPT.md) — prompt local de reprise de `0.0.3` depuis `v0.0.2` publiée et vérifiée.
|
||||||
|
- [`../prompts/004-V0_0_4_START_PROMPT.md`](../prompts/004-V0_0_4_START_PROMPT.md) — handoff préparé pendant `0.0.3` pour ouvrir `0.0.4` depuis la stable `v0.0.3` une fois son tag/archive et son rapport post-tag réellement publiés et vérifiés.
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
<!-- file: docs/architecture/001-PROJECT_PROFILE.md -->
|
<!-- file: docs/architecture/001-PROJECT_PROFILE.md -->
|
||||||
<!-- version: 16 -->
|
<!-- version: 20 -->
|
||||||
|
|
||||||
# Profil du projet Knowledge
|
# Profil du projet Knowledge
|
||||||
|
|
||||||
## Baseline autoritaire
|
## Baseline autoritaire
|
||||||
|
|
||||||
La stable produit depuis laquelle `0.0.2` est ouverte est :
|
La stable produit depuis laquelle `0.0.4` est ouverte est :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Knowledge stable tag v0.0.1
|
Knowledge stable tag v0.0.3
|
||||||
Knowledge stable commit 98ec46fe73bd50dacbe6be220650d87dd2000b74
|
Knowledge stable commit b3823972449150b72905930b806d4d15471cddce
|
||||||
Knowledge archive SHA-256 1f0b2cfaecee1cbee621abb9c3c95811773ebfc17c374c4369fdc220bcf7d72d
|
Knowledge archive SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d
|
||||||
Knowledge publish report sasedev-knowledge-0.0.1-publish.001-report.md = PASS
|
Knowledge publish report sasedev-knowledge-0.0.3-publish.001-report.md = PASS
|
||||||
Knowledge start prompt prompts/002-V0_0_2_START_PROMPT.md
|
Knowledge start prompt prompts/004-V0_0_4_START_PROMPT.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
L'archive taggée `v0.0.3` a été vérifiée avant le cadrage `0.0.4-alpha.1` : `VERSION` et le workspace Cargo valent `0.0.3`, les douze deltas et douze histories `0.0.3` sont appariés, `rel.1.fix.1 = PASS` clôt la release et le commentaire ZIP contient le commit stable ci-dessus.
|
||||||
|
|
||||||
La fondation `0.0.1` a été construite sous PGR `v0.1.1`. Cette identité reste historique dans les artefacts `0.0.1`.
|
La fondation `0.0.1` a été construite sous PGR `v0.1.1`. Cette identité reste historique dans les artefacts `0.0.1`.
|
||||||
|
|
||||||
Le démarrage explicite de `0.0.2` a réévalué PGR et a retenu la stable suivante comme source d'adoption :
|
Le démarrage explicite de `0.0.2` a réévalué PGR et a retenu la stable suivante comme source d'adoption :
|
||||||
@@ -34,7 +36,7 @@ L'archive PGR `v0.1.3` vérifiée possède `VERSION = 0.1.3`, `332` entrées ZIP
|
|||||||
|
|
||||||
Cette prévalidation ne remplace aucune gate opérateur Knowledge.
|
Cette prévalidation ne remplace aucune gate opérateur Knowledge.
|
||||||
|
|
||||||
## Nature de l'adoption
|
## Historique de l'adoption
|
||||||
|
|
||||||
Knowledge n'est plus un nouveau dépôt à instancier : `0.0.2-alpha.1` migre un projet existant de la baseline PGR `v0.1.1` vers `v0.1.3`.
|
Knowledge n'est plus un nouveau dépôt à instancier : `0.0.2-alpha.1` migre un projet existant de la baseline PGR `v0.1.1` vers `v0.1.3`.
|
||||||
|
|
||||||
@@ -83,13 +85,15 @@ Le détail de la migration depuis les owners PGR `v0.1.3`, y compris les décisi
|
|||||||
| Implémentation Git de `0.0.2` | `KEEP-LOCAL` | processus `git` contrôlé derrière `knowledge-git-api` ; décision réévaluable derrière le contrat |
|
| Implémentation Git de `0.0.2` | `KEEP-LOCAL` | processus `git` contrôlé derrière `knowledge-git-api` ; décision réévaluable derrière le contrat |
|
||||||
| Configuration physique Git | `KEEP-LOCAL` | `knowledge-git-lib` matérialisée en `0.0.2-alpha.2` ; source locale/remote + racine d'état ; pas dans le domaine |
|
| Configuration physique Git | `KEEP-LOCAL` | `knowledge-git-lib` matérialisée en `0.0.2-alpha.2` ; source locale/remote + racine d'état ; pas dans le domaine |
|
||||||
| Copies Git générées | `KEEP-LOCAL` | racine d'état explicite hors dépôt source, mirrors et snapshots adressés par clé sûre/révision |
|
| Copies Git générées | `KEEP-LOCAL` | racine d'état explicite hors dépôt source, mirrors et snapshots adressés par clé sûre/révision |
|
||||||
|
| Écriture Git contrôlée `0.0.3` | `KEEP-LOCAL` | contrat distinct de la lecture, worktree isolé, commit explicite et push de branche gardé par état attendu |
|
||||||
|
| Rebase/merge automatique | `DEFER` | reprendre avec un workflow de proposition réellement matérialisé ; divergence = conflit en `0.0.3` |
|
||||||
| Partage du cache Git entre processus | `DEFER` | état root exclusif à une instance backend en `0.0.2` ; reprendre avec verrouillage inter-processus réel |
|
| Partage du cache Git entre processus | `DEFER` | état root exclusif à une instance backend en `0.0.2` ; reprendre avec verrouillage inter-processus réel |
|
||||||
| Forge | `KEEP-LOCAL` | capacité optionnelle derrière adapter ; aucune API de forge dans le domaine |
|
| Forge | `KEEP-LOCAL` | capacité optionnelle derrière adapter ; aucune API de forge dans le domaine |
|
||||||
| Gitea | `DEFER` | premier adapter candidat ; reprendre lorsque des capacités forge-only sont implémentées |
|
| Gitea | `DEFER` | premier adapter candidat ; reprendre lorsque des capacités forge-only sont implémentées |
|
||||||
| Synchronisation sans webhook | `KEEP-LOCAL` | explicite/périodique convergent vers le même cas d'usage ; hooks/webhooks restent accélérateurs |
|
| Synchronisation sans webhook | `KEEP-LOCAL` | explicite/périodique convergent vers le même cas d'usage ; hooks/webhooks restent accélérateurs |
|
||||||
| Scheduler périodique concret | `DEFER` | créer seulement lorsque son owner runtime/lifecycle est démontré |
|
| Scheduler périodique concret | `DEFER` | créer seulement lorsque son owner runtime/lifecycle est démontré |
|
||||||
| Dernière révision observée en `0.0.2` | `KEEP-LOCAL` | fournie par l'appelant et retournée par le cas d'usage ; pas de pseudo-persistance locale |
|
| Dernière révision observée en `0.0.2` | `KEEP-LOCAL` | fournie par l'appelant et retournée par le cas d'usage ; pas de pseudo-persistance locale |
|
||||||
| PostgreSQL | `KEEP-LOCAL` | une base logique partagée future pour état transversal/projections ; jamais vérité du contenu Git publié |
|
| PostgreSQL | `KEEP-LOCAL` | une base logique partagée ; `0.0.4` matérialise sa première fondation sans déplacer la vérité éditoriale de Git |
|
||||||
| Rust stable / édition 2024 | `ADOPT` | owner local `docs/rules/510-RUST_CARGO.md` |
|
| Rust stable / édition 2024 | `ADOPT` | owner local `docs/rules/510-RUST_CARGO.md` |
|
||||||
| Workspace Cargo | `ADOPT` | configuration locale + owner `510-RUST_CARGO.md` |
|
| Workspace Cargo | `ADOPT` | configuration locale + owner `510-RUST_CARGO.md` |
|
||||||
| `Cargo.lock` | `KEEP-LOCAL` | généré localement et ignoré ; manifests Cargo versionnés |
|
| `Cargo.lock` | `KEEP-LOCAL` | généré localement et ignoré ; manifests Cargo versionnés |
|
||||||
@@ -103,8 +107,8 @@ Le détail de la migration depuis les owners PGR `v0.1.3`, y compris les décisi
|
|||||||
| Racine frontend | `KEEP-LOCAL` | package partagé `Web/knowledge-template-lab/` |
|
| Racine frontend | `KEEP-LOCAL` | package partagé `Web/knowledge-template-lab/` |
|
||||||
| Templates statiques Web | `KEEP-LOCAL` | harness de conception/validation ; rendu runtime Maud |
|
| Templates statiques Web | `KEEP-LOCAL` | harness de conception/validation ; rendu runtime Maud |
|
||||||
| Intégration manifest Vite / Maud | `KEEP-LOCAL` | matérialisée dans `knowledge-web-lib` |
|
| Intégration manifest Vite / Maud | `KEEP-LOCAL` | matérialisée dans `knowledge-web-lib` |
|
||||||
| Driver/ORM PostgreSQL | `DEFER` | reprendre à la première implémentation persistante réelle |
|
| Toolkit PostgreSQL | `KEEP-LOCAL` | `tokio-postgres` + `deadpool-postgres`, async/Tokio et SQL explicite ; aucun ORM sélectionné |
|
||||||
| Migrations PostgreSQL | `DEFER` | reprendre avec le premier schéma réel |
|
| Migrations PostgreSQL | `KEEP-LOCAL` | SQL versionné possédé par `knowledge-store-postgres-lib`, appliqué explicitement via Refinery |
|
||||||
| Moteur de recherche | `DEFER` | commencer par capacités PostgreSQL/index applicatif lorsque ce scope sera engagé |
|
| Moteur de recherche | `DEFER` | commencer par capacités PostgreSQL/index applicatif lorsque ce scope sera engagé |
|
||||||
| Fournisseur d'auth externe | `DEFER` | reprendre lorsque le workflow utilisateur l'exige |
|
| Fournisseur d'auth externe | `DEFER` | reprendre lorsque le workflow utilisateur l'exige |
|
||||||
| Surface admin par application | `KEEP-LOCAL` | chaque serveur protège ses opérations privilégiées ; URL exacte différée |
|
| Surface admin par application | `KEEP-LOCAL` | chaque serveur protège ses opérations privilégiées ; URL exacte différée |
|
||||||
@@ -127,14 +131,16 @@ knowledge-web-lib composition Actix/Maud/Vite partagée
|
|||||||
knowledge-pgr-server launcher PGR mince
|
knowledge-pgr-server launcher PGR mince
|
||||||
knowledge-howtos-server launcher HOWTOs mince
|
knowledge-howtos-server launcher HOWTOs mince
|
||||||
knowledge-snippets-server launcher snippets mince
|
knowledge-snippets-server launcher snippets mince
|
||||||
|
knowledge-store-postgres-lib pool, migrations et état applicatif PostgreSQL partagé
|
||||||
```
|
```
|
||||||
|
|
||||||
`knowledge-git-lib` devient réelle en `0.0.2-alpha.2` puis implémente le chemin de lecture Git dans `alpha.3`. `knowledge-app-lib` devient réelle en `0.0.2-alpha.5` : l'orchestration refresh / résolution / comparaison / snapshot confirme une responsabilité application partagée distincte du backend Git et des launchers.
|
`knowledge-git-lib` devient réelle en `0.0.2-alpha.2` puis implémente le chemin de lecture Git dans `alpha.3`. `knowledge-app-lib` devient réelle en `0.0.2-alpha.5` : l'orchestration refresh / résolution / comparaison / snapshot confirme une responsabilité application partagée distincte du backend Git et des launchers.
|
||||||
|
|
||||||
|
`0.0.4-alpha.2` matérialise `knowledge-store-postgres-lib` comme responsabilité réelle : configuration explicite, pool `deadpool-postgres`, migrations Refinery et premier schéma partagé. Aucun `knowledge-store-api` symétrique n'est introduit.
|
||||||
|
|
||||||
Les responsabilités suivantes restent candidates et non matérialisées :
|
Les responsabilités suivantes restent candidates et non matérialisées :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
knowledge-store-postgres-lib état applicatif PostgreSQL partagé
|
|
||||||
knowledge-forge-gitea-lib adapter Gitea optionnel
|
knowledge-forge-gitea-lib adapter Gitea optionnel
|
||||||
knowledge-logging-lib diagnostics partagés, seulement sur besoin multi-runtime réel
|
knowledge-logging-lib diagnostics partagés, seulement sur besoin multi-runtime réel
|
||||||
```
|
```
|
||||||
@@ -170,6 +176,30 @@ Elles justifient de conserver séparées l'identité/authentification partagée,
|
|||||||
|
|
||||||
`0.0.2-beta.1` confirme que le scope alpha est matérialisé par neuf packages Cargo : contrats domaine/Git/synchronisation, backend Git générique, orchestration application partagée, composition Web et trois launchers. Aucune responsabilité supplémentaire n'est nécessaire pour fermer la mission de lecture/synchronisation de `0.0.2`.
|
`0.0.2-beta.1` confirme que le scope alpha est matérialisé par neuf packages Cargo : contrats domaine/Git/synchronisation, backend Git générique, orchestration application partagée, composition Web et trois launchers. Aucune responsabilité supplémentaire n'est nécessaire pour fermer la mission de lecture/synchronisation de `0.0.2`.
|
||||||
|
|
||||||
Restent différés : adapter Gitea, PostgreSQL/migrations, moteur de recherche, auth/admin complète, quatrième application d'administration, modèle détaillé rôles/capabilities, formats finaux HOWTO/snippet, écriture/publication Git, scheduler concret, support explicite submodules/LFS, politique de nettoyage/rétention des snapshots et partage concurrent de la même racine Git entre plusieurs processus.
|
Restent différés après `0.0.2` : adapter Gitea, PostgreSQL/migrations, moteur de recherche, auth/admin complète, quatrième application d'administration, modèle détaillé rôles/capabilities, formats finaux HOWTO/snippet, scheduler concret, support explicite submodules/LFS, politique de nettoyage/rétention des snapshots et partage concurrent de la même racine Git entre plusieurs processus.
|
||||||
|
|
||||||
La revue d'horizon beta ne réserve aucune release suivante. Un changement de scope produit, un besoin mesuré ou une contrainte d'exploitation pourra engager ultérieurement une nouvelle mission ; il ne justifie pas un choix anticipé.
|
La revue d'horizon `0.0.2-beta.1` n'avait réservé aucune release suivante. L'ouverture explicite de `0.0.3` a consommé uniquement le `DEFER` d'écriture Git contrôlée : worktree d'écriture isolé, commit candidat et push de branche gardé. Rebase/merge automatique et workflow complet de proposition restent différés.
|
||||||
|
|
||||||
|
La revue `0.0.3-beta.1` sélectionne désormais la fondation PostgreSQL partagée comme prochaine mission `0.0.4`, sans préchoisir son driver/ORM avant l'étude de démarrage. Elle réordonne ensuite l'horizon produit pour rendre visibles les phases de modèles/projections, templates publics, templates admin, intégration Actix/Maud puis workflow de gestion/publication. Gitea, scheduler, recherche avancée, quatrième application admin et partage multi-processus restent différés tant que leurs déclencheurs ne sont pas matérialisés.
|
||||||
|
|
||||||
|
## Ouverture `0.0.4` — fondation PostgreSQL partagée
|
||||||
|
|
||||||
|
Après publication vérifiée de `v0.0.3`, `0.0.4-alpha.1` consomme les `DEFER` portant sur le toolkit PostgreSQL et les migrations.
|
||||||
|
|
||||||
|
La décision locale est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
owner knowledge-store-postgres-lib
|
||||||
|
driver tokio-postgres 0.7.18
|
||||||
|
modèle SQL explicite, sans ORM
|
||||||
|
pool deadpool-postgres 0.14.2
|
||||||
|
migrations SQL versionné + Refinery 0.9.2
|
||||||
|
premier état source logique + dernière révision synchronisée
|
||||||
|
autorité contenu Git
|
||||||
|
```
|
||||||
|
|
||||||
|
`alpha.2` matérialise cette décision avec une façade crate-root limitée à `PostgresStoreConfig`, `PostgresStore` et `PostgresStoreError`, plus `V1__initial_sources.sql`. La configuration est injectée ; la crate ne lit pas `KNOWLEDGE_POSTGRES_TEST_URL` ni une autre variable d'environnement. La première connexion utilise `NoTls` et un backend TLS reste différé tant qu'un déploiement n'en démontre pas le besoin.
|
||||||
|
|
||||||
|
La configuration physique Git reste possédée par `knowledge-git-lib`. Le premier schéma PostgreSQL ne stocke pas de credentials Git et ne transforme pas les URLs/paths physiques en vérité métier.
|
||||||
|
|
||||||
|
Les `DEFER` Gitea/forge, scheduler, recherche avancée, auth/capabilities, formats finaux de contenu, quatrième application admin, partage multi-processus de racine Git, LFS/submodules et rétention avancée restent inchangés. L'horizon `0.0.5` à `0.0.10` reste cohérent et n'est pas replanifié par cette étude.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md -->
|
<!-- file: docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 20 -->
|
||||||
|
|
||||||
# Architecture de fondation Knowledge
|
# Architecture de fondation Knowledge
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@ knowledge-app-lib cas d'usage partagé de synchronisation Changed/Unchanged
|
|||||||
|
|
||||||
Les deux crates `-api` sont justifiées par des implémentations réellement remplaçables ou multiples. Elles restent sans runtime, sans logging propre et sans dépendance à Actix, Gitea ou PostgreSQL.
|
Les deux crates `-api` sont justifiées par des implémentations réellement remplaçables ou multiples. Elles restent sans runtime, sans logging propre et sans dépendance à Actix, Gitea ou PostgreSQL.
|
||||||
|
|
||||||
|
`0.0.4-alpha.2` matérialise `knowledge-store-postgres-lib` comme owner concret du pool, des migrations et de l'état PostgreSQL partagé. La crate masque `tokio-postgres`, Deadpool et Refinery derrière sa façade publique et aucun `knowledge-store-api` symétrique n'est introduit sans besoin de substitution démontré.
|
||||||
|
|
||||||
Les tests unitaires Rust sont désormais placés hors de `src/` sous `unit_tests/`, conformément à la convention locale Knowledge sélectionnée sur l'option permise par PGR.
|
Les tests unitaires Rust sont désormais placés hors de `src/` sous `unit_tests/`, conformément à la convention locale Knowledge sélectionnée sur l'option permise par PGR.
|
||||||
|
|
||||||
Le workspace est une application déployable à train de release unique. Knowledge choisit néanmoins de conserver `Cargo.lock` comme état généré local et ignoré par Git ; les manifests Cargo portent le contrat versionné des dépendances. Les artefacts Cargo sont dirigés hors du dépôt vers `../builds/sasedev-knowledge/target/`.
|
Le workspace est une application déployable à train de release unique. Knowledge choisit néanmoins de conserver `Cargo.lock` comme état généré local et ignoré par Git ; les manifests Cargo portent le contrat versionné des dépendances. Les artefacts Cargo sont dirigés hors du dépôt vers `../builds/sasedev-knowledge/target/`.
|
||||||
@@ -135,17 +137,21 @@ Les données générées résident hors du dépôt applicatif et hors des dépô
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
<git-state-root>/
|
<git-state-root>/
|
||||||
|
empty-global.gitconfig
|
||||||
hooks-disabled/
|
hooks-disabled/
|
||||||
repositories/
|
repositories/
|
||||||
<source-key>.git/
|
<source-key>.git/
|
||||||
snapshots/
|
snapshots/
|
||||||
<source-key>/
|
<source-key>/
|
||||||
<resolved-revision>/
|
<resolved-revision>/
|
||||||
|
write-worktrees/
|
||||||
|
<source-key>/
|
||||||
|
candidate/
|
||||||
```
|
```
|
||||||
|
|
||||||
Le backend reçoit explicitement `<git-state-root>` ; il n'utilise aucun défaut implicite sous le workspace. Une variable de déploiement telle que `KNOWLEDGE_GIT_STATE_ROOT` pourra alimenter la composition, mais la librairie reçoit un chemin et ne lit pas elle-même l'environnement.
|
Le backend reçoit explicitement `<git-state-root>` ; il n'utilise aucun défaut implicite sous le workspace. Une variable de déploiement telle que `KNOWLEDGE_GIT_STATE_ROOT` pourra alimenter la composition, mais la librairie reçoit un chemin et ne lit pas elle-même l'environnement.
|
||||||
|
|
||||||
`source-key` est une clé sûre dérivée par l'implémentation. En `alpha.2`, elle encode de manière déterministe les octets UTF-8 du `SourceId` sous une forme hexadécimale préfixée ; un `SourceId` ou une URL remote ne sont jamais interpolés directement comme chemin de filesystem.
|
`source-key` est une clé sûre dérivée par l'implémentation Git. Depuis `0.0.2-alpha.2`, elle encode de manière déterministe les octets UTF-8 du `SourceId` sous une forme hexadécimale préfixée ; un `SourceId` ou une URL remote ne sont jamais interpolés directement comme chemin de filesystem.
|
||||||
|
|
||||||
Pendant `0.0.2`, une racine d'état appartient exclusivement à une instance backend/processus. Le partage simultané d'une même racine entre plusieurs processus reste `DEFER` jusqu'à matérialisation d'un verrouillage inter-processus.
|
Pendant `0.0.2`, une racine d'état appartient exclusivement à une instance backend/processus. Le partage simultané d'une même racine entre plusieurs processus reste `DEFER` jusqu'à matérialisation d'un verrouillage inter-processus.
|
||||||
|
|
||||||
@@ -177,9 +183,23 @@ Le snapshot de lecture est ensuite créé comme worktree détaché adressé par
|
|||||||
|
|
||||||
Les hooks Git sont désactivés pour les opérations de checkout/worktree appartenant au backend. Les commandes sont exécutées directement sans shell, avec prompts interactifs désactivés et sans journaliser de credential.
|
Les hooks Git sont désactivés pour les opérations de checkout/worktree appartenant au backend. Les commandes sont exécutées directement sans shell, avec prompts interactifs désactivés et sans journaliser de credential.
|
||||||
|
|
||||||
### Scope futur du contrat Git
|
### Écriture Git engagée en `0.0.3`
|
||||||
|
|
||||||
`alpha.3` étend `GitRepository` uniquement avec `prepare_snapshot`, parce que la matérialisation locale devient effectivement nécessaire au cas d'usage de lecture. Les opérations génériques à plus long terme peuvent inclure `status/diff`, branch, commit, merge/rebase et push. Elles ne sont pas ajoutées en `0.0.2`.
|
`0.0.3` ajoute une frontière d'écriture distincte du `GitRepository` de lecture. Le backend conserve le processus `git` contrôlé et matérialise un worktree d'écriture isolé sous la racine d'état, sans modifier les snapshots de lecture.
|
||||||
|
|
||||||
|
Le chemin cible est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
base GitRevision explicite
|
||||||
|
-> worktree d'écriture contrôlé
|
||||||
|
-> mutations de fichiers confinées
|
||||||
|
-> commit avec identité explicite
|
||||||
|
-> révision candidate
|
||||||
|
-> push vers refs/heads/... gardé par l'état distant attendu
|
||||||
|
-> succès ou conflit explicite
|
||||||
|
```
|
||||||
|
|
||||||
|
Une divergence ne déclenche ni rebase, ni merge, ni force-push non gardé. Ces décisions appartiennent à un futur workflow de proposition/publication. Les capacités forge-specific restent séparées.
|
||||||
|
|
||||||
La baseline conserve au moins trois sources indépendantes (`pgr`, `howtos`, `snippets`) mais le mapping reste configurable et extensible ; aucun `match` codé en dur n'impose exactement trois repositories.
|
La baseline conserve au moins trois sources indépendantes (`pgr`, `howtos`, `snippets`) mais le mapping reste configurable et extensible ; aucun `match` codé en dur n'impose exactement trois repositories.
|
||||||
|
|
||||||
@@ -230,7 +250,7 @@ Un webhook peut être perdu ou dupliqué. Le commit Git résolu reste l'identit
|
|||||||
|
|
||||||
La synchronisation partagée est une responsabilité de couche application, distincte du backend Git et des producteurs de signaux. `0.0.2-alpha.5` confirme cette responsabilité et matérialise `knowledge-app-lib` comme owner du cas d'usage provider-neutral ; les launchers n'ont pas à dupliquer cette orchestration.
|
La synchronisation partagée est une responsabilité de couche application, distincte du backend Git et des producteurs de signaux. `0.0.2-alpha.5` confirme cette responsabilité et matérialise `knowledge-app-lib` comme owner du cas d'usage provider-neutral ; les launchers n'ont pas à dupliquer cette orchestration.
|
||||||
|
|
||||||
Sans PostgreSQL dans cette release, la dernière révision connue n'est pas stockée dans un fichier ad hoc caché. Le cas d'usage reçoit `previous_revision` de l'appelant et retourne explicitement la révision observée ainsi qu'un résultat `Changed` ou `Unchanged`. La future persistance pourra remplacer cet état appelant sans modifier la frontière Git.
|
Le cas d'usage de synchronisation existant continue de recevoir `previous_revision` de l'appelant et retourne explicitement la révision observée ainsi qu'un résultat `Changed` ou `Unchanged`. `0.0.4-alpha.2` matérialise seulement le store et le schéma ; le raccord de cette persistance au cas d'usage reste réservé à `alpha.4`, sans modifier la frontière Git.
|
||||||
|
|
||||||
La cause `Periodic` reste supportée par `knowledge-sync-api`, mais `0.0.2-alpha.1` ne désigne pas encore de scheduler concret. Un scheduler n'est matérialisé que si son lifecycle/runtime est démontré ; lorsqu'il existe, il appelle exactement le même cas d'usage que la voie explicite.
|
La cause `Periodic` reste supportée par `knowledge-sync-api`, mais `0.0.2-alpha.1` ne désigne pas encore de scheduler concret. Un scheduler n'est matérialisé que si son lifecycle/runtime est démontré ; lorsqu'il existe, il appelle exactement le même cas d'usage que la voie explicite.
|
||||||
|
|
||||||
@@ -282,7 +302,30 @@ La baseline retient **une base PostgreSQL logique partagée** par les trois appl
|
|||||||
|
|
||||||
Ce choix est justifié par l'état transversal : identités, sessions, propositions, synchronisation, relations entre familles, index et recherche. Il évite trois bases divergentes pour des concepts qui appartiennent à la même plateforme.
|
Ce choix est justifié par l'état transversal : identités, sessions, propositions, synchronisation, relations entre familles, index et recherche. Il évite trois bases divergentes pour des concepts qui appartiennent à la même plateforme.
|
||||||
|
|
||||||
PostgreSQL MAY contenir des documents/index/métadonnées dérivés et assurer une première capacité de recherche. Aucun moteur de recherche externe n'est sélectionné en `0.0.1`.
|
### Fondation matérialisée pour `0.0.4`
|
||||||
|
|
||||||
|
`0.0.4-alpha.1` sélectionne le toolkit ; `0.0.4-alpha.2` matérialise `knowledge-store-postgres-lib` avec `tokio-postgres 0.7.18`, `deadpool-postgres 0.14.2` et Refinery `0.9.2`. La façade publique reste limitée à `PostgresStoreConfig`, `PostgresStore` et `PostgresStoreError`. Les types du driver, du pool et du migrator ne remontent ni dans le domaine ni dans les contrats applicatifs, et aucun `knowledge-store-api` symétrique n'est introduit sans seconde implémentation démontrée.
|
||||||
|
|
||||||
|
La configuration est injectée par la composition : la crate reçoit une URL PostgreSQL et une taille maximale de pool. Elle ne lit pas directement les variables d'environnement du processus, n'expose pas l'URL par son API publique et ne journalise jamais l'URL ou les credentials. Une configuration invalide, une acquisition de pool ou une migration impossible produit une erreur récupérable ; aucun fallback fichier/mémoire ne masque l'indisponibilité PostgreSQL. La première implémentation utilise `tokio_postgres::NoTls`; un backend TLS ne sera ajouté que lorsqu'une cible de déploiement le justifiera.
|
||||||
|
|
||||||
|
Le premier schéma utile est volontairement borné :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge_sources
|
||||||
|
source_id TEXT PRIMARY KEY
|
||||||
|
family TEXT NOT NULL CHECK (pgr/howto/snippet/project_local)
|
||||||
|
git_reference TEXT NOT NULL
|
||||||
|
|
||||||
|
knowledge_source_sync_state
|
||||||
|
source_id TEXT PRIMARY KEY REFERENCES knowledge_sources(source_id) ON DELETE CASCADE
|
||||||
|
last_synchronized_revision TEXT NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
`git_reference` est la référence Git logique à suivre ; la localisation physique locale/remote et les credentials restent hors de cette table et sous les frontières de configuration appropriées. La dernière révision synchronisée est un état applicatif, pas une nouvelle autorité de contenu : elle peut être reconstruite/réconciliée à partir de Git et une valeur obsolète provoque au pire une resynchronisation.
|
||||||
|
|
||||||
|
La migration `V1__initial_sources.sql` est embarquée au build et appliquée uniquement via `PostgresStore::apply_migrations`; Refinery conserve son historique dans PostgreSQL. Le test d'intégration réel reste `#[ignore]` pour ne pas transformer tout `cargo test` en dépendance externe implicite. La gate PostgreSQL vérifie explicitement `KNOWLEDGE_POSTGRES_TEST_URL`, crée un schéma unique, borne le `search_path` de la connexion du store, exécute la migration, vérifie `knowledge_sources`, `knowledge_source_sync_state` et `refinery_schema_history`, puis supprime le schéma. L'absence du service requis rend la gate `BLOCKED`.
|
||||||
|
|
||||||
|
PostgreSQL MAY contenir ultérieurement des documents/index/métadonnées dérivés et assurer une première capacité de recherche. Le corps publié et ses métadonnées éditoriales autoritaires restent dans Git. `0.0.4` ne matérialise pas encore le parsing/projection final prévu par l'horizon `0.0.5`.
|
||||||
|
|
||||||
Les enregistrements dérivés doivent porter au minimum l'identité de famille/source et la révision Git afin de savoir exactement quel contenu ils représentent.
|
Les enregistrements dérivés doivent porter au minimum l'identité de famille/source et la révision Git afin de savoir exactement quel contenu ils représentent.
|
||||||
|
|
||||||
@@ -348,7 +391,7 @@ La fondation matérialisée est cohérente avec les objectifs `0.0.1` :
|
|||||||
- frontend de référence indépendant et rendu runtime Maud effectivement relié au manifest Vite ;
|
- frontend de référence indépendant et rendu runtime Maud effectivement relié au manifest Vite ;
|
||||||
- aucune persistance PostgreSQL, auth complète, UI admin complète, forge spécifique ou format final HOWTO/snippet introduit par anticipation.
|
- aucune persistance PostgreSQL, auth complète, UI admin complète, forge spécifique ou format final HOWTO/snippet introduit par anticipation.
|
||||||
|
|
||||||
`0.0.2-alpha.1` conserve cette réconciliation comme baseline : la release courante ouvre uniquement le chemin Git générique de lecture et la synchronisation réelle ; les autres axes restent différés.
|
`0.0.2-alpha.1` a conservé cette réconciliation comme baseline et la stable `0.0.2` a fermé le chemin Git générique de lecture et la synchronisation réelle.
|
||||||
|
|
||||||
## Réconciliation `0.0.2-beta.1`
|
## Réconciliation `0.0.2-beta.1`
|
||||||
|
|
||||||
@@ -365,6 +408,39 @@ La mission Git/synchronisation est réconciliée avec l'implémentation réellem
|
|||||||
|
|
||||||
Les besoins de rétention de snapshots, submodules/LFS, partage multi-processus de la racine Git et scheduler concret restent différés jusqu'à preuve d'un besoin réel. Aucune prochaine release n'est numérotée par cette réconciliation.
|
Les besoins de rétention de snapshots, submodules/LFS, partage multi-processus de la racine Git et scheduler concret restent différés jusqu'à preuve d'un besoin réel. Aucune prochaine release n'est numérotée par cette réconciliation.
|
||||||
|
|
||||||
|
## Ouverture `0.0.3` — écriture Git contrôlée
|
||||||
|
|
||||||
|
Après publication vérifiée de `v0.0.2`, la prochaine mission explicitement engagée est l'écriture Git générique contrôlée. Elle réutilise le backend process existant, introduit un contrat d'écriture distinct de la lecture et garde la forge optionnelle.
|
||||||
|
|
||||||
|
`0.0.3` borne la mise à jour distante à des branches `refs/heads/...` et exige un état distant attendu. Un push n'est accepté que lorsque cette attente reste vraie ; sinon le résultat est un conflit récupérable. Le backend ne résout pas ce conflit par rebase/merge automatique.
|
||||||
|
|
||||||
|
`alpha.2` matérialise cette frontière dans `knowledge-git-api` sans implémentation de backend : `GitMutationPath` confine lexicalement les mutations à des chemins relatifs normalisés hors `.git`, `GitFileMutation` représente écriture ou suppression, `GitCommitIdentity` impose une identité explicite, `GitBranch` borne la cible à une référence de branche complète valide, et `GitRemoteState` représente soit l'absence attendue soit une révision distante attendue. `GitWriteRequest`, `GitPushRequest` et `GitPushOutcome` portent respectivement la création de candidate, la comparaison-et-échange distante et son résultat. Le trait `GitRepositoryWriter` reste séparé de `GitRepository` afin qu'un consommateur ou fake lecture seule n'acquière aucune obligation d'écriture.
|
||||||
|
|
||||||
|
`alpha.3` implémente la moitié locale de ce contrat dans `knowledge-git-lib`. Le backend crée un worktree détaché temporaire sous `write-worktrees/<source-key>/candidate`, applique uniquement les chemins demandés et refuse tout composant filesystem qui est un symlink. Le staging est contrôlé contre l'ensemble exact des mutations avant commit. L'identité auteur/committer est injectée explicitement ; la configuration Git système et globale est neutralisée, les hooks sont redirigés vers `hooks-disabled`, et le commit désactive également hook/signature à l'appel.
|
||||||
|
|
||||||
|
Après commit, le backend vérifie `HEAD` et `HEAD^1`, exige que le parent soit exactement la `GitRevision` de base puis conserve l'objet candidat sous `refs/knowledge/candidates/<revision>`. Le worktree possédé est retiré après succès ; après une erreur survenue dans un worktree créé par l'opération, le backend tente une suppression forcée de cet état temporaire. Un worktree préexistant n'est jamais supprimé silencieusement et produit une erreur récupérable.
|
||||||
|
|
||||||
|
Le push distant gardé reste réservé à `alpha.4`. `alpha.3` ne met à jour aucune branche remote et n'implémente ni rebase ni merge.
|
||||||
|
|
||||||
|
PostgreSQL, workflow complet de proposition, auth/admin, parsing final et adapter Gitea restent des missions distinctes.
|
||||||
|
|
||||||
|
## Ouverture `0.0.4` — persistance partagée
|
||||||
|
|
||||||
|
La stable `v0.0.3` ayant fermé le chemin Git générique, `0.0.4` matérialise la première persistance partagée nécessaire aux prochains cas d'usage sans déplacer l'autorité éditoriale.
|
||||||
|
|
||||||
|
La trajectoire retenue est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
alpha.1 étude, ownership, schéma, migrations et tests
|
||||||
|
alpha.2 crate PostgreSQL + pool + première migration
|
||||||
|
alpha.3 persistance source logique + état de synchronisation
|
||||||
|
alpha.4 raccord applicatif de la révision précédente persistée
|
||||||
|
beta réconciliation + horizon
|
||||||
|
rc/rel stabilisation et publication
|
||||||
|
```
|
||||||
|
|
||||||
|
Le raccord applicatif futur doit conserver la primitive `synchronize_repository` provider-neutral : la persistance fournit la révision précédente et reçoit la révision observée, mais ne devient ni un backend Git ni une condition pour interpréter l'identité du commit.
|
||||||
|
|
||||||
## 10. Administration
|
## 10. Administration
|
||||||
|
|
||||||
L'administration Web est une interface vers les mêmes cas d'usage que l'édition locale Git, pas une deuxième source de vérité.
|
L'administration Web est une interface vers les mêmes cas d'usage que l'édition locale Git, pas une deuxième source de vérité.
|
||||||
@@ -446,3 +522,51 @@ snippets source Git courte/contextualisée séparée
|
|||||||
Les dépôts de contenu ne sont pas fusionnés dans PGR et ne sont pas requis comme submodules Git du dépôt applicatif. Leur localisation et leurs credentials sont de la configuration de déploiement.
|
Les dépôts de contenu ne sont pas fusionnés dans PGR et ne sont pas requis comme submodules Git du dépôt applicatif. Leur localisation et leurs credentials sont de la configuration de déploiement.
|
||||||
|
|
||||||
Pendant `0.0.2`, cette configuration de déploiement est adaptée en configuration physique possédée par `knowledge-git-lib`. La racine de mirrors/worktrees reste un état généré reconstructible et ne remplace pas le futur stockage logique PostgreSQL.
|
Pendant `0.0.2`, cette configuration de déploiement est adaptée en configuration physique possédée par `knowledge-git-lib`. La racine de mirrors/worktrees reste un état généré reconstructible et ne remplace pas le futur stockage logique PostgreSQL.
|
||||||
|
|
||||||
|
## Écriture Git gardée acquise en 0.0.3-alpha.4
|
||||||
|
|
||||||
|
Le backend Git complète désormais le cycle provider-neutral d'écriture sans adapter de forge : un candidat retenu localement peut être publié vers une branche `refs/heads/...` uniquement sous garde explicite de l'état distant attendu.
|
||||||
|
|
||||||
|
Le chemin est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
candidat retenu sous refs/knowledge/candidates/<revision>
|
||||||
|
-> observation `git ls-remote --refs origin <branche>`
|
||||||
|
-> comparaison avec GitRemoteState attendu
|
||||||
|
-> git push --force-with-lease=<branche>:<attendu>
|
||||||
|
-> Updated(candidate) ou Conflict { actual }
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour `GitRemoteState::Absent`, la lease utilise une attente vide et autorise donc uniquement la création d'une branche encore absente. Pour `GitRemoteState::Revision(r)`, la mise à jour distante n'est acceptée que si la branche pointe encore exactement sur `r`. L'observation préalable fournit un conflit rapide ; `--force-with-lease` reste la garde autoritaire contre une course entre observation et push. Après un rejet de push, le backend observe à nouveau la branche afin de traduire une divergence concurrente en conflit récupérable.
|
||||||
|
|
||||||
|
Cette capacité ne fait aucun rebase/merge automatique et ne publie aucune pull/merge request. Elle utilise uniquement le remote `origin` possédé par le mirror contrôlé et conserve la forge hors du contrat.
|
||||||
|
|
||||||
|
## Réconciliation `0.0.3-beta.1` et trajectoire produit
|
||||||
|
|
||||||
|
Le chemin Git générique est maintenant complet pour la responsabilité de `0.0.3` : lecture/snapshot par `GitRepository`, création locale de candidat puis push gardé par `GitRepositoryWriter`, sans API de forge. Les deux contrats restent séparés et les conflits distants sont des résultats récupérables plutôt que des rebase/merge implicites.
|
||||||
|
|
||||||
|
La prochaine dépendance structurante commune aux trois produits n'est plus l'écriture Git mais l'état applicatif persistant partagé. `0.0.4` est donc retenue comme prochaine mission pour matérialiser PostgreSQL, son premier schéma et ses migrations, la configuration logique des sources et l'état minimal de synchronisation/indexation. Le choix concret du driver/ORM, du pooling et de la stratégie de test PostgreSQL reste à décider dans l'étude de démarrage `0.0.4` ; il n'est pas anticipé par `0.0.3`.
|
||||||
|
|
||||||
|
La trajectoire des façades Web est désormais explicitement séparée en étapes afin de ne pas confondre design, rendu et logique serveur :
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgreSQL partagé
|
||||||
|
-> modèles/projections PGR + HOWTO + snippet
|
||||||
|
-> templates publics des trois sites dans le template lab
|
||||||
|
-> templates d'administration des trois sites dans le template lab
|
||||||
|
-> routes publiques Actix + adaptation Maud des templates publics
|
||||||
|
-> auth/autorisation + routes admin Actix + adaptation Maud des templates admin
|
||||||
|
-> gestion/proposition/revue/publication appuyée sur Git + PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
Les pages de garde, listes, détails, navigation et états de recherche/filtrage visibles sont donc conçus d'abord comme templates Web de référence. Les dashboards, formulaires et états de proposition/revue suivent le même principe côté administration. Une phase ultérieure adapte le markup validé en composants Maud servis par les trois launchers Actix, plutôt que de développer simultanément deux interfaces indépendantes.
|
||||||
|
|
||||||
|
Cette trajectoire concerne les trois sites :
|
||||||
|
|
||||||
|
```text
|
||||||
|
pgr.sasedev.com consultation et gestion du référentiel PGR
|
||||||
|
howtos.sasedev.com consultation et gestion des HOWTOs
|
||||||
|
snippets.sasedev.com consultation et gestion des snippets
|
||||||
|
```
|
||||||
|
|
||||||
|
Git reste la vérité éditoriale publiée ; PostgreSQL porte l'état transversal et les projections reconstructibles. Un contenu accepté n'est publié qu'après production d'une révision Git autoritaire, puis la projection locale est réconciliée depuis cette révision.
|
||||||
|
|||||||
301
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
Normal file
301
docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
<!-- file: docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md -->
|
||||||
|
<!-- version: 12 -->
|
||||||
|
|
||||||
|
# Plan 0.0.3 — écriture Git contrôlée et push gardé
|
||||||
|
|
||||||
|
## Statut et sources
|
||||||
|
|
||||||
|
Plan historique de la stable `0.0.3`, ouverte depuis la stable Knowledge `v0.0.2` publiée et vérifiée.
|
||||||
|
|
||||||
|
Sources autoritaires de reprise :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge tag stable v0.0.2
|
||||||
|
Knowledge commit stable 183d354a67bec6d2d512c4898f5189759a6120b0
|
||||||
|
Knowledge archive SHA-256 0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce
|
||||||
|
Knowledge publish report sasedev-knowledge-0.0.2-publish.001-report.md = PASS
|
||||||
|
Knowledge start prompt prompts/003-V0_0_3_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
PGR `v0.1.3` reste une provenance historique de l'adoption fermée pendant `0.0.2`. Aucune version PGR plus récente n'est une source de cette release et aucune réévaluation PGR n'est engagée.
|
||||||
|
|
||||||
|
`0.0.3-alpha.1` et `alpha.1.fix.1` sont conservées `PARTIAL` avec leurs preuves réelles. `alpha.1.fix.2` a complété la gate documentaire et technique en `PASS`. `alpha.2` a matérialisé le contrat d'écriture provider-neutral en `PASS`. `alpha.3` reste historisée `FAIL` pour son défaut de test `E0369`; `alpha.3.fix.1` a corrigé uniquement cette assertion en `PASS`. `alpha.4` a fermé le scope fonctionnel alpha en `PASS` avec push gardé, création de branche sous attente d'absence et conflit concurrent sans écrasement. `beta.1` a réconcilié l'état acquis, préparé le changelog candidat, rejoué la non-régression Rust/Web et effectué la revue d'horizon produit en `PASS`. `rc.1` a rejoué toutes les preuves produit/runtime mais reste historisée `FAIL` pour une assertion lexicale de statut ; `rc.1.fix.1` a corrigé uniquement cette preuve en `PASS`. `rel.1` est la tranche courante de promotion mécanique pré-tag vers la stable `0.0.3`.
|
||||||
|
|
||||||
|
## Mission
|
||||||
|
|
||||||
|
`0.0.3` doit étendre le backend Git générique de Knowledge avec une capacité d'écriture contrôlée, indépendante d'une forge, capable de produire une révision candidate puis de mettre à jour une branche distante uniquement si son état précédent correspond encore à l'attente de l'appelant.
|
||||||
|
|
||||||
|
Le chemin cible est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
source Git configurée
|
||||||
|
-> base commit explicite
|
||||||
|
-> worktree d'écriture isolé
|
||||||
|
-> mutations de fichiers confinées
|
||||||
|
-> commit avec identité explicite
|
||||||
|
-> révision candidate
|
||||||
|
-> push gardé vers refs/heads/...
|
||||||
|
-> succès ou conflit explicite
|
||||||
|
```
|
||||||
|
|
||||||
|
La release fournit une primitive Git réutilisable aux futurs workflows de proposition/publication ; elle n'implémente pas encore ces workflows produit.
|
||||||
|
|
||||||
|
## État acquis et questions ouvertes
|
||||||
|
|
||||||
|
Acquis par `0.0.2` :
|
||||||
|
|
||||||
|
- Git reste la source autoritaire du contenu publié ;
|
||||||
|
- `knowledge-git-api::GitRepository` possède le contrat provider-neutral de lecture ;
|
||||||
|
- `knowledge-git-lib` possède la configuration physique, les mirrors, refresh, résolution et snapshots ;
|
||||||
|
- les opérations Git sont lancées sans shell, avec prompts interactifs désactivés et erreurs publiques redacted ;
|
||||||
|
- les snapshots de lecture sont adressés par révision et vérifiés avant réutilisation ;
|
||||||
|
- `knowledge-app-lib` possède uniquement le cas d'usage de synchronisation `Changed` / `Unchanged` ;
|
||||||
|
- Gitea, PostgreSQL, auth/admin et workflow complet de proposition restent différés.
|
||||||
|
|
||||||
|
Décisions prises par l'étude `006` :
|
||||||
|
|
||||||
|
- conserver le processus `git` contrôlé ;
|
||||||
|
- utiliser un worktree d'écriture isolé plutôt qu'une nouvelle implémentation Git ou un moteur plumbing complet ;
|
||||||
|
- introduire un contrat d'écriture distinct de `GitRepository` afin de préserver les consommateurs lecture seule ;
|
||||||
|
- limiter `0.0.3` aux branches `refs/heads/...` ;
|
||||||
|
- utiliser une attente explicite sur l'état distant pour empêcher l'écrasement concurrent ;
|
||||||
|
- traiter une divergence comme conflit récupérable, sans rebase/merge automatique ;
|
||||||
|
- ne créer aucune nouvelle crate tant qu'un owner distinct n'est pas démontré.
|
||||||
|
|
||||||
|
Décisions d'implémentation désormais résolues dans les alphas :
|
||||||
|
|
||||||
|
- mutations publiques provider-neutral matérialisées dans `knowledge-git-api` ;
|
||||||
|
- worktree d'écriture backend déterministe sous `write-worktrees/<source-key>/candidate` ;
|
||||||
|
- candidats retenus sous `refs/knowledge/candidates/<revision>` ;
|
||||||
|
- conflits distants représentés par `GitPushOutcome::Conflict`, séparés des erreurs process ;
|
||||||
|
- garde distante matérialisée par observation `ls-remote` puis `push --force-with-lease` exact ;
|
||||||
|
- `remote.origin.mirror=true` neutralisé uniquement sur le push ciblé afin de ne jamais publier toutes les refs internes du mirror.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
La release couvre :
|
||||||
|
|
||||||
|
- contrat passif d'écriture Git dans `knowledge-git-api`, séparé du contrat de lecture ;
|
||||||
|
- création/modification/suppression de fichiers par chemins relatifs validés ;
|
||||||
|
- worktree d'écriture sous la racine d'état contrôlée, séparé des snapshots de lecture ;
|
||||||
|
- commit à partir d'une base `GitRevision` explicite ;
|
||||||
|
- identité auteur/committer et message fournis explicitement ;
|
||||||
|
- neutralisation des hooks et de la configuration Git ambiante pertinente ;
|
||||||
|
- résultat de révision candidate explicite ;
|
||||||
|
- push vers une branche générique avec garde sur l'état distant attendu ;
|
||||||
|
- conflit explicite et non destructif en cas de divergence ;
|
||||||
|
- tests unitaires et d'intégration locaux couvrant sécurité de chemin, commit, push et concurrence ;
|
||||||
|
- documentation durable des invariants réellement matérialisés.
|
||||||
|
|
||||||
|
## Hors scope
|
||||||
|
|
||||||
|
`0.0.3` n'introduit pas :
|
||||||
|
|
||||||
|
- API Gitea/GitHub/GitLab, webhook, pull/merge request ou release de forge ;
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- workflow complet de proposition/revue, commentaires ou audit applicatif ;
|
||||||
|
- PostgreSQL, migrations ou persistance de dernière révision ;
|
||||||
|
- auth/admin ou modèle rôles/capabilities ;
|
||||||
|
- route HTTP d'écriture ou UI d'administration ;
|
||||||
|
- modification du rendu public ou du package Web ;
|
||||||
|
- signature de commits ;
|
||||||
|
- Git LFS/submodules ;
|
||||||
|
- partage concurrent de la racine d'état Git entre plusieurs processus ;
|
||||||
|
- formats finaux HOWTO/snippet ;
|
||||||
|
- scheduler/job de publication.
|
||||||
|
|
||||||
|
## Stratégie de validation
|
||||||
|
|
||||||
|
Les alphas fonctionnelles privilégient les preuves Git locales et ciblées :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
-> audits Rust structurels applicables
|
||||||
|
-> cargo metadata / cargo check
|
||||||
|
-> clippy strict
|
||||||
|
-> tests ciblés knowledge-git-api / knowledge-git-lib
|
||||||
|
-> tests d'intégration avec repositories temporaires locaux/bare
|
||||||
|
-> tests workspace lorsque la frontière publique ou le workspace change
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests d'écriture ne nécessitent ni Internet ni forge. Ils créent leur propre repository source/remote temporaire et fournissent explicitement l'identité de commit.
|
||||||
|
|
||||||
|
`Web/` reste hors scope alpha ; npm/Vite ne sont pas rejoués par défaut sans diff Web. La phase beta/RC pourra imposer la non-régression runtime globale conformément à l'owner de validation.
|
||||||
|
|
||||||
|
La gate de `alpha.1` reste une gate de cadrage : elle valide baseline, documentation, version, audits et non-régression du workspace existant, sans prétendre tester une implémentation d'écriture encore absente.
|
||||||
|
|
||||||
|
## Trajectoire prévisionnelle
|
||||||
|
|
||||||
|
### alpha.1 — baseline, étude et cadrage
|
||||||
|
|
||||||
|
- vérifier la stable `v0.0.2` et son rapport post-tag ;
|
||||||
|
- confirmer l'autonomie normative Knowledge sans nouvelle consultation PGR ;
|
||||||
|
- réconcilier les `DEFER` avec la prochaine mission ;
|
||||||
|
- produire l'étude `006` ;
|
||||||
|
- engager `0.0.3` et établir un premier horizon au-delà d'elle ;
|
||||||
|
- mettre à jour les owners/architecture vivants nécessaires ;
|
||||||
|
- établir le présent plan ;
|
||||||
|
- créer exceptionnellement `prompts/003-V0_0_3_START_PROMPT.md` comme rattrapage, puisque `0.0.2` avait été fermée sans prochaine mission décidée ;
|
||||||
|
- ne modifier aucun code sous `crates/` ni `Web/`.
|
||||||
|
|
||||||
|
### alpha.1.fix.1 — correction du cycle documentaire
|
||||||
|
|
||||||
|
- conserver `alpha.1` comme `PARTIAL` sans réécrire ses preuves ;
|
||||||
|
- retirer du changelog l'entrée candidate écrite prématurément ;
|
||||||
|
- faire de la roadmap une vue de la release en cours plus un horizon prévisionnel borné ;
|
||||||
|
- codifier que le changelog est rédigé vers la stabilisation à partir des changements réellement acquis ;
|
||||||
|
- codifier que le prompt de `N+1` est normalement préparé pendant les dernières phases de `N`, après revue d'horizon ;
|
||||||
|
- garder `prompts/003-V0_0_3_START_PROMPT.md` comme rattrapage historique valide ;
|
||||||
|
- ne modifier aucun code sous `crates/` ni `Web/`.
|
||||||
|
|
||||||
|
### alpha.1.fix.2 — complétion de la preuve opérateur
|
||||||
|
|
||||||
|
- conserver `alpha.1.fix.1` comme `PARTIAL` sans réécrire les preuves observées ;
|
||||||
|
- ne modifier aucune décision fonctionnelle du cadrage ;
|
||||||
|
- rejouer la gate utile et terminer explicitement les assertions documentaires, l'hygiène texte, l'invariance `crates/` / `Web/`, la politique `Cargo.lock` et le staging ;
|
||||||
|
- ne modifier aucun code sous `crates/` ni `Web/` ;
|
||||||
|
- autoriser `alpha.2` uniquement après un `PASS` complet.
|
||||||
|
|
||||||
|
### alpha.2 — contrat d'écriture provider-neutral
|
||||||
|
|
||||||
|
- étendre `knowledge-git-api` avec un contrat distinct du `GitRepository` de lecture ;
|
||||||
|
- matérialiser uniquement les types nécessaires aux mutations, identité de commit, branche cible, état attendu et résultat ;
|
||||||
|
- valider strictement les chemins relatifs et les branches `refs/heads/...` ;
|
||||||
|
- préserver les implémentations/fakes lecture seule sans leur imposer des méthodes d'écriture ;
|
||||||
|
- ne pas effectuer encore de push distant réel si le découpage de la tranche devient disproportionné.
|
||||||
|
|
||||||
|
### alpha.3 — worktree d'écriture et commit candidat
|
||||||
|
|
||||||
|
- partir de `alpha.2 = PASS` historisé et committé ;
|
||||||
|
- matérialiser dans `knowledge-git-lib` un worktree d'écriture isolé dérivé d'une base résolue ;
|
||||||
|
- appliquer create/update/delete sous confinement et refuser les traversées par symlink ;
|
||||||
|
- neutraliser la configuration Git système/globale pertinente et les hooks ;
|
||||||
|
- créer un commit avec identité auteur/committer explicite ;
|
||||||
|
- vérifier que le candidat a exactement la base demandée comme parent ;
|
||||||
|
- retenir le candidat sous une référence backend interne sans mettre à jour de branche distante ;
|
||||||
|
- nettoyer l'état temporaire de manière déterministe sans supprimer silencieusement un worktree préexistant.
|
||||||
|
|
||||||
|
### alpha.3.fix.1 — correction de l'assertion du test candidat
|
||||||
|
|
||||||
|
- conserver `alpha.3 = FAIL` avec la preuve exacte de compilation `E0369` ;
|
||||||
|
- ne modifier aucun code produit de `knowledge-git-lib` ni aucun contrat public ;
|
||||||
|
- remplacer uniquement l'assertion invalide sur `Result<String, std::io::Error>` par une vérification du cas `Ok("first\n")` qui n'exige pas `PartialEq` sur `std::io::Error` ;
|
||||||
|
- rejouer Clippy, le test `git_write_candidate` et les tests workspace ;
|
||||||
|
- autoriser `alpha.4` uniquement après `PASS` complet du fix.
|
||||||
|
|
||||||
|
### alpha.4 — push gardé et concurrence
|
||||||
|
|
||||||
|
- partir de `alpha.3.fix.1 = PASS` historisé et committé ;
|
||||||
|
- observer l'état de la branche distante via le remote générique `origin` ;
|
||||||
|
- exiger que la révision candidate soit encore retenue par le backend ;
|
||||||
|
- pousser la révision candidate uniquement si la cible correspond à l'attente explicite ;
|
||||||
|
- utiliser `--force-with-lease` comme garde compare-and-exchange autoritaire ;
|
||||||
|
- gérer création de branche lorsque l'absence est attendue ;
|
||||||
|
- réobserver après rejet afin de produire un conflit récupérable en cas de divergence concurrente ;
|
||||||
|
- implémenter `GitRepositoryWriter` sur `GitBackend` maintenant que create + push sont disponibles ;
|
||||||
|
- prouver l'absence d'écrasement par tests d'intégration avec remote bare local ;
|
||||||
|
- fermer le scope fonctionnel alpha sans ajouter workflow de proposition ou forge.
|
||||||
|
|
||||||
|
### beta.1 — réconciliation et revue d'horizon
|
||||||
|
|
||||||
|
- partir de `alpha.4 = PASS` historisé et committé ;
|
||||||
|
- réconcilier contrats, implémentation et documentation durable sans modifier `crates/` ni `Web/` ;
|
||||||
|
- préparer `CHANGELOG.md` en état `0.0.3 (candidate)` à partir des capacités réellement acquises ;
|
||||||
|
- vérifier les `DEFER` encore valides et reclasser la prochaine dépendance structurante ;
|
||||||
|
- retenir `0.0.4` comme fondation PostgreSQL partagée, sans préchoisir driver/ORM avant son étude de démarrage ;
|
||||||
|
- étendre l'horizon jusqu'aux phases de modèles/projections, templates publics, templates admin, intégration Actix/Maud et workflow de gestion/proposition/publication des trois sites ;
|
||||||
|
- produire l'étude durable `007-V0_0_3_GIT_WRITE_RECONCILIATION.md` ;
|
||||||
|
- préparer `prompts/004-V0_0_4_START_PROMPT.md` pendant cette stabilisation, sans inventer commit/tag/SHA postérieurs de `v0.0.3` ;
|
||||||
|
- rejouer la non-régression Rust et Web utile à la stabilisation ;
|
||||||
|
- n'introduire aucune fonctionnalité PostgreSQL, Web, auth, forge ou proposition dans `0.0.3`.
|
||||||
|
|
||||||
|
### rc.1 — candidate stable
|
||||||
|
|
||||||
|
- geler le scope ;
|
||||||
|
- rejouer la gate complète applicable à la release ;
|
||||||
|
- vérifier reproductibilité des tests Git d'écriture locaux, intégrité des deltas/histories, Cargo/Clippy/tests et non-régression Web/runtime prévue par la phase ;
|
||||||
|
- n'introduire aucune nouvelle fonctionnalité.
|
||||||
|
|
||||||
|
### rc.1.fix.1 — correction de la vérification des statuts historiques
|
||||||
|
|
||||||
|
- conserver `rc.1 = FAIL` avec la preuve exacte de l'assertion lexicale trop stricte ;
|
||||||
|
- ne réécrire aucun history antérieur, en particulier `alpha.1` dont le statut historique est écrit `` `PARTIAL` `` ;
|
||||||
|
- normaliser uniquement la lecture du statut lors des gates afin d'accepter la présentation Markdown existante ;
|
||||||
|
- ne modifier aucun fichier sous `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/` ou `prompts/` ;
|
||||||
|
- rejouer les contrôles documentaires, Cargo/Clippy/tests et la cohérence RC nécessaires à ce fix ;
|
||||||
|
- autoriser `rel.1` uniquement après `PASS` complet du fix.
|
||||||
|
|
||||||
|
### rel.1 — promotion stable pré-tag
|
||||||
|
|
||||||
|
- promouvoir mécaniquement `VERSION` et le workspace vers `0.0.3` ;
|
||||||
|
- finaliser le changelog à partir du scope réellement acquis ;
|
||||||
|
- fermer roadmap/plan sans nouveau scope et vérifier que le handoff de la prochaine release décidée est déjà présent ;
|
||||||
|
- rejouer la gate stable prévue ;
|
||||||
|
- produire le history observé avant commit produit/tag ;
|
||||||
|
- ne fabriquer aucun rapport post-tag avant publication réelle.
|
||||||
|
|
||||||
|
### rel.1.fix.1 — complétion de la preuve de clôture stable
|
||||||
|
|
||||||
|
- conserver `rel.1 = PARTIAL` avec toutes les preuves produit/runtime réellement acquises ;
|
||||||
|
- ne modifier aucun code produit, frontend, changelog, roadmap, architecture, étude, règle ou prompt ;
|
||||||
|
- compléter explicitement l'audit de composition du workspace à partir du JSON `cargo metadata` déjà prévu par la gate ;
|
||||||
|
- compléter explicitement l'audit d'hygiène texte sur les documents gouvernés ;
|
||||||
|
- réutiliser les preuves Cargo/Clippy/tests, Web et smokes HTTP `PASS` de `rel.1` puisqu'aucun fichier concerné n'est modifié ;
|
||||||
|
- autoriser le premier commit stable `0.0.3` uniquement après `PASS` complet de ce fix.
|
||||||
|
|
||||||
|
## Déclencheurs de REPLAN
|
||||||
|
|
||||||
|
REPLAN avant de poursuivre si :
|
||||||
|
|
||||||
|
- le contrat d'écriture exige de casser le contrat de lecture existant au lieu de rester séparé ;
|
||||||
|
- le push gardé ne peut pas garantir l'absence d'écrasement avec une attente explicite ;
|
||||||
|
- l'implémentation nécessite une forge spécifique ;
|
||||||
|
- un besoin de rebase/merge automatique devient nécessaire pour fermer la mission ;
|
||||||
|
- l'écriture révèle une responsabilité de persistance PostgreSQL indispensable à la correction du backend lui-même ;
|
||||||
|
- le cycle de vie des worktrees exige un owner/processus distinct ;
|
||||||
|
- le partage multi-processus de la racine d'état devient nécessaire ;
|
||||||
|
- la stratégie process Git actuelle devient inadéquate pour une raison mesurée ;
|
||||||
|
- une tranche atteint `.fix.3`, conformément à la gouvernance locale.
|
||||||
|
|
||||||
|
La conclusion du REPLAN doit utiliser `CONTINUE`, `SPLIT`, `CONTINUE-NEXT-SESSION` ou `ABANDON`.
|
||||||
|
|
||||||
|
## Clôture et transition
|
||||||
|
|
||||||
|
La release est fermable lorsque :
|
||||||
|
|
||||||
|
- l'écriture produit une révision candidate à partir d'une base explicite ;
|
||||||
|
- les mutations restent confinées au worktree contrôlé ;
|
||||||
|
- le commit n'utilise ni hooks ni identité Git ambiante ;
|
||||||
|
- la mise à jour distante est gardée contre une révision attendue ou une absence attendue ;
|
||||||
|
- une divergence produit un conflit sans écrasement ;
|
||||||
|
- les tests locaux réels couvrent les chemins critiques ;
|
||||||
|
- les règles/architecture décrivent l'état effectivement obtenu ;
|
||||||
|
- beta et RC ont fermé les non-régressions prévues.
|
||||||
|
|
||||||
|
Le `PARTIAL` de `alpha.1` a autorisé `0.0.3-alpha.1.fix.1`. Le premier fix est resté `PARTIAL`, puis `alpha.1.fix.2` a fermé la preuve en `PASS`. `alpha.2` a matérialisé le contrat d'écriture provider-neutral en `PASS`. `alpha.3` est conservée `FAIL` à cause de l'assertion de test `E0369`; `alpha.3.fix.1` a corrigé uniquement le test en `PASS`. `alpha.4` a ensuite fermé le scope fonctionnel alpha en `PASS`. `beta.1` a stabilisé et réconcilié cet état en `PASS`, sans nouveau code produit. `rc.1` a confirmé toutes les preuves produit/runtime mais reste `FAIL` à cause d'une assertion lexicale trop stricte sur le statut historique `alpha.1`. `rc.1.fix.1` a corrigé uniquement cette preuve en `PASS`. `rel.1` a acquis toutes les preuves produit/runtime de la promotion stable mais reste `PARTIAL` parce que deux contrôles annoncés — composition exacte du workspace et hygiène texte — ne figurent pas dans le transcript. `rel.1.fix.1` ferme uniquement ces deux preuves avant le premier commit stable réellement `PASS`.
|
||||||
|
|
||||||
|
## Revue d'horizon
|
||||||
|
|
||||||
|
La revue `0.0.3-beta.1` remplace l'horizon initial trop centré sur les seules briques backend. La prochaine mission engagée par la promotion stable est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.4 fondation PostgreSQL partagée et migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
L'horizon produit devient ensuite :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.5 modèles de contenu et projections PGR/HOWTO/snippet
|
||||||
|
0.0.6 templates publics des trois sites, pages de garde incluses
|
||||||
|
0.0.7 templates d'administration des trois sites
|
||||||
|
0.0.8 routes publiques Actix Web + adaptation Maud
|
||||||
|
0.0.9 auth/autorisation + routes admin Actix Web + adaptation Maud
|
||||||
|
0.0.10 gestion/proposition/revue/publication Git + PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
Ces versions après `0.0.4` restent des horizons révisables. Elles rendent cependant explicite l'objectif produit : trois sites distincts PGR, HOWTOs et snippets, chacun avec lecture publique et gestion, partageant Git comme vérité éditoriale, PostgreSQL comme état/projection applicative et un pipeline de templates Web validés avant adaptation runtime en Maud.
|
||||||
|
|
||||||
|
Recherche avancée, adapters de forge, webhooks forge-only, scheduler, administration transverse, opérations multi-domaines, Git LFS/submodules et partage multi-processus restent non numérotés tant qu'un besoin réel ne les engage pas.
|
||||||
|
|
||||||
|
`prompts/004-V0_0_4_START_PROMPT.md` a été préparé pendant `beta.1` conformément au cycle normal de handoff. La stable `0.0.3` le conserve : au prochain démarrage il exigera l'archive taggée réelle `v0.0.3` et son rapport post-tag, sans avoir inventé à l'avance le commit stable ou le SHA-256.
|
||||||
231
docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
Normal file
231
docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<!-- file: docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md -->
|
||||||
|
<!-- version: 3 -->
|
||||||
|
|
||||||
|
# Plan 0.0.4 — fondation PostgreSQL partagée
|
||||||
|
|
||||||
|
## Statut et sources
|
||||||
|
|
||||||
|
Release ouverte depuis la stable autoritaire :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge tag v0.0.3
|
||||||
|
Knowledge commit b3823972449150b72905930b806d4d15471cddce
|
||||||
|
Knowledge SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d
|
||||||
|
publish report sasedev-knowledge-0.0.3-publish.001-report.md = PASS
|
||||||
|
start prompt prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Le plan est actif à partir de `0.0.4-alpha.1`. PGR `v0.1.3` reste une provenance historique et aucune adoption PGR nouvelle n'est engagée.
|
||||||
|
|
||||||
|
## Mission
|
||||||
|
|
||||||
|
Matérialiser la première fondation PostgreSQL partagée de Knowledge afin de conserver un état transversal minimal pour les sources et la synchronisation, avec migrations reproductibles et tests PostgreSQL réels, sans déplacer la vérité éditoriale hors de Git.
|
||||||
|
|
||||||
|
À la clôture de `0.0.4`, le workspace doit posséder un owner PostgreSQL concret et partagé, un premier schéma versionné, un pool/configuration injectables et un chemin persistant pour la dernière révision synchronisée utilisable par l'orchestration applicative.
|
||||||
|
|
||||||
|
## État acquis et questions ouvertes
|
||||||
|
|
||||||
|
Acquis depuis `0.0.3` :
|
||||||
|
|
||||||
|
- Git générique de lecture et snapshot ;
|
||||||
|
- écriture Git contrôlée et push gardé ;
|
||||||
|
- `knowledge-app-lib` pour la synchronisation provider-neutral ;
|
||||||
|
- trois launchers Web et composition Maud/Vite ;
|
||||||
|
- base PostgreSQL logique partagée déjà retenue par l'architecture ;
|
||||||
|
- horizon produit `0.0.5` à `0.0.10` révisable.
|
||||||
|
|
||||||
|
Décisions de `alpha.1` :
|
||||||
|
|
||||||
|
- `tokio-postgres` sélectionné comme driver, `deadpool-postgres` comme pool et Refinery comme migrator, sans ORM ;
|
||||||
|
- `knowledge-store-postgres-lib` retenu comme owner ;
|
||||||
|
- migrations SQL versionnées possédées par la crate ;
|
||||||
|
- premier schéma limité aux sources logiques et à la dernière révision synchronisée ;
|
||||||
|
- test PostgreSQL via URL explicitement fournie et schéma éphémère isolé ;
|
||||||
|
- aucune lecture implicite de secrets par la crate et aucun fallback de stockage.
|
||||||
|
|
||||||
|
Décisions matérialisées par `alpha.2` :
|
||||||
|
|
||||||
|
- façade publique minimale `PostgresStoreConfig` / `PostgresStore` / `PostgresStoreError` ;
|
||||||
|
- pool borné par `max_pool_size` sans exposition de types Deadpool ;
|
||||||
|
- versions revalidées `tokio-postgres 0.7.18`, `deadpool-postgres 0.14.2`, Refinery `0.9.2` et Tokio test `1.53.1`, avec features minimales `runtime`, `rt_tokio_1` et `tokio-postgres` respectivement ;
|
||||||
|
- migration `V1__initial_sources.sql` embarquée et appliquée explicitement ;
|
||||||
|
- premier backend sans TLS via `NoTls`, sans dépendance TLS prématurée ;
|
||||||
|
- test réel de migration isolé par schéma et déclenché explicitement par la gate.
|
||||||
|
|
||||||
|
Questions laissées aux tranches suivantes :
|
||||||
|
|
||||||
|
- besoin réel d'un contrat passif de persistance dans `knowledge-app-lib` pour le raccord `alpha.4` ;
|
||||||
|
- backend TLS à sélectionner uniquement si le déploiement visé l'exige ;
|
||||||
|
- paramètres supplémentaires de pool uniquement sur besoin runtime observé.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
`0.0.4` couvre :
|
||||||
|
|
||||||
|
- une crate PostgreSQL partagée réelle ;
|
||||||
|
- `tokio-postgres` PostgreSQL/Tokio avec pool `deadpool-postgres` ;
|
||||||
|
- première migration reproductible ;
|
||||||
|
- persistance de `SourceId`, `ContentFamily`, référence Git logique et dernière révision synchronisée ;
|
||||||
|
- erreurs récupérables sans fuite de credentials ;
|
||||||
|
- tests unitaires ciblés et intégration PostgreSQL réelle ;
|
||||||
|
- raccord applicatif minimal permettant d'utiliser l'état persistant sans modifier le contrat Git ;
|
||||||
|
- documentation durable, beta, RC et fermeture stable.
|
||||||
|
|
||||||
|
## Hors scope
|
||||||
|
|
||||||
|
Restent hors `0.0.4` :
|
||||||
|
|
||||||
|
- corps/métadonnées éditoriales publiées en PostgreSQL comme vérité ;
|
||||||
|
- modèles de contenu finaux et parsing complet ;
|
||||||
|
- projections finales/indexation détaillée ;
|
||||||
|
- recherche avancée ;
|
||||||
|
- Gitea/forge et webhooks spécifiques ;
|
||||||
|
- scheduler/jobs ;
|
||||||
|
- auth/session/rôles/capabilities ;
|
||||||
|
- propositions/revues/publication produit ;
|
||||||
|
- templates publics/admin et routes finales ;
|
||||||
|
- quatrième application admin ;
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- partage multi-processus de la racine Git ;
|
||||||
|
- LFS/submodules et rétention avancée.
|
||||||
|
|
||||||
|
## Stratégie de validation
|
||||||
|
|
||||||
|
La validation reste proportionnée à chaque tranche.
|
||||||
|
|
||||||
|
Les invariants documentaires et Rust continuent d'utiliser :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Markdown + hygiène texte
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
audits Rust locaux
|
||||||
|
cargo metadata / cargo check
|
||||||
|
Clippy strict
|
||||||
|
tests ciblés puis workspace lorsque le contrat devient transverse
|
||||||
|
git diff --check / git diff --cached --check
|
||||||
|
```
|
||||||
|
|
||||||
|
À partir de la première migration, une gate PostgreSQL réelle devient obligatoire. Elle utilise `KNOWLEDGE_POSTGRES_TEST_URL`, isole chaque exécution dans un schéma temporaire et nettoie cet état après test. Si la tranche requiert cette preuve et qu'aucun PostgreSQL n'est disponible, le statut est `BLOCKED`.
|
||||||
|
|
||||||
|
Les gates Web/npm ne sont pas rejouées pour les alphas sans diff `Web/` ou contrat Web. Les phases de stabilisation peuvent imposer une non-régression runtime globale.
|
||||||
|
|
||||||
|
## Trajectoire prévisionnelle
|
||||||
|
|
||||||
|
### alpha.1 — baseline, étude et cadrage
|
||||||
|
|
||||||
|
- vérifier intégralement `v0.0.3` et `publish.001` ;
|
||||||
|
- relire owners, architecture, deltas/histories, `DEFER` et horizon ;
|
||||||
|
- confirmer l'autonomie normative Knowledge ;
|
||||||
|
- comparer les options PostgreSQL actuelles ;
|
||||||
|
- décider le driver `tokio-postgres`, le pool `deadpool-postgres`, Refinery, l'owner, le schéma, la configuration et la stratégie de test ;
|
||||||
|
- créer l'étude `008`, le présent plan et le delta ;
|
||||||
|
- ne modifier aucun code produit, dépendance ou migration ;
|
||||||
|
- autoriser `alpha.2` seulement après `PASS` opérateur.
|
||||||
|
|
||||||
|
### alpha.2 — owner PostgreSQL, pool et première migration
|
||||||
|
|
||||||
|
- créer `knowledge-store-postgres-lib` avec façade crate-root conforme ;
|
||||||
|
- ajouter uniquement les dépendances/features nécessaires de `tokio-postgres`, `deadpool-postgres`, Refinery et Tokio ;
|
||||||
|
- matérialiser la configuration explicite de connexion/pool et les erreurs publiques sans fuite de secret ;
|
||||||
|
- ajouter `migrations/` sous la crate et la migration initiale `knowledge_sources` + `knowledge_source_sync_state` ;
|
||||||
|
- exposer une opération explicite d'application des migrations ;
|
||||||
|
- prouver la migration sur PostgreSQL réel avec schéma de test isolé ;
|
||||||
|
- ne raccorder encore aucun launcher si aucun use case runtime ne l'exige.
|
||||||
|
|
||||||
|
### alpha.2.fix.1 — documentation de la crate de test PostgreSQL
|
||||||
|
|
||||||
|
- conserver `alpha.2` historisée `FAIL` à cause de Clippy strict ;
|
||||||
|
- ajouter uniquement la documentation de crate manquante dans `tests/postgres_migration.rs` ;
|
||||||
|
- ne modifier ni le code produit, ni le schéma, ni les dépendances, ni la façade publique ;
|
||||||
|
- rejouer Clippy strict, les tests Rust et la preuve PostgreSQL réelle ;
|
||||||
|
- autoriser `alpha.3` uniquement après `PASS` complet du fix.
|
||||||
|
|
||||||
|
### alpha.3 — persistance de source et état de synchronisation
|
||||||
|
|
||||||
|
- matérialiser les opérations minimales de création/mise à jour/lecture de configuration logique ;
|
||||||
|
- convertir `ContentFamily`, `SourceId`, référence logique et `GitRevision` sans faire remonter les types PostgreSQL/Deadpool/Refinery au domaine ;
|
||||||
|
- lire et enregistrer `last_synchronized_revision` ;
|
||||||
|
- tester insertion, mise à jour, absence d'état, contraintes et isolation des sources sur PostgreSQL réel ;
|
||||||
|
- prouver qu'aucun corps de contenu ou credential Git n'est persisté par ce schéma.
|
||||||
|
|
||||||
|
### alpha.4 — raccord applicatif persistant
|
||||||
|
|
||||||
|
- conserver `synchronize_repository` comme primitive provider-neutral ;
|
||||||
|
- ajouter seulement le contrat applicatif nécessaire pour charger/enregistrer la révision précédente sans dépendance au driver/pool/migrator dans `knowledge-app-lib` ;
|
||||||
|
- implémenter ce contrat côté `knowledge-store-postgres-lib` ;
|
||||||
|
- composer un cas d'usage persistant qui charge l'état, synchronise via Git et enregistre la révision uniquement après succès ;
|
||||||
|
- tester Changed/Unchanged, première synchronisation et absence d'écriture après erreur Git/store ;
|
||||||
|
- ne pas ajouter scheduler, projection de contenu ou route Web.
|
||||||
|
|
||||||
|
### beta.1 — réconciliation et revue d'horizon
|
||||||
|
|
||||||
|
- réconcilier le schéma, l'API, le lifecycle et les tests réellement obtenus ;
|
||||||
|
- vérifier que Git reste l'autorité du contenu et que le state PostgreSQL est reconstructible ;
|
||||||
|
- préparer `CHANGELOG.md` en `0.0.4 (candidate)` seulement à ce stade ;
|
||||||
|
- réévaluer `0.0.5` modèles/projections et au moins `0.0.6` / `0.0.7` ;
|
||||||
|
- préparer `prompts/005-V0_0_5_START_PROMPT.md` uniquement si la prochaine mission reste suffisamment décidée ;
|
||||||
|
- rejouer les preuves PostgreSQL et non-régression workspace nécessaires à la stabilisation.
|
||||||
|
|
||||||
|
### rc.1 — candidate stable
|
||||||
|
|
||||||
|
- geler le scope ;
|
||||||
|
- rejouer la gate complète Rust/PostgreSQL applicable ;
|
||||||
|
- vérifier migrations depuis un schéma vide, persistance source/state et raccord applicatif ;
|
||||||
|
- vérifier deltas/histories, hors-scope et hygiène de livraison ;
|
||||||
|
- rejouer la non-régression Web/runtime exigée par la phase de stabilisation ;
|
||||||
|
- n'introduire aucune fonctionnalité nouvelle.
|
||||||
|
|
||||||
|
### rel.1 — promotion stable pré-tag
|
||||||
|
|
||||||
|
- promouvoir mécaniquement `VERSION` et workspace vers `0.0.4` ;
|
||||||
|
- finaliser changelog, README, roadmap et plan depuis le résultat réellement acquis ;
|
||||||
|
- vérifier le handoff `0.0.5` seulement s'il a été autorisé par la revue beta ;
|
||||||
|
- rejouer la gate stable complète ;
|
||||||
|
- produire le history observé avant commit stable/tag ;
|
||||||
|
- ne fabriquer aucun rapport post-tag avant publication réelle.
|
||||||
|
|
||||||
|
## Déclencheurs de REPLAN
|
||||||
|
|
||||||
|
REPLAN avant de poursuivre si :
|
||||||
|
|
||||||
|
- les versions retenues de `tokio-postgres`, `deadpool-postgres` ou Refinery sont incompatibles avec la toolchain stable réellement utilisée ou les lints locaux ;
|
||||||
|
- le premier schéma exige déjà de stocker le corps éditorial publié pour fonctionner ;
|
||||||
|
- une seconde implémentation de store rend une frontière `-api` réellement nécessaire ;
|
||||||
|
- le raccord applicatif crée une dépendance cyclique ou oblige le domaine à dépendre du driver, du pool ou du migrator ;
|
||||||
|
- la stratégie de test par schéma isolé ne peut pas prouver les migrations de manière fiable ;
|
||||||
|
- une connexion PostgreSQL devient une dépendance obligatoire de tous les launchers sans cas d'usage qui la justifie ;
|
||||||
|
- un scheduler, worker ou service réseau supplémentaire devient nécessaire pour fermer la mission ;
|
||||||
|
- une tranche atteint `.fix.3`.
|
||||||
|
|
||||||
|
La conclusion du REPLAN utilise `CONTINUE`, `SPLIT`, `CONTINUE-NEXT-SESSION` ou `ABANDON`.
|
||||||
|
|
||||||
|
## Clôture et transition
|
||||||
|
|
||||||
|
`0.0.4` est fermable lorsque :
|
||||||
|
|
||||||
|
- `knowledge-store-postgres-lib` est un owner réel et testé ;
|
||||||
|
- une base PostgreSQL vide peut être migrée de manière reproductible ;
|
||||||
|
- les sources logiques et leur dernière révision synchronisée sont persistées ;
|
||||||
|
- le pool et les erreurs ne fuient aucun secret ;
|
||||||
|
- l'indisponibilité PostgreSQL n'est jamais masquée par un fallback ;
|
||||||
|
- l'orchestration peut utiliser la révision persistée sans coupler le domaine à SQL ;
|
||||||
|
- Git reste l'autorité de toute donnée éditoriale publiée ;
|
||||||
|
- beta/RC ont fermé les non-régressions et l'horizon.
|
||||||
|
|
||||||
|
À l'ouverture, seule `alpha.1` est matérialisée. Aucun history `0.0.4` n'existe avant la gate opérateur de cette tranche.
|
||||||
|
|
||||||
|
## Revue d'horizon
|
||||||
|
|
||||||
|
L'étude `alpha.1` ne modifie pas l'horizon issu de `0.0.3-beta.1` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.5 modèles de contenu et projections PGR/HOWTO/snippet
|
||||||
|
0.0.6 templates publics des trois sites
|
||||||
|
0.0.7 templates d'administration des trois sites
|
||||||
|
0.0.8 lecture publique Actix Web / Maud
|
||||||
|
0.0.9 administration Actix Web / Maud et autorisation
|
||||||
|
0.0.10 gestion, proposition, revue et publication
|
||||||
|
```
|
||||||
|
|
||||||
|
La revue beta de `0.0.4` devra confirmer ou replanifier au minimum `0.0.5`, `0.0.6` et `0.0.7` à partir de la persistance réellement obtenue.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/210-WORKFLOW.md -->
|
<!-- file: docs/rules/210-WORKFLOW.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Workflow local Knowledge
|
# Workflow local Knowledge
|
||||||
|
|
||||||
@@ -102,7 +102,11 @@ Les commandes ne sont pas différées à une réponse ultérieure. Lorsque le de
|
|||||||
|
|
||||||
## Prompts et changement de session
|
## Prompts et changement de session
|
||||||
|
|
||||||
Un prompt de démarrage versionné décrit la baseline exacte, les sources requises, la mission et les vérifications nécessaires pour ouvrir une release. Il ne complète pas silencieusement des données manquantes par un état supposé d'une conversation ou d'un workspace non vérifié.
|
Un prompt de démarrage versionné décrit la baseline attendue, les sources requises, la mission et les vérifications nécessaires pour ouvrir une release. Il ne complète pas silencieusement des données manquantes par un état supposé d'une conversation ou d'un workspace non vérifié.
|
||||||
|
|
||||||
|
Le cycle normal prépare le prompt de la release `N+1` pendant les dernières phases de `N`, après la revue d'horizon qui a suffisamment défini la prochaine mission et avant la clôture stable de `N`. Comme le commit/tag/rapport post-tag définitifs de `N` n'existent pas encore à ce moment, le prompt versionné ne doit pas inventer leur SHA ou leur résultat : il exige que l'introduction de la session suivante fournisse l'archive taggée et le rapport post-tag réels et qu'ils soient vérifiés avant développement.
|
||||||
|
|
||||||
|
Si une stable historique a été fermée sans prompt de la release suivante parce que la mission n'était pas encore décidée, le prompt peut exceptionnellement être créé au démarrage réel de cette release ; cette situation est un rattrapage et non le cycle normal.
|
||||||
|
|
||||||
Une coupure de session au milieu d'une tranche ne change pas `VERSION`. Si nécessaire, un handoff local décrit l'état exact, les décisions acquises, les preuves déjà obtenues et ce qui reste à exécuter.
|
Une coupure de session au milieu d'une tranche ne change pas `VERSION`. Si nécessaire, un handoff local décrit l'état exact, les décisions acquises, les preuves déjà obtenues et ce qui reste à exécuter.
|
||||||
|
|
||||||
@@ -116,7 +120,7 @@ Le tag stable `vX.Y.Z` est créé seulement après le commit stable. L'archive t
|
|||||||
|
|
||||||
Le rapport post-tag `sasedev-knowledge-X.Y.Z-publish.001-report.md` est une preuve distincte. Il vérifie le tag/archive réellement publiés et ne doit pas être préfabriqué comme preuve du delta `rel.1`.
|
Le rapport post-tag `sasedev-knowledge-X.Y.Z-publish.001-report.md` est une preuve distincte. Il vérifie le tag/archive réellement publiés et ne doit pas être préfabriqué comme preuve du delta `rel.1`.
|
||||||
|
|
||||||
Le prompt d'une release suivante n'est préparé que si sa mission est réellement décidée. Aucune version future n'est inventée pour remplir un handoff.
|
Le prompt d'une release suivante n'est préparé que si sa mission est suffisamment décidée par la revue d'horizon. Lorsqu'elle l'est, ce prompt fait partie des artefacts de fin de la release courante afin que la stable publiée contienne déjà son handoff vers la suite. Les éléments post-tag encore inconnus restent à vérifier au démarrage suivant plutôt qu'à être inventés.
|
||||||
|
|
||||||
## Immutabilité des artefacts historiques
|
## Immutabilité des artefacts historiques
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/300-DOCUMENTATION.md -->
|
<!-- file: docs/rules/300-DOCUMENTATION.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Documentation locale Knowledge
|
# Documentation locale Knowledge
|
||||||
|
|
||||||
@@ -22,9 +22,9 @@ La révision documentaire augmente lorsqu'un contenu réel du fichier change ; e
|
|||||||
|
|
||||||
`RULES.md` est l'entrée normative locale et renvoie vers les owners détaillés sous `docs/rules/`.
|
`RULES.md` est l'entrée normative locale et renvoie vers les owners détaillés sous `docs/rules/`.
|
||||||
|
|
||||||
`ROADMAP.md` décrit les objectifs et horizons produits réellement engagés. Il ne sert ni de journal quotidien ni de catalogue exhaustif des prereleases. Une version future n'y est pas réservée sans mission réelle.
|
`ROADMAP.md` décrit la release en cours et un horizon borné des prochaines releases. Il distingue explicitement une mission engagée d'une version d'horizon encore révisable ; l'horizon sert à ordonner les dépendances et à préparer la suite sans transformer chaque intention en engagement irrévocable. Il ne sert ni de journal quotidien ni de catalogue exhaustif des prereleases.
|
||||||
|
|
||||||
`CHANGELOG.md` synthétise les changements utiles aux jalons de release. Les fixes et essais alpha internes ne sont pas transformés en catalogue exhaustif lorsque leur détail appartient déjà aux deltas/histories.
|
`CHANGELOG.md` synthétise les changements réellement acquis d'une release. Il n'est normalement pas rédigé au démarrage ou pendant le cadrage alpha à partir d'intentions : une entrée candidate est préparée vers les phases de stabilisation, lorsque l'implémentation et les histories permettent de décrire ce qui existe effectivement, puis finalisée avant la promotion stable. Les fixes et essais alpha internes ne sont pas transformés en catalogue exhaustif lorsque leur détail appartient déjà aux deltas/histories.
|
||||||
|
|
||||||
`docs/000-README.md` indexe la documentation durable et fournit le chemin de lecture principal.
|
`docs/000-README.md` indexe la documentation durable et fournit le chemin de lecture principal.
|
||||||
|
|
||||||
@@ -64,14 +64,16 @@ Le plan est mis à jour lorsqu'une hypothèse de pilotage devient fausse, pas po
|
|||||||
|
|
||||||
## Revue d'horizon
|
## Revue d'horizon
|
||||||
|
|
||||||
Lorsqu'une release approche de sa stabilisation et que la suite n'est pas déjà décidée, une revue d'horizon explicite détermine s'il faut engager une prochaine mission, maintenir les domaines futurs non numérotés ou reclasser un `DEFER`.
|
Lorsqu'une release approche de sa stabilisation, une revue d'horizon explicite réévalue la release suivante et, autant que les dépendances sont suffisamment connues, au moins les deux releases d'horizon qui la suivent. Elle peut engager la prochaine mission, réordonner ou scinder un horizon, maintenir un domaine non numéroté ou reclasser un `DEFER`.
|
||||||
|
|
||||||
La revue n'invente pas un numéro de version uniquement pour assurer la continuité documentaire.
|
Une version marquée comme horizon n'est pas encore une promesse de scope : son numéro et sa mission peuvent changer à la revue suivante. La roadmap doit néanmoins donner assez de visibilité pour que la release en cours soit développée dans une trajectoire produit cohérente plutôt qu'en isolation.
|
||||||
|
|
||||||
## Prompts de nouvelle session
|
## Prompts de nouvelle session
|
||||||
|
|
||||||
Un prompt normal Knowledge repart de la stable Knowledge, de son rapport post-tag lorsqu'il existe, puis du prompt versionné local. Il vérifie les artefacts Knowledge locaux avant de préparer la tranche suivante.
|
Un prompt normal Knowledge repart de la stable Knowledge, de son rapport post-tag lorsqu'il existe, puis du prompt versionné local. Il vérifie les artefacts Knowledge locaux avant de préparer la tranche suivante.
|
||||||
|
|
||||||
|
Le prompt de la release suivante est normalement rédigé pendant les dernières phases de la release courante, après sa revue d'horizon. Il fait partie du handoff livré par la stable courante. Les identifiants post-tag qui n'existent pas encore lors de sa rédaction ne sont pas inventés : la session suivante les reçoit dans son texte d'introduction et les vérifie contre l'archive et le rapport réellement publiés.
|
||||||
|
|
||||||
Il MUST NOT demander de rechercher ou charger PGR ou un autre référentiel externe, sauf lorsque cette release contient explicitement une réévaluation/adoption de ce référentiel.
|
Il MUST NOT demander de rechercher ou charger PGR ou un autre référentiel externe, sauf lorsque cette release contient explicitement une réévaluation/adoption de ce référentiel.
|
||||||
|
|
||||||
Le pattern local est [`../templates/001-NEW_SESSION_PROMPT.md`](../templates/001-NEW_SESSION_PROMPT.md).
|
Le pattern local est [`../templates/001-NEW_SESSION_PROMPT.md`](../templates/001-NEW_SESSION_PROMPT.md).
|
||||||
|
|||||||
217
docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md
Normal file
217
docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
<!-- file: docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Étude 0.0.3 — écriture Git contrôlée
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Cette étude ouvre `0.0.3` depuis la stable autoritaire `v0.0.2` et détermine le prochain incrément utile après le chemin Git générique de lecture matérialisé en `0.0.2`.
|
||||||
|
|
||||||
|
Elle s'appuie uniquement sur les règles, l'architecture, la roadmap, les décisions différées et les preuves locales Knowledge. PGR `v0.1.3` reste une provenance historique d'adoption et aucune version PGR plus récente n'est consultée ou adoptée pour cette décision.
|
||||||
|
|
||||||
|
## Baseline observée
|
||||||
|
|
||||||
|
La stable de départ est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge tag stable v0.0.2
|
||||||
|
Knowledge commit stable 183d354a67bec6d2d512c4898f5189759a6120b0
|
||||||
|
Knowledge archive SHA-256 0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce
|
||||||
|
Knowledge publish report sasedev-knowledge-0.0.2-publish.001-report.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
`0.0.2` fournit déjà :
|
||||||
|
|
||||||
|
```text
|
||||||
|
source locale/remote
|
||||||
|
-> mirror contrôlé
|
||||||
|
-> refresh avec pruning
|
||||||
|
-> résolution vers commit
|
||||||
|
-> snapshot/worktree de lecture
|
||||||
|
-> synchronisation Changed / Unchanged
|
||||||
|
```
|
||||||
|
|
||||||
|
Le backend process Git contrôlé et les contrats provider-neutral sont donc acquis avant toute nouvelle capacité d'écriture.
|
||||||
|
|
||||||
|
## Revue des axes différés
|
||||||
|
|
||||||
|
La revue d'horizon `0.0.2-beta.1` laisse notamment ouverts :
|
||||||
|
|
||||||
|
- écriture Git contrôlée et publication ;
|
||||||
|
- PostgreSQL et migrations ;
|
||||||
|
- workflow complet de proposition/revue ;
|
||||||
|
- parsing final HOWTO/snippet ;
|
||||||
|
- recherche/indexation ;
|
||||||
|
- adapter Gitea ;
|
||||||
|
- auth/admin complète ;
|
||||||
|
- scheduler concret ;
|
||||||
|
- rétention de snapshots et partage multi-processus.
|
||||||
|
|
||||||
|
Le prochain axe doit apporter une capacité réellement réutilisable sans forcer plusieurs de ces décisions à la fois.
|
||||||
|
|
||||||
|
## Choix de la prochaine mission
|
||||||
|
|
||||||
|
La mission retenue est l'**écriture Git générique contrôlée avec mise à jour distante gardée**.
|
||||||
|
|
||||||
|
Ce choix est retenu parce qu'il :
|
||||||
|
|
||||||
|
- prolonge directement les responsabilités déjà matérialisées dans `knowledge-git-api` et `knowledge-git-lib` ;
|
||||||
|
- reste testable avec des repositories locaux et remotes bare temporaires, sans service externe ;
|
||||||
|
- conserve la forge optionnelle ;
|
||||||
|
- fournit la primitive nécessaire aux futurs workflows de proposition/publication sans implémenter prématurément leur état PostgreSQL, leurs rôles ou leur UI ;
|
||||||
|
- évite de sélectionner maintenant un driver/ORM PostgreSQL alors que l'écriture Git peut être fermée indépendamment ;
|
||||||
|
- transforme un `DEFER` explicitement identifié en une mission bornée sans absorber rebase/merge automatique ni publication produit complète.
|
||||||
|
|
||||||
|
`0.0.3` est donc le prochain incrément SemVer engagé.
|
||||||
|
|
||||||
|
## Stratégies d'écriture considérées
|
||||||
|
|
||||||
|
### A — worktree d'écriture isolé + commandes Git usuelles
|
||||||
|
|
||||||
|
Séquence conceptuelle :
|
||||||
|
|
||||||
|
```text
|
||||||
|
base commit explicite
|
||||||
|
-> worktree d'écriture isolé sous la racine d'état
|
||||||
|
-> mutations de fichiers validées
|
||||||
|
-> git add
|
||||||
|
-> commit avec identité explicite et hooks neutralisés
|
||||||
|
-> révision candidate
|
||||||
|
-> push gardé vers une branche autorisée
|
||||||
|
-> suppression contrôlée du worktree temporaire
|
||||||
|
```
|
||||||
|
|
||||||
|
Avantages :
|
||||||
|
|
||||||
|
- comportement proche de Git utilisé manuellement et facile à diagnostiquer ;
|
||||||
|
- réutilise le backend process déjà choisi et validé ;
|
||||||
|
- permet des tests locaux réalistes ;
|
||||||
|
- rend les mutations de fichiers et leur diff inspectables ;
|
||||||
|
- n'introduit pas de nouvelle bibliothèque Git.
|
||||||
|
|
||||||
|
Risques :
|
||||||
|
|
||||||
|
- cycle de vie d'un worktree temporaire à posséder explicitement ;
|
||||||
|
- validation stricte des chemins de mutation nécessaire ;
|
||||||
|
- nettoyage après erreur à traiter sans suppression destructive ambiguë.
|
||||||
|
|
||||||
|
### B — construction de commits par commandes Git plumbing
|
||||||
|
|
||||||
|
Une implémentation fondée sur `hash-object`, index temporaire, `write-tree` et `commit-tree` éviterait un worktree d'écriture classique.
|
||||||
|
|
||||||
|
Avantages :
|
||||||
|
|
||||||
|
- état filesystem potentiellement plus réduit ;
|
||||||
|
- contrôle fin de la construction du commit.
|
||||||
|
|
||||||
|
Inconvénients :
|
||||||
|
|
||||||
|
- complexité sensiblement supérieure pour les mutations multi-fichiers, suppressions et futurs cas de validation ;
|
||||||
|
- risque d'introduire une mini-couche Git plumbing difficile à maintenir alors que le projet possède déjà un modèle worktree pour la lecture ;
|
||||||
|
- bénéfice non démontré à ce stade.
|
||||||
|
|
||||||
|
### C — réouverture du choix `gix`/`git2`
|
||||||
|
|
||||||
|
Changer de backend uniquement pour l'écriture n'est pas retenu. `0.0.2` a déjà validé le processus `git` contrôlé comme implémentation locale remplaçable derrière le contrat. Une seconde implémentation demanderait une preuve de besoin distincte.
|
||||||
|
|
||||||
|
## Décision technique
|
||||||
|
|
||||||
|
`0.0.3` retient la stratégie A : **worktree d'écriture isolé piloté par le processus `git` existant**.
|
||||||
|
|
||||||
|
La capacité d'écriture reste séparée de la lecture dans l'API publique : `GitRepository` n'est pas transformé en interface obligatoire d'écriture pour tous ses implémenteurs. `knowledge-git-api` introduira un contrat d'écriture distinct uniquement lorsque `alpha.2` matérialisera les types nécessaires.
|
||||||
|
|
||||||
|
Aucune nouvelle crate n'est justifiée par le cadrage. L'implémentation appartient à `knowledge-git-lib` et l'orchestration métier future reste dans `knowledge-app-lib` lorsqu'un vrai cas d'usage de proposition/publication sera engagé.
|
||||||
|
|
||||||
|
## Invariants de sécurité et de cohérence
|
||||||
|
|
||||||
|
L'implémentation doit respecter les invariants suivants :
|
||||||
|
|
||||||
|
- aucune commande Git via shell ;
|
||||||
|
- prompts interactifs désactivés ;
|
||||||
|
- hooks neutralisés pour les opérations possédées par le backend ;
|
||||||
|
- identité auteur/committer fournie explicitement, sans dépendre de la configuration Git globale de la machine ;
|
||||||
|
- base d'écriture obligatoirement une `GitRevision` déjà résolue ;
|
||||||
|
- chemins de mutation obligatoirement relatifs, normalisés et confinés au worktree ;
|
||||||
|
- chemins absolus, composants `..`, accès `.git` et échappement par symlink refusés ;
|
||||||
|
- aucune mutation du snapshot de lecture stable ;
|
||||||
|
- aucune suppression/réinitialisation silencieuse d'un état inattendu ;
|
||||||
|
- le résultat d'écriture est une nouvelle révision Git explicite avant toute mise à jour distante ;
|
||||||
|
- la mise à jour distante porte uniquement sur une branche `refs/heads/...` dans `0.0.3` ;
|
||||||
|
- la branche distante est mise à jour avec une attente explicite sur son état précédent ; une divergence devient un conflit récupérable ;
|
||||||
|
- aucun `push --force` non gardé ;
|
||||||
|
- aucun rebase ou merge automatique pour masquer une divergence ;
|
||||||
|
- les erreurs publiques ne divulguent ni URL remote, ni credentials, ni arguments sensibles, ni stderr brut.
|
||||||
|
|
||||||
|
## Modèle de concurrence retenu
|
||||||
|
|
||||||
|
Le push doit utiliser une sémantique de comparaison-et-échange :
|
||||||
|
|
||||||
|
```text
|
||||||
|
expected remote revision + candidate revision + target branch
|
||||||
|
-> refresh/observation
|
||||||
|
-> push gardé
|
||||||
|
-> succès seulement si la cible possède encore l'état attendu
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour une nouvelle branche, l'état attendu est l'absence de branche. Pour une branche existante, l'état attendu est une révision explicite.
|
||||||
|
|
||||||
|
Une divergence n'entraîne ni rebase ni écrasement automatique. Le futur workflow de proposition décidera comment présenter et résoudre ce conflit.
|
||||||
|
|
||||||
|
## Scope fonctionnel proposé pour 0.0.3
|
||||||
|
|
||||||
|
La release doit fournir :
|
||||||
|
|
||||||
|
- un contrat d'écriture Git distinct et provider-neutral ;
|
||||||
|
- des types de mutation de fichiers suffisamment bornés pour créer/modifier/supprimer un contenu ;
|
||||||
|
- un worktree d'écriture isolé dérivé d'une révision de base ;
|
||||||
|
- un commit avec message et identité explicites ;
|
||||||
|
- une révision candidate vérifiée ;
|
||||||
|
- un push vers une branche Git générique avec garde sur l'état distant attendu ;
|
||||||
|
- un résultat de conflit explicite lorsque la branche distante a divergé ;
|
||||||
|
- des tests d'intégration sur repositories locaux/bare temporaires couvrant succès, création de branche, modification, suppression, conflit et redaction.
|
||||||
|
|
||||||
|
## Hors scope confirmé
|
||||||
|
|
||||||
|
Restent différés :
|
||||||
|
|
||||||
|
- workflow complet de proposition/revue ;
|
||||||
|
- publication directe déclenchée par une requête utilisateur ;
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- pull/merge request de forge ;
|
||||||
|
- adapter Gitea ;
|
||||||
|
- PostgreSQL, migrations et persistance des propositions ;
|
||||||
|
- auth/admin complète ;
|
||||||
|
- signature GPG/SSH des commits ;
|
||||||
|
- Git LFS/submodules ;
|
||||||
|
- partage concurrent d'une même racine d'état entre plusieurs processus ;
|
||||||
|
- scheduler et jobs de publication ;
|
||||||
|
- formats finaux HOWTO/snippet.
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-git-api contrat passif d'écriture Git distinct du contrat de lecture
|
||||||
|
knowledge-git-lib worktree d'écriture, mutations, commit, push gardé et erreurs redacted
|
||||||
|
knowledge-app-lib inchangée tant qu'aucun cas d'usage produit de proposition/publication n'est matérialisé
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucune `knowledge-forge-gitea-lib`, `knowledge-store-postgres-lib` ou autre crate candidate n'est créée par cette release uniquement pour préparer l'avenir.
|
||||||
|
|
||||||
|
## Validation attendue
|
||||||
|
|
||||||
|
Les preuves fonctionnelles doivent utiliser des repositories Git temporaires locaux et un remote bare local. Elles ne doivent dépendre ni d'Internet, ni de Gitea, ni d'un compte utilisateur Git préconfiguré.
|
||||||
|
|
||||||
|
La validation devra notamment démontrer :
|
||||||
|
|
||||||
|
- qu'un commit candidat est descendant de la base attendue ;
|
||||||
|
- qu'un fichier hors worktree ne peut pas être modifié ;
|
||||||
|
- qu'une branche absente peut être créée seulement lorsque l'appelant attend explicitement son absence ;
|
||||||
|
- qu'une branche existante peut avancer lorsque sa révision correspond à l'attente ;
|
||||||
|
- qu'une divergence concurrente produit un conflit sans écrasement ;
|
||||||
|
- que l'identité de commit ne provient pas de la configuration globale ;
|
||||||
|
- que les hooks ne s'exécutent pas ;
|
||||||
|
- que les erreurs restent expurgées.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
`0.0.3` est engagée comme release d'écriture Git contrôlée et push gardé. Le cadrage ne justifie aucun changement de forge, de base de données, d'authentification ou de rendu Web.
|
||||||
178
docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
Normal file
178
docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
<!-- file: docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Réconciliation 0.0.3 — écriture Git et horizon produit
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Cette étude de `0.0.3-beta.1` réconcilie la mission d'écriture Git avec ce qui est réellement matérialisé par les alphas, puis réévalue l'horizon produit de SaseDev Knowledge avant la release candidate.
|
||||||
|
|
||||||
|
Elle n'ajoute aucune fonctionnalité. Elle sert de baseline durable pour stabiliser `0.0.3` et préparer la prochaine release sans consulter ni réadopter automatiquement une nouvelle version de PGR.
|
||||||
|
|
||||||
|
## Chemin Git réellement obtenu
|
||||||
|
|
||||||
|
La lecture et l'écriture restent deux capacités provider-neutral distinctes :
|
||||||
|
|
||||||
|
```text
|
||||||
|
GitRepository
|
||||||
|
refresh
|
||||||
|
resolve_revision
|
||||||
|
prepare_snapshot
|
||||||
|
|
||||||
|
GitRepositoryWriter
|
||||||
|
create_candidate
|
||||||
|
push_candidate
|
||||||
|
```
|
||||||
|
|
||||||
|
Le chemin d'écriture réellement validé est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
GitRevision de base explicite
|
||||||
|
-> worktree d'écriture isolé
|
||||||
|
-> mutations create/update/delete confinées
|
||||||
|
-> commit avec identité explicite et hooks/config ambiante neutralisés
|
||||||
|
-> vérification du parent exact
|
||||||
|
-> référence interne refs/knowledge/candidates/<revision>
|
||||||
|
-> observation distante de refs/heads/...
|
||||||
|
-> push --force-with-lease contre l'état attendu
|
||||||
|
-> Updated(candidate) ou Conflict { actual }
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun adapter de forge, rebase/merge automatique ou force-push non gardé n'est nécessaire pour cette primitive.
|
||||||
|
|
||||||
|
## Invariants acquis
|
||||||
|
|
||||||
|
`0.0.3` acquiert les propriétés suivantes :
|
||||||
|
|
||||||
|
- chemins de mutation relatifs et normalisés, avec `.git`, traversées et doublons refusés ;
|
||||||
|
- vérification filesystem empêchant une traversée par symlink hors du worktree contrôlé ;
|
||||||
|
- auteur et committer fournis explicitement ;
|
||||||
|
- hooks Git désactivés pour le commit candidat ;
|
||||||
|
- configuration Git système/globale pertinente neutralisée ;
|
||||||
|
- candidat enfant direct de la base demandée ;
|
||||||
|
- candidat retenu par une ref backend interne avant nettoyage du worktree ;
|
||||||
|
- branche distante limitée à `refs/heads/...` ;
|
||||||
|
- création de branche uniquement lorsque son absence est attendue ;
|
||||||
|
- mise à jour d'une branche existante uniquement contre sa révision attendue ;
|
||||||
|
- `--force-with-lease` comme garde autoritaire contre la course concurrente ;
|
||||||
|
- divergence représentée comme conflit récupérable sans écrasement.
|
||||||
|
|
||||||
|
Ces invariants sont démontrés par des tests d'intégration utilisant uniquement des repositories et remotes bare locaux.
|
||||||
|
|
||||||
|
## DEFER reclassés
|
||||||
|
|
||||||
|
### Reclassé comme prochaine mission
|
||||||
|
|
||||||
|
La persistance PostgreSQL partagée est désormais le prochain axe structurant. Le chemin Git de lecture et d'écriture étant disponible, les futures façades ont besoin d'un état durable commun pour la configuration logique, la dernière révision synchronisée/indexée, les projections et les futurs workflows transversaux.
|
||||||
|
|
||||||
|
`0.0.4` est donc retenue comme prochaine mission :
|
||||||
|
|
||||||
|
```text
|
||||||
|
fondation PostgreSQL partagée + premier schéma + migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette décision n'impose pas encore :
|
||||||
|
|
||||||
|
- un driver/ORM particulier ;
|
||||||
|
- un pool particulier ;
|
||||||
|
- une stratégie Docker/service système particulière pour les tests ;
|
||||||
|
- un schéma de contenu final PGR/HOWTO/snippet ;
|
||||||
|
- auth, propositions ou moteur de recherche complet.
|
||||||
|
|
||||||
|
Ces points appartiennent à l'étude de démarrage de `0.0.4`.
|
||||||
|
|
||||||
|
### Toujours différés
|
||||||
|
|
||||||
|
Restent `DEFER` ou hors horizon numéroté tant que leur déclencheur n'est pas réel :
|
||||||
|
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- adapter Gitea ou autre forge et webhooks forge-only ;
|
||||||
|
- scheduler/jobs concrets ;
|
||||||
|
- moteur de recherche avancé au-delà des besoins de projection réellement observés ;
|
||||||
|
- quatrième application d'administration transverse ;
|
||||||
|
- partage simultané d'une même racine Git par plusieurs processus ;
|
||||||
|
- Git LFS/submodules ;
|
||||||
|
- politique avancée de rétention/cleanup ;
|
||||||
|
- observabilité ou service supplémentaire sans owner démontré.
|
||||||
|
|
||||||
|
## Horizon produit des trois sites
|
||||||
|
|
||||||
|
L'objectif produit n'est pas une succession de seules briques backend. Knowledge vise trois façades distinctes utilisant le même socle Git/PostgreSQL :
|
||||||
|
|
||||||
|
```text
|
||||||
|
pgr.sasedev.com affichage et gestion de PGR
|
||||||
|
howtos.sasedev.com affichage et gestion des HOWTOs
|
||||||
|
snippets.sasedev.com affichage et gestion des snippets
|
||||||
|
```
|
||||||
|
|
||||||
|
La revue beta distingue donc les phases suivantes.
|
||||||
|
|
||||||
|
### 0.0.4 — PostgreSQL partagé
|
||||||
|
|
||||||
|
Matérialiser la base logique commune, ses migrations et l'état minimal transversal/reconstructible sans déplacer l'autorité éditoriale hors de Git.
|
||||||
|
|
||||||
|
### 0.0.5 — modèles de contenu et projections
|
||||||
|
|
||||||
|
Travailler sur des corpus réels PGR/HOWTO/snippet, définir parsing/validation, métadonnées et relations nécessaires, puis produire les projections locales consommables par les façades.
|
||||||
|
|
||||||
|
### 0.0.6 — templates publics
|
||||||
|
|
||||||
|
Développer dans `Web/knowledge-template-lab` les références visuelles des trois sites : pages de garde, navigation, listes, détails, composants communs et états de recherche/filtrage visibles.
|
||||||
|
|
||||||
|
Cette phase valide le design et les contrats de markup/assets indépendamment du runtime Rust.
|
||||||
|
|
||||||
|
### 0.0.7 — templates d'administration
|
||||||
|
|
||||||
|
Développer les surfaces de gestion propres aux trois sites : dashboards, formulaires d'édition, listes d'objets, états de proposition/revue et retours d'erreur utiles.
|
||||||
|
|
||||||
|
Ces templates restent une référence de conception ; ils ne constituent pas encore une UI runtime complète.
|
||||||
|
|
||||||
|
### 0.0.8 — Actix Web / Maud public
|
||||||
|
|
||||||
|
Matérialiser les routes publiques réelles dans les trois launchers et adapter en Maud les templates publics validés. Les pages sont alimentées par les sources Git/projections locales et conservent le pipeline Vite/assets commun.
|
||||||
|
|
||||||
|
### 0.0.9 — Actix Web / Maud admin et autorisation
|
||||||
|
|
||||||
|
Matérialiser les besoins réels d'identité/session/capabilities, protéger les routes d'administration par façade et adapter en Maud les templates admin validés.
|
||||||
|
|
||||||
|
### 0.0.10 — gestion, proposition, revue et publication
|
||||||
|
|
||||||
|
Relier les interfaces d'administration aux cas d'usage applicatifs : édition/proposition basée sur une révision Git connue, revue, conflit explicite, création/push de la révision Git autoritaire, puis réconciliation PostgreSQL.
|
||||||
|
|
||||||
|
## Pourquoi séparer templates et Maud
|
||||||
|
|
||||||
|
Le laboratoire Web existe précisément pour concevoir et valider les références statiques sans lier chaque décision visuelle au cycle de compilation serveur.
|
||||||
|
|
||||||
|
La trajectoire retenue est donc :
|
||||||
|
|
||||||
|
```text
|
||||||
|
conception/validation template
|
||||||
|
-> contrat de composants/classes/assets utile
|
||||||
|
-> adaptation Maud
|
||||||
|
-> routes et données runtime
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela évite deux écueils :
|
||||||
|
|
||||||
|
- concevoir l'interface directement dans le code Maud avant validation visuelle ;
|
||||||
|
- maintenir durablement deux produits UI indépendants, l'un statique et l'autre runtime.
|
||||||
|
|
||||||
|
La future phase d'intégration doit définir des invariants ciblés permettant de détecter une dérive significative entre les références validées et leur adaptation Maud, sans imposer une duplication exhaustive.
|
||||||
|
|
||||||
|
## Changelog et handoff
|
||||||
|
|
||||||
|
`beta.1` est la première phase où l'implémentation de `0.0.3` est suffisamment acquise pour préparer une entrée `## 0.0.3 (candidate)` décrivant les capacités réelles plutôt que les intentions alpha.
|
||||||
|
|
||||||
|
La mission `0.0.4` est également suffisamment définie pour préparer maintenant :
|
||||||
|
|
||||||
|
```text
|
||||||
|
prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Le prompt ne contient pas de commit stable, SHA-256 d'archive ou résultat post-tag inventé pour `v0.0.3`. Ces preuves devront être fournies et vérifiées après publication réelle de la stable.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Le scope fonctionnel de `0.0.3` est cohérent avec sa mission et peut entrer en stabilisation sans nouveau code produit. La release candidate doit geler ce résultat et rejouer la preuve complète applicable.
|
||||||
|
|
||||||
|
La trajectoire suivante est désormais lisible à l'échelle du produit : Git lecture/écriture acquis, PostgreSQL ensuite, puis contenu/projections, conception des interfaces publiques et admin, adaptation Actix/Maud et enfin workflows de gestion/publication.
|
||||||
255
docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
Normal file
255
docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
<!-- file: docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md -->
|
||||||
|
<!-- version: 2 -->
|
||||||
|
|
||||||
|
# Étude 0.0.4 — fondation PostgreSQL partagée
|
||||||
|
|
||||||
|
## Objet
|
||||||
|
|
||||||
|
Cette étude de `0.0.4-alpha.1` décide l'ownership et la première architecture PostgreSQL de SaseDev Knowledge avant toute implémentation de stockage.
|
||||||
|
|
||||||
|
Elle part exclusivement de la stable Knowledge `v0.0.3` publiée et vérifiée. Elle ne consulte ni n'adopte une nouvelle version de PGR.
|
||||||
|
|
||||||
|
## Baseline autoritaire vérifiée
|
||||||
|
|
||||||
|
La baseline reçue au démarrage est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tag v0.0.3
|
||||||
|
commit b3823972449150b72905930b806d4d15471cddce
|
||||||
|
archive sasedev-knowledge-v0.0.3.zip
|
||||||
|
SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d
|
||||||
|
report sasedev-knowledge-0.0.3-publish.001-report.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
La vérification de démarrage confirme :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3` et `[workspace.package].version = 0.0.3` dans l'archive ;
|
||||||
|
- neuf packages Cargo et aucune crate PostgreSQL déjà matérialisée ;
|
||||||
|
- douze deltas et douze histories `0.0.3`, appariés jusqu'à `rel.1.fix.1 = PASS` ;
|
||||||
|
- Git lecture/écriture contrôlée stable et provider-neutral ;
|
||||||
|
- `ROADMAP.md` engage déjà `0.0.4` comme fondation PostgreSQL partagée ;
|
||||||
|
- `prompts/004-V0_0_4_START_PROMPT.md` est présent dans la stable ;
|
||||||
|
- PGR `v0.1.3` reste uniquement une provenance historique de l'adoption fermée en `0.0.2`.
|
||||||
|
|
||||||
|
## Contraintes acquises
|
||||||
|
|
||||||
|
La fondation doit préserver simultanément :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Git vérité du contenu publié
|
||||||
|
PostgreSQL état applicatif transversal et projections reconstructibles
|
||||||
|
knowledge-domain-lib aucun type SQL/Actix/Gitea
|
||||||
|
knowledge-git-api contrats Git provider-neutral
|
||||||
|
knowledge-git-lib configuration physique et opérations Git
|
||||||
|
knowledge-app-lib orchestration applicative
|
||||||
|
knowledge-web-lib composition Web partagée
|
||||||
|
```
|
||||||
|
|
||||||
|
La première implémentation PostgreSQL ne doit pas entraîner par symétrie une nouvelle crate `-api`, un service réseau, trois bases séparées, un ORM complet, un scheduler ou un modèle de contenu final.
|
||||||
|
|
||||||
|
## Sources techniques consultées
|
||||||
|
|
||||||
|
Consultation effectuée le `2026-09-30` sur les documentations publiées des crates comparées :
|
||||||
|
|
||||||
|
- SQLx `0.9.0` et ses features/MSRV : <https://docs.rs/crate/sqlx/0.9.0/source/Cargo.toml.orig> ;
|
||||||
|
- `PgPool` SQLx : <https://docs.rs/sqlx/0.9.0/sqlx/postgres/type.PgPool.html> ;
|
||||||
|
- migrations et `migrate!` SQLx : <https://docs.rs/sqlx/0.9.0/sqlx/migrate/index.html> et <https://docs.rs/sqlx/0.9.0/sqlx/macro.migrate.html> ;
|
||||||
|
- `tokio-postgres` `0.7.18` : <https://docs.rs/tokio-postgres/0.7.18/tokio_postgres/> ;
|
||||||
|
- `deadpool-postgres` `0.14.2` : <https://docs.rs/deadpool-postgres/0.14.2/deadpool_postgres/> ;
|
||||||
|
- Diesel `2.3.13` : <https://docs.rs/crate/diesel/2.3.13> ;
|
||||||
|
- `diesel-async` `0.9.2` : <https://docs.rs/crate/diesel-async/0.9.2> ;
|
||||||
|
- SeaORM `2.0.4` : <https://docs.rs/crate/sea-orm/2.0.4> ;
|
||||||
|
- Refinery `0.9.2` : <https://docs.rs/crate/refinery/0.9.2>.
|
||||||
|
|
||||||
|
## Comparaison des options Rust
|
||||||
|
|
||||||
|
État des versions consultées le `2026-09-30` : SQLx `0.9.0`, `tokio-postgres` `0.7.18`, `deadpool-postgres` `0.14.2`, Diesel `2.3.13`, `diesel-async` `0.9.2`, SeaORM `2.0.4` et Refinery `0.9.2`.
|
||||||
|
|
||||||
|
| Option | Atouts pour Knowledge | Coûts / inadéquations | Décision |
|
||||||
|
|----------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|----------|
|
||||||
|
| `tokio-postgres` + Deadpool + Refinery | driver PostgreSQL async natif, responsabilités séparées, SQL explicite, pool spécialisé et migrations SQL intégrées | trois briques à versionner et intégrer ; la façade Knowledge doit normaliser leurs erreurs | `SELECT` |
|
||||||
|
| SQLx `0.9` | async/Tokio, PostgreSQL natif, pool et migrations intégrés, SQL explicite | toolkit multi-SGBD plus large que le besoin ; MSRV `1.94`; ses macros de requêtes ne sont pas nécessaires ici | `REJECT` |
|
||||||
|
| Diesel + `diesel-async` | typage/DSL fort, écosystème migrations, pooling disponible | modèle ORM/query-builder plus large que le premier schéma et davantage de surface de génération/traits | `REJECT` |
|
||||||
|
| SeaORM | ORM async complet, migrations, relations et modèles riches | surdimensionné avant les modèles/projections `0.0.5`; risque de faire remonter l'ORM dans les contrats | `REJECT` |
|
||||||
|
|
||||||
|
La sélection privilégie des responsabilités explicitement bornées plutôt qu'un toolkit généraliste. Knowledge cible PostgreSQL comme choix de produit : l'abstraction multi-SGBD de SQLx n'apporte donc pas de valeur attendue pour cette fondation. Le coût de composition de trois briques reste acceptable parce que chacune possède une responsabilité unique et vérifiable.
|
||||||
|
|
||||||
|
## Décision de dépendance
|
||||||
|
|
||||||
|
La première implémentation utilisera `tokio-postgres` comme driver PostgreSQL asynchrone natif, `deadpool-postgres` comme pool et `refinery` comme owner technique des migrations SQL. Aucun ORM n'est sélectionné et aucune abstraction multi-SGBD n'est recherchée.
|
||||||
|
|
||||||
|
Les versions observées pendant l'étude sont `tokio-postgres 0.7.18`, `deadpool-postgres 0.14.2` et `refinery 0.9.2`. `tokio-postgres` et `deadpool-postgres` déclarent actuellement Rust 2024 avec un MSRV `1.85`; Refinery vise les Rust stable récents. `alpha.2` a revalidé ces versions le `2026-09-30` avant leur ajout au manifest et retient les features minimales `runtime` pour `tokio-postgres`, `rt_tokio_1` pour `deadpool-postgres` et `tokio-postgres` pour Refinery. Tokio `1.53.1` est ajouté uniquement comme runtime de test d'intégration.
|
||||||
|
|
||||||
|
La séparation est volontaire :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tokio-postgres protocole, connexion, requêtes et transactions PostgreSQL
|
||||||
|
deadpool-postgres cycle de vie et mutualisation des connexions
|
||||||
|
refinery découverte, historique et application des migrations SQL
|
||||||
|
```
|
||||||
|
|
||||||
|
La façade publique de `knowledge-store-postgres-lib` masque ces dépendances et normalise leurs erreurs dans le type d'erreur de la crate. Les types `tokio-postgres`, Deadpool ou Refinery ne doivent pas remonter dans `knowledge-domain-lib` ni dans les contrats applicatifs.
|
||||||
|
|
||||||
|
`alpha.2` matérialise la première connexion avec `tokio_postgres::NoTls` et n'ajoute aucune dépendance TLS prématurée. Si une cible de déploiement PostgreSQL exige TLS, une tranche ultérieure devra sélectionner explicitement l'intégration TLS minimale compatible avec `tokio-postgres` au lieu d'hériter d'un défaut implicite.
|
||||||
|
|
||||||
|
## Ownership
|
||||||
|
|
||||||
|
La responsabilité est suffisamment concrète pour justifier :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-store-postgres-lib
|
||||||
|
```
|
||||||
|
|
||||||
|
À partir d'`alpha.2`, cette crate possède :
|
||||||
|
|
||||||
|
- construction et lifecycle du pool `deadpool-postgres` ;
|
||||||
|
- migrations SQL de Knowledge ;
|
||||||
|
- mapping SQL des types nécessaires ;
|
||||||
|
- persistance de configuration logique de source ;
|
||||||
|
- persistance de l'état de synchronisation ;
|
||||||
|
- erreurs publiques récupérables de cette frontière.
|
||||||
|
|
||||||
|
Elle dépendra des types domaine/Git strictement nécessaires, jamais l'inverse.
|
||||||
|
|
||||||
|
Aucun `knowledge-store-api` n'est créé en `0.0.4-alpha.1`. Si une deuxième implémentation réelle ou un besoin de fake transverse rend plus tard une frontière passive utile, cette décision sera réévaluée à partir d'un usage concret.
|
||||||
|
|
||||||
|
## Connexion, pool et secrets
|
||||||
|
|
||||||
|
La crate de stockage ne lit pas l'environnement. La composition lui fournit une configuration contenant au minimum l'URL PostgreSQL et les paramètres de pool retenus.
|
||||||
|
|
||||||
|
L'URL peut contenir un secret et respecte donc les invariants suivants :
|
||||||
|
|
||||||
|
- aucune valeur réelle versionnée ;
|
||||||
|
- aucune URL complète dans les erreurs publiques ou logs ;
|
||||||
|
- aucune valeur par défaut de production codée en dur ;
|
||||||
|
- aucune tentative de fallback vers un fichier, SQLite ou un stockage mémoire si PostgreSQL est indisponible.
|
||||||
|
|
||||||
|
Le pool est partagé par l'instance applicative qui compose le store. Les trois produits peuvent pointer vers la même base logique sans que chaque launcher réimplémente sa propre couche SQL.
|
||||||
|
|
||||||
|
## Premier schéma utile
|
||||||
|
|
||||||
|
Le premier schéma est volontairement plus petit que l'état futur décrit dans l'architecture.
|
||||||
|
|
||||||
|
### `knowledge_sources`
|
||||||
|
|
||||||
|
```text
|
||||||
|
source_id TEXT PRIMARY KEY
|
||||||
|
family TEXT NOT NULL
|
||||||
|
git_reference TEXT NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
`source_id` correspond à `SourceId`. `family` représente `ContentFamily` avec une contrainte SQL sur les valeurs supportées. `git_reference` est la référence Git logique à résoudre, pas une URL remote ni un credential. Les valeurs `family` persistées sont `pgr`, `howto`, `snippet` et `project_local`; la migration doit les contraindre explicitement.
|
||||||
|
|
||||||
|
### `knowledge_source_sync_state`
|
||||||
|
|
||||||
|
```text
|
||||||
|
source_id TEXT PRIMARY KEY REFERENCES knowledge_sources(source_id) ON DELETE CASCADE
|
||||||
|
last_synchronized_revision TEXT NOT NULL
|
||||||
|
```
|
||||||
|
|
||||||
|
La révision stockée représente la dernière révision effectivement acceptée par le cas d'usage de synchronisation. Elle est un état applicatif de comparaison. Git reste capable de fournir l'identité autoritaire et une valeur PostgreSQL perdue ou obsolète conduit à une nouvelle réconciliation, pas à une perte du contenu publié.
|
||||||
|
|
||||||
|
Le schéma n'ajoute pas encore :
|
||||||
|
|
||||||
|
- corps ou métadonnées éditoriales de contenu ;
|
||||||
|
- révision indexée séparée avant l'implémentation réelle des projections ;
|
||||||
|
- utilisateurs/sessions ;
|
||||||
|
- propositions/revues ;
|
||||||
|
- jobs ;
|
||||||
|
- audit applicatif ;
|
||||||
|
- moteur de recherche.
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
Les migrations sont des fichiers SQL versionnés sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-store-postgres-lib/migrations/
|
||||||
|
```
|
||||||
|
|
||||||
|
La crate embarque le jeu de migrations au build et expose une opération explicite d'application. Le schéma n'est pas créé implicitement à chaque requête.
|
||||||
|
|
||||||
|
Les migrations sont forward-only pour cette fondation : une correction de schéma publiée utilise une nouvelle migration. Un rollback destructif automatique n'est pas requis pour fermer `0.0.4`.
|
||||||
|
|
||||||
|
## Stratégie de test PostgreSQL
|
||||||
|
|
||||||
|
Le test réel doit prouver PostgreSQL, pas une imitation en mémoire.
|
||||||
|
|
||||||
|
La stratégie retenue est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
KNOWLEDGE_POSTGRES_TEST_URL
|
||||||
|
-> PostgreSQL local ou service CI explicitement fourni
|
||||||
|
-> création d'un schéma de test unique
|
||||||
|
-> search_path borné à ce schéma
|
||||||
|
-> application des migrations
|
||||||
|
-> tests CRUD / état de synchronisation
|
||||||
|
-> suppression du schéma
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette stratégie évite d'imposer Docker dans la bibliothèque tout en autorisant un service container en CI. L'absence du service requis pendant une gate qui exige le test PostgreSQL est `BLOCKED`, pas `PASS`.
|
||||||
|
|
||||||
|
Les tests unitaires sans PostgreSQL restent utiles pour les validations de types/configuration qui n'ont pas besoin d'un serveur réel, mais ils ne remplacent pas l'intégration PostgreSQL de migration et persistance.
|
||||||
|
|
||||||
|
## Raccord au cas d'usage de synchronisation
|
||||||
|
|
||||||
|
Le cas d'usage existant `synchronize_repository` reçoit actuellement `previous_revision` de son appelant. `0.0.4` doit conserver cette primitive provider-neutral.
|
||||||
|
|
||||||
|
Le raccord prévu est une orchestration supplémentaire qui :
|
||||||
|
|
||||||
|
```text
|
||||||
|
charge last_synchronized_revision
|
||||||
|
-> appelle synchronize_repository
|
||||||
|
-> enregistre la révision observée après succès
|
||||||
|
```
|
||||||
|
|
||||||
|
Un contrat passif de persistance peut vivre dans `knowledge-app-lib` si `alpha.4` démontre qu'il est nécessaire pour tester cette orchestration sans coupler l'application au driver PostgreSQL. Cette possibilité ne justifie pas à elle seule une crate `knowledge-store-api`.
|
||||||
|
|
||||||
|
## Indisponibilité PostgreSQL
|
||||||
|
|
||||||
|
Les erreurs de connexion, acquisition de pool, migration ou requête sont récupérables et typées à la frontière store.
|
||||||
|
|
||||||
|
Il n'existe pas de mode où une écriture est annoncée persistée alors que PostgreSQL ne l'est pas. Une future façade publique pourra continuer à servir un contenu Git déjà matérialisé seulement si son cas d'usage n'exige pas le store ; cette politique appartient à la composition de la façade, pas à un fallback interne de la crate de stockage.
|
||||||
|
|
||||||
|
## Impact sur les trois applications
|
||||||
|
|
||||||
|
`0.0.4` ne crée pas trois implémentations ou trois schémas par façade. `knowledge-store-postgres-lib` est le propriétaire partagé et la base logique est commune.
|
||||||
|
|
||||||
|
Les launchers ne seront modifiés que lorsqu'un cas d'usage runtime exige réellement la connexion. La fondation peut donc être testée indépendamment avant d'ajouter une dépendance de démarrage PostgreSQL à `/` et `/healthz`.
|
||||||
|
|
||||||
|
## DEFER conservés
|
||||||
|
|
||||||
|
Restent explicitement hors scope de `0.0.4` :
|
||||||
|
|
||||||
|
- formats finaux et parsing PGR/HOWTO/snippet ;
|
||||||
|
- projections de contenu finales et révision indexée détaillée ;
|
||||||
|
- recherche avancée ;
|
||||||
|
- Gitea/forge et webhooks forge-only ;
|
||||||
|
- scheduler/jobs ;
|
||||||
|
- auth, sessions et rôles/capabilities ;
|
||||||
|
- propositions/revues/publication produit ;
|
||||||
|
- templates publics/admin et routes finales ;
|
||||||
|
- quatrième application admin ;
|
||||||
|
- partage multi-processus de la racine Git ;
|
||||||
|
- Git LFS/submodules et rétention avancée.
|
||||||
|
|
||||||
|
## Horizon
|
||||||
|
|
||||||
|
L'étude ne révèle aucune dépendance fausse dans la revue `0.0.3-beta.1`.
|
||||||
|
|
||||||
|
L'horizon reste donc :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.5 modèles de contenu et projections
|
||||||
|
0.0.6 templates publics
|
||||||
|
0.0.7 templates d'administration
|
||||||
|
0.0.8 lecture publique Actix Web / Maud
|
||||||
|
0.0.9 administration Actix Web / Maud et autorisation
|
||||||
|
0.0.10 gestion, proposition, revue et publication
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
`0.0.4` peut progresser sans REPLAN : l'owner PostgreSQL est démontré, `tokio-postgres`, `deadpool-postgres` et `refinery` couvrent respectivement le driver, le pool et les migrations nécessaires, le premier schéma est borné et reconstructible par rapport à Git, et une stratégie de test PostgreSQL réelle est définie sans imposer un outil de provisioning unique.
|
||||||
|
|
||||||
|
`alpha.1` a fermé le cadrage en `PASS`. `alpha.2` matérialise maintenant `knowledge-store-postgres-lib`, le trio `tokio-postgres` / `deadpool-postgres` / `refinery`, une configuration/pool sans fuite de secret, la migration initiale des sources et un test PostgreSQL réel isolé par schéma. Les opérations de persistance source/state restent réservées à `alpha.3`.
|
||||||
31
docs/templates/001-NEW_SESSION_PROMPT.md
vendored
31
docs/templates/001-NEW_SESSION_PROMPT.md
vendored
@@ -1,25 +1,42 @@
|
|||||||
<!-- file: docs/templates/001-NEW_SESSION_PROMPT.md -->
|
<!-- file: docs/templates/001-NEW_SESSION_PROMPT.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Pattern local — prompt de nouvelle session
|
# Pattern local — prompt de nouvelle session
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Ce pattern prépare le court message opérateur remis après une stable Knowledge publiée. Le prompt détaillé reste un fichier versionné sous `prompts/`.
|
Ce pattern prépare le prompt versionné qui permettra d'ouvrir une future release Knowledge.
|
||||||
|
|
||||||
|
Le cycle normal crée le prompt de `N+1` pendant les dernières phases de la release `N`, après une revue d'horizon ayant suffisamment défini la mission suivante et avant la clôture stable de `N`. Il doit donc être présent dans la stable `N` publiée.
|
||||||
|
|
||||||
Une session normale Knowledge ne consulte pas PGR. Une mention PGR n'apparaît que lorsque la release cible contient explicitement une réévaluation/adoption PGR.
|
Une session normale Knowledge ne consulte pas PGR. Une mention PGR n'apparaît que lorsque la release cible contient explicitement une réévaluation/adoption PGR.
|
||||||
|
|
||||||
## Forme normale
|
## Informations connues à la rédaction
|
||||||
|
|
||||||
Lorsque le rapport post-tag de la baseline est `PASS` :
|
Pendant la fin de `N`, les éléments suivants peuvent être établis :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Partir de l'archive sasedev-knowledge-vX.Y.Z.zip téléchargée depuis le tag Gitea vX.Y.Z et du rapport post-tag sasedev-knowledge-X.Y.Z-publish.001-report.md. Lire et exécuter prompts/NNN-<NEXT_RELEASE>_START_PROMPT.md. Vérifier intégralement l'archive Knowledge, les règles locales, la gouvernance, l'architecture, la structure documentaire, les deltas/histories, les décisions différées et la cohérence globale avant de préparer <NEXT_RELEASE>-alpha.1.
|
release stable attendue N
|
||||||
|
release suivante N+1
|
||||||
|
mission de N+1 décidée par la revue d'horizon
|
||||||
|
chemin du prompt prompts/NNN-<NEXT_RELEASE>_START_PROMPT.md
|
||||||
```
|
```
|
||||||
|
|
||||||
`X.Y.Z` désigne la stable autoritaire de départ et `<NEXT_RELEASE>` la release suivante réellement décidée ; ces deux valeurs ne sont pas supposées identiques.
|
Le commit stable exact, le SHA-256 de l'archive taggée et le résultat du rapport post-tag de `N` n'existent pas encore nécessairement. Le prompt MUST NOT inventer ces valeurs.
|
||||||
|
|
||||||
Les noms et versions sont remplacés uniquement par des valeurs établies. Si le rapport post-tag n'existe pas ou n'est pas `PASS`, il n'est pas présenté comme preuve.
|
## Forme normale du texte d'introduction de la session suivante
|
||||||
|
|
||||||
|
Après publication réelle de `N`, l'opérateur fournit :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Partir de l'archive sasedev-knowledge-vX.Y.Z.zip téléchargée depuis le tag Gitea vX.Y.Z et du rapport post-tag sasedev-knowledge-X.Y.Z-publish.001-report.md. Considérer cette stable comme la baseline autoritaire de SaseDev Knowledge. Lire et exécuter prompts/NNN-<NEXT_RELEASE>_START_PROMPT.md. Vérifier intégralement l'archive, les règles et owners normatifs locaux, la gouvernance, l'architecture, la structure documentaire, les deltas/histories, les décisions DEFER, la revue d'horizon et la cohérence globale avant de préparer <NEXT_RELEASE>-alpha.1.
|
||||||
|
```
|
||||||
|
|
||||||
|
Le prompt versionné exige alors de vérifier que l'archive et le rapport fournis correspondent réellement à la stable publiée avant tout développement.
|
||||||
|
|
||||||
|
## Rattrapage exceptionnel
|
||||||
|
|
||||||
|
Si une stable a été fermée sans prompt suivant parce qu'aucune mission n'était encore décidée, le prompt MAY être créé lors de l'ouverture réelle de la release suivante. Ce cas est documenté comme rattrapage ; il ne devient pas le cycle normal.
|
||||||
|
|
||||||
## Réévaluation externe explicite
|
## Réévaluation externe explicite
|
||||||
|
|
||||||
|
|||||||
8
docs/templates/002-RELEASE_PLAN.md
vendored
8
docs/templates/002-RELEASE_PLAN.md
vendored
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/templates/002-RELEASE_PLAN.md -->
|
<!-- file: docs/templates/002-RELEASE_PLAN.md -->
|
||||||
<!-- version: 1 -->
|
<!-- version: 2 -->
|
||||||
|
|
||||||
# Pattern local — plan de release
|
# Pattern local — plan de release
|
||||||
|
|
||||||
@@ -30,4 +30,8 @@ Le nombre de tranches n'est pas fixé. Les phases annoncées restent sous `## Tr
|
|||||||
|
|
||||||
Le plan pilote le travail restant ; il n'est pas un journal de commandes. Les preuves détaillées vivent dans `history/` et les règles durables vivent chez leur owner local.
|
Le plan pilote le travail restant ; il n'est pas un journal de commandes. Les preuves détaillées vivent dans `history/` et les règles durables vivent chez leur owner local.
|
||||||
|
|
||||||
Un REPLAN met à jour la trajectoire et les responsabilités devenues fausses. Aucune version postérieure n'est réservée uniquement pour remplir la roadmap.
|
Un REPLAN met à jour la trajectoire et les responsabilités devenues fausses.
|
||||||
|
|
||||||
|
La revue d'horizon de fin de release doit réévaluer la prochaine mission et un horizon borné au-delà d'elle. Lorsque `N+1` est suffisamment décidée, la fin de `N` prépare aussi son prompt versionné de démarrage afin que la stable `N` contienne déjà le handoff vers la suite.
|
||||||
|
|
||||||
|
Le changelog n'est pas une intention du plan : son entrée de release est préparée vers la stabilisation à partir du résultat réellement matérialisé, puis finalisée avant la promotion stable.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
|
<!-- file: docs/validation/001-VALIDATION_MATRIX.md -->
|
||||||
<!-- version: 32 -->
|
<!-- version: 43 -->
|
||||||
|
|
||||||
# Matrice de validation Knowledge
|
# Matrice de validation Knowledge
|
||||||
|
|
||||||
@@ -117,6 +117,156 @@ Les tests doivent permettre plusieurs `source_id` et ne pas confondre famille de
|
|||||||
|
|
||||||
Un test live de remote ou de forge doit être explicitement classé comme externe, séparé des tests unitaires et ne doit pas devenir une condition cachée pour les opérations locales.
|
Un test live de remote ou de forge doit être explicitement classé comme externe, séparé des tests unitaires et ne doit pas devenir une condition cachée pour les opérations locales.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-alpha.2`
|
||||||
|
|
||||||
|
La tranche de contrat d'écriture Git doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-alpha.2` et version workspace identique ;
|
||||||
|
- `knowledge-git-api` reste une crate passive sans dépendance de forge, runtime, filesystem backend ou PostgreSQL ;
|
||||||
|
- `GitRepository` de lecture reste inchangé et implémentable sans capacité d'écriture ;
|
||||||
|
- `GitRepositoryWriter` est une capacité distincte ;
|
||||||
|
- les chemins de mutation refusent valeur vide, chemin absolu/séparateur plateforme, `.` / `..`, composant vide et `.git` ;
|
||||||
|
- les mutations représentent écriture create/update et suppression, avec rejet des chemins dupliqués dans une même requête ;
|
||||||
|
- l'identité de commit et le message sont explicites et validés ;
|
||||||
|
- la branche cible est une référence complète valide sous `refs/heads/...` ;
|
||||||
|
- l'état distant attendu représente explicitement branche absente ou révision connue ;
|
||||||
|
- le résultat de push peut représenter succès ou conflit sans rebase/merge automatique ;
|
||||||
|
- `cargo fmt --all`, check, Clippy strict et tests ciblés `knowledge-git-api` passent ;
|
||||||
|
- les tests workspace passent car la façade publique de la crate change ;
|
||||||
|
- `knowledge-git-lib`, `knowledge-app-lib`, `Web/`, PostgreSQL et adapters forge ne sont pas modifiés par cette tranche.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-alpha.3`
|
||||||
|
|
||||||
|
La tranche de création locale du commit candidat doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-alpha.3` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/alpha.2.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- `knowledge-git-api` inchangé par rapport à `alpha.2` ;
|
||||||
|
- `knowledge-git-lib` crée un worktree d'écriture isolé sous sa racine d'état sans modifier les snapshots de lecture ;
|
||||||
|
- create/update/delete limités exactement aux chemins de mutation demandés ;
|
||||||
|
- toute traversée par symlink refusée avant écriture hors worktree ;
|
||||||
|
- configuration Git système/globale pertinente neutralisée et hooks désactivés ;
|
||||||
|
- identité auteur/committer explicitement fournie par `GitWriteRequest` ;
|
||||||
|
- commit candidat vérifié comme enfant direct de la révision de base ;
|
||||||
|
- candidat retenu sous une référence backend interne avant suppression du worktree temporaire ;
|
||||||
|
- worktree temporaire supprimé après succès et après erreur possédée, mais worktree préexistant jamais effacé silencieusement ;
|
||||||
|
- aucun push distant, rebase, merge, forge, PostgreSQL ou orchestration application ajouté ;
|
||||||
|
- test d'intégration Git réel prouvant modification/création/suppression, identité explicite, base inchangée et nettoyage ;
|
||||||
|
- test Unix prouvant qu'un symlink versionné ne permet pas d'écrire hors du worktree ;
|
||||||
|
- `cargo fmt --all`, check, Clippy strict, tests ciblés `knowledge-git-lib` et tests workspace propres ;
|
||||||
|
- `knowledge-app-lib` et `Web/` sans diff, `Cargo.lock` ignoré/non suivi et staging final propre.
|
||||||
|
|
||||||
|
|
||||||
|
## Gate de `0.0.3-alpha.4`
|
||||||
|
|
||||||
|
La tranche de push gardé et concurrence doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-alpha.4` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/alpha.3.fix.1.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- `GitBackend` implémente désormais `GitRepositoryWriter` sans modifier le contrat public `knowledge-git-api` ;
|
||||||
|
- le candidat à pousser doit exister sous la référence interne contrôlée créée par `alpha.3` ;
|
||||||
|
- l'état distant est observé par le remote générique `origin`, sans API de forge ;
|
||||||
|
- une attente `GitRemoteState::Revision(r)` utilise une lease exacte sur `r` ;
|
||||||
|
- une attente `GitRemoteState::Absent` n'autorise que la création d'une branche absente ;
|
||||||
|
- un écart observé avant le push produit immédiatement `GitPushOutcome::Conflict` ;
|
||||||
|
- `--force-with-lease` protège également la course entre observation et push ;
|
||||||
|
- après rejet d'un push, une nouvelle observation traduit une divergence en conflit récupérable sans écrasement ;
|
||||||
|
- aucun rebase/merge automatique, force-push non gardé, forge, PostgreSQL ou orchestration applicative n'est ajouté ;
|
||||||
|
- tests d'intégration Git réels avec remote bare local couvrant mise à jour d'une branche existante, création sous attente d'absence et conflit concurrent sans écrasement ;
|
||||||
|
- `cargo fmt --all`, check, Clippy strict, tests ciblés `knowledge-git-lib` et tests workspace propres ;
|
||||||
|
- `knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` et `ROADMAP.md` sans diff ;
|
||||||
|
- `Cargo.lock` ignoré/non suivi et staging final propre.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-beta.1`
|
||||||
|
|
||||||
|
La tranche de réconciliation/stabilisation doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-beta.1` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/alpha.4.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- aucun diff sous `crates/` ou `Web/` : la beta ne change pas le produit ;
|
||||||
|
- `CHANGELOG.md` contient `## 0.0.3 (candidate)` construit à partir des capacités réellement acquises ;
|
||||||
|
- étude `007-V0_0_3_GIT_WRITE_RECONCILIATION.md` présente et cohérente avec l'architecture ;
|
||||||
|
- `ROADMAP.md` retient `0.0.4` comme prochaine mission potentielle et explicite les phases templates publics/admin puis Actix/Maud pour les trois sites ;
|
||||||
|
- `prompts/004-V0_0_4_START_PROMPT.md` présent sans commit stable, SHA d'archive ou rapport post-tag `v0.0.3` inventé ;
|
||||||
|
- `0.0.4` n'introduit encore aucun driver/ORM, crate PostgreSQL, migration ou dépendance pendant la gate `0.0.3` ;
|
||||||
|
- rebase/merge automatique, forge, scheduler, recherche avancée, quatrième admin et partage multi-processus restent différés ;
|
||||||
|
- `cargo fmt --all`, audits Rust, metadata/check, Clippy strict, tests Git ciblés et tests workspace passent ;
|
||||||
|
- baseline Web `npm ci`, typecheck et build Vite passent ;
|
||||||
|
- manifest Vite de production reste parsable avec l'entrée stable `src/main.ts` ;
|
||||||
|
- les trois launchers restent compilables et la RC devra au minimum rejouer les smokes HTTP complets ;
|
||||||
|
- `Cargo.lock` local reste ignoré/non suivi, package-lock Web versionné et staging final propre.
|
||||||
|
|
||||||
|
Un `PASS` autorise uniquement `0.0.3-rc.1`. Toute régression produit exige un fix beta borné ou un retour de phase ; la beta ne doit pas masquer un défaut sous une réécriture documentaire.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-rc.1`
|
||||||
|
|
||||||
|
La release candidate doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-rc.1` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/beta.1.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- scope produit gelé : aucun diff sous `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/` ou `prompts/` ;
|
||||||
|
- tous les histories antérieurs de `0.0.3` présents avec leurs statuts réels et `rc.1.md` seul delta sans history correspondant ; les contrôles de statut doivent comparer la valeur sémantique après retrait d'un éventuel balisage inline Markdown historique, sans réécrire les histories ;
|
||||||
|
- `CHANGELOG.md` toujours en `## 0.0.3 (candidate)` et `ROADMAP.md` conservant `0.0.3` engagée avec l'horizon validé en beta ;
|
||||||
|
- handoff `prompts/004-V0_0_4_START_PROMPT.md` présent et toujours sans commit/SHA futur inventé ;
|
||||||
|
- `cargo fmt --all`, audits Rust, metadata/check, Clippy strict, tests Git ciblés et tests workspace passent ;
|
||||||
|
- baseline Web `npm ci`, typecheck, build Vite et manifest de production passent ;
|
||||||
|
- les trois launchers passent un smoke HTTP de production sur `/healthz`, `/` et le service effectif des assets référencés par le manifest ;
|
||||||
|
- PostgreSQL, migrations, forge, rebase/merge automatique, auth/admin et workflow produit différés restent absents ;
|
||||||
|
- `Cargo.lock` local reste ignoré/non suivi, package-lock Web versionné et staging final propre.
|
||||||
|
|
||||||
|
Un `PASS` autorise uniquement `0.0.3-rel.1`. Toute régression fonctionnelle exige un fix RC borné ou un retour de phase approprié ; aucune fonctionnalité ne peut être ajoutée en RC.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-rc.1.fix.1`
|
||||||
|
|
||||||
|
Le fix de validation RC doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-rc.1.fix.1` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/rc.1.md` présent avec statut `FAIL` et committé avant application ;
|
||||||
|
- `alpha.1` toujours historiquement `PARTIAL` sans modification de son fichier ;
|
||||||
|
- lecture robuste des statuts historiques, qu'ils soient bruts ou entourés de backticks Markdown ;
|
||||||
|
- aucun diff produit sous `crates/` ou `Web/` et aucun changement du changelog, de la roadmap, de l'architecture, des études, des règles ou des prompts ;
|
||||||
|
- audits Markdown/Rust locaux, `cargo fmt`, metadata/check, Clippy strict, tests Git ciblés et tests workspace propres ;
|
||||||
|
- présentation candidate `0.0.3` et handoff `0.0.4` inchangés ;
|
||||||
|
- `Cargo.lock` local ignoré/non suivi, package-lock Web versionné et staging final propre.
|
||||||
|
|
||||||
|
Un `PASS` autorise `0.0.3-rel.1`. Le fix ne rejoue pas les smokes HTTP ni le build Web déjà `PASS` sur `rc.1`, puisqu'aucun fichier produit ou Web n'est modifié ; la gate stable `rel.1` les rejouera avant publication.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-rel.1`
|
||||||
|
|
||||||
|
La fermeture stable pré-tag doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3` et version workspace identique ;
|
||||||
|
- history `history/0.0.3/rc.1.fix.1.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- `rel.1` comme seul delta `0.0.3` sans history correspondant pendant sa gate ;
|
||||||
|
- changelog stable `## 0.0.3` sans marqueur `candidate` ;
|
||||||
|
- roadmap avec `0.0.3` terminée, `0.0.4` engagée et `0.0.5` à `0.0.10` conservées comme horizons révisables ;
|
||||||
|
- README, index documentaire et plan `0.0.3` présentés comme stables/historiques ;
|
||||||
|
- prompt `004` présent, sans commit/SHA post-tag inventé et sans consultation PGR automatique ;
|
||||||
|
- `cargo fmt --all`, audits Rust, metadata/check, Clippy strict, tests Git ciblés et tests workspace passent ;
|
||||||
|
- baseline Web `npm ci`, typecheck, build Vite et manifest passent ;
|
||||||
|
- les trois smokes HTTP production passent sur `/healthz`, `/` et les assets du manifest ;
|
||||||
|
- PostgreSQL, migrations, forge, auth/admin et workflows encore différés restent absents du code produit de `0.0.3` ;
|
||||||
|
- `Cargo.lock` local reste ignoré/non suivi, package-lock Web versionné et staging final propre ;
|
||||||
|
- aucun tag stable ni rapport post-tag n'est déclaré comme existant avant le history `rel.1` `PASS` et le commit stable.
|
||||||
|
|
||||||
|
Un `PASS` de `rel.1` autorise le commit produit exact `0.0.3`, le push, puis seulement la création/push du tag annoté `v0.0.3`, le téléchargement de l'archive réellement servie et la production séparée du rapport `sasedev-knowledge-0.0.3-publish.001-report.md`.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-rel.1.fix.1`
|
||||||
|
|
||||||
|
Le fix de complétion de preuve stable doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3` et version workspace identique ; la clôture stable reste la même version produit ;
|
||||||
|
- history `history/0.0.3/rel.1.md` présent avec statut `PARTIAL` et committé avant application ;
|
||||||
|
- aucune modification sous `crates/`, `Web/`, `CHANGELOG.md`, `ROADMAP.md`, `docs/architecture/`, `docs/studies/`, `docs/rules/` ou `prompts/` ;
|
||||||
|
- fichier `cargo metadata --format-version 1 --no-deps` produit puis ensemble exact des neuf packages/workspace members vérifié explicitement ;
|
||||||
|
- hygiène texte explicite `PASS` sur README/RULES/ROADMAP/CHANGELOG/docs/prompts/deltas/history ;
|
||||||
|
- audits Markdown locaux propres après ajout du fix ;
|
||||||
|
- preuves `rel.1` conservées : formatage/audits Rust, Cargo/Clippy/tests, Web/Vite et trois smokes HTTP tous `PASS` ;
|
||||||
|
- tag `v0.0.3` et rapport post-tag toujours absents avant le history `rel.1.fix.1` `PASS` ;
|
||||||
|
- `Cargo.lock` local ignoré/non suivi, package-lock Web versionné et staging final propre.
|
||||||
|
|
||||||
|
Le premier `PASS` d'une clôture `rel.N[/fix.M]` autorise alors le commit produit exact `0.0.3`, le push, puis le tag annoté `v0.0.3` et la vérification post-tag.
|
||||||
|
|
||||||
## PostgreSQL
|
## PostgreSQL
|
||||||
|
|
||||||
Aucune gate PostgreSQL n'est créée avant matérialisation d'une dépendance ou d'un schéma réel.
|
Aucune gate PostgreSQL n'est créée avant matérialisation d'une dépendance ou d'un schéma réel.
|
||||||
@@ -840,3 +990,83 @@ La fermeture stable pré-tag doit prouver :
|
|||||||
- fichiers texte gouvernés terminés par exactement un LF ;
|
- fichiers texte gouvernés terminés par exactement un LF ;
|
||||||
- `git diff --check` et `git diff --cached --check` propres ;
|
- `git diff --check` et `git diff --cached --check` propres ;
|
||||||
- aucun tag stable ou rapport post-tag déclaré comme existant avant le history `rel.1` `PASS` et le commit stable.
|
- aucun tag stable ou rapport post-tag déclaré comme existant avant le history `rel.1` `PASS` et le commit stable.
|
||||||
|
|
||||||
|
## Gate de `0.0.3-alpha.1`
|
||||||
|
|
||||||
|
La première tranche de `0.0.3` est une gate de cadrage sans code produit. Elle doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.3-alpha.1` et `[workspace.package].version` identique ;
|
||||||
|
- baseline Knowledge `v0.0.2` identifiée par commit `183d354a67bec6d2d512c4898f5189759a6120b0`, SHA-256 `0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce` et rapport `publish.001 PASS` ;
|
||||||
|
- aucun chargement/adoption PGR nouveau et PGR `v0.1.3` conservé uniquement comme provenance historique ;
|
||||||
|
- `ROADMAP.md` engage uniquement `0.0.3` après les stables existantes et ne réserve aucune version ultérieure ;
|
||||||
|
- étude `006` et plan `003` présents ;
|
||||||
|
- mission bornée à l'écriture Git contrôlée et au push gardé ;
|
||||||
|
- contrat d'écriture prévu distinct de `GitRepository` afin de préserver les consommateurs lecture seule ;
|
||||||
|
- rebase/merge automatique, PostgreSQL, forge, auth/admin et workflow complet de proposition encore hors scope ;
|
||||||
|
- aucun diff sous `crates/` ou `Web/` ;
|
||||||
|
- aucun history `history/0.0.3/alpha.1.md` présent avant la gate ;
|
||||||
|
- aucun nouveau package Cargo ou npm et aucune nouvelle dépendance ;
|
||||||
|
- audits Markdown et hygiène texte propres ;
|
||||||
|
- `cargo fmt --all` puis `cargo fmt --all -- --check` exécutés avant les audits Rust ;
|
||||||
|
- audits Rust locaux propres ;
|
||||||
|
- `cargo metadata`, `cargo check --workspace`, Clippy strict, test d'intégration Git de lecture et tests workspace propres ;
|
||||||
|
- `Cargo.lock` généré localement si Cargo l'exige, mais ignoré/non suivi ;
|
||||||
|
- `package-lock.json` inchangé et versionné ;
|
||||||
|
- `git diff --cached --check` propre avant commit.
|
||||||
|
|
||||||
|
La baseline Web n'est pas rejouée automatiquement dans cette tranche, car aucun fichier sous `Web/` ni contrat runtime Web n'est modifié et la stable `0.0.2` a déjà acquis sa gate complète.
|
||||||
|
|
||||||
|
Un `PASS` de `0.0.3-alpha.1` autorise uniquement la préparation de `0.0.3-alpha.2`. Un autre statut doit d'abord être conservé dans son history puis conduire à un fix borné ou à un REPLAN.
|
||||||
|
|
||||||
|
## Gate de `0.0.4-alpha.1`
|
||||||
|
|
||||||
|
La première tranche de `0.0.4` est une gate de cadrage sans code produit, dépendance PostgreSQL Rust ni migration. Elle doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.4-alpha.1` et `[workspace.package].version` identique ;
|
||||||
|
- baseline `v0.0.3` identifiée par commit `b3823972449150b72905930b806d4d15471cddce`, SHA-256 `82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d` et `publish.001 = PASS` ;
|
||||||
|
- douze deltas et douze histories `0.0.3` appariés, avec `rel.1.fix.1 = PASS` ;
|
||||||
|
- aucun chargement/adoption PGR nouveau ;
|
||||||
|
- étude `008` et plan `004` présents ;
|
||||||
|
- `tokio-postgres`, `deadpool-postgres`, Refinery, `knowledge-store-postgres-lib`, le schéma minimal et la stratégie de test explicitement décidés ;
|
||||||
|
- aucun `knowledge-store-postgres-lib`, aucune migration et aucune nouvelle dépendance réellement ajoutés dans `alpha.1` ;
|
||||||
|
- aucun diff sous `crates/` ou `Web/` et aucun changement du changelog stable `0.0.3` ;
|
||||||
|
- roadmap/horizon `0.0.5` à `0.0.10` inchangés parce que l'étude ne révèle aucune dépendance fausse ;
|
||||||
|
- audits Markdown et hygiène texte propres ;
|
||||||
|
- `cargo fmt --all` puis `cargo fmt --all -- --check` exécutés avant les audits Rust ;
|
||||||
|
- audits Rust locaux propres ;
|
||||||
|
- `cargo metadata` et `cargo check --workspace` propres ;
|
||||||
|
- `Cargo.lock` local ignoré/non suivi et `package-lock.json` Web inchangé/versionné ;
|
||||||
|
- `git diff --cached --check` propre avant commit.
|
||||||
|
|
||||||
|
Clippy, tests workspace, npm/Vite et smokes HTTP ne sont pas requis par cette tranche : elle ne modifie ni source Rust, ni dépendance, ni frontend, ni runtime. La gate vérifie néanmoins `cargo check --workspace` après la promotion de version du manifest.
|
||||||
|
|
||||||
|
Un `PASS` autorise uniquement `0.0.4-alpha.2`. Un autre statut est historisé puis conduit à un fix borné ou à un REPLAN.
|
||||||
|
|
||||||
|
## Gate de `0.0.4-alpha.2`
|
||||||
|
|
||||||
|
La deuxième tranche matérialise le premier owner PostgreSQL réel. Elle doit prouver :
|
||||||
|
|
||||||
|
- `VERSION = 0.0.4-alpha.2` et `[workspace.package].version` identique ;
|
||||||
|
- `history/0.0.4/alpha.1.md` présent avec statut `PASS` et committé avant application ;
|
||||||
|
- workspace composé exactement de dix packages, avec `knowledge-store-postgres-lib` comme seul nouveau membre ;
|
||||||
|
- `tokio-postgres 0.7.18` avec `runtime`, `deadpool-postgres 0.14.2` avec `rt_tokio_1`, Refinery `0.9.2` avec `tokio-postgres` et Tokio test `1.53.1` ajouté uniquement à la crate PostgreSQL ;
|
||||||
|
- façade crate-root limitée à `PostgresStoreConfig`, `PostgresStore` et `PostgresStoreError` ;
|
||||||
|
- configuration injectée, sans lecture d'environnement ni getter public de l'URL PostgreSQL ;
|
||||||
|
- pool `deadpool-postgres` borné par `max_pool_size`, driver `tokio-postgres` et première connexion `NoTls` ;
|
||||||
|
- migration `V1__initial_sources.sql` embarquée via Refinery et appliquée seulement par `PostgresStore::apply_migrations` ;
|
||||||
|
- tables `knowledge_sources` et `knowledge_source_sync_state` avec contrainte des familles et cascade de suppression de l'état de synchronisation ;
|
||||||
|
- aucun corps de contenu, credential Git, compte, proposition, job ou projection finale ajouté au schéma ;
|
||||||
|
- test unitaire de configuration sans PostgreSQL ;
|
||||||
|
- test d'intégration PostgreSQL réel isolé dans un schéma unique et exécuté explicitement avec `KNOWLEDGE_POSTGRES_TEST_URL` ;
|
||||||
|
- absence de PostgreSQL réel pendant cette gate classée `BLOCKED`, jamais masquée par un skip ;
|
||||||
|
- aucun diff sous `Web/`, les trois launchers, `knowledge-app-lib`, `knowledge-git-api`, `knowledge-git-lib`, `knowledge-sync-api` ou `knowledge-domain-lib` ;
|
||||||
|
- audits Markdown et hygiène texte propres ;
|
||||||
|
- `cargo fmt --all` puis `cargo fmt --all -- --check` exécutés avant les audits Rust ;
|
||||||
|
- audits Rust locaux propres sur les dix crates ;
|
||||||
|
- `cargo metadata`, `cargo check`, Clippy strict, tests ciblés store et tests workspace propres ;
|
||||||
|
- `Cargo.lock` local ignoré/non suivi et `package-lock.json` Web inchangé/versionné ;
|
||||||
|
- `git diff --cached --check` propre avant commit.
|
||||||
|
|
||||||
|
La gate ne rejoue pas npm/Vite/HTTP : aucun fichier Web, launcher ou contrat runtime Web n'est modifié. Le test PostgreSQL marqué `#[ignore]` reste exclu des tests workspace ordinaires et est rejoué explicitement avec `--ignored` dans la gate afin que la dépendance externe soit volontaire et visible.
|
||||||
|
|
||||||
|
Un `PASS` autorise uniquement `0.0.4-alpha.3`. Un `FAIL` de code ou migration produit un fix borné ; une incompatibilité de toolchain/dépendances, une impossibilité d'isoler correctement le test PostgreSQL ou un besoin de modifier l'ownership déclenche un REPLAN.
|
||||||
|
|||||||
134
history/0.0.3/alpha.1.fix.1.md
Normal file
134
history/0.0.3/alpha.1.fix.1.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.1.fix.1 — cycle documentaire et horizon
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PARTIAL
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.1.fix.1` exécutée le `2026-09-29` acquiert toutes les preuves transmises jusqu'aux tests workspace, mais le transcript reçu s'arrête avant le bloc final explicitement requis par le delta.
|
||||||
|
|
||||||
|
Aucune commande observée n'a échoué. Le statut exact est donc `PARTIAL`, et non `PASS` ni `FAIL`.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.1` `PARTIAL` a été historisé, committé et poussé avant application de `alpha.1.fix.1` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.1.md PARTIAL
|
||||||
|
commit a74a198
|
||||||
|
message 0.0.3-alpha.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `183d354` à `a74a198`.
|
||||||
|
|
||||||
|
## Version et audits documentaires
|
||||||
|
|
||||||
|
Les contrôles observés passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.1.fix.1
|
||||||
|
Cargo workspace version 0.0.3-alpha.1.fix.1
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (102 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 102 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 102 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
L'ordre local est respecté :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (32 file(s))
|
||||||
|
Rust item-spacing audit clean (32 file(s))
|
||||||
|
Rust module-order audit clean (32 file(s))
|
||||||
|
Rust trait-import-block audit clean (32 file(s))
|
||||||
|
Rust re-export-block audit clean (32 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
Les neuf packages compilent en `0.0.3-alpha.1.fix.1`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 1 PASS
|
||||||
|
knowledge-git-lib 8 unit PASS + 2 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les deux tests d'intégration Git réels continuent de prouver les chemins de lecture déjà acquis : refresh/résolution/snapshot, rejet d'une référence invalide et rejet d'un snapshot existant sale.
|
||||||
|
|
||||||
|
## Correction documentaire acquise dans le contenu
|
||||||
|
|
||||||
|
Le delta appliqué porte bien la correction décidée après `alpha.1` :
|
||||||
|
|
||||||
|
- l'entrée candidate `0.0.3` a été retirée du changelog ;
|
||||||
|
- le changelog est désormais défini comme artefact de stabilisation plutôt que de démarrage ;
|
||||||
|
- le prompt de `N+1` est normalement préparé pendant les dernières phases de `N` après revue d'horizon ;
|
||||||
|
- `prompts/003-V0_0_3_START_PROMPT.md` reste le rattrapage historique nécessaire pour `0.0.3` ;
|
||||||
|
- la roadmap distingue la release engagée d'un horizon prévisionnel révisable ;
|
||||||
|
- le plan `0.0.3` prévoit une revue d'horizon en `beta.1`.
|
||||||
|
|
||||||
|
La revue opérateur postérieure précise que l'horizon actuel reste volontairement incomplet : les futures revues devront aussi intégrer explicitement les phases de templates/frontends publics et admin des trois sites PGR, Snippets et Howtos, leur intégration Actix Web/Maud, ainsi que Git et PostgreSQL. Cette précision est un besoin d'horizon futur, pas un échec de la correction courante.
|
||||||
|
|
||||||
|
## Preuves manquantes
|
||||||
|
|
||||||
|
Le delta `alpha.1.fix.1` exigeait encore après les tests workspace :
|
||||||
|
|
||||||
|
```text
|
||||||
|
audit Python du cycle documentaire/horizon
|
||||||
|
hygiène texte des fichiers gouvernés
|
||||||
|
absence de diff sous crates/ et Web/
|
||||||
|
absence du history alpha.1.fix.1 pendant la gate
|
||||||
|
politique Cargo.lock
|
||||||
|
présence du package-lock Web
|
||||||
|
git add -A
|
||||||
|
git diff --cached --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
Ces résultats ne figurent pas dans le transcript opérateur reçu. Ils ne sont donc ni inventés ni assimilés à un PASS.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.1.fix.1` est `PARTIAL`.
|
||||||
|
|
||||||
|
La correction documentaire et la non-régression Rust observée sont acquises, mais la tranche ne peut pas être clôturée en `PASS` sans les preuves finales explicitement requises.
|
||||||
|
|
||||||
|
La transition autorisée est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
appliquer ce history
|
||||||
|
-> git add / diff --cached --check / status
|
||||||
|
-> commit exact "0.0.3-alpha.1.fix.1"
|
||||||
|
-> push origin master
|
||||||
|
-> vérifier le workspace propre
|
||||||
|
-> appliquer seulement ensuite 0.0.3-alpha.1.fix.2
|
||||||
|
```
|
||||||
|
|
||||||
|
`alpha.1.fix.2` reste une correction de validation bornée : aucune fonctionnalité Git, Web ou PostgreSQL ne doit y être ajoutée. Un `PASS` de ce fix autorisera `alpha.2`.
|
||||||
143
history/0.0.3/alpha.1.fix.2.md
Normal file
143
history/0.0.3/alpha.1.fix.2.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.1.fix.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.1.fix.2 — complétion de la preuve opérateur
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.1.fix.2` exécutée le `2026-09-29` est complète et propre. Elle ferme uniquement la preuve restée incomplète après `alpha.1.fix.1` et autorise l'ouverture fonctionnelle de `alpha.2`.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.1.fix.1` `PARTIAL` a été historisé, committé et poussé avant application de `alpha.1.fix.2` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.1.fix.1.md PARTIAL
|
||||||
|
commit dd63e85
|
||||||
|
message 0.0.3-alpha.1.fix.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `a74a198` à `dd63e85`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
Les contrôles de version et Markdown passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.1.fix.2
|
||||||
|
Cargo workspace version 0.0.3-alpha.1.fix.2
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (104 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 104 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 104 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
La séquence locale est respectée et propre :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (32 file(s))
|
||||||
|
Rust item-spacing audit clean (32 file(s))
|
||||||
|
Rust module-order audit clean (32 file(s))
|
||||||
|
Rust trait-import-block audit clean (32 file(s))
|
||||||
|
Rust re-export-block audit clean (32 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
Les neuf packages compilent en `0.0.3-alpha.1.fix.2`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 1 PASS
|
||||||
|
knowledge-git-lib 8 unit PASS + 2 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
## Complétion de la preuve documentaire
|
||||||
|
|
||||||
|
L'audit spécifique de fin de gate passe :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.1.fix.2 completion audit: clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Il confirme notamment :
|
||||||
|
|
||||||
|
- `history/0.0.3/alpha.1.fix.1.md = PARTIAL` conservé sans réécriture ;
|
||||||
|
- aucune section `0.0.3` prématurée dans `CHANGELOG.md` ;
|
||||||
|
- `ROADMAP.md` contient `0.0.3` engagée et `0.0.4` à `0.0.7` comme horizon prévisionnel ;
|
||||||
|
- le changelog est défini comme artefact de stabilisation et non de démarrage ;
|
||||||
|
- le prompt `N+1` est normalement préparé pendant les dernières phases de `N` ;
|
||||||
|
- `prompts/003-V0_0_3_START_PROMPT.md` reste le rattrapage historique attendu ;
|
||||||
|
- le plan autorise `0.0.3-alpha.2` seulement après le présent PASS ;
|
||||||
|
- aucune crate Gitea/PostgreSQL et aucun répertoire `migrations/` ne sont introduits.
|
||||||
|
|
||||||
|
La revue d'horizon plus large des trois sites PGR, Snippets et Howtos reste volontairement prévue plus tard dans la release ; elle n'est pas absorbée par ce fix de validation.
|
||||||
|
|
||||||
|
## Hygiène texte, scope et lockfiles
|
||||||
|
|
||||||
|
L'hygiène texte passe sur les `104` fichiers gouvernés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.1.fix.2 text hygiene: clean (104 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
Les contrôles finaux passent également :
|
||||||
|
|
||||||
|
- aucun diff sous `crates/` ni `Web/` ;
|
||||||
|
- aucun history `alpha.1.fix.2` présent pendant la gate ;
|
||||||
|
- `Cargo.lock` présent localement, ignoré et non suivi ;
|
||||||
|
- `Web/knowledge-template-lab/package-lock.json` présent ;
|
||||||
|
- staging final sans erreur via `git diff --cached --check`.
|
||||||
|
|
||||||
|
Le staging observé est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/alpha.1.fix.2.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.1.fix.2` est `PASS`.
|
||||||
|
|
||||||
|
La séquence de cadrage/correction est fermée. La transition autorisée est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
appliquer ce history
|
||||||
|
-> git add / diff --cached --check / status
|
||||||
|
-> commit exact "0.0.3-alpha.1.fix.2"
|
||||||
|
-> push origin master
|
||||||
|
-> vérifier le workspace propre
|
||||||
|
-> appliquer seulement ensuite 0.0.3-alpha.2
|
||||||
|
```
|
||||||
|
|
||||||
|
`alpha.2` peut maintenant matérialiser le contrat d'écriture Git provider-neutral dans `knowledge-git-api`, sans implémenter encore le worktree d'écriture ni le push distant réel.
|
||||||
84
history/0.0.3/alpha.1.md
Normal file
84
history/0.0.3/alpha.1.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.1 — cadrage de l'écriture Git contrôlée
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
`PARTIAL`
|
||||||
|
|
||||||
|
## Baseline observée
|
||||||
|
|
||||||
|
La tranche a été appliquée sur la stable autoritaire `v0.0.2` puis a porté `VERSION` et le workspace Cargo à :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuves opérateur reçues
|
||||||
|
|
||||||
|
Les preuves transmises le `2026-09-29` montrent :
|
||||||
|
|
||||||
|
- contrôle `VERSION` / `workspace.package.version` conforme ;
|
||||||
|
- `git diff --check` propre ;
|
||||||
|
- audits Markdown headers/liens/tables propres ;
|
||||||
|
- `cargo fmt --all` puis `cargo fmt --all -- --check` PASS ;
|
||||||
|
- audits Rust structurels propres sur les neuf crates / trente-deux fichiers concernés ;
|
||||||
|
- `cargo metadata --format-version 1 --no-deps` exécuté ;
|
||||||
|
- `cargo check --workspace` PASS ;
|
||||||
|
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` PASS ;
|
||||||
|
- `cargo test -p knowledge-git-lib --test git_read_path` : `2 passed` ;
|
||||||
|
- `cargo test --workspace --all-targets --all-features` : tous les tests observés PASS.
|
||||||
|
|
||||||
|
Les comptes observés dans la gate workspace restent cohérents avec la stable :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 1 PASS
|
||||||
|
knowledge-git-lib 8 unit + 2 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun, compilation PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limites de la gate reçue
|
||||||
|
|
||||||
|
Le retour fourni s'arrête après `cargo test --workspace --all-targets --all-features`.
|
||||||
|
|
||||||
|
Les commandes de fin annoncées dans le delta `alpha.1` ne sont donc pas présentées comme exécutées :
|
||||||
|
|
||||||
|
- audit Python spécifique de cadrage `alpha.1` ;
|
||||||
|
- audit d'hygiène texte ;
|
||||||
|
- vérifications finales `crates/` / `Web/`, lockfiles et absence de history prématuré ;
|
||||||
|
- `git add -A`, `git diff --cached --check` et `git status --short`.
|
||||||
|
|
||||||
|
Cette absence suffit à empêcher un statut `PASS` intégral de la gate telle qu'elle était spécifiée.
|
||||||
|
|
||||||
|
## Revue opérateur du cycle documentaire
|
||||||
|
|
||||||
|
La revue manuelle a en outre identifié trois corrections de gouvernance/documentation à matérialiser avant de poursuivre le développement fonctionnel :
|
||||||
|
|
||||||
|
1. `CHANGELOG.md` ne doit pas être rédigé au démarrage d'une release ; il doit synthétiser les changements réellement acquis vers les phases de stabilisation/fin de release ;
|
||||||
|
2. le prompt de démarrage de la release suivante doit normalement être préparé pendant les dernières phases de la release en cours, après revue d'horizon ; l'ajout de `prompts/003-V0_0_3_START_PROMPT.md` pendant `0.0.3-alpha.1` est accepté uniquement comme rattrapage parce que `0.0.2` avait été fermée sans ce handoff ;
|
||||||
|
3. `ROADMAP.md` doit montrer à la fois la release en cours et un horizon borné des releases suivantes, en distinguant clairement l'engagement courant des versions d'horizon encore révisables.
|
||||||
|
|
||||||
|
Ces défauts ne remettent pas en cause la mission Git choisie pour `0.0.3`, mais rendent le cadrage documentaire incomplet.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.1` est conservée avec le statut `PARTIAL`.
|
||||||
|
|
||||||
|
La correction reste bornée au workflow documentaire et utilise :
|
||||||
|
|
||||||
|
```text
|
||||||
|
0.0.3-alpha.1.fix.1
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fix doit :
|
||||||
|
|
||||||
|
- retirer la section candidate `0.0.3` prématurée du changelog ;
|
||||||
|
- transformer la roadmap en roadmap courante + horizon prévisionnel ;
|
||||||
|
- rendre explicite chez les owners locaux le moment de rédaction du changelog et du prompt de release suivante ;
|
||||||
|
- mettre à jour le plan `0.0.3` pour refléter cette correction sans modifier la mission Git ;
|
||||||
|
- ne modifier aucun code sous `crates/` ni aucun fichier sous `Web/`.
|
||||||
142
history/0.0.3/alpha.2.md
Normal file
142
history/0.0.3/alpha.2.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.2 — contrat d'écriture Git provider-neutral
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.2` exécutée le `2026-09-29` est complète et propre. Elle valide la frontière publique d'écriture Git provider-neutral et autorise `0.0.3-alpha.3` pour le worktree d'écriture isolé et la création réelle du commit candidat.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.1.fix.2` `PASS` a été historisé, committé et poussé avant application de `alpha.2` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.1.fix.2.md PASS
|
||||||
|
commit 3004e4b
|
||||||
|
message 0.0.3-alpha.1.fix.2
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `dd63e85` à `3004e4b`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
Les contrôles de version et Markdown passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.2
|
||||||
|
Cargo workspace version 0.0.3-alpha.2
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (106 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 106 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 106 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
La séquence locale imposée a été respectée :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (33 file(s))
|
||||||
|
Rust item-spacing audit clean (33 file(s))
|
||||||
|
Rust module-order audit clean (33 file(s))
|
||||||
|
Rust trait-import-block audit clean (33 file(s))
|
||||||
|
Rust re-export-block audit clean (33 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
Les neuf packages compilent en `0.0.3-alpha.2` et Clippy ne produit aucun warning avec `-D warnings`.
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-api --all-targets --all-features PASS (7)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit PASS + 2 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les sept tests `knowledge-git-api` valident notamment :
|
||||||
|
|
||||||
|
- la séparation du contrat d'écriture du contrat de lecture ;
|
||||||
|
- l'absence de dépendance à une forge ;
|
||||||
|
- la validation des chemins de mutation et la protection de `.git` ;
|
||||||
|
- l'identité de commit explicite ;
|
||||||
|
- les branches bornées à `refs/heads/...` ;
|
||||||
|
- les états distants absent/révision ;
|
||||||
|
- le rejet des requêtes sans mutation, messages invalides et chemins dupliqués.
|
||||||
|
|
||||||
|
## Audit spécifique d'écriture
|
||||||
|
|
||||||
|
L'audit de tranche passe :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.2 write-contract audit: clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Il confirme notamment :
|
||||||
|
|
||||||
|
- la présence et le réexport des dix types/traits d'écriture attendus ;
|
||||||
|
- `GitRepository` de lecture inchangé et sans méthodes `create_candidate` / `push_candidate` ;
|
||||||
|
- la validation `refs/heads/...`, `ProtectedGitPath` et `DuplicateMutationPath` ;
|
||||||
|
- l'absence de dépendances Actix, Tokio, forge, PostgreSQL ou ORM dans `knowledge-git-api` ;
|
||||||
|
- `CHANGELOG.md` toujours sans section prématurée `0.0.3` ;
|
||||||
|
- absence de history `alpha.2` avant staging ;
|
||||||
|
- absence de crates Gitea/PostgreSQL et de migrations.
|
||||||
|
|
||||||
|
## Hygiène et scope
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.2 text hygiene: clean (106 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
La gate confirme également :
|
||||||
|
|
||||||
|
- aucun diff sous `crates/knowledge-git-lib`, `crates/knowledge-app-lib` ou `Web/` ;
|
||||||
|
- `Cargo.lock` local présent, ignoré et non suivi ;
|
||||||
|
- `Web/knowledge-template-lab/package-lock.json` présent ;
|
||||||
|
- `git diff --cached --check` propre.
|
||||||
|
|
||||||
|
Le staging final contient exactement les changements attendus de la tranche :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M VERSION
|
||||||
|
M crates/knowledge-git-api/src/lib.rs
|
||||||
|
A crates/knowledge-git-api/src/write.rs
|
||||||
|
M crates/knowledge-git-api/unit_tests/lib.rs
|
||||||
|
A deltas/0.0.3/alpha.2.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.2` est `PASS`.
|
||||||
|
|
||||||
|
La frontière provider-neutral est acquise. La tranche suivante peut implémenter dans `knowledge-git-lib` le worktree d'écriture isolé, les mutations confinées, l'identité explicite, la neutralisation des hooks/configurations pertinentes et la création d'une révision candidate vérifiée. Le push distant gardé reste réservé à `alpha.4`.
|
||||||
123
history/0.0.3/alpha.3.fix.1.md
Normal file
123
history/0.0.3/alpha.3.fix.1.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.3.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.3.fix.1 — correction de l'assertion du test candidat
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.3.fix.1` exécutée le `2026-09-29` est complète et propre. Le défaut de compilation `E0369` de `alpha.3` est corrigé sans modification du code produit : l'assertion du test d'intégration vérifie désormais le cas `Ok("first\n")` sans exiger `PartialEq` sur `std::io::Error`.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.3` `FAIL` a été historisé, committé et poussé avant application du fix :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.3.md FAIL
|
||||||
|
commit c3a8256
|
||||||
|
message 0.0.3-alpha.3
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `88a63b0` à `c3a8256`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.3.fix.1
|
||||||
|
Cargo workspace version 0.0.3-alpha.3.fix.1
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (110 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 110 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 110 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (35 file(s))
|
||||||
|
Rust item-spacing audit clean (35 file(s))
|
||||||
|
Rust module-order audit clean (35 file(s))
|
||||||
|
Rust trait-import-block audit clean (35 file(s))
|
||||||
|
Rust re-export-block audit clean (35 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
La correction compile et l'ensemble des gates Rust passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 4 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les deux tests d'écriture réels prouvent maintenant :
|
||||||
|
|
||||||
|
- create/update/delete exacts dans le candidat ;
|
||||||
|
- identité auteur/committer explicite ;
|
||||||
|
- parent candidat égal à la base demandée ;
|
||||||
|
- référence candidate interne conservée ;
|
||||||
|
- snapshot de lecture laissé inchangé ;
|
||||||
|
- nettoyage du worktree temporaire ;
|
||||||
|
- refus d'une traversée par symlink hors du worktree.
|
||||||
|
|
||||||
|
## Contrôles spécifiques
|
||||||
|
|
||||||
|
Le contrôle spécifique a été exécuté avec succès puis rejoué commande par commande après un problème de copier/coller terminal. Les deux passages produisent les mêmes résultats :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.3.fix.1 compile-fix audit: clean
|
||||||
|
Knowledge 0.0.3-alpha.3.fix.1 text hygiene: clean (110 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
La gate confirme également :
|
||||||
|
|
||||||
|
- `history/0.0.3/alpha.3.md = FAIL` conservé ;
|
||||||
|
- ancienne assertion `assert_eq!` absente ;
|
||||||
|
- nouvelle assertion `matches!(snapshot_content.as_deref(), Ok("first\n"))` présente ;
|
||||||
|
- aucun diff sous le code produit `knowledge-git-lib/src`, ses unit tests, `knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` ou `ROADMAP.md` ;
|
||||||
|
- crates Gitea/PostgreSQL et migrations toujours absentes ;
|
||||||
|
- `Cargo.lock` local présent, ignoré et non suivi ;
|
||||||
|
- package-lock Web présent ;
|
||||||
|
- `git diff --cached --check` propre.
|
||||||
|
|
||||||
|
Le staging final est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M VERSION
|
||||||
|
M crates/knowledge-git-lib/tests/git_write_candidate.rs
|
||||||
|
A deltas/0.0.3/alpha.3.fix.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.3.fix.1` est `PASS`.
|
||||||
|
|
||||||
|
La création locale du candidat est acquise. `0.0.3-alpha.4` peut implémenter l'observation distante et le push gardé avec comparaison-et-échange, conflit récupérable et tests sur remote bare local, sans introduire forge, PostgreSQL, rebase/merge automatique ni workflow applicatif de proposition.
|
||||||
149
history/0.0.3/alpha.3.md
Normal file
149
history/0.0.3/alpha.3.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.3.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.3 — worktree d'écriture et commit candidat
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
FAIL
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.3` exécutée le `2026-09-29` échoue sur la compilation du nouveau test d'intégration `git_write_candidate`. Le défaut est limité à une assertion de test qui tente de comparer directement deux `Result<String, std::io::Error>` alors que `std::io::Error` n'implémente pas `PartialEq`.
|
||||||
|
|
||||||
|
L'implémentation produit passe `cargo check --workspace`; les audits structurels et spécifiques de la tranche restent propres. Le statut demeure néanmoins `FAIL`, car Clippy `--all-targets` et les tests requis ne compilent pas.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.2` `PASS` a été historisé, committé et poussé avant application de `alpha.3` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.2.md PASS
|
||||||
|
commit 88a63b0
|
||||||
|
message 0.0.3-alpha.2
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `3004e4b` à `88a63b0`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
Les contrôles initiaux passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.3
|
||||||
|
Cargo workspace version 0.0.3-alpha.3
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (108 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 108 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 108 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (35 file(s))
|
||||||
|
Rust item-spacing audit clean (35 file(s))
|
||||||
|
Rust module-order audit clean (35 file(s))
|
||||||
|
Rust trait-import-block audit clean (35 file(s))
|
||||||
|
Rust re-export-block audit clean (35 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
`cargo metadata` et `cargo check --workspace` passent. La première défaillance obligatoire survient pendant Clippy :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
--all-features -- -D warnings FAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
Erreur observée :
|
||||||
|
|
||||||
|
```text
|
||||||
|
error[E0369]: binary operation `==` cannot be applied to type
|
||||||
|
`Result<String, std::io::Error>`
|
||||||
|
|
||||||
|
crates/knowledge-git-lib/tests/git_write_candidate.rs:253
|
||||||
|
assert_eq!(snapshot_content, Ok(String::from("first\n")));
|
||||||
|
|
||||||
|
std::io::Error does not implement PartialEq
|
||||||
|
```
|
||||||
|
|
||||||
|
Le chemin de lecture historique reste propre :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Le nouveau test ciblé et la gate workspace reproduisent le même défaut de compilation :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate FAIL E0369
|
||||||
|
cargo test --workspace --all-targets --all-features FAIL E0369
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun échec runtime du worktree/candidat n'est donc observé dans cette gate : `git_write_candidate` ne peut pas encore être exécuté jusqu'à ses assertions.
|
||||||
|
|
||||||
|
## Audits spécifiques après l'échec
|
||||||
|
|
||||||
|
Les contrôles statiques et de scope exécutés après l'échec passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-alpha.3 candidate-worktree audit: clean
|
||||||
|
Knowledge 0.0.3-alpha.3 text hygiene: clean (108 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
Ils confirment notamment :
|
||||||
|
|
||||||
|
- le contrat `alpha.2` historisé `PASS` ;
|
||||||
|
- la présence du worktree candidat, des contrôles de symlink, de l'identité explicite, de `--no-verify`, `--no-gpg-sign`, `HEAD^1` et de la référence candidate interne ;
|
||||||
|
- la neutralisation des configurations Git globale/système et des hooks ;
|
||||||
|
- l'absence de `push_candidate` dans l'implémentation `alpha.3` ;
|
||||||
|
- l'absence de diff sous `knowledge-git-api`, `knowledge-app-lib`, `Web/`, `CHANGELOG.md` et `ROADMAP.md` ;
|
||||||
|
- `Cargo.lock` présent localement, ignoré et non suivi ;
|
||||||
|
- le package-lock Web présent.
|
||||||
|
|
||||||
|
Le staging final est cohérent avec la tranche :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
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
|
||||||
|
A crates/knowledge-git-lib/src/write.rs
|
||||||
|
A crates/knowledge-git-lib/tests/git_write_candidate.rs
|
||||||
|
M crates/knowledge-git-lib/unit_tests/backend.rs
|
||||||
|
A deltas/0.0.3/alpha.3.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Diagnostic
|
||||||
|
|
||||||
|
Le défaut est borné au test :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let snapshot_content = std::fs::read_to_string(snapshot.path().join("content.txt"));
|
||||||
|
assert_eq!(snapshot_content, Ok(String::from("first\n")));
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette comparaison exige `PartialEq` sur le type d'erreur du `Result`, ce que `std::io::Error` ne fournit pas. Le test doit vérifier le cas `Ok("first\n")` sans comparer la variante `Err`.
|
||||||
|
|
||||||
|
Aucun élément du retour opérateur ne justifie un REPLAN, une modification du contrat public ou un changement de l'implémentation de worktree.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.3` est `FAIL`.
|
||||||
|
|
||||||
|
La correction autorisée est `0.0.3-alpha.3.fix.1`, bornée à l'assertion de test fautive et au suivi version/plan. Elle doit rejouer Clippy, le test `git_write_candidate` et la gate workspace avant d'autoriser `alpha.4`.
|
||||||
132
history/0.0.3/alpha.4.md
Normal file
132
history/0.0.3/alpha.4.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<!-- file: history/0.0.3/alpha.4.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-alpha.4 — push gardé et concurrence
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-alpha.4` exécutée le `2026-09-29` est complète et propre. Le chemin Git d'écriture provider-neutral sait désormais pousser un candidat vers une branche distante uniquement lorsque l'état distant correspond à l'attente explicite, avec conflit récupérable en cas de divergence concurrente et sans écrasement silencieux.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.3.fix.1` `PASS` a été historisé, committé et poussé avant application de `alpha.4` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.3.fix.1.md PASS
|
||||||
|
commit aca00e6
|
||||||
|
message 0.0.3-alpha.3.fix.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `c3a8256` à `aca00e6`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-alpha.4
|
||||||
|
Cargo workspace version 0.0.3-alpha.4
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (112 file(s))
|
||||||
|
Markdown link audit clean (78 local link(s), 112 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 112 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (36 file(s))
|
||||||
|
Rust item-spacing audit clean (36 file(s))
|
||||||
|
Rust module-order audit clean (36 file(s))
|
||||||
|
Rust trait-import-block audit clean (36 file(s))
|
||||||
|
Rust re-export-block audit clean (36 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_push PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests réels sur remote bare local prouvent :
|
||||||
|
|
||||||
|
- mise à jour d'une branche existante lorsque sa révision correspond exactement à l'attente ;
|
||||||
|
- création d'une branche uniquement lorsque son absence est attendue ;
|
||||||
|
- protection compare-and-exchange via `--force-with-lease` ;
|
||||||
|
- neutralisation locale de `remote.origin.mirror=true` pour le refspec ciblé du push ;
|
||||||
|
- détection d'une modification distante concurrente ;
|
||||||
|
- retour d'un conflit sans écrasement de la révision distante concurrente.
|
||||||
|
|
||||||
|
## Contrôles spécifiques
|
||||||
|
|
||||||
|
Les contrôles courts et indépendants passent tous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history alpha.3.fix.1 PASS confirmé
|
||||||
|
pub fn push_candidate présent
|
||||||
|
--force-with-lease présent
|
||||||
|
remote.origin.mirror=false présent
|
||||||
|
GitOperation::InspectRemote présent
|
||||||
|
GitOperation::PushCandidate présent
|
||||||
|
impl GitRepositoryWriter for GitBackend présent
|
||||||
|
tests succès/création et conflit concurrent présents
|
||||||
|
CHANGELOG 0.0.3 encore absent
|
||||||
|
history alpha.4 avant gate absent
|
||||||
|
crates Gitea/PostgreSQL et migrations absentes
|
||||||
|
aucun diff knowledge-git-api / knowledge-app-lib / Web PASS
|
||||||
|
aucun diff CHANGELOG.md / ROADMAP.md PASS
|
||||||
|
Knowledge 0.0.3-alpha.4 text hygiene clean (112 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
package-lock Web présent
|
||||||
|
```
|
||||||
|
|
||||||
|
Après staging, `git diff --cached --check` passe.
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M VERSION
|
||||||
|
M crates/knowledge-git-lib/src/error.rs
|
||||||
|
M crates/knowledge-git-lib/src/lib.rs
|
||||||
|
M crates/knowledge-git-lib/src/write.rs
|
||||||
|
A crates/knowledge-git-lib/tests/git_write_push.rs
|
||||||
|
A deltas/0.0.3/alpha.4.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-alpha.4` est `PASS`.
|
||||||
|
|
||||||
|
Le scope fonctionnel alpha de `0.0.3` est fermé : lecture Git, création de candidat et push gardé provider-neutral sont acquis. `0.0.3-beta.1` peut maintenant réconcilier l'architecture et les `DEFER`, préparer le changelog candidat, rejouer la non-régression de stabilisation et effectuer la revue d'horizon produit complète des trois sites PGR / HOWTOs / snippets avant la release candidate.
|
||||||
149
history/0.0.3/beta.1.md
Normal file
149
history/0.0.3/beta.1.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<!-- file: history/0.0.3/beta.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-beta.1 — réconciliation et revue d'horizon
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-beta.1` exécutée le `2026-09-29` est complète et propre. La tranche réconcilie le chemin Git d'écriture acquis, prépare le changelog candidat et élargit l'horizon produit des trois sites sans modifier `crates/` ni `Web/`.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.4` `PASS` a été historisé, committé et poussé avant application de `beta.1` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/alpha.4.md PASS
|
||||||
|
commit a87f3ba
|
||||||
|
message 0.0.3-alpha.4
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `aca00e6` à `a87f3ba`.
|
||||||
|
|
||||||
|
## Version et documentation
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-beta.1
|
||||||
|
Cargo workspace version 0.0.3-beta.1
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (116 file(s))
|
||||||
|
Markdown link audit clean (82 local link(s), 116 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 116 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
Le changelog contient désormais `## 0.0.3 (candidate)` à partir des capacités réellement acquises. La revue d'horizon retient `0.0.4` comme fondation PostgreSQL partagée et rend explicites les phases ultérieures de modèles/projections, templates publics, templates d'administration, Actix Web / Maud puis gestion/proposition/revue/publication des trois sites.
|
||||||
|
|
||||||
|
Le handoff `prompts/004-V0_0_4_START_PROMPT.md` est présent sans commit stable, SHA-256 ou rapport post-tag `v0.0.3` inventé.
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (36 file(s))
|
||||||
|
Rust item-spacing audit clean (36 file(s))
|
||||||
|
Rust module-order audit clean (36 file(s))
|
||||||
|
Rust trait-import-block audit clean (36 file(s))
|
||||||
|
Rust re-export-block audit clean (36 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests Git
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_push PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests Git réels confirment encore le confinement du candidat, le refus des traversées par symlink, la création/suppression exacte des mutations, le push gardé, la création sous attente d'absence et le conflit concurrent sans écrasement.
|
||||||
|
|
||||||
|
## Baseline Web de stabilisation
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm ci PASS
|
||||||
|
npm audit observé 0 vulnérabilité
|
||||||
|
npm run typecheck PASS
|
||||||
|
npm run build PASS
|
||||||
|
Vite 8.3.1
|
||||||
|
modules transformés 63
|
||||||
|
Vite manifest audit PASS
|
||||||
|
entrée src/main.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Le build produit le manifest et les assets hashés attendus. Les avertissements Sass/Bootstrap de dépréciation ainsi que l'avertissement npm `@parcel/watcher` sont non bloquants ; le build termine avec succès.
|
||||||
|
|
||||||
|
## Revue d'horizon et contrôles spécifiques
|
||||||
|
|
||||||
|
Les contrôles opérateur confirment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
CHANGELOG 0.0.3 (candidate) présent
|
||||||
|
ROADMAP 0.0.4 PostgreSQL partagé présent
|
||||||
|
ROADMAP 0.0.6 templates publics présent
|
||||||
|
ROADMAP 0.0.7 templates administration présent
|
||||||
|
ROADMAP 0.0.8 Actix Web / Maud public présent
|
||||||
|
ROADMAP 0.0.9 admin Actix / Maud + autorisation présent
|
||||||
|
ROADMAP 0.0.10 gestion/revue/publication présent
|
||||||
|
étude 007 présente
|
||||||
|
prompt 004 présent
|
||||||
|
prompt 004 sans commit/SHA futur inventé PASS
|
||||||
|
aucun diff crates/ PASS
|
||||||
|
aucun diff Web/ PASS
|
||||||
|
crate PostgreSQL absente
|
||||||
|
crate Gitea absente
|
||||||
|
migrations absentes
|
||||||
|
history beta.1 avant gate absent
|
||||||
|
text hygiene clean (116 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
package-lock Web présent
|
||||||
|
```
|
||||||
|
|
||||||
|
Après staging, `git diff --cached --check` passe.
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M CHANGELOG.md
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M ROADMAP.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/beta.1.md
|
||||||
|
M docs/000-README.md
|
||||||
|
M docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
A docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
A prompts/004-V0_0_4_START_PROMPT.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-beta.1` est `PASS`.
|
||||||
|
|
||||||
|
Le scope produit est réconcilié et l'horizon post-`0.0.3` est suffisamment explicite. `0.0.3-rc.1` peut maintenant geler le scope et rejouer la gate complète, y compris les trois smokes HTTP de production, sans introduire de nouvelle fonctionnalité.
|
||||||
134
history/0.0.3/rc.1.fix.1.md
Normal file
134
history/0.0.3/rc.1.fix.1.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
<!-- file: history/0.0.3/rc.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-rc.1.fix.1 — correction de la vérification des statuts historiques
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-rc.1.fix.1` exécutée le `2026-09-29` est complète et propre.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`rc.1` `FAIL` a été historisé, committé et poussé avant application du fix :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/rc.1.md FAIL
|
||||||
|
commit 038b918
|
||||||
|
message 0.0.3-rc.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `282d59c` à `038b918`.
|
||||||
|
|
||||||
|
## Version, Markdown et format
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3-rc.1.fix.1
|
||||||
|
Cargo workspace version 0.0.3-rc.1.fix.1
|
||||||
|
history rc.1 FAIL
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (120 file(s))
|
||||||
|
Markdown link audit clean (82 local link(s), 120 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 120 file(s))
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (36 file(s))
|
||||||
|
Rust item-spacing audit clean (36 file(s))
|
||||||
|
Rust module-order audit clean (36 file(s))
|
||||||
|
Rust trait-import-block audit clean (36 file(s))
|
||||||
|
Rust re-export-block audit clean (36 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_push PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests Git réels reconfirment le chemin de lecture, la création de candidat avec confinement et nettoyage, le push gardé et le conflit concurrent sans écrasement.
|
||||||
|
|
||||||
|
## Correction de la preuve RC
|
||||||
|
|
||||||
|
La vérification robuste des statuts historiques passe après retrait de l'éventuel balisage inline Markdown :
|
||||||
|
|
||||||
|
```text
|
||||||
|
alpha.1 PARTIAL
|
||||||
|
alpha.1.fix.1 PARTIAL
|
||||||
|
alpha.1.fix.2 PASS
|
||||||
|
alpha.2 PASS
|
||||||
|
alpha.3 FAIL
|
||||||
|
alpha.3.fix.1 PASS
|
||||||
|
alpha.4 PASS
|
||||||
|
beta.1 PASS
|
||||||
|
rc.1 FAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fichier historique `history/0.0.3/alpha.1.md` n'a pas été réécrit.
|
||||||
|
|
||||||
|
Les contrôles confirment aussi :
|
||||||
|
|
||||||
|
```text
|
||||||
|
histories 0.0.3 avant fix history 9
|
||||||
|
deltas 0.0.3 10
|
||||||
|
CHANGELOG 0.0.3 candidate présent
|
||||||
|
ROADMAP 0.0.3 engagée présente
|
||||||
|
ROADMAP 0.0.4 PostgreSQL horizon présente
|
||||||
|
prompt 004 présent
|
||||||
|
scope crates/Web/docs gelés PASS
|
||||||
|
text hygiene clean (120 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
package-lock Web présent
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun build Web ni smoke HTTP n'était requis par ce fix parce qu'aucun fichier produit ou Web n'a changé ; ces preuves avaient déjà passé sur `rc.1` et seront rejouées pendant `rel.1`.
|
||||||
|
|
||||||
|
## Staging final
|
||||||
|
|
||||||
|
Le staging observé avant ajout du présent history est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/rc.1.fix.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
`git diff --cached --check` passe.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-rc.1.fix.1` est `PASS`.
|
||||||
|
|
||||||
|
La release peut passer à `0.0.3-rel.1` pour promotion stable pré-tag. Cette promotion doit rester mécanique, rejouer la gate stable complète — y compris Web et les trois smokes HTTP — puis produire son history réel avant commit stable et tag `v0.0.3`.
|
||||||
63
history/0.0.3/rc.1.md
Normal file
63
history/0.0.3/rc.1.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<!-- file: history/0.0.3/rc.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-rc.1 — release candidate
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
FAIL
|
||||||
|
|
||||||
|
## Résultat observé
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-rc.1` a été exécutée le `2026-09-29` après commit/push de `0.0.3-beta.1`.
|
||||||
|
|
||||||
|
Toutes les preuves produit et runtime exécutées sont `PASS` :
|
||||||
|
|
||||||
|
- version `0.0.3-rc.1` et history `beta.1 = PASS` ;
|
||||||
|
- audits Markdown propres sur `118` fichiers, `82` liens locaux et `4` tables ;
|
||||||
|
- `cargo fmt --all` puis `cargo fmt --all -- --check` ;
|
||||||
|
- audits Rust structurels propres sur `36` fichiers et `9` crates ;
|
||||||
|
- complétude d'exports : `0 candidate(s)` ;
|
||||||
|
- `cargo metadata`, `cargo check --workspace` et Clippy strict ;
|
||||||
|
- `git_read_path` : `2 PASS` ;
|
||||||
|
- `git_write_candidate` : `2 PASS` ;
|
||||||
|
- `git_write_push` : `2 PASS`, incluant le conflit concurrent sans écrasement ;
|
||||||
|
- tests workspace : tous `PASS` ;
|
||||||
|
- `npm ci`, typecheck et build Vite `PASS`, manifest de production valide ;
|
||||||
|
- workspace Cargo : `9` packages attendus ;
|
||||||
|
- smokes HTTP production PGR, HOWTOs et snippets : `/healthz`, `/` et assets manifest servis, tous `PASS` ;
|
||||||
|
- scope RC gelé, politique `Cargo.lock` et staging final conformes.
|
||||||
|
|
||||||
|
Les avertissements Sass/Bootstrap observés pendant le build restent des dépréciations non bloquantes ; le build termine avec succès.
|
||||||
|
|
||||||
|
## Échec de gate
|
||||||
|
|
||||||
|
Une seule commande de contrôle d'intégrité a échoué :
|
||||||
|
|
||||||
|
```text
|
||||||
|
grep -A2 '^## Statut$' history/0.0.3/alpha.1.md | grep -Fx 'PARTIAL'
|
||||||
|
```
|
||||||
|
|
||||||
|
`history/0.0.3/alpha.1.md` contient historiquement le statut Markdown :
|
||||||
|
|
||||||
|
```text
|
||||||
|
`PARTIAL`
|
||||||
|
```
|
||||||
|
|
||||||
|
Le sens du history est donc correct. L'échec provient de l'assertion RC qui exigeait la représentation lexicale non formatée `PARTIAL` au lieu de normaliser l'éventuel balisage inline Markdown.
|
||||||
|
|
||||||
|
Les autres histories utilisent la forme non balisée et ont passé les contrôles attendus.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-rc.1` est conservée `FAIL` parce qu'une commande appartenant à sa gate a réellement échoué.
|
||||||
|
|
||||||
|
L'ancien history `alpha.1` ne doit pas être réécrit : il constitue une preuve historique déjà committée.
|
||||||
|
|
||||||
|
La correction utilise `0.0.3-rc.1.fix.1` et reste limitée à :
|
||||||
|
|
||||||
|
- corriger la règle de vérification des statuts historiques pour accepter leur représentation Markdown existante ;
|
||||||
|
- conserver sans changement les statuts historiques eux-mêmes ;
|
||||||
|
- rejouer les contrôles nécessaires au fix avant toute promotion `rel.1`.
|
||||||
|
|
||||||
|
Aucun défaut produit Git, Web ou runtime n'a été observé pendant `rc.1`.
|
||||||
139
history/0.0.3/rel.1.fix.1.md
Normal file
139
history/0.0.3/rel.1.fix.1.md
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
<!-- file: history/0.0.3/rel.1.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-rel.1.fix.1 — complétion de la preuve de clôture stable
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-rel.1.fix.1` exécutée le `2026-09-29` est complète et propre. Elle complète exactement les deux preuves absentes de `rel.1` sans modifier le produit, le Web, la version stable visée ni les documents de release déjà finalisés.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`rel.1` `PARTIAL` a été historisé, committé et poussé avant application du fix :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/rel.1.md PARTIAL
|
||||||
|
commit 9a1f73d
|
||||||
|
message 0.0.3-rel.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `0351aa0` à `9a1f73d`.
|
||||||
|
|
||||||
|
## Version et audits documentaires
|
||||||
|
|
||||||
|
La version produit reste volontairement stable pendant cette tranche opérationnelle :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3
|
||||||
|
Cargo workspace version 0.0.3
|
||||||
|
history rel.1 PARTIAL
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (124 file(s))
|
||||||
|
Markdown link audit clean (82 local link(s), 124 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 124 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuve workspace complétée
|
||||||
|
|
||||||
|
La preuve explicitement manquante dans `rel.1` est maintenant acquise :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
workspace packages exacts PASS
|
||||||
|
workspace_members 9
|
||||||
|
```
|
||||||
|
|
||||||
|
L'ensemble exact observé est :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib
|
||||||
|
knowledge-domain-lib
|
||||||
|
knowledge-git-api
|
||||||
|
knowledge-git-lib
|
||||||
|
knowledge-sync-api
|
||||||
|
knowledge-web-lib
|
||||||
|
knowledge-pgr-server
|
||||||
|
knowledge-howtos-server
|
||||||
|
knowledge-snippets-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Le contrôle opérateur imprime :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-rel.1.fix.1 workspace audit: clean
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuve d'hygiène texte complétée
|
||||||
|
|
||||||
|
La seconde preuve manquante est acquise sur les racines documentaires et de gouvernance prévues :
|
||||||
|
|
||||||
|
```text
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/
|
||||||
|
prompts/
|
||||||
|
deltas/
|
||||||
|
history/
|
||||||
|
```
|
||||||
|
|
||||||
|
Résultat observé :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.3-rel.1.fix.1 text hygiene: clean (124 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun retour chariot CR, fin de fichier incorrecte, double newline terminale ou espace/tab de fin de ligne n'est détecté par cette gate.
|
||||||
|
|
||||||
|
## Scope et invariants de clôture
|
||||||
|
|
||||||
|
Les contrôles finaux passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
aucun diff VERSION/Cargo.toml/crates/Web/CHANGELOG/ROADMAP/
|
||||||
|
architecture/studies/rules/prompts PASS
|
||||||
|
history/0.0.3/rel.1.fix.1.md avant history absent
|
||||||
|
Cargo.lock présent, ignoré, non suivi
|
||||||
|
Web/knowledge-template-lab/package-lock.json présent
|
||||||
|
tag local v0.0.3 absent
|
||||||
|
sasedev-knowledge-0.0.3-publish.001-report.md absent
|
||||||
|
git diff --cached --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
A deltas/0.0.3/rel.1.fix.1.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuves produit héritées de rel.1
|
||||||
|
|
||||||
|
Ce fix ne modifie aucun fichier produit ou Web. Les preuves complètes acquises pendant `rel.1` restent donc autoritaires pour la clôture stable :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt / audits Rust PASS
|
||||||
|
cargo check / Clippy PASS
|
||||||
|
Git read path PASS (2)
|
||||||
|
Git write candidate PASS (2)
|
||||||
|
Git guarded push PASS (2)
|
||||||
|
workspace tests PASS
|
||||||
|
npm ci / typecheck / Vite build PASS
|
||||||
|
Vite manifest PASS
|
||||||
|
knowledge-pgr-server smoke PASS
|
||||||
|
knowledge-howtos-server smoke PASS
|
||||||
|
knowledge-snippets-server smoke PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-rel.1.fix.1` est `PASS`.
|
||||||
|
|
||||||
|
La clôture stable de `0.0.3` est maintenant suffisamment prouvée. Le présent history doit être appliqué puis inclus, avec les trois fichiers déjà stagés par le fix, dans le premier commit stable portant le message exact `0.0.3`. Après push de `master`, le tag annoté `v0.0.3` peut être créé et poussé. Le rapport post-tag `sasedev-knowledge-0.0.3-publish.001-report.md` ne doit être produit qu'après vérification des identifiants réellement publiés et de l'archive réellement servie par Gitea.
|
||||||
175
history/0.0.3/rel.1.md
Normal file
175
history/0.0.3/rel.1.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
<!-- file: history/0.0.3/rel.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.3-rel.1 — promotion stable pré-tag
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PARTIAL
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.3-rel.1` exécutée le `2026-09-29` acquiert toutes les preuves produit/runtime et atteint le staging final attendu, mais deux contrôles explicitement demandés n'apparaissent pas dans le transcript : l'audit de composition du workspace à partir du JSON `cargo metadata` et l'audit d'hygiène texte. La tranche est donc conservée `PARTIAL` plutôt que promue implicitement en `PASS`.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`rc.1.fix.1` `PASS` a été historisé, committé et poussé avant application de `rel.1` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.3/rc.1.fix.1.md PASS
|
||||||
|
commit 0351aa0
|
||||||
|
message 0.0.3-rc.1.fix.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
workspace après push propre
|
||||||
|
```
|
||||||
|
|
||||||
|
Un premier `git push origin master` a été interrompu à l'invite utilisateur, puis le second push a réussi et a avancé `origin/master` de `038b918` à `0351aa0`.
|
||||||
|
|
||||||
|
## Promotion stable et documentation
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.3
|
||||||
|
Cargo workspace version 0.0.3
|
||||||
|
history rc.1.fix.1 PASS
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (122 file(s))
|
||||||
|
Markdown link audit clean (82 local link(s), 122 file(s))
|
||||||
|
Markdown table audit clean (4 table(s), 122 file(s))
|
||||||
|
```
|
||||||
|
|
||||||
|
Les contrôles confirment également :
|
||||||
|
|
||||||
|
```text
|
||||||
|
CHANGELOG ## 0.0.3, sans marqueur candidate
|
||||||
|
ROADMAP 0.0.3 terminée
|
||||||
|
ROADMAP 0.0.4 engagée
|
||||||
|
ROADMAP 0.0.5 -> 0.0.10 horizons révisables
|
||||||
|
README 0.0.3 présentée comme stable
|
||||||
|
plan 003 historique de la stable 0.0.3
|
||||||
|
prompt 004 présent
|
||||||
|
prompt 004 sans commit/SHA post-tag inventé
|
||||||
|
PostgreSQL / migrations absents
|
||||||
|
forge Gitea absente
|
||||||
|
```
|
||||||
|
|
||||||
|
Les dix histories antérieurs sont présents avec leurs statuts réels :
|
||||||
|
|
||||||
|
```text
|
||||||
|
alpha.1 PARTIAL
|
||||||
|
alpha.1.fix.1 PARTIAL
|
||||||
|
alpha.1.fix.2 PASS
|
||||||
|
alpha.2 PASS
|
||||||
|
alpha.3 FAIL
|
||||||
|
alpha.3.fix.1 PASS
|
||||||
|
alpha.4 PASS
|
||||||
|
beta.1 PASS
|
||||||
|
rc.1 FAIL
|
||||||
|
rc.1.fix.1 PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (36 file(s))
|
||||||
|
Rust item-spacing audit clean (36 file(s))
|
||||||
|
Rust module-order audit clean (36 file(s))
|
||||||
|
Rust trait-import-block audit clean (36 file(s))
|
||||||
|
Rust re-export-block audit clean (36 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests Git
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS (fichier JSON produit)
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets --all-features
|
||||||
|
-- -D warnings PASS
|
||||||
|
cargo test -p knowledge-git-lib --test git_read_path PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_candidate PASS (2)
|
||||||
|
cargo test -p knowledge-git-lib --test git_write_push PASS (2)
|
||||||
|
cargo test --workspace --all-targets --all-features PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests Git réels confirment encore le chemin de lecture, le candidat isolé et confiné, le push gardé et le conflit concurrent sans écrasement.
|
||||||
|
|
||||||
|
## Baseline Web et runtime
|
||||||
|
|
||||||
|
```text
|
||||||
|
npm ci PASS
|
||||||
|
npm audit observé 0 vulnérabilité
|
||||||
|
npm run typecheck PASS
|
||||||
|
npm run build PASS
|
||||||
|
Vite 8.3.1
|
||||||
|
modules transformés 63
|
||||||
|
Vite manifest audit PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Les avertissements Sass/Bootstrap de dépréciation et l'avertissement npm sur le script d'installation `@parcel/watcher` restent non bloquants ; le build termine avec succès.
|
||||||
|
|
||||||
|
Les trois serveurs ont ensuite été reconstruits et les smokes de production passent avec `/healthz`, `/` et les assets hashés du manifest :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-pgr-server PASS
|
||||||
|
knowledge-howtos-server PASS
|
||||||
|
knowledge-snippets-server PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuves manquantes
|
||||||
|
|
||||||
|
Deux commandes annoncées dans la gate `rel.1` ne sont pas présentes dans le retour opérateur :
|
||||||
|
|
||||||
|
```text
|
||||||
|
workspace audit depuis /tmp/sasedev-knowledge-0.0.3-rel.1-metadata.json NON FOURNI
|
||||||
|
text hygiene sur README/RULES/ROADMAP/CHANGELOG/docs/prompts/deltas/history NON FOURNI
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fichier metadata a bien été produit et utilisé indirectement par les commandes Cargo, mais la vérification explicite de l'ensemble exact des neuf packages n'a pas été exécutée dans le transcript. De même, les audits Markdown passent mais ne remplacent pas l'audit d'hygiène texte demandé.
|
||||||
|
|
||||||
|
## Scope, lockfiles et staging
|
||||||
|
|
||||||
|
Les contrôles finaux fournis confirment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
aucun diff crates/Web/architecture/studies/rules/prompts PASS
|
||||||
|
Cargo.lock présent, ignoré, non suivi
|
||||||
|
package-lock Web présent
|
||||||
|
tag local v0.0.3 absent
|
||||||
|
rapport publish.001 absent
|
||||||
|
git diff --cached --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M CHANGELOG.md
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M ROADMAP.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.3/rel.1.md
|
||||||
|
M docs/000-README.md
|
||||||
|
M docs/plans/003-V0_0_3_GIT_WRITE_PLAN.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.3-rel.1` est `PARTIAL`.
|
||||||
|
|
||||||
|
Aucune régression produit n'est observée et aucune correction fonctionnelle n'est nécessaire. Conformément au workflow local, ce résultat doit être historisé, committé et poussé avec le message `0.0.3-rel.1`, sans créer le tag `v0.0.3`. La transition autorisée est uniquement `rel.1.fix.1`, bornée à la complétion des deux preuves manquantes avant le premier commit stable réellement `PASS`.
|
||||||
140
history/0.0.4/alpha.1.md
Normal file
140
history/0.0.4/alpha.1.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<!-- file: history/0.0.4/alpha.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.4-alpha.1 — baseline, étude et cadrage PostgreSQL
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.4-alpha.1` exécutée le `2026-09-30` est complète et propre. La tranche ouvre la fondation PostgreSQL partagée depuis la stable autoritaire `v0.0.3`, fixe l'owner et les choix techniques de stockage, sans créer encore de crate PostgreSQL, migration ou dépendance runtime.
|
||||||
|
|
||||||
|
## Baseline autoritaire vérifiée
|
||||||
|
|
||||||
|
Les contrôles opérateur confirment la baseline documentée :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge stable tag v0.0.3
|
||||||
|
Knowledge stable commit b3823972449150b72905930b806d4d15471cddce
|
||||||
|
Knowledge archive SHA-256 82407dc2a83be878daa65692f975681428d0adc721ff249c3c9020e3a9d1f29d
|
||||||
|
Knowledge publish report sasedev-knowledge-0.0.3-publish.001-report.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Les douze deltas et douze histories `0.0.3` sont présents et appariés. `history/0.0.3/rel.1.fix.1.md` est `PASS`.
|
||||||
|
|
||||||
|
## Version et cadrage
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.4-alpha.1
|
||||||
|
Cargo workspace version 0.0.4-alpha.1
|
||||||
|
étude docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
plan docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
delta deltas/0.0.4/alpha.1.md
|
||||||
|
history alpha.1 avant gate absent
|
||||||
|
```
|
||||||
|
|
||||||
|
Les décisions vérifiées sont :
|
||||||
|
|
||||||
|
```text
|
||||||
|
driver PostgreSQL tokio-postgres
|
||||||
|
pool deadpool-postgres
|
||||||
|
migrations Refinery + SQL versionné
|
||||||
|
ORM aucun
|
||||||
|
owner knowledge-store-postgres-lib
|
||||||
|
premier schéma knowledge_sources + knowledge_source_sync_state
|
||||||
|
test PostgreSQL KNOWLEDGE_POSTGRES_TEST_URL + schéma isolé
|
||||||
|
autorité du contenu Git
|
||||||
|
```
|
||||||
|
|
||||||
|
PGR `v0.1.3` reste une provenance historique : aucun chargement ou adoption PGR nouveau n'est engagé.
|
||||||
|
|
||||||
|
## Scope négatif confirmé
|
||||||
|
|
||||||
|
La gate confirme :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-store-postgres-lib absent
|
||||||
|
migrations/ absent
|
||||||
|
history/0.0.4/alpha.1.md avant gate absent
|
||||||
|
aucun diff crates/ PASS
|
||||||
|
aucun diff Web/ PASS
|
||||||
|
aucun diff CHANGELOG.md PASS
|
||||||
|
aucun diff ROADMAP.md PASS
|
||||||
|
aucun diff prompts/ PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
L'horizon `0.0.5` à `0.0.10` reste inchangé.
|
||||||
|
|
||||||
|
## Markdown, formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
git diff --check PASS
|
||||||
|
Markdown header audit clean (128 file(s))
|
||||||
|
Markdown link audit clean (86 local link(s), 128 file(s))
|
||||||
|
Markdown table audit clean (5 table(s), 128 file(s))
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (9 crate(s))
|
||||||
|
Rust blank-line audit clean (36 file(s))
|
||||||
|
Rust item-spacing audit clean (36 file(s))
|
||||||
|
Rust module-order audit clean (36 file(s))
|
||||||
|
Rust trait-import-block audit clean (36 file(s))
|
||||||
|
Rust re-export-block audit clean (36 file(s))
|
||||||
|
Rust crate-root facade audit clean (6 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 9 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo et structure workspace
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
workspace_members 9
|
||||||
|
cargo check --workspace PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Les neuf packages observés sont exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib
|
||||||
|
knowledge-domain-lib
|
||||||
|
knowledge-git-api
|
||||||
|
knowledge-git-lib
|
||||||
|
knowledge-sync-api
|
||||||
|
knowledge-web-lib
|
||||||
|
knowledge-pgr-server
|
||||||
|
knowledge-howtos-server
|
||||||
|
knowledge-snippets-server
|
||||||
|
```
|
||||||
|
|
||||||
|
Clippy, tests workspace, npm/Vite et smokes HTTP n'étaient pas requis par cette tranche documentaire et n'ont pas été annoncés comme exécutés.
|
||||||
|
|
||||||
|
## Hygiène et lockfiles
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.4-alpha.1 text hygiene clean (128 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
Web package-lock.json présent
|
||||||
|
git diff --cached --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M RULES.md
|
||||||
|
M VERSION
|
||||||
|
A deltas/0.0.4/alpha.1.md
|
||||||
|
M docs/000-README.md
|
||||||
|
M docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
A docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
A docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.4-alpha.1` est `PASS`.
|
||||||
|
|
||||||
|
`0.0.4-alpha.2` peut maintenant matérialiser `knowledge-store-postgres-lib`, les dépendances PostgreSQL sélectionnées, le pool/configuration explicites et la première migration versionnée, avec preuve sur PostgreSQL réel dans un schéma de test isolé.
|
||||||
155
history/0.0.4/alpha.2.fix.1.md
Normal file
155
history/0.0.4/alpha.2.fix.1.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<!-- file: history/0.0.4/alpha.2.fix.1.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.4-alpha.2.fix.1 — documentation de la crate de test PostgreSQL
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
PASS
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.4-alpha.2.fix.1` exécutée le `2026-10-01` est complète et propre. Le fix corrige uniquement le lint `missing_docs` observé sur la target d'intégration PostgreSQL de `alpha.2`, sans modifier le code produit, le schéma SQL, les dépendances ni la façade publique.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.2` historisée `FAIL` a été committée puis poussée avant application du fix :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.4/alpha.2.md FAIL
|
||||||
|
commit 095ac37
|
||||||
|
message 0.0.4-alpha.2
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
La première tentative de `git push origin master` a été interrompue pendant la saisie d'identifiants. La seconde a réussi et a avancé `origin/master` de `b55eb23` à `095ac37`.
|
||||||
|
|
||||||
|
## Correction bornée
|
||||||
|
|
||||||
|
Les contrôles initiaux confirment :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.4-alpha.2.fix.1
|
||||||
|
Cargo workspace version 0.0.4-alpha.2.fix.1
|
||||||
|
history/0.0.4/alpha.2.md présent, suivi, FAIL
|
||||||
|
history/0.0.4/alpha.2.fix.1.md avant gate absent
|
||||||
|
documentation de crate //! présente
|
||||||
|
git diff --check PASS
|
||||||
|
scope négatif PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le plan actif conserve explicitement `alpha.2 = FAIL` et n'autorise `alpha.3` qu'après `PASS` complet du présent fix.
|
||||||
|
|
||||||
|
Aucun diff n'est observé sous :
|
||||||
|
|
||||||
|
```text
|
||||||
|
crates/knowledge-store-postgres-lib/src/
|
||||||
|
crates/knowledge-store-postgres-lib/unit_tests/
|
||||||
|
crates/knowledge-store-postgres-lib/migrations/
|
||||||
|
crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
README.md
|
||||||
|
RULES.md
|
||||||
|
ROADMAP.md
|
||||||
|
CHANGELOG.md
|
||||||
|
docs/architecture/
|
||||||
|
docs/studies/
|
||||||
|
docs/validation/
|
||||||
|
Web/
|
||||||
|
prompts/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Markdown, formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
Markdown header audit clean (132 file(s))
|
||||||
|
Markdown link audit clean (86 local link(s), 132 file(s))
|
||||||
|
Markdown table audit clean (5 table(s), 132 file(s))
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (10 crate(s))
|
||||||
|
Rust blank-line audit clean (42 file(s))
|
||||||
|
Rust item-spacing audit clean (42 file(s))
|
||||||
|
Rust module-order audit clean (42 file(s))
|
||||||
|
Rust trait-import-block audit clean (42 file(s))
|
||||||
|
Rust re-export-block audit clean (42 file(s))
|
||||||
|
Rust crate-root facade audit clean (7 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 10 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
cargo clippy --workspace --all-targets
|
||||||
|
--all-features -- -D warnings PASS
|
||||||
|
workspace_members 10
|
||||||
|
```
|
||||||
|
|
||||||
|
Le défaut `missing_docs` de `alpha.2` est donc fermé par Clippy strict.
|
||||||
|
|
||||||
|
Tests ciblés de la crate PostgreSQL :
|
||||||
|
|
||||||
|
```text
|
||||||
|
config unit tests 3 PASS
|
||||||
|
postgres_migration 1 ignored, gate explicite requise
|
||||||
|
doc-tests 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
knowledge-store-postgres-lib 3 PASS + 1 intégration ignorée
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
Aucun warning `missing_docs` n'est observé pendant ces commandes.
|
||||||
|
|
||||||
|
## Preuve PostgreSQL réelle
|
||||||
|
|
||||||
|
La précondition opérateur `KNOWLEDGE_POSTGRES_TEST_URL` est présente pendant la gate du fix.
|
||||||
|
|
||||||
|
La commande obligatoire :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo test -p knowledge-store-postgres-lib --test postgres_migration -- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
produit :
|
||||||
|
|
||||||
|
```text
|
||||||
|
initial_migration_runs_in_isolated_schema PASS
|
||||||
|
1 passed; 0 failed
|
||||||
|
```
|
||||||
|
|
||||||
|
La migration initiale, le schéma temporaire isolé et son nettoyage restent donc prouvés sur PostgreSQL réel après la correction documentaire.
|
||||||
|
|
||||||
|
## Hygiène et lockfiles
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.4-alpha.2.fix.1 text hygiene clean (187 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
Web package-lock.json présent et inchangé
|
||||||
|
git diff --cached --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M VERSION
|
||||||
|
M crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
A deltas/0.0.4/alpha.2.fix.1.md
|
||||||
|
M docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.4-alpha.2.fix.1` est `PASS`.
|
||||||
|
|
||||||
|
Le défaut de validation de `alpha.2` est fermé sans changement fonctionnel. `0.0.4-alpha.3` peut maintenant matérialiser la persistance minimale de configuration de source et d'état de synchronisation sur le schéma PostgreSQL déjà validé.
|
||||||
221
history/0.0.4/alpha.2.md
Normal file
221
history/0.0.4/alpha.2.md
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
<!-- file: history/0.0.4/alpha.2.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# History 0.0.4-alpha.2 — owner PostgreSQL, pool et première migration
|
||||||
|
|
||||||
|
## Statut
|
||||||
|
|
||||||
|
FAIL
|
||||||
|
|
||||||
|
La gate opérateur de `0.0.4-alpha.2` exécutée le `2026-10-01` échoue sur Clippy strict : la crate de test d'intégration `crates/knowledge-store-postgres-lib/tests/postgres_migration.rs` ne possède pas de documentation de crate alors que `missing_docs` est activé et promu en erreur par `-D warnings`.
|
||||||
|
|
||||||
|
La fondation PostgreSQL compile, les tests Rust hors Clippy passent, et la migration réelle finit par passer sur PostgreSQL après définition explicite de `KNOWLEDGE_POSTGRES_TEST_URL`. Le statut reste néanmoins `FAIL`, car la gate obligatoire Clippy n'est pas propre.
|
||||||
|
|
||||||
|
## Baseline opérateur
|
||||||
|
|
||||||
|
`alpha.1` `PASS` a été historisé, committé et poussé avant application de `alpha.2` :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history/0.0.4/alpha.1.md PASS
|
||||||
|
commit b55eb23
|
||||||
|
message 0.0.4-alpha.1
|
||||||
|
branch master
|
||||||
|
push origin/master PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le push observé a avancé `origin/master` de `b382397` à `b55eb23`.
|
||||||
|
|
||||||
|
## Version, scope et dépendances
|
||||||
|
|
||||||
|
Les contrôles initiaux passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
VERSION 0.0.4-alpha.2
|
||||||
|
Cargo workspace version 0.0.4-alpha.2
|
||||||
|
history/0.0.4/alpha.1.md présent, suivi, PASS
|
||||||
|
history/0.0.4/alpha.2.md avant gate absent
|
||||||
|
knowledge-store-postgres-lib présent
|
||||||
|
V1__initial_sources.sql présent
|
||||||
|
git diff --check PASS
|
||||||
|
surfaces hors scope propres
|
||||||
|
```
|
||||||
|
|
||||||
|
Les versions et features observées sont exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tokio-postgres 0.7.18 default-features=false, runtime
|
||||||
|
deadpool-postgres 0.14.2 default-features=false, rt_tokio_1
|
||||||
|
refinery 0.9.2 default-features=false, tokio-postgres
|
||||||
|
tokio dev 1.53.1 default-features=false, macros + rt-multi-thread
|
||||||
|
```
|
||||||
|
|
||||||
|
La façade publique contient exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
PostgresStoreConfig
|
||||||
|
PostgresStoreError
|
||||||
|
PostgresStore
|
||||||
|
```
|
||||||
|
|
||||||
|
La crate produit ne lit pas `KNOWLEDGE_POSTGRES_TEST_URL`, ne lit pas directement l'environnement et ne contient pas de `println!`, `eprintln!` ou `dbg!`.
|
||||||
|
|
||||||
|
## Migration initiale
|
||||||
|
|
||||||
|
Les contrôles statiques de la migration passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge_sources présent
|
||||||
|
family CHECK pgr/howto/snippet/project_local
|
||||||
|
knowledge_source_sync_state présent
|
||||||
|
FK source_id ON DELETE CASCADE présente
|
||||||
|
last_synchronized_revision NOT NULL présent
|
||||||
|
contenu/body/credential/account/
|
||||||
|
proposal/job absents de la migration
|
||||||
|
```
|
||||||
|
|
||||||
|
Le test d'intégration possède bien son marqueur `ignore`, crée un schéma isolé et le supprime avec `DROP SCHEMA ... CASCADE`.
|
||||||
|
|
||||||
|
## Markdown, formatage et audits Rust
|
||||||
|
|
||||||
|
```text
|
||||||
|
Markdown header audit clean (130 file(s))
|
||||||
|
Markdown link audit clean (86 local link(s), 130 file(s))
|
||||||
|
Markdown table audit clean (5 table(s), 130 file(s))
|
||||||
|
cargo fmt --all PASS
|
||||||
|
cargo fmt --all -- --check PASS
|
||||||
|
Rust test-layout audit clean (10 crate(s))
|
||||||
|
Rust blank-line audit clean (42 file(s))
|
||||||
|
Rust item-spacing audit clean (42 file(s))
|
||||||
|
Rust module-order audit clean (42 file(s))
|
||||||
|
Rust trait-import-block audit clean (42 file(s))
|
||||||
|
Rust re-export-block audit clean (42 file(s))
|
||||||
|
Rust crate-root facade audit clean (7 crate root(s))
|
||||||
|
Rust export completeness audit 0 candidate(s) across 10 package(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo, Clippy et tests
|
||||||
|
|
||||||
|
La résolution des nouvelles dépendances et la compilation workspace passent :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo metadata --format-version 1 --no-deps PASS
|
||||||
|
cargo check --workspace PASS
|
||||||
|
workspace_members 10
|
||||||
|
```
|
||||||
|
|
||||||
|
Les dix packages observés sont exactement les neuf packages historiques plus `knowledge-store-postgres-lib`.
|
||||||
|
|
||||||
|
La première défaillance obligatoire survient pendant Clippy :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings FAIL
|
||||||
|
```
|
||||||
|
|
||||||
|
Erreur observée :
|
||||||
|
|
||||||
|
```text
|
||||||
|
error: missing documentation for the crate
|
||||||
|
crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
-D missing-docs implied by -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
Les tests exécutés après cet échec passent fonctionnellement mais reproduisent le warning de documentation :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo test -p knowledge-store-postgres-lib
|
||||||
|
unit tests PASS (3)
|
||||||
|
postgres_migration ignored (1, gate explicite requise)
|
||||||
|
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
workspace PASS
|
||||||
|
postgres_migration ignored (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
Tests workspace observés :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-app-lib 3 PASS
|
||||||
|
knowledge-domain-lib 3 PASS
|
||||||
|
knowledge-git-api 7 PASS
|
||||||
|
knowledge-git-lib 8 unit + 6 integration PASS
|
||||||
|
knowledge-sync-api 1 PASS
|
||||||
|
knowledge-web-lib 4 PASS
|
||||||
|
knowledge-store-postgres-lib 3 PASS + 1 intégration ignorée
|
||||||
|
launchers 0 test chacun
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preuve PostgreSQL réelle
|
||||||
|
|
||||||
|
Une première invocation explicite du test ignoré a été effectuée alors que `KNOWLEDGE_POSTGRES_TEST_URL` n'était pas encore définie. Le test a correctement refusé l'exécution :
|
||||||
|
|
||||||
|
```text
|
||||||
|
KNOWLEDGE_POSTGRES_TEST_URL is required
|
||||||
|
postgres_migration FAILED
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette première tentative ne révèle pas un défaut PostgreSQL du produit ; elle constate une précondition opérateur absente.
|
||||||
|
|
||||||
|
L'opérateur a ensuite défini :
|
||||||
|
|
||||||
|
```text
|
||||||
|
KNOWLEDGE_POSTGRES_TEST_URL=postgresql://knowledge:knowledge@localhost/knowledge
|
||||||
|
```
|
||||||
|
|
||||||
|
puis rejoué exactement le test réel :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo test -p knowledge-store-postgres-lib --test postgres_migration -- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
Résultat :
|
||||||
|
|
||||||
|
```text
|
||||||
|
initial_migration_runs_in_isolated_schema PASS
|
||||||
|
1 passed; 0 failed
|
||||||
|
```
|
||||||
|
|
||||||
|
La migration initiale, l'isolation par schéma et le nettoyage sont donc prouvés sur PostgreSQL réel. Le warning `missing_docs` reste toutefois présent pendant cette exécution.
|
||||||
|
|
||||||
|
## Hygiène et lockfiles
|
||||||
|
|
||||||
|
```text
|
||||||
|
Knowledge 0.0.4-alpha.2 text hygiene clean (185 file(s))
|
||||||
|
Cargo.lock présent localement, ignoré, non suivi
|
||||||
|
Web package-lock.json présent et inchangé
|
||||||
|
git diff --cached --check PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
Le staging final observé avant ajout du présent history est exactement :
|
||||||
|
|
||||||
|
```text
|
||||||
|
M Cargo.toml
|
||||||
|
M README.md
|
||||||
|
M RULES.md
|
||||||
|
M VERSION
|
||||||
|
A crates/knowledge-store-postgres-lib/Cargo.toml
|
||||||
|
A crates/knowledge-store-postgres-lib/migrations/V1__initial_sources.sql
|
||||||
|
A crates/knowledge-store-postgres-lib/src/config.rs
|
||||||
|
A crates/knowledge-store-postgres-lib/src/error.rs
|
||||||
|
A crates/knowledge-store-postgres-lib/src/lib.rs
|
||||||
|
A crates/knowledge-store-postgres-lib/src/store.rs
|
||||||
|
A crates/knowledge-store-postgres-lib/tests/postgres_migration.rs
|
||||||
|
A crates/knowledge-store-postgres-lib/unit_tests/config.rs
|
||||||
|
A deltas/0.0.4/alpha.2.md
|
||||||
|
M docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
M docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
M docs/plans/004-V0_0_4_POSTGRES_FOUNDATION_PLAN.md
|
||||||
|
M docs/studies/008-V0_0_4_POSTGRES_FOUNDATION_STUDY.md
|
||||||
|
M docs/validation/001-VALIDATION_MATRIX.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Diagnostic
|
||||||
|
|
||||||
|
Le défaut est borné à l'en-tête documentaire de la crate de test d'intégration. Les autres tests d'intégration du dépôt utilisent déjà une documentation de crate `//! ...`, ce qui satisfait le lint `missing_docs` appliqué aux targets de test.
|
||||||
|
|
||||||
|
Aucun retour opérateur ne justifie de modifier le driver, le pool, Refinery, la façade publique, le schéma SQL, les migrations ou le plan d'ownership. Aucun REPLAN n'est requis.
|
||||||
|
|
||||||
|
## Décision
|
||||||
|
|
||||||
|
`0.0.4-alpha.2` est `FAIL`.
|
||||||
|
|
||||||
|
La correction autorisée est `0.0.4-alpha.2.fix.1`, bornée à l'ajout de la documentation de crate manquante dans `tests/postgres_migration.rs` et au suivi version/plan. Elle doit rejouer Clippy strict, les tests workspace et la preuve PostgreSQL réelle avant d'autoriser `alpha.3`.
|
||||||
133
prompts/003-V0_0_3_START_PROMPT.md
Normal file
133
prompts/003-V0_0_3_START_PROMPT.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
<!-- file: prompts/003-V0_0_3_START_PROMPT.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Knowledge 0.0.3 — prompt de démarrage
|
||||||
|
|
||||||
|
## Condition d'utilisation
|
||||||
|
|
||||||
|
Ce prompt est matérialisé lors de l'ouverture explicite de `0.0.3`, après fermeture et publication complète de `0.0.2`.
|
||||||
|
|
||||||
|
La reprise n'est valide que si les preuves suivantes sont disponibles :
|
||||||
|
|
||||||
|
```text
|
||||||
|
history rel.1 de 0.0.2 PASS
|
||||||
|
commit produit 0.0.2 183d354a67bec6d2d512c4898f5189759a6120b0
|
||||||
|
tag stable v0.0.2 publié
|
||||||
|
archive sasedev-knowledge-v0.0.2.zip vérifiée
|
||||||
|
rapport post-tag publish.001 PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
## Baseline autoritaire
|
||||||
|
|
||||||
|
Partir de :
|
||||||
|
|
||||||
|
```text
|
||||||
|
tag Knowledge v0.0.2
|
||||||
|
commit Knowledge 183d354a67bec6d2d512c4898f5189759a6120b0
|
||||||
|
archive SHA-256 0e518c7eaad115811cccc8fb2b38a1e68150ec985c2ca72ec08a9ca5bbb202ce
|
||||||
|
publish report sasedev-knowledge-0.0.2-publish.001-report.md = PASS
|
||||||
|
```
|
||||||
|
|
||||||
|
L'archive taggée est la baseline produit. L'absence de `.git` dans cette archive est normale.
|
||||||
|
|
||||||
|
Ne pas reconstruire l'état depuis une conversation, un workspace local ou une version ultérieure non vérifiée.
|
||||||
|
|
||||||
|
## Autonomie normative
|
||||||
|
|
||||||
|
Knowledge est autonome après l'adoption fermée en `0.0.2`.
|
||||||
|
|
||||||
|
Ne pas rechercher, charger ni adopter automatiquement une nouvelle version de PGR. PGR `v0.1.3` reste uniquement une provenance historique locale. Une réévaluation PGR n'est autorisée que si elle est explicitement ajoutée au scope par un REPLAN ou une release future.
|
||||||
|
|
||||||
|
Relire les owners Knowledge :
|
||||||
|
|
||||||
|
```text
|
||||||
|
RULES.md
|
||||||
|
docs/rules/200-GOVERNANCE.md
|
||||||
|
docs/rules/210-WORKFLOW.md
|
||||||
|
docs/rules/300-DOCUMENTATION.md
|
||||||
|
docs/rules/400-VALIDATION.md
|
||||||
|
docs/rules/510-RUST_CARGO.md
|
||||||
|
docs/rules/530-WEB_FRONTEND.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mission
|
||||||
|
|
||||||
|
`0.0.3` matérialise l'écriture Git générique contrôlée et un push gardé indépendant d'une forge.
|
||||||
|
|
||||||
|
La release doit pouvoir :
|
||||||
|
|
||||||
|
- partir d'une `GitRevision` de base explicite ;
|
||||||
|
- créer un worktree d'écriture isolé sous la racine d'état contrôlée ;
|
||||||
|
- appliquer des mutations de fichiers confinées ;
|
||||||
|
- créer un commit avec identité explicite et hooks neutralisés ;
|
||||||
|
- produire une révision candidate ;
|
||||||
|
- mettre à jour une branche `refs/heads/...` uniquement si son état distant correspond à l'attente fournie ;
|
||||||
|
- signaler un conflit sans écraser une divergence concurrente.
|
||||||
|
|
||||||
|
## Contrats à préserver
|
||||||
|
|
||||||
|
Réutiliser la séparation actuelle :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-git-api contrats passifs provider-neutral
|
||||||
|
knowledge-git-lib implémentation process Git contrôlée
|
||||||
|
knowledge-app-lib orchestration application existante, sans backend Git/DB/forge
|
||||||
|
```
|
||||||
|
|
||||||
|
Ne pas transformer le contrat de lecture existant en interface d'écriture obligatoire. Introduire un contrat distinct seulement lorsque les types d'écriture sont matérialisés.
|
||||||
|
|
||||||
|
## Étude obligatoire de cadrage
|
||||||
|
|
||||||
|
Avant toute implémentation fonctionnelle, vérifier :
|
||||||
|
|
||||||
|
- pourquoi l'écriture Git est le prochain axe utile parmi les `DEFER` ;
|
||||||
|
- worktree d'écriture isolé versus plumbing Git ;
|
||||||
|
- confinement des chemins et risques `.git`/`..`/symlinks ;
|
||||||
|
- identité auteur/committer explicite ;
|
||||||
|
- neutralisation des hooks et prompts ;
|
||||||
|
- sémantique d'attente distante/compare-and-exchange ;
|
||||||
|
- création d'une branche absente ;
|
||||||
|
- conflit sur branche divergente ;
|
||||||
|
- cleanup après succès/échec ;
|
||||||
|
- redaction des erreurs ;
|
||||||
|
- tests reproductibles avec remote bare local.
|
||||||
|
|
||||||
|
Le résultat est documenté dans :
|
||||||
|
|
||||||
|
```text
|
||||||
|
docs/studies/006-V0_0_3_GIT_WRITE_STUDY.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Non-objectifs
|
||||||
|
|
||||||
|
Ne pas absorber par anticipation :
|
||||||
|
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- workflow complet de proposition/revue ;
|
||||||
|
- PostgreSQL/migrations ;
|
||||||
|
- Gitea ou autre API de forge ;
|
||||||
|
- auth/admin ;
|
||||||
|
- routes HTTP d'écriture ;
|
||||||
|
- UI de contribution ;
|
||||||
|
- signature de commits ;
|
||||||
|
- scheduler/jobs ;
|
||||||
|
- formats finaux HOWTO/snippet ;
|
||||||
|
- Git LFS/submodules ;
|
||||||
|
- partage multi-processus de la racine Git.
|
||||||
|
|
||||||
|
## Première tranche
|
||||||
|
|
||||||
|
`0.0.3-alpha.1` doit :
|
||||||
|
|
||||||
|
1. vérifier intégralement la stable `v0.0.2` et son rapport post-tag ;
|
||||||
|
2. relire les règles/owners locaux, architecture, roadmap, deltas/histories et `DEFER` ;
|
||||||
|
3. confirmer qu'aucune adoption PGR n'est engagée ;
|
||||||
|
4. déterminer et documenter l'écriture Git contrôlée comme mission `0.0.3` ;
|
||||||
|
5. produire l'étude `006` et le plan `003` ;
|
||||||
|
6. mettre à jour les documents vivants nécessaires ;
|
||||||
|
7. ne modifier aucun fichier produit sous `crates/` ou `Web/` ;
|
||||||
|
8. préparer la gate de cadrage puis attendre le résultat opérateur avant tout history.
|
||||||
|
|
||||||
|
## Sortie attendue
|
||||||
|
|
||||||
|
À la fin de `0.0.3`, Knowledge doit posséder une primitive d'écriture Git générique, sûre et testable permettant de produire puis pousser une révision candidate sans dépendre d'une forge et sans écraser silencieusement une branche ayant divergé.
|
||||||
140
prompts/004-V0_0_4_START_PROMPT.md
Normal file
140
prompts/004-V0_0_4_START_PROMPT.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<!-- file: prompts/004-V0_0_4_START_PROMPT.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Knowledge 0.0.4 — prompt de démarrage
|
||||||
|
|
||||||
|
## Condition d'utilisation
|
||||||
|
|
||||||
|
Ce prompt est préparé pendant la stabilisation de `0.0.3`, après la revue d'horizon `0.0.3-beta.1` qui retient la fondation PostgreSQL partagée comme prochaine mission.
|
||||||
|
|
||||||
|
Il ne doit être exécuté qu'après publication réelle de la stable `v0.0.3` et vérification de son rapport post-tag.
|
||||||
|
|
||||||
|
Les valeurs exactes du commit stable, du SHA-256 de l'archive et du résultat `publish.001` ne sont volontairement pas écrites ici avant leur existence réelle.
|
||||||
|
|
||||||
|
## Baseline autoritaire à fournir au démarrage
|
||||||
|
|
||||||
|
La session de `0.0.4` doit recevoir :
|
||||||
|
|
||||||
|
```text
|
||||||
|
archive sasedev-knowledge-v0.0.3.zip téléchargée depuis le tag Gitea v0.0.3
|
||||||
|
rapport sasedev-knowledge-0.0.3-publish.001-report.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Avant tout développement, vérifier que :
|
||||||
|
|
||||||
|
- le rapport post-tag est `PASS` ;
|
||||||
|
- le tag déréférencé, le commit produit et le commentaire/identité de l'archive convergent ;
|
||||||
|
- `VERSION` et `[workspace.package].version` valent `0.0.3` dans l'archive ;
|
||||||
|
- la chaîne deltas/histories de `0.0.3` est complète ;
|
||||||
|
- la roadmap, le changelog stable, l'étude de réconciliation `007` et le présent prompt sont cohérents avec la publication réelle.
|
||||||
|
|
||||||
|
L'archive taggée est la baseline produit. L'absence de `.git` dans cette archive est normale.
|
||||||
|
|
||||||
|
## Autonomie normative
|
||||||
|
|
||||||
|
Knowledge reste autonome.
|
||||||
|
|
||||||
|
Ne pas rechercher, charger ni adopter automatiquement une nouvelle version de PGR. PGR `v0.1.3` reste une provenance historique de l'adoption fermée dans `0.0.2`. Une nouvelle réévaluation PGR n'est autorisée que si elle est explicitement ajoutée au scope de `0.0.4` par une décision future ; elle ne fait pas partie de la mission prévue ici.
|
||||||
|
|
||||||
|
Relire en priorité :
|
||||||
|
|
||||||
|
```text
|
||||||
|
RULES.md
|
||||||
|
docs/rules/200-GOVERNANCE.md
|
||||||
|
docs/rules/210-WORKFLOW.md
|
||||||
|
docs/rules/300-DOCUMENTATION.md
|
||||||
|
docs/rules/400-VALIDATION.md
|
||||||
|
docs/rules/510-RUST_CARGO.md
|
||||||
|
docs/rules/530-WEB_FRONTEND.md
|
||||||
|
docs/architecture/001-PROJECT_PROFILE.md
|
||||||
|
docs/architecture/002-KNOWLEDGE_ARCHITECTURE.md
|
||||||
|
docs/studies/007-V0_0_3_GIT_WRITE_RECONCILIATION.md
|
||||||
|
ROADMAP.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mission prévue
|
||||||
|
|
||||||
|
`0.0.4` doit matérialiser la première fondation PostgreSQL partagée de Knowledge sans déplacer la vérité éditoriale hors de Git.
|
||||||
|
|
||||||
|
La release doit au minimum étudier puis matérialiser, dans un scope proportionné :
|
||||||
|
|
||||||
|
- le propriétaire applicatif de la persistance PostgreSQL partagée ;
|
||||||
|
- le choix du driver/ORM ou client Rust réellement nécessaire ;
|
||||||
|
- la configuration de connexion et le lifecycle/pooling ;
|
||||||
|
- un premier schéma et une chaîne de migrations reproductible ;
|
||||||
|
- la configuration logique persistante des sources lorsque cela apporte une valeur réelle ;
|
||||||
|
- la dernière révision synchronisée/indexée ou un état équivalent nécessaire aux futurs cas d'usage ;
|
||||||
|
- les invariants de reconstruction depuis Git ;
|
||||||
|
- une stratégie de tests PostgreSQL locale/éphémère reproductible et proportionnée.
|
||||||
|
|
||||||
|
## Contrats à préserver
|
||||||
|
|
||||||
|
Git reste autoritaire pour le contenu publié.
|
||||||
|
|
||||||
|
PostgreSQL peut devenir autoritaire pour l'état applicatif transversal qui n'est pas un contenu éditorial Git, par exemple configuration logique, sessions futures, états de revue futurs, index et projections reconstructibles.
|
||||||
|
|
||||||
|
Ne pas écrire le corps publié d'un contenu uniquement en PostgreSQL comme raccourci architectural.
|
||||||
|
|
||||||
|
Conserver les frontières existantes :
|
||||||
|
|
||||||
|
```text
|
||||||
|
knowledge-git-api contrats Git passifs provider-neutral
|
||||||
|
knowledge-git-lib lecture + écriture Git contrôlées
|
||||||
|
knowledge-app-lib orchestration applicative partagée
|
||||||
|
knowledge-web-lib composition Actix/Maud/Vite partagée
|
||||||
|
```
|
||||||
|
|
||||||
|
Créer une nouvelle crate uniquement si son ownership est démontré par l'étude de démarrage ; `knowledge-store-postgres-lib` reste un candidat, pas une obligation de nom ou d'architecture avant étude.
|
||||||
|
|
||||||
|
## Étude obligatoire de démarrage
|
||||||
|
|
||||||
|
Avant la première implémentation PostgreSQL, comparer au moins :
|
||||||
|
|
||||||
|
- clients/ORM Rust compatibles avec les règles locales et les besoins réels ;
|
||||||
|
- migrations embarquées ou outillées ;
|
||||||
|
- stratégie de pool/lifecycle ;
|
||||||
|
- représentation des identifiants de source et révisions Git ;
|
||||||
|
- séparation état autoritaire applicatif versus projection reconstructible ;
|
||||||
|
- stratégie de test locale, CI et données éphémères ;
|
||||||
|
- gestion des secrets/configurations de connexion sans valeur sensible versionnée ;
|
||||||
|
- comportement si PostgreSQL est indisponible ;
|
||||||
|
- impact sur les trois applications sans dupliquer trois bases ou trois implémentations.
|
||||||
|
|
||||||
|
Ne pas sélectionner une technologie par simple familiarité ou par copie d'un autre projet.
|
||||||
|
|
||||||
|
## Hors scope initial
|
||||||
|
|
||||||
|
Ne pas absorber automatiquement dans `0.0.4` :
|
||||||
|
|
||||||
|
- formats finaux PGR/HOWTO/snippet et parsing complet ;
|
||||||
|
- templates publics des trois sites ;
|
||||||
|
- templates d'administration ;
|
||||||
|
- routes publiques finales Actix/Maud ;
|
||||||
|
- auth complète et modèle final de rôles/capabilities ;
|
||||||
|
- workflow complet de proposition/revue/publication ;
|
||||||
|
- adapter Gitea ou autre forge ;
|
||||||
|
- moteur de recherche avancé ;
|
||||||
|
- scheduler/jobs ;
|
||||||
|
- rebase/merge automatique ;
|
||||||
|
- partage multi-processus de la racine Git ;
|
||||||
|
- Git LFS/submodules.
|
||||||
|
|
||||||
|
Ces éléments appartiennent à l'horizon réévaluable documenté par `0.0.3-beta.1`.
|
||||||
|
|
||||||
|
## Première tranche attendue
|
||||||
|
|
||||||
|
`0.0.4-alpha.1` doit :
|
||||||
|
|
||||||
|
1. vérifier intégralement la stable `v0.0.3` et son rapport post-tag réels ;
|
||||||
|
2. relire les owners locaux, architecture, étude `007`, roadmap, deltas/histories et décisions `DEFER` ;
|
||||||
|
3. confirmer qu'aucune adoption PGR n'est engagée ;
|
||||||
|
4. étudier les choix PostgreSQL sans créer prématurément une architecture de stockage ;
|
||||||
|
5. décider l'ownership, les contrats, le premier schéma utile, la stratégie de migration et la stratégie de test ;
|
||||||
|
6. produire l'étude et le plan `0.0.4` correspondants ;
|
||||||
|
7. mettre à jour l'horizon seulement si cette étude rend une dépendance future fausse ou insuffisante ;
|
||||||
|
8. ne rédiger le changelog `0.0.4` qu'à l'approche de sa stabilisation ;
|
||||||
|
9. préparer une gate de cadrage proportionnée puis attendre son résultat opérateur avant tout history.
|
||||||
|
|
||||||
|
## Sortie attendue de 0.0.4
|
||||||
|
|
||||||
|
À la fin de `0.0.4`, Knowledge doit posséder une fondation PostgreSQL partagée, testée et migrable, capable de conserver l'état transversal minimal requis par les prochaines projections et façades tout en restant reconstructible/cohérente avec Git lorsque les données sont dérivées du contenu publié.
|
||||||
Reference in New Issue
Block a user