Dev-phase destructive operations allowed (pre-launch only)

mandatory

Enquanto a Koder Stack está em fase pré-launch e o único usuário é a equipe Koder, operações destrutivas reais (DELETE de versão, DELETE de app, rename livre sem reservation) são **permitidas** em componentes do registry/storage, gated por flag explícita `KODER_HUB_DEV_DESTRUCTIVE=1`. Quando a Stack atinge a primeira release pública para usuários externos, este bypass **some**: yank/rename canônicos passam a ser as únicas operações disponíveis.

Dev-phase destructive operations allowed

Contexto

Em produção real (registries públicos), delete de pacote / version / slug é uma classe de risco autocontida — incidente left-pad (npm, 2016) demonstrou que tirar bytes do storage quebra cadeias de build globalmente sem aviso. Por isso o desenho canônico do Koder Hub é:

  • Versão errada → *ank*(esconde mas mantém bytes; lockfiles seguem

    funcionando).

  • Slug errado / rebrand → *ename via redirect*(slug velho fica

    reservado pra sempre).

  • App morto → *rchive*(status="archived", linha persiste).

*as*enquanto a Koder Stack só serve a equipe Koder internamente, muitas operações exigiriam delete real:

  • 20 apps placeholder com version=1.0.0, size=0: precisamos remover,

    não yankar.

  • Test data acumulada de khub publish em smoke tests: idem.
  • Slug experimental que nunca foi compartilhado externamente: rename

    livre faz mais sentido que reservation eterna.

  • Schema redesigns em desenvolvimento: drop de tabela inteira é normal.

A regra dura ("nunca delete real") não cabe nessa fase. Mas o desenho do código tem que *ntecipar*o dia em que cabe — sem isso, a transição pra produção pública vira uma série de retrabalhos.

Regra

R1 — Toda operação destrutiva fica gated por env flag

Handlers que fazem DELETE real devem checar os.Getenv("KODER_HUB_DEV_DESTRUCTIVE") == "1" antes de proceder. Default desligado. Resposta 403 Forbidden quando desligado, com mensagem mencionando o env var pra orientar o operador.

R2 — Yank/rename canônicos são always-on, idempotentes

Mesmo na fase pré-launch, os endpoints canônicos ficam ativos:

  • POST /api/v1/publish/{slug}/versions/{version}/yank
  • POST /api/v1/publish/{slug}/versions/{version}/unyank
  • PATCH /api/v1/publish/{slug}/rename

Eles existem desde o dia 1 e são a interface preferida. O bypass é a exceção, não o caminho padrão.

R3 — Bypass não dispensa logging

Operações destrutivas via bypass devem emitir um log estruturado claro (level=warn msg="dev-phase destructive op" path=<route> slug=<slug>) para que o histórico fique reconstrutível. Nenhuma operação destrutiva silenciosa.

R4 — Bypass expira automaticamente quando o gate cair

Quando a Koder Stack publicar primeira release pública para usuários externos, abrir ticket products/dev/hub/#NNN remove dev-phase bypass que (a) remove os checks devDestructiveAllowed() dos handlers, (b) substitui pelos handlers de yank/rename canônicos, (c) atualiza esta policy declarando que ela está aposentada. Não esperar que alguém lembre — a expiração é parte do critério de "go-live".

Aplicações concretas (Koder Hub, 20260504)

Endpoint Comportamento default Comportamento com KODER_HUB_DEV_DESTRUCTIVE=1
DELETE /api/v1/publish/{slug} 200 + status=archived (sem ?force=1) idem; com ?force=1: drop real
DELETE /api/v1/publish/{slug}/versions/{version} 403 Forbidden drop real da AppVersionRow
PATCH /api/v1/publish/{slug}/rename rename + cria reservation sempre cria reservation (sem mudança)
DELETE /api/v1/publish/{slug}/versions/{version} (não destrutivo) n/a n/a — yank é o caminho normal

Reservations só são deletáveis pelo bypass — em produção, append-only estrito.

Antipadrões

  • *ão*suprimir o bypass via "feature flag database-driven" — o flag

    é env var deliberadamente. Ele é uma posture de operação, não configuração de tenant.

  • *ão*usar o bypass como atalho de "consertar bug em produção" —

    ele é pra cleanup de dev/test data e migrações estruturais. Bug em produção real merece migration script auditável.

  • *ão*estender o bypass para outros componentes "por consistência" —

    cada registry/storage decide se quer usar ou não a tag, deliberada e per-componente.

Referências

  • Memória: feedback_dev_phase_destructive_allowed (decisão de 20260504

    do owner).

  • Companheira: policies/registry-naming.kmd — o que essa exceção

    preserva sob o nome de "imutabilidade".

  • Implementação: tickets

    products/dev/hub/backlog/done/106-yank-version-semantics.md, products/dev/hub/backlog/done/107-slug-immutability-rename-redirect.md.

Source: ../home/koder/dev/koder/meta/docs/stack/policies/dev-phase-destructive-allowed.kmd