Commit Graph
775 Commits
Author SHA1 Message Date
LogiFlow 00e09a71e3 Lots 8 à 47 : migration Rust — routes, moteur, tâches, optimiseur, tour de la façade et tous les outils de l'agent (#33)
Lots 8 à 47 : migration Rust — routes, moteur, tâches, optimiseur, tour de la façade et tous les outils de l'agent
2026-10-16 09:31:59 +02:00
Claude cbbe62ff97 Tests : faux outils écrits par un sh à part (ETXTBSY)
plan_par_machine (lot 41) a vu « cpu » au lieu de « cuda » en CI : le faux
nvidia-smi, écrit par le process des tests puis lancé aussitôt, peut être
encore ouvert en écriture dans le fils qu'un test voisin vient de forker ;
son exec échoue alors sur ETXTBSY et l'outil paraît absent. Les scripts des
tests de la compilation et le faux Chrome (lot 44, lancé à côté des tests
parallèles de tests/lot6.rs) sont désormais écrits par un sh à part, comme
les autres faux binaires du dépôt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 07:26:36 +00:00
Claude e8563d992c Lot 47 : journal du sous-agent indépendant du poste
Le briefing machine des vues des rôles nomme l'hôte et le compte : masqués
dans le journal ({{HOTE}}, {{COMPTE}}), qui différait sur le poste de la CI.
Il nomme aussi le système et l'architecture : journal tiré sous Linux
seulement, comme les autres journaux qui dépendent du poste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 07:15:53 +00:00
Claude ded1c97c17 Lot 47 : outil subagent du mode Code exécuté par la façade
Le builder délègue à un rôle en lecture seule (explorer, code-reviewer,
planner) sans passer par /tool/exec : src/chat/subagent.rs porte
toolSubagent. Mêmes refus avant tout appel au moteur (rôle inconnu,
verifier compris, tâche vide, récursion), contexte isolé servi par le Go
(POST /internal/v1/turn/subagent : prompt du rôle + briefing machine,
outils, capacités ; subagentView et subagentFraming partagés), tour
imbriqué run_turn avec l'endpoint, le raisonnement, l'écho et l'arrêt du
tour qui délègue, température 0,6, travail annexe (second slot, effacé à
la fin), télémétrie à part. Seul le texte écrit après le dernier outil
revient, rogné, préfixé du rôle, tronqué à 6000 runes.

Le tour du rôle est porté par SubHost, qui délègue tout à son GoTurnHost
sauf un nouvel appel à subagent ; les outils de la façade passent dans
GoTurnHost::exec_tool_base, que seul le tour principal complète de
subagent (le type du tour imbriqué ne se contient pas lui-même).

