Parfait. J’enchaîne maintenant sur TAM-845 avec /implementing-ticket dans Softizy/tamag0-desktop : récupération du ticket/RFC, branche basée sur main, implémentation test-first, PR puis review obligatoire — sans merge automatique.
Challenge RFC → code actuel
Les deux prérequis sont bien mergés : finalizer ordonné présent, backend TAM-844 disponible sur main.
Le hook legacy wakeParentOnResolve() existe encore, exactement comme prévu pour cette dernière phase.
Les quatre transports disposent déjà du desktop_threads.id ou de TAMAG0_THREAD_ID; il manque seulement sa propagation mécanique comme header runtime.
Le client SSE typé, la relecture canonique (getThread/getLinkedThreads) et le verrou anti-double-worker (activeWorkers) existent déjà.
Challenge KISS : aucune nouvelle couche spéculative n’est nécessaire. Je réutilise le client SSE, l’API client et le chemin de wake existants ; pas de lease, ACK, cache d’état ni second modèle de relation. Il n’y a pas de divergence bloquante avec la RFC, donc je poursuis.
Contrat d’implémentation TAM-845
AC-1.2 runtime — Claude CLI, native in-process, standalone tamag0-agent et Codex CLI ajoutent mécaniquement X-Tamag0-Thread-Id depuis desktop_threads.id; aucun session_id fourni par le modèle ne peut le remplacer.
AC-5.1 — Le signal SSE typé par agent déclenche immédiatement le consume ciblé puis une relecture canonique.
AC-5.3 — Un parent occupé ne lance jamais deux workers; un hint périmé ne modifie pas l’état FSM.
AC-5.4 — La médiane entre settlement et démarrage du worker reste sous deux secondes avec SSE connecté.
AC-7.1 — wakeParentOnResolve() et son appel fire-and-forget disparaissent.
Branche créée : feature/TAM-845-desktop-transport-parent-identity-and-wake-hints
Linear : In Progress. Je passe aux tests de contrat des quatre transports en premier.
TAM-844 étant bien mergée, j’enchaîne sur TAM-845 comme prévu. La branche est déjà ouverte et le socle test-first est en place ; je poursuis maintenant l’implémentation du consume ciblé, du réveil atomiquement réservé et de la suppression du hook legacy, puis je validerai avant PR et review parallèle.
Le chemin critique est maintenant codé : le signal SSE consomme la file bornée, revalide parent/enfant/liens depuis l’état canonique, puis réserve le parent dans le même registre que tous les workers avant tout spawn. Je vérifie ce lot ciblé avant de retirer définitivement l’ancien mécanisme de réveil.
Le test de non-usurpation a bien reproduit un défaut réel : extraEnv pouvait écraser l’identité canonique dans les chemins Claude, Codex et natif. J’ai durci chaque transport afin que les credentials restent surchargeables, mais jamais TAMAG0_THREAD_ID. Je passe maintenant le contrôle TypeScript avant la validation ciblée complète.
Les AC sont maintenant implémentées et les contrats backend réels ont été rejoués sur PostgreSQL (8 + 9 tests verts, sans skip après configuration explicite de la base Hub). Je passe à /validate-rfc-coverage pour vérifier chaque critère contre le diff avant la suite finale et la création de PR.
Couverture RFC validée : 28/28 AC couvertes sur les deux passes.
Déjà mergé : TAM-843 couvre AC-2.x ; TAM-844 couvre le contrat PostgreSQL, RLS, watchdog, index et pertes de poke.
Cette branche : AC-1.2 (quatre transports), AC-5.1, AC-5.3, AC-5.4 et AC-7.1.
Aucun critère partiel, manquant ou différé.
La vérification croisée a aussi corrigé le mock de graphe : l’API Desktop expose bien le lien canonique spawned_from, tandis que blocked_by reste utilisé par la transaction backend.
Je lance maintenant l’unique passe complète de la suite Desktop, après contrôle d’absence d’autre runner local.
Suite finale verte :
Desktop : 10 037 tests passés, 4 ignorés.
Runtime standalone : 186 tests passés.
TypeScript node/web/runtime : ✅
PostgreSQL réel TAM-844 : 17 tests passés.
Arbre de diff propre (git diff --check) : ✅
J’enchaîne avec /finishing-tickets : contrôle final du diff, commit, push et PR vers main, sans merger.
PR créée : https://github.com/Softizy/tamag0-desktop/pull/1665
Je déclenche maintenant la review obligatoire /parallel-code-review sur le diff réel de la PR, avant toute attente CI et sans merger.
Diff de la PR : 1 103 lignes modifiées dans 31 fichiers. Je lance les trois clusters en parallèle : exactitude/tests/réalisme, architecture/KISS/performance, puis sécurité/conformité RFC. Ils sont en lecture seule ; je consoliderai et posterai le verdict complet sur GitHub.
Je verrouille maintenant les derniers correctifs de review par les tests réels, notamment le benchmark PostgreSQL → SSE → consume → processus. Il reste aussi un point d’infrastructure CI à traiter proprement : le checkout privé inter-repositories doit utiliser un credential strictement read-only, pas le PAT de release.
La validation complète est verte. Je termine maintenant les contrôles de bundle, puis je committe et pousse les correctifs de review sur la PR TAM-845 — sans merger.
Les contrôles de bundle passent, l’arbre ne contient aucun artefact de build accidentel et aucun secret n’est présent dans le diff. Je crée maintenant le commit final de durcissement.
Le commit d9b870bb892c2c4952eff9de6c26fc79443c8d83 est poussé. Je lance maintenant la revue indépendante obligatoire sur la PR, pendant que la CI démarre.
La CI a confirmé le seul prérequis externe : le checkout du dépôt backend privé échoue avant les tests, faute de credential lecture seule. Je laisse les autres jobs et les reviews avancer ; je ne remplacerai pas ce credential par le PAT de release, trop permissif.
Codecov remonte 17 lignes nouvelles non couvertes dans dispatcher/service.ts. Je les traite maintenant : je localise précisément les branches, j’ajoute les régressions ciblées, puis je repousse — aucune dette de test ne restera.
Les nouvelles régressions ciblées passent (97/97) et TypeScript est vert. Je lance l’unique passe finale avec couverture afin de vérifier que les lignes signalées par Codecov sont toutes exécutées dans la suite complète.
La couverture de patch de dispatcher/service.ts est désormais 318/318 lignes exécutables (100 %) ; la suite complète passe avec 10 075 tests. Je committe et pousse ces régressions de review.
Les reviewers ont remonté quatre vrais points : posture autonome des wakes, dernière fenêtre Stop→spawn, retry immédiat après échec de consume, et contournement de capacité par le plan mode. Je les corrige tous maintenant, avec tests de course et d’admission, plus le timeout CI — sans aucun report.
Les quatre correctifs de review passent sur 441 tests ciblés : posture autonome, garde Stop au vrai seuil de démarrage (y compris retry overflow), backoff externe après échec de consume, et admission plan mode partagée/tokenisée. Je mesure maintenant toute la couverture du diff pour éliminer aussi les lignes résiduelles signalées dans les autres fichiers.
Je poursuis TAM-845 jusqu’à une PR réellement prête à relire. Il reste douze lignes modifiées non couvertes dans les correctifs de review ; je vais ajouter les régressions ciblées, refaire la validation complète, puis pousser et redemander les reviews — sans merger.
Les douze lignes sont maintenant chacune exercées par un scénario réel de contrôle : rollback SSE, compatibilité d’admission, propagation du token au démarrage fournisseur, et quatre chemins d’échec du direct spawn. Je lance les seuls tests couvrant ces changements avant toute suite large.
Les 329 tests ciblés et le typecheck passent. Je passe maintenant à l’unique exécution complète avec couverture ; le critère est strict : aucune ligne exécutable modifiée ne doit rester non testée.
La suite complète est verte : 10 084 tests, 5 ignorés. L’audit de couverture confirme désormais 487/487 lignes exécutables modifiées couvertes (les deux dernières ont aussi été vérifiées dans une passe ciblée). Je termine les gates de build et du runtime embarqué avant de rejouer le benchmark PostgreSQL/Hub réel.
Les gates statiques, les 186 tests du runtime embarqué et le build de production passent. Pour le benchmark final, j’utilise le commit backend exact de TAM-844, la base PostgreSQL de test existante et un namespace Redis vide distinct de mes données persistantes.
Le premier benchmark a échoué avant la mesure : la base locale de test n’avait pas les migrations 123–146, donc l’API rencontrait une colonne absente. Ce n’est pas une régression desktop. J’applique les migrations du commit TAM-844 uniquement à cette base de test isolée, puis je relance avec le token Hub explicité des deux côtés.
Le benchmark réel passe sur le contrat backend exact 68d8794a882155af19adccaa49e099a72bbf9772, avec cinq cycles de settle PostgreSQL → SSE ciblé → démarrage d’un vrai processus et une médiane inférieure à 2 s. Je fais maintenant la dernière inspection du diff, puis je committe, rebase sur la branche cible actualisée et pousse.
L’inspection a révélé un dernier angle mort réel : le même Stop→spawn pouvait toucher un message Hub ordinaire ou une reprise après timeout, pas seulement un wake/UI. Je l’ai fermé au même point atomique et ajouté deux régressions déterministes. Les 348 tests ciblés passent ; je relance maintenant la validation complète finale sur ce diff exact.
La validation finale est entièrement verte sur le diff exact :
10 087 tests racine, 5 ignorés
493/493 lignes exécutables modifiées couvertes
186 tests runtime
typecheck, contrats IPC, garde bypass, YAML, build et bundle ✅
benchmark PostgreSQL/Hub/processus réel ✅
Je committe maintenant ces correctifs de review, puis je pousse la PR et redemande les validations GitHub.
Deux commentaires bot restaient effectivement non dépilés dans GitHub malgré les passes précédentes. L’un révèle un vrai bug d’identité : le consume primaire utilisait encore la clé ambiante d’un agent secondaire. J’ajoute d’abord sa régression, ainsi que le cas target_agent_id que la review signalait comme absent, puis je corrige la source.
Parfait. Je poursuis TAM-845 jusqu’à une PR réellement prête à relire : je termine les corrections restantes, valide le contrat best-effort de la RFC, puis je reposte les réponses de review et relance la relecture — sans merger.
Les deux warnings de Teuteu étaient fondés. J’ai retiré la file locale de retries des pokes déjà consommés : les événements restent désormais strictement best-effort, et seul le watchdog porte la récupération. J’ai aussi remplacé la soustraction d’horloges backend/desktop par deux segments cohérents (horloge serveur puis horloge monotone locale). Les tests ciblés sont verts.
Je termine maintenant TAM-845 : audit final du diff, commit, rebase sur la cible main, push, réponses aux threads Codex, puis nouvelle passe de review et CI. Je ne merge rien sans ton accord explicite.
L’audit final est propre : aucun vestige de la file de retry locale, aucun log de debug, et le diff respecte bien le contrat best-effort. Les validations finales sont déjà vertes, y compris 10 091 tests, 458/458 lignes exécutables modifiées couvertes et le benchmark inter-repo sur PostgreSQL réel. Je committe maintenant ces corrections de review, puis je rebase sur la cible exacte de la PR.
Le commit d8f74af069e89b69fc45b6e624119bcaa0d83804 est poussé, et la branche est déjà exactement basée sur origin/main (0 commit de retard). Je réponds maintenant précisément aux deux threads Codex et les résous avec les tests de régression associés.
Correction : le SHA complet exact du commit poussé est d8f74af03bc2b449f28a3a9f58ca2985f666a599.
Les deux threads Codex sont répondus et résolus. La synthèse complète est publiée sur la PR, une nouvelle review Codex est déclenchée, et la re-review /parallel-code-review est relancée auprès de agent-code-reviewer, agent-test-debug et Teuteu sur le HEAD final. Je contrôle maintenant les premiers statuts CI et les verdicts GitHub.
La CI finale est entièrement verte sur d8f74af03bc2b449f28a3a9f58ca2985f666a599 :
build ✅
Windows permissions ✅
benchmark wake réel ✅
Codecov patch/projet ✅
Il reste seulement à recueillir et dépiler les verdicts de re-review indépendants ; aucune fusion n’est lancée.
Les re-reviewers ont trouvé un dernier bloquant réel : le consume primaire utilise désormais la bonne clé provider-bound, mais le spawn peut encore recalculer une clé ambiante secondaire dans tryStartOwnerWorker() puis executeWithRouting(). Je le traite test-first : un wake primaire complet lancé sous contexte secondaire doit prouver que le worker, pas seulement le consume, s’exécute sous la clé primaire.
La reproduction est nette : les deux nouveaux tests échouent exactement comme annoncé — le worker reçoit la clé secondaire, et l’absence de clé provider primaire ne bloque pas le spawn. Je corrige la résolution d’identité à la racine, tout en conservant la clé ambiante uniquement pour les appels sans agentId explicite (scheduler/turn courant).
Les régressions passent maintenant : 170/170 sur service + execute-with-routing, et TypeScript est vert. La correction couvre aussi les deux autres surfaces explicites qui partageaient le même piège (advanceOnboarding et la propagation d’arrêt), sans changer le comportement ambiant des turns sans agent explicite. Je lance l’unique suite complète avant commit.
Le garde de concurrence s’est auto-détecté dans sa propre ligne de commande ; aucune suite n’a été lancée. Je corrige le contrôle au niveau du nom de processus (node/npm) puis relance, sans boucle.
Le correctif final est vert :
170 tests ciblés
10 092 tests complets, 5 ignorés
TypeScript ✅
garde bypass ✅
contrats IPC ✅
diff propre ✅
Je committe ce dernier blocage, puis je vérifie à nouveau la base main, pousse et redemande les verdicts sur le nouveau HEAD.
Le correctif 15d71b2a04b8224411ef5d978e00e67e80c2aa4f est poussé, commenté et soumis aux trois re-reviewers ainsi qu’à Codex. La CI a redémarré sur ce SHA exact ; je reste dessus jusqu’aux verdicts finaux, toujours sans merge.
Codex vient de signaler deux nouveaux points sur le HEAD final. Je ne les prends pas au pied de la lettre : je vérifie chacun contre la RFC, le backend TAM-844 et les vrais chemins d’exécution avant de décider. Les hypothèses sont plausibles — enfant cross-agent et identité runtime du process plan — donc je les traite comme bloquantes jusqu’à preuve contraire.
Vérification faite :
Cross-agent child : faux positif. La RFC D4 et le backend TAM-844 créent volontairement le child shard bloquant sous l’agent émetteur, identique au parent ; le receiver shard cross-agent n’est jamais lié au parent. Le contrôle child.agent_id === target_agent_id protège donc le contrat exact. Je vais répondre avec les références code/RFC et résoudre ce thread sans élargir la surface.
Plan mode : finding valide. La clé de pool ${threadId}:plan est réutilisée comme identité runtime, donc le UUID canonique est perdu. L’API possède déjà un champ options.threadId mais ne l’honore pas. Je reproduis et corrige ce chemin maintenant.
Le test plan a reproduit le problème puis passe après correction : la clé de pool reste suffixée pour la gestion du process, mais TAMAG0_THREAD_ID reçoit désormais le thread canonique. 230 tests ciblés et TypeScript sont verts. Le faux positif cross-agent est documenté et résolu sur GitHub ; je relance la suite complète avant le commit de ce second correctif.
La correction plan est propre et la suite complète passe à 10 093 tests. Je committe, rebase-vérifie la cible, pousse, réponds au second thread Codex puis relance la dernière CI/re-review.
Le second correctif est poussé sur 913ce11f3abae7825cfc62345d000fe1501c7c3a, les deux threads Codex sont résolus, et la review indépendante est relancée sur ce SHA exact. Je surveille maintenant la dernière CI et les verdicts ; aucun merge.
La re-review relève encore un fail-open réel au boundary public executeWithRouting : même si les callers actuels wake/onboarding gardent en amont, un agentId explicite non résolu retombe encore sur la clé ambiante. Je vais le fermer au boundary, test-first.
Codex signale aussi un possible header littéral ${TAMAG0_THREAD_ID} sur les threads non UUID. Je vérifie l’expansion réelle des trois transports avant de modifier la config : si le placeholder non résolu est bien conservé, il faut supprimer le header optionnel plutôt que l’envoyer.
Les quatre régressions rouges confirment les deux problèmes : le boundary exécute encore sous la clé ambiante, et le client native envoie bien le placeholder littéral. Je corrige maintenant le fail-close et rends X-Tamag0-Thread-Id strictement runtime-owned : UUID canonique injecté depuis l’env, sinon header omis, y compris dans la copie .mcp.json utilisée par Claude CLI.
Les deux nouveaux blocages sont reproduits en rouge. J’ai maintenant appliqué le correctif sur les trois transports MCP et fermé le repli d’identité explicite ; je passe aux validations ciblées avant toute nouvelle suite complète.
La validation ciblée est verte : 227 tests desktop et 4 tests d’intégration HTTP du runtime, plus les deux typechecks et les garde-fous IPC/bypass. Je lance maintenant l’unique passe complète, les ressources locales étant libres.
La passe complète a révélé un vrai défaut de déterminisme dans un test Windows non lié : il énumérait le trousseau de certificats macOS et dépassait 5 s sous contention. Je l’ai isolé avec des certificats de test déterministes ; le fichier passe désormais 49/49 en 484 ms. Je relance la suite complète pour obtenir un vert net.
Les trois revues 10D sont vertes, mais Teuteu a maintenu trois résiduels actionnables. Je ne les laisse pas en dette : je supprime le timer de continuation redondant, centralise les bornes de drain/validation et documente explicitement la métrique comme estimation basse. Nouveau test rouge d’abord sur l’absence de second scheduler.
La CI et les trois revues finales sont vertes. L’audit des threads inline révèle toutefois deux commentaires Codex restés ouverts — dont un nouveau sur Dependabot et un ancien sur l’admission avant escalade. Je les vérifie dans le code réel avant de déclarer la PR prête ; je ne vais pas les masquer sous les approvals générales.
Je termine TAM-845 : les tests rouges reproduisent bien les deux derniers problèmes (admission trop précoce et secrets indisponibles pour Dependabot). J’applique maintenant les correctifs de production, puis je relance les validations ciblées et complètes avant de repousser la PR — sans merger.
Le correctif garde les invariants existants : les messages bloqués par un plan sont toujours persistés avant l’ACK, tandis que le compteur de dialogue n’est incrémenté qu’après une admission réussie. Je passe maintenant les quatre régressions rouges au vert.
Les quatre régressions passent désormais. J’élargis aux fichiers de tests complets du dispatcher et du workflow, afin de détecter les effets de bord liés au déplacement de l’admission et de la persistance.
L’élargissement a trouvé un test devenu obsolète : il simulait précisément le garde « précoce » que le correctif doit supprimer. Je le remplace par la garantie pertinente restante pour les intégrations de compatibilité : le thread occupé est contrôlé au dernier moment, juste avant tout spawn.
Les 178 tests ciblés élargis sont verts. Je vérifie maintenant les contrats TypeScript et les garde-fous statiques avant la suite complète.
Les typechecks et garde-fous statiques sont verts. Je lance l’unique suite complète locale ; si elle reste verte, je commite, pousse, réponds aux deux threads Codex et redemande la review obligatoire.
La suite complète a isolé un unique test sans rapport fonctionnel qui utilisait lui aussi escalate: true pour tester la résolution d’un fournisseur. Depuis que l’escalade court-circuite correctement tout spawn, ce montage ne peut plus atteindre la résolution fournisseur ; je corrige le scénario pour tester son objectif réel, puis je relance ce fichier et la suite complète.
J’ai ajouté deux garde-fous supplémentaires : conservation du message utilisateur lorsqu’un plan bloque le worker, et absence d’incrément du compteur lors d’un rejet de capacité. Les 255 tests concernés passent ; je fais la dernière passe complète incluant ces nouvelles régressions.
La passe finale est verte : 10 102 tests réussis, 5 ignorés. Je vérifie maintenant que la branche est toujours basée sur le main cible actuel, puis je crée le commit signé et le pousse sur la PR #1665.
Le commit est poussé. Je réponds maintenant précisément aux deux commentaires inline, je résous leurs threads, puis j’audite de nouveau toutes les conversations ouvertes avant de relancer les reviewers.
L’audit a découvert un troisième thread Codex, plus récent et non résolu : le header de thread peut encore fuiter dans la configuration MCP globale, où aucun UUID de thread canonique n’existe. Le constat est valide ; je le reproduis par un test ciblé avant de corriger syncGlobal, puis je repousse une dernière fois.
Le test échouait exactement comme prévu. Le correctif copie les headers, supprime systématiquement X-Tamag0-Thread-Id pour l’installation globale, puis résout seulement la clé API ; les configurations runtime par thread restent inchangées.
Les 19 tests de bootstrap et le typecheck passent. Comme ce dernier correctif touche un chemin global, je refais la passe complète finale avant le second commit ; je ne laisserai pas un correctif de review moins validé que le reste.
La dernière passe complète est verte : 10 103 tests réussis, 5 ignorés. Je crée le commit de correction du bootstrap global, le pousse, réponds au thread restant et relance l’audit/review sur le nouveau HEAD.
Tous les threads inline sont maintenant résolus. Je publie la synthèse du nouveau HEAD, déclenche une nouvelle review Codex et redemande la /parallel-code-review aux trois reviewers sur ce commit exact.
La review parallèle est relancée sur le HEAD exact f34584e9b87ca05af08a9e27aa13b28a99b4d89e. Les trois jobs CI tournent ; je les surveille avec les nouveaux verdicts, toujours sans merger.
La re-review a trouvé un dernier trou réel, convergent chez les reviewers : le compteur est encore incrémenté avant la détection « aucun fournisseur utilisable », alors que ce chemin laisse le message non ACKé. Je corrige test-first pour que seul un worker réellement démarré consomme un round, puis je relance la review. |