Dev-phase destructive operations allowed (pre-launch only)
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 publishem 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}/yankPOST /api/v1/publish/{slug}/versions/{version}/unyankPATCH /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 20260504do owner).
- Companheira:
policies/registry-naming.kmd— o que essa exceçãopreserva 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.