Parité : journal tiré du Go contre un faux moteur scripté
(parity_subagent_test.go → tests/parity_subagent/subagent.json : refus,
réponse directe, rapport après deux outils, texte vide relancé, rapport
tronqué, cadrage du planner, moteur qui refuse ; requêtes du rôle),
rejoué par un tour complet de la façade (tests/lot6.rs) ;
TestSubagentVueInterne côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 07:12:29 +00:00
Claude 92e9adee7d Lot 46 : outils recall et recall_search exécutés par la façade
Les deux outils qui rappellent un bloc archivé par la compaction ne passent
plus par /tool/exec du Go : src/recall.rs porte toolRecall et
toolRecallSearch (id tolérant comme recallIDToSeq, mots-clés en minuscules
rune par rune comme strings.ToLower, score libellé double et plus récent
d'abord, limites, extraits et textes du Go). L'archive reste au Go, seul à
tenir la séquence de la base, qui la sert à la façade
(GET /internal/v1/recall/block?seq=N et /internal/v1/recall/blocks, blocs
chiffrés sous une mémoire verrouillée exclus) ; ses propres outils passent
par les mêmes lectures. Go injoignable : l'appel passe par lui.

Parité : journal de 41 appels sur une archive de 35 blocs tiré du Go
(parity_recall_test.go → tests/parity_recall/recall.json), rejoué par
tests/recall.rs ; un tour de la façade qui rappelle et cherche
(tests/lot6.rs) ; TestRecallInterneLecture côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 06:54:35 +00:00
Claude db1f20c0a4 Lot 45 : outils d'image (see_image, web_screenshot) tenus par la façade
La façade exécute les deux outils qui montrent une image au modèle
(src/imagetools.rs, port de chat_vision_tool.go, chat_screenshot.go et
prepareImageForModel) : préparation de l'image comme les pièces jointes
(orientation EXIF, grand côté sous 1568 px, mêmes pixels), sonde du moteur
(/props authentifié, gardée 10 s ; preset externe : sa case), see_image aux
textes du Go, web_screenshot par Playwright (adresses locales quand internet
est coupé, mêmes arguments, erreur résumée, capture rangée dans le dossier
de la discussion, ménage à 20 captures et 40 Mo, relayée au modèle s'il
voit les images). Le Go garde les siens pour ses sous-agents et tâches IA.

Parité : journal tiré du Go avec ses fichiers, un faux moteur et un faux
Playwright (tests/parity_images/images.json), rejoué par
tests/imagetools.rs ; un tour de la façade sans passer par le Go
(tests/lot6.rs) ; tests unitaires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 06:36:08 +00:00
Claude fe51bdeee9 Lot 44 : navigateur piloté tenu par la façade
La façade tient la session Chrome des outils browser_* (src/browser/,
port de computer_cdp.go, computer_use.go, browser_grid.go et
cu_autoshot.go) : lancement (profil temporaire, mêmes arguments, tué avec
la façade), client CDP multiplexé sur websocket, attente du chargement et
du calme réseau, scripts injectés copiés à l'octet près, les neuf outils
aux textes du Go (snapshot dédoublonné, recherche, clics, saisie et listes
déroulantes, touches, défilement, navigation et ses erreurs), capture
rangée propre et quadrillée pour le modèle (mêmes pixels), aperçus JPEG en
anneau servis par /api/chat/cushot.

Un seul navigateur : quand la façade tient la conversation, le Go lui
passe les outils de ses sous-agents et tâches IA, ses aperçus et la
fermeture (POST /internal/facade/v1/browser) ; façade injoignable, le
navigateur du Go prend le relais. Couper l'interrupteur ferme les deux.

Parité : journal de 42 étapes tiré du Go contre un faux Chrome (script +
faux serveur CDP qui note chaque commande, scripts résumés par leur
empreinte, pixels de la grille), rejoué par tests/browser.rs ; un vrai
Chromium de bout en bout avec LOKI_CU_SMOKE=1 ; un tour de la façade qui
ouvre une page (tests/lot6.rs) ; TestFacadeNavigateur côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 06:17:51 +00:00
Claude 7288db632e Lot 43 : jobs d'arrière-plan du mode Code tenus par la façade
bash_bg et bash_tail des tours de la façade ne passent plus par /tool/exec
du Go : src/tools/jobs.rs porte code_jobs.go. Refus avant lancement
(commande vide ou dangereuse, optimisation en cours), journal
.loki/jobs/<id>.log dans le dossier de la discussion, processus qui survit
au tour, shell sans le jeton interne ; relecture bornée (40 lignes par
défaut, 200 au plus), état et durée, arrêt relu aussitôt comme le Go.
paths::go_mkdir_all reproduit os.MkdirAll, erreur comprise.

Les jobs d'une discussion supprimée s'arrêtent avec elle. L'optimiseur du
Go compte ceux de la façade (POST /internal/facade/v1/bgjobs) avant de
mesurer ; façade injoignable : les siens seulement.

Parité : journal de 24 étapes tiré du Go (parity_jobs_test.go →
tests/parity_jobs/jobs.json), rejoué par tests/jobs.rs ; un tour de la
façade qui lance un job (tests/lot6.rs) ; TestFacadeJobsArrierePlan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 05:42:32 +00:00
Claude f66d1b7621 Lot 42 : outils web de l'agent exécutés par la façade
Les outils web des tours de la façade (web_search, web_images, web_open,
web_read, web_grep) ne passent plus par /tool/exec du Go : src/webtools.rs
porte chat_internet_tools.go, chat_internet.go et chat_web_images.go. Le
cache des pages (10 min, clé MD5 des options comme le Go) est dans la
façade ; client Crawl4AI au corps près, réponses lues comme json.Unmarshal ;
résultats DuckDuckGo tirés du markdown (uddg, entités, extraits) ;
recherche d'images avec le jeton vqd et les cookies gardés par hôte ;
web_grep sur les regex du Go, erreurs au texte de regexp.

Le moteur intégré lit le HTML avec go-readability et html-to-markdown, que
rien en Rust ne reproduit au caractère près : la conversion d'une page et la
lecture des résultats restent au Go, derrière /internal/v1/web/fetch et
/internal/v1/web/search (sans état). web_screenshot reste au Go.

Le traducteur go_regex refuse la répétition d'une répétition et les comptes
au-delà de 1000, et rend littéral un « { » qui n'ouvre pas de répétition,
comme le Go (motifs retry_patterns compris).

Parité : journal de 74 appels tiré du Go (parity_web_tools_test.go →
tests/parity_web/outils.json : résultats, requêtes reçues par les faux
Crawl4AI et DuckDuckGo, pages demandées au moteur intégré), rejoué par
tests/webtools.rs ; un tour de la façade qui ouvre une page (tests/lot6.rs) ;
TestInternalMoteurWebIntegre côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 05:29:35 +00:00
Claude bed6d07739 Optimiseur : l'essai muet du banc tient son port
L'essai qui ne répond jamais (tests/tune.rs, essai_muet) ne liait pas le
port choisi par l'optimiseur : pendant les 2 s d'attente, un autre test
lancé en parallèle pouvait le prendre et répondre à /health à sa place
(« /props illisible » au lieu de « pas prêt après 2s », vu sur la CI). Il
le lie désormais sans jamais accepter, comme un moteur bloqué au
chargement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 04:56:31 +00:00
Claude 5197ee2542 Lot 41 : compilation de llama.cpp depuis les sources tenue par la façade
Avec LOKI_BACKENDS=rust, la façade compile elle-même llama.cpp sous Linux
et macOS (backends/build.rs) : install {force}, update {clean} et
install-custom {repo, name, ref}, port de backend_build.go, des jobs
lcRunInstall / lcRunUpdate / lcBuildAndSwitch et d'installCustomBackend.

- plan complet (Metal, CUDA avec racine du toolkit et architectures, HIP,
  Vulkan), build/ déplacé ou propre mis de côté en build.old ;
- étapes suivies : sorties des deux flux dans un même tube, découpage du
  sinkWriter (git) et du scanner (CMake), configure.log et build.log,
  fin des journaux remontée en cas d'échec ;
- outils manquants installés au mieux, compilateur vérifié ;
- service moteur arrêté le temps du build et relancé même en échec ;
- refus comme le Go : moteur de l'image, URL absente, optimisation en
  cours, job déjà lancé.

Côté Go, facadeBackendsRoutes gagne les trois routes hors Windows (la
chaîne MSVC reste au Go, qui rapporte ses étapes au job de la façade).

Parité : internal/loki/parity_build_test.go tire un journal de 15 étapes
(tests/parity_backends/build.json) que tests/backends.rs rejoue par la
façade ; tests unitaires du découpage, du plan, du cache et des erreurs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 04:54:32 +00:00
Claude 90d6dd4a9a Lot 40 : outils mémoire de l'agent exécutés par la façade
Quand la façade tient la mémoire (LOKI_MEM=rust), les outils mem_search,
mem_read, mem_add, mem_edit et tracker de ses tours s'exécutent chez elle
(src/memory/tools.rs, port de chat_memory.go et de l'outil de tracker.go) :
mêmes résultats et diffs, mêmes lectures et écritures que les routes de la
mémoire (scellées selon le chiffrement, .bak, index tenu par le code, seed
au nom du projet). Recherche classée par couverture puis pertinence,
extrait coupé à l'octet comme le Go ; trackers vus niveau par niveau.

ChatShared porte la même mémoire que les routes ; tracker_add prend l'heure
en paramètre (tracker_add_at). Côté Go, l'heure des trackers passe par
trackerNow, que le journal fixe.

Parité : journal de 57 appels tiré du Go (parity_mem_tools_test.go →
tests/parity_mem/outils.json : résultats, diffs, fichiers de la mémoire et
trackers), rejoué par tests/memory.rs ; un tour de la façade qui crée une
page (tests/lot6.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 04:29:53 +00:00
Claude 35886da9b1 Lot 39 : anneau de télémétrie et /api/perf/summary tenus par la façade
L'anneau des complétions (perf_log.go) est porté en Rust (src/perfring.rs) :
perte de cache contre la complétion précédente de la même discussion et de
la même nature, ce qui s'est intercalé, seuil d'alerte du preset (dans
l'ordre du lancement, 2048 sur un modèle hybride), ligne [perf], résumé de
/api/perf/summary. ChatShared le porte : tours, vérifications, résumés de
compaction et préchauffage de la façade y rangent leurs complétions sans
aller-retour au Go.

Côté Go, quand la façade tient la conversation, perfRecord range chez elle
(/internal/facade/v1/perf/record) et perfSnapshot relit son anneau
(optimiseur) ; /api/perf/summary lui est remis. Façade injoignable : l'anneau
du Go prend le relais. La route /internal/v1/turn/perf, plus appelée, est
retirée.

Parité : journal tiré du Go (parity_perf_test.go → tests/parity_perf/
perf.json : 14 complétions, résumé, onze seuils), rejoué par
src/perfring/tests.rs ; deux tours qui perdent le cache (tests/lot6.rs),
l'API de l'anneau pour le Go, et TestFacadePerfAnneau côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 04:17:04 +00:00
Claude 76d9dce74a Lot 38 : l'outil criteria du mode Code exécuté par la façade
L'outil par lequel le modèle tient les critères d'acceptation de sa tâche
(poser, marquer faits, lister, effacer) partait au Go, qui renvoyait la
liste à la façade comme delta. Il s'exécute maintenant dans le tour de la
façade (chat/crittool.rs, port de toolCriteria) : mêmes messages, ids en
nombre ou en chaîne, vingt critères au plus, `passed` réservé à la passe de
vérification, rangement scellé (chat/crit:<id>), liste poussée à l'écran à
chaque changement par un nouveau champ `deltas` de l'hôte du tour.

Les critères sont rangés sous la discussion du tour (le Go prenait la
discussion active : elles ne diffèrent que si l'utilisateur en change
pendant la génération). see_image reste au Go (préparation de l'image).

Parité : journal de 22 appels tiré du Go (parity_criteria_test.go →
tests/parity_tools/criteria.json), rejoué par tests/tools.rs ; le contrat
du mode Code de tests/lot6.rs est désormais vérifié par la façade seule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 04:01:03 +00:00
Claude 1d33ca5a49 Lot 37 : journal des outils task_* réservé à Linux, comme celui des tâches
Il emprunte parityTaskIDRe et parityApplyFiles aux journaux Linux : go vet
échouait sur macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:59:28 +00:00
Claude e94b625931 Lot 37 : outils task_* de l'agent exécutés par la façade
Quand la façade tient les tâches (LOKI_TASKS=rust), les quatre outils par
lesquels le modèle gère ses tâches planifiées — task_list, task_create,
task_update, task_delete — s'exécutent dans ses tours (src/tasks/tools.rs,
port de tasks_tools.go), sans passer par le Go : mêmes messages, projet
actif, fuseau de l'utilisateur, tâche unique « dans N minutes » ou « à
HH:MM », fréquence validée, script vérifié, compte-rendu coupé comme le Go.
ChatShared porte la même base de tâches que les routes.

Côté Go, les outils prennent l'heure des tâches (tasksNow) au lieu de
time.Now : rien ne change en service, le journal de parité la fixe.

Parité : journal de 44 appels tiré du Go
(parity_tasks_tools_test.go → tests/parity_tasks/tools.json), rejoué par
tests/tasks.rs ; un tour de la façade qui crée une tâche (tests/lot6.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:51:09 +00:00
Claude 869bd18bff Lot 36 : appels d'outils MCP du tour de la façade par son pool
Quand la façade tient les serveurs MCP (LOKI_MCP=rust), un appel mcp__*
d'un de ses tours partait au Go (/internal/v1/tool/exec), qui le renvoyait
aussitôt à la façade (/internal/facade/v1/mcp/call) : deux allers-retours
pour revenir au même pool. Le Go n'ajoute rien au résultat d'un outil MCP.

ChatShared porte désormais le pool (le même que celui des routes, créé une
fois) ; GoTurnHost::exec_tool l'appelle directement — même résultat que la
route interne, reconnexion comprise. Une reconnexion annonce la version du
Go (/internal/v1/auth), lue une fois et retenue. Sans pool tenu, rien ne
change. Le Go garde la route interne pour les tours qu'il exécute encore.

Test : un tour de la façade contre un faux serveur MCP stdio — rien par le
Go, résultats tels que le modèle les reçoit, erreur après reconnexion,
version du Go annoncée.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:47:58 +00:00
Claude 9dfce204bf Lot 28 : tests contre les mutations survivantes de l'optimiseur
105 mutations du verrou et du job ; le premier passage en tuait 39. Les
survivantes le sont désormais par des tests ajoutés :

- l'essai noté dans le verrou pendant la mesure, la sauvegarde notée avant
  d'écrire, l'application refusée pendant la mesure, la cible inconnue ;
- l'application gardée : preset qui n'est plus en service, API externe,
  clé changée ailleurs pendant l'application (défaite) ;
- l'application planifiée : clé sur option refusée sans l'option ;
- l'essai muet (délai de chargement), /props sans
  default_generation_settings, SIDE_SLOT sur un seul slot, threads comptés
  sur les cœurs de la sonde, aucun changement de moteur signalé quand son
  build est inconnu ;
- VRAM mesurée par un faux nvidia-smi, budget, profondeur, notes et poids,
  écarts de mesure, essais écartés, moteur piloté par la façade.

nvidia-smi, la pause entre deux lectures de VRAM et l'unité du budget
deviennent réglables (tests). Le test de l'essai orphelin lit l'identité
du processus une fois l'exec visible dans /proc : lue trop tôt, elle
portait l'exécutable du test (échec intermittent en CI).

Les vingt survivantes restantes, équivalentes ou hors de portée d'un test
(courses, estimation seulement), sont décrites dans la spec.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:46:44 +00:00
Claude c157da2141 Lot 35 : routes de base de la façade aussi par le tunnel du relais
Les neuf routes que la façade sert toujours (ping, status, config, ligne
de commande du moteur, couches du modèle, chemins, RAM, VRAM, journal du
service) arrivent désormais à la façade par le tunnel du relais aussi :
withFacadeChat les lui remet dès qu'elle tourne (facade_core.go). Les
handlers du Go ne servent plus que sans façade (LOKI_FACADE=0).

Côté Rust, une requête relayée par le Go (jeton interne) n'est pas
réauthentifiée, comme pour les routes des lots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:19:00 +00:00
Claude ad52da18a9 Lot 34 : bloc projet figé (PROJ_SNAPSHOT) dans le tour de la façade
PROJ_SNAPSHOT était le dernier réglage qui gardait la conversation au
Go : la façade la tient désormais dans tous les cas (LOKI_CHAT=go reste
la porte de sortie). facadeChatBlocker et la décision prise une fois par
processus disparaissent.

Go (facade_projsnap.go) :
- /internal/v1/turn/view, avec la clé et l'agent : tête du tour portant
  le bloc figé (ou le vivant à rafraîchir), préambule sans date ni
  dossier, et la décision projsnap (rafraîchir, mise à jour à poser,
  instantané à ranger, clé et système du tour) ; dry pour le
  préchauffage ;
- /internal/v1/turn/projsnap/refresh : bloc vivant et instantané qu'il
  devient, pour une compaction en plein tour.

Rust :
- ChatService::assemble_turn, partagé par le tour et la relance du
  builder : instantané rangé avec la discussion, mise à jour posée en tête
  du dernier message utilisateur (prependCtxUpdate), historique nettoyé
  au rafraîchissement ;
- TurnHost::project_refresh après une compaction ou une réduction en
  plein tour (replaceProjectHead) ; l'instantané posé devient celui de la
  discussion ;
- le préchauffage demande la vue en dry et garde les mises à jour tant
  que le bloc n'est pas rafraîchi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 03:13:27 +00:00
Claude ff5c6d4571 Lot 33 : préchauffage (PREWARM) tenu par la façade
PREWARM ne garde plus la conversation au Go. La façade tient
l'historique : elle assemble et envoie la requête du prochain tour ; le
Go garde le compteur des requêtes en vol et ce qui l'annule.

Go (facade_prewarm.go) :
- /internal/v1/prewarm/check : moteur libre d'un vrai travail, loin de
  minuit, en bonne santé, slot visé ;
- /internal/v1/prewarm/begin : rechargement SLOT_PERSIST puis inscription
  comme LA requête en vol ; son annulation est relayée à la façade ;
- /internal/v1/prewarm/end : place rendue, cache repris noté ;
- /internal/v1/oai/begin : prewarm_keep pour l'étape qui le prolonge ;
- fin de tâche planifiée : le déclencheur part à la façade.

Rust (chat/prewarm.rs) :
- assemblage par les mêmes fonctions que le tour, sentinelle « . », un
  seul jeton, non streamé ; abstention (capacités inconnues, tour en
  cours, discussion vide, compaction attendue, assemblage inattendu) ;
- déclencheurs : fin de tour, fin de tâche, changement de discussion ou
  de projet (full : après 3 s) ;
- le tour garde le préchauffage qu'il prolonge (empreintes des messages
  et des options de rendu) et coupe tout de suite les autres ;
- télémétrie kind=prewarm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 02:52:44 +00:00
Claude f79b74dac3 Lot 32 : images gardées (KEEP_TURN_IMAGES) dans le tour de la façade
KEEP_TURN_IMAGES ne garde plus la conversation au Go. L'état du tour vit
dans la façade ; les fichiers et la vision restent au Go.

Go (facade_keepimg.go) :
- /internal/v1/turn/keepimg : vision réellement active, budget des images
  gardées ;
- /internal/v1/turn/keepimg/store : une image relayée rangée par
  référence (refRelayImage), relue à l'octet près.

Rust :
- chat/keepimg.rs : port de keepImages (image à l'essai, mesure par le
  moteur répartie au prorata des octets, budget, publication, fin de tour).
- La boucle du tour : budget demandé pour le fil principal, image rangée
  par le Go, historique réécrit publié sans les relais non gardés ; les
  images gardées partent avant tout résumé (filet réactif, en cours de
  tour, fenêtre pleine).
- Début et fin de tour : retirées si le contexte le demande ou si elles
  ne doivent plus partir (clé retirée, vision absente).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 02:30:59 +00:00
Claude a005626e66 Lot 31 : slots du moteur pour le tour tenu par la façade
SIDE_SLOT et SLOT_PERSIST ne gardent plus la conversation au Go. La
machinerie reste au Go (configuration, requêtes en vol, tampon du slot) ;
la façade la consulte avant chaque requête au moteur local.

Go :
- /internal/v1/turn/slot (facade_slots.go) rend engineSlotFor et, pour
  une étape de tour, recharge d'abord l'état gardé de la discussion
  (slotPersistRestore). La sauvegarde à la bascule voit les étapes de la
  façade : /internal/v1/oai/served marque le slot (engineMarkMain).
- facadeChatBlocker : SIDE_SLOT et SLOT_PERSIST retirés.

Rust :
- TurnHost::engine_slot : l'id_slot posé dans chaque requête (0 pour le
  fil de la discussion, 1 pour la vérification), le ticket « en vol »
  ouvert sur le second slot.
- Cache KV plein en plein calcul (sideSlotPoolError) : rejoué deux fois
  tel quel, sans compaction, puis l'erreur.
- Résumés de compaction comptés en vol comme postSummary : transcription
  sur le second slot, continuation sur le slot 0.
- La passe de vérification est un travail annexe (engineSideJob) : le Go
  efface le slot à la fin s'il a servi (écart du lot 7 levé).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 02:13:20 +00:00
Claude 89ed527c96 Fusion de main : conversation Rust par défaut, télémétrie du tour
main a reçu en parallèle la télémétrie du tour tenu par la façade
(/internal/v1/turn/perf), les tickets « en vol » du moteur et la
conversation tenue par la façade par défaut (LOKI_CHAT=go pour revenir
au Go). Le lot 29 faisait la même chose par /internal/v1/perf/record :
un seul chemin est gardé, celui de main.

- TurnHost::perf_record (main) remplace TurnHost::perf ; la nature est
  posée par l'hôte (verify pour la passe isolée seulement, main pour les
  corrections du builder, comme le Go).
- Les résumés de compaction passent par le même point d'entrée, nature
  compact : chat/perf.rs ne garde que le relevé de la réponse entière
  (perfRecFromWire) et l'envoi.
- La sonde du gabarit reste à la façade (lot 23) : /internal/v1/turn/perf
  ne la lance plus côté Go.
- facade_perf.go retiré ; ses cas (perte, intercalé, alerte, preset
  externe, incomplète, GET) rejoignent web_internal_turnperf_test.go.
- README : LOKI_CHAT défaut Rust, avec ce que les lots 19, 23, 29 et 30
  y ajoutent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 01:53:53 +00:00
Claude e444202b4b Lot 30 : motifs de relance personnalisés tenus par la façade
La surcharge retry_patterns (tableau JSON de regex du Go, chacune en
(?mi)) ne garde plus la conversation au Go : la façade la lit au
démarrage de sa conversation, comme le Go la compile une fois par
processus ; illisible ou vide, les motifs embarqués s'appliquent, une
regex illisible est sautée.

Les regex restent celles du Go : chat::stream::go_regex réécrit pour le
crate regex ce qui n'a pas le même sens — classes Perl et limites de mot
ASCII (\w \d \s \b et leurs contraires, dans et hors d'une classe),
\Q…\E (refusé dans une classe, comme le Go), [ && -- ~~ dans une classe,
octal permis, référence arrière refusée.

Parité : parity_retry_test.go compile côté Go une soixantaine de regex
(dont celles qu'il refuse) et note leur première correspondance sur onze
textes (accents, chiffres non ASCII, espaces insécables, crochets) ;
tests/retry.rs rejoue le journal à l'identique et vérifie la lecture de
la surcharge au démarrage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 01:24:16 +00:00
Claude 8a61879bfd Lot 29 : télémétrie des complétions de la façade
Quand la façade tient la conversation (LOKI_CHAT=rust), ses complétions
rejoignent l'anneau de télémétrie du Go (perf_log.go) au lieu de passer
inaperçues : /api/perf/summary les compte, l'optimiseur en tire ses
poids, et l'interface reçoit à nouveau la perte de cache.

- chat/perf.rs : perfRecFromStats, perfRecFromWire (lu sans échec
  possible, mêmes tolérances que perfNum, timings d'un autre type
  présents mais vides comme le Go) et perfPrefix (FNV-1a cumulé) ;
- la boucle du tour range chaque complétion lue, numérotée dans le tour,
  avec ses empreintes si elle est complète ; une complétion coupée par un
  stop est rangée incomplète ; la dernière statistique porte la nature,
  la perte, ce qui s'est intercalé et l'alerte ;
- natures : main (tour), verify (vérification du mode Code), compact
  (résumé de compaction, rangé dès que la réponse se lit) ;
- le Go (/internal/v1/perf/record) range le relevé comme les siens et
  calcule l'alerte au seuil du preset actif, jamais pour un preset
  externe.

Tests : relevés et empreintes (chat/perf.rs), télémétrie de la boucle
(complète, coupée), du vrai hôte et des résumés (tests/lot6.rs),
TestFacadePerf côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 01:17:49 +00:00
Claude f6c191922f Lot 28 : optimiseur tenu par la façade (LOKI_TUNE=rust)
Avec LOKI_TUNE=rust (expérimental, Unix seulement), la façade sert
/api/tune, /api/tune/status, /api/tune/cancel, /api/tune/apply et
/api/tune/last, et déroule l'optimisation sans perte (rust/loki-rs/src/tune) :
vrai moteur arrêté, essais composés à blanc puis lancés par les « loki
serve » du Go (LOKI_TUNE_TRIAL), chacun dans son groupe de processus,
mesurés par le bench complet à profondeur fixe, retenus au-delà du
bruit ; vrai moteur relancé. Rien n'est écrit sans un clic : copie du
preset, ou le preset lui-même — redémarrage, sonde, et ancienne version
rétablie si elle échoue.

- tune/core.rs : la partie pure (EXTRA_ARGS, réglages, essais de chaque
  étape, garde-fous, diff et réécriture du preset, score) ;
- tune/lock.rs : le verrou tune.lock au format du Go, identité des
  processus comme procIdentity (la reprise du Go reconnaît la façade
  vivante, et écarte son verrou si elle meurt), ménage d'un verrou
  périmé (essai orphelin arrêté, application interrompue défaite) ;
- tune/job.rs : routes, job, essais et application.

Le verrou de génération reste celui du Go, pris par le bail du moteur au
motif « tune » (refus de l'optimiseur pour les autres, stop du chat qui
annule). Le Go prête /internal/v1/tune/context (bench, génération, jobs
du mode code, état du moteur, télémétrie) et le démarrage ou l'arrêt du
moteur sans le refus de l'optimiseur (service/action « os »). Hors
Unix, la façade passe LOKI_TUNE vide au Go, qui garde l'optimiseur.

serde_json lit désormais les flottants à l'arrondi exact
(float_roundtrip), comme le Go.

Parité : parity_tune_test.go tire du Go un journal (partie pure, contenu
du verrou, résultat enregistré, vérification après application) rejoué
par tests/tune_core.rs. tests/tune.rs déroule de vraies optimisations, le
binaire de test jouant « loki serve » ; les tests du verrou du Go sont
portés ; TestFacadeTune côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 01:06:31 +00:00
Claude 84ae3a0cc4 Lot 27 : tests contre les mutations survivantes du banc d'essai
84 mutations du port Rust, 81 tuées ; trois équivalentes documentées
dans la spécification (annulation déjà vue au premier tour du select,
progression d'un job remplacé, PID nul du moteur).

- bench/tests.rs : un mini moteur qui garde les corps des requêtes ;
  tranche pile à la fin du corpus, tour sans attendu, débits nuls
  recalculés, niveau rejoué une seule fois, préchauffage et ligne courte
  d'un run rapide, profondeur arrêtée (contexte plein, corpus épuisé,
  réponse reprise, note de reprise hybride ou gabarit) ;
- tests/bench.rs : second lancement pendant la prise du bail, preset
  relu au départ du job, mesure enregistrée sous le preset actif ;
- journal de parité : un prompt juste au-delà de la prose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 00:41:20 +00:00
Claude 8f61870224 Lot 27 : banc d'essai tenu par la façade (LOKI_BENCH=rust)
Avec LOKI_BENCH=rust (expérimental), la façade sert /api/bench,
/api/bench/status, /api/bench/cancel et /api/bench/last, et déroule le
protocole de mesure contre le moteur local (rust/loki-rs/src/bench) :
mode rapide (préchauffage, ligne courte) et mode complet (prefill à froid
à profondeur réelle, trois tours qui reprennent le cache). Corpus,
requêtes, calculs et messages sont ceux du Go ; seule une mesure complète
est enregistrée (last_bench, bench_presets, empreinte de configuration du
Go).

Le verrou de génération reste celui de la conversation du Go : la façade
le prend par le bail du moteur, avec le motif « bench ». Le Go note alors
benching et donne aux autres le refus du benchmark ; changer de
discussion ne rompt pas le bail ; benchRunning() le voit (clients /v1,
préchauffage, optimiseur). Un stop du chat, un Reset ou un bail perdu
annulent le bench. Le Go prête aussi le travail annexe (effacement du
slot à la fin, /internal/v1/engine/sidejob) et la télémétrie
(/internal/v1/bench/perf).

Correction des tests du Go : trois tests du préchauffage échouaient dix
minutes par jour, avant minuit (prewarmNearMidnight lisait l'heure
réelle). L'heure du garde-fou devient remplaçable (prewarmNow), fixée à
midi dans les tests.

Parité : parity_bench_test.go tire du Go un journal rejoué par
tests/bench.rs contre le même faux moteur ; chaque requête reçue par le
moteur est comparée (longs textes réduits à leur taille et à leur
empreinte). Il couvre les refus, les deux modes, les échecs de chaque
phase, le raisonnement refusé puis rejoué, la profondeur partielle et
l'annulation. Les tests du Go sont portés (bench/tests.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-16 00:09:09 +00:00
Claude 9a0455be8f Lot 26 : tâches planifiées tenues par la façade (LOKI_TASKS=rust)
Avec LOKI_TASKS=rust (expérimental), la façade sert les routes
/api/tasks* et fait tourner le planificateur, que le Go ne démarre plus.
Elle exécute elle-même les tâches « script seul » (shell de l'agent,
limite de 10 min, arrêt à la main). Une tâche IA passe par le verrou de
génération et les outils de l'agent, restés au Go : la façade la lui fait
exécuter (/internal/v1/tasks/run), comme son propre planificateur l'aurait
fait. Les notifications partent par la façade si elle les tient, sinon par
le Go (/internal/v1/push/send).

- src/tz.rs : les fuseaux IANA comme le Go (TZif v1/v2 et règle POSIX,
  time.Local, time.Date et ses heures qui sautent ou se répètent) ;
- tasks/cron.rs : les quatre formes de fréquence, avec ParseDuration du Go
  et ses messages d'erreur ; instants sur 128 bits (dates au-delà de
  2262) ;
- tasks/mod.rs : la base, l'exécution, le planificateur (un tic par
  minute, une tâche due par tic) et les sept routes.

Le Go reçoit un crochet de test pour l'heure des tâches (tasksNow).

Correction de la façade : les appels à l'API interne du Go étaient tous
bornés à 10 s. Un outil exécuté par le Go qui dure plus longtemps (page
lente, navigateur, MCP tenu par le Go) était coupé, alors qu'il continuait.
Ces appels sont désormais bornés à 30 min ; une tâche IA est attendue
jusqu'à 24 h.

Parité : parity_tasks_test.go tire du Go un journal rejoué par
tests/tasks.rs, l'heure des tâches fixée pas à pas. Il couvre
l'enregistrement (chaque forme et chaque erreur de fréquence, plusieurs
fuseaux), la bascule, l'interrupteur maître, la suppression, les
lancements et les tics du planificateur. Les mutations (79, dont 77
tuées et 2 équivalentes) ont fait trouver et corriger cinq écarts du
port : dates lointaines, intervalle en jours démesuré, heure qui saute au
printemps, compte-rendu coupé au milieu d'un caractère, noms de fuseau
contenant « .. ».

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 23:30:03 +00:00
Claude ed9b2e8c1c Lot 25 : Strata tenu par la façade (LOKI_STRATA=rust)
Port de backend_strata.go (rust/loki-rs/src/strata) : la machine (cartes
par nvidia-smi, Python 3.10+ et venv, RAM totale et disponible, RAM du
moteur Strata en marche, place libre), ce qui y tient et la
recommandation, la configuration de lancement en clair des modèles
installés (placement des experts drop/ram/arena/disk, carte d'aide,
réglages du preset), les réglages d'un modèle installé et l'installation
(job des moteurs : paquet figé vérifié par SHA-256, installeur lancé et
journalisé comme le sinkWriter du Go, preset écrit puis activé par le Go).

Tenu seulement avec LOKI_BACKENDS=rust ; le lancement (loki serve) reste
au Go, qui relaie les routes du tunnel.

Correction du Go : /api/strata/settings décidait « en service » après
avoir écrit les nouveaux réglages, donc jamais pour un réglage changé ;
décidé désormais avant l'écriture, comme SavePresetApplying. Le port suit.

Parité : parity_strata_test.go (crochets de test : release, meminfo,
place libre, action de service) tire du Go un journal rejoué par
tests/strata.rs ; les tests du Go portés dans strata/tests.rs ;
TestFacadeStrata côté Go. Mutations : 71 tuées sur 71.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 22:43:04 +00:00
Claude 8c9bb6b450 Lot 24 : moteur de l'image tenu par la façade (LOKI_ENGINE_IMAGE=rust)
Port de web_engine.go, web_engine_reco.go et backend_engine_oci.go
(rust/loki-rs/src/engineimg) : statut du moteur (build, source, variante,
versions installées, avis, version recommandée, version précédente,
relevé du rendu), dernier build publié, plan (dernière ou recommandée,
risque de rendu du raisonnement au défaut de b10763), mise à jour depuis
le registre officiel (couches du sommet, liens, runtime ignoré, essai à
blanc, REASONING_PRESERVE=off dans le preset actif ou la configuration,
ménage, vérification du rendu avant/après par la sonde de gabarit),
bascules, suppression et retour à la version précédente.

Tenu seulement avec LOKI_BACKENDS=rust : la mise à jour est un job des
moteurs. La sonde est celle de la conversation quand la façade la tient.
/api/engine/cmdline reste au Go, qui relaie les routes du tunnel.

Parité : parity_engineimg_test.go tire du Go un journal (faux registre
OCI, faux moteur local pour la sonde) rejoué par tests/engineimg.rs ;
TestFacadeMoteurImage côté Go. Les aides de fichiers des rejeux passent
dans tests/common/parity.rs. Mutations : 71 tuées sur 74, trois
équivalentes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 22:15:30 +00:00
Claude 0f3a3d52c1 Lot 23 : sonde de gabarit en Rust (avec LOKI_CHAT=rust)
Port de chat_tplprobe.go : la façade sonde le gabarit du moteur après ses
complétions (/health, /props, /apply-template), avec les verdicts
preserves_history, prefix_stable et renders_reasoning, le cache par
build/modèle/gabarit/forme et la relance bornée.

- l'écho du raisonnement suit le verdict de la sonde (echo_policy_for) ;
- NUDGE_IN_TOOL ne garde plus la conversation au Go : le rappel de budget
  va dans le dernier résultat d'outil quand le préfixe n'est pas stable ;
- le Go lit le verdict de la façade (/internal/facade/v1/chat/tpl) quand
  elle tient la conversation.

Parité : parity_tplprobe_test.go tire du Go un journal d'échanges rejoué
octet pour octet par tests/tplprobe.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 21:34:57 +00:00
Claude 78766a28d9 Tests des backends : resetBackendsState hors du fichier réservé à Linux
TestFacadeBackends tourne sur toutes les plateformes (job macOS de la CI).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 21:08:37 +00:00
Claude 2c926a8274 Lot 22 : backends llama.cpp et job des moteurs tenus par la façade (LOKI_BACKENDS=rust)
La façade (rust/loki-rs/src/backends) sert le statut du moteur llama.cpp
(dépôt git, binaire, moteur de l'image, précompilé, moteur téléchargé,
plan de build et conseil), la vérification git, la dernière release et
l'installation des binaires précompilés (extraction avec liens, VERSION,
ménage), la bascule fast/opt, les backends custom et leur désinstallation,
et les GPU vus par un moteur (caches mémoire et disque).

Elle détient LE job des moteurs (state/build_job + loki-build.log, repris
au démarrage). Les jobs que le Go fait encore tourner (compilation depuis
les sources, moteur de l'image, Strata) lui rapportent chaque étape par
/internal/facade/v1/build-job/* ; le Go garde le garde de l'optimiseur et
ne restaure plus de job quand la façade tient le lot.

Correction du Go : prebuiltVersionFormat lisait le format comme 3e champ
du marqueur VERSION ; hors CUDA Windows (« b300  fmt2 ») il n'y en a que
deux, et chaque vérification retéléchargeait la release au lieu de dire
« déjà à jour ». Le port suit.

Parité : parity_backends_test.go (Linux) → tests/parity_backends, rejoué
par tests/backends.rs (fausses machines dans le PATH, faux GitHub, dépôt
git reproductible) ; TestFacadeBackends pour les étapes transmises.
Mutations : 55 tuées sur 57, deux inobservables sous Linux (cudart
Windows, garde d'un avertissement).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 20:58:28 +00:00
Claude 25e31c1c26 Lot 21 : mise à jour de Loki tenue par la façade (LOKI_UPDATE=rust)
En conteneur, l'image se met à jour par Docker : /api/update rend la
version du Go sans mise à jour disponible et la note Docker,
/api/update/apply la refuse (409). La façade sert ces deux routes quand
LOKI_UPDATE=rust et LOKI_CONTAINER=1 (sysupdate.rs) ; hors conteneur,
loki-rs ne passe pas la variable et le Go garde le remplacement du
binaire.

Parité : parity_update_test.go tire du Go les quatre réponses en
conteneur, rejouées par tests/sysupdate.rs ; TestFacadeMiseAJour côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 20:01:37 +00:00
Claude 090f15615e Lot 20 : serveurs MCP tenus par la façade (LOKI_MCP=rust)
La façade tient les serveurs MCP quand LOKI_MCP=rust (expérimental ; le
Go reste le défaut) : configuration (state/mcp), catalogue embarqué (le
même fichier que le Go, copié dans l'étape Rust de l'image), état et
routes /api/mcp*. Un client MCP en Rust (mcp/client.rs) parle JSON-RPC
sur stdio et en Streamable HTTP (réponses JSON ou SSE, session suivie,
en-têtes configurés, ping du serveur servi). Le pool suit mcp_client.go :
session gardée entre les tours, connexions parallèles et partagées,
session invalidée pendant sa connexion jamais republiée, délais npx/uvx,
appel borné avec une reconnexion, sortie plafonnée à la fenêtre.

Un seul pool, celui de la façade : le Go, qui exécute encore les outils
de l'agent, lui demande les outils MCP, leurs appels et le
pré-chauffage (/internal/facade/v1/mcp/*, facade_mcp.go).

Parité : parity_mcp_test.go tire du Go les fonctions pures, seize
mutations de configuration, et un vrai serveur du SDK MCP Go (onze
outils) : ce qu'il met sur le fil et ce que le client Go en tire (état,
outils annoncés, appels). tests/mcp.rs le rejoue contre un faux serveur
qui resert ces réponses, et vérifie stdio, reconnexion, routes, catalogue
et session invalidée de bout en bout ; TestFacadeMCP côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 19:56:11 +00:00
Claude 3fc36a3acc Lot 19 : export, mode Code et critères tenus par la façade (avec LOKI_CHAT=rust)
Quand la façade tient la conversation, /api/chat/export, /api/chat/mode
et /api/chat/criteria rejoignent ses routes (chatx.rs, facadeChatRoutes
côté Go), sans nouvelle variable. L'export Markdown ou JSON part de la
conversation vivante (le Go relisait la copie enregistrée, en retard d'un
tour au besoin) : raisonnement replié, outils dans leur état final,
compaction signalée, export partiel annoncé, JSON indenté et HTML
échappé, sans mise à jour de contexte ni image en base64. Le mode et les
critères (scellés, vingt au plus, statuts ramenés) vont sur le fil de la
façade. Le reste du mode Code (indice, travaux de fond, outil criteria)
reste au Go et lit les mêmes clés.

Parité : parity_chatx_test.go tire du Go l'export d'une conversation
riche sous 22 jeux d'options et 23 appels au mode et aux critères (base
et fil après chacun, erreurs JSON imbriquées comprises), rejoués octet
pour octet par tests/chatx.rs ; tests du relais mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 19:26:27 +00:00
Claude ec55f7d3d6 Lot 18 : hub des projets tenu par la façade (LOKI_PROJECTS=rust)
La façade sert /api/projects quand LOKI_PROJECTS=rust (expérimental ; le
Go reste le défaut) : liste avec discussions, pages et trackers comptés,
création (slug translittéré et unique, index amorcé), renommage,
description, suppression (discussions, trackers et mémoire du projet,
actif réassigné, dernier projet indestructible) et bascule, refusées
pendant une génération.

Le projet actif passe toujours par le Go, qui en garde une copie en RAM.
Les discussions sont à qui tient la conversation : la façade bascule et
supprime elle-même quand elle la tient, sinon le Go garde
/internal/v1/project/switch et /internal/v1/project/drop-convs
(facade_projects.go).

Parité : parity_projects_test.go tire du Go 40 appels sur une base
amorcée (pages, discussions, trackers piégés), avec le registre, l'actif,
memory/, les trackers et l'index après chacun, rejoués par
tests/projects_api.rs contre un faux Go qui bascule et supprime comme le
vrai ; bascule et suppression par la façade de bout en bout ;
TestFacadeProjets côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 19:10:49 +00:00
MichaelandClaude Opus 5.5 4c8ac93581 Lot 6, étape 6 : la conversation tenue par la façade Rust par défaut
Sans LOKI_CHAT, c'est désormais la façade qui tient la conversation ;
LOKI_CHAT=go la rend au Go (porte de sortie, aussi listée dans le compose
Unraid). Le Go la garde de toute façon si un réglage que la façade ne
sait pas encore honorer est posé (PROJ_SNAPSHOT, KEEP_TURN_IMAGES,
NUDGE_IN_TOOL, SIDE_SLOT, SLOT_PERSIST, PREWARM, motifs retry_patterns),
décidé au démarrage comme avant.

Vérifié de bout en bout avec les vrais binaires et un faux moteur, dans
les deux modes : deux tours (texte, puis appel d'outil), même état, même
journal rejoué, discussion intacte après relance, et un panneau perf
identique (complétions, tours, pertes, sonde du gabarit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:06:27 +02:00
MichaelandClaude Opus 5.5 5edcad50a5 Conversation tenue par Rust : télémétrie et requêtes en vol comme le Go
Trois choses manquaient au tour tenu par la façade, actives même sans
clé opt-in, et qu'il fallait avant d'en faire le défaut :

- la télémétrie de chaque complétion (perfRecord) : /api/perf/summary et
  l'alerte « cache perdu » posée sur les stats finales d'une étape. La
  boucle Rust la remet au Go (/internal/v1/turn/perf) au même moment que
  la boucle Go, avec ses empreintes de messages ; le Go range, calcule la
  perte et rend nature, jetons perdus et alerte, posés sur la copie finale
  des stats ;
- la sonde du gabarit, lancée dans les mêmes conditions (requête de forme
  normale du moteur local) ;
- le comptage des requêtes en vol : chaque requête au moteur local ouvre
  un ticket (/internal/v1/oai/begin), marqué servi à la réponse 200
  (engineServed, engineMarkMain pour une étape de la discussion), rendu en
  fin de lecture du flux. Le slot de la discussion n'est ainsi ni effacé
  pendant une requête ni pris pour un autre.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:04:05 +02:00
Claude 2db4cf1090 Parité moteur et système : tests réservés à Linux
parity_engine_test.go appelle lastErrorLine (sys_service_rust_linux.go)
et suppose la supervision par fichier PID du conteneur ; la façade ne
tourne qu'en conteneur Linux. La CI macOS ne compilait plus les tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 18:53:36 +00:00
Claude a4f8bd3ec4 Lot 17 : interrupteurs système tenus par la façade (LOKI_SYSTEM=rust)
La façade sert /api/internet (moteur web, serveur Crawl4AI et sa clé
jamais rendue, joignabilité avec son cache de 30 s), /api/computer
(interrupteur du navigateur, binaire trouvé, vision), /api/apikey (clé de
complétion, repli sur config.env, adresse /v1 à coller dans un client
tiers) et /api/network (écoute du moteur) quand LOKI_SYSTEM=rust
(expérimental ; le Go reste le défaut).

Restent au Go derrière son API interne (facade_system.go) : le cache de
joignabilité de ses outils web (/internal/v1/web/reach/reset), le
Chromium des outils browser_* (/internal/v1/cdp/shutdown) et le
redémarrage qui applique une clé neuve quand la façade ne pilote pas le
moteur (/internal/v1/service/action). Le relais du tunnel garde l'hôte
et X-Forwarded-Proto du client.

Correction en passant : maskAPIKey paniquait sur une clé d'un octet, et
/api/apikey tombait à chaque lecture.

Parité : parity_system_test.go tire du Go 61 appels de route avec la
base et les sondes reçues par trois faux Crawl4AI après chacun, et huit
dispositions du dossier de Playwright, rejoués par tests/system.rs ; ce
qui reste au Go vérifié de bout en bout ; TestFacadeSysteme,
TestFacadeRelaisGardeHote et TestMaskAPIKeyCourte côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 18:51:58 +00:00
Claude 2da2efc1fd Lot 16 : pilotage du moteur tenu par la façade (LOKI_ENGINE=rust)
La façade sert /api/start, /api/stop, /api/restart, /api/vram/unload et
/api/vram/reload quand LOKI_ENGINE=rust (expérimental ; le Go reste le
défaut). Elle lance elle-même « loki-rs engine <action> », le superviseur
que le Go appelait déjà en conteneur, avec l'environnement qu'il lui
passait et de quoi joindre l'API interne du Go ; sa dernière ligne
d'erreur devient l'erreur rendue. Le contrôle d'avant démarrage
(preflightEngine : preset externe, Strata, BIN, MODEL, tranches) et le
bilan de VRAM du déchargement (gpuSettle) sont portés.

La façade ne tient ce lot qu'en conteneur : sous systemd ou avec
LOKI_ENGINE_SUPERVISOR=go, loki-rs ne passe pas la variable au Go.

Restent au Go derrière son API interne (facade_engine.go) : le verrou de
l'optimiseur avant tout démarrage (/internal/v1/service/prestart), la
réponse en cours quand il tient la conversation
(/internal/v1/chat/generating) et l'arrêt de la dictée quand il la tient
(/internal/v1/dictate/stop).

Parité : parity_engine_test.go tire du Go dix bilans de VRAM, vingt-six
installations passées au préflight, les messages du superviseur et dix
réponses de route, rejoués par tests/enginectl.rs ; démarrage, arrêt,
redémarrage, déchargement chiffré et rechargement vérifiés de bout en bout
avec un faux superviseur et un faux nvidia-smi ; TestFacadeMoteur côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 18:33:31 +00:00
Claude a50b8f3cc6 Lot 15 : presets tenus par la façade (LOKI_PRESETS=rust)
La façade sert la liste des presets (pastilles, mesures du bench quand
elles correspondent à la configuration), leur ordre, l'éditeur (lecture,
graine d'un preset neuf, enregistrement avec nom de fichier unique,
prompt système), la suppression (refusée pour l'actif, modèle compris sur
demande), les aperçus de l'éditeur (cache de prompts, second slot, avis
MoE et de chargement, réutilisant le port de loki serve), la copie en
placement auto, les presets externes (la clé n'est jamais rendue, test
par un vrai appel de complétion) et la bascule, quand LOKI_PRESETS=rust
(expérimental ; le Go reste le défaut).

Ce qui touche au moteur reste au Go jusqu'à son lot : facade_presets.go
expose /internal/v1/preset/apply (bascule avec état du slot gardé et
moteur relancé ou arrêté, preset en service réappliqué) et
/internal/v1/tune/guard (verrou de l'optimiseur), et remet les routes du
tunnel à la façade.

Parité : parity_presets_test.go tire du Go les fonctions pures et 70
appels de route avec presets/, models/ et la base après chacun, rejoués
par tests/presets_api.rs ; bascule et application vérifiées de bout en
bout contre le faux Go, presetApply côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 18:13:13 +00:00
Claude 13bcbc74fe Lot 14 : ligne de commande du moteur composée en Rust (LOKI_SERVE=rust)
loki-rs engine serve fait ce que faisait loki serve : lire la
configuration (par l'API interne du Go), sonder le monde (aide du moteur,
--version, --list-devices, GGUF, RAM, VRAM, cgroup, cpuset, variables
LLAMA_ARG_*/GGML_*), composer la ligne de llama-server, vérifier le port,
garder la commande pour l'interface, puis exec. Le superviseur le lance à
la place de loki serve quand LOKI_SERVE=rust (expérimental ; le Go reste
le défaut).

serve/ porte backend_serve*.go fonction par fonction (couches GPU, --fit
et sa marge, threads, files CUDA, second slot, mode tensor, cache de
prompts, points de reprise, décodage spéculatif, fidélité, LOAD_GUARD,
SLOT_PERSIST), toute la décision restant pure. Strata, l'essai de
l'optimiseur et un lancement sans API interne passent toujours par
loki serve (Go). La façade donne au Go l'adresse de son API interne
(LOKI_GO_ADDR), héritée par le superviseur.

Parité : parity_serve_test.go tire du Go 1 872 lancements (12 machines ×
112 presets × 3 aides de moteur × 13 jeux de variables) et les sondes
pures, rejoués argument par argument par tests/serve.rs ; deux essais de
bout en bout lancent le vrai binaire contre un faux Go et un faux moteur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 17:58:24 +00:00
Claude 1e5f0904f2 Lot 13 : mémoire tenue par la façade (LOKI_MEM=rust)
La façade sert l'éditeur des pages mémoire (lecture, enregistrement,
renommage, suppression, déplacement vers un autre projet, index MEMORY.md
tenu par le code), le panneau des trackers, le mode mémoire du projet
actif et les interrupteurs du mode agent, quand LOKI_MEM=rust
(expérimental ; le Go reste le défaut).

Les pages restent des fichiers memory/<projet>/*.md écrits de façon sûre
(.bak, temporaire synchronisé puis renommé, relecture vérifiée), les
trackers des valeurs du bucket tracker. Le chiffrement reste au Go : la
façade fait ouvrir et sceller ses contenus par /internal/v1/crypt/*, et
n'écrit jamais en clair quand la mémoire est verrouillée.

gojson.rs reproduit le décodage d'encoding/json (première valeur, EOF,
erreurs du scanner et de type, structs anonymes) pour renvoyer au client
les mêmes messages que le Go ; il servira aux lots suivants.

Côté Go : facade_mem.go remet ces routes à la façade quand elles arrivent
par le tunnel ; coffre, instantanés, sauvegarde et projets restent au Go.

Parité : parity_gojson_test.go (81 corps) → tests/gojson.rs ;
parity_mem_test.go (noms, titres, slugs, modes, 36 dates, 102 appels de
route avec le disque et la base après chacun) → tests/memory.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 17:33:22 +00:00
Claude db9b0b44f5 Clippy 1.99 : as_chunks au lieu de chunks_exact(4) pour la vignette
Le clippy de la CI (Rust 1.99) refuse chunks_exact à taille constante
(chunks_exact_to_as_chunks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 17:05:47 +00:00
Claude eddcf97e3b Lot 12 : notifications Web Push tenues par la façade (LOKI_PUSH=rust)
La façade sert la clé VAPID et les (dés)abonnements, et pousse elle-même
les notifications « réponse prête » quand LOKI_PUSH=rust (expérimental ;
le Go reste le défaut) : chiffrement RFC 8291 (aes128gcm, enregistrement
complété à 4096 octets comme webpush-go), signature VAPID RFC 8292 (JWT
ES256), abonnements purgés sur 404/410, par ring déjà présent via rustls.
Mêmes clés en base que le Go.

Côté Go : facade_push.go remet les routes du tunnel à la façade, et
sendPushToAll (fin de tour, fin de tâche) lui délègue l'envoi.

Parité : parity_push_test.go tire du Go les durées et onze appels de route
avec la liste des abonnements en base, rejoués par tests/push.rs ; le
chiffrement reproduit l'exemple de la RFC 8291 à l'octet près, et un envoi
de bout en bout est déchiffré côté navigateur.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 17:01:07 +00:00
Claude cbd0d5e356 Tests : scripts d'essai écrits par un sh à part (plus d'ETXTBSY)
tests/engine.rs (redemarre) a échoué en CI sur « Text file busy » : un
test écrivait son faux binaire pendant qu'un test voisin, dans un autre
thread du même process, lançait un enfant — qui héritait du descripteur
d'écriture le temps de son exec, et l'exec du script échouait. Les faux
binaires (moteur, serveur de dictée) sont désormais écrits par un sh à
part : le process des tests ne tient jamais le fichier ouvert en
écriture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
2026-10-15 16:50:25 +00:00