Architecture cible · référence

La chaîne Notula–Broulala

Ce qui appartient à qui, ce qui franchit la frontière entre les deux composants, et la trame complète des appels — d'un enregistrement déposé jusqu'à une carte de révision, sur un cours de deux phrases. Les deux tournent sur le même serveur.

Notula — l'application, ses données, son stockage Broulala — la plateforme, ses moteurs, ses machines
01

Vue d'ensemble

Deux systèmes, deux propriétaires, et une seule chose qui les relie : des appels HTTP sur l'internet public. Notula garde l'audio, la base et l'identité de ses utilisatrices ; Broulala garde les moteurs et les machines.

MÊME SERVEUR · MÊME EXPLOITANT · APPELS LOCAUX NOTULA BROULALA Navigateur · PWA React — l’interface seule est en cache Serveur Notula API Fastify + worker de jobs PostgreSQL passages, notes, cartes Stockage audio, chiffré au repos dépôt tus · lecture écrit chiffre Stockage audio reçu, supprimé après « done » API /v1 jobs, templates, capacité File + moteurs whisper · modèle de langue Superviseur plafond d’allumage, repli, EEE met en file lit l’audio lève et couche Fournisseur de calcul · pods à la minute PATCH tus · audio déchiffré (réseau local) POST /v1/jobs · GET · DELETE segments · markdown · json · capacité
Une frontière de composant, pas une frontière juridique : les deux moitiés tournent sur la même machine et appartiennent au même exploitant, donc Broulala n'est pas un sous-traitant — on ne se sous-traite pas à soi-même. Ce qui compte reste la discipline de ce qui traverse : une seule flèche sort de Notula avec du contenu, l'audio, le temps d'une suite de PATCH. Le seul vrai tiers de tout le schéma est en bas à droite, hors des deux zones — et il l'est dès que le superviseur loue une machine.
02

Ce qui traverse, ce qui ne traverse jamais

Broulala tourne sur le même serveur et appartient au même exploitant : il n'y a donc pas de tiers ici, et rien à déclarer. La discipline reste entière pour une autre raison — elle est ce qui garde la frontière franchissable dans l'autre sens. Le jour où le moteur déménage, ou devient un service, rien n'est à défaire. Et la colonne de droite tient debout toute seule : les notes personnelles ne partent pas parce qu'un modèle les reformulerait, pas parce qu'un tiers les lirait.

Ce qui traverse

  • L'enregistrement, déchiffré, une seule fois, supprimé chez Broulala dès la transcription réussie.
  • Le texte du cours — verbatim, ou corrigé quand elle a corrigé.
  • Les identifiants de passage de Notula, pour qu'une citation revienne ancrée.
  • Les surlignages, pour la fiche de révision uniquement.
  • Le domaine d'étude et le titre du cours, comme données de contexte.
  • Une liste de mots de vocabulaire, donnée au moteur de transcription.

Ce qui ne traverse jamais

  • Les notes personnelles. Ce sont ses mots ; un modèle à qui on les donne les reformule et les rend signées « proposition de Notula ».
  • L'identité — nom, adresse, identifiant de compte. Broulala ne connaît que l'intégration qui l'appelle.
  • Les sessions, mots de passe et la clé maîtresse de chiffrement.
  • Le rangement — UE, semestre, mots-clés, échéance de conservation, attestation d'accord.
  • L'historique de correction. Seul le texte final part ; qui a corrigé quoi, et quand, reste chez Notula.
03

La vie d'un identifiant

C'est le mécanisme le moins évident de toute la chaîne, et celui dont dépendent les cartes, la recherche au bon minutage et les notes ancrées. Broulala rend des segments courts avec ses propres identifiants ; Notula les jette, recoupe en passages de vingt à soixante secondes et frappe les siens. Ce sont ceux-là qu'elle renverra, et ceux-là que les citations lui rendront.

