diff --git a/config/agentrun.yaml b/config/agentrun.yaml index 944b1089..68195dbf 100644 --- a/config/agentrun.yaml +++ b/config/agentrun.yaml @@ -500,6 +500,9 @@ controlPlane: gitopsPath: deploy/gitops/node/nc01/runtime-v02 argoApplication: agentrun-nc01-v02 nc01-release: + status: + readOnlyCapture: + timeoutMs: 20000 deployment: format: unidesk-yaml-only gitopsRoot: deploy/gitops/node/nc01 diff --git a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R8_Task_Report.md b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R8_Task_Report.md index 6545ff78..9f72d009 100644 --- a/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R8_Task_Report.md +++ b/docs/MDTODO/details/pr-merge-driven-automatic-delivery/R8_Task_Report.md @@ -15,6 +15,8 @@ - 首轮修复已建立稳定的 typed observation,使 PaC `status/history/debug-step` 与 AgentRun `control-plane status` 在 timeout、remote failure、empty response 和 parse failure 时不再以空 stdout 结束。 - 首轮仍把 PaC 只读 capture 预算 `25000ms` 与 AgentRun status capture 预算 `20000ms` 写成 TypeScript 常量。 - 这些预算属于可调运维事实;源码常量绕过 owning YAML,无法披露来源路径,也无法在选中对象缺失预算时 fail-closed。 +- `18d0bd89` 已把预算迁入 YAML,但只给默认 `nc01-v02` lane 声明 AgentRun 预算;parser 会校验 `controlPlane.lanes` 的全部对象,因此 `nc01-release` 缺失 `status.readOnlyCapture` 时,`agentrun control-plane status` 在任何目标选择前即失败。 +- AgentRun 默认文本只显示嵌套 PaC timeout,未披露选中 lane 的 budget path;PaC observation 实际返回 `configPath`,其 TypeScript 接口却漏声明该字段。 ## 官方最佳实践 @@ -25,19 +27,20 @@ ## Current Control Flow 1. CLI 读取 PaC 或 AgentRun YAML。 -2. parser 解析目标、consumer 或 lane。 -3. capture 调用使用 TypeScript 文件内固定的 timeout 常量。 -4. typed observation 披露实际 timeout 数值,但无法证明该数值的 owning config path。 -5. YAML 缺失预算时仍可继续执行,不能按当前选中对象 fail-closed。 +2. parser 解析目标、consumer 或全部 lane。 +3. `18d0bd89` 后 capture 已使用 YAML timeout,但 AgentRun 只有一个 lane 声明预算,导致 parser 在发起只读 capture 前失败。 +4. PaC text/JSON 已披露 timeout 与 config path;AgentRun JSON 有 `readCaptureBudget`,默认文本未披露该字段。 +5. 嵌套 PaC 与 runtime capture 串行执行;当前有效预算为 `25000ms + 20000ms`,需要用原入口实测其是否与外层 `trans` 60 秒短连接竞争。 ## Desired Control Flow 1. PaC parser 必填读取 `config/platform-infra/pipelines-as-code.yaml#observability.readOnlyCapture.timeoutMs`。 -2. AgentRun lane parser 必填读取 `config/agentrun.yaml#controlPlane.lanes..status.readOnlyCapture.timeoutMs`。 +2. AgentRun 每个 owning lane 都必须声明 `config/agentrun.yaml#controlPlane.lanes..status.readOnlyCapture.timeoutMs`,parser 对全部 lane 必填校验且不补默认值。 3. parser 将预算值与精确 config path 放入 typed spec,不提供源码默认值。 4. capture 只使用已验证的 YAML 预算。 5. PaC text/JSON observation 与 AgentRun status text/JSON 摘要同时披露 config path 和实际 timeout。 6. 缺失字段时 parser 在发起远端读取前失败,并指出缺失 YAML 路径。 +7. status JSON 显式声明 `mutation: false`;失败保持非空 typed 结果,不触发 PipelineRun、Argo、mirror 或 runtime 写操作。 ## 修改 @@ -49,22 +52,38 @@ - `status/history/debug-step` capture 使用 YAML 预算。 - typed observation 与默认 text 输出披露预算路径和实际数值。 - `config/agentrun.yaml` - - 在 `controlPlane.lanes.nc01-v02.status.readOnlyCapture.timeoutMs` 声明 `20000`。 + - 在 `controlPlane.lanes.nc01-v02.status.readOnlyCapture.timeoutMs` 与 `controlPlane.lanes.nc01-release.status.readOnlyCapture.timeoutMs` 分别声明 `20000`,保证所有 owning lane 通过必填 parser。 - `scripts/src/agentrun-lanes.ts` - lane parser 必填解析 status capture 预算与精确 config path。 - `scripts/src/agentrun/control-plane.ts` - 删除 `agentRunStatusCaptureTimeoutMs` 源码常量。 - source、runtime 和 git-mirror 只读 capture 使用选中 lane 的 YAML 预算。 - status 结果披露 `readCaptureBudget`。 + - status 结果显式披露 `mutation: false`。 +- `scripts/src/agentrun/public-exposure.ts` + - 默认文本新增 `AGENTRUN READ CAPTURE BUDGET`,披露选中 lane 的实际 timeout 与 config path。 + - 嵌套 `PAC READ OBSERVATION` 同时披露 PaC budget config path。 +- `scripts/src/platform-infra-pipelines-as-code.ts` + - 补齐 `PacReadOnlyCaptureObservation.configPath` 类型声明,使 typed JSON 与 TypeScript 合同一致。 ## 验证证据 -- 定向 Bun 测试验证 PaC typed observation 保留 timeout、config path、首断点与只读 Next。 -- TypeScript 语法检查覆盖修改的 parser、PaC 与 AgentRun control-plane 文件。 -- YAML 读取和原 CLI 入口验证证明预算由选中配置解析,并在 text/JSON 中可见。 +- 定向 Bun 测试:`7 pass / 0 fail`,覆盖 PaC typed observation 与 AgentRun text 两层 YAML budget path。 +- `bun --check` 覆盖修改的 parser、PaC、AgentRun control-plane、renderer 与 capture helper。 +- PaC status JSON:约 `28s` 返回 `status=timeout`、`timeoutMs=25000`、PaC config path、`mutation=false`,exit `1`,stdout 非空。 +- PaC history text:约 `29s` 返回 `pac-read-timeout`、PaC config path 与只读 Next,exit `1`,stdout 非空。 +- PaC debug-step text:约 `3s` 成功,`34/34` fixtures,通过同一 YAML budget path,exit `0`。 +- AgentRun status text:内部 PaC `25609ms`、runtime `20150ms`、total `45759ms`;外层 `trans` 总耗时约 `47.273s`,在 60 秒短连接预算内稳定返回,披露 PaC `25000ms` 与 lane `20000ms` 两层 config path,exit `1`,stdout 非空。 +- AgentRun `--raw` JSON:约 `45.242s` 返回;完整 JSON 的 `readCaptureBudget` 与 `summary.pac.readObservation` 分别披露 AgentRun/PaC config path,且 status 结果声明 `mutation=false`。 - `git diff --check` 验证补丁格式。 - 所有目标源码、Git、验证和原入口观察均通过 `trans NC01:/root/unidesk/.worktree/2165-pac-status-visible ...` 执行。 +## 取消与超时结论 + +- 上次任务取消不能归因于当前 CLI 嵌套预算:修正 YAML 完整性后,最慢 AgentRun 原入口内部总耗时约 `45.759s`,外层 `trans` 约 `47.273s`,未触发 60 秒短连接 timeout。 +- 当前链路确实串行消费 PaC `25000ms` 与 runtime `20000ms`,但仍保留约 14 秒外层余量;本轮没有证据支持新增 job、锁、租约、第二状态库或额外 authority。 +- 取消更符合 Artificer/session 外部终止,而不是 CLI 预算嵌套 first break;本报告只记录可复现证据,不猜测未观测的调度原因。 + ## 权限与变更边界 - 保留现有 typed observation、即时 progress、只读 Next 和非 mutation 行为。 diff --git a/scripts/src/agentrun/control-plane.ts b/scripts/src/agentrun/control-plane.ts index 02770ef8..01f21fe8 100644 --- a/scripts/src/agentrun/control-plane.ts +++ b/scripts/src/agentrun/control-plane.ts @@ -527,6 +527,7 @@ export async function statusPipelinesAsCodeLane(config: UniDeskConfig, options: ok: runtimeProbe.value.exitCode === 0 && pacStatus.ok === true && blockers.length === 0, command: "agentrun control-plane status", mode: "pipelines-as-code-migrated-lane", + mutation: false, configPath: target.configPath, readCaptureBudget: spec.status.readOnlyCapture, target: options.full || options.raw ? agentRunLaneSummary(spec) : compactAgentRunLaneStatusTarget(spec), @@ -902,6 +903,7 @@ export async function statusYamlLane(config: UniDeskConfig, options: StatusOptio ok: sourceProbe.value.exitCode === 0 && runtimeProbe.value.exitCode === 0 && mirrorProbe.value.exitCode === 0 && blockers.length === 0, command: "agentrun control-plane status", mode: "yaml-declared-node-lane", + mutation: false, configPath: target.configPath, readCaptureBudget: spec.status.readOnlyCapture, target: options.full || options.raw ? agentRunLaneSummary(spec) : compactAgentRunLaneStatusTarget(spec), diff --git a/scripts/src/agentrun/public-exposure.ts b/scripts/src/agentrun/public-exposure.ts index 2a203f1b..8877bfad 100644 --- a/scripts/src/agentrun/public-exposure.ts +++ b/scripts/src/agentrun/public-exposure.ts @@ -75,6 +75,7 @@ export function renderAgentRunControlPlaneStatusSummary(result: Record `- ${warning}`)].join("\n"), "", "NEXT", @@ -176,6 +184,12 @@ export function renderAgentRunControlPlaneStatusSummary(result: Record `- ${warning}`)].join("\n"), "", "NEXT", diff --git a/scripts/src/platform-infra-pac-failure-evidence.test.ts b/scripts/src/platform-infra-pac-failure-evidence.test.ts index 0c4846be..d877dc70 100644 --- a/scripts/src/platform-infra-pac-failure-evidence.test.ts +++ b/scripts/src/platform-infra-pac-failure-evidence.test.ts @@ -5,6 +5,7 @@ import { resolve } from "node:path"; import { createRequire } from "node:module"; import { sentinelPublishImageBuildShell } from "./hwlab-node-web-sentinel-cicd-jobs"; import type { SentinelCicdState } from "./hwlab-node-web-sentinel-cicd-shared"; +import { renderAgentRunControlPlaneStatusSummary } from "./agentrun/public-exposure"; import { compactHistoryJson, compactStatusSummary, pacReadOnlyCaptureObservation, renderDebugStep, renderHistory, renderStatus } from "./platform-infra-pipelines-as-code"; const require = createRequire(import.meta.url); @@ -87,6 +88,41 @@ describe("PaC 失败证据合同", () => { expect(output).toContain("next-read-only:"); }); + test("AgentRun status 文本披露 lane 与 PaC 两层 YAML capture 预算", () => { + const output = renderAgentRunControlPlaneStatusSummary({ + ok: false, + target: { node: { id: "NC01" }, lane: "nc01-v02" }, + summary: { + migration: { migrated: true, sourceAuthority: "gitea-pipelines-as-code" }, + pac: { + readObservation: { + stage: "status-remote-observe", + status: "timeout", + elapsedMs: 25001, + timeoutMs: 25000, + configPath: "config/platform-infra/pipelines-as-code.yaml#observability.readOnlyCapture.timeoutMs", + exitCode: 124, + firstBreak: { code: "pac-read-timeout" }, + next: "bun scripts/cli.ts platform-infra pipelines-as-code status --full", + }, + }, + blockers: ["pac-not-ready"], + warnings: [], + nextAction: {}, + }, + readCaptureBudget: { + timeoutMs: 20000, + configPath: "config/agentrun.yaml.controlPlane.lanes.nc01-v02.status.readOnlyCapture.timeoutMs", + }, + next: {}, + timings: {}, + }).renderedText; + expect(output).toContain("PAC READ OBSERVATION"); + expect(output).toContain("config/platform-infra/pipelines-as-code.yaml#observability.readOnlyCapture.timeoutMs"); + expect(output).toContain("AGENTRUN READ CAPTURE BUDGET"); + expect(output).toContain("config/agentrun.yaml.controlPlane.lanes.nc01-v02.status.readOnlyCapture.timeoutMs"); + }); + test("feature-config warning 在 status、history、debug-step 同构有界投影", () => { const observation = extractPacSourceObservation([ { diff --git a/scripts/src/platform-infra-pipelines-as-code.ts b/scripts/src/platform-infra-pipelines-as-code.ts index f889a6e5..612835b4 100644 --- a/scripts/src/platform-infra-pipelines-as-code.ts +++ b/scripts/src/platform-infra-pipelines-as-code.ts @@ -307,6 +307,7 @@ interface PacReadOnlyCaptureObservation { readonly stage: string; readonly elapsedMs: number; readonly timeoutMs: number; + readonly configPath: string; readonly exitCode: number; readonly stdoutBytes: number; readonly stderrBytes: number;