ai-engine · Intégration

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=IOKOO

Le 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 gesteLa routeClé
Un réglage de l'app, n'importe lequelPUT/DELETE /v1/app/settings/{key}app
Un réglage d'une organisationPUT/DELETE /v1/tenant/settings/{key}tenant
Écrire un thème du cataloguePUT /v1/app/skills/{id}app
Ouvrir la recherche web d'un thèmePUT /v1/app/skills/{id}/webapp
Retirer du service une fiche capitaliséePUT /v1/app/procedures/{id}app
Retirer du service une image partagéePUT /v1/app/illustrations/{id}app
Durcir un thème pour une organisationPUT /v1/tenant/skills/{id}/overlaytenant
Trancher un sujet refuséPOST /v1/tenant/scope-candidates/{id}/decisiontenant

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 refusePourquoi, 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_ACTIONle plafond d'action de l'installation ; un tenant ne peut que descendre sous lui
APPS, TENANTSla 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 SQLce 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écriviezCe que c'était vraiment
six clés que rien ne relitsept : 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 inerteappliqué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.md promettait « 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 sur POST /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_KEY est « 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 par INTERNAL_KEY, pas par API_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.

  1. Un tenant ne naît pas provisioned: false. C'est l'état d'un échec ; le chemin ordinaire rend true. Votre phrase « le tenant naît avec provisioned: false » décrit le mauvais jour, pas le jour normal.
  2. 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 écartCe qui est fait
max_output_tokens annoncé « 1 à 4 000 », 502 opaque sous 16422 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 mediumUn 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 sCorrigé, 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_skillsIl 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.

  1. « 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.
  2. « scopeIdFor est 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).
  3. « GET /v1/usage/runs donne la vraie durée. » Non — le même champ, le même défaut (§6).
  4. « Le tenant naît avec provisioned: false. » Seulement sur échec (§5).
  5. « answer.step sous recommendations » présenté comme une entorse au contrat. C'est au contrat, et documenté (§6).
  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é.

On this page