Propostas Rejeitadas do OpenSpec: Uma Convenção de Memória de Decisão

Sem estado de rejeição. Veja a solução alternativa.

Conteúdo da página

Um agente que propôs e implementou “mover a persistência para uma biblioteca compartilhada” há seis meses propô-lo-á novamente no próximo trimestre, a menos que algo durável informe que a ideia já foi investigada e rejeitada — e o OpenSpec não possui estado embutido para isso atualmente.

/opsx:archive foi projetado para um único resultado: uma mudança que foi implementada. Ele sincroniza os deltas de especificação para openspec/specs/ e move a pasta para openspec/changes/archive/YYYY-MM-DD-<name>/ como registro do que mudou e por quê. Não há um /opsx:reject ou /opsx:abandon correspondente, e nada no formato de arquivamento informa a uma proposta futura que “essa ideia exata foi investigada e recusada”. Essa lacuna é mais crítica exatamente nos codebases onde o OpenSpec seria um bom ajuste: sistemas brownfield com um número pequeno de contribuidores e agentes que periodicamente reexploram as mesmas perguntas arquitetônicas — mesclar estes dois serviços, compartilhar esta camada de persistência, substituir esta fronteira HTTP por uma importação direta.

Um arquivo de decisões em camadas alimentando contexto de volta para uma nova proposta

Isso não é uma lacuna hipotética. Foi levantado diretamente com os próprios mantenedores do OpenSpec como uma solicitação de funcionalidade, e a forma como aquela conversa se desenrolou vale a pena conhecer antes de improvisar sua própria correção: o que o projeto concluiu na verdade molda qual convenção vale a pena adotar. Este guia passa por o que acontece se você depender apenas do /opsx:archive, a discussão real que já aconteceu no rastreador de problemas do OpenSpec e um padrão leve de decision.md que você pode adotar hoje sem esperar — ou precisar — de suporte no núcleo.

Por que Arquivar Sozinho Não Registra uma Decisão Rejeitada

Arquivar uma mudança que você decidiu não construir tecnicamente funciona — a pasta sai da sua lista ativa de qualquer forma. O problema é o que essa pasta arquivada falha em comunicar quando está ao lado de dezenas de mudanças implementadas:

  • Nenhum campo de status. Uma mudança arquivada parece idêntica, seja ela implementada ou abandonada três mensagens dentro de /opsx:propose. Um colega de equipe ou um agente varrendo openspec/changes/archive/ não pode distinguir a diferença sem abrir cada pasta de proposta e ler os artefatos dentro dela.
  • Nenhum sinal para verificar primeiro. Nada no fluxo de trabalho padrão instrui um agente a pesquisar o arquivo antes de rascunhar uma nova proposta. /opsx:propose rascunha a partir da sua solicitação atual e do estado do codebase, ponto final — ele não faz referência cruzada a mudanças rejeitadas anteriores, a menos que você o instrua a fazê-lo.
  • Deltas de especificação que você não deseja sincronizar. Se uma mudança rejeitada já tem deltas de especificação rascunhados e você a arquiva da maneira comum, /opsx:archive oferecerá para sincronizar esses deltas para openspec/specs/ primeiro. Aceitar essa oferta ensina às suas especificações canônicas a descrever um comportamento que você decidiu não construir, o que corrompe silenciosamente o registro do “o que o sistema faz atualmente” que toda outra proposta lê antes de planejar qualquer coisa.

Nada disso é um bug. /opsx:archive está fazendo exatamente o que a documentação diz que faz: completar uma mudança que foi implementada. O caso de rejeição fica fora desse escopo documentado propositalmente, e o próprio guia de fluxo de trabalho em equipe do OpenSpec é explícito que a maior parte do que recomenda — convenções de branch, ordem de revisão de PR, quando arquivar — é convenção empilhada sobre a ferramenta, não algo que o OpenSpec força por você. Lidar com uma rejeição é mais uma convenção que você pode definir por si mesmo, e o CLI já lhe dá a bandeira que você precisa para fazê-lo de forma limpa: passe --skip-specs quando arquivar uma mudança que não está sendo implementada, para que openspec archive investigate-shared-persistence --skip-specs arquivue a pasta sem tocar em openspec/specs/ de todo.

O que os Mantenedores do OpenSpec Decidiram Realmente sobre Suporte a ADR

