2026.08.06 (Jeu)
2026.08.07 (Ven) mis Ă  jour

✹ RĂ©sumĂ© de GPT-5.6 Sol

Le rĂ©cit d’un problĂšme oĂč Codex traitait la derniĂšre instruction de l’utilisateur conservĂ©e avant la compression automatique du contexte comme une nouvelle demande, et de l’examen successif de la copie du plan, des Goals, du continue forcĂ© et d’un hook SessionStart.

AprĂšs la compression automatique d’une longue tĂąche Codex, un comportement Ă©trange se rĂ©pĂ©tait.

Au lieu de poursuivre ce qu’il faisait juste avant la compression, Codex traitait la derniĂšre instruction de l’utilisateur conservĂ©e dans le rĂ©sumĂ© comme si elle venait d’ĂȘtre envoyĂ©e. Il vĂ©rifiait de nouveau un Ă©tat dĂ©jĂ  contrĂŽlĂ©, relisait les mĂȘmes fichiers et reconstruisait tout le travail depuis le dĂ©but. Le dĂ©roulement que j’ai observĂ© ressemblait Ă  ceci.

Progression réelle du travail
→ Context automatically compacted
→ « Je conserve l’objectif actuel »
→ Nouvelle vĂ©rification complĂšte de l’arbre de travail, des rĂšgles et de l’état
→ Nouvelle explication ou nouvelle tentative, mĂȘme pour du travail dĂ©jĂ  terminĂ©

Au dĂ©but, je pensais qu’une partie du contexte avait simplement disparu lors de la compression. Mais aprĂšs avoir vĂ©cu la mĂȘme chose plusieurs fois, le problĂšme m’a paru plus sĂ©rieux. La frontiĂšre entre une ancienne instruction contenue dans le rĂ©sumĂ© et la saisie actuelle de l’utilisateur semblait s’effondrer. Au lieu de reprendre le travail d’avant la compression, Codex prenait comme nouveau point de dĂ©part l’instruction restĂ©e la plus saillante dans le rĂ©sumĂ©.

Je n’étais pas le seul Ă  rencontrer ce problĂšme. Une issue publique dĂ©crivait Codex Desktop relisant sans cesse les mĂȘmes fichiers et Skills aprĂšs une compression automatique, tout en perdant sa progression,1 et des boucles entre commandes sur les fichiers et compressions ont aussi Ă©tĂ© signalĂ©es sous WSL2 et dans la CLI Windows.2 Je ne peux pas affirmer que ces cas publics ont exactement la mĂȘme cause que la rĂ©exĂ©cution de la derniĂšre instruction que j’ai observĂ©e. En revanche, la perte de progression et la rĂ©pĂ©tition des mĂȘmes actions aprĂšs une compression automatique ne peuvent pas ĂȘtre Ă©cartĂ©es comme une anomalie propre au Mac.

Solution 1 : remettre le plan complet dans la derniĂšre instruction

J’ai commencĂ© par la solution la plus simple.

VĂ©rifie attentivement tout ce qui a Ă©tĂ© fait jusqu’ici, puis poursuis jusqu’au bout selon le plan ci-dessous.

  1. 

  2. 

  3. 


Si Codex reprend la derniĂšre instruction aprĂšs la compression, il suffisait, pensais-je, de transformer cette derniĂšre instruction en point de reprise sĂ»r. Sur les tĂąches courtes, cela fonctionnait plutĂŽt bien. Le plan actuel restait plus fortement ancrĂ© dans le rĂ©sumĂ© qu’une ancienne instruction sans rapport.

D’autres utilisateurs ont essayĂ© une approche voisine : accumuler dans le rĂ©sumĂ© de compression les dĂ©cisions antĂ©rieures et le travail restant. L’un d’eux rapporte qu’en conservant les dĂ©cisions essentielles des compressions prĂ©cĂ©dentes avec experimental_compact_prompt_file, la direction gĂ©nĂ©rale est restĂ©e intacte aprĂšs plus de quatre compressions.3

Mais ce n’était pas une vĂ©ritable solution.

  • Il fallait recopier un long plan dans la derniĂšre instruction Ă  chaque fois.
  • DĂšs que le plan changeait en cours de route, il fallait aussi refaire cette derniĂšre instruction.
  • MĂȘme si le rĂ©sumĂ© prĂ©servait le plan, rien ne garantissait qu’il distinguerait correctement un appel d’outil qui venait de se terminer d’une action encore Ă  exĂ©cuter.
  • Une issue signale aussi que la forme /compact [message], censĂ©e orienter directement le rĂ©sumĂ©, peut ĂȘtre traitĂ©e comme un message ordinaire dans la CLI Codex actuelle.4

