mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 21:41:42 +02:00
mcp: consolidate lot 1 metadata and planning
This commit is contained in:
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Patterns : Auth
|
||||||
|
domain: backend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [auth, requestid, api-errors, sessions, tokens]
|
||||||
|
applies_to: [analysis, implementation, review, debug]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-16
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Patterns : Auth
|
# Backend — Patterns : Auth
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Patterns : Contracts
|
||||||
|
domain: backend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [contracts, zod, api, error-codes, monorepo]
|
||||||
|
applies_to: [analysis, implementation, review, architecture]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-20
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Patterns : Contracts
|
# Backend — Patterns : Contracts
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Patterns : NestJS
|
||||||
|
domain: backend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [nestjs, guards, auth, redis, quota]
|
||||||
|
applies_to: [analysis, implementation, review, debug]
|
||||||
|
severity: medium
|
||||||
|
validated_on: 2026-03-07
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Patterns : NestJS
|
# Backend — Patterns : NestJS
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/patterns/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Risques & vigilance : Auth
|
||||||
|
domain: backend
|
||||||
|
bucket: risques
|
||||||
|
tags: [auth, guards, request-user, sessions, admin]
|
||||||
|
applies_to: [implementation, review, debug]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-24
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Risques & vigilance : Auth
|
# Backend — Risques & vigilance : Auth
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Risques & vigilance : Contracts
|
||||||
|
domain: backend
|
||||||
|
bucket: risques
|
||||||
|
tags: [contracts, zod, validation, error-codes, requestid]
|
||||||
|
applies_to: [analysis, implementation, review, debug]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-24
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Risques & vigilance : Contracts
|
# Backend — Risques & vigilance : Contracts
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Risques & vigilance : NestJS
|
||||||
|
domain: backend
|
||||||
|
bucket: risques
|
||||||
|
tags: [nestjs, controllers, guards, providers, review]
|
||||||
|
applies_to: [implementation, review, debug]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-30
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Risques & vigilance : NestJS
|
# Backend — Risques & vigilance : NestJS
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Backend — Risques & vigilance : Prisma
|
||||||
|
domain: backend
|
||||||
|
bucket: risques
|
||||||
|
tags: [prisma, transactions, tenant, schema, race-condition]
|
||||||
|
applies_to: [implementation, review, debug, architecture]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-23
|
||||||
|
source_projects: [app-template-resto, app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Backend — Risques & vigilance : Prisma
|
# Backend — Risques & vigilance : Prisma
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/backend/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Frontend — Patterns : Navigation
|
||||||
|
domain: frontend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [navigation, react, expo-router, useeffect, async]
|
||||||
|
applies_to: [analysis, implementation, review]
|
||||||
|
severity: medium
|
||||||
|
validated_on: 2026-03-21
|
||||||
|
source_projects: [app-template-resto, app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Frontend — Patterns : Navigation
|
# Frontend — Patterns : Navigation
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/patterns/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/patterns/README.md` pour l'index complet.
|
||||||
@@ -165,4 +176,3 @@ return (
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Frontend — Patterns : Tests
|
||||||
|
domain: frontend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [tests, react-native, jest, styles, ui]
|
||||||
|
applies_to: [implementation, review]
|
||||||
|
severity: medium
|
||||||
|
validated_on: 2026-03-19
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Frontend — Patterns : Tests
|
# Frontend — Patterns : Tests
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/patterns/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/patterns/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Frontend — Risques & vigilance : Navigation
|
||||||
|
domain: frontend
|
||||||
|
bucket: risques
|
||||||
|
tags: [navigation, expo-router, zustand, useeffect, deep-link]
|
||||||
|
applies_to: [implementation, review, debug]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-25
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
|
||||||
# Frontend — Risques & vigilance : Navigation
|
# Frontend — Risques & vigilance : Navigation
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/frontend/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: Workflow — Risques & vigilance : Story tracking
|
||||||
|
domain: workflow
|
||||||
|
bucket: risques
|
||||||
|
tags: [bmad, story, file-list, review, completion]
|
||||||
|
applies_to: [analysis, implementation, review]
|
||||||
|
severity: high
|
||||||
|
validated_on: 2026-03-31
|
||||||
|
source_projects: [app-alexandrie, app-template-resto]
|
||||||
|
---
|
||||||
|
|
||||||
# Workflow — Risques & vigilance : Story tracking
|
# Workflow — Risques & vigilance : Story tracking
|
||||||
|
|
||||||
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/workflow/risques/README.md` pour l'index complet.
|
> Extrait de la base de connaissance Lead_tech. Voir `knowledge/workflow/risques/README.md` pour l'index complet.
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ Serveur MCP **sidecar** pour brancher la base Lead_tech dans un workflow BMAD sa
|
|||||||
|
|
||||||
Etat actuel : **prototype exploitable** pour un rollout advisory.
|
Etat actuel : **prototype exploitable** pour un rollout advisory.
|
||||||
|
|
||||||
|
Documents de référence phase 1 :
|
||||||
|
|
||||||
|
- `docs/mcp_v1.md` — contrat fonctionnel stable pour les tools/resources
|
||||||
|
- `docs/knowledge_metadata.md` — format de front matter pour `knowledge/`
|
||||||
|
- `docs/implementation_plan.md` — plan de chantier et checklist inter-sessions
|
||||||
|
|
||||||
## Objectif
|
## Objectif
|
||||||
|
|
||||||
- BMAD garde l'orchestration (story, roles, statut, handoff).
|
- BMAD garde l'orchestration (story, roles, statut, handoff).
|
||||||
|
|||||||
144
mcp/leadtech_bmad_mcp/docs/implementation_plan.md
Normal file
144
mcp/leadtech_bmad_mcp/docs/implementation_plan.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# leadtech-bmad-mcp — Plan d'implementation
|
||||||
|
|
||||||
|
Ce document sert de **plan de chantier vivant** pour transformer le prototype actuel en MCP propre, stable et exploitable dans BMAD.
|
||||||
|
|
||||||
|
Mode d'usage :
|
||||||
|
|
||||||
|
- cocher les taches terminees
|
||||||
|
- mettre a jour le statut des lots en debut/fin de session
|
||||||
|
- ajouter les decisions structurantes dans `40_decisions_et_archi.md` si besoin
|
||||||
|
- garder ici le suivi operationnel, pas les longues explications
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
| Lot | Objectif | Statut |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Lot 1 | Contrat MCP v1 + metadonnees `knowledge` + compatibilite loader | En cours avance |
|
||||||
|
| Lot 2 | Index compile local + branchement de la recherche MCP dessus | A faire |
|
||||||
|
| Lot 3 | Gates configurables + packaging + rollout BMAD | A faire |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lot 1 — Contrat v1 et base structuree
|
||||||
|
|
||||||
|
### Objectif
|
||||||
|
|
||||||
|
Stabiliser le contrat du MCP et preparer un corpus `knowledge/` assez structure pour servir de base au futur index.
|
||||||
|
|
||||||
|
### Livrables
|
||||||
|
|
||||||
|
- [x] Documenter le contrat MCP v1
|
||||||
|
- [x] Documenter le format de front matter `knowledge/`
|
||||||
|
- [x] Ajouter la lecture du front matter dans le loader
|
||||||
|
- [x] Exposer un champ structure `matched_docs` dans `get_guidance`
|
||||||
|
- [x] Ajouter des tests sur le parsing des metadonnees
|
||||||
|
- [x] Annoter un premier noyau de documents critiques
|
||||||
|
|
||||||
|
### Noyau pilote vise
|
||||||
|
|
||||||
|
- [x] `knowledge/backend/patterns/auth.md`
|
||||||
|
- [x] `knowledge/backend/patterns/contracts.md`
|
||||||
|
- [x] `knowledge/backend/patterns/nestjs.md`
|
||||||
|
- [x] `knowledge/backend/risques/auth.md`
|
||||||
|
- [x] `knowledge/backend/risques/contracts.md`
|
||||||
|
- [x] `knowledge/backend/risques/nestjs.md`
|
||||||
|
- [x] `knowledge/backend/risques/prisma.md`
|
||||||
|
- [x] `knowledge/frontend/patterns/navigation.md`
|
||||||
|
- [x] `knowledge/frontend/patterns/tests.md`
|
||||||
|
- [x] `knowledge/frontend/risques/navigation.md`
|
||||||
|
- [x] `knowledge/workflow/risques/story-tracking.md`
|
||||||
|
|
||||||
|
### Reste a faire avant cloture complete du lot
|
||||||
|
|
||||||
|
- [ ] Verifier si `knowledge/backend/patterns/prisma.md` doit aussi entrer dans le noyau pilote
|
||||||
|
- [ ] Verifier si `knowledge/frontend/risques/tests.md` doit aussi entrer dans le noyau pilote
|
||||||
|
- [ ] Faire un commit de cloture explicite du Lot 1
|
||||||
|
|
||||||
|
### Critere de fin
|
||||||
|
|
||||||
|
- le contrat v1 est stable
|
||||||
|
- un noyau representatif de docs critiques est structure
|
||||||
|
- la recherche MCP sait exploiter les metadonnees sans casser le comportement existant
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lot 2 — Index compile local
|
||||||
|
|
||||||
|
### Objectif
|
||||||
|
|
||||||
|
Remplacer le scan Markdown a la volee par un index local plus rapide, plus fiable et plus facile a faire evoluer.
|
||||||
|
|
||||||
|
### Taches
|
||||||
|
|
||||||
|
- [ ] Definir le format de l'index (JSON d'abord)
|
||||||
|
- [ ] Creer un script de build d'index
|
||||||
|
- [ ] Indexer les docs `knowledge/*`
|
||||||
|
- [ ] Indexer les docs globaux `10_*`, `40_*`, `90_*`
|
||||||
|
- [ ] Prevoir un mode fallback si l'index n'existe pas
|
||||||
|
- [ ] Rebrancher `search_knowledge()` sur l'index
|
||||||
|
- [ ] Rebrancher `search_global_docs()` sur l'index
|
||||||
|
- [ ] Ajouter des tests d'integration sur un mini corpus indexe
|
||||||
|
|
||||||
|
### Livrables attendus
|
||||||
|
|
||||||
|
- `src/leadtech_bmad_mcp/indexer.py`
|
||||||
|
- un artefact d'index local versionnable ou regenerable
|
||||||
|
- documentation de rebuild
|
||||||
|
|
||||||
|
### Critere de fin
|
||||||
|
|
||||||
|
- les tools de recherche utilisent d'abord l'index
|
||||||
|
- le fallback texte brut reste disponible pour ne pas bloquer le dev
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lot 3 — Gates configurables et industrialisation
|
||||||
|
|
||||||
|
### Objectif
|
||||||
|
|
||||||
|
Sortir les regles du code dur, rendre l'installation reproductible, puis cabler proprement le rollout BMAD.
|
||||||
|
|
||||||
|
### Taches
|
||||||
|
|
||||||
|
- [ ] Extraire les gates dans une config versionnee
|
||||||
|
- [ ] Distinguer advisory et blocking dans la config
|
||||||
|
- [ ] Ajouter des tests sur les faux positifs/faux negatifs des gates
|
||||||
|
- [ ] Stabiliser l'installation `pip install -e ".[dev]"`
|
||||||
|
- [ ] Ajouter une doc machine vierge / quickstart
|
||||||
|
- [ ] Preparer un rollout BMAD advisory
|
||||||
|
- [ ] Definir 2-3 blocages stricts seulement apres validation
|
||||||
|
|
||||||
|
### Livrables attendus
|
||||||
|
|
||||||
|
- `config/gates.yaml`
|
||||||
|
- quickstart dev local
|
||||||
|
- procedure de rollout BMAD
|
||||||
|
|
||||||
|
### Critere de fin
|
||||||
|
|
||||||
|
- modifier une regle ne demande plus un patch Python
|
||||||
|
- une autre machine peut installer et lancer le serveur sans bricolage
|
||||||
|
- BMAD sait quand appeler quels tools et comment interpreter leur sortie
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Journal de progression
|
||||||
|
|
||||||
|
### 2026-03-31
|
||||||
|
|
||||||
|
- Lot 1 demarre
|
||||||
|
- contrat `mcp_v1.md` ajoute
|
||||||
|
- convention `knowledge_metadata.md` ajoutee
|
||||||
|
- loader front matter ajoute
|
||||||
|
- `matched_docs` ajoute a `get_guidance`
|
||||||
|
- noyau pilote annote sur backend, frontend et workflow
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Regles de maintenance de ce plan
|
||||||
|
|
||||||
|
- un lot passe a `Termine` uniquement quand son critere de fin est satisfait
|
||||||
|
- une case ne se coche pas si le code est seulement commence
|
||||||
|
- si un arbitrage durable apparait, l'inscrire aussi dans `40_decisions_et_archi.md`
|
||||||
108
mcp/leadtech_bmad_mcp/docs/knowledge_metadata.md
Normal file
108
mcp/leadtech_bmad_mcp/docs/knowledge_metadata.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# leadtech-bmad-mcp — Métadonnées `knowledge/`
|
||||||
|
|
||||||
|
Ce document définit le **front matter minimal** attendu pour rendre la base `knowledge/` mieux exploitable par le MCP.
|
||||||
|
|
||||||
|
## Objectif
|
||||||
|
|
||||||
|
- améliorer le ranking de recherche
|
||||||
|
- permettre des filtres structurés
|
||||||
|
- préparer un futur index compilé
|
||||||
|
|
||||||
|
## Format retenu
|
||||||
|
|
||||||
|
Chaque document `knowledge/*/*.md` peut commencer par :
|
||||||
|
|
||||||
|
```md
|
||||||
|
---
|
||||||
|
title: Backend — Patterns : NestJS
|
||||||
|
domain: backend
|
||||||
|
bucket: patterns
|
||||||
|
tags: [nestjs, guards, auth, redis]
|
||||||
|
applies_to: [analysis, implementation, review, debug]
|
||||||
|
severity: medium
|
||||||
|
validated_on: 2026-03-07
|
||||||
|
source_projects: [app-alexandrie]
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
Le front matter est **optionnel** dans la phase 1.
|
||||||
|
|
||||||
|
Le loader doit :
|
||||||
|
|
||||||
|
- fonctionner avec ou sans front matter
|
||||||
|
- ignorer proprement les champs inconnus
|
||||||
|
- continuer à servir le markdown brut si demandé en resource
|
||||||
|
|
||||||
|
## Champs v1
|
||||||
|
|
||||||
|
### `title`
|
||||||
|
|
||||||
|
- titre logique du document
|
||||||
|
- type : `string`
|
||||||
|
|
||||||
|
### `domain`
|
||||||
|
|
||||||
|
- domaine principal
|
||||||
|
- type : `backend | frontend | ux | n8n | product | workflow`
|
||||||
|
|
||||||
|
### `bucket`
|
||||||
|
|
||||||
|
- type de document
|
||||||
|
- type : `patterns | risques`
|
||||||
|
|
||||||
|
### `tags`
|
||||||
|
|
||||||
|
- mots-clés de recherche
|
||||||
|
- type : `string[]`
|
||||||
|
|
||||||
|
### `applies_to`
|
||||||
|
|
||||||
|
- moments du workflow où le document est pertinent
|
||||||
|
- type : `analysis | implementation | review | debug | architecture`
|
||||||
|
- type de stockage : `string[]`
|
||||||
|
|
||||||
|
### `severity`
|
||||||
|
|
||||||
|
- niveau de criticité métier ou technique du document
|
||||||
|
- type : `low | medium | high`
|
||||||
|
|
||||||
|
### `validated_on`
|
||||||
|
|
||||||
|
- date de validation terrain
|
||||||
|
- type : `YYYY-MM-DD`
|
||||||
|
|
||||||
|
### `source_projects`
|
||||||
|
|
||||||
|
- projets ayant validé le pattern ou révélé le risque
|
||||||
|
- type : `string[]`
|
||||||
|
|
||||||
|
## Règles de gouvernance
|
||||||
|
|
||||||
|
- les README d'index n'ont pas besoin de front matter dans la phase 1
|
||||||
|
- priorité aux documents les plus consultés ou les plus critiques
|
||||||
|
- ne pas bloquer une capitalisation parce que le front matter n'existe pas encore
|
||||||
|
|
||||||
|
## Politique de migration
|
||||||
|
|
||||||
|
Phase 1 :
|
||||||
|
- ajouter le front matter à un petit noyau de documents pilotes
|
||||||
|
|
||||||
|
Noyau pilote actuellement couvert :
|
||||||
|
|
||||||
|
- `knowledge/backend/patterns/auth.md`
|
||||||
|
- `knowledge/backend/patterns/contracts.md`
|
||||||
|
- `knowledge/backend/patterns/nestjs.md`
|
||||||
|
- `knowledge/backend/risques/auth.md`
|
||||||
|
- `knowledge/backend/risques/contracts.md`
|
||||||
|
- `knowledge/backend/risques/nestjs.md`
|
||||||
|
- `knowledge/backend/risques/prisma.md`
|
||||||
|
- `knowledge/frontend/patterns/navigation.md`
|
||||||
|
- `knowledge/frontend/patterns/tests.md`
|
||||||
|
- `knowledge/frontend/risques/navigation.md`
|
||||||
|
- `knowledge/workflow/risques/story-tracking.md`
|
||||||
|
|
||||||
|
Phase 2 :
|
||||||
|
- généraliser aux documents backend, frontend et workflow les plus actifs
|
||||||
|
|
||||||
|
Phase 3 :
|
||||||
|
- exiger ces métadonnées pour l'index compilé
|
||||||
209
mcp/leadtech_bmad_mcp/docs/mcp_v1.md
Normal file
209
mcp/leadtech_bmad_mcp/docs/mcp_v1.md
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# leadtech-bmad-mcp — Contrat v1
|
||||||
|
|
||||||
|
Ce document fige le **contrat fonctionnel v1** du sidecar MCP Lead_tech.
|
||||||
|
|
||||||
|
Objectif :
|
||||||
|
|
||||||
|
- stabiliser ce que le serveur promet aux agents BMAD
|
||||||
|
- éviter les changements de payload implicites
|
||||||
|
- préparer les itérations suivantes (index compilé, gates configurables) sans casser les prompts existants
|
||||||
|
|
||||||
|
## Positionnement
|
||||||
|
|
||||||
|
- BMAD garde l'orchestration
|
||||||
|
- Lead_tech garde la doctrine et la validation humaine
|
||||||
|
- le MCP sidecar fournit une couche outillée de lecture, guidance, gate qualité, et capitalisation
|
||||||
|
|
||||||
|
## Version fonctionnelle
|
||||||
|
|
||||||
|
- Nom logique : `leadtech-bmad-mcp`
|
||||||
|
- Version de contrat : `v1`
|
||||||
|
- Statut : advisory-first
|
||||||
|
|
||||||
|
## Outils v1
|
||||||
|
|
||||||
|
### `get_guidance(domain, task_type, story_text?, keywords?, max_items?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- retrouver les patterns, risques et docs globaux pertinents pour une story
|
||||||
|
|
||||||
|
Entrées :
|
||||||
|
- `domain` : `backend | frontend | ux | n8n | product | workflow`
|
||||||
|
- `task_type` : `analysis | implementation | review | debug | architecture`
|
||||||
|
- `story_text` : texte libre
|
||||||
|
- `keywords` : liste de mots-clés optionnelle
|
||||||
|
- `max_items` : entier optionnel
|
||||||
|
|
||||||
|
Sortie :
|
||||||
|
- `must_do[]`
|
||||||
|
- `should_do[]`
|
||||||
|
- `red_flags[]`
|
||||||
|
- `blocking_issues[]`
|
||||||
|
- `references[]`
|
||||||
|
- `confidence`
|
||||||
|
- `gates[]`
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- advisory par défaut
|
||||||
|
- pas d'écriture
|
||||||
|
|
||||||
|
### `validate_plan(domain, plan_text, agent_role?, strict?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- vérifier qu'un plan BMAD couvre les gates Lead_tech les plus importantes
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- `strict=true` peut produire des `blocking_issues`
|
||||||
|
- `strict=false` reste advisory
|
||||||
|
|
||||||
|
### `validate_patch(domain, diff_text, changed_files?, strict?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- contrôler un diff par rapport à des gates Lead_tech
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- détecte les signaux faibles, pas une preuve formelle de conformité
|
||||||
|
- un résultat vide ne remplace jamais une review humaine
|
||||||
|
|
||||||
|
### `emit_checklist(agent_role, domain, story_text?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- produire une checklist opérationnelle par rôle BMAD
|
||||||
|
|
||||||
|
### `propose_capitalization(project_name, target_file, why, proposal, dry_run?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- préparer une entrée `FILE_UPDATE_PROPOSAL` pour `95_a_capitaliser.md`
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- `dry_run=true` par défaut
|
||||||
|
- écriture réelle uniquement si `LEADTECH_MCP_ALLOW_WRITE=1`
|
||||||
|
|
||||||
|
### `triage_capitalization(project_filter?, max_entries?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- analyser les entrées du buffer de capitalisation
|
||||||
|
|
||||||
|
Statut :
|
||||||
|
- heuristique, non décisionnaire
|
||||||
|
|
||||||
|
### `route_to_project_memory(project_name, section, content, dry_run?)`
|
||||||
|
|
||||||
|
Rôle :
|
||||||
|
- router un apprentissage de portée projet vers le `CLAUDE.md` du projet
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- écriture réelle uniquement si `LEADTECH_MCP_ALLOW_WRITE=1`
|
||||||
|
|
||||||
|
## Resources v1
|
||||||
|
|
||||||
|
- `leadtech://index`
|
||||||
|
- `leadtech://capitalisation/pending`
|
||||||
|
- `leadtech://projects/conf`
|
||||||
|
- `leadtech://knowledge/{domain}/{bucket}/{slug}`
|
||||||
|
- `leadtech://global/architecture`
|
||||||
|
- `leadtech://global/debug`
|
||||||
|
- `leadtech://global/conventions`
|
||||||
|
|
||||||
|
## Payload commun
|
||||||
|
|
||||||
|
Les tools de guidance et de validation retournent un payload homogène :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"must_do": [],
|
||||||
|
"should_do": [],
|
||||||
|
"red_flags": [],
|
||||||
|
"blocking_issues": [],
|
||||||
|
"confidence": "MEDIUM",
|
||||||
|
"references": [],
|
||||||
|
"matched_docs": [],
|
||||||
|
"gates": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Niveaux de sévérité
|
||||||
|
|
||||||
|
- `must_do` : action attendue fortement recommandée
|
||||||
|
- `should_do` : amélioration ou vérification utile
|
||||||
|
- `red_flags` : signal de risque ou d'incertitude
|
||||||
|
- `blocking_issues` : empêche la progression en mode strict
|
||||||
|
|
||||||
|
## Confiance
|
||||||
|
|
||||||
|
- `HIGH`
|
||||||
|
- `MEDIUM`
|
||||||
|
- `LOW`
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- la confiance exprime la qualité du matching heuristique, pas la vérité terrain
|
||||||
|
|
||||||
|
## Références
|
||||||
|
|
||||||
|
`references[]` contient :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "/abs/path/doc.md",
|
||||||
|
"reason": "Match patterns score=7"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- toujours renvoyer des chemins absolus si possible
|
||||||
|
- référencer la source, pas uniquement un résumé
|
||||||
|
|
||||||
|
## Matched docs
|
||||||
|
|
||||||
|
`matched_docs[]` expose les meilleurs documents remontés par le moteur de guidance.
|
||||||
|
|
||||||
|
Structure cible v1 :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"path": "/abs/path/doc.md",
|
||||||
|
"bucket": "patterns",
|
||||||
|
"title": "Backend — Patterns : NestJS",
|
||||||
|
"score": "12",
|
||||||
|
"severity": "medium",
|
||||||
|
"applies_to": "analysis, implementation, review, debug",
|
||||||
|
"tags": "nestjs, guards, auth, redis, quota",
|
||||||
|
"read_uri": "leadtech://knowledge/backend/patterns/nestjs"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Règle :
|
||||||
|
- champ informatif, non bloquant
|
||||||
|
- utile pour les agents qui veulent afficher ou prioriser explicitement les docs remontés
|
||||||
|
|
||||||
|
## Erreurs
|
||||||
|
|
||||||
|
Conventions v1 :
|
||||||
|
|
||||||
|
- tool de lecture : lever une erreur explicite si ressource introuvable
|
||||||
|
- tool d'écriture : retourner un objet `{ "error": "..." }` si écriture désactivée ou cible introuvable
|
||||||
|
- ne jamais écrire dans `knowledge/*`
|
||||||
|
|
||||||
|
## Sécurité d'écriture
|
||||||
|
|
||||||
|
Écritures autorisées uniquement vers :
|
||||||
|
|
||||||
|
- `95_a_capitaliser.md`
|
||||||
|
- `CLAUDE.md` projet
|
||||||
|
|
||||||
|
Écritures interdites :
|
||||||
|
|
||||||
|
- `knowledge/*`
|
||||||
|
- `10_*`
|
||||||
|
- `40_*`
|
||||||
|
- `90_*`
|
||||||
|
|
||||||
|
## Compatibilité BMAD
|
||||||
|
|
||||||
|
Ce contrat v1 est pensé pour :
|
||||||
|
|
||||||
|
- enrichir une story à l'entrée
|
||||||
|
- bloquer certains cas en pre-implémentation ou post-patch
|
||||||
|
- tracer la décision humaine dans la story
|
||||||
|
|
||||||
|
Les prompts BMAD doivent considérer ce contrat comme stable tant qu'un document `v2` n'existe pas.
|
||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
VALID_DOMAINS = {"backend", "frontend", "ux", "n8n", "product", "workflow"}
|
VALID_DOMAINS = {"backend", "frontend", "ux", "n8n", "product", "workflow"}
|
||||||
@@ -17,6 +18,13 @@ class LeadtechPaths:
|
|||||||
projects_conf: Path
|
projects_conf: Path
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class KnowledgeDocument:
|
||||||
|
path: Path
|
||||||
|
metadata: dict[str, Any]
|
||||||
|
body: str
|
||||||
|
|
||||||
|
|
||||||
def get_paths() -> LeadtechPaths:
|
def get_paths() -> LeadtechPaths:
|
||||||
root = Path(os.getenv("LEADTECH_ROOT", "/srv/helpers/_Assistant_Lead_Tech")).resolve()
|
root = Path(os.getenv("LEADTECH_ROOT", "/srv/helpers/_Assistant_Lead_Tech")).resolve()
|
||||||
return LeadtechPaths(
|
return LeadtechPaths(
|
||||||
@@ -52,6 +60,56 @@ def read_text(path: Path) -> str:
|
|||||||
EXCERPT_LENGTH = 400
|
EXCERPT_LENGTH = 400
|
||||||
|
|
||||||
|
|
||||||
|
def _coerce_metadata_scalar(value: str) -> Any:
|
||||||
|
raw = value.strip()
|
||||||
|
if not raw:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
low = raw.lower()
|
||||||
|
if low in {"true", "false"}:
|
||||||
|
return low == "true"
|
||||||
|
|
||||||
|
if raw.startswith("[") and raw.endswith("]"):
|
||||||
|
inner = raw[1:-1].strip()
|
||||||
|
if not inner:
|
||||||
|
return []
|
||||||
|
return [item.strip().strip("'\"") for item in inner.split(",") if item.strip()]
|
||||||
|
|
||||||
|
return raw.strip("'\"")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_front_matter(content: str) -> tuple[dict[str, Any], str]:
|
||||||
|
if not content.startswith("---\n"):
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
lines = content.splitlines()
|
||||||
|
end_idx = None
|
||||||
|
for idx in range(1, len(lines)):
|
||||||
|
if lines[idx].strip() == "---":
|
||||||
|
end_idx = idx
|
||||||
|
break
|
||||||
|
|
||||||
|
if end_idx is None:
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
metadata: dict[str, Any] = {}
|
||||||
|
for line in lines[1:end_idx]:
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or stripped.startswith("#") or ":" not in stripped:
|
||||||
|
continue
|
||||||
|
key, value = stripped.split(":", 1)
|
||||||
|
metadata[key.strip()] = _coerce_metadata_scalar(value)
|
||||||
|
|
||||||
|
body = "\n".join(lines[end_idx + 1 :]).lstrip("\n")
|
||||||
|
return metadata, body
|
||||||
|
|
||||||
|
|
||||||
|
def read_knowledge_document(path: Path) -> KnowledgeDocument:
|
||||||
|
raw = read_text(path)
|
||||||
|
metadata, body = parse_front_matter(raw)
|
||||||
|
return KnowledgeDocument(path=path, metadata=metadata, body=body)
|
||||||
|
|
||||||
|
|
||||||
def _extract_excerpt(content: str, tokens: list[str]) -> str:
|
def _extract_excerpt(content: str, tokens: list[str]) -> str:
|
||||||
"""Retourne un extrait centré sur la première occurrence d'un token, ou le début du fichier."""
|
"""Retourne un extrait centré sur la première occurrence d'un token, ou le début du fichier."""
|
||||||
low = content.lower()
|
low = content.lower()
|
||||||
@@ -78,17 +136,23 @@ def search_knowledge(domain: str, query: str, bucket: str | None = None, max_ite
|
|||||||
|
|
||||||
for b in buckets:
|
for b in buckets:
|
||||||
for file_path in list_domain_files(domain, b):
|
for file_path in list_domain_files(domain, b):
|
||||||
content = read_text(file_path)
|
doc = read_knowledge_document(file_path)
|
||||||
score = sum(content.lower().count(tok) for tok in tokens)
|
body_low = doc.body.lower()
|
||||||
|
metadata_text = " ".join(str(value) for value in doc.metadata.values()).lower()
|
||||||
|
score = sum(body_low.count(tok) for tok in tokens)
|
||||||
|
score += sum(metadata_text.count(tok) * 3 for tok in tokens)
|
||||||
if score <= 0:
|
if score <= 0:
|
||||||
continue
|
continue
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
"path": str(file_path),
|
"path": str(file_path),
|
||||||
"bucket": b,
|
"bucket": b,
|
||||||
"title": file_path.stem,
|
"title": str(doc.metadata.get("title", file_path.stem)),
|
||||||
"score": str(score),
|
"score": str(score),
|
||||||
"excerpt": _extract_excerpt(content, tokens),
|
"excerpt": _extract_excerpt(doc.body, tokens),
|
||||||
|
"tags": ", ".join(doc.metadata.get("tags", [])),
|
||||||
|
"severity": str(doc.metadata.get("severity", "")),
|
||||||
|
"applies_to": ", ".join(doc.metadata.get("applies_to", [])),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ def empty_gate_output() -> dict[str, Any]:
|
|||||||
"blocking_issues": [],
|
"blocking_issues": [],
|
||||||
"confidence": CONFIDENCE_MEDIUM,
|
"confidence": CONFIDENCE_MEDIUM,
|
||||||
"references": [],
|
"references": [],
|
||||||
|
"matched_docs": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,19 @@ def _base_output() -> dict:
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def _metadata_label(item: dict[str, str]) -> str:
|
||||||
|
parts: list[str] = []
|
||||||
|
if item.get("severity"):
|
||||||
|
parts.append(f"severity={item['severity']}")
|
||||||
|
if item.get("applies_to"):
|
||||||
|
parts.append(f"applies_to={item['applies_to']}")
|
||||||
|
if item.get("tags"):
|
||||||
|
parts.append(f"tags={item['tags']}")
|
||||||
|
if not parts:
|
||||||
|
return ""
|
||||||
|
return " [" + " | ".join(parts) + "]"
|
||||||
|
|
||||||
|
|
||||||
@mcp.resource("leadtech://index")
|
@mcp.resource("leadtech://index")
|
||||||
def resource_index() -> str:
|
def resource_index() -> str:
|
||||||
return read_text(get_paths().root / "00_INDEX.md")
|
return read_text(get_paths().root / "00_INDEX.md")
|
||||||
@@ -89,12 +102,25 @@ def get_guidance(domain: Domain, task_type: TaskType, story_text: str = "", keyw
|
|||||||
for item in matches:
|
for item in matches:
|
||||||
slug = Path(item["path"]).stem
|
slug = Path(item["path"]).stem
|
||||||
read_uri = f"leadtech://knowledge/{domain}/{item['bucket']}/{slug}"
|
read_uri = f"leadtech://knowledge/{domain}/{item['bucket']}/{slug}"
|
||||||
label = f"{item['title']} — {item['excerpt']} [lire complet: {read_uri}]"
|
metadata = _metadata_label(item)
|
||||||
|
label = f"{item['title']}{metadata} — {item['excerpt']} [lire complet: {read_uri}]"
|
||||||
if item["bucket"] == "patterns":
|
if item["bucket"] == "patterns":
|
||||||
out["must_do"].append(f"Appliquer: {label}")
|
out["must_do"].append(f"Appliquer: {label}")
|
||||||
else:
|
else:
|
||||||
out["red_flags"].append(f"Surveiller: {label}")
|
out["red_flags"].append(f"Surveiller: {label}")
|
||||||
add_reference(out, item["path"], f"Match {item['bucket']} score={item['score']}")
|
add_reference(out, item["path"], f"Match {item['bucket']} score={item['score']}")
|
||||||
|
out["matched_docs"].append(
|
||||||
|
{
|
||||||
|
"path": item["path"],
|
||||||
|
"bucket": item["bucket"],
|
||||||
|
"title": item["title"],
|
||||||
|
"score": item["score"],
|
||||||
|
"severity": item.get("severity", ""),
|
||||||
|
"applies_to": item.get("applies_to", ""),
|
||||||
|
"tags": item.get("tags", ""),
|
||||||
|
"read_uri": read_uri,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Fichiers globaux (decisions, postmortems, conventions)
|
# Fichiers globaux (decisions, postmortems, conventions)
|
||||||
global_matches = search_global_docs(query=query, max_items=3)
|
global_matches = search_global_docs(query=query, max_items=3)
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ from leadtech_bmad_mcp.knowledge import (
|
|||||||
search_knowledge,
|
search_knowledge,
|
||||||
search_global_docs,
|
search_global_docs,
|
||||||
read_knowledge_doc,
|
read_knowledge_doc,
|
||||||
|
read_knowledge_document,
|
||||||
_extract_excerpt,
|
_extract_excerpt,
|
||||||
|
parse_front_matter,
|
||||||
LeadtechPaths,
|
LeadtechPaths,
|
||||||
get_paths,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -122,6 +123,30 @@ def test_search_knowledge_single_bucket(tmp_path):
|
|||||||
assert all(r["bucket"] == "risques" for r in results)
|
assert all(r["bucket"] == "risques" for r in results)
|
||||||
|
|
||||||
|
|
||||||
|
def test_search_knowledge_uses_front_matter_tags(tmp_path):
|
||||||
|
knowledge = tmp_path / "knowledge"
|
||||||
|
(knowledge / "backend" / "patterns").mkdir(parents=True)
|
||||||
|
(knowledge / "backend" / "patterns" / "nestjs.md").write_text(
|
||||||
|
"---\n"
|
||||||
|
"title: Backend — Patterns : NestJS\n"
|
||||||
|
"tags: [guards, auth]\n"
|
||||||
|
"---\n\n"
|
||||||
|
"# NestJS\n\n"
|
||||||
|
"Texte volontairement sans le mot cle demande.\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
paths = LeadtechPaths(
|
||||||
|
root=tmp_path,
|
||||||
|
knowledge=knowledge,
|
||||||
|
capitalisation=tmp_path / "95_a_capitaliser.md",
|
||||||
|
projects_conf=tmp_path / "_projects.conf",
|
||||||
|
)
|
||||||
|
with patch("leadtech_bmad_mcp.knowledge.get_paths", return_value=paths):
|
||||||
|
results = search_knowledge("backend", "guards")
|
||||||
|
assert results
|
||||||
|
assert results[0]["title"] == "Backend — Patterns : NestJS"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# read_knowledge_doc
|
# read_knowledge_doc
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -147,6 +172,23 @@ def test_read_knowledge_doc_traversal_blocked(tmp_path):
|
|||||||
read_knowledge_doc("backend", "patterns", "../../etc/passwd")
|
read_knowledge_doc("backend", "patterns", "../../etc/passwd")
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_knowledge_document_splits_metadata_and_body(tmp_path):
|
||||||
|
file_path = tmp_path / "doc.md"
|
||||||
|
file_path.write_text(
|
||||||
|
"---\n"
|
||||||
|
"title: Test Doc\n"
|
||||||
|
"tags: [alpha, beta]\n"
|
||||||
|
"---\n\n"
|
||||||
|
"# Heading\n\n"
|
||||||
|
"Contenu principal.\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
doc = read_knowledge_document(file_path)
|
||||||
|
assert doc.metadata["title"] == "Test Doc"
|
||||||
|
assert doc.metadata["tags"] == ["alpha", "beta"]
|
||||||
|
assert doc.body.startswith("# Heading")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# _extract_excerpt
|
# _extract_excerpt
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -176,6 +218,31 @@ def test_extract_excerpt_length_bounded():
|
|||||||
assert len(excerpt) <= 500
|
assert len(excerpt) <= 500
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_front_matter_returns_body_unchanged_without_header():
|
||||||
|
content = "# Heading\n\nPlain body"
|
||||||
|
metadata, body = parse_front_matter(content)
|
||||||
|
assert metadata == {}
|
||||||
|
assert body == content
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_front_matter_parses_lists_and_scalars():
|
||||||
|
content = (
|
||||||
|
"---\n"
|
||||||
|
"title: Demo\n"
|
||||||
|
"tags: [alpha, beta]\n"
|
||||||
|
"severity: high\n"
|
||||||
|
"enabled: true\n"
|
||||||
|
"---\n\n"
|
||||||
|
"Body\n"
|
||||||
|
)
|
||||||
|
metadata, body = parse_front_matter(content)
|
||||||
|
assert metadata["title"] == "Demo"
|
||||||
|
assert metadata["tags"] == ["alpha", "beta"]
|
||||||
|
assert metadata["severity"] == "high"
|
||||||
|
assert metadata["enabled"] is True
|
||||||
|
assert body == "Body\n"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# search_global_docs
|
# search_global_docs
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ def _mock_mcp_module():
|
|||||||
|
|
||||||
_mock_mcp_module()
|
_mock_mcp_module()
|
||||||
|
|
||||||
from leadtech_bmad_mcp.server import validate_plan, validate_patch # noqa: E402
|
from leadtech_bmad_mcp.server import get_guidance, validate_plan, validate_patch # noqa: E402
|
||||||
from leadtech_bmad_mcp.knowledge import LeadtechPaths # noqa: E402
|
from leadtech_bmad_mcp.knowledge import LeadtechPaths # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
@@ -88,6 +88,41 @@ class TestValidatePlanContracts:
|
|||||||
assert not contract_blocks
|
assert not contract_blocks
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetGuidanceMetadata:
|
||||||
|
def test_exposes_matched_docs_with_metadata(self, tmp_path):
|
||||||
|
paths = _fake_paths(tmp_path)
|
||||||
|
with patch(
|
||||||
|
"leadtech_bmad_mcp.server.search_knowledge",
|
||||||
|
return_value=[
|
||||||
|
{
|
||||||
|
"path": str(tmp_path / "knowledge" / "backend" / "patterns" / "nestjs.md"),
|
||||||
|
"bucket": "patterns",
|
||||||
|
"title": "Backend — Patterns : NestJS",
|
||||||
|
"score": "9",
|
||||||
|
"excerpt": "Pattern sur les guards globaux.",
|
||||||
|
"severity": "medium",
|
||||||
|
"applies_to": "analysis, implementation, review",
|
||||||
|
"tags": "nestjs, guards, auth",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
), patch(
|
||||||
|
"leadtech_bmad_mcp.server.search_global_docs",
|
||||||
|
return_value=[],
|
||||||
|
), patch(
|
||||||
|
"leadtech_bmad_mcp.server.get_paths",
|
||||||
|
return_value=paths,
|
||||||
|
):
|
||||||
|
result = get_guidance("backend", "implementation", story_text="Ajouter un guard NestJS")
|
||||||
|
|
||||||
|
assert result["matched_docs"]
|
||||||
|
matched = result["matched_docs"][0]
|
||||||
|
assert matched["bucket"] == "patterns"
|
||||||
|
assert matched["severity"] == "medium"
|
||||||
|
assert "implementation" in matched["applies_to"]
|
||||||
|
assert matched["read_uri"] == "leadtech://knowledge/backend/patterns/nestjs"
|
||||||
|
assert any("severity=medium" in item for item in result["must_do"])
|
||||||
|
|
||||||
|
|
||||||
class TestValidatePlanRequestId:
|
class TestValidatePlanRequestId:
|
||||||
def test_suggests_requestid_when_error_without_requestid(self, tmp_path):
|
def test_suggests_requestid_when_error_without_requestid(self, tmp_path):
|
||||||
paths = _fake_paths(tmp_path)
|
paths = _fake_paths(tmp_path)
|
||||||
|
|||||||
Reference in New Issue
Block a user