planP4E2 completed / E4 pendingimplemented · gate-open2026-09-18

R8-Fix-5.2:CLI Post-Core Final Delivery Absolute Deadline 修复方案

独立审计发现的 P0

R8-Fix-5.1 只把 core 的关闭阶段收进了单一 CloseoutDeadline;perfowl_collector_core_cli.py 在 stream_candidate_session() 返回之后仍同步执行一段 Post-Core Final Delivery,且完全不受 stop_control_received_monotonic_ns 对应的 deadline 约束:

finalize_live_candidate_session(final_output)
read report-seal.json / manifest.json / metrics-manifest.json / report-final.json
integrity boundary validation / SHA256
write foundation-delivery.json
read launch-trace-report.json
emit report_sealed / emit launch_trace / emit stopped

这段工作发生在 stopped 之前。

独立验收复现(真实 run_stream)

1. stdin 真实发送 stop
2. fake stream_candidate_session() 等待 stop 后立即返回
3. report-seal.json 已存在
4. finalize_live_candidate_session() 换成 sleep(30)
5. 父进程等待 3 秒

实测:3 秒后 Python 子进程仍然活着
      wire 只输出到 stop_requested + capture_stopped
      stopped 从未发出

Stop -> Core closeout 正常返回 -> finalize 阻塞 -> process alive
     -> Swift 10s watchdog -> graceful_stop_timeout -> SIGTERM/SIGKILL

结论:R8-Fix-5.1 当时不能通过最终验收(status = partial / blocked)。

本批目标

Stop command
 -> Core closeout
 -> live delivery
 -> report_sealed / launch_trace
 -> stopped
 -> wire terminal flush
 -> wire close
 -> Python process exit

T0       = stop_control_received_monotonic_ns
deadline = T0 + 8.0s

CLI 不得重新创建第二个 8 秒窗口:必须复用 _terminal_exit_deadline(stop_event),而不是 CloseoutDeadline(time.monotonic_ns())。

修复主线

禁止项

不得修改 FPS / FrameTime / P95 FrameTime / Jitter / Refresh Rate 公式、FrameLifetime 语义、ActivityTrace 与 CoreProfile Source Owner、CapturePlan、MetricRegistry、Swift 10s gracefulTimeout、R8-Fix-5 的 pump 32events/1ms 与 filter 64messages/1ms 公平门禁;不得把宿主超时改长、不得重新引入 historical replay 到 live Stop、不得提前假装 stopped、不得删除 partial 诊断、不得 timeout 后继续无界同步 I/O、不得用普通 ThreadPoolExecutor(非守护 worker)承载收尾工作、不得只测 helper 而不测真实 run_stream subprocess。

测试 H–N

编号场景必须断言
H真实 subprocess + 真实 run_stream + finalize sleep(30),短预算 0.30s不等待 30s;Stop → Process Exit < 1s;证明 slow delivery 线程仍 alive 而进程已退出;stopped 仍发出且带 partial reason;未伪造 report_sealed
I正常 live delivery(seal / manifest / metrics-manifest / report-final 全有效)liveDeliveryStatus=completed;report_sealed 发出;stopped 发出;wire flush completed;foundation-delivery.json 存在且 deliverySHA256 非空;overall clean
J缺 report-seal.json不进入 historical replay;quality=partial + partialReason=live-report-seal-missing;foundation-delivery.json 写入受 deadline 控制
K缺失 seal 路径上 atomic_manifest() sleep 30s仍受 live-delivery 阶段 deadline 控制,不得走成另一条无界分支
Llaunch-trace-report.json 读取/解码很慢整个 live-delivery 阶段有界;已完成 delivery 仍被上报,仅放弃 launch trace
Mslow live delivery timeoutstopped 必须可见并带 closeoutFailureStage=live-delivery 与 partial-live-delivery-timeout
Nslow delivery + stdout wire 阻塞delivery / wire flush / wire close 全部有界,进程退出 < 9s,两条阻塞线程均为被遗弃 daemon

E2 门禁 G1–G18

G1  真实 run_stream slow live delivery 不等待 30s
G2  slow live delivery Stop -> Process Exit < 9s
G3  短预算 fixture Stop -> Process Exit < 1s
G4  slow live delivery timeout 后仍 emit stopped
G5  stopped 包含 partial terminal closeout reason
G6  missing report seal 不进入 historical replay
G7  missing seal 的 atomic_manifest 同样有界
G8  slow launch trace read 有界
G9  slow delivery + blocked wire 仍 process exit < 9s
G10 正常 live delivery 正常产生 foundation-delivery.json
G11 正常 report_sealed 正常产生
G12 normal path overall closeout clean
G13 Python 全量 0 failures
G14 Swift 0 failures
G15 site PASS
G16 Display 公式未变
G17 Source Owner 未变
G18 Swift core timeout 仍为 10s

不接受的实现

await asyncio.to_thread(finalize_live_candidate_session)
普通 ThreadPoolExecutor + wait_for
把 Swift 10s 改长
live delivery timeout 后 shutdown(wait=True)
重新创建新的 8 秒 CLI deadline
stopped 前继续无限文件读写
恢复 historical finalizer
delivery timeout 后不给 stopped
delivery timeout 后仍报告 clean
只测 helper,不测真实 run_stream subprocess

E2 实现与验证结论(2026-09-18)

真实 subprocess 实测(预算压缩为 0.30s):Test H Stop → Process Exit = 0.3012s,此时被遗弃的 perfowl-cli-closeout worker 仍在 30s sleep 中(cliCloseoutWorkerThreadAlive=true、cliCloseoutExecutorAbandoned=true),stopped 与 closeout_exit 仍然发出,closeoutStatus=partial / closeoutFailureReason=partial-live-delivery-timeout;Test N(slow delivery + blocked stdout)Stop → Process Exit = 0.3633s,perfowl-core-wire-writer 与 perfowl-cli-closeout 两条线程均被遗弃为 daemon。生产 8.0s 预算下 worst case Stop → Process Exit 仍为 8.0s + 终端 teardown,保持 Swift 10s watchdog ≥ 1s 余量。

Python 全量 654/654 OK(共享工作区,含并行批次测试);R8-Fix-5.2 专项 105/105 OK;Swift 183 tests / 3 skipped / 0 failures;站点知识门禁 + VitePress 构建 PASS;git diff --check 干净。状态:implemented · gate-open / E2 completed / E4 pending。完整实现细节与字段清单见站点 devlog Canonical 文档。