Files
qinglong/docs/adr/ADR-0450-closed-window-legacy-shadow-terminal-difference-audit.md
T

80 lines
6.6 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-0450:闭合窗口的 Legacy Shadow 终态差异审计
- 状态:Accepted
- 日期:2026-08-18
- 关联 RFCQL-RFC-0001 D-02、D-358、PR-4
- 关联 ADRADR-0002、ADR-0448、ADR-0449
- AmendsADR-0449 的跨测量窗口待办
## 上下文
ADR-0448/0449 只证明一次启动时 active Shadow Run 的恢复结果,不能回答一个明确时间段内已经写入的 Shadow Run 是否与 Legacy 执行终态一致。
直接用 startup scanned 计算比例会把未知尾页和未稳定执行混入分母;把全部 `RunningInstances` 当成 Shadow 应写集合也不成立,因为 QingLong 2.x
表没有可信的 execution originmanual、system crond 与其他 Legacy 路径可能复用同一 Cron。
部署跨度还要求同一能力既能在低性能路由设备运行,也能在 standalone 节点处理较大窗口。审计不能增加启动查询、常驻 timer、第二数据库 authority、
无界数组或高基数指标,更不能为了“补齐”历史数据而改写 Run/Attempt/RunningInstance。
## 决策
1. 新增显式、只读、一次性的 `audit:legacy-shadow-terminal:ql3` 运维入口。调用方必须给出至少一个已支持 Shadow origin 以及
`[windowStartMs, windowEndMs)`;窗口 cohort 以 `legacy-owned Shadow Run.created_at_ms` 为准,不自动选择“最近一段时间”。
2. 窗口只有在 `windowEndMs <= observedAtMs - minimumSettlingAgeMs` 时闭合,默认 settling age 为五分钟。窗口未闭合、候选页未扫完或
Legacy evidence 达到硬上限时,不输出任何比例。
3. Source 使用 `(created_at_ms, run_id)` 稳定 keyset,复用现有 `Runs(project_id, created_at_ms)` 索引;Run 与 latest Attempt 在同一有界查询中读取。
每页再执行一次有界 RunningInstance evidence 查询,不逐 candidate 发出 N+1 查询。单页候选硬上限 64、Legacy evidence 硬上限 512。
4. Profile 默认预算保持与 startup recovery 一致:edge `8 × 1 page`standalone `32 × 4 pages`。查询不安装 timer、watcher、线程、进程、连接池
常驻生命周期或后台续扫;预算耗尽返回 `incomplete`,操作者缩小窗口后重试。
5. 关联优先级固定为 direct Attempt/Run reference,其次同 Cron 下的 opaque log artifact ID、PID,最后才允许唯一且在容差内的 started time。
多个候选或 evidence 截断一律归入 ambiguous,不用“最近一条”猜测。原始 log path 只在 adapter 内哈希,不进入 application/report。
6. 每个 Shadow candidate 必须且只能归入八类之一:`matched``shadow_not_terminal``shadow_attempt_missing`
`shadow_attempt_ambiguous``legacy_evidence_missing``legacy_evidence_ambiguous``status_mismatch``field_mismatch`
aggregate 与按 origin matrix 都必须守恒。
7. status、exit code、started time、finished time 与 log artifact 使用固定 dimension counters 记录 compared/matched/mismatched/unavailable。
时间默认允许两秒精度差,硬上限一分钟;`terminalAgreementPermille``fullyComparablePermille` 只在窗口闭合、候选与 evidence 都完整且分母非零时出现。
8. 报告 schema 为 `qinglong/legacy-shadow-terminal-difference-report@v1`。报告只包含 Profile、窗口、预算、固定计数、最多七条 origin matrix 和
`matched/empty/differences_found/window_open/incomplete` assessment;不输出 Project/Run/Attempt/Cron/PID/log/task/user/error identity。
9. 报告明确声明 `direction=shadow_to_legacy``legacyWithoutShadow=not_measured`。当前数据模型无法可靠证明“存在 Legacy 执行但 Shadow Run 未写入”,
因而本报告不能单独作为 Primary gateD-360 必须组合 observer failure/capture evidence,或先引入可信 origin-scoped admission ledger。
10. 审计不写数据库、不修复差异、不启动 Executor、不改变 Legacy 返回值。`--fail-on-difference` 只把非 `matched` assessment 映射为进程退出码 1
供人工 rollout/CI gate 使用。
## 资源与部署影响
- 不新增 package、生产依赖、schema、migration、表、索引、数据库 authority、HTTP/gRPC 路由、timer、watcher、worker、容器或 Kubernetes 对象。
- edge 默认最多读取 8 个 Shadow candidate 与 64 条 Legacy evidencestandalone 默认最多 128 个 candidate 与每页 256 条 evidence。
- CLI 数据库连接固定只读、单连接池;报告为固定字段 aggregate,不保留 candidate identity,也不建立进程内累计 registry。
- `Runs(project_id, created_at_ms)` 负责 cohort keysetRunningInstance 查询每页一次且结果有硬上限。若未来固定设备证明表扫描 CPU 不可接受,应通过新 migration
增加 `(cron_id, started_at, id)` 索引并单独评审升级写放大,而不是在本 ADR 中静默修改已发布 migration checksum。
## 被拒绝的替代方案
### 双向扫描所有 RunningInstances 并计算 Shadow 捕获率
拒绝。2.x `RunningInstances` 没有可信 execution origin,无法知道某条记录是否属于已开启 Shadow 的入口;把它们都放进分母会制造假阴性。
### 在每次 HTTP startup 自动审计历史窗口
拒绝。它会重复 D-356 的启动查询、延迟路由设备监听,并需要隐式选择时间窗口。历史证据必须由操作者显式触发。
### 为每个 Shadow candidate 单独查 RunningInstances
拒绝。即使 candidate 数有上限,N+1 仍会让低性能 SQLite 设备承担不必要的查询调度和重复表扫描。
### 只输出差异明细 ID 方便排障
拒绝。运维日志会泄露高基数任务身份;需要明细时应新增受认证、Project-scoped 的诊断产品面,而不是扩大默认 CLI 报告。
## 验证
- 真实 SQLite 覆盖完全一致、status/exit field 差异、missing/ambiguous evidence、active Shadow、窗口未闭合、edge 页预算耗尽、evidence overflow、
双 origin 守恒、空分母和只读 CLI 脱敏输出。
- 真实 SQLite/CLI 聚焦测试 `14/14`、Legacy/Shadow 串行扩展 `91/91``build:back`、完整 backend
`1,455 pass / 0 fail / 2 conditional skip`、18-package clean build/test、14/14 static audit 与 14/14 artifact audit 全部通过。
- edge/standalone 的 base、adopted、application、application-api、AI、application+AI、MCP 产物分别为
`2,589,998 / 2,590,076``2,809,293 / 2,809,416``3,632,877 / 3,632,997`
`3,800,430 / 3,800,574``3,069,251 / 3,069,341``4,493,151 / 4,493,283`
`7,315,930 / 7,316,038` bytes,与 D-357 完全一致。
- 本切片不修改 PostgreSQL、容器或 Kubernetes 部署面,因此物理 PostgreSQL HA/K3s 门不作为本切片的新证据;D-359 仍负责 edge/standalone 资源压力与
Shadow-off 回滚演练。