Antes de inventar uma convenção interna, vale a pena ler como essa exata pergunta se desenrolou publicamente, porque a resolução é mais específica — e mais interessante — do que “não”. GitHub issue #557 abriu em janeiro de 2026 com uma solicitação de suporte de primeira classe para Architecture Decision Record: registros duráveis que persistem independentemente do ciclo de vida de qualquer mudança única, para que uma decisão rejeitada ou substituída permaneça visível para toda proposta futura. Um contribuidor até abriu uma pull request implementando isso.

O que se seguiu foram sete meses de ida e vinda genuinamente substanciais envolvendo o mantenedor principal Tabish Bidiwale (@TabishB) e vários membros da comunidade profundamente envolvidos, cobrindo registros imutáveis versus mutáveis, se um ADR pertence à fase de pesquisa ou à fase de design, propriedade entre mudanças quando uma decisão se desdobra em uma dúzia de mudanças posteriores, e como os ADRs se relacionam com as especificações como a descrição “autoritativa” do sistema. O enquadramento inicial de Tabish Bidiwale estabeleceu a direção em que a thread finalmente se assentou: o OpenSpec deve permanecer leve por padrão e tornar fluxos de trabalho especializados como ADRs configuráveis através do seu sistema de esquema em vez de incorporá-los ao núcleo. Um membro da comunidade resumiu posteriormente onde a discussão se assentou:

Fluxos de trabalho de ADR são valiosos, mas o OpenSpec não possui atualmente suporte de primeira classe/nativo para ADRs… a direção discutida aqui é manter o fluxo de trabalho padrão leve e tornar fluxos de trabalho especializados configuráveis.

O mantenedor Clay Good (@clay-good) fechou a issue nessa base em agosto de 2026 e moveu-a para GitHub Discussion #1553 para que a conversa pudesse continuar evoluindo sem permanecer aberta como um bug não resolvido. Essa é uma chamada razoável para uma ferramenta cuja única proposta é evitar a cerimônia estilo Spec Kit por padrão. Isso também significa que a correção vive um nível acima, em um de dois lugares:

  1. Um esquema da comunidade. O esquema spec-driven-with-adr, construído pelo conselheiro técnico do OpenSpec Hari Krishnan (@harikrishnan83) e documentado em intent-driven.dev, adiciona um quinto artefato ao pipeline padrão de quatro artefatos do OpenSpec. Ele existe porque o esquema padrão perde o raciocínio do design.md no momento em que uma mudança é arquivada — apenas os deltas de especificação são sincronizados para frente, para que o “porquê” por trás de uma decisão desapareça com a mudança, a menos que algo a preserve.
  2. Uma convenção ao nível do repositório. Um pequeno arquivo decision.md feito à mão mais uma regra de nomenclatura, que custa nada para adotar e não requer instalar um esquema personalizado.

O resto deste guia cobre a opção dois em profundidade, já que é o ponto de partida de menor atrito para a maioria das equipes — e, como a seção sobre o esquema da comunidade abaixo mostra, é compatível com a troca para aquela ferramenta mais pesada mais tarde, se seu log de rejeições crescer o suficiente para merecê-lo.

A Convenção de decision.md para Registrar uma Mudança Rejeitada

Estruture uma investigação rejeitada da mesma forma que você faria com uma implementada, mas pare antes de sincronizar qualquer delta, e adicione um arquivo que declara o resultado claramente:

openspec/
  changes/
    archive/
      2026-09-16-rejected-shared-persistence-layer/
        proposal.md
        decision.md

decision.md responde às mesmas quatro perguntas que um apropriado Architecture Decision Record faz — o que foi decidido, por quê, quais alternativas existiram e o que mudaria a resposta:

# Decisão

Status: Rejeitada

## Decisão

Não substituir a fronteira HTTP serviço-a-serviço por uma importação
de pacote direta entre os dois serviços Go.

## Razões

- Aumenta o acoplamento em tempo de compilação entre serviços implantados independentemente.
- Torna a camada de persistência um contrato implícito e não documentado.
- O benefício medido (latência, duplicação de código) era menor do que
  o custo de acoplamento neste codebase.

## Alternativas consideradas

- Módulo interno Go compartilhado — rejeitado pela mesma razão de acoplamento.
- gRPC em vez de HTTP — adiado, não rejeitado; reavaliar se a sobrecarga
  HTTP se tornar uma garrafa de gargalo medida.

## Reconsiderar apenas se

