Observé le 02/10 sur une maison d'essai et reproduit sur les trois profils de
référence : une même typologie retenue jusqu'à sept fois dans un axe, des
accessoires forcés sans principal (conduit sans poêle, ballon sans poêle hydro),
EAU correcte à 3 000 € mais « budget insuffisant, minimum 7 280 € » à 6 000 €,
chaque FORCE écrit deux à quatre fois dans le journal. Un test par défaut, qui
échoue sur v0.1.0 (master 648015a) et passe ici.
CHANGELOG
- reajuste (CAS B, stratégie 1) : une ligne forcée par couplage n'est plus
« upgradée » (l'origine la triait en tête avec son score 0,8 et la
transformait, puis la passe couplages re-forçait l'original) ; la cible d'un
upgrade n'est jamais le choix d'un autre poste ; le coût des couplages requis
compte le requis égal au code actuel de la ligne ; un upgrade dont l'effet net
baisse le total (il lâche un accessoire plus cher que lui) est refusé.
- reajuste (CAS B, stratégie 2) : un poste = une ligne (l'origine testait le
code et pas le poste, sur une liste figée avant la boucle : jusqu'à cinq
basse_planche_bec). La quantité d'un poste est sa dimension. La ligne ajoutée
porte ses vrais couplages (l'origine écrivait []).
- reajuste (CAS A) : un downgrade ne cible jamais le choix d'un autre poste ; la
baisse réelle compte les accessoires forcés lâchés ; après chaque retrait ou
downgrade, les accessoires orphelins sont lâchés (le plancher de coût n'est
plus verrouillé par des couplages que plus rien ne requiert).
- reajuste : une ligne remplacée porte SA dimension, pas celle de la ligne
remplacée (une batterie au plomb affichait « 0,4 kWc », une chaudière « 6 ml » ;
le taux énergie lisait ces kWc fantômes).
- couplages : passe 0 — RETIRE les lignes forcées que plus rien ne requiert,
ABSORBE celles dont la typologie est déjà le choix d'un poste ; helpers
codesRequisPar, codesChoisis, retireForcesOrphelins exportés.
- algo (finaliseSelection) : le journal ne se ré-ajoute plus à lui-même à
chaque tour (reajusteBudget le renvoie en écho) ; la boucle vise la bande
[−underspend, +overrun] et pas seulement le dépassement, trois tours au plus,
toujours une passe couplages en dernier ; si le dernier forçage refranchit le
seuil, le statut le dit. finaliseSelection exporté du noyau.
Tests : test/correctifs-v0.1.1.test.mjs (n° 1, 2, 3, 5, 7 + dimension héritée),
test/monotonie.test.mjs (trois profils × trois axes, 1 000 → 40 000 € par 500 :
le statut ne repasse jamais de correct à insuffisant ; le sous-total ne baisse
jamais au-delà de la tolérance de dépassement — la stricte croissance n'est pas
tenue par l'heuristique, −4,4 % au plus sur A/énergie, voir RECAP-LOT-v0.1.1).
Les 28 tests d'origine passent, inchangés.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tgtn7bJxTLiL2fRCkQMgtR
@transformer/moteurs
Moteurs de calcul purs pour la rénovation et l'autonomie d'une maison, partagés par calculs.trans-former.fr et plateforme.trans-former.fr. Un moteur reçoit des entrées et des données, rend des sorties avec ses sources et ses avertissements, et ne lit rien d'autre : ni réseau, ni disque, ni horloge. Jamais de prix dans le paquet : les prix, barèmes et tables sont des données publiées ailleurs et injectées.
Licence MIT (le contenu de la plateforme reste sous sa propre licence).
Ce que le paquet contient (v0.1.0)
Moteur (id) |
Entrées | Données injectées | Sorties |
|---|---|---|---|
commun.contexte |
code postal | france_climat |
département (Corse 2A/2B, DOM 97x), zone RT, zone biogéographique, pluviométrie, DJU |
commun.geometrie |
emprise, niveaux, hauteur, pente, mitoyenneté, SHAB connue (facultatif) | — | murs extérieurs et opaques, vitrage, toiture, plancher bas, SHAB estimée, volume |
autonomie.eau · autonomie.energie · autonomie.alimentation |
autodiag, budget d'axe, contexte | postes, options, algo_config |
postes retenus (typologie, dimension, coût), alternatives, statut, journal des couplages, taux de couverture de l'axe |
autonomie.integrateur |
autodiag, budget total, mode global (= calculs) ou par_axe (= plateforme), axes actifs |
idem + france_climat |
contexte, poids d'axes, budget par axe, sélection par axe, total, statut, taux par axe |
Plus, dans commun : la table des coefficients (COEFFICIENTS), valider(table, json), les helpers
d'argent (fourchettes, mensualité, annuité) ; dans autonomie : le noyau V2 complet (autonomie.noyau.selectionner),
les taux de couverture et les prix d'options (prixOption, prixPreset).
Consommer
"dependencies": {
"@transformer/moteurs": "git+https://git.trans-former.fr/jules/moteurs.git#v0.1.0"
}
Toujours une étiquette (#vX.Y.Z), jamais une branche : deux sites qui ne calculent pas pareil sans
que personne ne le sache est exactement le risque à éviter. Le dépôt est public, dist/ est committé :
l'installation ne demande aucune étape de build, seulement git sur la machine qui fait npm ci
(piège connu : node:22-alpine n'a pas git → apk add --no-cache git).
import { contexte, eau, integrateur, valider } from '@transformer/moteurs'
// 1. les données viennent de l'extérieur (URL publiée, instantané daté…) et se valident à la frontière
for (const [table, json] of Object.entries(donnees)) {
const v = valider(table, json)
if (!v.ok) throw new Error(v.erreurs.join('\n'))
}
// 2. contexte depuis le code postal
const ctx = contexte.calculer({ code_postal: '31170' }, { france_climat: donnees.france_climat }).sorties
// 3. un moteur d'axe sur son budget
const r = eau.calculer(
{ autodiag: { nb_personnes: 4, surface_toit_m2: 80, surface_parcelle_m2: 800, orientation_toit: 'sud-est', temps_dispo: 2 }, budget_axe: 14000, contexte: ctx },
{ postes: donnees.postes, options: donnees.options, algo_config: donnees.algo_config },
)
r.sorties.postes // typologies retenues, dimensions, coûts
r.sorties.taux_couverture // % du besoin substituable couvert
r.sources // chaque coefficient et chaque prix utilisés, avec source et date
r.avertissements // chaque défaut appliqué, chaque donnée manquante
r.version // '0.1.0'
Le mode global de l'intégrateur reproduit exactement le simulateur de calculs (pipeline complet,
couplages et réajustement entre axes) ; le mode par_axe calcule chaque branche du menu sur son
budget et additionne.
Sous-chemins : @transformer/moteurs/commun, /autonomie, /autonomie/eau, /autonomie/energie,
/autonomie/alimentation, /autonomie/integrateur, /autonomie/noyau, /schemas/<table>.schema.json,
/fixtures/<fichier> (algo-config.json, profils-reference.json).
Les coefficients
Chaque grandeur physique existe une fois, dans src/commun/coefficients.ts, avec sa source et sa date ;
la version lisible est docs/CONSTANTES.md (ancienne valeur → retenue, source,
ce que ça change). Les heuristiques de conception du simulateur (poids d'axes, matrices, tolérances) sont
des données : fixtures/algo-config.json, injectées.
Développer
npm install # typescript seulement
npm test # build (tsc → dist/, schémas) puis node --test test/ — hors ligne
npm run ecarts # table des écarts origine → harmonisée sur les trois profils de référence
Règles du dépôt et carte détaillée : AGENTS.md. Jeux de données figés : fixtures/README.md.
Publier une étiquette
npm test vert → git commit (avec dist/ et schemas/ reconstruits) → git tag vX.Y.Z →
git push && git push --tags → monter la version chez les consommateurs le même jour. Patch = correction
de formule ; mineur = nouvelle sortie ou nouveau moteur ; majeur = entrée modifiée.