10 KiB
ADR-0453:Origin-scoped Legacy Shadow 捕获权威与 Primary 门禁
- 状态:Accepted
- 日期:2026-08-19
- 关联 RFC:QL-RFC-0001 D-02、D-360、PR-4、PR-5
- 关联 ADR:ADR-0002、ADR-0449、ADR-0450、ADR-0451
- Amends:ADR-0002 的 Shadow→Primary 门禁、ADR-0451 的 Legacy→Shadow capture 缺口
上下文
ADR-0450 能证明已存在的 legacy-owned Shadow Run 与 Legacy 终态一致,但它明确不测量没有 Shadow Run 的 Legacy execution。ADR-0451 又证明
资源预算和 enabled→off 回滚,却仍不能把“看到的 Shadow 都正确”推导成“每个 Legacy admission 都被捕获”。旧
shadowBridgeFailureSnapshot() 只有进程累计失败 key,没有 admission 分母、pending 守恒、测量窗口或可供 rollout loader 重放的权威文件。
Primary manifest v1 还只要求维护者填写五个 "passed"。即使审批和文件摘要有效,运行时也无法重算这些字符串背后的 capture、terminal agreement、
startup convergence 与 rollback evidence;把字符串替换为一份只声明 eligible 的 receipt 同样不构成门禁。
决策
- 已配置 origin 进入默认 Legacy Shadow bridge 后、构造 fact 前,必须取得一次 process-local admission token。token 只能结算为
captured | failed | pending;captured + failed + pending = admitted,failure 进一步固定为fact | observer | initialization | accept四类并守恒。 Legacy spawn、返回值和失败开放语义不变;测试 override 不构成生产 capture authority。 LegacyShadowRunObserver暴露只读异步captureSettled():只有真实LegacyShadowRunWriter.accept()成功才结算 captured。初始化失败、fact 构造失败、 observer begin 失败或 accept 失败分别结算到固定失败类;后续 spawned/running/terminal 写入仍由 ADR-0450 终态审计裁决,不能用 accept 成功替代终态一致。- snapshot schema 为
qinglong/legacy-shadow-capture-snapshot@v1,用随机 process epoch、时间和最多七行 origin 固定计数表达,不含 PID、Run、Attempt、Cron、 task、command、path、用户或错误消息。window report schema 为qinglong/legacy-shadow-capture-report@v1;起始 snapshot 必须零 pending,同 epoch 前后差分、 origin exact coverage 和计数单调必须成立。 - HTTP worker 在 Legacy normalization 和 ADR-0449 startup reconciliation 之后、Primary bootstrap/listen 之前装配一次性 exporter。只有显式设置
QL3_SHADOW_CAPTURE_EVIDENCE_FILE=<basename>.json才开启;文件固定写入既有 config root,basename 禁止路径、.前缀和..。exporter 无 timer、watcher、 listener、重试队列或数据库连接,只在干净 shutdown 写一次0600、wxno-replace JSON;失败只形成低敏审计,不改变 Legacy shutdown。 - capture evidence schema 为
qinglong/legacy-shadow-capture-evidence@v1,同时嵌入同进程启动时的 ADR-0449 report。只有 startupconverged、Profile 与 origin 顺序精确一致、capture assessment 为captured才 qualified;crash、非干净退出、partial/no-replace 写失败、empty、pending 或任一失败都不能产生 Primary eligibility。 gate:legacy-shadow-primary:ql3组合三份低敏输入:capture/startup evidence、ADR-0450 closed terminal report、ADR-0451 compiled-backend full rollback/resource report。当前只允许manual:edge 必须精确 8 个 admission;standalone 为 32–128 个。terminal window 必须与 capture window 逐值相等,scanned/matched 必须 等于 captured,closed、evidence complete、无 remaining,terminal agreement 与 fully comparable 都必须为 1000/1000。资源报告必须同 Profile、full、 compiled backend、qualified,并证明 Legacy continued、Shadow stopped 与 SQLite integrityok。- Primary gate bundle schema 为
qinglong/legacy-shadow-primary-gate@v1。它嵌入三份 source report、各自 canonical JSON SHA-256、固定计数、window、结论与固定 violation code;不嵌入原文件路径。CLI 用O_NOFOLLOW、1 MiB 上限读取输入,并以0600、no-replace 写 bundle;ineligible 时不写输出。 - Rollout manifest 升为 schema v2。enabled manifest 必须增加
primaryGatereference,只允许manual、同 config 目录 basename 和 64-hex bundle digest。 loader 以 no-follow、64 KiB 上限读取 bundle,验证 manifest digest,重新计算 embedded source canonical digests,并从 source reports 重新执行完整 Primary gate; 它不信任 CLI 写入的assessment。bundle 必须 eligible、生成时间不晚于审批时间且 Profile 与实际 Local deployment 相同,之后才可惰性加载 Primary stack。 - v1 enabled manifest 失败关闭,不自动补写或猜测 gate。disabled/missing/rejected 仍保持零 Primary stack、router、timer 和连接。
defaultMode=off、manual-only、 最长 30 天审批、rollback plan 和既有 durable cancellation/atomic projection gates 保留。
被拒绝的替代方案
给 RunningInstances 增加 origin 后直接当分母
拒绝。2.x 多条 Node/Shell 路径只在 spawn/callback 后写 RunningInstances,缺失行本身不可见,无法证明 admission capture;为兼容观测改成数据库 fail-closed 还会改变 Legacy 可用性。
每次 Legacy execution 同步写一条新 admission ledger
拒绝作为本阶段方案。它会让 Shadow 数据库写参与 Legacy spawn 前置路径,并在低配 flash 上形成第二份逐执行持久化权威。若未来需要跨 crash 的在线连续窗口,必须以 独立 migration、retention、写放大和故障语义重新评审,不能暗中加入兼容桥。
只在日志里输出累计 counter
拒绝。日志片段没有同 epoch 起止、pending baseline、startup report binding、no-replace 文件或 loader 重放;丢日志时也不能 fail-closed。
Loader 只验证 eligible receipt 的摘要
拒绝。摘要只能证明文件没变,不能证明内容真实执行 gate。v2 bundle 必须携带低敏 source reports,loader 必须独立重算 digest 和结论。
资源、安全与部署影响
- 每个已启用 Legacy admission 增加一个常数 token 和四个固定 counter 更新;最多七行 origin,没有按执行身份保存集合,不增加 timer、watcher、listener、线程、连接或 schema。未配置 Shadow origins 时仍在 fact factory、observer/Repository import 和 capture authority admission 前返回。
- 正常运行不写 capture 文件;只有显式证据 canary 的干净 shutdown 写一个有界文件。edge/standalone runtime 不需要 Prometheus、OTel、外部数据库、对象存储或 Cluster 组件。
- bundle 是本机 config-root trust domain 内的 rollout evidence,不是签名供应链 attestation。拥有 config root 写权限的 operator 仍是本机信任根;公开分发或跨主机 delegation 需另加签名 ceremony。
- 本 Gate 只开放 manual eligibility 的判定能力,不自动写 manifest、不启用 Primary、不接管 scheduled/system/boot/gRPC origin,也不证明物理 flash、断电或生产任务内容。
验证
- 纯 authority 覆盖成功、四类失败、pending、跨 epoch、非零 pending baseline、origin coverage 和脱敏守恒。
- exporter 覆盖 armed→exported、clean shutdown、
0600、no-replace、重复 close、缺失 startup、未配置和失败开放。 - gate/CLI 覆盖 exact edge cohort、样本不足、terminal 漂移、audit-only rollback、symlink、no-replace、embedded source 篡改和 canonical digest 重算。
- rollout v2 覆盖 missing/tampered/ineligible bundle、审批时间、Profile mismatch、unknown field/path traversal 和 disabled lazy path。
- 真实 compiled backend 的
ScheduleService.runTaskenabled child 已通过默认 observer/Repository 产生一个systemcapture:admitted/captured 为 1/1,failed/pending 为 0/0;它只证明 bridge 真实结算链,不冒充 manual Primary 的 8/32 条正式 canary。 - 阶段门已从 clean package artifacts 重跑:D-360 聚焦测试
48/48、Legacy/Shadow 串行扩展117/117、资源/回滚专项4/4、build:back、 完整 backend1,469 pass / 0 fail / 2 conditional skip、18-package clean build/test、四项可执行架构审计与14/14Local Profile artifact audit 全部通过。产物字节保持 D-358 基线:base2,589,998 / 2,590,076、adopted2,809,293 / 2,809,416、application3,632,877 / 3,632,997、application-api3,800,430 / 3,800,574、AI3,069,251 / 3,069,341、application+AI4,493,151 / 4,493,283、MCP7,315,930 / 7,316,038。 - Linux arm64 Docker 资源门再次通过:router stress 保持
128 MiB / 0.5 CPU / 0 swap / 64 PID,cgroup peak95,113,216bytes;Edge release 保持256 MiB / 1 CPU / 0 swap / 128 PID,13 个 workload 全部通过,cgroup peak144,740,352bytes,memory.events的max/oom/oom_kill增量均为 0。Edge full rollback 中默认systembridge 的真实 capture 为1/1,terminal audit p955.236 ms、RSS delta2,621,440bytes、数据库存储前后稳定。Docker arm64 仍不是物理路由、flash wear 或断电证据。 - D-360 未修改 PostgreSQL schema、migration、依赖树或 Kubernetes 拓扑,因此不重跑 PostgreSQL HA;相邻 D-359 的 PostgreSQL HA
142/142与 timeline1→2只作为未被本阶段触碰的既有证据,不冒充本阶段新结果。
后续
ADR-0454 已把目标实例的 prepare、observe、resource、qualify、显式短期 approve、只读 audit 与 crash-replay rollback 固化为一次性状态机,并明确
primary_selected 不等于运行态 activated。正式启用 manual Primary 前,维护者仍必须在目标 edge/standalone 实例实际执行该仪式并保留 bootstrap activated audit;
仓库内 synthetic fixture 或 compiled resource child 不能替代真实产品入口的八/三十二条 admission。固定物理路由、flash 写放大、断电、非干净退出和 config-root
签名/备份仍是独立发布证据;其他 origin 必须分别建立自己的 admission authority、样本预算和 rollback gate,不能复用 manual receipt。