Files
qinglong/docs/adr/ADR-0368-bounded-project-run-discovery-http-api.md
T

50 lines
6.2 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-0368:有界、Project-scoped 的 Run Discovery HTTP API
- 状态:Proposed
- 日期:2026-08-11
- 关联:QL-RFC-0001 D-45、D-46、D-49、D-52、D-269、D-278、D-279、D-280ADR-0052、ADR-0349、ADR-0367
## 背景
Local 与 Cluster 已有同构的 `GET /api/v3/projects/{projectId}/runs/{runId}`,但调用方必须预先知道 `runId`。这使 HTTP API 无法从“最近运行”进入诊断闭环。MCP 已证明 Project-scoped keyset pagination、双方言 repository 和低敏字段白名单可用;HTTP 不应复制另一套排序、投影或存储协议。
该切片不得因增加第二条 route 再拆 package。Local 继续使用现有可选 `@qinglong/local-api` 单进程组合根;Cluster 继续使用现有 `@qinglong/cluster-control` route registry、admission pipeline 与 PostgreSQL Pool。
## 决策
1. Local 与 Cluster 同时增加 `GET /api/v3/projects/{projectId}/runs`operation/permission 固定为 `run.list`/`run.read`。每页都必须经过 route/query validation → Authentication → Project Policy → durable security audit → credential fenceLocal)→ bounded repository read。
2. query 只允许 `limit``after_created_at_ms``after_run_id``limit` 默认 32、最大 64;两个 cursor 参数必须同时出现,时间必须是 canonical 非负 safe integerRun ID 必须满足现有 128 字符资源标识约束。重复、空值、未知、部分 cursor 或非 canonical decoded 数字在 Authentication 前失败关闭。Local loopback transport 额外拒绝 raw percent encodingCluster transport 先按 Node URL 语义解码,再对 decoded query 做 exact semantic validation,因此不宣称保留或拒绝原始编码形式。
3. Runtime Core 在现有 `src/run/projection/` 增加纯 `bounded-run-list-projection` subpath,统一输入、`limit + 1` storage fetch、排序复验、Project 遮蔽、低敏投影、`hasMore``next`。MCP `qinglong.run.list` 改为薄协议 adapter 并保持公开 Tool schema/错误不变;Local/Cluster HTTP 不依赖 MCP 或 Tool Registry/SemVer。
4. 返回固定 `{runs, hasMore, next?}`。Run item 只包含 id、taskId/revision、status/version/eventSequence、priority、execution origin/owner 和四个生命周期时间;不包含 Project、request/ref/trigger/attempt、executor、错误、Artifact、日志或 Secret 邻接字段。
5. 双方言复用现有 `(project_id, created_at_ms, id)` 索引与 `ProjectRunListReader`;不新增 migration、table、connection、transaction authority 或缓存。排序固定 `created_at_ms DESC, id DESC`,cursor 为上一页最后一个已返回项。
6. Local HTTP response 上限从只容纳 point read 的 4 KiB 调整为覆盖 64 条最坏字段长度低敏记录的固定 64 KiB;静态最坏 JSON envelope 为 61,516 bytes。Edge 并发仍为 4、Standalone 仍为 32,请求/URL/header/drain 上限不变。默认 headless Local artifact/image 仍不安装 `local-api`
7. workspace 保持 18 package。新增代码只能进入 Runtime Core `run/projection`、Local API 既有 `run/admission/transport/application-runtime` 与 Cluster Control 既有 `run/application-runtime` 领域目录。不得以 route、projection、codec 或“一文件一目录”的形式制造新的 package/层级;单文件 capability 默认留在 owning domain,只有独立部署、authority、第三方运行闭包或被多生命周期复用的稳定安全叶子才允许形成 package 边界。
## 不采用方案
- **只给 Local 增加列表**:会让相同 `/api/v3` 在单机和集群具有不同的基础发现闭环。
- **HTTP 直接导入 MCP projection**:会把 MCP SDK/Tool Registry/SemVer 闭包带入普通 API 制品。
- **offset pagination**:深页成本随历史增长,且并发插入时容易重复或遗漏。
- **把 cursor 编成 opaque token**:首版没有签名/密钥 authority,编码并不增加可信度,反而扩大解析面。
- **返回数据库 Run row 或错误摘要**:`run.read` 不是调试、Artifact、日志或 Secret 权限。
- **新增 run-query package/service**:没有新的部署或权限边界,只增加 manifest、importer、SBOM 和低配安装成本。
## 完成门
- shared projection 覆盖默认/最大页、cursor、空页、排序、跨 Project/畸形 row、repository failure 与 module closure
- Local 覆盖 canonical query、认证前拒绝、`run.list` audit、credential fence、真实 SQLite HTTP E2E、过载与 drain
- Cluster 覆盖 reviewed route/operation allowlist、query、Policy/audit 顺序、真实 PostgreSQL repository contract
- Runtime Core、MCP、Local API、Cluster Control/PostgreSQL 定向与完整 package/backend 通过;
- package/source/dependency boundary、默认 Local image、Local/Cluster artifact 与 PostgreSQL HA 不回归。
## 实现证据(2026-08-11
- Runtime Core 466/466、Local MCP 38/38、Local API 19/19、Cluster Control 189 pass/2 条件 skip18-package 完整门退出 0backend 1,156 pass/2 条件 skip。
- workspace 仍为 18 package/1,018 source,其中 1,000 个位于 package 内部 domain/capability 目录,18 个 `src/` 根文件全部是受审 public/binary entry;无 single-source、shallow-source 或 package-boundary finding。
- dependency 回归 52/52,完整 dependency audit `findings=[]`MCP 只新增 `@qinglong/runtime-core/bounded-run-list-projection` 这一条纯读 subpath allowlist,默认 Local image 继续不安装 `local-api`
- 14 个 Local Profile artifact 全部 compatible。Edge/Standalone API 分别为 5,085,520/5,085,664 bytes、518 files、57 loaded modulesRSS 增量 12,681,216/13,041,664 bytes,低于 6 MiB/640 files/24 MiBEdge/Standalone 并发仍为 4/32。
- 最紧的 Edge/Standalone Application+AI 分别为 6,281,356/6,281,488 bytes,距 6 MiB 上限仅 10,100/9,968 bytes;预算未放宽,后续闭包增长必须先裁剪,不能通过新增 wrapper/package 转移成本。
- PostgreSQL 18.4 arm64 HA Docker contract 112 gates 全部通过,timeline 1→2report SHA-256 为 `a25e7226286f063d9b2e85a2a25c20834021627aa192e069f3bb4d40572c0447`;结束后临时容器、卷、网络均为零。
固定物理路由器上的 API Profile RSS/延迟报告仍未取得,因此 ADR 保持 Proposed;该缺口不影响本轮自动化实现完成结论。