ADR 0153 — DEPLOY_ENABLED:十份文档引用、却无人创建的触发器
状态
Proposed. 等待产品负责人对阶段 B(见下文)的决定,那不是工程选择。本文档本身不改变任何角色或 gate 的 status。
Context
DEPLOY_ENABLED 是平台层所缺的一切的声明触发器。它被十份文档引用:
- ADR 0091 和
ADR 0092,就在状态字段中——"its activation stays synchronized with
DEPLOY_ENABLED, which doesn't exist"; - ADR 0089;
docs/fluxo.yml,在三个地方——platform.ativacao(“sincronizada com DEPLOY_ENABLED”)、area-infra.gate_saida(deployavel,status: planned,带注释# DEPLOY_ENABLED)以及secops-runtime的两个缺口交付物(resposta-a-incidente和postmortem-de-seguranca,都带有motivo: exige tráfego de produção real (pós DEPLOY_ENABLED + platform ativo));docs/gates.yml,在 gateoperavel中(backlog: deploy (DEPLOY_ENABLED + Environments));- 以及
business-rules.md、backlog.md、modelo-de-time.md、auditoria-fluxo-vs-codigo.md、historico-de-fases.md和CLAUDE.md。
在代码中它出现两次,都是注释——
apps/api/src/domain/actions/external-effect.ts:144("Estes não têm ação
tipada ainda (DEPLOY_ENABLED está no backlog)")和
apps/engine/lib/engine/harness/tools/listar_metricas_de_produto.ex:185。
在 apps/、docker/、deploy/、scripts/、.env.example 或 Makefile 中没有任何定义,没有任何读取。
十份文档指向一个没有墙的开关。
卡在这上面的东西
| 被卡住的 | 位置 | 后果 |
|---|---|---|
platform | fluxo.yml,status: planned | 反馈回路的所有者不存在;SLO、仪表盘、runbook 和 postmortem 没有作者 |
gate operavel | gates.yml:312-321,status: planned | 已声明,从未被评估 |
gate deployavel | fluxo.yml:247 | 不在注册表中——见下面的发现 |
resposta-a-incidente 和 postmortem-de-seguranca | fluxo.yml:223-226 | secops-runtime(它是 status: active)的两个缺口交付物 |
一个与部署无关的发现
gate deployavel 在 docs/fluxo.yml:247 中被声明为 area-infra 的 gate_saida,却不存在于 docs/gates.yml 中。
ADR 0054 把注册表定义为声明现有 gate 的地方;一个被团队模型的一部分点名、却在另一部分缺席的 gate,正是 agent-areas.ts、gates.yml 和 fluxo.yml 之间的交叉测试会抓到的那种不一致——而这个测试是 CLAUDE.md 中声明的 backlog,今天并不存在。
这一点无需对部署做任何决定就能修复,也是本文档中唯一不依赖于有人购买服务器的部分。
决定次序的张力
在没有带真实流量的生产环境的情况下开启 flag,并不会激活这些角色:它会造出空通过的 gate。一个因为没有可拒绝的东西而批准的 gate,比一个声明为 planned 的 gate 更糟,因为它会教读者以为回路已经闭合。这与 ADR 0091 和 0092 拒绝创建 agent、转而交付按需脚本的理由相同——那次拒绝依然正确。
Decision
两个阶段,在属于工程的与不属于工程的之间有一条硬边界。
阶段 A——今天可以决定的,刻意地少
本次交付已经应用的: 只有第 1 项。它修复的是注册表的一个漏洞,不依赖关于部署的决定——这是本文档中即使阶段 B 永远不发生也有价值的唯一部分。第 2 和第 3 项描述接受所意味着的东西;第 3 项(十份文档的文字)没有被应用,因为在决定之前改写措辞就等于断言一个尚未做出的选择。
deployavel进入docs/gates.yml,带status: planned、dono: area-infra、severidade: warn和aprovacao_humana: false,完全按照operavel(gates.yml:312-321)的模子。关闭注册表的漏洞。用warn而不是block,因为注册表的规则(RN-070/RN-071)要求有verificacao: script才能提升,而没有脚本。- 两个提前交付的脚本不变。
apps/api/scripts/relatorio-telemetria.ts和apps/api/scripts/relatorio-seguranca-runtime.ts已经是active,已经按需运行,已经交付了 ADR 0091/0092 承诺的东西。 - 文字不再把
DEPLOY_ENABLED当作变量。 今天读到“与DEPLOY_ENABLED同步”的地方,改为点名这个表述一直以来所指的里程碑,并带有客观标准:存在一个被运营的目标环境,接收真实流量,其交付流水线有人维护。
阶段 B——被操作员阻塞,而不是被代码阻塞
一个带流量的生产环境。只有在那之后,platform 才离开 planned,operavel 和 deployavel 不再是 planned,secops-runtime 的两个缺口交付物才成为可能。
基础设施已经就绪,不是瓶颈:带有 local/staging/prod overlay 的 deploy/k8s/、自 ADR 0119 起按 digest 发布到 GHCR 的四个镜像、make imagens-do-release、bootstrap.sh、smoke.sh、rollout-test.sh 和 test-restore.sh。缺的是有人运行并维护——而这些都不会自己部署。
考虑过并被拒绝的备选方案
现在就创建 DEPLOY_ENABLED,作为默认 false 的环境变量。 这是第一版设计,而它得不偿失:一个没有任何代码读取的变量,正是本 ADR 存在所要揭露的空洞承诺,只是现在多了一行配置来支撑它。仓库已经有解决这一问题的规则——capability 只在被测试套件证明时才声明;没有证明,就声明 false 并降级(ADR 0041/0042)。flag 在拥有第一个真实消费者的 PR 中诞生,而那个 PR 就是阶段 B 的。
名字 DEPLOY_ENABLED 被保留:十份文档已经在用它,现在给这个里程碑改名只会把债务换个地方。
本 ADR 明确拒绝的
- 在 engine 中创建
platform或secops-runtimeagent。 那将是对一个不会闭合的回路凭空发明权威——ADR 0091/0092 的拒绝,因为问题会再次出现而在此重复。 - 在没有验证脚本的情况下把
operavel或deployavel提升为block(RN-070/RN-071)。 - 把只取决于环境的东西标记为“永远不测量”:MTTR 和变更失败率继续被声明为带理由的缺口,而不是不可能。
Consequences
- gate 注册表开始包含团队模型所点名的所有 gate——
deployavel不再只存在于一侧。 - 运行时没有任何变化。没有新变量,没有被修改的默认值,没有任何以前不被评估的 gate 被评估。
- 平台层仍然停滞,而现在它诚实地说出原因:缺的不是一个 flag,而是一个环境。读
fluxo.yml的人不再去代码里寻找一个从未存在过的变量。 - 记录在案的是,“已声明”与“存在”之间的距离已经付出过一次代价:十份文档彼此就一个机制达成了一致,而这种一致之所以存活,是因为没有任何东西把它们与代码交叉核对。团队模型三部分之间的交叉测试仍在 backlog 中,这是它本可以抓到的第二个发现。