BROULALA seg_0001 seg_0002 seg_0003 trois segments de quelques secondes transcrit NOTULA seg_0001…3 0199f2c1-…c40 un passage, son identifiant à elle recoupe et persiste NOTULA input.type = “transcript” id: 0199f2c1-…c40 le texte corrigé, avec ses identifiants renvoie pour générer BROULALA segment_id: “0199f2c1-…c40” start: 104 end: 213 la sortie cite l’identifiant de Notula ancre la sortie Aucune table de correspondance, d’aucun côté : chacun ne connaît que les identifiants qu’on lui a tendus.
Broulala n'invente jamais d'identifiant sur le contenu d'un client, et n'en mémorise aucun. C'est ce qui permet à Notula de recoller une carte sur son propre passage sans rien tenir en phase entre les deux systèmes.
04

La chaîne complète, sur deux phrases

Le cours d'exemple s'appelle « Le développement tonique », il appartient à l'UE 3.2, et il ne contient que ceci :

« Le tonus de fond est une contraction musculaire involontaire et permanente, qui persiste même au repos. Pour Wallon, le dialogue tonique est la première forme de communication entre le nourrisson et son entourage. »
01

Le dépôt

Navigateur→Notula

L'enregistrement monte par tus, en morceaux reprenables. À plusieurs parties, Notula les joint en copie de flux et vérifie le type réel sur les octets. L'enregistrement joint est chiffré et stocké : c'est lui qui sera servi au lecteur, et Broulala ne le rendra jamais.

02

Demander une URL d'envoi

Notula→Broulala
Requête
POST /v1/upload-urls
Authorization: Bearer blr_live_7f2a…
Content-Type: application/json

Tus-Resumable: 1.0.0
Upload-Length: 184213765
Upload-Metadata: content_type YXVkaW8veC1tNGE=
Pas de nom de fichier. Personne ne le consommait, et c'est de la donnée personnelle bavarde — « CR consultation Dupont 12-09.m4a » — dans une API qui promet de ne mettre aucun contenu dans ses journaux. Upload-Length est requis : il permet de refuser hors plafond avant que quelqu'un passe une demi-heure à envoyer. content_type est facultatif et n'est jamais cru — le type se décide sur les octets, à la fin.
Réponse · 201, sans corps
Tus-Resumable: 1.0.0
Location: http://broulala-api:3000/v1/uploads/up_8f3a1c2e
Upload-Expires: Fri, 18 Sep 2026 14:30:00 GMT
03

Envoyer l'audio

Notula→Stockage Broulala
Requête
PATCH http://broulala-api:3000/v1/uploads/up_8f3a1c2e
Authorization: Bearer <api_key>
Tus-Resumable: 1.0.0
Upload-Offset: 0
Content-Type: application/offset+octet-stream

<4 Mio d'octets bruts — répété jusqu'à 184213765>

Notula déchiffre à la volée pour cet envoi, et rien d'autre. Un tronçon à la fois : le protocole accepte un PATCH unique, mais un corps de 150 Mo est refusé par le reverse-proxy devant la plateforme, et quatre mébioctets se transfèrent en quelques secondes — assez pour qu'un délai d'inactivité de cent secondes ne devienne jamais un sujet. Un désaccord de décalage se refuse en 409 au lieu de se rattraper : c'est ce qui rend la reprise sûre plutôt qu'optimiste. Du clair parce que l'adresse est injoignable depuis l'internet — c'est l'adresse qui décide, pas le schéma, et monter un certificat pour parler au conteneur d'à côté n'achèterait rien. Les deux copies restent chiffrées au repos : même disque veut dire deux copies, et un disque volé les prend toutes les deux. Le content_type déclaré n'est pas cru : Broulala vérifie les octets, faute de quoi un fichier mal étiqueté donnerait une transcription vide plutôt qu'une erreur.

04

Créer le job de transcription

Notula→Broulala
Requête
POST /v1/jobs
Authorization: Bearer blr_live_7f2a…
Idempotency-Key: 0199f2b8-11c4-7a90-8e3d-4f7c2a1b9d55
Content-Type: application/json

