feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,120 @@
# ADR-0121Production Worker Headless Execution Composition
- 状态:Accepted(执行平面具体装配已实现;Session heartbeat、证书/credential 产品流程、部署入口与 retention 仍为发布 Gate
- 日期:2026-07-23
- 关联 RFCQL-RFC-0001 D-05、D-06、D-14、D-85、D-108、D-111、D-114、D-115、D-116、D-120
- 关联 ADRADR-0061、ADR-0087、ADR-0110ADR-0120
> ADR-0122 当前增量:`@qinglong/worker-runtime/product` 已在不新增 package 的前提下接入 exact Session
> wire、共享 Agent、journal-derived capacity 与单 cadence shutdown;本 ADR 下方 98/98、294 sources 是
> D-120 接受时快照。当前累计证据为 Worker 122/122、runtime-core 159/159、Cluster Control 129 pass/2
> external skip、23 importer/303 TypeScript sources。credential material provider 已由 ADR-0123 实现,
> 远端 recovery 和设备证据仍未完成。
## 背景
ADR-0110 至 ADR-0118 已分别完成 Offer、单一 inbox、Activation、Secret、日志、POSIX Executor、
Artifact/completion 与 Lease control,但都只提供默认关闭的原语。继续由最终部署入口逐个手工实例化会
产生多个 HTTPS Agent、不同 storage root、遗漏 completion-first supervision 或错误 shutdown 顺序。
`WorkerRemoteExecutionHeadlessLifecycle.stop()` 还会直接 Abort Pull 并释放 journal owner。如果 Pull
已经取得 Offer,或本地进程仍在运行,部署进程可能在没有 durable drain 证明时退出;这不是可接受的
production shutdown。
## 决策
### 1. 不新增 package,只开放显式 production subpath
具体执行平面放在 `@qinglong/worker-runtime/production`。默认 package 根入口继续只暴露证书 store 与
续期原语,不加载 `runtime-core` remote execution、`proper-lockfile`、HTTPS client 或 POSIX Executor。
`enabled=false` 在读取 Profile、路径、credential、Session 或创建对象前返回。启用只接受 `worker`
Profile,并要求三个互不相同、互不嵌套、非文件系统根的绝对 storage authority
- Offer/inbox journal
- 本地日志 spool
- completion receipt。
Edge 默认 4 条 startup/supervision page、64 条 journal、2 秒 cadence、4 MiB Attempt/32 MiB reserve
Node 默认 16/32 条 page、256 条 journal、500 ms cadence、64 MiB Attempt/256 MiB reserve。所有值仍受
164 page、1024 journal、100 ms60 s cadence 等硬上限约束,不能由部署无限放大。
### 2. 一个 stack 装配全部现有具体能力
production factory 只使用现有 package 和 public subpath,装配:
1. 单 owner `WorkerRemoteOfferFileJournal`
2. 单一 `WorkerIngressHttpsClient` 与 keep-alive Agent
3. Offer、Activation、Secret、Artifact upload、Completion 与 Lease-control adapters
4. Secret-before-Artifact materializer 与 Edge/Node 文件日志 policy
5. reviewed launcher 的 POSIX Executor、同一 receipt root 和 `LocalProcessController`
6. completion-first control coordinator、Processor 与 headless lifecycle。
stack 不导入 cluster-control、PostgreSQL 或 legacy root,也不创建第二个 Agent、数据库连接、per-Run timer
或新 schema。credential provider 与 Session lifecycle 由更外层注入,因为证书签发、`ql3w` credential
恢复和 heartbeat 是独立部署 authority。
### 3. 返回 active 前必须完成有界 startup reconciliation
显式启动先取得 journal owner,再按配置 page 完整扫描历史 inbox。只有得到 `reconciled` 才创建一个
`unref` 全局 cadence`recovery_required` 或超过由 journal hard cap 推导的最大 tick 数立即清理并拒绝
启动。启动扫描期间不 Pull 新任务。
定时 tick 继续使用 lifecycle 内部 coalescing;异常只进入低敏 diagnostic,不重叠创建另一轮。每轮先
completion/lease supervision,再在 Session available 时最多 Pull 并处理一个 Offer。
### 4. Draining 必须在释放 owner 前关闭 Pull 竞态
lifecycle 增加显式 `beginDrain()`
1. 原子标记本地 draining
2. Abort 在途 Pull 并等待其结束;
3. 替换请求 AbortController,但继续持有 journal owner
4. 后续 tick 只做既有执行监督,永不 Pull。
production stop 随后要求外层 `Session.beginDrain()` 已耐久关闭新工作,并复验当前 Session 不再
`available`。它在固定 1 秒–10 分钟总预算内重复 completion-first supervision 和最多 64 条一页的
journal 检查。只有全部记录进入 `start_failure_acknowledged|completion_acknowledged`,才按
cadence → lifecycle/owner → Offer transport → shared Agent 顺序关闭。
drain timeout 返回 `drain_timed_out`,保留 owner 并继续 draining cadence,允许稍后重试;durable
`recovery_required` 返回同名状态并保留 authority,禁止自动假装停止。Session drain 调用失败或不能
证明离开 available 也不得释放 owner。shutdown 等待 timer 保持 ref,防止进程在证明完成前自然退出;
只有常驻 cadence 使用 `unref`
### 5. 本 ADR 不开放完整 Worker 发布
该入口完成的是 execution plane,而不是完整 Worker Profile。发布仍要求外层提供并验证:
- Worker Session register/heartbeat/drain/offline lifecycle
- certificate enrollment/renewal 与 `ql3w` credential provision/recovery
- deployment config、私有路径创建、告警和进程 shutdown budget
- 本地 spool retention/delete 与对象存储 temporary lifecycle
- 固定 Edge/Node x64/arm64、休眠、断网、断电和磁盘压力证据。
缺少任一项时部署入口继续默认关闭,不能把注入式测试 provider 当作产品配置。
## 被否决的替代方案
1. **新建 worker-application package**:没有新依赖或发布责任,违反 D-85。
2. **每个 client 自建 Agent**:重复 TLS credential authority、socket 与关闭状态。
3. **stop 直接释放 journal**:运行进程和丢响应 Offer 会失去唯一监督 owner。
4. **Session drain 后仍允许 Pull**:控制面和本地状态存在竞态,可能在 shutdown 接收新任务。
5. **drain timeout 后强制 close**:把“预算耗尽”伪造成“副作用已停止”。
6. **嵌套 journal/log/receipt root**:扩大扫描、清理和路径 capability 的误伤范围。
7. **production 子入口从 package 根导出**:让证书-only steady state eager-load 网络与文件锁依赖。
## 验收证据
1. lifecycle 测试证明 `beginDrain` Abort Pull、draining tick 不再 Pull、最终 stop 前 owner 不释放。
2. production 测试证明 disabled 零 option access、错误 Profile/重叠路径在文件系统前拒绝、空 execution
plane 真实取得并释放 owner、Session drain 失败保持可重试。
3. 独立进程证明 package 根不加载 journal/`proper-lockfile`,只有 `/production` 加载执行图,且该入口
不加载任何 `ql3-cluster-*`
4. Worker Runtime 98/98 通过,包含显式 enabled factory gate 与两个真实 TLS 1.3 mTLS 回环集成;
严格类型检查通过。
5. workspace 仍为 23 个 importer、294 个 TypeScript source filedependency/source audit
`findings=[]`,没有新增依赖、schema、migration、数据库连接或 per-Run timer。
6. 23 package clean build 与全量测试通过;六种本机制品再次通过真实 pack/offline install/import
最大仍为 2,457,770 bytes、409 files、72 loaded modules,当前抽样 RSS delta 最大
12,566,528 bytes,且均不安装 `@aws-sdk/*`