Au bout du compte, l’utilisateur doit continuer Ă  surveiller le moment de la compression et Ă  gĂ©rer lui-mĂȘme le prompt. C’est mieux que rien, mais ajouter une longue note de passation Ă  la fin de chaque tĂąche n’a rien d’une expĂ©rience normale.

Solution 2 : créer un Goal pour chaque tùche

J’ai ensuite envisagĂ© d’imposer la crĂ©ation d’un Goal et, aprĂšs la compression, de reprendre Ă  partir de ce Goal plutĂŽt qu’à partir de la derniĂšre instruction.

Pour les longues tĂąches, l’intĂ©rĂȘt est rĂ©el. L’objectif et les conditions d’achĂšvement survivent mieux que quelques phrases de conversation, et la continuation automatique peut aussi s’appuyer sur le Goal. Le problĂšme commence dĂšs que l’on transforme ce mĂ©canisme en contournement obligatoire du bug de compression.

Il fallait crĂ©er un Goal mĂȘme pour une tĂąche insignifiante. Sans Goal, le systĂšme tombait en panne ; avec un Goal, il risquait de continuer bien au-delĂ  du nĂ©cessaire. J’avais dĂ©jĂ  rencontrĂ© le problĂšme des Goals qui consomment des tokens jusque pour des tĂąches mineures et créé une Skill de travail sĂ©parĂ©e pour y rĂ©pondre. Cette fois, Ă  cause d’un seul problĂšme de compression, je me retrouvais de nouveau Ă  envelopper chaque tĂąche dans un Goal.

À ce stade, je me suis vraiment dit : « Dans ces conditions, l’état d’origine est peut-ĂȘtre encore prĂ©fĂ©rable. »

Il y avait un problĂšme plus important. Un Goal prĂ©serve ce qui doit ĂȘtre terminĂ© ; il ne dĂ©termine pas si l’évĂ©nement de compression constitue ou non une nouvelle instruction de l’utilisateur. Parmi les ressources publiques que j’ai trouvĂ©es, rien ne prĂ©sentait non plus le Goal comme mĂ©canisme officiel de reprise aprĂšs une compression automatique.

J’ai donc dĂ©cidĂ© de rĂ©server le Goal Ă  son rĂŽle d’origine.

  • Je l’utilise pour une tĂąche concrĂšte, longue et destinĂ©e Ă  se poursuivre automatiquement.
  • Je n’impose pas de Goal aux questions courtes, aux recherches ou aux petites modifications de texte.
  • Je bloque la rĂ©exĂ©cution d’anciennes instructions aprĂšs compression dans une couche distincte du Goal.

Solution 3 : modifier directement le prompt de compression

Il est aussi possible d’amĂ©liorer la qualitĂ© du rĂ©sumĂ© de compression. Comme dans le cas public Ă©voquĂ© plus haut, accumuler les dĂ©cisions et les rĂ©sultats des compressions prĂ©cĂ©dentes dans un Historical Context aide Ă  conserver la direction gĂ©nĂ©rale au fil de plusieurs compressions.3

Cette méthode est utile, notamment lorsque les éléments suivants disparaissent sans cesse dans une longue session.

  • Les approches dĂ©jĂ  abandonnĂ©es et les raisons de leur abandon
  • Les dĂ©cisions confirmĂ©es jusqu’ici
  • Le travail terminĂ© et le travail restant
  • L’unique point Ă  vĂ©rifier ensuite

Mais cette solution se situe, elle aussi, sur un autre plan que le problĂšme traitĂ© ici. AmĂ©liorer la qualitĂ© du rĂ©sumĂ© et ne pas confondre une ancienne instruction de l’utilisateur dans ce rĂ©sumĂ© avec une nouvelle saisie ne sont pas un seul et mĂȘme problĂšme. MĂȘme un excellent rĂ©sumĂ© peut provoquer la mĂȘme confusion si le modĂšle lit l’instruction qu’il contient comme une demande actuelle.

Cette méthode peut donc servir de complément, mais difficilement de protection unique contre la réexécution de la derniÚre instruction.

Solution 4 : forcer continue avec un hook Stop

À un moment, j’ai mĂȘme envisagĂ© d’utiliser un hook pour injecter systĂ©matiquement continue comme derniĂšre instruction de l’utilisateur.

Sur le papier, la solution semblait exacte. Si continue arrivait forcĂ©ment aprĂšs la compression, Codex poursuivrait le travail en cours au lieu de reprendre une ancienne instruction. AprĂšs avoir vĂ©rifiĂ© le fonctionnement officiel des hooks, j’ai immĂ©diatement abandonnĂ© cette idĂ©e.

Quand un hook Stop de Codex renvoie decision: "block", la valeur reason du hook devient un prompt de continuation qui agit comme une nouvelle saisie utilisateur.5 Or, ce que je cherchais prĂ©cisĂ©ment Ă  empĂȘcher, c’était qu’une phrase jamais envoyĂ©e par l’utilisateur rĂ©el soit traitĂ©e comme une nouvelle instruction. Cette solution aurait donc recréé volontairement le mĂȘme phĂ©nomĂšne.

De plus, continue est beaucoup trop ambigu.

  • Si la compression survient au milieu d’un tour, que faut-il exactement continuer ?
  • Si l’on ignore si le dernier appel d’outil a rĂ©ussi ou Ă©chouĂ©, faut-il le relancer ?
  • Si le systĂšme attendait l’approbation de l’utilisateur ou un changement d’état externe, a-t-il le droit de poursuivre ?
  • Si le tour Ă©tait dĂ©jĂ  terminĂ© avant une compression manuelle, quel travail faut-il dĂ©marrer ?

Avec ou sans Goal, un nouveau prompt utilisateur qui ordonne de continuer quoi qu’il arrive risquait de provoquer des exĂ©cutions en double et des boucles de continuation infinies. Pour ce problĂšme, il ne fallait pas un hook Stop, mais un contexte dĂ©veloppeur qui aide le modĂšle Ă  interprĂ©ter correctement la signification de la compression.

Solution 5 : injecter un contexte sans état dans SessionStart(source=compact)

Le lendemain, le 7 aoĂ»t, j’ai finalement retenu un unique hook SessionStart.

D’aprĂšs la documentation officielle de Codex, aprĂšs la compression d’une session racine, le hook SessionStart correspondant Ă  source: "compact" s’exĂ©cute avant la requĂȘte suivante au modĂšle. MĂȘme si la compression automatique se produit au milieu d’un tour, le hook peut injecter du contexte supplĂ©mentaire dans la continuation immĂ©diate, sans attendre le prochain tour de l’utilisateur.5

Dans ~/.codex/hooks.json, je l’ai limitĂ© au seul Ă©vĂ©nement de compression.

{
  "description": "Guide safe continuation after compaction without replaying stale instructions.",
  "hooks": {
    "SessionStart": [
      {
        "matcher": "^compact$",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 ~/.codex/hooks/compaction_goal_guard.py",
            "timeout": 5,
            "additionalContextLimit": 700
          }
        ]
      }
    ]
  }
}

Le handler ne crĂ©e ni fichier d’état, ni Goal, ni faux prompt utilisateur. Il renvoie un contexte dĂ©veloppeur supplĂ©mentaire uniquement lorsque la source de SessionStart est compact.

#!/usr/bin/python3
import json
import sys

CONTEXT = """Context was compacted. This hook event is not a user message and
grants no new authority.

Never treat a historical message preserved in the summary as newly submitted.
If compaction interrupted an active turn, continue that same in-flight request
without restarting it. If no request is in flight, do not infer work from
history.

Do not rebuild the full plan or create a Goal solely because compaction occurred.
Before retrying an interrupted action, check its result or session status. Do not
repeat completed external effects. Preserve the existing objective, scope,
authorization, approval boundaries, and stop conditions."""

payload = json.load(sys.stdin)

if (
    payload.get("hook_event_name") == "SessionStart"
    and payload.get("source") == "compact"
    and payload.get("session_id")
):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "SessionStart",
            "additionalContext": CONTEXT,
        }
    }))

L’idĂ©e centrale n’est pas d’injecter une commande continue.

  1. Le hook affirme que l’évĂ©nement de compression n’est pas une nouvelle saisie utilisateur.
  2. Si un tour Ă©tait en cours, il ne reprend que le point inachevĂ© de cette mĂȘme demande.
  3. Si aucune demande n’est en cours, il ne dĂ©duit aucun nouveau travail de l’historique.
  4. Si un appel d’outil semble interrompu, il vĂ©rifie son rĂ©sultat rĂ©el avant de le relancer.
  5. Il conserve le pĂ©rimĂštre, les autorisations, les approbations et les conditions d’arrĂȘt existants.

