Trois dictees d'affilee sans le moindre son, chacune terminee par la fermeture
forcee du halo. Cause : Recorder.start() ecrasait self._stream sans fermer le
precedent. Des qu'un stop() se bloquait sur un peripherique accapare, le flux
restait ouvert POUR TOUJOURS ; l'essai suivant en empilait un autre par-dessus, et
Zonza finissait par tenir le micro qu'elle tentait d'ouvrir. 25 threads dans le
process en temoignaient.
start() libere donc le flux precedent avant d'en ouvrir un nouveau. La liberation
DETACHE la reference d'abord — meme ratee, elle ne doit pas laisser un flux mort
barrer le prochain demarrage — et utilise abort() plutot que stop(), ce dernier
drainant les tampons et pouvant se bloquer. Elle ne leve jamais.
Le flux est desormais injectable, ce qui rend Recorder testable sans peripherique.
Prouve sur le vrai micro, en enchainant deux demarrages sans arret entre les deux :
1066 blocs au premier, 1070 au second. Avant, le second en recevait zero.
80 tests passent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deux dictees ont ete transcrites correctement puis n'ont jamais atteint la zone de
saisie, sans la moindre erreur : inject_text avait bien envoye ses touches. Cause :
la hotkey Cmd+Ctrl+D etait encore enfoncee quand Zonza a simule Cmd+V. Le systeme a
recu Cmd+Ctrl+V, que l'application cible ignore. Le code croyait avoir reussi.
inject_text patiente donc jusqu'a ce qu'aucun modificateur physique ne soit enfonce
(drapeaux Quartz), au plus 2 s. Au-dela, il colle quand meme : mieux vaut un collage
douteux qu'une transcription perdue. Le delai du presse-papier passe de 0,05 a 0,12 s,
0,05 etant juste.
Et surtout, le collage se TRACE desormais ("[collage] ok" ou l'avertissement) : sans
cela, un echec futur resterait invisible dans le log, comme celui-ci l'a ete.
L'attente est une unite pure et bornee (core.attendre, horloge et sommeil injectables).
Detecteur verifie en conditions reelles : False au repos, retour immediat.
55 tests passent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sans contexte, le modele rendait « Playwright » en « PlayWreck », « iCloud » en
« Auriclaw », « Supabase » en « Supabass », « Vercel » en « Versell » : 4 termes
sur 5 faux, mesure sur audio reel. Avec l'amorce (initial_prompt), les 5 sont
corrects, sans changer de modele ni ralentir (1,3 s).
Passer a large-v3-turbo a ete teste et ecarte : PIRE sur le meme echantillon
(« commit sur Gitea » -> « commis sur JTA », « Stripe » -> « Trip »), pour 1,5 Go
de plus. C'est l'amorce qui paie, pas la taille du modele.
VOCABULAIRE est une simple liste a completer dans core.py. Whisper ne lit que
~224 tokens d'amorce et tronque le reste EN SILENCE : la longueur est donc
bornee explicitement, pour qu'un ajout de termes ne soit jamais sans effet a
l'insu de celui qui l'ecrit (47 termes = 416 caracteres sur 800).
34 tests passent (27 + 7 nouveaux).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reconstruction propre de l'ancien projet Dictée. Bundle id neuf
(cloud.mrtechlab.zonza), chemin de build unique vers /Applications
(plus jamais de builds /tmp enregistrés dans LaunchServices).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>