{
  "input": { "type": "media", "media": ["up_8f3a1c2e"] },
  "language": "fr",
  "transcription": {
    "vocabulary": ["Wallon", "Ajuriaguerra", "tonus de fond", "dialogue tonique"]
  },
  "service": { "on_degraded": "proceed" }
}
Réponse · 201
{
  "job_id": "job_5b7e9d21",
  "status": "queued",
  "created_at": "2026-09-08T14:16:02Z"
}
Pas de template_id : Notula veut les segments et rien d'autre. L'Idempotency-Key est l'identifiant du job local de Notula — un worker qui redémarre entre l'appel et la persistance du job_id retrouve le même job au lieu d'en créer un second. on_degraded: "proceed" : le chemin lent rend le même transcript, attendre ne gagnerait rien — quand il existe. C'est un conteneur du compose de la plateforme, qu'un exploitant lève ou couche ; couché, la capacité annonce unavailable et le job attend la carte louée, borné par max_wait_seconds. Ce qu'il faut lire pour savoir lequel des deux est vrai est serves, sur GET /v1/capacity.
Le vocabulary ci-dessus n'a fonctionné qu'à partir du 20/09/2026, et il est au contrat depuis la version 1.0. Le service de transcription passait initial_prompt à un pipeline qui ne le prend pas : tout appel qui en portait un répondait 500. Le premier vrai cours de Notula est le premier à en avoir porté un, et il a été abandonné après quatre tentatives sur une carte louée à 0 % de GPU. La correction est dans l'image de la plateforme, et npm run test:pod-image la vérifie désormais — sur processeur, avant de louer quoi que ce soit.
05

Suivre le job

Notula→Broulala
Requête · toutes les 10 s, puis 20, 30, 60
GET /v1/jobs/job_5b7e9d21
Authorization: Bearer blr_live_7f2a…
Réponse · 200, en cours
{
  "job_id": "job_5b7e9d21",
  "status": "processing",
  "stage": "transcribing",
  "created_at": "2026-09-08T14:16:02Z",
  "started_at": "2026-09-08T14:16:40Z",
  "updated_at": "2026-09-08T14:17:30Z",
  "estimated_completion_at": "2026-09-08T14:23:00Z",
  "service": { "mode": "full", "waited_seconds": 38 },
  "result": null
}

estimated_completion_at peut valoir null, et le vaut par défaut : aucune approximation n'est fabriquée pour faire joli. Notula affiche sa propre estimation, calculée sur son historique.

Cette boucle a une alternative, et Notula ne la prend pas — voir la section 05. Elle existe pour un client qui n'a pas de processus durable.
06

La transcription revient

Broulala→Notula
Réponse · 200, terminé
{
  "job_id": "job_5b7e9d21",
  "status": "done",
  "stage": null,
  "created_at": "2026-09-08T14:16:02Z",
  "started_at": "2026-09-08T14:16:40Z",
  "updated_at": "2026-09-08T14:22:51Z",
  "estimated_completion_at": null,
  "service": { "mode": "full", "waited_seconds": 38 },
  "result": {
    "transcript": {
      "language": "fr",
      "duration_ms": 13400,
      "segments": [
        {
          "id": "seg_0001",
          "start_ms": 0,
          "end_ms": 4120,
          "text": "Le tonus de fond est une contraction musculaire involontaire et permanente,"
        },
        {
          "id": "seg_0002",
          "start_ms": 4120,
          "end_ms": 7980,
          "text": "qui persiste même au repos."
        },
        {
          "id": "seg_0003",
          "start_ms": 7980,
          "end_ms": 13400,
          "text": "Pour Valon, le dialogue tonique est la première forme de communication entre le nourrisson et son entourage."
        }
      ]
    },
    "output": null,
    "engine": {
      "transcription_model": "whisperx-large-v3",
      "generation_model": null,
      "mode": "full"
    },
    "warnings": []
  }
}
« Valon » n'est pas une faute d'exemple. C'est ce que le moteur entend, et c'est exactement la classe de mot pour laquelle le cours a été enregistré — un nom d'auteur. La suite de la chaîne existe en partie pour ça.
07