- Os dois serviços forem intencionalmente mesclados em uma única implantável, ou
- Medições de latência mostrarem que o salto HTTP é uma garrafa de gargalo comprovada.

## Relacionados

- Regra arquitetural: serviços comunicam via HTTP, não pacotes compartilhados.

A única regra dura que faz toda essa convenção funcionar: não execute a etapa de sincronização para uma mudança rejeitada. Se /opsx:propose já rascunhou deltas de especificação antes de você decidir contra a mudança, use a bandeira que o CLI já lhe dá para exatamente essa situação:

openspec archive investigate-shared-persistence --skip-specs

--skip-specs informa openspec archive para arquivar a mudança sem tocar em openspec/specs/ de todo, o que é o padrão mais seguro para qualquer coisa que você está arquivando sem implementar. Aceitar o prompt de sincronização comum em vez disso mesclaria os deltas de especificação da ideia rejeitada nas suas especificações canônicas, e as especificações canônicas openspec/specs/ devem descrever o que o sistema faz atualmente, não cada ideia que foi rascunhada e recusada. Se uma mudança permanentemente não produz mudanças de especificação por uma razão estrutural — uma pasta pura de investigação, digamos — o OpenSpec também suporta declarar skip_specs: true no .openspec.yaml dessa mudança para que ela arquivue limpo sem a bandeira toda vez.

Nomeando Mudanças Rejeitadas para Humanos e Agentes Podem Varrer o Arquivo

Um arquivo decision.md só ajuda se alguém abrir a pasta. Prefixe o nome da pasta com o resultado para que tanto um humano vasculhando ls openspec/changes/archive/ quanto um agente listando mudanças possam ver o status sem abrir um único arquivo:

2026-09-16-rejected-shared-persistence-layer/
2026-09-20-abandoned-react-router-migration/
2026-10-01-superseded-old-auth-design/
2026-10-10-add-project-filtering/          # implementada, nenhum prefixo necessário

Isso espelha o vocabulário de status já recomendado para registros de decisão autônomos — proposto, aceito, substituído, deprecado — aplicado ao próprio arquivo do OpenSpec em vez de uma pasta separada docs/decisions/. Mantenha o vocabulário pequeno. Três ou quatro prefixes consistentes superam uma linha de status em texto livre que cada proposta escreve ligeiramente diferente.

Como Fazer Seu Agente Verificar o Arquivo Antes de Propor Novamente

Nomear e um arquivo decision.md resolvem a descoberta para um humano vasculhando a pasta. Eles não fazem nada por si só para fazer um agente pesquisar o arquivo antes de rascunhar uma nova proposta — isso tem que ser uma instrução explícita, porque /opsx:propose não faz isso por padrão, e nenhuma quantidade de nomenclatura de arquivo organizada muda isso por si só.

Dois lugares para colocar essa instrução, correspondendo a como o OpenSpec já espera que orientações específicas do projeto sejam injetadas:

Em openspec/config.yaml, sob o campo context: que é injetado em cada solicitação de planejamento (cuidado com o limite de 50KB coberto no Quickstart do OpenSpec):

context: |
  Antes de propor uma mudança, pesquise openspec/changes/archive por pastas
  prefixadas com "rejected-" ou "abandoned-" que descrevem uma ideia
  materialmente semelhante. Se uma existir, resuma seu decision.md e declare o que
  mudou antes de propor a ideia novamente. Não reabrir uma decisão
  rejeitada sem novas evidências.

Em AGENTS.md ou nas instruções do próprio agente do seu projeto, como uma regra permanente em vez de um blob de contexto por solicitação:

## Mudanças do OpenSpec rejeitadas

Quando uma proposta é investigada e rejeitada:

1. Não sincronize nem aplique seus deltas de especificação.
2. Adicione `decision.md` com Status, Decisão, Razões, Alternativas
   consideradas, e Reconsiderar apenas se.
3. Prefixe o nome da pasta arquivada: `rejected-<name>` ou `abandoned-<name>`.
4. Antes de propor uma mudança materialmente semelhante, pesquise
   `openspec/changes/archive/` e refira a decisão anterior.
5. Não reabra uma decisão rejeitada a menos que suas condições
   documentadas de reconsideração tenham realmente mudado.
flowchart TD A[Nova ideia que vale uma mudança] --> B{Pesquisar openspec/changes/archive} B -->|Decisão rejeitada semelhante encontrada| C[Resumir decision.md anterior] C --> D{Condições de reconsideração mudaram?} D -->|Não| E[Não repropor. Referir a decisão.] D -->|Sim| F["/opsx:propose com o contexto alterado declarado"] B -->|Nada semelhante encontrado| F

