docs: LM-1011 dans la spec, les trois nombres au contrat, R3 proposée
TROIS choses, plus une sauvegarde. 1. LM-1011 — la précédence BELOW_MIN_POWER / DRAW_CAP est dans la SPEC. Je l'avais écrite hier dans DESIGN_10.md : une note de conception se périme, la spec fait autorité, et l'index ne voyait donc pas la règle. DESIGN_10 n'en garde qu'un renvoi. Autorisation résiduelle non nulle mais sous le plus petit palier → BELOW_MIN_POWER ; DRAW_CAP réservé au résiduel nul. Ce n'est pas une règle nouvelle : c'est celle de +etm19 transposée à une seconde ressource — le surplus a déjà SURPLUS_INSUFFICIENT (« il n'y a rien ») contre BELOW_MIN_POWER (« il y en a, mais pas au bon format »), et il aurait été absurde qu'une ressource se lise autrement que l'autre. Ce qui décide n'est pas la véracité mais le GESTE appelé : revoir le plafond, ou lire la fiche technique. Conséquence de l'agent app retenue : le point de rupture de l'écran 2 est ancré sur le DRAW_CAP, donc sur l'épuisement réel. Une charge refusée APRÈS lui pour cause de palier ne le déplace pas — sans quoi deux charges successives produiraient deux points de rupture concurrents dans un cycle. 2. LES TROIS NOMBRES au contrat — décidé, appliqué, mesuré. Ma correction sur allocatedW a fait apparaître un troisième nombre que ni la maquette ni nous n'avions nommé. INTERFACE.md porte le tableau ET ce que chaque écart signifie, parce que c'est là qu'un écran se trompe de diagnostic : décidé ≠ appliqué est une affaire de FORMAT, appliqué ≠ mesuré une affaire d'OBÉISSANCE — et cette seconde lecture dépend encore de measurement.source. 3. R3 proposée à l'app avant d'être écrite : funding dit d'où viennent les watts, il ne dit pas dans quel compteur ils tombent. Un niveau publierait sa ventilation (counts), Σ counts == targetW toujours, et l'identité devient calculable SANS exception — y compris quand draw.committedW arrivera. Et la PRÉCONDITION du §10 est consignée : il ne part pas sans l'extension du délestage au waterfall (LM-1006 couplage 1). La protection de surcharge ne déleste aujourd'hui que les bornes ; livrer l'éco sans elle donnerait un plancher capable de faire disjoncter une installation. Aucun code. ci-quality 3/3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
This commit is contained in:
parent
df20be1404
commit
b6a6c7eebc
22
AGENTS.md
22
AGENTS.md
@ -333,7 +333,27 @@ actifs.
|
||||
est tranché. Décisions et exigences : `specs/spec_loadmodel.md` **LM-1010**. Précédence des
|
||||
motifs et réponse à R4 : `docs/DESIGN_10.md`.
|
||||
|
||||
**PROCHAINE ACTION DU §10 : R2 — `funding` descend au NIVEAU.** C'est la seule des huit
|
||||
> ⚠️ **PRÉCONDITION DU §10, et elle n'est pas dans la maquette** *(Patrick, 2026-08-30)* : **le
|
||||
> §10 ne part pas sans l'extension du DÉLESTAGE au waterfall.** C'est LM-1006 couplage 1 — si
|
||||
> tous les planchers éco se servent au réseau le même matin sans soleil, ils s'additionnent sur
|
||||
> le disjoncteur de branchement. Or la protection de surcharge d'aujourd'hui ne déleste **que
|
||||
> les bornes**. Éco et délestage sont **le même lot** ; les séparer livrerait un plancher
|
||||
> capable de faire disjoncter une installation.
|
||||
|
||||
**FAIT — R2 : `funding` est descendu au NIVEAU** (`1.15.2+etm30`, posé sur `.75`). `levels[]` est
|
||||
en ligne, `funding` a disparu du niveau de la charge, et l'identité de réconciliation est écrite
|
||||
dans `INTERFACE.md` — **vérifiée sur la vraie box** : Σ targetW(surplus) = 5 000 =
|
||||
`budget.allocatedW`.
|
||||
|
||||
**PROCHAINE EXIGENCE : R3 — chaque watt dans exactement un compteur, mapping publié.** Trois
|
||||
destinations coexisteront : `budget.allocatedW` (surplus), `budget.evReservedW` (réseau, proxy
|
||||
VE), `draw.committedW` (réseau, plancher éco — n'existe pas encore). **`EV_GRID_START` la viole
|
||||
déjà** : il partage une allocation entre deux compteurs, et sa contribution au surplus est
|
||||
`decision.params.budgetW`, pas `targetW`. L'exception est **écrite** dans `INTERFACE.md`, elle
|
||||
n'est pas **résolue** — c'est ce que R3 doit faire.
|
||||
|
||||
*Ancienne prochaine action, faite :*
|
||||
**R2 — `funding` descend au NIVEAU.** C'est la seule des huit
|
||||
exigences qui soit un **changement de forme d'un champ existant** et non un ajout : chaque écran
|
||||
qui s'appuiera d'ici là sur un `funding` par charge en augmentera le coût de déplacement, les
|
||||
sept autres attendent sans se renchérir. Au §10, la même charge sera financée **au réseau pour
|
||||
|
||||
32
INTERFACE.md
32
INTERFACE.md
@ -593,6 +593,38 @@ le cas normal.
|
||||
> niveau il parle (`decision.level`, R7). On conserve un champ hérité **quand on peut en dire
|
||||
> quelque chose de vrai**, jamais par symétrie.
|
||||
|
||||
### Les TROIS nombres d'une charge — décidé, appliqué, mesuré
|
||||
|
||||
*(Rendu explicite le 2026-08-30, après qu'une prémisse de la maquette app eut confondu les deux
|
||||
premiers. Trois nombres coexistent pour une même charge dans un même cycle, ils disent trois
|
||||
choses différentes, et **aucun ne se déduit d'un autre**.)*
|
||||
|
||||
| | Où | Ce qu'il dit | Ce qu'un écart avec le suivant signifie |
|
||||
|---|---|---|---|
|
||||
| **DÉCIDÉ** | `allocatedW`, et `levels[].targetW` | ce que la cascade a **alloué** | → l'**arrondi** du mécanisme : 1 400 W décidés tombent sur un palier de 1 500 W |
|
||||
| **APPLIQUÉ** | `mechanism.stageW` · `setpointW` · `state` · `currentA` | ce qui a été **commandé au matériel** | → la charge **n'obéit pas** : véhicule qui refuse, thermostat ouvert, verrou qui a mordu |
|
||||
| **MESURÉ** | `measurement.powerW` (avec sa `source`) | ce qu'un appareil ou un compteur a **vu** | — |
|
||||
|
||||
> ⚠️ **`allocatedW` est du DÉCIDÉ, pas du commandé.** `buildTelemetry()` publie le plan, et
|
||||
> `applyActionsToAdapters()` reçoit le slot **`const`** : rien n'est réécrit après le dispatch.
|
||||
> Mesurable — `testUnpluggedChargerTakesNoBudget` attend `allocatedW == 3000` sur une borne dont
|
||||
> le courant commandé donne 2 990 W.
|
||||
>
|
||||
> **Conséquence** : `Σ levels[].targetW == allocatedW` tient **exactement**, les deux étant du
|
||||
> décidé. L'écart « décidé → appliqué » ne se lit donc **pas** entre eux, mais entre
|
||||
> `allocatedW` et la charge utile de **mécanisme**.
|
||||
>
|
||||
> **Et l'arrondi ne se répartit pas entre les niveaux.** Toute clé de répartition serait une
|
||||
> fiction, dont un client hériterait comme d'un fait : l'écart appartient à la **charge**, pas à
|
||||
> l'un de ses niveaux.
|
||||
|
||||
> **Pourquoi trois et pas deux.** Chaque frontière répond à une question différente, et les
|
||||
> confondre envoie diagnostiquer le mauvais problème (règle 7-b). Décidé ≠ appliqué est une
|
||||
> affaire de **format** — le mécanisme ne sait pas prendre n'importe quelle valeur. Appliqué ≠
|
||||
> mesuré est une affaire d'**obéissance** — et ce que cet écart autorise à conclure dépend
|
||||
> encore de `measurement.source` (§11) : sous `device` il accuse la charge, sous `meter` il
|
||||
> ouvre trois hypothèses, sous `none` il n'est pas calculable.
|
||||
|
||||
### L'identité de réconciliation — écrite ici, jamais reconstruite
|
||||
|
||||
```
|
||||
|
||||
@ -629,6 +629,47 @@ que **R3** généralisera ; d'ici là l'exception est dans le contrat, pas dans
|
||||
> `levels[]` **absent** = box antérieure à `+etm30`, **ou mode dégradé** — sans plan, il n'y a
|
||||
> pas de financement. Votre repli sur `allocatedW`/`decision` reste donc juste, et il couvre les
|
||||
> deux cas.
|
||||
>
|
||||
> **`+etm30` est posé sur `.75`** : `levels[]` y est en ligne, `entry.funding` a disparu, et
|
||||
> l'identité a été vérifiée sur la vraie box — Σ targetW(surplus) = 5 000 = `budget.allocatedW`.
|
||||
> C'est donc votre branche neuve qui s'exécute désormais, plus le repli.
|
||||
|
||||
### LES TROIS NOMBRES — porté au contrat, parce que votre R4 en confondait deux
|
||||
|
||||
Votre correction a fait apparaître un troisième nombre que ni la maquette ni nous n'avions
|
||||
nommé. `INTERFACE.md` porte désormais le tableau :
|
||||
|
||||
| | Où | Ce qu'il dit |
|
||||
|---|---|---|
|
||||
| **DÉCIDÉ** | `allocatedW`, `levels[].targetW` | ce que la cascade a alloué |
|
||||
| **APPLIQUÉ** | `mechanism.stageW` · `setpointW` · `state` · `currentA` | ce qui a été commandé au matériel |
|
||||
| **MESURÉ** | `measurement.powerW` + sa `source` | ce qu'un appareil ou un compteur a vu |
|
||||
|
||||
Et surtout **ce que chaque écart signifie**, parce que c'est là qu'un écran se trompe de
|
||||
diagnostic : *décidé ≠ appliqué* est une affaire de **format** — le mécanisme ne sait pas prendre
|
||||
n'importe quelle valeur ; *appliqué ≠ mesuré* est une affaire d'**obéissance**, et ce qu'elle
|
||||
autorise à conclure dépend encore de `measurement.source` (sous `device` elle accuse la charge,
|
||||
sous `meter` elle ouvre trois hypothèses, sous `none` elle n'est pas calculable).
|
||||
|
||||
Votre carte « 1 400 W décidés → palier 1 500 W » lit donc le **premier** écart, entre
|
||||
`allocatedW` et `mechanism.*` — pas entre `targetW` et `allocatedW`, qui sont égaux par
|
||||
construction.
|
||||
|
||||
### R3 : ce qu'il nous faudra de vous
|
||||
|
||||
Nous la prenons ensuite, et `EV_GRID_START` est le cas qui la force. Notre intention, à
|
||||
critiquer avant qu'on l'écrive : **`funding` dit d'où viennent les watts, il ne dit pas dans
|
||||
quel compteur ils tombent** — ce sont deux questions, et R3 demande la seconde. Un niveau
|
||||
publierait donc sa ventilation :
|
||||
|
||||
```jsonc
|
||||
{ "level": "comfort", "targetW": 1380, "funding": "grid",
|
||||
"counts": { "budget.allocatedW": 900, "budget.evReservedW": 480 } }
|
||||
```
|
||||
|
||||
`Σ counts == targetW`, toujours. L'identité devient calculable **sans exception**, et elle vaudra
|
||||
telle quelle quand `draw.committedW` arrivera. Dites-nous si cette forme vous va, ou si vous
|
||||
préférez lire la ventilation autrement — c'est votre réconciliation qui en dépend.
|
||||
|
||||
> Et deux fils antérieurs de votre brief sont **clos**, au cas où ils ne vous seraient pas
|
||||
> parvenus : l'**entrelacement des domaines** est tranché — grouper à l'affichage, **jamais à
|
||||
|
||||
@ -231,32 +231,11 @@ une entrée de clôture (`testDecisionReasonRendering`).
|
||||
|---|---|---|
|
||||
| `DRAW_CAP` | `capW`, `source`, `requiredW`, `o:phase` | Le plafond de soutirage refuse, et **`source` dit lequel** : raccordement, gestionnaire de réseau, ou auto-imposé. `phase` présent si c'est une phase qui borne. **Réservé à l'autorisation résiduelle NULLE** — voir la précédence ci-dessous. |
|
||||
|
||||
**PRÉCÉDENCE `DRAW_CAP` ↔ `BELOW_MIN_POWER` — tranchée le 2026-08-29 (Patrick), sur une question
|
||||
de l'agent app.**
|
||||
|
||||
Autorisation résiduelle **non nulle mais inférieure au plus petit palier** du mécanisme :
|
||||
**`BELOW_MIN_POWER`**. Les deux motifs sont vrais, mais pas au même niveau.
|
||||
|
||||
`DRAW_CAP` dit « **il n'y a plus d'autorisation** ». Or il en reste — simplement **pas au bon
|
||||
format**. C'est exactement la distinction posée en `+etm19` entre `SURPLUS_INSUFFICIENT` (« il
|
||||
n'y a rien ») et `BELOW_MIN_POWER` (« il y en a, mais pas au bon format »), **transposée telle
|
||||
quelle au soutirage** : ce n'est pas une règle nouvelle, c'est la même appliquée à une seconde
|
||||
ressource.
|
||||
|
||||
**Ce qui la décide est le GESTE qu'elle appelle** — la règle 7-b : un motif faux envoie
|
||||
diagnostiquer le mauvais problème, avec l'autorité d'une explication.
|
||||
|
||||
| Motif | Ce qu'il dit | Le geste utile |
|
||||
|---|---|---|
|
||||
| `DRAW_CAP` | l'autorisation est **épuisée** | revoir le plafond, ou attendre |
|
||||
| `BELOW_MIN_POWER` | il reste de l'autorisation, **la charge ne sait pas la prendre** | lire la fiche technique du mécanisme |
|
||||
|
||||
> **Conséquence sur le « point de rupture » de l'écran 2** : il est bien un `DRAW_CAP`. Une
|
||||
> charge refusée **après** lui pour cause de palier porte `BELOW_MIN_POWER` et **ne déplace pas**
|
||||
> le point de rupture — elle est en aval de la coupure, pas la coupure elle-même.
|
||||
>
|
||||
> **La règle est écrite ICI, une fois, par le moteur** — pas établie de chaque côté de la
|
||||
> frontière. Deux précédences dérivées séparément divergeraient au premier motif ajouté.
|
||||
**PRÉCÉDENCE `DRAW_CAP` ↔ `BELOW_MIN_POWER`** — tranchée le 2026-08-29, et son domicile est
|
||||
`specs/spec_loadmodel.md` **LM-1011**, pas ici : une note de conception se périme, la spec fait
|
||||
autorité. En un mot : autorisation résiduelle **non nulle mais sous le plus petit palier** →
|
||||
`BELOW_MIN_POWER` ; `DRAW_CAP` réservé au résiduel **nul**. Le point de rupture de l'écran 2 est
|
||||
ancré sur le `DRAW_CAP`.
|
||||
|
||||
**RÉPONSE À R4 — ce que les compteurs de budget comptent : le DÉCIDÉ.** *(Question de l'agent
|
||||
app, 2026-08-29.)*
|
||||
|
||||
@ -850,6 +850,35 @@ se déduit.
|
||||
> La maquette **vaut comme contrat, pas comme feuille de route** : elle fixe la forme de ce qui
|
||||
> sera publié, pas une date. Et côté app, rien ne commence avant que `levels[]` soit en ligne.
|
||||
|
||||
**LM-1011 — `BELOW_MIN_POWER` prime sur `DRAW_CAP` dès qu'il reste de l'autorisation.**
|
||||
*(Précédence tranchée par Patrick le 2026-08-29, sur une question de l'agent app. Écrite ICI,
|
||||
une fois, pour qu'aucun côté de la frontière ne l'établisse pour son compte.)*
|
||||
|
||||
Autorisation résiduelle **non nulle mais inférieure au plus petit palier** du mécanisme :
|
||||
**`BELOW_MIN_POWER`**. `DRAW_CAP` reste réservé à l'autorisation résiduelle **nulle**.
|
||||
|
||||
Les deux motifs sont vrais, mais pas au même niveau. `DRAW_CAP` dit « **il n'y a plus
|
||||
d'autorisation** » — or il en reste, simplement **pas au bon format**.
|
||||
|
||||
**Ce n'est pas une règle nouvelle : c'est celle de `+etm19`, transposée à une seconde
|
||||
ressource.** Le surplus l'a déjà : `SURPLUS_INSUFFICIENT` dit « il n'y a rien »,
|
||||
`BELOW_MIN_POWER` dit « il y en a, mais pas au bon format ». Le soutirage hérite du même couple,
|
||||
et il aurait été absurde qu'une ressource se lise autrement que l'autre.
|
||||
|
||||
**Ce qui décide n'est pas la véracité, c'est le GESTE que le motif appelle** — règle 7-b : un
|
||||
motif faux envoie diagnostiquer le mauvais problème, avec l'autorité d'une explication.
|
||||
|
||||
| Motif | Ce qu'il dit | Le geste utile |
|
||||
|---|---|---|
|
||||
| `DRAW_CAP` | l'autorisation est **épuisée** | revoir le plafond, ou attendre |
|
||||
| `BELOW_MIN_POWER` | il en reste, **la charge ne sait pas la prendre** | lire la fiche technique du mécanisme |
|
||||
|
||||
> **Conséquence retenue de l'agent app** : le **point de rupture** de l'écran « ordre de service
|
||||
> du cycle » est **ancré sur le `DRAW_CAP`**, donc sur l'épuisement réel. Une charge refusée
|
||||
> **après** lui pour cause de palier porte `BELOW_MIN_POWER` et **ne déplace pas** ce point :
|
||||
> elle est en aval de la coupure, elle n'est pas la coupure. Sans cette précédence, deux charges
|
||||
> successives auraient produit deux points de rupture concurrents dans le même cycle.
|
||||
|
||||
**LM-1007 — Compatibilité descendante, et elle est gratuite.** Une charge qui ne déclare
|
||||
**aucun** besoin éco ne participe qu'à la passe 2 — soit exactement le comportement
|
||||
d'aujourd'hui. Le modèle à deux niveaux **dégrade proprement** vers le modèle actuel, ce qui
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user