Merge pull request #2067 from pikasTech/fix/2064-cicd-over-budget-warning
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Success
Pipelines as Code CI / unidesk-host- Success

CI/CD 自动交付超预算时输出非阻塞 env reuse 提示
This commit is contained in:
Lyon
2026-07-15 02:33:23 +08:00
committed by GitHub
6 changed files with 282 additions and 8 deletions
+2
View File
@@ -50,6 +50,8 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
- 唯一推荐入口是 `platform-infra pipelines-as-code delivery-timing --target NC01 --consumer selfmedia-nc01`
- 一次调用直接披露 GitHub PR `mergedAt`、trigger wait、PipelineRun/TaskRun、端到端耗时、Argo、runtime health 和 `evidenceGaps`
- 证据不完整时保留已成立的耗时字段并返回 `partial` 与明确缺口,不得手工串联第二状态源;
- 端到端耗时严格超过 owning YAML 预算时输出 `pac-automatic-delivery-over-budget`,固定 `blocking=false``mutation=false`
- 超预算提示只下钻 env identity、依赖缓存、BuildKit/cache 和 stage timing,不给出人工恢复命令;
- 该入口固定只读且 `mutation=false`,不得用于同步、触发、补跑或运行面写入。
- 新增 PaC consumer 的首次引导:
@@ -62,6 +62,22 @@ The history/status env reuse display is log-derived:
- Sentinel's compact table headed `ENV_REUSE NODE_DEPS ...` is parsed as human-readable fallback.
- Missing env reuse text is not proof that no reuse happened; first check the consumer type, the relevant TaskRun logs, and whether the pipeline emits the expected fields.
## 自动交付预算告警
- 自动交付端到端预算的唯一真相是 `config/platform-infra/pipelines-as-code.yaml#deliveryTiming.endToEndBudgetSeconds`
- `delivery-timing` 只有在 PR `mergedAt`、PipelineRun 时间、Argo、runtime、health 和 provenance 证据完整时才判定预算。
- 证据不完整时状态固定为 `partial``overBudget=null`,不得提前输出超预算告警。
- 严格超过预算时输出 `pac-automatic-delivery-over-budget` typed warning
- `blocking=false`
- `mutation=false`
- 不改变 PipelineRun、artifact、GitOps、Argo、runtime 或 health 的成功终态。
- 告警只指向现有只读 `status``history``delivery-timing` 入口,并提示检查:
- env identity
- 依赖缓存;
- BuildKit/cache
- stage timing。
- `status/history` 复用同一预算摘要;只有流水线阶段耗时而缺少完整端到端证据时仍显示 `partial`,不得把 PipelineRun 完成等同于 runtime closeout 完成。
## Interpreting Results
Use these interpretations:
@@ -19,6 +19,8 @@ defaults:
consumerId: hwlab-nc01-v03
display:
timeZone: Asia/Shanghai
deliveryTiming:
endToEndBudgetSeconds: 120
observability:
configRef: config/platform-infra/observability.yaml
tracesEndpoint: http://otel-collector.platform-infra.svc.cluster.local:4318/v1/traces
@@ -0,0 +1,67 @@
import { describe, expect, test } from "bun:test";
import { observePacDeliveryBudget, observePacHistoryDeliveryBudget, observePacStatusDeliveryBudget } from "./platform-infra-pac-delivery-timing";
const policy = {
endToEndBudgetSeconds: 120,
configPath: "config/platform-infra/pipelines-as-code.yaml#deliveryTiming.endToEndBudgetSeconds",
};
function observe(endToEndSeconds: number | null, complete: boolean) {
return observePacDeliveryBudget({
policy,
targetId: "NC01",
consumerId: "selfmedia-nc01",
pipelineRunId: "selfmedia-nc01-fixture",
endToEndSeconds,
complete,
});
}
describe("PaC automatic delivery budget warning", () => {
test("120 seconds stays within budget without a warning", () => {
const result = observe(120, true);
expect(result).toMatchObject({ state: "complete", overBudget: false, warning: null, blocking: false, mutation: false });
});
test("more than 120 seconds emits a non-blocking env reuse warning", () => {
const result = observe(121, true);
expect(result.overBudget).toBe(true);
expect(result.warning).toMatchObject({
code: "pac-automatic-delivery-over-budget",
severity: "warning",
blocking: false,
mutation: false,
observedSeconds: 121,
budgetSeconds: 120,
configPath: policy.configPath,
});
const warningText = JSON.stringify(result.warning);
expect(warningText).toContain("env reuse");
expect(warningText).toContain("BuildKit/cache");
expect(warningText).not.toMatch(/trigger-current|refresh|sync|PipelineRun create/iu);
});
test("missing timing evidence remains partial and never warns", () => {
const result = observe(null, false);
expect(result).toMatchObject({ state: "partial", overBudget: null, warning: null, blocking: false, mutation: false });
});
test("status and history summaries stay partial without exact end-to-end evidence", () => {
const status = observePacStatusDeliveryBudget({
policy,
targetId: "NC01",
consumerId: "selfmedia-nc01",
summary: { ready: true, latestPipelineRun: { name: "fixture", durationSeconds: 193 } },
});
const history = observePacHistoryDeliveryBudget({
policy,
targetId: "NC01",
consumerIds: ["selfmedia-nc01"],
defaultConsumerId: "selfmedia-nc01",
rows: [{ id: "fixture", consumer: "selfmedia-nc01", durationSeconds: 193 }],
});
expect(status).toMatchObject({ state: "partial", overBudget: null, warning: null, pipelineDurationSeconds: 193, mutation: false });
expect(history).toMatchObject({ state: "partial", blocking: false, mutation: false });
expect((history.observations as Array<Record<string, unknown>>)[0]).toMatchObject({ state: "partial", warning: null, pipelineDurationSeconds: 193 });
});
});
@@ -11,6 +11,21 @@ export interface PacDeliveryTimingBinding {
branch: string;
pipeline: string;
};
policy: PacDeliveryTimingPolicy;
}
export interface PacDeliveryTimingPolicy {
endToEndBudgetSeconds: number;
configPath: string;
}
export interface PacDeliveryBudgetObservationInput {
policy: PacDeliveryTimingPolicy;
targetId: string;
consumerId: string;
pipelineRunId: string | null;
endToEndSeconds: number | null;
complete: boolean;
}
interface PacDeliveryTimingObservers {
@@ -34,6 +49,106 @@ function durationBetween(start: unknown, end: unknown): number | null {
return Math.max(0, Math.round((b - a) / 1000));
}
export function observePacDeliveryBudget(input: PacDeliveryBudgetObservationInput): Record<string, unknown> {
const evidenceState = input.complete && input.endToEndSeconds !== null ? "complete" : "partial";
const overBudget = evidenceState === "complete" && (input.endToEndSeconds as number) > input.policy.endToEndBudgetSeconds;
const warning = overBudget ? {
code: "pac-automatic-delivery-over-budget",
severity: "warning",
blocking: false,
mutation: false,
object: { kind: "consumer", id: input.consumerId },
configPath: input.policy.configPath,
metric: "end-to-end-seconds",
observedSeconds: input.endToEndSeconds,
budgetSeconds: input.policy.endToEndBudgetSeconds,
hint: {
summary: "优先检查并优化 env reuse;对照 env identity、依赖缓存、BuildKit/cache 和 stage timing 定位慢阶段。",
status: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${input.targetId} --consumer ${input.consumerId} --json`,
history: input.pipelineRunId === null
? `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${input.targetId} --consumer ${input.consumerId} --limit 10 --json`
: `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${input.targetId} --consumer ${input.consumerId} --id ${input.pipelineRunId} --full`,
focus: ["env-identity", "dependency-cache", "buildkit-cache", "stage-timing"],
mutation: false,
valuesPrinted: false,
},
valuesPrinted: false,
} : null;
return {
state: evidenceState,
metric: "end-to-end-seconds",
observedSeconds: input.endToEndSeconds,
budgetSeconds: input.policy.endToEndBudgetSeconds,
overBudget: evidenceState === "complete" ? overBudget : null,
configPath: input.policy.configPath,
warning,
blocking: false,
mutation: false,
valuesPrinted: false,
};
}
export function observePacStatusDeliveryBudget(input: {
policy: PacDeliveryTimingPolicy;
targetId: string;
consumerId: string;
summary: Record<string, unknown> | null;
}): Record<string, unknown> {
const latest = record(input.summary?.latestPipelineRun);
const observedEndToEnd = typeof latest.endToEndSeconds === "number" ? latest.endToEndSeconds : null;
return {
...observePacDeliveryBudget({
policy: input.policy,
targetId: input.targetId,
consumerId: input.consumerId,
pipelineRunId: typeof latest.name === "string" ? latest.name : null,
endToEndSeconds: observedEndToEnd,
complete: input.summary?.ready === true && observedEndToEnd !== null,
}),
evidenceSource: "status-summary",
pipelineDurationSeconds: typeof latest.durationSeconds === "number" ? latest.durationSeconds : null,
exactCommand: `bun scripts/cli.ts platform-infra pipelines-as-code delivery-timing --target ${input.targetId} --consumer ${input.consumerId} --json`,
};
}
export function observePacHistoryDeliveryBudget(input: {
policy: PacDeliveryTimingPolicy;
targetId: string;
consumerIds: readonly string[];
defaultConsumerId: string;
rows: readonly Record<string, unknown>[];
}): Record<string, unknown> {
const observations = input.rows.map((row) => {
const consumerId = typeof row.consumer === "string" ? row.consumer : input.consumerIds[0] ?? input.defaultConsumerId;
const observedEndToEnd = typeof row.endToEndSeconds === "number" ? row.endToEndSeconds : null;
return {
...observePacDeliveryBudget({
policy: input.policy,
targetId: input.targetId,
consumerId,
pipelineRunId: typeof row.id === "string" ? row.id : typeof row.pipelineRun === "string" ? row.pipelineRun : null,
endToEndSeconds: observedEndToEnd,
complete: row.deliveryComplete === true && observedEndToEnd !== null,
}),
consumer: consumerId,
pipelineRun: row.id ?? row.pipelineRun ?? null,
evidenceSource: "history-summary",
pipelineDurationSeconds: typeof row.durationSeconds === "number" ? row.durationSeconds : null,
};
});
return {
state: observations.length > 0 && observations.every((item) => item.state === "complete") ? "complete" : "partial",
policy: input.policy,
observations,
exactCommand: input.consumerIds.length === 1
? `bun scripts/cli.ts platform-infra pipelines-as-code delivery-timing --target ${input.targetId} --consumer ${input.consumerIds[0]} --json`
: null,
blocking: false,
mutation: false,
valuesPrinted: false,
};
}
export async function observePacDeliveryTiming(
binding: PacDeliveryTimingBinding,
observers: PacDeliveryTimingObservers,
@@ -85,11 +200,28 @@ export async function observePacDeliveryTiming(
if (argo.sync === undefined || argo.health === undefined) evidenceGaps.push("argo-closeout-missing");
if (runtime.readyReplicas === undefined) evidenceGaps.push("runtime-health-missing");
if (provenance.relation === "unknown") evidenceGaps.push("runtime-provenance-unknown");
if (statusSummary.ready !== true) evidenceGaps.push("runtime-closeout-not-ready");
const state = evidenceGaps.length === 0 ? "complete" : "partial";
const endToEndSeconds = durationBetween(mergedAt, completionTime);
const pipelineRunId = typeof row.id === "string"
? row.id
: typeof latest.name === "string"
? latest.name
: null;
const deliveryBudget = observePacDeliveryBudget({
policy: binding.policy,
targetId: String(binding.target.id ?? binding.consumer.node),
consumerId: binding.consumer.id,
pipelineRunId,
endToEndSeconds,
complete: state === "complete",
});
const warning = record(deliveryBudget.warning);
return {
ok: commit !== null && startTime !== null && completionTime !== null,
action: "platform-infra-pipelines-as-code-delivery-timing",
mutation: false,
state: evidenceGaps.length === 0 ? "complete" : "partial",
state,
target: binding.target,
consumer: binding.consumer,
source: { commit, pullRequest, github: githubEvidence },
@@ -99,10 +231,13 @@ export async function observePacDeliveryTiming(
pipelineCompletion: completionTime,
triggerWaitSeconds: durationBetween(mergedAt, startTime),
pipelineDurationSeconds: row.durationSeconds ?? durationBetween(startTime, completionTime),
endToEndSeconds: durationBetween(mergedAt, completionTime),
endToEndSeconds,
},
pipeline: { id: row.id ?? latest.name ?? null, status: row.status ?? latest.status ?? null, taskRuns: row.taskRuns ?? statusSummary.taskRuns ?? null },
deliveryBudget,
warnings: Object.keys(warning).length === 0 ? [] : [warning],
pipeline: { id: pipelineRunId, status: row.status ?? latest.status ?? null, taskRuns: row.taskRuns ?? statusSummary.taskRuns ?? null },
runtime: { argo, health: runtime, provenance },
evidenceGaps: [...new Set(evidenceGaps)],
valuesPrinted: false,
};
}
@@ -37,7 +37,12 @@ import {
resolvePacBootstrapMirrorRepository,
} from "./platform-infra-pipelines-as-code-bootstrap";
import { featureConfigSchemaHistoryFields, renderFeatureConfigSchemaLine } from "./platform-infra-pac-feature-config-projection";
import { observePacDeliveryTiming } from "./platform-infra-pac-delivery-timing";
import {
observePacDeliveryTiming,
observePacHistoryDeliveryBudget,
observePacStatusDeliveryBudget,
type PacDeliveryTimingPolicy,
} from "./platform-infra-pac-delivery-timing";
const configFile = rootPath("config", "platform-infra", "pipelines-as-code.yaml");
const configLabel = "config/platform-infra/pipelines-as-code.yaml";
@@ -80,6 +85,7 @@ export interface PacConfig {
display: {
timeZone: string;
};
deliveryTiming: PacDeliveryTimingPolicy;
observability: {
configRef: string;
tracesEndpoint: string;
@@ -461,6 +467,7 @@ export function parsePacConfigDocument(
const gitOpsMirrorFlush = y.objectField(closeout, "gitOpsMirrorFlush", "closeout");
const gitea = y.objectField(root, "gitea", "");
const display = y.objectField(root, "display", "");
const deliveryTiming = y.objectField(root, "deliveryTiming", "");
const observability = y.objectField(root, "observability", "");
const observabilitySampling = y.objectField(observability, "sampling", "observability");
const exporterFailure = y.objectField(observability, "exporterFailure", "observability");
@@ -495,6 +502,10 @@ export function parsePacConfigDocument(
display: {
timeZone: y.stringField(display, "timeZone", "display"),
},
deliveryTiming: {
endToEndBudgetSeconds: positiveInteger(deliveryTiming, "endToEndBudgetSeconds", "deliveryTiming"),
configPath: `${configLabel}#deliveryTiming.endToEndBudgetSeconds`,
},
observability: {
configRef: y.stringField(observability, "configRef", "observability"),
tracesEndpoint: urlField(observability, "tracesEndpoint", "observability"),
@@ -1297,6 +1308,8 @@ async function status(config: UniDeskConfig, options: CommonOptions): Promise<Re
const parsed = parseJsonOutput(result.stdout);
const summary = parsed === null ? null : statusSummary(parsed);
const deliveryAuthority = resolveCicdDeliveryAuthority({ consumerId: consumer.id, node: consumer.node, lane: consumer.lane });
const deliveryBudget = observePacStatusDeliveryBudget({ policy: pac.deliveryTiming, targetId: target.id, consumerId: consumer.id, summary });
const deliveryWarning = record(deliveryBudget.warning);
return {
ok: result.exitCode === 0 && summary?.ready === true && deliveryAuthority.kind === "pac-pr-merge",
action: "platform-infra-pipelines-as-code-status",
@@ -1307,8 +1320,9 @@ async function status(config: UniDeskConfig, options: CommonOptions): Promise<Re
deliveryAuthority,
coverage: consumerCoverage(pac, target.id),
summary,
deliveryBudget,
remote: parsed === null ? compactCapture(result, { full: true }) : options.raw ? parsed : undefined,
warnings: pac.validationWarnings,
warnings: [...pac.validationWarnings, ...(Object.keys(deliveryWarning).length === 0 ? [] : [deliveryWarning])],
next: nextCommands(target.id, consumer.id, pac.defaults.consumerId),
};
}
@@ -1513,6 +1527,17 @@ async function history(config: UniDeskConfig, options: HistoryOptions): Promise<
const parsed = parseJsonOutput(result.stdout);
const remote = parsed ?? compactCapture(result, { full: true });
const historyErrors = arrayRecords(record(remote).historyErrors);
const rows = arrayRecords(record(remote).rows);
const deliveryBudget = observePacHistoryDeliveryBudget({
policy: pac.deliveryTiming,
targetId: target.id,
consumerIds: selectedConsumers.map((consumer) => consumer.id),
defaultConsumerId: pac.defaults.consumerId,
rows,
});
const deliveryWarnings = arrayRecords(deliveryBudget.observations)
.map((observation) => record(observation.warning))
.filter((warning) => Object.keys(warning).length > 0);
return {
ok: result.exitCode === 0 && parsed?.ok !== false && historyErrors.length === 0,
action: "platform-infra-pipelines-as-code-history",
@@ -1527,9 +1552,10 @@ async function history(config: UniDeskConfig, options: HistoryOptions): Promise<
},
consumers: selectedConsumers.map((consumer) => consumer.id),
detailId: options.detailId,
rows: arrayRecords(record(remote).rows),
rows,
deliveryBudget,
historyErrors,
warnings: pac.validationWarnings,
warnings: [...pac.validationWarnings, ...deliveryWarnings],
controlPlane: parsed === null ? undefined : {
crdPresent: parsed.crdPresent === true,
controllerReady: parsed.controllerReady,
@@ -2051,6 +2077,7 @@ function configSummary(pac: PacConfig, consumer: PacConsumer, repository: PacRep
metadata: pac.metadata,
release: pac.release,
capabilities: capabilitySummary(pac),
deliveryTiming: pac.deliveryTiming,
gitea: { configRef: pac.gitea.configRef, internalBaseUrl: pac.gitea.internalBaseUrl, webhookBranch: pac.gitea.webhook.branch },
display: pac.display,
repositories: pac.repositories.map(repositorySummary),
@@ -2070,6 +2097,7 @@ function compactConfigSummary(pac: PacConfig, consumer: PacConsumer, repository:
providerType: repository.providerType,
sourceUrl: repository.cloneUrl,
displayTimeZone: pac.display.timeZone,
deliveryTiming: pac.deliveryTiming,
consumer: `${consumer.node}/${consumer.lane}`,
consumerId: consumer.id,
pipeline: consumer.pipeline,
@@ -2159,6 +2187,7 @@ function compactPlanJson(result: Record<string, unknown>): Record<string, unknow
capabilities: config.capabilities,
gitea: config.gitea,
display: config.display,
deliveryTiming: config.deliveryTiming,
valuesPrinted: false,
},
repository: result.repository,
@@ -2202,6 +2231,7 @@ function compactStatusJson(result: Record<string, unknown>): Record<string, unkn
deliveryAuthority: result.deliveryAuthority,
capabilities: record(result.config).capabilities,
warnings: result.warnings,
deliveryBudget: result.deliveryBudget,
summary: compactStatusSummary(record(result.summary)),
next: result.next,
valuesPrinted: false,
@@ -2321,6 +2351,7 @@ export function compactHistoryJson(result: Record<string, unknown>, includeTaskD
config: result.config,
consumers: result.consumers,
detailId: result.detailId,
deliveryBudget: result.deliveryBudget,
rows: arrayRecords(result.rows).map((row) => {
const taskRuns = record(row.taskRuns);
const artifact = record(row.artifact);
@@ -2393,6 +2424,7 @@ export function compactHistoryJson(result: Record<string, unknown>, includeTaskD
};
}),
historyErrors: result.historyErrors,
warnings: result.warnings,
next: result.next,
valuesPrinted: false,
};
@@ -2476,6 +2508,7 @@ export function renderStatus(result: Record<string, unknown>): RenderedCliResult
const coverage = arrayRecords(result.coverage);
const warnings = arrayRecords(result.warnings);
const latest = record(summary.latestPipelineRun);
const deliveryBudget = record(result.deliveryBudget);
const taskRuns = arrayRecords(summary.taskRuns);
const artifact = record(summary.artifact);
const argo = record(summary.argo);
@@ -2521,6 +2554,7 @@ export function renderStatus(result: Record<string, unknown>): RenderedCliResult
"",
"LATEST PIPELINERUN",
...table(["NAME", "STATUS", "REASON", "DURATION_S", "SOURCE"], [[stringValue(latest.name), stringValue(latest.status), stringValue(latest.reason), stringValue(latest.durationSeconds), short(stringValue(latest.sourceCommit))]]),
` delivery-budget: state=${stringValue(deliveryBudget.state)} end-to-end=${stringValue(deliveryBudget.observedSeconds)}s/${stringValue(deliveryBudget.budgetSeconds)}s exact=${stringValue(deliveryBudget.exactCommand)}`,
"",
"DELIVERY OBSERVATION",
...table(["MODE", "VALID", "SOURCE_MATCHED", "AFFECTED", "ROLLOUT", "BUILD", "REUSED", "REASON"], [[
@@ -2585,12 +2619,21 @@ function renderPacConfigWarnings(warnings: readonly Record<string, unknown>[]):
return [
"",
"NON-BLOCKING WARNINGS",
...table(["OBJECT", "BLOCKING", "CODE", "CONFIG"], warnings.map((item) => [
...table(["OBJECT", "BLOCKING", "CODE", "CONFIG", "OBSERVED/BUDGET"], warnings.map((item) => [
`${stringValue(record(item.object).kind)}:${stringValue(record(item.object).id)}`,
boolText(item.blocking),
stringValue(item.code),
stringValue(item.configPath),
item.observedSeconds === undefined ? "-" : `${stringValue(item.observedSeconds)}s/${stringValue(item.budgetSeconds)}s`,
])),
...warnings.flatMap((item) => {
const hint = record(item.hint);
return Object.keys(hint).length === 0 ? [] : [
` hint: ${compactLine(stringValue(hint.summary))}`,
` status: ${stringValue(hint.status)}`,
` history: ${stringValue(hint.history)}`,
];
}),
];
}
@@ -2681,6 +2724,7 @@ async function deliveryTiming(config: UniDeskConfig, options: CommonOptions): Pr
branch: authority.consumer.sourceBranch,
pipeline: consumer.pipeline,
},
policy: pac.deliveryTiming,
}, {
history: async () => await history(config, historyOptions),
status: async () => await status(config, options),
@@ -2694,14 +2738,18 @@ function renderDeliveryTiming(result: Record<string, unknown>): RenderedCliResul
const runtime = record(result.runtime);
const argo = record(runtime.argo);
const health = record(runtime.health);
const deliveryBudget = record(result.deliveryBudget);
const warnings = arrayRecords(result.warnings);
const lines = [
"PLATFORM-INFRA DELIVERY TIMING",
`STATE: ${stringValue(result.state)} MUTATION: ${boolText(result.mutation)}`,
`SOURCE: ${stringValue(record(result.consumer).repository)}@${stringValue(source.commit)} PR#${stringValue(pr.number)}`,
`MERGED_AT: ${stringValue(timing.mergedAt)} PIPELINE_START: ${stringValue(timing.pipelineStart)} PIPELINE_DONE: ${stringValue(timing.pipelineCompletion)}`,
`TRIGGER_WAIT_S: ${stringValue(timing.triggerWaitSeconds)} PIPELINE_S: ${stringValue(timing.pipelineDurationSeconds)} END_TO_END_S: ${stringValue(timing.endToEndSeconds)}`,
`BUDGET_S: ${stringValue(deliveryBudget.budgetSeconds)} OVER_BUDGET: ${stringValue(deliveryBudget.overBudget)} BUDGET_STATE: ${stringValue(deliveryBudget.state)}`,
`ARGO: ${stringValue(argo.sync)}/${stringValue(argo.health)} RUNTIME_READY: ${stringValue(health.readyReplicas)}/${stringValue(health.replicas)}`,
`EVIDENCE_GAPS: ${(Array.isArray(result.evidenceGaps) ? result.evidenceGaps.map(String) : []).join(", ") || "-"}`,
...renderPacConfigWarnings(warnings),
];
return rendered(result, "platform-infra pipelines-as-code delivery-timing", lines);
}
@@ -2710,12 +2758,16 @@ export function renderHistory(result: Record<string, unknown>): RenderedCliResul
const rows = arrayRecords(result.rows);
const config = record(result.config);
const historyErrors = arrayRecords(result.historyErrors);
const deliveryBudget = record(result.deliveryBudget);
const warnings = arrayRecords(result.warnings);
const detailId = stringValue(result.detailId);
const timeHeader = `TIME(${stringValue(config.displayTimeZone)})`;
const lines = [
"PLATFORM-INFRA PIPELINES-AS-CODE HISTORY",
`DATA: ${stringValue(config.source)}`,
`ROWS: ${stringValue(rows.length)} LIMIT_PER_CONSUMER: ${stringValue(config.limitPerConsumer)} READ_ERRORS: ${stringValue(historyErrors.length)}`,
`DELIVERY_BUDGET: state=${stringValue(deliveryBudget.state)} budget=${stringValue(record(deliveryBudget.policy).endToEndBudgetSeconds)}s exact=${stringValue(deliveryBudget.exactCommand)}`,
...renderPacConfigWarnings(warnings),
...(historyErrors.length === 0 ? [] : [
"",
"READ ERRORS",