Files
qinglong/docs/adr/ADR-0402-bounded-latest-task-run-outcome-comparison.md
T

104 lines
7.4 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-0402:有界的 Task 最近成功/失败 Run 对比
- 状态:Accepted
- 日期:2026-08-14
- 关联 RFCQL-RFC-0001 D-310、Phase 2
- 关联 ADRADR-0347、ADR-0400、ADR-0401
## 问题
ADR-0400/0401 已提供按两个显式 Run ID 对比的共享受信 Tool 和可选本机 MCP 入口,但“选择
最近一次成功与失败运行”仍可能被留给模型。模型如果循环调用 `run.list`、翻页并自行过滤,
搜索次数、历史范围和停止条件都不可证明;在低配路由设备上会形成不可控 SQLite I/O,在
Cluster 上也会放大数据库读取和模型上下文,同时给 Prompt 注入制造更大的输入面。
直接把 Task/status 过滤继续塞入通用 Run Reader 也不合理。GitNexus 显示
`LocalSqliteRunReader` 为 CRITICAL(14 个直接、29 个总影响、1 条产品启动流程),
`PostgresRunReader` 为 HIGH(7 个直接、15 个总影响)。为了一个 AI 只读用例扩大这两个基础
Reader 的职责,会让本地恢复、API、Cluster recovery 和写事务继承树承担无关回归风险。
## 决策
1. 增加 `qinglong.task.runs.compare@1.0.0`。输入精确为一个不超过 255 字符的 `taskId`
没有 `limit`、cursor、status、SQL、排序或 Project 参数;Project 只来自受信 composition。
Definition 固定为 `read/low``run.read`、5 秒 deadline。
2. Runtime Core 在 `run/outcome-comparison/` 定义窄
`TaskRunOutcomeWindowReader`。repository 一次只返回按
`(created_at_ms DESC, id DESC)` 排列的最小五字段记录:Run/Project/Task identity、status 和
created time。Tool 固定请求 65 条,其中前 64 条是搜索窗口,第 65 条只证明还有更老记录;
协议永远不返回 cursor,模型不能扩大窗口。
3. 前 64 条中第一个 `succeeded` 是 baseline,第一个 `failed` 是 candidate。两者都找到,
或存储明确返回不超过 64 条时,`selection.complete=true`;如果窗口已满且缺少任一结果,
输出 `complete=false`,不能把“窗口内未找到”伪装成历史上不存在。
4. 选择后按 succeeded → failed 固定顺序执行最多两个既有 Project-scoped Run 点查,并复用
ADR-0400 的低敏 projection、changed fields 和时长差值算法。选择出的 Run 如果消失、变更
Task/outcome、跨 Project 或损坏,整个调用稳定 unavailable;不回退到继续搜索。
5. 输出携带
`consistency=bounded_task_window_then_ordered_point_reads`。它明确表示一次有界选择查询后跟
两次独立点查,不声称数据库事务快照。baseline 永远代表 succeededcandidate 永远代表
failed,差值方向固定为 failed succeeded。
6. SQLite 与 PostgreSQL 各自新增独立、二级目录内的窄 adapter,不修改现有通用 Run Reader
或 Repository 继承树。SQLite 真实 `EXPLAIN QUERY PLAN` 必须命中既有
`ql3_local_runs_task_created_idx`PostgreSQL adapter 使用同样的 Project/Task/order/`LIMIT 65`
查询。复用既有索引避免新增 migration、索引空间和每次 Run 写放大。
7. 可选 `ql3-mcp` 复用既有逐调用 Owner credential → Tool Policy → durable allowed audit →
credential confirm 链,审计理由固定为 `tool_qinglong_task_runs_compare`。默认 Edge/Standalone
仍不装载 MCPCluster 只获得显式 PostgreSQL adapter subpath,不开放 ambient MCP/HTTP
endpoint,内部 Copilot 仍须走完整 Trusted Tool completion 链。
## 低配、集群与 package 布局
- 默认低配设备没有新增进程、连接、timer、listener、watcher、cache、migration 或索引;启用
MCP 时每次调用固定为一个 65 行上限的最小列查询和最多两个串行点查。
- Cluster adapter 复用调用方现有 queryable/Pool,不创建第二连接池;driver 错误收敛为稳定
repository operation error,畸形 bigint/status 收敛为 constraint error。
- package 数保持 18。能力落入 Runtime Core 既有 `run/outcome-comparison/`
`tool-execution/builtin-run-compare/`,持久化实现落入两端既有
`run/outcome-comparison/`;不为一两个文件创建微型 package,也不回到 package `src` 根平铺。
- Runtime Core、Local SQLite、Cluster PostgreSQL 只提供显式 subpathpackage 根入口不扩大;
Local MCP dependency firewall 只放行两个新 Runtime Core 精确 subpath。
## 被否决方案
1. **让模型分页 `run.list`**:查询次数、停止条件和上下文大小不可证明。
2. **把 task/status/cursor 加入通用 Run list**:会把 AI 用例扩散到 HTTP/API 和基础 Reader
且仍允许模型无界翻页。
3. **分别查询所有成功和失败历史**:即使只返回一条,缺少固定窗口时存储扫描和语义仍无界。
4. **为两种 outcome 新增索引**:会增加所有设备的磁盘与 Run 写放大;固定 64 条窗口可复用
已有 Task 时间索引,当前收益不足以支付 migration 和低配常驻成本。
5. **修改 `LocalSqliteRunReader`/`PostgresRunReader`**GitNexus 已证明 HIGH/CRITICAL blast
radius,独立 reader 能用更小权限和故障域完成目标。
6. **只返回两个 Run ID**:仍迫使调用方再次自由组合字段和差值;本 Tool 应交付完整的有界
低敏对比结果。
## 当前验证
1. Runtime Core 定向 10/10:覆盖成功/失败选择顺序、差值、固定 64+1 窗口、无 cursor、
incomplete 语义、跨 Project/乱序/畸形/消失/存储失败、Definition/binding 漂移、显式
subpath 和根入口零导出。
2. Local SQLite 1/1fresh production migration 后写入多 Project/Task/状态 Run,经 MCP 的
单 authority 读取精确窗口,并用真实 `EXPLAIN QUERY PLAN` 证明命中既有 Task 时间索引。
PostgreSQL adapter 2/2:精确 SQL/参数/顺序/上限、bigint 映射、畸形状态和 driver 错误
收敛全部通过。
3. Local MCP 47/47:新增 Tool discovery、固定 read-only annotations、双 permission、durable
audit reason、credential confirm、真实 stdio + fresh SQLite 的选择和两次点查均通过。
4. dependency firewall 53/53,完整 dependency audit 与 package boundary audit 零 finding
package 保持 18 个,`singleSourcePackages=[]``shallowSourcePackages=[]`
5. 最终 18-package clean build/test 退出 0backend 1,208 项为 1,206 pass、2 条平台条件
skip、0 fail。Edge import 与 Cluster deployment 审计零 finding。Runtime Core 为 165/164
nested、Local SQLite 为 197/196 nested、Cluster PostgreSQL 为 168/167 nested,三者 package
根入口均未增长。
6. 默认 Edge artifact 保持 2,589,812 bytes/315 files/56 modules,证明未装配能力被完全裁掉;
RSS 增量 11,173,888 bytes,低于 16 MiB 门。Edge-MCP 为 7,237,187 bytes/795 files/
220 modules/RSS 38,699,008 bytesStandalone-MCP 为 7,237,295 bytes/795 files/
220 modules/RSS 38,600,704 bytes,均低于 16 MiB/1,536 files/48 MiB 门。
7. PostgreSQL 18.4 arm64 HA 125/125 Gate 通过,timeline `1→2`,报告 SHA-256 为
`229c7cac328ee960f667f92868374264a10cb75090ef93d644da1326385d8774`;容器、网络与卷零残留。
## 后续门禁
1. Cluster 产品 composition 装配 Definition/binding,并以真实 PostgreSQL 完成加密结果和
StepRun/Trace completion
2. 日志解释另建 Artifact range、redaction、prompt-injection 和字节预算协议;
3. 固定物理 Edge MCP 单次选择/点查延迟与 RSS,并验证撤权竞态。