La carte declare un proprietaire, et le conseiller la lit
Essai du 12/09 : avec la seule liste des ecrivains, le conseiller voyait le conflit mais accusait les deux workflows n8n — l intrus etait une automatisation Home Assistant. La carte sait lire QUI PEUT ecrire, elle ne peut pas deviner QUI DEVRAIT : le proprietaire est une decision humaine, desormais declaree et commentee dans l outil. Resultat : 3 ecrivains hors proprietaire sur le contacteur reseau, 1 sur le courant de la borne, 1 sur le verrou. Injectee dans le briefing entre bornes explicites, regeneree par --injecte. Le conseiller enonce maintenant la bonne forme (« contradiction entre le proprietaire et un autre ecrivain ») mais ne nomme toujours pas lequel : il lui manque l ATTRIBUTION — rien ne relie une bascule a son auteur. Le contexte Home Assistant distingue un appel de service d une automatisation ; c est la prochaine piste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
@@ -19,14 +19,31 @@ const HA = '/mnt/ARCH3/docker/HA';
|
||||
const INSTANTANE = 'docs/mesures/carte-decisions.json';
|
||||
|
||||
// Actionneurs dont une ecriture concurrente a une consequence physique.
|
||||
// ⚠️ LE PROPRIETAIRE EST UNE DECISION HUMAINE, PAS UNE DEDUCTION.
|
||||
// La carte sait lire QUI PEUT ecrire ; elle ne peut pas deviner QUI DEVRAIT.
|
||||
// Sans cette declaration, le conseiller voit bien le conflit mais designe le
|
||||
// mauvais coupable — essai du 12/09 : il a accuse les deux workflows n8n alors
|
||||
// que l intrus etait une automatisation Home Assistant.
|
||||
// Un ecrivain hors de cette liste n est pas forcement fautif : il est A
|
||||
// EXPLIQUER. C est precisement ce qui manquait pendant sept mois.
|
||||
const ACTIONNEURS = [
|
||||
['switch.edf_general_ok_switch', 'Contacteur reseau EDF'],
|
||||
['number.gx_device_ac_power_setpoint', 'Consigne de puissance reseau'],
|
||||
['switch.victron_evcs_charging', 'Charge de la borne'],
|
||||
['number.victron_evcs_set_current_command', 'Courant de la borne'],
|
||||
['input_boolean.victron_grid_hysteresis_latch', 'Verrou hysteresis'],
|
||||
['input_boolean.victron_evcs_night_grid_takeover_needed', 'Reprise reseau nuit'],
|
||||
['number.gx_device_ess_min_soc_limit', 'SOC minimal de l ESS'],
|
||||
['switch.edf_general_ok_switch', 'Contacteur reseau EDF',
|
||||
'Victron - Battery / Grid Hysteresis Fix V1',
|
||||
'seul cet automate ouvre et ferme le contacteur ; le controleur EVCS le DEMANDE via le helper de reprise nuit, les scripts sont des interventions manuelles assumees'],
|
||||
['number.gx_device_ac_power_setpoint', 'Consigne de puissance reseau',
|
||||
'HA Snapshot EVCS Surplus Controller V2.6',
|
||||
'depuis le 01/09 : fermer le contacteur ne suffit pas, il faut demander la puissance'],
|
||||
['switch.victron_evcs_charging', 'Charge de la borne',
|
||||
'HA Snapshot EVCS Surplus Controller V2.6', 'proprietaire unique'],
|
||||
['number.victron_evcs_set_current_command', 'Courant de la borne',
|
||||
'HA Snapshot EVCS Surplus Controller V2.6',
|
||||
'les scripts BOOST sont des interventions manuelles, reecrites au cycle suivant'],
|
||||
['input_boolean.victron_grid_hysteresis_latch', 'Verrou hysteresis',
|
||||
'Victron - Battery / Grid Hysteresis Fix V1', 'les scripts d urgence le forcent volontairement'],
|
||||
['input_boolean.victron_evcs_night_grid_takeover_needed', 'Reprise reseau nuit',
|
||||
'HA Snapshot EVCS Surplus Controller V2.6', 'proprietaire unique'],
|
||||
['number.gx_device_ess_min_soc_limit', 'SOC minimal de l ESS',
|
||||
null, 'aucun automate ne devrait y toucher — reglage GX'],
|
||||
];
|
||||
|
||||
function cleApi() {
|
||||
@@ -94,8 +111,8 @@ if (fs.existsSync(paquets)) {
|
||||
|
||||
// ── Carte ─────────────────────────────────────────────────────────────
|
||||
const carte = {};
|
||||
for (const [entite, libelle] of ACTIONNEURS) {
|
||||
carte[entite] = { libelle, ecrivains: [], lecteurs: [] };
|
||||
for (const [entite, libelle, proprietaire, note] of ACTIONNEURS) {
|
||||
carte[entite] = { libelle, proprietaire, note, ecrivains: [], lecteurs: [] };
|
||||
for (const s of sources) {
|
||||
if (!s.texte.includes(entite)) continue;
|
||||
const services = ecrit(s.texte, entite);
|
||||
@@ -128,7 +145,9 @@ if (process.argv.includes('--json')) {
|
||||
for (const e of d.ecrivains.filter(x => x.desactive)) {
|
||||
console.log(` ⊘ ${e.famille.padEnd(4)} ${e.nom} — desactive`);
|
||||
}
|
||||
if (actifs.length > 1) console.log(` ⚠️ ${actifs.length} ECRIVAINS CONCURRENTS`);
|
||||
console.log(` proprietaire declare : ${d.proprietaire || 'aucun'}`);
|
||||
const intrus = actifs.filter(e => e.nom !== d.proprietaire);
|
||||
if (intrus.length) console.log(` ⚠️ ${intrus.length} ECRIVAIN(S) HORS PROPRIETAIRE : ${intrus.map(e => e.nom).join(', ')}`);
|
||||
console.log(` ${d.lecteurs.length} lecteur(s)\n`);
|
||||
}
|
||||
console.log('═══ SEUILS DE SOC FIGES ═══\n');
|
||||
@@ -156,5 +175,35 @@ if (!process.argv.includes('--json')) {
|
||||
console.log('\n(premier instantane — l ecart sera affiche aux executions suivantes)');
|
||||
}
|
||||
}
|
||||
// ── Injection dans le briefing du conseiller ──────────────────────────
|
||||
// La carte est STRUCTURELLE : elle change quand on ajoute un automate, pas
|
||||
// toutes les 15 minutes. Elle a donc sa place dans le prompt, regeneree ici
|
||||
// plutot que transmise a chaque cycle. Bornes explicites pour qu une relecture
|
||||
// voie tout de suite ce qui est genere et ce qui est ecrit a la main.
|
||||
if (process.argv.includes('--injecte')) {
|
||||
const P = 'src/advisor/BUILD_PROMPT.js';
|
||||
const DEBUT = ' // <<< CARTE GENEREE — ne pas editer a la main, voir tools/carte-decisions.mjs';
|
||||
const FIN = ' // CARTE GENEREE >>>';
|
||||
const lignes = [" 'QUI ECRIT SUR QUOI (carte generee depuis les sources) :',"];
|
||||
for (const [entite, d] of Object.entries(carte)) {
|
||||
const actifs = d.ecrivains.filter(e => !e.desactive);
|
||||
if (actifs.length < 2) continue;
|
||||
const court = n => n.replace(/\.yaml$/, '').replace(/^(HA Snapshot |Victron - )/, '');
|
||||
const intrus = actifs.filter(e => e.nom !== d.proprietaire).map(e => court(e.nom));
|
||||
lignes.push(` '- ${d.libelle} : proprietaire = ${court(d.proprietaire || 'aucun')}${intrus.length ? `, AUTRES ECRIVAINS : ${intrus.join(', ')}` : ''}',`);
|
||||
}
|
||||
const vals = Object.keys(parValeur).map(Number).sort((a, b) => a - b);
|
||||
lignes.push(` 'SEUILS DE SOC FIGES dans le code : ${vals.join(', ')} % — chacun peut contredire le bilan.',`);
|
||||
lignes.push(" 'Quand un actionneur bascule de facon incoherente, le coupable le plus probable',");
|
||||
lignes.push(" 'est un ecrivain AUTRE QUE SON PROPRIETAIRE. Nomme-le.',");
|
||||
let src = fs.readFileSync(P, 'utf8');
|
||||
const bloc = [DEBUT, ...lignes, FIN].join('\n');
|
||||
src = src.includes(DEBUT)
|
||||
? src.slice(0, src.indexOf(DEBUT)) + bloc + src.slice(src.indexOf(FIN) + FIN.length)
|
||||
: src.replace(" 'STRATEGIES POSSIBLES :',", bloc + "\n '',\n 'STRATEGIES POSSIBLES :',");
|
||||
fs.writeFileSync(P, src);
|
||||
console.log(`\ncarte injectee dans ${P} (${lignes.length} lignes)`);
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(INSTANTANE), { recursive: true });
|
||||
fs.writeFileSync(INSTANTANE, JSON.stringify(resultat, null, 2));
|
||||
|
||||
Reference in New Issue
Block a user