Notula recoupe et persiste

Notula

Les trois segments courts deviennent un passage de vingt à soixante secondes — un découpage qui porte sur la façon dont le texte se lit, pas sur la façon dont un moteur découpe sa sortie. Les identifiants de Broulala sont jetés.

Ligne écrite en base
TranscriptSegment {
  id:          "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
  courseId:    "0199f2b0-…",
  index:       0,
  startMs:     0,
  endMs:       13400,
  textMachine: "Le tonus de fond est une contraction musculaire involontaire
                et permanente, qui persiste même au repos. Pour Valon, le
                dialogue tonique est la première forme de communication
                entre le nourrisson et son entourage.",
  textEdited:  null
}

Transcription {
  provider:             "broulala",
  model:                "whisperx-large-v3",
  parameters:           { "job_id": "job_5b7e9d21", "vocabulary_count": 4 },
  degraded:             false,
  leavesInfrastructure: false
}
leavesInfrastructure vaut false : le moteur tourne sur la machine. Il est écrit sur la ligne et non déduit à la lecture, parce que le jour où l'installation changerait de côté, une vieille transcription doit continuer de dire ce qui était vrai quand elle a été faite. Le job_id est conservé : c'est lui qui permettra l'effacement à l'étape 13.
08

Elle relit, corrige, surligne

Navigateur→Notula

Elle sélectionne « Valon » et corrige en « Wallon ». Le verbatim machine n'est jamais écrasé : la correction s'écrit à côté.

Après correction
textMachine: "… Pour Valon, le dialogue tonique est …"   ← intact, pour toujours
textEdited:  "… Pour Wallon, le dialogue tonique est …"  ← ce qui s'affiche
editedAt:    "2026-09-09T21:04:17Z"

Elle surligne aussi la seconde phrase. Ce surlignage partira vers Broulala à l'étape 9 ; une note qu'elle aurait écrite, jamais.

09

Demander la fiche de révision

Notula→Broulala
Requête
POST /v1/jobs
Authorization: Bearer blr_live_7f2a…
Idempotency-Key: 0199f3d2-6b81-7e02-a4c9-118d5e0a7b31
Content-Type: application/json

{
  "input": {
    "type": "transcript",
    "segments": [
      {
        "id": "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
        "start_ms": 0,
        "end_ms": 13400,
        "text": "Le tonus de fond est une contraction musculaire involontaire et permanente, qui persiste même au repos. Pour Wallon, le dialogue tonique est la première forme de communication entre le nourrisson et son entourage."
      }
    ]
  },
  "language": "fr",
  "template_id": "tpl_notula_fiche_revision",
  "template_version": 4,
  "context": {
    "study_field": "la psychomotricité",
    "course_title": "Le développement tonique",
    "highlights": [
      "le dialogue tonique est la première forme de communication"
    ]
  },
  "service": { "on_degraded": "wait", "max_wait_seconds": 21600 }
}
Le texte envoyé est textEdited ?? textMachine : « Wallon » et non « Valon ». C'est tout l'intérêt d'une entrée transcript — sans elle, le travail de relecture serait invisible au modèle. on_degraded: "wait", cette fois : un résumeur dégradé ne rend pas la même chose plus lentement, il rend une chose moindre. study_field est un groupe nominal, pas une phrase : il ne peut rien ordonner au modèle.
Réponse · 200, terminé
{
  "job_id": "job_6c0f4a83",
  "status": "done",
  "stage": null,
  "service": { "mode": "full", "waited_seconds": 94 },
  "result": {
    "transcript": null,
    "output": {
      "template_id": "tpl_notula_fiche_revision",
      "template_version": 4,
      "kind": "markdown",
      "markdown": "**Le tonus de fond**\n\nC'est une contraction musculaire involontaire et permanente. Elle ne cesse pas au repos : le muscle garde une tension de base en dehors de tout mouvement volontaire.\n\n**Le dialogue tonique**\n\nWallon le décrit comme la première forme de communication du nourrisson, antérieure au langage. L'enfant exprime son état par son tonus, et son entourage lui répond par le sien."
    },
    "engine": {
      "transcription_model": null,
      "generation_model": "qwen2.5-14b-instruct-q5_k_m",
      "mode": "full"
    },
    "warnings": []
  }
}

