Réponses aux demandes du 1er octobre 2026
Ce document répond au document « Réponses à l'équipe ai-engine, et nos demandes » du
1er octobre 2026, section par section. Il est écrit comme le précédent
(REPONSES-MANQUES-IOKOO.md) : chaque affirmation porte le
fichier où elle se vérifie, et les chiffres sont mesurés, pas promis.
Ce qui l'encadre. Votre décision du 1er octobre — le moteur est exploité par nous, en service géré — change la nature de vos demandes. Un réglage sans route n'est plus une gêne d'intégration : c'est un aller-retour avec nous, par client. Nous l'avons pris comme tel, et c'est pourquoi la réponse au §3 est plus large que ce que vous demandiez.
Ce qu'il ne fait pas. Il ne vous renvoie pas vos propres erreurs. Vous aviez lu le vrai arbre : presque tous vos numéros de ligne tombaient juste, et deux des défauts que vous décrivez sont pires que ce que vous en dites. Les six points où nous vous contredisons sont au §7, et aucun ne change une de vos conclusions.
Ce qui a changé dans le contrat est dans CHANGELOG-CONTRAT.md,
sous 1.1, où ⚠️ signale ce qui casse un appelant. La version ne monte pas : 1.1 n'est
pas encore livrée, et tout ce qui suit y entre — contract_version vaut toujours 1.1, et
c'est la seule valeur que vous ayez à comparer.
0. Ce qui vous débloque aujourd'hui, et qui ne demandait aucun code
Vous écrivez : « Ce qu'il nous faut tout de suite, et c'est petit : le fichier d'import de votre catalogue conseil, la commande qui le joue, et les deux drapeaux posés à vrai. Un fichier et trois lignes. »
La commande existe, et elle accepte une app nommée depuis toujours :
pnpm skills:seed --app=IOKOOLe paquet est dans l'arbre — packages/skills/catalog/, 34 fiches : 25 theme.*,
5 conseil.*, 2 request.*, 1 resolution.*, 1 triage.*. Le sous-dossier proposed/
n'est pas lu. La commande charge les registres avant de résoudre l'app
(packages/skills/src/seed.ts), précisément pour que --app= puisse désigner une app créée
depuis le contrat ou la console. Elle est idempotente au sens fort : rejouée, elle
n'écrit rien et ne paie aucun embedding.
Les deux drapeaux sont posés. WEB_SEARCH_ENABLED et CAPITALIZE_ENABLED valent false
au schéma — le bon défaut pour un dépôt, le mauvais pour une installation qui sert des
plateformes. Ils sont désormais dans le ConfigMap de l'installation gérée
(deploy/k8s/base/configmap-engine.yaml), avec la raison écrite à côté.
Une correction à votre diagnostic, et elle vous donne plus que ce que vous demandiez : ce
ne sont pas des « drapeaux d'installation ». Ni l'un ni l'autre n'a d'entrée dans la table des
portées (packages/core/src/config.ts, SCOPED) : les trois portées ont un effet depuis
toujours. Ce qui manquait n'était pas la portée, c'était le chemin pour l'écrire — et il
existe maintenant (§2). Vous pouvez donc les ouvrir ou les refermer pour votre app, sans
nous.
Et une chose que vous demandiez sans le savoir. « Déposer un document au socle ne crée pas
de compétence » : vous avez mesuré juste, et la cause était pire que le symptôme. Le juge
d'indexation ne juge que contre le catalogue existant ; sur un catalogue vide il levait,
l'erreur était avalée, et le document restait indexé avec theme: null — définitivement,
sous toute configuration. C'est corrigé : le dépôt au socle joue maintenant la couverture,
comme la synchronisation d'une organisation le fait depuis l'Étape 1b. Le thème du catalogue
qui couvre le sujet, ou un thème générique créé pour lui. Et pnpm skills:cover --app=IOKOO
rattrape les dix documents déjà déposés.
1. Vos deux bornes du §2
person.groups : 20 → 100, et deux choses que vous n'avez pas vues
Accordé. Votre cas tient : une PME de trente personnes avec un groupe par service, par site et par projet dépasse vingt sans rien faire d'anormal.
Notre borne de 20 n'était pas une mesure, et c'est à nous de le dire : groups porte un
rôle d'accès (packages/core/src/person.ts), il n'entre dans aucun prompt, dans aucun
souvenir et dans aucune fiche capitalisée. Cent étiquettes ne coûtent pas un jeton. La
borne d'avant était une symétrie avec le plafond des périmètres, et une position éditoriale
(« au-delà, c'est un annuaire »).
Ce que vous n'avez pas vu, et qui va dans votre sens. Vous écriviez : « nous préférons un
422 à une troncature ». Cette troncature existait déjà, et pas à l'endroit que vous
surveilliez : le prédicat d'audience coupait à vingt groupes dans le WHERE SQL
(packages/starrocks/src/knowledge.ts). Au-delà, une personne perdait les documents de ses
groupes 21 et suivants — sans erreur, sans ligne de journal. Un produit qui marche et qui
perd des données en silence, c'est-à-dire exactement ce que votre point 8 nous reprochait
ailleurs. Les deux bornes ne sont plus qu'une seule constante, la validation d'entrée rend un
422 qui donne le compte, et le prédicat lève plutôt que de tronquer.
⚠️ Et une mauvaise nouvelle, qu'il vaut mieux lire ici qu'en production. Vos étiquettes
sont des chaînes libres de 2 à 80 caractères, choisies par le client : « Comptabilité »,
« Agence de Lyon ». Elles n'auraient jamais rien ouvert. L'audience d'un document est
bornée depuis le début à 64 caractères [A-Za-z0-9_.:-] — pas d'espace, pas d'accent — et
les deux côtés sont comparés par égalité de chaîne dans un prédicat SQL. Une étiquette
avec un espace était acceptée, stockée, et ne correspondait à aucun document.
person.groups applique donc maintenant la même règle de forme que l'audience d'un
document, et refuse le reste en 422 en disant pourquoi. Ce que ça vous demande : envoyer une
étiquette stable — l'identifiant de votre groupe, ou un slug que vous tenez — des deux
côtés, à l'audience du document comme au person.groups du tour. Nous ne slugifions rien
nous-mêmes : deux libellés différents pourraient se replier sur la même étiquette, et un
document s'ouvrirait à un groupe qui n'est pas le sien.
L'administrateur : accordé sur le besoin, refusé sur la forme — et c'est mieux pour vous
Votre cas qui décide : « un administrateur voit tous les documents de groupe de son espace sans être membre d'aucun ». Vous en concluez qu'il faut lui envoyer la liste de tous les groupes de l'espace.
Non, et nous ne le ferons pas : ce droit vient d'un rôle, pas d'une appartenance. L'envoyer en cent étiquettes opaques modélise un droit en données — ce que nous avons déjà refusé dans nos réponses du 30 septembre (§4) — et vous obligerait à tenir la liste de tous vos groupes à jour dans chaque message, pour chaque administrateur.
Un booléen le dit mieux : person.sees_all_groups: true ouvre les documents de tous
les groupes de l'organisation. Il coûte un booléen, il ne demande aucun annuaire, il sert
toute app, et il n'ouvre jamais les documents personnels des autres — ils vivent dans
une autre table, filtrée par l'empreinte de leur propriétaire, et aucun drapeau du profil ne
l'atteint. Le moteur ne vérifie aucun rôle, parce qu'il n'en connaît aucun : c'est vous qui
déclarez le droit, à chaque message, comme vous déclarez les groupes. Le défaut est le plus
étroit.
Un risque différé, que nous vous devons. Un IN (…) de cent termes est un WHERE posé
devant la recherche. Sans conséquence tant que ce cluster cherche en exact — c'est le cas —,
mais notre propre code prévient qu'un tel prédicat peut défaire le plan d'un index HNSW
(knowledge.ts). Le jour où le mode de recherche repassera en approx, cent étiquettes
seront exactement le cas visé. C'est une raison de plus de préférer le booléen pour un
administrateur.
Profondeur de schéma : 6 → 7. Accordé, et votre arithmétique était juste
Un niveau de tableau consomme un cran (packages/core/src/json-schema-guard.ts), donc
racine → implementation → steps[] → actions[] → title compte sept. Votre guide de cas
d'usage passe.
C'était notre règle, pas celle d'un fournisseur, et nous le disons parce que la garde dit
explicitement quand elle encode une contrainte de fournisseur — elle le fait pour required,
elle ne le faisait pas pour la profondeur. La garde qui tient réellement la dépense est la
taille (32 Kio) et les 200 propriétés : sept niveaux laissent encore voir un modèle de
données déguisé en contrat de sortie, et c'est la taille qui l'arrête.
Un contrôle de banc épingle maintenant ce cran — profondeur 7 admise, profondeur 8 refusée — parce qu'une borne qu'aucun contrôle ne tient redescend à la première relecture.
Ce que nous ne savons pas, et que nous n'allons pas deviner : si le mode strict d'un fournisseur a son propre plafond d'imbrication, il n'est ni documenté chez lui, ni appliqué chez nous. Si un appel échoue à sept niveaux chez un fournisseur donné, c'est à cet endroit qu'il faudra regarder.
2. Votre §3 : le back-office devient une API du contrat
Accordé, et plus largement que demandé. Votre argument est le nôtre, et vous l'avez cité
avec nos mots : GET /v1/usage existe parce qu'une plateforme ne pouvait pas lire sa propre
consommation sans notre console. Le paramétrage est le même geste, pour la même raison.
Dix routes, et deux familles seulement. La CLÉ dit laquelle : ce qui appartient au
produit (/v1/app/…) et ce qui appartient à une organisation (/v1/tenant/…).
| Le geste | La route | Clé |
|---|---|---|
| Un réglage de l'app, n'importe lequel | PUT/DELETE /v1/app/settings/{key} | app |
| Un réglage d'une organisation | PUT/DELETE /v1/tenant/settings/{key} | tenant |
| Écrire un thème du catalogue | PUT /v1/app/skills/{id} | app |
| Ouvrir la recherche web d'un thème | PUT /v1/app/skills/{id}/web | app |
| Retirer du service une fiche capitalisée | PUT /v1/app/procedures/{id} | app |
| Retirer du service une image partagée | PUT /v1/app/illustrations/{id} | app |
| Durcir un thème pour une organisation | PUT /v1/tenant/skills/{id}/overlay | tenant |
| Trancher un sujet refusé | POST /v1/tenant/scope-candidates/{id}/decision | tenant |
WEB_ALLOWLIST, les deux drapeaux et toute clé future passent par la première paire. Ce
qui est réglable, dans quelle portée, et avec quelle valeur est déjà une donnée du moteur
(settableKeys, scopeRule, settingWriteProblem), pas une suite de if : une clé ajoutée
au schéma devient réglable sans qu'une route s'ajoute. Trois de vos huit demandes
s'effondrent donc dans une paire de routes, et la prochaine app n'aura rien à demander.
Il n'y a pas de route de LECTURE, et c'est un choix. La réponse d'une écriture rend la ligne écrite et sa date — de quoi vérifier qu'elle a pris. Lire tout l'écran dirait aussi le plancher que l'exploitant pose pour l'installation, qui ne vous appartient pas.
Cinq refus en 422, chacun avec sa raison. La clé reste dans .env (un secret, une adresse
de service) ; elle ne se change pas à chaud (elle périmerait des données déjà écrites) ; cette
portée n'a pas d'effet pour elle (LLM_* et WEB_ALLOWLIST se règlent par app) ; la
valeur ne passe pas son schéma ; ou la clé appartient à l'exploitant.
Ce dernier refus est nouveau, et il est nommé clé par clé — c'est l'autre moitié de votre demande, celle que vous n'avez pas formulée et que nous vous devons quand même :
| Ce que la route refuse | Pourquoi, et où c'était déjà écrit |
|---|---|
LLM_* — modèle, fournisseur, effort, niveau de service | « vous ne choisissez pas le modèle qui vous sert », et le coût reste imputable à l'exploitant (API.md § App) |
GENERATE_MAX_OUTPUT_TOKENS | « le niveau est déclaré par la plateforme, le plafond est décidé par l'exploitant » — une borne qu'on relève soi-même n'est pas une borne |
RATE_* | le débit et la dépense journalière protègent l'installation entière |
GENERAL_DEBUG_MAX_ACTION | le plafond d'action de l'installation ; un tenant ne peut que descendre sous lui |
APPS, TENANTS | la déclaration de l'installation : une app et une organisation se créent par POST /v1/apps et POST /v1/tenants, qui provisionnent |
PLATFORM_JW* | l'ancre de confiance de l'identité signée : la changer permettrait de faire vérifier des jetons de personnes contre une clé apportée |
| le processus — adresse d'écoute, journal d'un conteneur, trace SQL | ce que l'orchestrateur fixe et que l'application subit |
Ce qui reste dehors, et nous le répétons alors que vous ne le demandez pas : le corps d'un
thème du catalogue de l'installation et la promotion d'un brouillon — ils décideraient de
ce que l'assistant dit, pas seulement d'où il cherche ; la lecture d'un secret, dans aucun
sens ; les écarts qui valent pour l'installation entière, parce qu'ils portent sur les clients
des autres. Votre PUT /v1/app/skills/{id}, lui, écrit le catalogue de votre app : il est
à vous, et le même contrat que celui d'un paquet livré le valide — schéma, neuf invariants,
lint de neutralité. Cette route ne peut rien écrire qu'un paquet ne pourrait écrire.
setOverlay ne peut toujours que durcir, et ce n'est pas une politesse de validation : le
garde effectif est recalculé à chaque lecture du catalogue. Une ligne écrite à la main
dans la base avec un risque abaissé ne désarmerait rien non plus. Par la route : abaisser le
risque d'un thème HIGH ou lever une escalade imposée rend 422.
Une nuance de franchise sur votre meilleur argument. Vous citez l'entrée de notre journal de contrat — « les périmètres s'écrivent par le contrat […] une application tierce peut ouvrir et fermer les périmètres de ses clients elle-même » — comme un précédent. Elle en est un, mais pas celui que vous croyez : elle est sous « 1.0 — l'état livré, reconstitué », et elle existe parce que six routes sont arrivées sans être annoncées. C'est un précédent de ce qu'il ne faut plus faire. Les dix routes de ce lot sont au journal avant d'être servies.
Sur scopeIdFor, que vous désignez comme « le seul vrai coût » : ce n'est pas un modèle
d'autorisation, et son propre commentaire le dit. Il compare le corps d'une écriture à
l'écran d'où part le geste — une cohérence, pas une habilitation —, et il n'est branché que
sur setSetting. Ce qui tient réellement ces dix routes est plus simple, et c'était déjà
notre règle : le contexte porte app ou tenant, jamais les deux, et aucune route ne
prend un identifiant d'app ou de tenant dans son corps (INTEGRATION.md).
La clé EST l'autorisation ; il n'y a rien à recouper. Un corps qui porterait app, tenant ou
actor est refusé en 422 plutôt qu'ignoré — une tentative se lit, un silence se répète. Ce
qui a bel et bien été extrait, c'est le 404-et-non-403 de DELETE /v1/tenants/{id} : il sert
aux routes qui nomment un autre objet, et le contrat ne dit jamais à une app qu'un
identifiant existe ailleurs.
3. Vos six clés inertes : accordé, et c'est pire que ce que vous décrivez
Vous écrivez que six clés sont acceptées à l'écriture et relues par personne. C'est vrai, et deux d'entre elles étaient dans un état plus mauvais que « inerte ».
| Ce que vous décriviez | Ce que c'était vraiment |
|---|---|
| six clés que rien ne relit | sept : GENERATE_MAX_OUTPUT_TOKENS était du même lot, et c'est elle qui faisait mentir notre promesse « l'exploitant tient la borne » — la console l'acceptait, la route ne la lisait jamais |
| « l'écran ne les marque pas inertes » | l'écran CONFIRMAIT une écriture morte. USER_MEMORY=tdam posé en portée installation s'affichait tdam · base:installation, journalisé, registre rechargé — et tous les lecteurs lisaient encore off. Vous décriviez un silence ; le code produisait un faux témoignage |
ILLUSTRATION_SEARCH_ENABLED inerte | à moitié branchée : quatre lecteurs par la cascade, sept par l'instantané. Une écriture mettait le moteur dans un état mi-ouvert — le tour obéissait, la passerelle rendait 404 sur l'image |
MCP_ENABLED inerte | appliquée par l'instantané, affichée par la cascade : l'écran annonçait MCP allumé pendant que chaque appel était refusé |
Les sept clés sont relues par la cascade, et une huitième avec elles
(PROCEDURE_REFRESH_ENABLED, du même défaut : le tour la lisait par l'instantané, le travail
de fond par la cascade). Avec elles, sept portées incomplètes ont été corrigées : un
configFor({ tenant }) saute la couche du milieu, donc une ligne posée pour une app
restait inerte — y compris à la porte d'écriture de CAPITALIZE_ENABLED, c'est-à-dire sur
l'un des deux drapeaux que vous demandez. Un seul appel construit désormais la portée complète
(settingAtFor), et l'app est dérivée du tenant : elle ne voyage plus dans dix-huit
signatures, où on pouvait l'oublier dix-huit fois.
Une clé que nous avons reclassée au lieu de la rendre réglable à chaud, et nous vous devons
la raison. ILLUSTRATIONS_ENABLED entre dans l'index_version des documents : basculée à
chaud, elle ne change pas un comportement — elle rend périmées les lignes déjà indexées
sous l'autre régime, que la recherche cesse alors de lire. Elle rejoint donc la famille « ne
se change pas à chaud » : .env ou le ConfigMap, et une réindexation annoncée. Son pendant
ILLUSTRATION_SEARCH_ENABLED — la bibliothèque partagée — se règle bien par les trois
portées, à chaud.
Et une honnêteté que vous n'avez pas demandée : le problème est plus large que vos six
clés. Le dépôt compte des centaines de lectures env.X, et rien ne déclare, clé par clé, si
ses lecteurs passent par la cascade — notre propre code l'assume par écrit. Ce que nous avons
corrigé, ce sont les clés dont un réglage à chaud a un sens pour une plateforme, plus
celles qui mentaient. Si vous en trouvez une autre dans ce cas, c'est un défaut, pas un
arbitrage : dites-le, c'est quatre lignes.
Votre §5 sur USER_MEMORY cesse d'être une question d'exploitant : vous pouvez la poser
pour votre app, et pour une organisation en particulier, par PUT /v1/app/settings/USER_MEMORY
— ou la refermer. La réponse de l'installation gérée reste au §9.
4. Ce que vous avez manqué, et qui va dans votre sens
Trois défauts du même sang que ceux que vous relevez. Aucun n'était dans votre document.
- La troncature à vingt groupes dans le prédicat SQL (§1). C'est votre propre argument, et il était déjà vrai chez nous.
- Le plafond de l'exploitant que la route ne lisait pas.
docs/API.mdpromettait « on peut demander moins, jamais plus », et la route lisait l'instantané du démarrage : une valeur posée par la console n'avait aucun effet surPOST /v1/generate. Corrigé par la cascade, et la clé est maintenant refusée à votre écriture, parce que c'est un plafond. - Un secret qui pouvait descendre en base.
ENGINE_INTERNAL_KEYest « un secret partagé entre la passerelle et l'agent du même hôte, jamais publié » — et le motif qui écarte les secrets de la table des réglages ne le voyait pas : il finit parINTERNAL_KEY, pas parAPI_KEY. Il pouvait donc être écrit dans StarRocks, où aucune primitive de chiffrement ne le protège. Le motif le voit maintenant. Vous ne pouviez pas le trouver — il n'est pas dans la surface du contrat —, mais il touche à ce que vous nous demandez de garantir.
5. Votre dernier manque : reprendre un provisionnement en échec
C'est votre demande la mieux placée, et elle est livrée : POST /v1/tenants/{id}/provision,
clé d'app ou d'installation, 404 pour une organisation d'une autre app.
Rejouable par construction. Il n'existe aucune colonne provisioned : l'état est
dérivé en sondant les tables présentes, et le provisionnement crée ce qui manque sans toucher
à ce qui est là. Sur une organisation déjà en service, la route rend created: 0. Ce qu'elle
ne fait pas : reconstruire une table dont la forme a dérivé — on y perdrait son contenu.
Ce cas rend 422 en nommant la table, et demande un geste de notre côté.
Deux corrections à votre prémisse, et la seconde vous protège plus que la route.
- Un tenant ne naît pas
provisioned: false. C'est l'état d'un échec ; le chemin ordinaire rendtrue. Votre phrase « le tenant naît avecprovisioned: false» décrit le mauvais jour, pas le jour normal. - Le moteur ne réessayait jamais. Toute panne de provisionnement était convertie en erreur terminale, ce qui court-circuitait les trois tentatives qui existaient déjà juste en dessous. Une panne d'une seconde de la base laissait donc un client sans base, définitivement. Seule une divergence de schéma est désormais terminale — elle seule ne se corrige pas en réessayant. C'est quatre lignes, et c'est ce qui vous protège vraiment : la route est le dernier recours, pas le premier.
Et deux gestes qui existaient déjà de notre côté, que notre documentation ne disait pas :
l'écran Tenants de la console porte un bouton « Provisionner », et pnpm db:bootstrap
rejoue tous les tenants du registre. API.md § Administration les nomme
maintenant.
Un petit écart distinct, que nous vous signalons parce que vous le rencontrerez : un
tour sur une organisation non provisionnée ne rend pas 503 « provisionner le tenant » — il
rend 502, parce qu'il échoue dans la recherche, hors des sept pré-contrôles de la
passerelle. Le message est juste, le statut ne l'est pas. Ce n'est pas corrigé dans ce lot :
ajouter un huitième pré-contrôle coûterait une requête à chaque tour de chaque client,
pour un cas qui ne survit qu'au premier appel. Si vous préférez l'inverse, dites-le — la
décision est à vous, et l'aide à réutiliser existe (missingTenantTables).
6. Vos quatre écarts du §4
| Votre écart | Ce qui est fait |
|---|---|
max_output_tokens annoncé « 1 à 4 000 », 502 opaque sous 16 | 422 en pré-vol, qui nomme les deux bornes : 16 est le plancher du fournisseur, 4 000 le plafond de l'exploitant. Notre page était fausse par le bas ; elle est corrigée |
4 000 jetons entiers en effort low, et No object generated à 3 000 en medium | Un plancher par effort, refusé avant l'appel : au-dessus de low, au moins 4 000 jetons de sortie — le budget que le moteur se donne à lui-même pour une sortie structurée. Les jetons de raisonnement sont décomptés de max_output_tokens, donc la réponse était tronquée avant le JSON. Vous ne paierez plus pour l'apprendre |
trace.duration_ms à 34 ms sur un tour de 112 s | Corrigé, et ce n'était pas une approximation : l'heure de départ était lue hors du journal, donc recalculée à chaque reprise — la valeur mesurait le temps depuis la dernière reprise. Les deux bouts se lisent maintenant dans le journal |
POST /v1/apps ne rend pas catalogue_skills | Il le rend à nouveau, et pour une vraie raison cette fois : il porte le nombre de thèmes hérités (§0 et §8). Nous l'avions retiré le 1er octobre parce qu'il valait toujours 0 — vous nous demandiez « soit la clé, soit la ligne de documentation », et nous avions choisi la ligne ; il a maintenant quelque chose à dire |
⚠️ Un piège que votre diagnostic n'a pas vu, sur le même sujet. Vous dites que
{ max_output_tokens: 4000, effort: "low" } marche aujourd'hui. C'est vrai — mais la
configuration passe devant l'appelant : une préférence de thème, puis
LLM_GENERATE_EFFORT de l'app, puis votre effort. Un effort posé par l'exploitant écrasait
donc le vôtre en silence, et vous auriez cherché la cause d'une sortie trop courte dans
votre code. La réponse porte maintenant effort et effort_origin quand l'effort appliqué
n'est pas le vôtre, et rien quand il l'est.
Sur GET /v1/usage/runs, que vous proposiez comme oracle de durée : c'est le même champ.
Il est écrit depuis la même trace. Avant ce lot, aucune route ne rendait la vraie durée — y
compris celle que vous comptiez utiliser pour nous contrôler. Les deux sont corrigées par la
même ligne.
Sur answer.step sous answer_form: "recommendations" : ce n'est pas une entorse, c'est au
contrat — notre référence le disait déjà, en le présentant comme rare. Les deux cas ne le
sont pas : une demande explicite d'être guidé, et une escalade. La page le dit maintenant
franchement. Nous n'avons pas touché au prompt du composeur, qui dit encore « c'est rare » : il
est gelé (quatorze prompts, pnpm prompts:check), et le corriger demanderait de tout
regeler et de remesurer un banc — ce n'est pas le prix d'un adverbe. Votre écran a donc raison
de le prévoir.
7. Six choses que vous nous opposez, et qui ne sont pas exactes
Aucune ne change une de vos conclusions. Nous les disons parce qu'un document qu'on ne corrige pas devient une source.
- « Les deux drapeaux sont des réglages d'installation. » Non : ni l'un ni l'autre n'a d'entrée dans la table des portées. Les trois portées ont un effet depuis toujours. Ce qui manquait était le chemin pour les écrire.
- «
scopeIdForest l'endroit où brancher le contrôle d'appartenance. » C'est l'endroit que nous avons désigné pour le jour où un opérateur sera limité à une app — mais ce n'est pas un contrôle d'autorisation, et il ne vous concerne pas : pour vos routes, la clé est l'autorisation (§2). - «
GET /v1/usage/runsdonne la vraie durée. » Non — le même champ, le même défaut (§6). - « Le tenant naît avec
provisioned: false. » Seulement sur échec (§5). - «
answer.stepsousrecommendations» présenté comme une entorse au contrat. C'est au contrat, et documenté (§6). - Deux de vos citations ont glissé :
docs/API.md:89-90(le catalogue vide) et:100-101(le provisionnement). Le texte vivant est dans la section Administration, et il a bougé depuis — nous citons désormais les sections plutôt que les lignes pour cette raison.
8. Votre correction du §6, acceptée telle quelle
Vous corrigez votre propre relevé : 10 entrées pour 9 adresses distinctes, et non 13 pour 12. Vous écrivez que notre chiffre était le bon.
Nous le prenons comme ça, et nous ne le retournons pas. Ce qui mérite d'être lu dans votre
correction n'est pas l'erreur, c'est sa cause : un balayage du fichier jusqu'au premier
]; alors que la déclaration se ferme sur };. C'est exactement la classe de défaut que nos
quatre compteurs de routes écrits à la main produisaient chez nous, et qu'un contrôle dérivé
de la table — et non d'une relecture — a seul fermé. Vos deux autres mesures (3 374 caractères
sur 18 lignes, 125 domaines distincts) tiennent.
9. Votre §5 : ce que nous pouvons écrire, et ce qui vous sera donné à part
La sixième question, la seule réglementaire, a sa réponse ici.
USER_MEMORY vaut tdam sur l'installation gérée (deploy/k8s/base/configmap-engine.yaml).
Ce n'est donc pas off, et votre sujet ne se clôt pas d'une phrase : le problème de résidus
existe.
Ce que le moteur garantit, et c'est vérifié de bout en bout : DELETE /v1/me/memory et
DELETE /v1/me effacent en deux passages, avec une pause entre les deux — sans elle,
une extraction déjà lancée réécrirait un fait derrière l'effacement. residue_pending dit si
des copies internes de la passerelle de mémoire attendent encore la purge de maintenance, et
GET /v1/me/memory le rend. Vous pouvez donc constater l'état de l'effacement d'une
personne par le contrat, à tout moment.
Ce qui reste à notre charge, et que nous vous devons par écrit : la cadence du passage de
purge (pnpm memory:purge-residues) et la réponse à « pouvons-nous le déclencher ». Les deux
sont des engagements d'exploitation, pas des lignes de code — ils vous seront donnés avec les
cinq réponses ci-dessous.
Durée de conservation : nous n'avons rien d'écrit là-dessus aujourd'hui, et nous ne vous servirons pas une durée inventée pour faire bonne figure. La vôtre est de 30 jours avec droit de rétractation ; la nôtre doit être écrite, et elle le sera dans le même document.
À fournir avant l'envoi de ce document — cinq réponses commerciales ou d'exploitation, qu'aucune ligne de code ne porte, et qu'il serait malhonnête d'écrire ici à la place de qui en décide : où tourne le moteur (hébergeur, région) ; quels fournisseurs de modèle, et dans quelle région ; un client peut-il exiger une région ou un fournisseur, et à quelles conditions ; quel engagement de disponibilité et quelle fenêtre de maintenance ; quel prix, et facturé à qui. Plus la cadence de purge et la durée de conservation ci-dessus.
Ce que le contrat garantit déjà, et qui ne dépend pas de ces réponses : il n'expose ni le modèle ni le fournisseur, délibérément, et aucune clé de fournisseur ne peut venir d'un client. Votre décision d'architecture n° 4 promet « géré, européen, ou entièrement chez vous » : les trois restent possibles — c'est le même moteur, et la troisième ligne de votre promesse est précisément l'installation que vous avez choisi de ne pas faire. Ce que ce choix déplace, ce n'est pas le produit : c'est qui répond de l'hébergement. Nous devons donc vous le dire noir sur blanc, et pas dans un document de contrat d'API.
10. Ce qui reste
De notre côté, et pas dans ce lot :
- le 502 au lieu d'un 503 sur un tour vers une organisation non provisionnée (§5) : la décision est à vous ;
- la collection Postman ne couvre pas encore les onze routes de ce lot — 54 des 79. Les
routes sont éprouvées par notre vérification de contrat (
pnpm contract:verify), pas encore par la collection que vous jouez ; - le prompt du composeur sur
answer.step(§6), et son banc.
De votre côté, ce qui vous débloque aujourd'hui (§0) : pnpm skills:seed --app=IOKOO est
à jouer par nous sur l'installation gérée, puis pnpm skills:cover --app=IOKOO pour les dix
documents déjà déposés. Dites-nous quand vous voulez que ce soit fait — c'est une minute, et
c'est ce qui rend vos tours utiles.
Ce qui vous attend quand vous reprendrez l'intégration : vos étiquettes de groupe doivent devenir des étiquettes et non des libellés (§1), et votre plan d'action expert doit demander 4 000 jetons et non 2 500 (§6) — vous l'aviez déjà noté.