Skip to content

feat(voix): l'émotion en balise, la musique qui se tait, le refus qui compte - #12

Merged
phmatray merged 1 commit into
mainfrom
feat/emote-inline-et-ducking
Sep 6, 2026
Merged

feat(voix): l'émotion en balise, la musique qui se tait, le refus qui compte#12
phmatray merged 1 commit into
mainfrom
feat/emote-inline-et-ducking

Conversation

@phmatray

@phmatray phmatray commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Trois choses reprises de l'agent fullstack-agent de jaredrhod, après audit de sa pile (ai-memory-vault, backtalk, ai-visualizer, barehands) : les seules qui n'existaient pas déjà ici. Tout le reste — la FSM tracée, les réflexes, le journal, les rêves, le routage de cerveau, le filtre de bruit, l'undo, le doctor, le paquet — était déjà là, et plus abouti.

Ce qui change

Les émotions passent par une balise. Le cerveau glisse <<proud>> dans sa réponse parlée au lieu d'appeler omarchy-shell macarchy.jarvis emote en outil Bash. Un tour de modèle entier économisé par émotion. La balise ne sort jamais par la voix, ni dans la bulle, ni dans le journal, ni dans la trace.

Elle est parkée, pas envoyée : les deux surfaces dessinent l'état du pipeline par-dessus toute émotion tant qu'il travaille (Service.qml lie mood !== "idle" en premier, touchbar.py jette une émote hors idle), donc une émotion tirée pendant qu'il parle ne serait jamais vue. mascot_park existe exactement pour ça. Le gain de la balise est le coût, pas le timing.

La musique se tait pendant qu'il parle et repart au retour à idle — pas à followup, qui est justement la fenêtre où le wake daemon garde le micro ouvert pour une relance. Une fois par réponse, dans on_state_change, le seul entonnoir que toute transition traverse.

Un refus de permission laisse une note dans FAILURES.md, pour que les rêves proposent la rangée d'allowlist qui manque. Lu dans le champ permission_denials que le CLI publie lui-même dans son événement result — pas en cherchant « permission » dans le corps d'un outil en échec : un Permission denied ordinaire, venu d'un outil autorisé qui bute sur un répertoire illisible, n'est pas un trou dans l'allowlist, et trois fausses notes suffisent à armer un rêve qui réécrit LEARNED.md.

Et quand la faute est dans son propre code, il dispatche une mission sur son arbre au lieu de répondre qu'il ne peut pas.

Ce qui a été refusé de cette pile

Le coffre Obsidian (memory/ + Journal.qml le font sans dépendre d'une app GUI), le bus de fichiers .voice_state (l'IPC macarchy.jarvis porte déjà chaque état), la galerie de quatre visages, la voie ElevenLabs (clé cloud et plan payant contre une âme locale-d'abord), barehands, le son de réflexion (le sprite thinking porte le même signal, en silence), et le wizard d'installation en markdown.

Revue

Sept agents ont écrit, puis une passe de revue a sorti six défauts, tous corrigés dans la branche :

  1. l'émotion n'atteignait aucune des deux surfaces — parkée désormais ;
  2. la musique repartait sur followup, dans le micro ouvert ;
  3. la regex sur texte libre notait un faux échec sur un Permission denied banal ;
  4. <<Proud>> capitalisé passait au travers et piper lisait les chevrons ;
  5. une rangée REFLEXES pour reset ressuscitait la carte du panneau et notifiait deux fois — supprimée ;
  6. jusqu'à 2 s de playerctl bloquant sur l'arête thinking → speaking.

Le diff a fondu au passage : une regex, un drapeau global, un bloc dupliqué et une rangée de table en moins.

Vérification

  • ./tests/run420 réussite(s), 0 échec(s) (3 cas neufs : émotion parkée et jamais prononcée, ducking, refus d'allowlist — plus son miroir, le Permission denied ordinaire qui ne doit rien noter)
  • shellcheck -S warning -x bin/jarvis bin/jarvis-fsm.sh bin/jarvis-wake install.sh bootstrap.sh tests/run → propre
  • python3 -m pytest -q tests/test_touchbar.py → 24 passed

🤖 Generated with Claude Code

https://claude.ai/code/session_0144GFZfmBcCFEMmQdaz3cAt

… compte

Trois choses reprises de l'agent « fullstack-agent » de jaredrhod, après
audit de sa pile : les seules qui n'existaient pas déjà ici, et mieux que
là-bas.

- Les émotions passent par une balise `<<proud>>` glissée dans la réponse
  parlée plutôt que par un appel d'outil Bash. Le tour de modèle entier
  que coûtait `omarchy-shell macarchy.jarvis emote` disparaît ; la balise
  ne sort jamais par la voix, ni dans la bulle, ni dans le journal. Elle
  est parkée comme n'importe quel aparté : les deux surfaces dessinent
  l'état du pipeline par-dessus toute émotion tant qu'il travaille, donc
  une émotion tirée pendant qu'il parle ne serait jamais vue.

- La musique se tait pendant qu'il parle et repart au retour à `idle` —
  pas à `followup`, qui est justement la fenêtre où le micro est ouvert
  pour une relance. Une fois par réponse, dans on_state_change, le seul
  entonnoir que toute transition traverse.

- Un refus de permission laisse une note dans FAILURES.md, pour que les
  rêves proposent la rangée d'allowlist qui manque. Lu dans le champ
  `permission_denials` que le CLI publie lui-même : un `Permission denied`
  ordinaire, venu d'un outil autorisé qui bute sur un répertoire
  illisible, n'est pas un trou dans l'allowlist et ne doit rien noter.

- Et quand la faute est dans son propre code, il dispatche une mission sur
  son arbre au lieu de répondre qu'il ne peut pas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0144GFZfmBcCFEMmQdaz3cAt
@phmatray
phmatray merged commit 3a585fc into main Sep 6, 2026
2 checks passed
@phmatray
phmatray deleted the feat/emote-inline-et-ducking branch September 6, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant