Files
qinglong/docs/adr/ADR-0371-bounded-project-run-step-list-http-and-mcp-api.md
T

49 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0371:有界、Project-scoped 的 Run StepRun HTTP 与 MCP API
- 状态:Accepted
- 日期:2026-08-11
- 关联:QL-RFC-0001 D-279、D-280、D-281、D-282、D-283ADR-0367、ADR-0368、ADR-0369、ADR-0370
## 背景
D-282 已让 Local、Cluster 与 MCP 观察 RunEvent 时间线,但事件只能解释聚合状态变化,不能给出 Workflow DAG 中每个 Step 的当前状态、重试次数和父子关系。Cluster 的 Plugin Package Workflow 专用管理路由已有 StepRun 查询语义,通用 Run API 与本机 MCP 却仍缺少同一能力。直接复用专用管理模块会把 Package/Workflow identity、管理审计与更大的闭包带入基础诊断面;在三个产品面复制投影则会产生字段和分页漂移。
## 决策
1. Local 与 Cluster 增加同构 `GET /api/v3/projects/{projectId}/runs/{runId}/steps`,固定 operation `run.steps.list`、permission `run.read`。不得接受 bodyquery 只允许 canonical `after_step_key``after_step_run_id``limit`,cursor 必须成对,默认 32、最大 64。
2. Runtime Core 在既有 `run/projection/` 中拥有唯一 `boundedRunStepListProjection`。它先用 `RunRepositoryReader.findRunById` 验证 Project 归属,再调用只读 `StepRunRepository.listByRun`;不存在与跨 Project 返回同一 `found=false`repository、shape、cursor、identity、state 或 order 异常失败关闭。
3. 成功响应只包含 `steps``hasMore``next`。每项只公开 `id``parentStepRunId``stepKey``kind``required``status``version``attemptCount``readyAtMs``startedAtMs``finishedAtMs``resultCode``createdAtMs``updatedAtMs`。禁止 `definitionRef``definitionDigest``inputRef``outputRef``approvalRequestId``errorSummary``lastMutationId``stepRunDigest` 越过投影。
4. 顺序固定为 `(stepKey ASC, id ASC)` keyset`next` 只在 `hasMore=true` 时返回最后一条已返回记录的 `{stepKey, stepRunId}`。该 API 是每页重新认证、授权和审计的 forward-only 当前视图,不宣称跨页快照。
5. HTTP 的不存在和跨 Project 统一映射为 404 `run_not_found`。Local 复用 loopback listener、Edge 4/Standalone 32 并发、Bearer、Project Policy、持久 Audit、credential/Pepper confirm 与唯一 SQLite authorityCluster 复用 route registry、两阶段 admission、持久 Audit 与既有 PostgreSQL Pool。
6. 本机 MCP 增加低风险只读 `qinglong.run.steps.list` Tool,输入输出语义与共享投影一致。MCP read database 只暴露 `StepRunRepository.listByRun`HTTP 不导入 MCP/Tool RegistryMCP 不取得 StepRun `apply` mutation authority。
7. Local Application product-surface contract 把 Run 与 StepRun 能力收窄为所需只读方法:Run 只允许 `findRunById/listRunsByProject/listEvents`StepRun 只允许 `listByRun`。底层仍复用同一已打开 storage authority,不新增连接或第二个 repository owner。
8. 不新增 workspace package、第三方依赖、migration、index、repository method、数据库连接、listener、sidecar、timer、watcher、cache 或 authority。实现必须落在既有 Runtime Core、Local API、Local MCP、Local SQLite 与 Cluster Control 领域目录。
9. D-281 的全部文件、flash、module 与 RSS 门不得放宽。完整 backend/packages、14 artifact、默认 Local image、package/dependency boundary、Local Edge/Standalone live contract 与 PostgreSQL HA Docker 门必须通过。
## 不采用方案
- **复用 Plugin Package Workflow administration**:它绑定 Package/Workflow identity 和管理事务,不是通用 Run 低敏查询叶。
- **直接返回完整 StepRunRecord**definition、input/output、approval 和 error 字段可能是 secret-adjacent 或内部恢复事实。
- **只实现 HTTP 或只实现 MCP**:会让人在面板与 AI 排障时观察到不同的 Run 事实。
- **offset、无界列表或按更新时间排序**DAG Step identity 天然由稳定 `(stepKey,id)` 排序,offset 会随变化和规模放大。
- **新增 Step/timeline package**:共享纯投影没有独立 deployment、authority 或版本边界,不足以承担第 19 个 package 成本。
- **新增表或索引**:双方言现有 `StepRunRepository.listByRun``(run_id,step_key,id)` keyset 已满足查询。
## 完成门
- Runtime Core 覆盖默认/最大 page、cursor、严格 order、duplicate、状态时间不变量、低敏字段、缺失/跨 Project 与 repository failure
- Local route、transport、admission、product-surface contract 与真实 SQLite HTTP 覆盖同一 storage authority、安全 admission、404 遮蔽和最大响应;
- Cluster route、registry 与 production composition 复用既有 runtime `runs/stepRuns`
- MCP Tool definition、adapter、admission 与真实 SQLite MCP composition 证明只读能力且不加载 mutation authority
- 完整源码、制品、真实 Local image 与 PostgreSQL HA 门全绿,并记录最终 package/source、artifact、module、RSS 与 Docker 证据。
## 实现与验证证据
- Runtime Core 已提供唯一 `boundedRunStepListProjection`Local/Cluster HTTP 与 `qinglong.run.steps.list` MCP Tool 均只做协议适配。Runtime Core 476/476、Local API 27/27、Local MCP 41/41、Cluster Control 196 pass/2 环境条件 skip、Local Application 45 pass/4 平台条件 skip、Local SQLite 209/209。
- 真实 SQLite HTTP 覆盖 Bearer、Project Policy、持久审计、跨 Project 遮蔽、成对 cursor 与同一 storage authority。构造全部低敏字段上界的 64 条 StepRun 响应为 48,895 bytes,低于固定 64 KiB transport 门。
- 完整 18-package clean build/test 退出 0backend 1,160 pass/2 skip/0 fail。workspace 为 18 package/1,026 source,其中 1,008 nested、18 个 `src/` 根文件全部是登记的 public/binary entrypackage boundary 与 cluster dependency audit 均为 `compatible=true``findings=[]`,没有 single-source 或 shallow-source package。
- 14 个 Edge/Standalone artifact 全部 compatible。最紧 Edge/Standalone Application+AI 分别为 6,269,636/6,269,768 bytes、646 files、133 loaded modules,距 6 MiB 尚余 21,820/21,688 bytesRSS 分别为 21,233,664/21,364,736 bytes,未放宽 D-281 门。Edge/Standalone MCP 分别为 9,878,070/9,878,178 bytes、955 files、210 modules。
- AI-excluded arm64 Local image`sha256:8e1b1852ef101f4bb6ad900109dec7d700ece3fcf44ba7db9e958bbe3609cebc`)为 477 files/4,705,667 bytes、精确 10-package closure;同一镜像在 Edge 128 MiB/64 PIDs 与 Standalone 256 MiB/256 PIDs 的 read-only、network-none 门均 graceful stop 且 SQLite integrity `ok`
- PostgreSQL 18.4 arm64 HA 合同通过 112 gatestimeline 1→2;私有报告 SHA-256 为 `c83fb5eb49fccbb6e5a6b3bdcd8a86c9fea88e73b995624d82b9c4b71b9f0d97`,离线证据审计 `compatible=true``findings=[]`,测试容器、卷与网络清理后均无残留。
- 本切片只增加既有领域内的投影、route/adapter、只读 composition 与测试;没有新增 package、第三方依赖、migration、index、连接、listener、sidecar、timer、watcher、cache 或写 authority。