framework= — étendre axm-init et axm-audit à Node/Svelte
Branche worktree :
feat/node-svelte-support. Aucun changement surmain. Principe directeur : on ne crée pas de nouveaux outils. On ajoute une dimensionframework(défautpython, nouvelles valeursnodeetsvelte,sveltehéritant denode). On porte l'intention de chaque règle/check Python vers son équivalent d'écosystème Node, pas l'outil.
1. Invariant de non-régression
Tout l'existant Python doit continuer à fonctionner à l'identique :
- chaque règle audit et chaque check init garde
framework="python"par défaut ; - quand aucun
framework=n'est passé, il est auto-détecté à partir des marqueurs du projet, et un projet Python est détecté commepython; - les signatures MCP/CLI existantes ne changent pas (ajout d'un paramètre
optionnel
frameworkuniquement).
2. Détection de framework (partagée)
detect_framework(path) -> Framework (enum python | node | svelte) :
| Marqueur trouvé | Résultat |
|---|---|
pyproject.toml (et pas de package.json au même niveau) |
python |
package.json avec dép/devDep svelte ou svelte.config.js présent |
svelte |
package.json sans marqueur Svelte |
node |
| rien de tout ça | python (défaut conservateur) |
Override explicite : le paramètre framework= passé à l'outil court-circuite la
détection. svelte hérite de node (un projet svelte fait tourner les règles
node plus les règles svelte-spécifiques).
3. axm-audit — point d'injection
- Aujourd'hui :
@register_rule(category)remplit_RULE_REGISTRY[category], etget_rules_for_category(category)instancie ces classes. - Extension :
@register_rule(category, framework="python")→ le registry devient indexé(category, framework);get_rules_for_category(category, framework=...)ne renvoie que les règles du framework demandé (avec héritage node→svelte).audit_project()/AuditTool.execute()acceptent unframeworkoptionnel, sinondetect_framework(project_path). - Les règles Python existantes ne changent pas :
frameworkpar défaut =pythondans le décorateur.
4. axm-init — point d'injection
Scaffold (templates)
TemplateTypereste {standalone, workspace, member} ; on ajoute une dimensionframework.get_template_path(template_type, framework)choisit le bon dossier Copier (templates/node-project,templates/svelte-project, …).InitScaffoldTool.execute()accepteframework=(défaut python).
init_check (gold standard)
- Aujourd'hui :
_discover_checks()scanne les modules deaxm_init.checks; le nom de module = catégorie ; toutes les fnscheck_*publiques sont prises. - Extension : checks rangés par framework (sous-package
axm_init.checks.node.*,axm_init.checks.svelte.*, l'existant restant le jeupython).CheckEngine(project_path, framework=...)choisit le jeu de checks ;frameworkauto-détecté si non fourni.
5. Mapping d'intention (résumé — détail dans AUDIT_RULE_MAPPING.md)
| Catégorie | Intention conservée | Outil Python | Outil Node/Svelte |
|---|---|---|---|
| lint | style + bugs + simplifs | ruff | ESLint (typescript-eslint, eslint-plugin-svelte) |
| type | typage statique | mypy | tsc --noEmit (+ svelte-check) |
| complexity | cc<10 / cog<15 | ruff C901 + complexipy | ESLint complexity + sonarjs cognitive |
| security | secrets + vulnérabilités | bandit + regex | eslint-plugin-security + npm audit (+ regex réutilisée) |
| deps | pins + inutilisées + vulns | deptry + pip-audit | depcheck + npm audit |
| testing | suite verte + couverture | pytest + coverage | vitest run + c8/istanbul |
| test_quality | pyramide, dup, tautologie, naming | AST python | AST TS (mêmes invariants, fichiers *.test.ts) |
| architecture | pas de cycle, couplage, god class | imports python | madge / dependency-cruiser |
| structure | manifest complet + layout | pyproject.toml | package.json + tsconfig + svelte.config |
| practices | bare except, blocking io, docstrings, mirror | AST python | no empty catch, no sync fs en async, TSDoc, test-mirror |
6. Plan de prototypage (ordre)
detect_frameworkpartagé (dansaxm-ingotou helper local par package).- axm-audit : registry
(category, framework)+ filtrage + 1 règle node POC (lint ESLint) prouvant la chaîne de bout en bout. - axm-init :
frameworkdansCheckEngine+ 1 jeu de checks node POC (package.jsoncompleteness) + 1 templatenode-projectminimal. - Tests unit + e2e ; audit_test ; commit sur la branche worktree.
7. Résultats du POC (livré sur feat/node-svelte-support)
Mécanisme framework= câblé de bout en bout dans axm-audit et axm-init,
100% rétro-compatible (défaut python, auto-détection sinon).
axm-audit
- core/framework.py : Framework (python/node/svelte), detect_framework,
resolve_frameworks (svelte → node+svelte).
- Registry @register_rule(category, framework=…) keyé (category, framework) ;
get_registry() reste la vue python (back-compat), get_registry_for(fw) est
le nouvel accès ; auditor filtre via _merged_registry + auto-détection.
- Règle POC NodeLintRule (catégorie lint, framework node) → ESLint JSON,
même rule_id=QUALITY_LINT, même scoring 100 - issues*2.
- Prouvé e2e sur un vrai projet ESLint : 3 issues réelles → score 94,
passed=False. Faux-vert (npx non-install) attrapé et corrigé : un outil non
installé localement = ERROR non-vert.
axm-init
- core/framework.py (miroir), CheckEngine(framework=…) + CHECKS_BY_FRAMEWORK.
- Jeu de checks checks/node/package_json.py (existence + métadonnées).
- Template Copier templates/node-project + get_template_path(type, framework) ;
InitScaffoldTool accepte framework=.
- Boucle prouvée : scaffold framework=node → init_check auto-détecte node
→ score 100/A.
Validation : axm-audit 2010 tests verts (lint A 99.8 / type 100) ; axm-init 707 tests verts (lint A 99.8 / type 100). 28 nouveaux tests ; 9 tests existants adaptés à l'API framework-aware (aucune régression fonctionnelle).
8. Architecture en couches (react-ready)
Le découpage python | node | svelte initial était trop grossier. Modèle final
en couches, piloté par resolve_frameworks :
node= socle commun JS/TS (ESLint, tsc, prettier, vitest, knip, madge, jscpd, npm audit, gitleaks). Réutilisé tel quel par tous.svelte=node+ delta.svelte(svelte-check : type + a11y quetscne couvre pas).resolve_frameworks(svelte) → (node, svelte).react=node+ delta.jsx/.tsx(eslint-plugin-react-hooks, jsx-a11y, react-refresh).resolve_frameworks(react) → (node, react).
Ajouter un framework UI (vue, solid…) = 1 membre d'enum + 1 entrée dans
_NODE_UI_FRAMEWORKS + un sous-package rules/<fw>/ (audit) / checks/<fw>/
(init). Aucun refactor. React est déjà câblé (enum + détection + résolution
+ _FRAMEWORK_CHECK_PACKAGES) ; seul son delta de règles reste à écrire.
9. État livré (node base + svelte delta)
axm-audit — 16 règles node déclarées (12 implémentées + 4 placeholders) :
| Catégorie | Règles implémentées | Outil |
|---|---|---|
| lint | QUALITY_LINT, QUALITY_FORMAT, QUALITY_DEAD_CODE | ESLint, Prettier, knip |
| type | QUALITY_TYPE | tsc |
| complexity | QUALITY_COMPLEXITY | ESLint + sonarjs |
| deps | DEPS_HYGIENE, DEPS_AUDIT | knip, npm audit |
| security | PRACTICE_SECURITY | gitleaks |
| testing | QUALITY_TESTING | vitest |
| architecture | ARCH_CIRCULAR, ARCH_DUPLICATION | madge, jscpd |
| structure | STRUCTURE_PACKAGE_JSON | (lecture package.json+tsconfig) |
| test_quality | NON IMPLÉMENTÉ (4 placeholders) | requiert AST TS |
Delta svelte : SVELTE_CHECK (type+a11y). → svelte = 13 règles, react = 12
(node) en attendant son delta.
axm-init — 6 checks node + 1 delta svelte : package_json (×2), tsconfig
(exists+strict), tooling (eslint config + test script) ; delta svelte =
svelte.config. Templates node-project (enrichi) et svelte-project (nouveau)
qui passent leurs propres checks ET les règles audit out-of-the-box.
Doctrine appliquée partout (issue de la recherche) : exit code = verdict,
JSON = détail. Un outil non installé localement → ERROR non-vert (jamais de
faux-vert). findings_returncodes gère les outils qui surchargent un exit non-nul
pour signaler des findings (tsc=2, npm audit=1, vitest=1, prettier=1).
Validé e2e sur projets réels : audit node sur un projet à 7 outils installés (madge/knip/eslint+sonarjs/tsc/prettier/vitest/npm-audit) → chaque règle score de vraies findings ; jscpd+gitleaks absents → fail loud. init scaffold→check node ET svelte → 100/A.
10. La question de l'AST TS (décision)
Deux familles de règles :
- Famille 1 (déléguées à un outil externe) — lint, type, complexity, format,
deps, dead_code, security, testing, architecture, duplication. L'AST est fait
par l'outil node ; axm-audit score le JSON. Ne touche pas axm-ast. → toutes
implémentées.
- Famille 2 (analyse AST propre en Python) — les invariants test_quality
spécifiques AXM (mirror, pyramid, tautology, duplicate). En Python elles
utilisent le tree-sitter d'axm-ast. Pas d'outil node équivalent → il faut un
AST TS côté Python (étendre axm-ast avec tree-sitter-typescript, ou un helper
ts-morph en subprocess). Décision différée : ces 4 règles sont déclarées
comme placeholders NOT_IMPLEMENTED (score=None, jamais de faux-vert) qui
nomment explicitement la dépendance manquante.
11. Reste à faire
- Delta react : eslint-plugin-react-hooks (rules-of-hooks, exhaustive-deps),
jsx-a11y, react-refresh — sous
rules/react/(le câblage est déjà prêt). - AST TS : trancher famille 2 (axm-ast tree-sitter-typescript vs ts-morph) pour implémenter les 4 placeholders test_quality.
- Factoriser
framework.pydansaxm-ingot(actuellement dupliqué audit/init). - Exposer
framework=dans les signatures MCP/CLI des toolsaudit/init_*.