transcript vaut null : l'entrée était déjà du texte, il n'y avait rien à transcrire. Le Markdown n'emploie que la grammaire restreinte que Notula sait rendre — gras, puces, citation. Un # s'afficherait littéralement.

10

Demander des cartes à trou

Notula→Broulala

Même entrée, autre template. Pas de highlights : une priorité de lecture ne dit pas quel mot masquer.

Requête · extrait
POST /v1/jobs
Idempotency-Key: 0199f3d9-02aa-7b55-9c01-7e2b4d8f6a13

{
  "input": { "type": "transcript", "segments": [ … identique à l'étape 09 … ] },
  "template_id": "tpl_notula_cartes_trou",
  "template_version": 2,
  "context": {
    "study_field": "la psychomotricité",
    "course_title": "Le développement tonique"
  },
  "service": { "on_degraded": "wait", "max_wait_seconds": 21600 }
}
Réponse · 200, terminé
{
  "job_id": "job_71b2e5d0",
  "status": "done",
  "service": { "mode": "full", "waited_seconds": 0 },
  "result": {
    "transcript": null,
    "output": {
      "template_id": "tpl_notula_cartes_trou",
      "template_version": 4,
      "kind": "json",
      "data": {
        "cards": [
          {
            "sentence": {
              "segment_id": "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
              "text": "La contraction du muscle lisse est involontaire et continue.",
              "start_offset": 0,
              "end_offset": 60
            },
            "answer": {
              "segment_id": "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
              "text": "involontaire",
              "start_offset": 35,
              "end_offset": 47
            }
          },
          {
            "sentence": {
              "segment_id": "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
              "text": "Wallon distingue le tonus de fond et le tonus d'action.",
              "start_offset": 104,
              "end_offset": 159
            },
            "answer": {
              "segment_id": "0199f2c1-4a3e-7c81-9b22-6d1f0a5e8c40",
              "text": "Wallon",
              "start_offset": 104,
              "end_offset": 110
            }
          }
        ]
      }
    },
    "engine": {
      "transcription_model": null,
      "generation_model": "qwen2.5-14b-instruct-q5_k_m",
      "mode": "full"
    },
    "warnings": []
  }
}
Le moteur répète, la plateforme compte. sentence et answer sont deux segment_ref : le moteur y écrit l'identifiant du passage et le texte repris mot pour mot, et rien d'autre. Les offsets sont calculés par la plateforme, qui localise ce texte dans le passage — un passage inconnu, ou un texte qu'il ne contient pas, ne peuvent pas atteindre done. La position du trou dans la phrase est alors une soustraction : answer.start_offset − sentence.start_offset.

C'est la décision B5, du 15/09/2026, et elle vient d'une mesure : un modèle ne sait pas compter des caractères. À qui on demandait les indices, il rendait la bonne longueur à la mauvaise position — des cartes conformes au schéma qui masquaient quatre caractères de bruit.
11

Notula vérifie avant d'accepter

Notula

Depuis la décision B5, l'essentiel du contrôle est fait avant Notula. La plateforme exige que answer.text se trouve dans le passage qu'il cite : une carte qui reformule au lieu de citer fait échouer le job en OUTPUT_INVALID et n'arrive jamais ici. C'est plus fort qu'une somme de contrôle, parce que ça ne jette pas une carte en silence — ça refuse la sortie.

Ce qu'il reste à Notula de vérifier
// La plateforme garantit : answer.text est dans le PASSAGE.
// Elle ne garantit pas : answer.text est dans la PHRASE citée.
// Un terme repris ailleurs dans le même passage passerait.

card.sentence.text.includes(card.answer.text)
   ✓   la carte est gardée
   ✗   la carte est jetée, sans bruit

// Et la position du trou, que plus personne n'a besoin de deviner :
debut = card.answer.start_offset - card.sentence.start_offset
fin   = debut + [...card.answer.text].length

Mesuré le 15/09/2026 : sur un cours réel, une carte masquait « Aristote » dans une phrase parlant d'anesthésie. La plateforme l'a refusée. L'ancienne forme l'aurait laissée passer, offsets conformes et tout.

12

L'état de la plateforme

Notula→Broulala

Un canal à part, interrogé une fois par minute et décompté d'aucun quota. C'est ce qui remplace toute la supervision que Notula portait elle-même.

Requête et réponse
GET /v1/capacity

{
  "mode": "degraded",
  "mode_until": "2026-09-09T15:04:00Z",
  "degraded_since": "2026-09-09T13:58:00Z",
  "queue_depth": 3,
  "observed_at": "2026-09-09T14:32:11Z"
}
mode_until est un bail, pas un drapeau. Une valeur dans le passé se lit « on ne sait pas », jamais « c'est toujours vrai » — et Notula traite alors l'état comme dégradé, parce que c'est l'échec sûr. À l'écran : « La transcription est plus lente que d'habitude en ce moment. » Ni GPU, ni hébergeur, ni file : un fait de produit.
13

Effacer

Notula→Broulala

Le cours est supprimé, ou son échéance de conservation arrive, ou le compte s'en va. Notula supprime chez elle, et le même jour chez Broulala — même disque ou pas : une copie oubliée est une copie oubliée.

Requête et réponse
DELETE /v1/jobs/job_5b7e9d21
DELETE /v1/jobs/job_6c0f4a83
DELETE /v1/jobs/job_71b2e5d0

→ 204 No Content   (idempotent : un job déjà supprimé répond pareil)

Un échec ne bloque pas la suppression locale : il est journalisé et repris au passage suivant. La purge automatique à trente jours est le filet en dernier ressort, jamais le mécanisme.

05

La variante réactive

Les étapes 05 et 06 supposent un processus qui tourne et qui interroge. Un client hébergé en application web n'en a pas : rien ne survit entre deux requêtes HTTP, donc il ne peut être que réactif. Une intégration déclare alors une URL, et Broulala l'appelle à chaque changement d'état.

Broulala le job vient de changer d’état Client réactif application web, sans worker POST /events — event_id, job_id, status signé · horodaté · aucun contenu GET /v1/jobs/{job_id} — Authorization: Bearer après vérification de la signature, en temps constant transcript · output · engine
Deux allers-retours au lieu d'une boucle. Le message ne porte rien : il dit d'aller voir, et le client va voir avec sa propre clé — de sorte qu'un webhook forgé ne provoque qu'un GET inutile, et qu'une livraison perdue coûte un appel plutôt qu'un job.
—

Ce que Broulala envoie

Broulala→Client
Requête sortante
POST https://cse.exemple.fr/broulala/events
Content-Type: application/json
Broulala-Event-Id: evt_9c1a4f20
Broulala-Signature: t=1757683451,v1=3f8a1d07c9b2…

{
  "event_id": "evt_9c1a4f20",
  "type": "job.status_changed",
  "created_at": "2026-09-12T14:24:11Z",
  "data": {
    "job_id": "job_5b7e9d21",
    "status": "done",
    "template_id": "tpl_cr_reunion"
  }
}
L'URL est déclarée sur l'intégration, côté administration, par un humain muni d'un token de session — jamais dans un corps de job. Ce n'est pas une commodité : notifier veut dire émettre une requête sortante vers une adresse qu'on nous a donnée, c'est-à-dire une surface SSRF. Tant que l'adresse vient de la configuration, il n'y a rien à filtrer — et un administrateur qui déclare http://notula:3000 sur un réseau de conteneurs l'a fait exprès, là où un filtre d'adresses privées aurait refusé un déploiement légitime.
—

Ce que le client fait, dans cet ordre

Client
Vérification
v1 attendu = HMAC_SHA256( secret , `${t}.${corps brut}` )

1. comparer en temps constant, AVANT d'analyser le corps
2. rejeter si |maintenant − t| > 5 min        ← sinon un message capturé se rejoue
3. rejeter si event_id déjà vu                ← écrit dans la transaction qui agit
4. répondre 2xx en moins de 10 s, travailler après
5. aller chercher le résultat : GET /v1/jobs/{job_id}
L'ordre de livraison n'est pas garanti, et ça n'a aucune importance ici. Le message ne porte pas un état qu'on applique : il dit d'aller lire. Un processing arrivé après un done ne peut rien défaire, puisque la vérité est le GET. Un webhook qui transporterait le résultat aurait besoin d'un numéro de séquence et d'une règle « ne pas appliquer plus ancien ».
Et le polling ne s'arrête jamais, il ralentit. Un client qui ne va chercher un résultat que lorsqu'un webhook arrive perd un job à la première livraison manquée, et le perd en silence. Reprises à 1, 5, 15, 60 et 360 minutes, puis abandon — le job, lui, reste consultable.
06

Ce que Notula garde de chaque échange

La provenance est stockée avec la sortie, jamais relue depuis la configuration. C'est ce qui garde une vieille transcription et une vieille fiche interprétables après un changement de moteur ou de template.

Et depuis le 18/09/2026 elle se constate au lieu de se recopier (décision B16). La forme dit sa propre fiabilité : une valeur portant un @ a été lue chez le moteur — fichier de poids et commit — une valeur sans @ est le nom configuré, écrit parce que personne n'a pu regarder. Ce qui l'impose : llama-server résout son dépôt en un commit à chaque démarrage, donc un dépôt qui bouge ferait charger d'autres poids sous exactement le même nom, et rien ne le dirait.

Champ de la réponse Où il atterrit Pourquoi on le garde
engine.transcription_model Transcription.model Le moteur de transcription. Sans @ : ce moteur-là ne dit nulle part ce qu'il a chargé, et l'absence le dit au lieu de le taire
engine.generation_model SummaryDraft.model, CardSet.model Constaté, pas configuré — le fichier de poids et le commit lus chez le moteur : …q4_k_m-00001-of-00003.gguf@b466e1f8…
output.template_version parameters (JSON opaque) À ces tailles de modèle, le prompt pèse plus lourd que le modèle
service.mode Transcription.degraded Segmente l'échantillon de débit : trois minutes et cent cinq minutes sont deux populations
job_id Conservé tant que la ressource existe C'est ce qui rend l'effacement possible
— leavesInfrastructure false ici. Valeur de configuration, écrite par ligne : un vieux transcript doit dire ce qui était vrai
07

Ce que Notula cesse de porter

Le bénéfice de l'extraction n'est pas la réutilisabilité, c'est ce qui sort du dépôt. Tout ce qui suit vit désormais à droite de la frontière.

Parti chez Broulala

  • Le superviseur qui lève et couche une machine louée à la minute
  • L'adaptateur du fournisseur de calcul, et le tunnel qui donne une adresse stable à un pod
  • Le plafond de créations et le plafond dur d'allumage qui bornent la facture
  • La liste des centres de données vérifiée pour rester dans l'EEE
  • Les deux adaptateurs de moteur, et la réécriture d'en-tête MP4 qui existait parce qu'un service lisait son entrée comme un flux non rembobinable
  • L'état de la machine en base : les baux, la frise d'allumage, la dépense

Ce qui reste, et pourquoi

  • L'estimateur de débit. Il mesure l'historique de Notula pour dessiner une barre ; deux clients n'ont pas le même genre d'audio.
  • La jonction des parties. C'est l'enregistrement joint que Notula chiffre, stocke et sert à son lecteur — elle doit le fabriquer de toute façon.
  • Le découpage en passages. Une décision sur la façon dont le texte se lit, pas sur la façon dont un moteur découpe.
  • La décision d'attendre. La politique reste au client — on_degraded par job —, seule l'exécution passe à la plateforme.