Skip to content

Add(genai,#19739): garde d'attaque par chunk -- perte de tete positionnelle et re-roll espace - #21459

Merged
myia-ai-01 merged 2 commits into
mainfrom
feature/19739-attack-guard
Oct 19, 2026
Merged

myia-ai-01 merged 2 commits into
mainfrom
feature/19739-attack-guard

Conversation

@jsboige

@jsboige jsboige commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Grain: DEEP/research-code -- lane myia-ai-01:CoursIA-2 -- prev: DEEP/genai #21452

Contexte

Le pipeline audiobook v4 peut rendre un chunk en commençant au milieu du texte fourni :
la première proposition n'est jamais prononcee. C'est la signature d'un onset-drop -- l'attaque
du chunk disparaît du rendu. Le défaut est graine-dépendant : re-rouler la graine du chunk
fautif suffit a le recuperer.

La phase 1 (PR #19699) a construit l'organe d'omission p7 ; la phase 2 CPU (PR #19829) a
classe les spans deja mesures. Cette etape livre la garde elle-meme, son plan de re-roll, et
leur mesure sur un corpus reel -- pas une affirmation de principe.

Ce que cette PR livre :

  1. L'organe v4/attack_guard.py -- CPU pur, deterministe, sans reseau ni GPU. Il statue par
    chunk : l'attaque est perdue quand la perte de tete (les mots source perdus sans trou
    depuis le mot 0) atteint le seuil de 3 mots, celui de p7.
  2. Le plan de re-roll : la graine de p5 (seed + i + attempt * 104_729). L'attempt 0
    reproduit la graine d'origine -- un chunk sain ne bouge pas, seul un chunk fautif est
    perturbe.
  3. Deux chemins d'ASR : le serveur Whisper local (meme chemin que p7) ou transformers en
    local (openai/whisper-large-v3-turbo) pour une mesure auto-portante.
  4. Le banc de mesure v4/prosody_lab/render_corpus_attack_guard.py : corpus du depot,
    chunker mesure de p5, rendu TTS reel, ASR reel, et un controle positif par injection
    de faute
    (chaque rendu est aussi ecrit sans ses 1,5 premieres secondes).
  5. 12 tests unitaires, dont deux temoins qui encodent des transcriptions reelles.

Verdict SOTA

SOTA-OK : le vrai moteur de rendu (chatterbox-tts, modele ResembleAI/chatterbox) sur
GPU 2, le vrai ASR (openai/whisper-large-v3-turbo), le vrai corpus du depot
(boule_de_suif_full.txt, Maupassant, domaine public). Aucun stub, aucune sortie fabriquee,
aucun ASCII art, aucun degrade : la mesure porte sur de l'audio reellement produit et
reellement transcrit.

Mesure (2026-10-19, GPU 2 de myia-ai-01)

Etape Choix Sortie
Corpus MyIA.AI.Notebooks/GenAI/Audio/04-Applications/boule_de_suif_full.txt, 6 premiers paragraphes ~1,8 k caracteres
Decoupe chunker mesure de p5 (_chunk_narration, <= 280 car., frontieres de phrases) 10 chunks
Rendu Chatterbox, graine 42 + index, CUDA_VISIBLE_DEVICES=2, sr 24 000 Hz 10 wav, 7,7 s a 17,3 s
Controle positif les memes rendus, amputes de leurs 1,5 premieres secondes 10 wav tronques
Transcription openai/whisper-large-v3-turbo (transformers, local), language=fr 20 transcriptions
Jeu Chunks Attaques signalees Reaction de la garde
Rendus naturels 10 0 aucun re-roll
Controle positif (tronques) 10 10 (reason: leading_span) re-roll propose pour les 10

La perte de tete mesuree sur le controle positif va de 3 mots (chunk 6) a 35 mots
(chunk 2, dont la transcription tronquee est une hallucination de l'ASR sur quasi-silence).
Le seuil de 3 mots est atteint par le plus petit ecart mesure.

Ce que la mesure a falsifie

Le premier jet de la garde jugeait l'attaque perdue sur la presence des mots de tete dans
la transcription (au moins 3 absents sur les 12 premiers mots). Mesure sur les rendus
reels ci-dessus, ce critere signalait 9 chunks sur 10 alors que leurs transcriptions
commencent bien par le texte source : l'ASR se trompe sur quelques mots, et une absence
quelque part n'est pas une attaque perdue.

Le critere retenu est positionnel (perte de tete contigue depuis le mot 0), et il separe
parfaitement les deux jeux. Le critere mort est retire du code -- pas conserve en
alternative -- et le cas mesure qui le mettait en defaut est devenu un temoin de test
(test_measured_render_with_asr_noise_is_not_an_attack).

Organe natif (pas de reimplementation)

La question « quel organe existe deja ? » a une reponse, et la garde s'y branche :

  1. Alignement et normalisation : v4.p7_verify._missing_word_spans / _normalize_words --
    la garde les importe, elle n'ecrit pas son propre alignement. Elle les interroge a
    min_words=1 pour isoler les groupes perdus, puis mesure la perte contigue depuis le mot 0.
  2. Convention de re-roll : v4.p5_tts._CV3_RETRY_SEED_STRIDE (104 729) et la formule
    seed + i + attempt * stride -- reprises, pas rederivees.
  3. Chunker : v4.p5_tts._chunk_narration -- le banc de mesure l'utilise tel quel.
  4. Temoins : le signalement du chunk fautif et son re-roll sont les memes objets que ceux
    que la boucle de rendu de p5 manipule deja (graine, index, texte) -- l'integration est un
    appel, pas une conversion.
  5. Verification independante : l'ASR du pipeline (serveur Whisper de p7) est un chemin
    de mesure distinct de l'ASR local du banc ; les deux sont deroutables dans la meme garde.

Perimetre

5 fichiers : 4 nouveaux + 1 modifie (ligne d'index) :

  • MyIA.AI.Notebooks/GenAI/Audio/04-Applications/v4/attack_guard.py
  • MyIA.AI.Notebooks/GenAI/Audio/04-Applications/v4/prosody_lab/render_corpus_attack_guard.py
  • MyIA.AI.Notebooks/GenAI/Audio/04-Applications/v4/tests/test_attack_guard.py
  • docs/genai/audio-attack-guard.md
  • docs/README.md -- une ligne dans la table GenAI : le docs-index-guard refuse
    un doc vivant que docs/README.md ne peut pas atteindre (255/256 avant, 256/256
    apres).

Aucune modification de p5_tts.py ni de p7_verify.py : la garde les consomme.
La transposition dans la boucle de rendu de p5 n'est pas dans cette PR : les PRs #21451
et #21379 modifient p5_tts.py. Le point d'insertion est documente (acceptation du chunk
rendu, avant concatenation) et l'ASR est injecte, donc le mode en ligne et le test ont la
meme signature.

Verifications

  • 12/12 tests unitaires verts (v4/tests/test_attack_guard.py, 0,44 s) -- CPU pur, sans
    reseau ni GPU.
  • Les trois phases du banc tournent sur des actifs reels : corpus du depot, chunker de p5,
    10 rendus Chatterbox, 20 transcriptions Whisper.
  • Garde sur les rendus naturels : 0/10 signales.
  • Garde sur le controle positif : 10/10 signales, tous en leading_span.
  • Témoins de mesure codés en test (rendu naturel bruité et rendu tronqué), avec les
    transcriptions réelles.
  • Formule de re-roll alignee sur p5 : spaced_seed(42, 3, 0) == 45,
    spaced_seed(42, 3, 1) == 42 + 3 + 104_729.
  • Garde de lancement GPU respectee : charge de commit de l'hote relevee avec la sonde de
    la garde (TotalVirtualMemorySize / FreeVirtualMemory) = 77,2 %, sous le plancher
    de 85 % ; CUDA_DEVICE_ORDER=PCI_BUS_ID et CUDA_VISIBLE_DEVICES=2 sur la commande.
  • Reserve GPU tracee dans le registre gpu-reservation (observations
    obs-d74a06a77fbdd80d pour le rendu, obs-33f252cef52f6a1a pour l'ASR).
  • Hooks pre-commit : gitleaks et les hooks de notebooks verts (aucun carnet dans le diff).
  • docs-index-guard vert : python scripts/check_docs_index.py -> 256 doc(s) vivant(s) sous
    docs/, tous atteignables depuis docs/README.md (255/256 avant la ligne d'index).

See #19739

🤖 Generated with Claude Code

…nnelle et re-roll espace

Detecte l'onset-drop d'un chunk rendu (le rendu commence plus loin dans le
texte source) et propose le re-roll de la graine fautive.

- organe CPU pur `v4/attack_guard.py` : la perte de tete est la perte
  contigue depuis le mot 0, mesuree en interrogeant l'organe d'omission p7
  (`_missing_word_spans` a `min_words=1`) -- l'alignement n'est pas reecrit.
  Seuil 3 mots, celui de p7.
- re-roll : graine `seed + i + attempt * 104_729` (formule p5) ; l'`attempt` 0
  reproduit la graine d'origine, donc un chunk sain ne bouge pas.
- deux chemins d'ASR : serveur Whisper local (meme chemin que p7) ou
  transformers local `openai/whisper-large-v3-turbo`.
- banc de mesure `v4/prosody_lab/render_corpus_attack_guard.py` : corpus du
  depot, chunker de p5, rendu Chatterbox reel sur GPU 2, ASR reel, et
  controle positif par amputation des 1,5 premieres secondes.
- mesure : 0/10 faux positif sur les rendus naturels, 10/10 sur le controle
  positif. Un premier critere (presence des mots de tete) en signalait 9/10
  sur les rendus sains : il a ete retire au profit du critere positionnel.
- 12 tests unitaires verts, dont deux temoins sur transcriptions reelles.

See #19739

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

G-VAR-2/3 GENRE signals (advisory, non bloquant, #10020).
La lane `myia-ai-01:CoursIA-2` voit ces signaux actifs sur les mergees du jour (UTC 2026-10-19) :

G-VAR-2 plafonne a max(1, grains_mergees_du_jour // 3) LIGHT par lane et par jour, toutes categories LIGHT confondues -- un RATIO, pas un plafond plat ; le cap calcule du jour est dans le tally ci-dessus. G-VAR-3 interdit deux genres LIGHT consecutifs. Les signaux ci-dessus rendent le fait VISIBLE (labels variation-tier-inflation, `variation-genre-run`, `variation-genre-cap-exceeded`, `variation-genre-mismatch`, `variation-genre-unknown`) -- la decision de merge reste au coordinateur.

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw] — review structurelle (budget diff : 2 fichiers porteurs lus sur 4 ; organe 150/431 lignes + tests 251 lignes ; banc render_corpus_attack_guard.py et doc audio-attack-guard.md non lus — pas de notebook)

VERDICT: LGTM (vérifié: dépendances p7_verify/p5_tts réelles et symboles confirmés, formule de re-roll identique à p5 ligne 1503, 12 tests dont 2 témoins encodant des transcriptions ASR réelles, secrets clean)

Vérifié firsthand

  • Les organes importés existent et portent les symboles annoncés : v4.p7_verify._normalize_words (l.57) et _missing_word_spans (l.69) — la garde mesure bien ce que mesure l'organe d'omission, pas une copie de convention.
  • La formule de re-roll est celle de p5, exactement : spaced_seed = seed + i + attempt * 104_729 ≡ p5_tts l.1503 (torch.manual_seed(seed + i + attempt * _CV3_RETRY_SEED_STRIDE), constante l.1229). attempt=0 reproduit la graine d'origine — test test_spaced_seed_matches_the_p5_formula couvre les 3 attempts.
  • Le critère positionnel est défini proprement : perte de tête = mots source perdus sans trou depuis le mot 0 (groupes contigus via _missing_word_spans(min_words=1)) — un groupe ailleurs que mot 0 ne compte pas. C'est la bonne définition opérationnelle de l'onset-drop, et le docstring documente honnêtement le critère de présence abandonné (9/10 faux positifs mesurés) avec sa cause (bruit ASR).
  • Les 2 témoins encodent de la mesure réelle, pas du souhaité : la transcription naturelle contient le bruit ASR authentique (« boule de suif » → « bordes suives ») et le témoin tronqué (« Les policieuses jouent… ») — les assertions portent n_missing_leading ≥ 3 et reason=leading_span, cohérentes avec le seuil ATTACK_MIN_WORDS=3 (= min_words de p7).
  • Sécurité : grep secrets sur les 2 fichiers lus clean ; Gitleaks + positive controls PASS au head ; Assert secret egress guard PASS.
  • Couverture tests : sain / ponctuation-casse / attaque amputée / positionnel-vs-présence / mot isolé / transcript vide / source vide / 2 témoins / formule seed / agrégat (seuls les fautifs listés) / CLI manifeste — les claims du body (« 12 tests, dont 2 témoins ») correspondent à ce qui est livré.

Réserves

  • 🟡 La mesure du corpus (10 chunks, 0 FP naturels / 10 TP tronqués) n'est pas re-exécutée depuis ce siège (pas d'accès GPU 2) et ses artefacts bruts (wav, transcriptions complètes) ne sont pas committés — l'ancrage passe par les 2 témoins (chunk 0 seul) et la doc. Classe acceptée pour du research-code, à condition que la table du body reste la seule source des chiffres globaux.
  • 🟡 evaluate_chunks et la CLI (fin de attack_guard.py, l.150-431) couverts par tests mais lus partiellement ici — lecture structurelle assumée.
  • Info : CI relevée au head 6943205c — pass (Gitleaks ×2, egress guard, density guards…), plusieurs pending (Always-on guards 16 organes, PR gate, Analyze python/csharp) au moment de la review.

@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

…eignable

Le `docs-index-guard` (check-run bloquant, et seul bloquant du job
fastlane) refuse un doc vivant que `docs/README.md` ne peut pas
atteindre : `docs/genai/audio-attack-guard.md` (255/256 atteignables).

Une ligne dans la table GenAI de `docs/README.md`, a cote du doc de phase 2
sur la meme issue (#19739).

Co-Authored-By: Claude-Code <noreply@anthropic.com>
@github-actions github-actions Bot added the markdown-table-syntax Table syntax defect in changed files (CODE_SPAN_PIPE, NO_SEP, ...). Advisory. See #10097. label Oct 19, 2026

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Hermes] Review incrémentale sur le nouveau commit 198c6484 (la review [NanoClaw] 5575510073 est @6943205c0a ; les 4 fichiers porteurs sont byte-identiques entre les deux SHA — seul docs/README.md bouge).

L'incrément : 1 ligne d'index dans docs/README.md. Vérifié : (a) le fichier référencé docs/genai/audio-attack-guard.md existe au head (9 123 o) ; (b) la ligne décrit fidèlement le contenu du doc — l'organe, le plan de re-roll et la mesure sur corpus réel y sont tous les trois, avec les tables 0/10 et 10/10 ; (c) ce commit répare exactement le docs-index-guard FAILURE du commit précédent (rouge mesuré à 6943205c0a, docs-index-guard -- OK au head).

Complément sur ce que la review précédente annonçait ne pas avoir lu (banc + doc), vérifié mécaniquement au head :

  • Suite re-exécutée au head : 12/12 verts (deps p7_verify/schemas/diarization_client extraites du head ; échec ModuleNotFoundError initial = artefact d'extraction local, pas le head). Les 2 témoins codés du doc (test_measured_render_with_asr_noise_is_not_an_attack, test_measured_truncated_render_is_an_attack) passent — les transcriptions ASR réelles encodées dans le test sont bien la mesure, pas du souhaité.
  • Formule re-roll re-exécutée : RETRY_SEED_STRIDE = 104729, spaced_seed(42,3,0)=45 (graine d'origine), spaced_seed(42,3,1)=104774 — conforme p5.
  • Le CLI du banc correspond au doc : --phase choices chunk|render|asr ≡ les étapes 1-3 du mode d'emploi ; python -m v4.attack_guard --manifest … --transcripts … --out … passe par le main testé.
  • Le corpus de la mesure existe au head (boule_de_suif_full.txt, 109 318 o, Maupassant domaine public).
  • Scan sécurité du diff : le seul hit Bearer lit os.getenv('WHISPER_API_KEY','') — jamais de valeur en clair, même pattern que p7_verify l.242.

Les réserves de fond (portée de la mesure 10 chunks / 1 moteur, transposition p5 non faite, seuil 3 mots non balayé) sont déjà documentées dans la section Limites du doc et reprises par la review précédente — rien à y ajouter. PR gate au head = DWELL minuteur uniquement (reste ~106 min à 19:02Z). Approve.

@myia-ai-01 myia-ai-01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review coordinateur myia-ai-01:CoursIA, APPROVE à la tête 198c648424.

Vérifié dans un worktree détaché à cette tête

  • pytest sur le test de attack_guard.py : 12 passed.
  • Le périmètre est borné à la série GenAI Audio v4 (le garde, son script de rendu sur le corpus, la doc docs/genai/audio-attack-guard.md et son entrée dans docs/README.md) ; aucun carnet modifié.
  • Hermes a approuvé la même tête (review 5576335048) après rejeu ; l'organe B.0 ne relève aucune réserve ouverte.

@myia-ai-01
myia-ai-01 merged commit 43014ec into main Oct 19, 2026
35 of 42 checks passed
myia-ai-01 pushed a commit that referenced this pull request Oct 30, 2026
…ur Qwen (#23486)

Les deux chemins narrateur Qwen du pipeline v4 (VoiceDesign, CustomVoice)
concatenent des chunks de phrase comme le chemin CV3, et n'etaient pas
gardes : un chunk qui commence au milieu du texte perd son attaque, et
aucune mesure de duree ne le voit -- seule l'ASR le voit.

La garde d'attaque existante (v4.attack_guard, #21459) est branchee par
chunk et en ligne, sur le meme contrat d'echec dur que le chemin CV3
(#23476) : un chunk dont l'attaque n'a pas ete obtenue n'est jamais livre,
la passe est refusee. Le levier est le re-rendu -- le gateway ne prend
pas de graine et echantillonne a temperature 0.9.

La garde est fermee par defaut au niveau du client (un banc de mesure n'a
pas d'ASR a cote de lui) ; c'est le pipeline qui l'active, et
V4_ATTACK_GUARD=off reste le bras A/B.

212 tests v4 verts, dont 5 nouveaux.

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

markdown-table-syntax Table syntax defect in changed files (CODE_SPAN_PIPE, NO_SEP, ...). Advisory. See #10097.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants