diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 0865d76..8ccc8b0 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,7 +12,7 @@ "name": "flux", "source": "./plugins/flux", "description": "Ciclo de trabalho agêntico para qualquer repo Git: probe, refine, issue, build, peek, review, iterate, land e reply, o chain que os encadeia, mais o map e o equip, que preparam a máquina e o repo. Global e context-agnóstico, com o que é específico do seu time vindo de um manifesto, nunca hardcoded.", - "version": "1.45.2" + "version": "1.46.0" } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 0865d76..8ccc8b0 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -12,7 +12,7 @@ "name": "flux", "source": "./plugins/flux", "description": "Ciclo de trabalho agêntico para qualquer repo Git: probe, refine, issue, build, peek, review, iterate, land e reply, o chain que os encadeia, mais o map e o equip, que preparam a máquina e o repo. Global e context-agnóstico, com o que é específico do seu time vindo de um manifesto, nunca hardcoded.", - "version": "1.45.2" + "version": "1.46.0" } ] } diff --git a/plugins/flux/.claude-plugin/plugin.json b/plugins/flux/.claude-plugin/plugin.json index 2505cf5..b9a3502 100644 --- a/plugins/flux/.claude-plugin/plugin.json +++ b/plugins/flux/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "flux", "description": "Ciclo de trabalho agêntico para qualquer repo Git: probe, refine, issue, build, peek, review, iterate, land e reply, o chain que os encadeia, mais o map e o equip, que preparam a máquina e o repo. Global e context-agnóstico, com o que é específico do seu time vindo de um manifesto, nunca hardcoded.", - "version": "1.45.2", + "version": "1.46.0", "author": { "name": "Gabriel Gripp", "email": "550632+grippado@users.noreply.github.com" diff --git a/plugins/flux/.codex-plugin/plugin.json b/plugins/flux/.codex-plugin/plugin.json index 5a9c678..0c1f53c 100644 --- a/plugins/flux/.codex-plugin/plugin.json +++ b/plugins/flux/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "flux", - "version": "1.45.2", + "version": "1.46.0", "description": "A complete, harness-neutral work cycle for Git repositories: probe, refine, issue, build, peek, review, iterate, land, and reply, chain to link them, plus map and equip, which prepare the machine and the repository.", "author": { "name": "Gabriel Gripp", diff --git a/plugins/flux/.cursor-plugin/plugin.json b/plugins/flux/.cursor-plugin/plugin.json index 2505cf5..b9a3502 100644 --- a/plugins/flux/.cursor-plugin/plugin.json +++ b/plugins/flux/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "flux", "description": "Ciclo de trabalho agêntico para qualquer repo Git: probe, refine, issue, build, peek, review, iterate, land e reply, o chain que os encadeia, mais o map e o equip, que preparam a máquina e o repo. Global e context-agnóstico, com o que é específico do seu time vindo de um manifesto, nunca hardcoded.", - "version": "1.45.2", + "version": "1.46.0", "author": { "name": "Gabriel Gripp", "email": "550632+grippado@users.noreply.github.com" diff --git a/plugins/flux/shared/codex-compat.md b/plugins/flux/shared/codex-compat.md index 717b1d3..0092abd 100644 --- a/plugins/flux/shared/codex-compat.md +++ b/plugins/flux/shared/codex-compat.md @@ -40,12 +40,42 @@ O resto do protocolo não muda e não é repetido aqui: vale ### Perguntas ao usuário -Este adaptador não define uma ferramenta de pergunta estruturada ao usuário, nem um contrato -verificado de entrega visual e retorno da escolha à mesma sessão. Portanto, no Codex o round-trip não -está comprovado: para todo GATE, a main apresenta o fallback numerado de -[`hitl.md`](hitl.md), com as opções e descrições completas aplicáveis, e espera uma escolha explícita -no chat antes de agir. A disponibilidade de uma ferramenta ou a aceitação técnica de uma solicitação -não muda esse caminho. Não delegar o gate a um subagente. +No Codex, a pergunta estruturada é a ferramenta nativa `request_user_input`. Ela só existe quando a +feature `default_mode_request_user_input` está ligada (`under development` e desligada por padrão no +Codex; ligar é configuração da máquina, não do flux). + +Quando `request_user_input` estiver disponível na sessão, a main a usa para todo GATE, com uma +question single-select por gate, e espera a escolha retornar à mesma sessão antes de agir. O gate +continua na main: não delegar a um subagente. O round-trip está comprovado para o Codex 0.161.0 ou +superior com a feature ligada; uma resposta com escolha na própria sessão é a confirmação de que ele +funciona ali, e não se exige prova adicional antes da primeira chamada. + +Round-trip comprovado em 2026-10-08, no Codex 0.161.0 com +`-c features.default_mode_request_user_input=true`: a ferramenta desenhou o menu com seta e descrição +por opção, e a escolha voltou à mesma sessão ("Questions 1/1 answered"). Esta comprovação vale para +essa versão com a feature ligada e se estende a versões posteriores até um caso em contrário. Não foi apurado o comportamento em `codex exec` headless nem com menus de 5 ou mais opções. + +Cai no fallback numerado de [`hitl.md`](hitl.md), com as opções e descrições completas e espera de +escolha explícita no chat, quando qualquer destas condições valer: + +- a ferramenta não está disponível na sessão (feature desligada ou versão anterior à 0.161.0); +- a chamada é aceita sem devolver escolha nenhuma (por exemplo, `{"accepted":true}` da variante + assíncrona): isso não comprova round-trip, e o gate cai no numerado na hora; +- a pergunta estruturada voltou vazia (`{"answers":{}}`) três vezes seguidas para o mesmo gate; +- o menu do gate não cabe na ferramenta ou a chamada é recusada: nunca truncar opção em silêncio; +- a sessão é headless (o bloco de preflight traz `gate_signal:`): vale o sinal `flux-gate/1` de + `hitl.md`, "Execução headless". + +**Resposta vazia não é recusa.** A chamada espera a pessoa por um limite fixo (cerca de 120 segundos +no Codex 0.161.0, sem chave de configuração conhecida; acompanhamento em +[openai/codex#37472](https://github.com/openai/codex/issues/37472)) e devolve `{"answers":{}}` quando ele expira. Isso +só diz que a pessoa ainda não respondeu: repetir a mesma pergunta estruturada, sem alterar texto nem +opções, até três vezes, e só então cair no numerado. Nenhuma ação acontece sem escolha explícita, em +nenhuma das tentativas. + +Quando cair no numerado porque a pergunta estruturada está ausente, foi aceita sem escolha ou voltou +vazia nas três tentativas, declarar `pergunta estruturada ausente` em `degradacoes:` +(`${FLUX_ROOT}/shared/preflight.md`, Passo 5). ### Adaptador de instruções de agente diff --git a/plugins/flux/shared/hitl.md b/plugins/flux/shared/hitl.md index a33f2db..4cd34a6 100644 --- a/plugins/flux/shared/hitl.md +++ b/plugins/flux/shared/hitl.md @@ -53,7 +53,12 @@ quando o round-trip estiver comprovado na sessão atual: a pergunta e as opçõe escolha dela retorna ao mesmo contexto principal. A mera disponibilidade de uma ferramenta, a aceitação técnica da chamada ou uma resposta sem escolha não comprovam esse round-trip. -- Use `AskUserQuestion` só quando esse round-trip estiver comprovado. +- Use `AskUserQuestion` (Claude Code, Cursor) ou `request_user_input` (Codex) só quando esse + round-trip estiver comprovado. A comprovação e as condições de queda de cada harness vivem em + `${FLUX_ROOT}/shared/codex-compat.md`, "Perguntas ao usuário". +- O Codex acrescenta sozinho a opção "None of the above" com campo de notas ao menu de + `request_user_input`. Não declará-la no menu: a saída inócua continua sendo a última opção + declarada. - Sem round-trip comprovado, use o fallback numerado abaixo, mesmo que a ferramenta esteja disponível ou tenha aceitado a solicitação. @@ -66,7 +71,7 @@ aceitação técnica da chamada ou uma resposta sem escolha não comprovam esse ## Fallback quando o round-trip não está comprovado -`AskUserQuestion` é um tool do harness, não uma garantia da linguagem nem da interface. Quando a +`AskUserQuestion` e `request_user_input` são tools do harness, não uma garantia da linguagem nem da interface. Quando a sessão não oferece o mecanismo ou o round-trip não está comprovado, o gate **não desaparece** — muda de forma: @@ -77,8 +82,11 @@ de forma: a recomendada por iniciativa própria, não interpretar silêncio, aceitação técnica da chamada ou retorno sem escolha como consentimento. Numa execução headless não há quem responda: antes de encerrar, gravar o sinal da seção "Execução headless" abaixo. -3. Declarar a degradação no banner de perfil, como qualquer `soft` ausente - (`${FLUX_ROOT}/shared/preflight.md`, Passo 5). +3. Declarar no banner a degradação correspondente ao motivo do fallback, conforme o contrato do + harness e a tabela de `${FLUX_ROOT}/shared/preflight.md`, Passo 5. No Codex, ferramenta estruturada + ausente, aceita sem escolha ou vazia nas três tentativas gera `pergunta estruturada ausente`; menu que não cabe + na ferramenta e sessão headless seguem o fallback numerado sem esse token, pois esses casos são + tratados como caminhos esperados pelo adaptador (`${FLUX_ROOT}/shared/codex-compat.md`, "Perguntas ao usuário"). > **A degradação é de forma, nunca de rigor.** Um gate que vira "escolhi a recomendada porque não > tinha como perguntar" é pior do que não ter gate nenhum: produz uma ação não autorizada com diff --git a/plugins/flux/shared/preflight.md b/plugins/flux/shared/preflight.md index 0232e0b..8158532 100644 --- a/plugins/flux/shared/preflight.md +++ b/plugins/flux/shared/preflight.md @@ -461,6 +461,7 @@ que o banner precisa ser. | `transporte mcp` | um canal com dois caminhos (`${FLUX_ROOT}/shared/api-first.md`) foi servido pelo MCP em vez da API — sai com o canal e o motivo em grafia fixa: `transporte mcp (linear: sem token \| token nao autentica \| alvo nao enxergado \| canario falhou \| leitura falhou \| opt-out)`. Caminho por API não gera token: é o default, e declarar o default é ruído | o elo que aplicou o contrato (`issue`, `build`, `land`) | | `versao ilegivel` | `FLUX_VERSION = unknown`: nenhum dos três manifests (`plugin.json`) foi lido com sucesso ou o campo `version` estava ausente — a linha `carimbo:` exibe `unknown` | o Passo 1a-harness | | `chain interrompido` | um elo do chain parou por falha, ou deixou decisão sem resposta, e os elos seguintes **não rodaram** — sai com o elo e o motivo, e acompanha o bloco de estado do chain. Usuário que escolhe a saída inócua de um gate **não** gera este token: é decisão, não interrupção | o `flux:chain` (`${FLUX_ROOT}/shared/chain.md`, "Falha no meio") | +| `pergunta estruturada ausente` | o harness é Codex, `request_user_input` não está disponível na sessão foi aceita sem escolha ou voltou vazia nas três tentativas, e o GATE caiu no menu numerado de `${FLUX_ROOT}/shared/hitl.md`; sai sem motivo adicional. Menu que não coube na ferramenta e sessão headless não geram token: o numerado ali é o contrato, não ausência | o elo que apresenta o gate (`${FLUX_ROOT}/shared/codex-compat.md`, "Perguntas ao usuário") | | `gate signal indisponivel` | o bloco `PREFLIGHT RESOLVIDO` da invocação traz `gate_signal: indisponivel ()`: a CLI não tem como receber o sinal de gate pendente nesta execução; sai com o motivo que o bloco trouxe, sem reescrever. Campo ausente **não** gera token | ainda não implementado por nenhum elo: quem passar a **ler** o bloco da invocação e puder parar num gate (`${FLUX_ROOT}/shared/hitl.md`, "Execução headless") | | `gate signal nao gravado` | o bloco traz `gate_signal: ` e a gravação do sinal falhou (diretório ausente, sem permissão, rename recusado): sai com o motivo literal do erro, e o gate ficou pendente sem sinal. Não bloqueia o elo | ainda não implementado por nenhum elo: o mesmo que lê `gate_signal` (`${FLUX_ROOT}/shared/hitl.md`, "Execução headless") |