Nenhuma instrução garante conformidade — um agente ainda pode pular a etapa de pesquisa, da mesma forma que pode pular a leitura de qualquer outro contexto que você injeta. Mas é a diferença entre “a informação existe em algum lugar no repositório” e “o agente é informado, toda vez, para ir procurá-la”, e apenas a segunda realmente reduz investigações repetidas na prática.

Um Exemplo Prático: Rejeitando uma Proposta, Depois Reconsiderando-a Corretamente

Junte as peças em um caso concreto. Digamos que um colega de equipe peça a um agente para olhar para substituir uma chamada HTTP serviço-a-serviço por uma importação direta de pacote Go, para reduzir a latência de rede.

  1. Explorar, depois propor. /opsx:explore lê ambos os serviços, e /opsx:propose replace-http-with-direct-import rascunha uma proposta, um documento de design ponderando o ganho de latência contra o custo de acoplamento, e uma especificação de delta rascunhada.
  2. Investigar e rejeitar. Após revisar o documento de design, a equipe decide que o custo de acoplamento — dois serviços implantados independentemente agora compartilhando uma dependência de tempo de compilação — supera um ganho de latência que ninguém mediu na verdade como um problema. Nada é construído.
  3. Arquivar sem sincronizar. Em vez de excluir a pasta, execute openspec archive replace-http-with-direct-import --skip-specs, depois adicione decision.md à pasta arquivada com Status: Rejected, as razões acima e uma cláusula Reconsider only if nomeando a condição que mudaria a resposta — por exemplo, “medições de latência mostram que o salto HTTP é uma garrafa de gargalo comprovada”. Renomeie a pasta com um prefixo rejected- para que leia como openspec/changes/archive/2026-09-16-rejected-replace-http-with-direct-import/.
  4. Meses depois, alguém levanta novamente. Um contribuidor diferente, ou o mesmo agente em uma sessão nova, é pedido para “acelerar a chamada do checkout para o inventário” e começa a rascunhar uma proposta que se parece muito com a mesma ideia. Porque openspec/config.yaml instrui o agente a pesquisar o arquivo primeiro, ele encontra a pasta rejeitada, lê decision.md e reporta de volta: “Uma mudança materialmente semelhante foi proposta e rejeitada em 16/09/2026 por razões de acoplamento. A condição de reconsideração era ‘medições de latência mostram que o salto HTTP é uma garrafa de gargalo comprovada.’ Você tem novas medições, ou este é um problema diferente?”
  5. A equipe fornece novas evidências. Se o profiling agora mostra que o salto HTTP genuinamente domina a latência do checkout, essa é exatamente a circunstância alterada que o decision.md original pediu. O agente prossegue com /opsx:propose, e o decision.md da nova proposta — uma vez que esta também for arquivada, aceita ou rejeitada — refere a anterior sob Related, para que o arquivo leia como um histórico de decisão contínuo em vez de duas pastas desconexas que acontecem de descrever a mesma ideia.

Esse quinto passo é o ponto inteiro da convenção. Sem isso, o passo 4 ou não acontece de todo — o agente apenas reinvestiga do zero — ou acontece por sorte, porque um humano se lembrou da conversa anterior. O arquivo decision.md e a instrução de pesquisa do arquivo transformam “alguém pode se lembrar” em algo que o fluxo de trabalho realmente verifica.

O Arquivo do OpenSpec vs. um Log de ADR Dedicado: Quem Posso o quê

Uma vez que você está mantendo arquivos decision.md dentro do arquivo, vale a pena ser explícito sobre qual artefato responde qual pergunta, para que a convenção não se torne silenciosamente documentação duplicada:

Artefato Responde
openspec/specs/ O que o sistema faz atualmente?
openspec/changes/<name>/ (ativo) O que estamos propondo mudar, agora mesmo?
openspec/changes/archive/<name>/ O que mudou (ou foi rejeitado) no passado, e por quê?
docs/adr/ (autônomo, neutro de ferramenta) Qual regra arquitetural durável aprendemos, independente de qualquer mudança única?

