mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): add strong local manual run retry
This commit is contained in:
@@ -11,6 +11,19 @@
|
||||
|
||||
最新增量证据(2026-08-12):
|
||||
|
||||
- D-293/ADR-0381(已接受)
|
||||
Local Edge/Standalone 已增加强认证的人工 `run.retry` 纵向切片。共享 `qinglong/run-manual-retry@v1` 契约位于 Runtime Core;
|
||||
手工 retry 只接受 `failed|cancelled|timed_out` 的顶层 runtime-owned 非 Workflow Run,源 Run 与历史保持终态不变,并创建以
|
||||
`retry_of_run_id` 关联的新 queued Run、claimed Attempt 与双 Event。SQLite 在单个 `BEGIN IMMEDIATE` 中重验 credential、
|
||||
Project/RoleBinding、source Run、Task/current execution revision fence,原子写入新 aggregate 与 allowed audit;精确 replay 不重复
|
||||
创建或审计。一次人工 retry 不继承自动 retry policy,`lost` 仍必须由 D-292 reconciliation 收敛。产品入口复用统一 Local CLI,
|
||||
通过私有命令文件与 Owner credential/pepper/POSIX proof 建立五分钟内 `local_console` User,再执行 `run.retry` Policy;现有 Local
|
||||
HTTP bearer 只有 `single_factor`,本批明确不开放 HTTP/MCP/AI Tool。Edge/Standalone 分别限制同 Project、同 User 每分钟 4/16
|
||||
个新 Run,复用既有 Runs ledger/index,不新增 package、migration、表、索引、timer、watcher、listener、常驻连接、cache 或 sidecar。
|
||||
完整 18-package clean build/test 退出 0,backend 1163 pass/2 skip/0 fail;workspace 为 18 package/1060 source/1042 nested,
|
||||
`singleSourcePackages=[]`、`shallowSourcePackages=[]`,dependency/package/Edge import 边界零 finding,14 种 Local Profile artifact 与
|
||||
Local image static audit 全部 compatible。Cluster/PostgreSQL adapter、多副本全局 quota、可信 HTTP MFA/hardware adapter、UI 与
|
||||
Workflow 专用 recovery 仍未包含。
|
||||
- D-292/ADR-0380(已接受)
|
||||
Local Edge/Standalone 已补齐 admission-safe lost Run retry 纵向闭环,并修正“共享纯策略却暴露 Cluster-only 名称”的边界。
|
||||
Runtime Core 在既有 Run 域发布 canonical `RunLostRetry*`/`run-lost-retry` subpath,同时保留早期 Cluster 名称兼容;SQLite
|
||||
@@ -19,8 +32,8 @@
|
||||
把一页 retry 接入现有 execution-control 周期,顺序为 completion→control→lost retry→慢速 cleanup;启动时在 recovery 后、Scheduler
|
||||
前执行首轮。Edge 为 2 条/5 秒,Standalone 为 16 条/1 秒,不新增 package、migration、表、索引、timer、连接、listener 或 sidecar。
|
||||
无 policy/禁用、unknown safety、attempt 耗尽均终态失败关闭;只有 admission 时已证明 `idempotent|deduplicated` 的策略才创建全新
|
||||
Attempt。workspace 仍为 18 package/1055 source/1037 nested,`singleSourcePackages=[]`、`shallowSourcePackages=[]`。人工
|
||||
`run.retry` API/CLI/UI、强认证、审计与 rate limit 仍未包含,不能把自动 lost retry 误报为人工恢复完成。
|
||||
Attempt。workspace 仍为 18 package/1055 source/1037 nested,`singleSourcePackages=[]`、`shallowSourcePackages=[]`。该批当时尚缺的
|
||||
人工 `run.retry` 产品语义、强认证 CLI、审计与 Local rate limit 已由 D-293 补齐;HTTP/UI/Cluster 对等实现仍未包含。
|
||||
- D-291/ADR-0379(已接受)
|
||||
Cluster Run Attempt 日志 retention 已完成多副本纵向闭环,且没有新增 package:共享 claim contract 位于 Runtime Core 既有 Run
|
||||
log-retention 目录;PostgreSQL v54 提供 durable control、immutable tombstone、terminal remote Worker candidate index 与最小权限
|
||||
@@ -8495,7 +8508,7 @@ flowchart LR
|
||||
|
||||
> ADR-0058 至 ADR-0092 的以下段落是历史快照,其 PostgreSQL 数字与“下一切片”已由 ADR-0104/0105 及后续 Accepted ADR 取代;资源、Package 和物理证据边界仍保留作为演进记录:PostgreSQL 当时 baseline 为 11 条 reviewed migration、capability v10、19 张表和 migration/runtime/admin/worker-ingress 四角色;本机 SQLite 为二十八条 reviewed migration、capability v14、二十六张 owned table,并新增正式 TaskDefinition head/immutable revision Repository。当前有二十七个受审 3.0 importer:Owner maintenance 已合并为两个权限隔离 subpath,execution/control/recovery/dispatch 已合并为 `@qinglong/local-execution` 四个单向 subpath,bootstrap/credential-recovery 已合并为 `@qinglong/local-owner-ceremony` 两个互不依赖 subpath;三个 package 都不提供聚合根入口,hard cap 已同步降为 27。一次拓扑 build/test 会先清理已登记 QL3 package 的 stale dist;27 包全量测试、dependency/source boundary、联网 vulnerability audit 与六种 Profile 制品门禁均通过。ADR-0090 让 production packlist 只发布 JS、`.d.ts` 和受审 assets/drizzle,不再携带开发 map;ADR-0092 后当前最大 application 为 1,691,009 bytes、267 files、61 loaded modules,最大抽样 RSS delta 11,780,096 bytes,低于 4 MiB/512 files/16 MiB。所有 package build 已变为 self-only,全量从约 198 次编译降至精确 27 次,clean 状态单包测试仍按依赖闭包工作。资源门禁已拆为原生 Linux x64/arm64 的 128 MiB router stress、256 MiB Edge release guard 与 512 MiB Cluster control guard,并从容器内部验证 cgroup v2、零 swap/OOM、非 root、只读挂载、seccomp 和 `NoNewPrivs`;本轮本地原生 arm64 三档已通过,x64 等待远端 CI,所有档位均明确不是物理设备或生产容量承诺。物理 Edge candidate recorder 已绑定设备 manifest、实际 Linux/存储环境、Edge/SQLite 基准与 no-replace SHA-256 报告,并会拒绝容器/VM;同设备同 boot 的 idle sampler、不主动填盘的专用文件系统 fault probe,以及通过正式 Repository 写入/扫描 100/1000/10000 个 `qinglong/command@v1` TaskDefinition 的规模记录协议均已具备契约门禁,但尚未取得固定实机报告或 signature。ADR-0091 已冻结 1–32 个 exact descriptor 的不可变 TaskSpec semantic registry、内建 command v1 和本机写前门禁;历史 revision 在 provider 缺失时仍可读。ADR-0092 已实现绑定 source revision/content digest 的 Profile-neutral command plan 与确定性本机 context/execution 映射,且保持 subpath-only。
|
||||
|
||||
> 下表 PR-1 的旧“未完成”累计文字中,`completion/cancellation/timeout` 已由 ADR-0072 取代并闭环,Artifact range read 与 Local/Cluster retention 已由 ADR-0377/0378/0379 闭环,Local admission-safe lost retry lifecycle 已由 ADR-0380 闭环;当前本机剩余项是人工 recovery、手工 retry 产品策略/API、HTTP/CLI/UI、部署 controller 与固定路由设备实机门。远端 Worker completion、Workflow cancellation 和 Cluster expiry/retry 的核心数据库链、HA 重放与启动装配也已闭环;仍缺真实 Kubernetes 多节点故障证据,不能与本机结论混用。
|
||||
> 下表 PR-1 的旧“未完成”累计文字中,`completion/cancellation/timeout` 已由 ADR-0072 取代并闭环,Artifact range read 与 Local/Cluster retention 已由 ADR-0377/0378/0379 闭环,Local admission-safe lost retry lifecycle 已由 ADR-0380 闭环,强认证的 Local 手动 retry CLI、审计与 rate limit 已由 ADR-0381 闭环;当前本机剩余项是可信 HTTP MFA/hardware adapter、UI、Workflow recovery、部署 controller 与固定路由设备实机门。远端 Worker completion、Workflow cancellation 和 Cluster expiry/retry 的核心数据库链、HA 重放与启动装配也已闭环;仍缺 Cluster 手动 retry/PostgreSQL 全局 quota 与真实 Kubernetes 多节点故障证据,不能与本机结论混用。
|
||||
>
|
||||
> 下表 PR-0 的累积长文本仍含“四角色、21 条 migration、capability v20、21 项 HA”历史短语;当前权威基线应读取为六角色、23 条 migration、capability v22/36 表和 23 项 physical HA gate。ADR-0145/0146 又增加默认关闭的 manager-only TLS 1.3 management process、可选双副本 operation 与 durable distributed quota;它仍缺全副本重启 keyset anti-rollback 和真实 IdP/live ingress,所以“受认证管理入口”保持“已孵化但生产失败关闭”。
|
||||
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# ADR-0381:强认证的 Local 手动 Run retry
|
||||
|
||||
- 状态:Accepted
|
||||
- 日期:2026-08-12
|
||||
- 关联 RFC:QL-RFC-0001 D-293
|
||||
- 前置决策:ADR-0001、ADR-0035、ADR-0066、ADR-0365、ADR-0367、ADR-0380
|
||||
|
||||
## 上下文
|
||||
|
||||
ADR-0380 完成的是 admission-time policy 驱动的 lost Run 自动重试,不是用户发起的恢复操作。QingLong 3.0 已经定义 `run.retry` permission,Owner、Admin 与 Operator 角色也已获得该权限,但此前没有冻结以下产品语义:终态 Run 是否原地重开、怎样绑定认证与 Policy fence、重放如何避免重复创建 Run、低配路由设备如何限制写放大,以及 Local 与 Cluster 是否必须同时开放。
|
||||
|
||||
现有 Local HTTP bearer 只能建立 `single_factor` Principal。把它直接用于恢复执行会降低 ADR-0035 的强认证边界;为了 HTTP 对称而复制一个弱认证入口也会让 transport 决定领域安全语义。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. 手动 retry 创建新 Run,不改写源 Run
|
||||
|
||||
`run.retry` 只接受同一 Project 内状态为 `failed`、`cancelled` 或 `timed_out` 的顶层、runtime-owned、非 Workflow Run。源 Run 的状态、版本、Attempt 与 Event 历史保持不变;成功操作创建一个新的 queued Run,并以 `retry_of_run_id` 指向源 Run。
|
||||
|
||||
新 Run 复制源 Run 已冻结的 Task revision、snapshot reference、input 与 priority,但必须在提交事务内再次证明:
|
||||
|
||||
- 源 Run 的期望状态与版本未变化;
|
||||
- source snapshot 与 Task revision 一致,且对应 execution revision 仍可解析为 Local process;
|
||||
- 当前 Task 仍然启用;
|
||||
- 源 Run 不是 nested Run 或 `plugin_package_workflow`。
|
||||
|
||||
一次手动 retry 只创建一个新的顶层 Run,不继承源 Run 的自动 retry policy。`lost` 必须先由 ADR-0380 的自动 reconciliation 收敛,不能借人工入口绕过 unknown-outcome safety。
|
||||
|
||||
### 2. 共享契约不绑定 transport 或 Profile
|
||||
|
||||
canonical `RunManualRetry*` request、command、result、错误和 Repository contract 位于 `@qinglong/runtime-core/run-manual-retry`。契约要求调用方提供:
|
||||
|
||||
- canonical mutation ID、source Run identity、期望状态与版本;
|
||||
- 服务端生成的新 Run、Attempt 与 Event identity;
|
||||
- 完整的强 User Principal;
|
||||
- Project、RoleBinding 与 credential policy fence;
|
||||
- request/audit identity。
|
||||
|
||||
该层不依赖 CLI、SQLite 或 Local Profile,后续 Cluster/PostgreSQL adapter 必须复用相同领域语义,而不是复制另一种“原地重开”行为。
|
||||
|
||||
### 3. Local 产品入口只接受强本机证明
|
||||
|
||||
首个产品入口加入现有统一产品 CLI:`ql3 run retry --command-file <private-file>`,实现 binary 为 `ql3-run`。私有命令文件只描述期望与部署位置,不得注入新 Run/Attempt/Event identity、时间戳、Task snapshot、command、environment、Secret 或执行 placement。
|
||||
|
||||
入口复用 Local Owner credential、pepper provenance 与 POSIX 私有文件证明,建立不超过五分钟的 `local_console` User Principal;随后按顺序执行 credential activation、`run.retry` Policy authorize、fence confirm 和 SQLite mutation。Owner、Admin、Operator 由既有角色矩阵授权,其他主体失败关闭。
|
||||
|
||||
本批不向 Local HTTP 暴露 retry:现有 bearer 只有 `single_factor`,在可信 MFA/hardware adapter 完成前不得升级或旁路。CLI 的存在也不授权 MCP、AI Tool 或常驻 application 获得该 mutation authority。
|
||||
|
||||
### 4. SQLite 是唯一原子 authority
|
||||
|
||||
SQLite adapter 使用短生命周期 connection 与 `BEGIN IMMEDIATE`。数据库时钟下,它在同一事务内完成:
|
||||
|
||||
1. 精确 idempotency replay 检查;
|
||||
2. credential、Project、RoleBinding 与 source Run fence 重验;
|
||||
3. 新 Run、claimed Attempt、`run.created`/`run.queued` Event 写入;
|
||||
4. allowed security audit 写入。
|
||||
|
||||
相同 mutation 的精确重放返回原结果,不创建第二个 Run 或第二条 audit;语义漂移返回 conflict。认证、授权、fence、状态、资源限额或存储失败均通过既有安全审计 authority 记录有界失败事实,不回显 credential、command、input 或内部路径。
|
||||
|
||||
### 5. 限流复用既有耐久事实
|
||||
|
||||
不新增 rate-limit 表或进程内 token bucket。adapter 使用既有 `Runs(project_id, created_at_ms, id)` 索引,对同 Project、同 User、`run_manual_retry` trigger 的一分钟窗口计数:
|
||||
|
||||
- Edge:最多 4 个新 Run/分钟;
|
||||
- Standalone:最多 16 个新 Run/分钟。
|
||||
|
||||
精确重放不消耗新额度。该设计在重启后仍然有效,且不增加低配路由设备的 timer、watcher、listener、常驻连接、cache 或 sidecar。Cluster 的多副本全局 quota 必须由后续 PostgreSQL adapter 独立证明。
|
||||
|
||||
## 验收
|
||||
|
||||
- Runtime Core 验证 request、strong Principal、fence 与低敏 response contract;
|
||||
- SQLite 真库验证源 Run 终态不变、新 Run linkage、Attempt/Event/audit 原子提交、精确重放、状态/Task/auth fence 拒绝与 durable rate limit;
|
||||
- Local Owner CLI 验证真实 credential、pepper、private command、Policy authorize、成功及拒绝审计;
|
||||
- 受影响 package build/test、完整 QL3 package/backend 回归、dependency/package/Edge import audit 必须通过后才允许阶段性提交;
|
||||
- workspace package、migration、表、索引和常驻资源数量不得增加。
|
||||
|
||||
### 验收证据(2026-08-12)
|
||||
|
||||
- Runtime Core 502/502;Local SQLite 真库包含 source/new Run linkage、exact replay、fence 与 rate-limit 专项;Local Owner CLI 160 pass/5 条环境条件 skip,其中产品 retry 成功、重放、拒绝审计与 binary 边界全部通过;
|
||||
- `pnpm run test:packages:ql3`:18 个 QL3 package 全量清理、构建与测试通过,最终退出码 0;
|
||||
- backend 回归:1165 项中 1163 通过、2 项按环境条件跳过、0 失败;
|
||||
- package boundary audit:18 个 package、1060 个 source file、1042 个 nested source file,`singleSourcePackages=[]`、`shallowSourcePackages=[]`,非 ledger 密集目录为空;
|
||||
- cluster dependency audit 与 Edge import audit 均为零 finding;Edge 实际加载 121 个 module,不含禁用的 Cluster/PostgreSQL/AWS SDK 边界;
|
||||
- 14 种 Local Profile artifact 与 Local image static audit 全部 `compatible:true`;本切片未新增 package、migration、表、索引、timer、listener、watcher、常驻连接、cache 或 sidecar。
|
||||
|
||||
## 被否决的替代方案
|
||||
|
||||
1. **原地把终态 Run 改回 queued**:会改写历史、破坏 Event/Attempt 不变量,拒绝。
|
||||
2. **允许对 lost 直接人工重跑**:无法证明旧执行已停止,可能复制外部副作用,拒绝。
|
||||
3. **直接开放现有 Local HTTP bearer**:只有单因子认证,不满足恢复执行的强认证要求,拒绝。
|
||||
4. **新增 `ql3-run-management` package**:只有一个短生命周期组合根,会继续制造薄包,拒绝。
|
||||
5. **为限流新增表、timer 或内存 bucket**:增加 migration、空闲资源和重启绕过,既有 Run ledger 足以裁决,拒绝。
|
||||
6. **强求 Local 与 Cluster 同批上线**:会把 PostgreSQL 多副本 quota、集群身份和 HA 证明混入本机切片,拒绝。
|
||||
|
||||
## 后续工作
|
||||
|
||||
- 为 Local HTTP 接入可信 MFA/hardware Principal,并保持本 ADR 的共享契约与强认证门;
|
||||
- 实现 PostgreSQL/Cluster adapter、全局 quota、HA replay 与最小权限角色门;
|
||||
- 在受审 UI 中提供失败原因、source/new Run linkage 与 retry 预览;
|
||||
- 为 Workflow/StepRun 单独冻结 recovery 语义,不复用顶层 Run retry 入口。
|
||||
@@ -384,6 +384,7 @@
|
||||
| [ADR-0378](./ADR-0378-local-run-attempt-log-retention-and-tombstones.md) | Local Run Attempt 日志有界保留与 durable tombstone | Accepted |
|
||||
| [ADR-0379](./ADR-0379-cluster-run-attempt-log-retention.md) | Cluster Run Attempt 日志多副本保留与条件删除 | Accepted |
|
||||
| [ADR-0380](./ADR-0380-local-lost-run-retry-single-control-cadence.md) | Local lost Run retry 复用单一 execution-control cadence | Accepted |
|
||||
| [ADR-0381](./ADR-0381-strong-local-manual-run-retry.md) | 强认证的 Local 手动 Run retry | Accepted |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user