Agent IA planificateur EVCS en MODE OMBRE (Advisor Shadow V1)

- Workflow n8n tdfInZns7cqo8fU2 actif (15 min): snapshot HA compact -> prompt FR
  -> LLM local Spark (modèle dynamique via /v1/models) avec fallback OpenRouter
  claude-haiku-4.5 (credential chiffré, clé hors git) -> validation stricte
  (enums, bornes, rejet total si invalide) -> helpers advisor_* + divergence
  + compteurs accord/divergence + logbook.
- GARANTIE OMBRE structurelle: écritures limitées à advisor_*/logbook,
  vérifiée par tests/advisor/run.mjs (16 tests, bloquants au push).
- Kill-switch input_boolean.evcs_advisor_enabled.
- Extension R2 multi-workflows (lib-node-map WORKFLOWS[], build/extract/push
  par fichier, n8n-create.mjs, gen-advisor-workflow.mjs).
- Helpers HA via package NFS + carte dashboard EVCS Master (backup fait).
- 1re reco réelle: auto_solar_plus, cible 35%, accord avec le réel.
- Fix sécurité n8n-push: plus de fallback d'id implicite vers le workflow EVCS.
This commit is contained in:
Tallagrand Thierry
2026-08-07 22:42:06 +02:00
parent 1ae8129658
commit 63d56bf284
13 changed files with 1282 additions and 69 deletions

View File

@@ -15,14 +15,14 @@ if (!INPUT) {
process.exit(1);
}
// R2 : src/nodes/ = SOURCE DE VÉRITÉ pour le workflow EVCS.
// Le build injecte src/nodes/*.js dans le JSON AVANT toute vérification :
// toute édition faite directement dans le JSON (ou l'UI n8n non ré-extraite)
// est écrasée par src/. Workflow EVCS uniquement.
if (path.basename(INPUT) === 'evcs-surplus-controller-v2.6.json' && fs.existsSync('src/nodes')) {
console.log('Build src/nodes/ → JSON (source de vérité : src/)...');
// R2 : src/ = SOURCE DE VÉRITÉ pour les workflows enregistrés dans le node-map.
// Le build injecte src/**/*.js dans le JSON AVANT toute vérification.
const { findWorkflowByFile } = await import('./lib-node-map.mjs');
const registered = findWorkflowByFile(INPUT);
if (registered) {
console.log(`Build ${registered.srcDir}/ → JSON (source de vérité : src/)...`);
const { spawnSync } = await import('child_process');
const b = spawnSync('node', ['tools/n8n-build.mjs'], { stdio: 'inherit' });
const b = spawnSync('node', ['tools/n8n-build.mjs', INPUT], { stdio: 'inherit' });
if (b.status !== 0) {
console.error('ABANDON: échec du build src → JSON.');
process.exit(1);
@@ -30,7 +30,11 @@ if (path.basename(INPUT) === 'evcs-surplus-controller-v2.6.json' && fs.existsSyn
}
const local = JSON.parse(fs.readFileSync(INPUT, 'utf8'));
const WORKFLOW_ID = process.argv[3] || local.id || 'ATswvGfG4KJUls0K';
const WORKFLOW_ID = process.argv[3] || local.id;
if (!WORKFLOW_ID) {
console.error('ABANDON: aucun id de workflow (ni argument, ni champ id dans le JSON).');
process.exit(1);
}
function loadApiConfig() {
if (process.env.N8N_API_KEY) {
@@ -101,20 +105,25 @@ if (syntaxErrors) {
}
console.log('Syntaxe OK sur tous les nodes jsCode.');
// 3bis. Suite golden (R3) : obligatoire pour le workflow EVCS, sauf --skip-golden
// 3bis. Suites de tests (R3) : bloquantes par workflow, sauf --skip-golden
const isEvcsWorkflow = path.basename(INPUT) === 'evcs-surplus-controller-v2.6.json';
const isAdvisorWorkflow = path.basename(INPUT) === 'evcs-advisor-shadow-v1.json';
const skipGolden = process.argv.includes('--skip-golden');
if (isEvcsWorkflow && fs.existsSync('tests/golden/run.mjs')) {
const testSuite =
isEvcsWorkflow && fs.existsSync('tests/golden/run.mjs') ? 'tests/golden/run.mjs' :
isAdvisorWorkflow && fs.existsSync('tests/advisor/run.mjs') ? 'tests/advisor/run.mjs' :
null;
if (testSuite) {
if (skipGolden) {
console.log('⚠️ Suite golden SAUTÉE (--skip-golden) — à n\'utiliser qu\'en connaissance de cause.');
console.log('⚠️ Suite de tests SAUTÉE (--skip-golden) — à n\'utiliser qu\'en connaissance de cause.');
} else {
console.log('Exécution de la suite golden (tests/golden/run.mjs)...');
console.log(`Exécution de la suite de tests (${testSuite})...`);
const { spawnSync } = await import('child_process');
const g = spawnSync('node', ['tests/golden/run.mjs'], { stdio: 'inherit' });
const g = spawnSync('node', [testSuite], { stdio: 'inherit' });
if (g.status !== 0) {
console.error('ABANDON: la suite golden échoue. Rien n\'a été poussé.');
console.error('ABANDON: la suite de tests échoue. Rien n\'a été poussé.');
console.error('(Évolution volontaire du comportement ? Mettre à jour les attendus');
console.error(' dans tests/golden/scenarios.mjs dans le même commit, puis re-pousser.)');
console.error(' de la suite dans le même commit, puis re-pousser.)');
process.exit(1);
}
}