Para uma decisão estreita o suficiente para pertencer a uma investigação — “olhamos para compartilhar esta camada de persistência e dissemos não” — a convenção de decision.md-dentro-do-arquivo acima é suficiente. Para uma decisão que deve sobreviver e restringir muitas mudanças futuras — “serviços comunicam via HTTP, nunca pacotes compartilhados” — promova-a para um Architecture Decision Record autônomo em docs/adr/, e faça o decision.md da mudança rejeitada referenciá-lo sob Related. Essa separação mantém o arquivo do OpenSpec focado em investigações individuais, enquanto o log de ADRs segura o pequeno número de regras que devem sobreviver ao ciclo de vida de qualquer ferramenta única — incluindo uma futura migração do OpenSpec por completo.

Quando Adotar o Esquema spec-driven-with-adr em Vez

A convenção feita à mão acima custa nada e cabe em quinze minutos de configuração, o que a torna o padrão certo. Mas vale a pena entender o que a alternativa mais estruturada realmente faz antes de decidir que você superou um prefixo de nomenclatura.

spec-driven-with-adr insere um quinto artefato, adr, entre design e tasks no pipeline do OpenSpec. Em vez de escrever conteúdo de ADR diretamente na pasta da mudança, a etapa adr produz um manifesto de revisão local da mudança adr.md curto e, quando a mudança introduz um compromisso arquitetural genuinamente durável, um registro numerado na raiz do repositório — /adr/0042-use-postgres-for-catalog.md, irmão de openspec/, não aninhado dentro dele. Cada ADR que o esquema cria é imutável uma vez aceito: as próprias instruções do esquema chamam isso de “regra de ferro” — você nunca edita o status, corpo ou data de um registro aceito. Para mudar uma decisão anterior, você escreve um novo ADR cujo campo Supersedes: nomeia o antigo, e designs futuros percorrem essa cadeia de substituição para saber quais decisões ainda estão em vigor. Essa é uma versão mais rigorosa de exatamente a ideia de “reconsiderar apenas se” na convenção de decision.md acima, forçada pelo esquema em vez de deixada para um humano se lembrar de anotar.

Vale a pena ser preciso sobre o que esse esquema faz e não faz. Ele é construído para decisões que são aceitas e precisam sobreviver ao arquivamento — Postgres sobre DynamoDB, JWT sobre cookies de sessão — não para propostas que foram investigadas e rejeitadas sem implementar nada. Uma investigação rejeitada ainda não tem um lugar óbvio para viver sob este esquema também; você empilharia a mesma convenção de decision.md-e-nomenclatura deste guia sobre ela, apenas referenciando registros /adr/ em vez de uma pasta autônoma docs/adr/.

Recorra a ele uma vez que você perceba qualquer destes:

  • Seu contagem de decisões rejeitadas é grande o suficiente que grepping openspec/changes/archive/ por prefixes para de ser rápido.
  • Você deseja decisões arquiteturais duráveis validadas e referenciadas cruzadamente contra cada novo design automaticamente, em vez de por convenção e grep.
  • Vários contribuidores continuam inventando formatos ligeiramente diferentes de decision.md, e você deseja um esquema para forçar um formato numerado imutável único.

Instalar um esquema personalizado é um compromisso maior do que uma convenção de nomenclatura — ele muda o que /opsx:propose gera para cada mudança futura, não apenas as rejeitadas — então trate isso como um passo para cima uma vez que a versão leve estiver visivelmente sob pressão, não um movimento inicial padrão.

Conclusão

O arquivo do OpenSpec foi projetado ao redor de um único resultado — uma mudança que foi implementada — e seus próprios mantenedores foram explícitos, após uma discussão pública de sete meses, que suporte de rejeição ou ADR de primeira classe não está chegando ao fluxo de trabalho principal em breve. Isso deixa a correção onde o OpenSpec já coloca a maioria de suas convenções de equipe: no seu repositório, não na ferramenta. Um arquivo decision.md, a bandeira --skip-specs no arquivamento, um prefixo de nomenclatura rejected-/abandoned- e uma instrução explícita informando ao agente para pesquisar o arquivo antes de propor são suficientes para parar a maioria das investigações repetidas. Recorra ao esquema spec-driven-with-adr apenas uma vez que essa convenção leve estiver genuinamente sob pressão pelo número de decisões que você está rastreando — e mesmo assim, mantenha a distinção clara: ele gerencia decisões que você aceitou e deseja que sobrevivam ao arquivamento, não as que você recusou.

Subscrever

Receba novos artigos sobre sistemas, infraestrutura e engenharia de IA.