ADR 0092 — platform 以按需报告脚本的形式诞生,而不是 agent
状态
已接受。不改变 docs/fluxo.yml(ADR 0085)中 platform 角色的 status: planned 和 gate_saida——它的激活仍与 DEPLOY_ENABLED 同步,而后者并不存在。本文档只交付背后已有真实数据的那一半。
背景
docs/fluxo.yml(camada_plataforma)把 platform 描述为“SRE /
Platform — dono do loop de retorno”:它会从 Infra 领域接收 pipeline-verde,从 Arquiteto 接收 nfrs-mensuraveis,并返回 slo + dashboard + runbook、telemetria-consolidada 和 postmortem。status 是 planned,ativacao 写的是“sincronizada com DEPLOY_ENABLED”——一个今天产品中不存在的标志。没有带真实流量的生产环境,任何地方都没有定义数值 SLO,也没有真实的事故可供任何事后复盘分析。
产品所有者有意识地决定提前这一角色的一部分——不是作为 LLM agent,也不是作为受监督的进程(GenServer),那会在一个尚未闭合的循环上编造权力,而是作为今天已经可以诚实做到的东西:一个读取脚本,与 apps/api/scripts/medir-execucao.ts(FASE 13b)和 apps/api/scripts/validacao-gates.ts(FASE 15)同一模子。
数据已经存在。DomainGaugesCollector(apps/api/src/infrastructure/observability/domain-gauges.collector.ts)每隔 METRICS_GAUGE_INTERVAL_MS 运行一次,维护三个 Prometheus gauge:按项目的活动/closing 会话、按项目的被阻塞任务,以及最后一次备份的状态(backup_runs——年龄、状态、大小,始终是全局的,因为产品备份整个数据库,而不是按项目)。缺的不是指标:而是一种现在、针对特定项目、不用打开 Grafana 就能看这张快照的方式。
决策
apps/api/scripts/relatorio-telemetria.ts,通过 pnpm --filter api relatorio:telemetria [--projeto <uuid>] [--json] 调用。它提出与 DomainGaugesCollector 相同的三个问题——但作为一次性的、独立的读取,由请求者触发,打印后结束。它不是第二个收集器:不注册任何 gauge,不在 setInterval 中运行。
SQL 查询是复制的,而不是导入的。 收集器的方法(collectSessions/collectBlockedTasks/collectBackup)是私有的,并以写入 this.metrics.*.set(...) 结束——不存在一个可以抽取的“只有查询”的纯粹部分,而不让一个独立脚本耦合到一个只在模块内部才有意义的 NestJS @Injectable 的生命周期。复制一个读取查询(相同的表、相同的过滤条件)比打开这一依赖更便宜。
报告绝不编造它没有的东西。 每次输出都有两个固定小节:
- “Onde ver mais”——指向已经存在且版本化的东西的链接:
deploy/k8s/observability/dashboards/*.json中的三个仪表盘、deploy/k8s/observability/alerts/brabo-alerts.yaml中的告警、docs/runbook.md#observabilidade,以及用于启动本地可观测性的pnpm dev:obs。脚本链接,绝不复制这些文件的内容。 - “Não medido”——三个具名的缺口:正式的数值 SLO(产品中任何地方都没有定义——在这里编造一个,就是 ADR 0042 对模型评分、ADR 0077 对代码质量已经拒绝的同一个错误);事后复盘(依赖真实事故);以闭环回到产品的遥测(这个脚本观察并打印——它不自行决定或行动;正是这一点会让
platform变为active,而触发条件仍然缺失)。
docs/fluxo.yml 获得一条注记,而不是新状态。 platform 角色的 status: planned 不变,gate_saida: { id: operavel, status: planned } 也不变。输出 telemetria-consolidada 获得一个 nota 字段,说明手动/按需的版本已经是真实的(这个脚本),而自动/闭环的版本仍待 DEPLOY_ENABLED——这与 ADR 0077 已经对“recomendado”与“nota inventada”应用的纪律相同:存在的东西按存在来说,不存在的东西继续按不存在来说。
后果
开始存在的东西。 一种回答“这个项目现在怎么样”(会话、被阻塞的任务、备份)的方式,无需打开 Grafana,也无需等待下一次抓取——之所以有用,恰恰是因为 DomainGaugesCollector 已经把这些数据发布给 Prometheus,但以前没有人按需读取它。
保持原样的东西。 platform 仍然是 planned。没有新的 agent,没有 GenServer,没有数值 SLO,没有模拟的事后复盘。脚本不做决定也不行动——它只打印数据库中已有的内容。
查询的重复是一种选择,而不是疏忽。 相同的表(sessions、tasks/stories、backup_runs)通过两条不同的路径被读取两次——周期性的收集器和这个按需的脚本——如果其中一个改变而另一个没有跟进,两个查询可能随时间产生分歧。接受的代价小于为了复用私有代码而把一个 CLI 脚本耦合到 NestJS @Injectable 生命周期的代价。