From 4f9a17d41b1c12916f7b2d6f827320d6f8d07ca3 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 16 Jul 2026 13:00:23 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=81=9A=E5=90=88=E5=8F=AF=E8=A7=82?= =?UTF-8?q?=E6=B5=8B=E6=80=A7=E6=94=B6=E5=8F=A3=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/unidesk-otel/SKILL.md | 3 + config/platform-infra/observability.yaml | 1 + .../R5_Task_Report.md | 25 +++++ .../MDTODO/observability-trace-reliability.md | 4 + docs/reference/observability.md | 5 + .../src/platform-infra-observability.test.ts | 36 ++++++- .../platform-infra-observability/closeout.ts | 102 ++++++++++++++++++ .../platform-infra-observability/config.ts | 3 +- .../src/platform-infra-observability/index.ts | 1 + .../platform-infra-observability/options.ts | 43 +++++++- .../src/platform-infra-observability/types.ts | 8 +- scripts/src/platform-infra/entry.ts | 1 + 12 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 docs/MDTODO/details/observability-trace-reliability/R5_Task_Report.md create mode 100644 scripts/src/platform-infra-observability/closeout.ts diff --git a/.agents/skills/unidesk-otel/SKILL.md b/.agents/skills/unidesk-otel/SKILL.md index cf3dce2c..8ea223e8 100644 --- a/.agents/skills/unidesk-otel/SKILL.md +++ b/.agents/skills/unidesk-otel/SKILL.md @@ -11,12 +11,15 @@ Skill(cli-spec)。用于 UniDesk OpenTelemetry/Tempo 链路追踪运维、按 tr ```bash bun scripts/cli.ts platform-infra observability status +bun scripts/cli.ts platform-infra observability closeout --target [--query ]... [--trace-id ] [--service ] bun scripts/cli.ts platform-infra observability trace bun scripts/cli.ts platform-infra observability search --grep --limit 20 ``` 基本状态、trace 查询、噪声压制、读取窗口与乱序调查见 [references/traces.md](references/traces.md);业务 trace 映射、Code Agent/AgentRun 排障、codex-stdio 追穿和何时先改进 OTel 见 [references/code-agent.md](references/code-agent.md)。 +同类运行面复核优先用 `closeout` 一次汇总 status、validate 及显式选择的 PromQL/trace;子检查失败固定为 non-blocking warning,单项细节再走原命令下钻。 + ## P0 边界 - 微服务契约版本、源码/镜像版本和 `commitId` 漂移只能记录为非阻塞 warning;不得建议或实现线上 admission、dispatch、readiness 或用户业务门禁。业务已成功时,漂移告警不能改写成功结论。 diff --git a/config/platform-infra/observability.yaml b/config/platform-infra/observability.yaml index 1c4e3dd8..a1967086 100644 --- a/config/platform-infra/observability.yaml +++ b/config/platform-infra/observability.yaml @@ -105,6 +105,7 @@ metricsBackend: timeoutSeconds: 15 maxSeries: 200 maxResponseBytes: 262144 + maxCloseoutQueries: 8 sampling: mode: parentbased_traceidratio diff --git a/docs/MDTODO/details/observability-trace-reliability/R5_Task_Report.md b/docs/MDTODO/details/observability-trace-reliability/R5_Task_Report.md new file mode 100644 index 00000000..4e62e35e --- /dev/null +++ b/docs/MDTODO/details/observability-trace-reliability/R5_Task_Report.md @@ -0,0 +1,25 @@ +# R5 任务报告 + +## 目标 + +完成 [UniDesk #2322](https://github.com/pikasTech/unidesk/issues/2322),提供 YAML/参数驱动的 `platform-infra observability closeout` 只读聚合入口,减少同类运行面复核的重复 CLI 调用。 + +## 实现 + +- 固定聚合现有 `status` 与 `validate`,不复制 Kubernetes、Tempo 或 Prometheus 访问实现。 +- 支持可重复 `--query `、可选 `--trace-id ` 与 `--service `。 +- 所有子操作并发执行;聚合结果固定 `mutation=false`、`blocking=false`,可选证据失败只输出 non-blocking warning。 +- 默认输出有界表格,`--full`/`--raw` 返回脱敏结构;命令与参数未写死产品、namespace 或 instance。 +- query fan-out 上限由 `config/platform-infra/observability.yaml#metricsBackend.query.maxCloseoutQueries` 控制,并在远端访问前校验。 +- 同步更新 `unidesk-otel` skill 与 observability 长期参考。 + +## 验证 + +- `bun test scripts/src/platform-infra-observability.test.ts`:10 pass,0 fail。 +- `bun scripts/cli.ts platform-infra observability closeout --help`:scoped help 正常,无 PikaOA 产品硬编码。 +- NC01 真实只读 smoke 以一次 CLI 调用汇总 status、validate、两项 readiness PromQL 与已有 trace,5 项检查全部 `ok=true`,输出 `mutation=false blocking=false`。 +- `git diff --check` 通过。 + +## 边界 + +未修改公共运行面、Prometheus/Tempo/Collector、scrape/relabel、CI/CD 或业务 readiness 门禁;PR 仅创建,不合并。 diff --git a/docs/MDTODO/observability-trace-reliability.md b/docs/MDTODO/observability-trace-reliability.md index 113aca61..d9fb298c 100644 --- a/docs/MDTODO/observability-trace-reliability.md +++ b/docs/MDTODO/observability-trace-reliability.md @@ -110,3 +110,7 @@ ##### R4.4.2.10 [in_progress] 执行 [HWLAB #2538](https://github.com/pikasTech/HWLAB/issues/2538) P0 架构纠偏:确认 PR #2540 把已工作的 agentrun.event.v1→hwlab.event.v1→实时 SSE/Kafka retention 回放错误替换为强制 PostgreSQL transactional projector,构成重大架构偏移;从最新 v0.3 分析偏移提交并创建新的精确纠偏提交,恢复 direct publish、live Kafka SSE、Kafka refresh replay 及其单一 Kafka authority,删除强制 v8 schema 启动门禁和数据库迁移依赖,保留 canonical duration 修复、feature-config schema、HTTP fallback 禁令及其他无关改动;通过新 PR 和自动 PaC/GitOps/Argo 上线后校对实时、回放、terminal/final 与 canonical duration,完成任务后将详细报告写入[任务报告](./details/observability-trace-reliability/R4.4.2.10_Task_Report.md)。 + +## R5 [completed] + +实现 [UniDesk #2322](https://github.com/pikasTech/unidesk/issues/2322):新增 YAML/参数驱动的 `platform-infra observability closeout` 只读聚合入口,在一次 CLI 调用中有界汇总 status、validate 及可选 PromQL/OTel trace 证据;保持 `mutation=false`、non-blocking,不修改公共运行面,完成任务后将详细报告写入任务报告,完成任务后将详细报告写入[任务报告](./details/observability-trace-reliability/R5_Task_Report.md)。 diff --git a/docs/reference/observability.md b/docs/reference/observability.md index 760b58cc..175b2d79 100644 --- a/docs/reference/observability.md +++ b/docs/reference/observability.md @@ -4,6 +4,11 @@ UniDesk 的可观测性优先级高于静默成功。CLI、服务日志、Docker ## 共享 Prometheus 指标面 +- 同类运行面收口优先使用单次只读聚合入口: + - `bun scripts/cli.ts platform-infra observability closeout --target ` 固定汇总 status 与 validate; + - 可重复 `--query ` 增加指标证据,可选 `--trace-id ` 与 `--service ` 增加 trace 证据; + - 子检查失败只产生 `blocking=false` warning,不修改运行面,也不改变业务成功终态; + - 需要单项完整证据时再使用对应 status、validate、metrics-query 或 trace 下钻。 - `config/platform-infra/observability.yaml` 同时拥有 Collector、Tempo 和 Prometheus 的 image、retention、storage、port、目标 `targetIds`、服务发现与查询预算;TypeScript 只校验和渲染。 - Prometheus 仅作用于 `metricsBackend.targetIds` 选中的 observability target;其他 target 的 plan 显示 `disabled-for-target`,manifest、status probe/capture 和 `metrics-query` 均不访问 Prometheus。 - Prometheus 使用 Kubernetes 原生 pod 服务发现。YAML 通过结构化 `labelSelector.key/value` 声明稳定 label,并用 `prometheus.io/scrape=true`、`prometheus.io/path`、`prometheus.io/port` annotation 声明抓取;显式 path 优先,仅在 annotation 缺失时使用 `defaultPath`,不允许业务专属分支。 diff --git a/scripts/src/platform-infra-observability.test.ts b/scripts/src/platform-infra-observability.test.ts index 8c4eec8a..1af1e134 100644 --- a/scripts/src/platform-infra-observability.test.ts +++ b/scripts/src/platform-infra-observability.test.ts @@ -7,6 +7,7 @@ import { resolveTarget } from "./platform-infra-observability/actions"; import { isKubernetesLabelValue, isKubernetesQualifiedName, readObservabilityConfig } from "./platform-infra-observability/config"; import { renderObservabilityPlanFailure } from "./platform-infra-observability/plan-render"; import { prometheusMetaLabel, re2Literal } from "./platform-infra-observability/prometheus"; +import { parseCloseoutOptions } from "./platform-infra-observability/options"; import { statusScript } from "./platform-infra-observability/search-script"; import { renderManifest } from "./platform-infra-observability/trace-script"; @@ -123,13 +124,46 @@ describe("platform-infra observability progressive disclosure", () => { expect(result.status).toBe(0); const payload = JSON.parse(result.stdout) as Record; expect(payload.data.operations.map((item: Record) => item.name)).toEqual([ - "plan", "apply", "status", "validate", "trace", "search", "diagnose-code-agent", "metrics-query", + "plan", "apply", "status", "validate", "trace", "search", "diagnose-code-agent", "metrics-query", "closeout", ]); expect(payload.data.scopedHelp).toContain(" --help"); expect(payload.data.usage).toBeUndefined(); expect(result.stdout).not.toContain("platform-infra sub2api"); }); + test("closeout accepts repeatable parameter-driven evidence without a product-specific default", () => { + expect(parseCloseoutOptions([ + "--target", "NC01", + "--query", "database_ready", + "--query", "migration_ready", + "--trace-id", "a7a8f10dc985505c74fad89eeb1ba39f", + "--service", "example-api", + "--full", + ])).toEqual({ + targetId: "NC01", + queries: ["database_ready", "migration_ready"], + traceId: "a7a8f10dc985505c74fad89eeb1ba39f", + service: "example-api", + full: true, + raw: false, + }); + + const help = cli(["platform-infra", "observability", "closeout", "--help"]); + expect(help.status).toBe(0); + const payload = JSON.parse(help.stdout) as Record; + expect(payload.data.command).toBe("platform-infra observability closeout"); + expect(payload.data.boundary).toContain("non-blocking warning"); + expect(help.stdout).not.toContain("pikaoa"); + expect(() => parseCloseoutOptions(["--target", "NC01", "--service", "example-api"])).toThrow("--service requires --trace-id"); + expect(readObservabilityConfig().metricsBackend.query.maxCloseoutQueries).toBe(8); + }); + + test("closeout rejects query fan-out above the YAML budget before remote access", async () => { + const args = ["closeout", "--target", "NC01"]; + for (let index = 0; index < 9; index += 1) args.push("--query", `metric_${index}`); + await expect(runPlatformObservabilityCommand({} as never, args)).rejects.toThrow("at most 8 --query values"); + }); + test("configRef failures name the source and a bounded next command", () => { const rendered = renderObservabilityPlanFailure( new Error("config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01.runtime.namespace is missing segment namespace"), diff --git a/scripts/src/platform-infra-observability/closeout.ts b/scripts/src/platform-infra-observability/closeout.ts new file mode 100644 index 00000000..e2f41fd8 --- /dev/null +++ b/scripts/src/platform-infra-observability/closeout.ts @@ -0,0 +1,102 @@ +// 只读聚合 status、validate 以及调用者显式选择的 metrics/trace 证据。 +import type { UniDeskConfig } from "../config"; +import type { RenderedCliResult } from "../output"; +import { redactSensitiveUnknown } from "../platform-infra-ops-library"; +import { metricsQuery } from "./prometheus"; +import { status, validate } from "./actions"; +import { trace } from "./render"; +import { formatTable, shortenEnd, textValue } from "./manifest"; +import { resolveTarget } from "./actions"; +import { readObservabilityConfig } from "./config"; +import { targetSummary } from "./summary"; +import type { CloseoutOptions } from "./types"; + +type Result = Record; + +export async function closeout(config: UniDeskConfig, options: CloseoutOptions): Promise { + const observability = readObservabilityConfig(); + const target = resolveTarget(observability, options.targetId); + if (options.queries.length > observability.metricsBackend.query.maxCloseoutQueries) { + throw new Error(`observability closeout accepts at most ${observability.metricsBackend.query.maxCloseoutQueries} --query values from config/platform-infra/observability.yaml`); + } + const operations: Array> = [ + settled("status", () => status(config, { targetId: target.id, full: true, raw: false })), + settled("validate", () => validate(config, { targetId: target.id, full: true, raw: false })), + ]; + for (const query of options.queries) { + operations.push(settled(`metrics:${query}`, () => metricsQuery(config, { targetId: target.id, query, full: true, raw: false }))); + } + if (options.traceId !== null) { + operations.push(settled("trace", () => trace(config, { + targetId: target.id, + traceId: options.traceId, + grep: options.service, + limit: 40, + full: true, + raw: true, + }))); + } + const settledResults = await Promise.all(operations); + const statusResult = settledResults[0]; + const validateResult = settledResults[1]; + const statusOk = statusResult.ok === true; + const validateOk = validateResult.ok === true; + const warnings = settledResults + .filter((item) => item.ok !== true) + .map((item) => ({ code: "observability-closeout-child-failed", operation: item.operation, blocking: false, detail: item.error ?? "child operation returned not-ok" })); + const result: Result = { + ok: statusOk && validateOk, + action: "platform-infra-observability-closeout", + mutation: false, + blocking: false, + target: targetSummary(target), + requested: { queries: options.queries, traceId: options.traceId, service: options.service }, + status: redactSensitiveUnknown(statusResult), + validate: redactSensitiveUnknown(validateResult), + optional: settledResults.slice(2).map((item) => redactSensitiveUnknown(item)), + warnings, + }; + if (options.full || options.raw) return result; + return renderCloseout(result, target.id, target.namespace); +} + +async function settled(operation: string, run: () => Promise): Promise { + try { + const value = await run(); + if (typeof value === "object" && value !== null && !Array.isArray(value)) { + return { operation, ...(value as Result) }; + } + return { operation, ok: false, error: "child operation returned an unsupported payload" }; + } catch (error) { + return { operation, ok: false, error: error instanceof Error ? error.message : String(error) }; + } +} + +function renderCloseout(result: Result, targetId: string, namespace: string): RenderedCliResult { + const statusResult = asRecord(result.status); + const validateResult = asRecord(result.validate); + const optional = Array.isArray(result.optional) ? result.optional.filter(asRecord) : []; + const rows = [ + ["status", textValue(statusResult?.ok)], + ["validate", textValue(validateResult?.ok)], + ...optional.map((item) => [textValue(item.operation), textValue(item.ok)]), + ]; + const warnings = Array.isArray(result.warnings) ? result.warnings.filter(asRecord) : []; + const lines = [ + `platform-infra observability closeout (${result.ok === true ? "ok" : "not-ok"})`, + "", + `target=${targetId} namespace=${namespace} ready=${result.ok === true} mutation=false blocking=false`, + "", + "Checks:", + formatTable(["OPERATION", "OK"], rows.length > 0 ? rows : [["-", "-"]]), + ]; + if (warnings.length > 0) { + lines.push("", "Warnings:", formatTable(["OPERATION", "BLOCKING", "DETAIL"], warnings.map((item) => [shortenEnd(textValue(item.operation), 28), textValue(item.blocking), shortenEnd(textValue(item.detail), 100)]))); + } + lines.push("", "Disclosure:", " status/validate are always collected; metrics and trace are opt-in via --query/--trace-id.", " Optional failures remain non-blocking warnings; use --full for structured child results."); + return { ok: result.ok === true, command: "platform-infra observability closeout", contentType: "text/plain", renderedText: lines.join("\n") }; +} + +function asRecord(value: unknown): Result | null { + return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Result : null; +} diff --git a/scripts/src/platform-infra-observability/config.ts b/scripts/src/platform-infra-observability/config.ts index 31c86b15..9c5c3de5 100644 --- a/scripts/src/platform-infra-observability/config.ts +++ b/scripts/src/platform-infra-observability/config.ts @@ -124,6 +124,7 @@ export function readObservabilityConfig(): ObservabilityConfig { timeoutSeconds: integerField(metricsQuery, "timeoutSeconds", "metricsBackend.query"), maxSeries: integerField(metricsQuery, "maxSeries", "metricsBackend.query"), maxResponseBytes: integerField(metricsQuery, "maxResponseBytes", "metricsBackend.query"), + maxCloseoutQueries: integerField(metricsQuery, "maxCloseoutQueries", "metricsBackend.query"), }, }, sampling: { @@ -152,7 +153,7 @@ export function readObservabilityConfig(): ObservabilityConfig { if (!isKubernetesQualifiedName(config.metricsBackend.discovery.labelSelector.key)) throw new Error(`${configLabel}.metricsBackend.discovery.labelSelector.key must be a Kubernetes qualified name`); if (!isKubernetesLabelValue(config.metricsBackend.discovery.labelSelector.value)) throw new Error(`${configLabel}.metricsBackend.discovery.labelSelector.value must be a Kubernetes label value`); if (config.collector.replicas < 0 || config.traceBackend.replicas < 0 || config.metricsBackend.replicas < 0) throw new Error(`${configLabel} replicas must be >= 0`); - if (config.metricsBackend.query.timeoutSeconds < 1 || config.metricsBackend.query.maxSeries < 1 || config.metricsBackend.query.maxResponseBytes < 1024) throw new Error(`${configLabel}.metricsBackend.query budgets must be positive and maxResponseBytes must be >= 1024`); + if (config.metricsBackend.query.timeoutSeconds < 1 || config.metricsBackend.query.maxSeries < 1 || config.metricsBackend.query.maxResponseBytes < 1024 || config.metricsBackend.query.maxCloseoutQueries < 1) throw new Error(`${configLabel}.metricsBackend.query budgets must be positive and maxResponseBytes must be >= 1024`); if (config.sampling.ratio < 0 || config.sampling.ratio > 1) throw new Error(`${configLabel}.sampling.ratio must be between 0 and 1`); if (!config.probes.traceQueryPathTemplate.includes("{{traceId}}")) throw new Error(`${configLabel}.probes.traceQueryPathTemplate must include {{traceId}}`); return config; diff --git a/scripts/src/platform-infra-observability/index.ts b/scripts/src/platform-infra-observability/index.ts index 540492df..45c411e4 100644 --- a/scripts/src/platform-infra-observability/index.ts +++ b/scripts/src/platform-infra-observability/index.ts @@ -9,5 +9,6 @@ export * from "./apply-status-scripts"; export * from "./trace-script"; export * from "./search-script"; export * from "./diagnose-code-agent-script"; +export * from "./closeout"; export * from "./summary"; export * from "./plan-render"; diff --git a/scripts/src/platform-infra-observability/options.ts b/scripts/src/platform-infra-observability/options.ts index b9bea663..5960471a 100644 --- a/scripts/src/platform-infra-observability/options.ts +++ b/scripts/src/platform-infra-observability/options.ts @@ -19,13 +19,14 @@ import { capture, } from "../platform-infra-ops-library"; -import type { ApplyOptions, CommonOptions, DiagnoseCodeAgentOptions, MetricsQueryOptions, SearchOptions, TraceOptions } from "./types"; +import type { ApplyOptions, CloseoutOptions, CommonOptions, DiagnoseCodeAgentOptions, MetricsQueryOptions, SearchOptions, TraceOptions } from "./types"; import { apply, plan, status, validate } from "./actions"; import { diagnoseCodeAgent, search, trace } from "./render"; import { renderObservabilityPlan, renderObservabilityPlanFailure } from "./plan-render"; import { metricsQuery } from "./prometheus"; +import { closeout } from "./closeout"; -const observabilityOperations = ["plan", "apply", "status", "validate", "trace", "search", "diagnose-code-agent", "metrics-query"] as const; +const observabilityOperations = ["plan", "apply", "status", "validate", "trace", "search", "diagnose-code-agent", "metrics-query", "closeout"] as const; export function observabilityHelp(scope: string | null = null): Record { const scoped = scope === null ? null : observabilityOperationHelp(scope); @@ -64,6 +65,7 @@ export async function runPlatformObservabilityCommand(config: UniDeskConfig, arg if (action === "search") return await search(config, parseSearchOptions(args.slice(1))); if (action === "diagnose-code-agent") return await diagnoseCodeAgent(config, parseDiagnoseCodeAgentOptions(args.slice(1))); if (action === "metrics-query") return await metricsQuery(config, parseMetricsQueryOptions(args.slice(1))); + if (action === "closeout") return await closeout(config, parseCloseoutOptions(args.slice(1))); return { ok: false, error: "unsupported-platform-infra-observability-command", args, help: observabilityHelp() }; } @@ -113,6 +115,13 @@ function observabilityOperationHelp(scope: string): Record | nu usage: ["bun scripts/cli.ts platform-infra observability metrics-query --target --query [--full|--raw]"], options: ["--target ", "--query ", "--full", "--raw"], }; + if (scope === "closeout") return { + ...common, + command: "platform-infra observability closeout", + usage: ["bun scripts/cli.ts platform-infra observability closeout --target [--query ]... [--trace-id ] [--service ] [--full|--raw]"], + options: ["--target ", "--query (repeatable)", "--trace-id <32-hex>", "--service ", "--full", "--raw"], + boundary: "只读聚合 status/validate 及显式 metrics/trace;所有可观测性缺口均为 non-blocking warning。", + }; if (scope === "search") return { ...common, command: "platform-infra observability search", @@ -155,6 +164,36 @@ export function parseMetricsQueryOptions(args: string[]): MetricsQueryOptions { return { ...parseCommonOptions(commonArgs), query }; } +export function parseCloseoutOptions(args: string[]): CloseoutOptions { + const commonArgs: string[] = []; + const queries: string[] = []; + let traceId: string | null = null; + let service: string | null = null; + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + const value = args[index + 1]; + if (arg === "--query") { + if (value === undefined || value.startsWith("--")) throw new Error("--query requires a value"); + queries.push(value); + index += 1; + } else if (arg === "--trace-id" || arg === "--service") { + if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`); + if (arg === "--trace-id") traceId = value; + else service = value; + index += 1; + } else { + commonArgs.push(arg); + if (arg === "--target") { + commonArgs.push(value ?? ""); + index += 1; + } + } + } + if (traceId !== null && !/^[0-9a-fA-F]{32}$/u.test(traceId)) throw new Error("trace id must be a 32-character OpenTelemetry trace id encoded as hex"); + if (service !== null && traceId === null) throw new Error("--service requires --trace-id"); + return { ...parseCommonOptions(commonArgs), queries, traceId, service }; +} + export function parseCommonOptions(args: string[]): CommonOptions { let targetId: string | null = null; let full = false; diff --git a/scripts/src/platform-infra-observability/types.ts b/scripts/src/platform-infra-observability/types.ts index 629ff0d7..08978220 100644 --- a/scripts/src/platform-infra-observability/types.ts +++ b/scripts/src/platform-infra-observability/types.ts @@ -111,7 +111,7 @@ export interface PrometheusConfig { scrapeInterval: string; scrapeTimeout: string; }; - query: { path: string; timeoutSeconds: number; maxSeries: number; maxResponseBytes: number }; + query: { path: string; timeoutSeconds: number; maxSeries: number; maxResponseBytes: number; maxCloseoutQueries: number }; } export interface ImageSpec { @@ -196,3 +196,9 @@ export interface DiagnoseCodeAgentOptions extends CommonOptions { export interface MetricsQueryOptions extends CommonOptions { query: string | null; } + +export interface CloseoutOptions extends CommonOptions { + queries: string[]; + traceId: string | null; + service: string | null; +} diff --git a/scripts/src/platform-infra/entry.ts b/scripts/src/platform-infra/entry.ts index 3f378ca4..244d8227 100644 --- a/scripts/src/platform-infra/entry.ts +++ b/scripts/src/platform-infra/entry.ts @@ -483,6 +483,7 @@ export function platformInfraHelp(): unknown { "bun scripts/cli.ts platform-infra observability apply --target D601 --confirm", "bun scripts/cli.ts platform-infra observability status --target D601", "bun scripts/cli.ts platform-infra observability validate --target D601", + "bun scripts/cli.ts platform-infra observability closeout --target D601 [--query 'up'] [--trace-id ] [--service ] [--full|--raw]", "bun scripts/cli.ts platform-infra observability trace --target D601 [--grep text] [--limit 40] [--full|--raw]", "bun scripts/cli.ts platform-infra observability search --target D601 --grep 'no rollout found' [--lookback-minutes 360] [--candidate-limit 80] [--limit 20]", "bun scripts/cli.ts platform-infra observability diagnose-code-agent --target D601 --business-trace-id [--full|--raw]",