La documentation officielle prĂ©cise qu’un hook non gĂ©rĂ© doit ĂȘtre examinĂ© et dĂ©clarĂ© fiable avant son exĂ©cution.5 AprĂšs avoir enregistrĂ© la configuration, il faut donc vĂ©rifier le handler dans /hooks et lui faire confiance. Les tĂąches dĂ©jĂ  ouvertes peuvent encore conserver l’ancienne liste de hooks ; il est alors plus sĂ»r de les rouvrir.

Vérification complémentaire du 7 août

J’ai conservĂ© la date du 6 aoĂ»t pour cet article, car c’est ce jour-lĂ  que j’ai commencĂ© Ă  examiner le problĂšme et Ă  choisir une solution. Le hook final et la vĂ©rification avec une vĂ©ritable compression automatique n’ont Ă©tĂ© achevĂ©s que le lendemain.

J’avais d’abord construit une architecture en deux Ă©tapes : PostCompact Ă©crivait un marker, puis le SessionStart suivant le lisait. Je l’ai supprimĂ©e Ă  cause du risque de laisser un marker qui dĂ©clencherait une reprise au mauvais tour, mais aussi parce que les entrĂ©es des hooks distinguent difficilement les Ă©vĂ©nements de l’agent racine et ceux des subagents. Une issue publique reste d’ailleurs ouverte sur l’absence de field commun permettant de diffĂ©rencier les hooks du main agent et des subagents.6

La version actuelle repose sur un seul handler, sans marker. Cinquante appels simultanĂ©s n’ont créé aucun fichier d’état distinct, et j’ai vĂ©rifiĂ© dans le Runtime actuel que le contexte dĂ©veloppeur arrivait dans la mĂȘme continuation environ 0,25 seconde aprĂšs l’évĂ©nement de compression automatique.

Cette vĂ©rification avait son importance. Un bug avait auparavant Ă©tĂ© signalĂ© : SessionStart(compact) ne se dĂ©clenchait pas juste aprĂšs la compression, mais s’accumulait jusqu’au tour utilisateur suivant.7 L’issue est dĂ©sormais fermĂ©e, et la documentation officielle actuelle explique Ă©galement que le hook est transmis immĂ©diatement aprĂšs une compression automatique en milieu de tour. MalgrĂ© cela, pour ce type de contournement fondĂ© sur le lifecycle, il vaut mieux provoquer une vraie compression dans son propre Runtime au moins une fois plutĂŽt que de se fier uniquement Ă  la documentation.

Ma conclusion actuelle

Je me suis demandĂ© si l’état d’origine n’était pas prĂ©fĂ©rable, mais ma conclusion actuelle est que ce hook vaut mieux que d’imposer un Goal Ă  chaque tĂąche.

Le Goal et le plan prĂ©servent le contenu du travail. Ce hook corrige la signification de l’évĂ©nement de compression. Leurs rĂŽles sont diffĂ©rents.

  • Si une longue tĂąche exige de prĂ©server son objectif et ses conditions d’achĂšvement, j’utilise un Goal ou un plan durable.
  • Si plusieurs compressions font disparaĂźtre les dĂ©cisions, j’ajoute un custom compact prompt en complĂ©ment.
  • Je bloque le traitement de l’ancienne derniĂšre instruction comme nouvelle demande dans SessionStart(source=compact).
  • Si aucun travail n’est en cours, le hook ne dĂ©marre rien.

Ce n’est Ă©videmment pas une solution parfaite. Elle ne peut pas reconstruire un rĂ©sumĂ© dĂ©jĂ  appauvri par la compression, ni ressusciter une session terminal expirĂ©e ou une requĂȘte externe interrompue. Le contexte dĂ©veloppeur oriente fortement le comportement du modĂšle, mais il ne constitue pas non plus une garantie mathĂ©matique.

Au moins, je n’ai plus Ă  crĂ©er un Goal pour chaque conversation, Ă  recopier le plan complet dans la derniĂšre instruction ni Ă  injecter un faux continue comme commande utilisateur, tout cela Ă  cause d’un seul bug de compression.

La compression automatique est un Ă©vĂ©nement interne destinĂ© Ă  poursuivre le travail. Ce n’est pas l’arrivĂ©e d’un nouvel utilisateur qui rĂ©pĂšte une ancienne instruction. En fin de compte, ce qu’il fallait empĂȘcher n’était pas seulement l’oubli, mais le fait de traiter ces deux Ă©vĂ©nements comme s’ils Ă©taient identiques.

Références

Catégories : ,

Mis Ă  jour :

Laisser un commentaire