P4 Startup Phase C · Hybrid Total 正式方案
> 当前状态:Project Owner 已于 2026-09-22 明确批准 Phase C,授权 Implementation AI 按本方案执行 C0/C1;C2 仍禁止进入。 > > 2026-09-22 增量:C0 仪表化已完成(implemented · gate-open,E2 完成 / E4 待执行)。本批只做 C0, > C1 未开始、C2 未进入、C0 Gate 未自判。Project Owner 裁定 C0 真机证据由 Test AI 采集、由 Lead AI 判定, > Implementation AI 交付「仪表化 + E2 + 取证工具」后停止。详见文末 Implementation Update。 > > 基线:c77e363b9e900f09baa326ff01748b3c38f715a2。Phase A = PASS;Phase B 架构 = PASS / CLOSED;Phase B 性能 = B-PARTIAL。Phase C 不反向修改 A/B 的 owner、prewarm、SOURCE_ACTIVE barrier 或 reconnect 语义。
1. Phase C 要解决什么
现有 Startup Total 的直接端点来自 Dedicated CoreProfile lifecycle:
System Interface BEGIN
↓
Initial Frame END
现有公式是同一条 CoreProfile RAW v2 中的 device mach ticks 差:
legacyTotalMs =
ticksToNs(initialFrameEndMachTicks - systemInterfaceBeginMachTicks)
/ 1_000_000
优点是同源、同钟、语义清楚;缺点是 lifecycle BEGIN / END 任一丢失时 Total 会缺失,现有 Mode A / C 虽可在严格条件下修复一部分端点,但不能保证每轮都得到 Total。
Phase C 的目标不是“无论如何给一个数”,而是:
- 研究并产品化更稳定的 Hybrid Total:
- START:优先使用可证明属于目标 App、且位于 device mach clock 的启动锚点;
- END:使用 ActivityTrace
FrameLifetime的首个合格 presented frame;
- lifecycle 九阶段继续保留为诊断明细,不因 Hybrid Total 而弱化;
- 在无法证明时钟、进程归因或首帧归因时保持
missing; - 先 shadow 旁路计算,E4 通过后才讨论替换主 Total。
2. 明确非目标
Phase C 不做以下事情:
- 不回退 Phase A Dedicated Startup;
- 不回退 Phase B Full Prewarm;
- 不让 Startup 与 FrameTime 同时打开第二个 CoreProfile/kperf recording;
- 不把
didFinishLaunching当 Total END; - 不把“launch request 后收到的任意第一帧”直接当目标 App 首帧;
- 不把 host
time.monotonic_ns()与 device mach timestamp 直接相减; - 不用截图轮询、OCR、视频画面变化伪装 first frame;
- 不用 0 填充缺失;
- 不为了提高成功率放宽 PID / generation / sourceEpoch / reconnectGeneration / foreground / source-gap 门禁;
- C0/C1 未通过前不修改 UI 中当前 Startup Total 的主值。
3. 当前已有数据源事实
3.1 Legacy lifecycle START / END
RealtimeLaunchTraceDecoder 当前保留:
System Interface BEGIN的startMachTicks;Initial Frame END的_initial_frame_end_ticks;timeConfig.numer / denom;- target PID / targetGeneration / dynamic attribution;
- source loss / unmatched begin/end / schema gates。
现有 launch_trace.py 仅在端点满足顺序与质量门禁时发布 Total;Mode A/C 修复仍带 repaired 来源,不得伪装成 clean endpoint。
3.2 ActivityTrace FrameLifetime
当前 FrameTime 已由独立于 kperf 的:
com.apple.instruments.server.services.activitytracetap
subsystem = com.apple.coreanimation
category = IOMFBServer
name = FrameLifetime
提供。其 event 含:
begin_timeend_timeprev_framerefresh_intervalframe_seeddisplay_idsurface_id
FrameLifetimeDecoder 已把 end_time 转换为 presentationTimestampNs,并有 sourceEpoch / reconnectGeneration / targetGeneration / foreground / continuity / source-gap 语义。
重要限制:当前 adapter scope 是 foreground-device,并不天然等价于“目标 App 的 frame”。
3.3 Process-control launch request
launch_requested_ns / launch_request_envelope.host_receive_monotonic_ns 是 Mac/Sidecar host monotonic clock 上的控制边界。
它可以作为:
- Start click → launch request 的诊断;
- launch ordering / round identity 的控制锚点;
但 C0 证明 host↔device clock bridge 之前,不允许直接参与 Hybrid Total 数学公式。
4. Phase C 固定拆成 C0 / C1 / C2
C0 · Feasibility & Attribution Gate
只研究、加实验观测和测试 fixture;不改主 Total。
必须证明三件事:
- Clock Gate:Hybrid START 与 FrameLifetime END 能落在同一 device mach clock / epoch;
- Start Gate:存在稳定且可归因到目标 App 的 START;
- End Gate:能把首个合格 FrameLifetime 归因到本轮目标 App,而不是任意前台设备 frame。
任一门禁失败,Phase C 停止在 research,现有 Total 保持不变。
C1 · Shadow Hybrid Total
C0 通过后:
- 新增独立 Hybrid Total evaluator;
- 每轮同时计算 legacy lifecycle Total 与 hybrid shadow Total;
- raw/report/diagnostic 中记录两者及 provenance;
- UI 主 Total 仍读 legacy;
- 不改变 lifecycle decoder 的九阶段 FSM。
C2 · Promotion Gate
只有 Lead AI 独立 E4 验收通过,Project Owner 再次明确批准,才允许:
- 把 Hybrid Total 晋升为 Startup Total 首选来源;
- legacy lifecycle Total 退为 comparator / fallback / diagnostics;
- 更新 knowledge、公式 Registry、UI provenance。
C1 开发完成 不自动进入 C2。
5. START 语义与优先级
Phase C 不先假定 host launch request 就是最终 START。START 候选按证据等级研究:
S0 · direct lifecycle start
System Interface BEGIN mach ticks
- device clock;
- process scope;
- 已有 target attribution;
- clean 时作为 paired comparator 与可用 START。
S1 · repaired early lifecycle start
沿用当前 Mode C 的早期 BEGIN:
min(System Interface / dyld / UIKit early init BEGIN)
要求:
- 必须是 surviving BEGIN;
- 只允许 PHASES[0..2];
- 必须带
repaired; - 不得扩展到 scene / didFinish / later frame。
S2 · stable device process-start / resume marker(C0 研究目标)
C0 应检查现有 Dedicated Startup RAW / ProcessControl 可否得到:
- 与目标 PID 直接绑定;
- 位于 device mach clock;
- SOURCE_ACTIVE 之后、真实 launch/resume 附近;
- reconnect / generation 可隔离;
的 process launch / exec / resume kernel marker。
不得预先假设某个 debugID。 Implementation AI 只能通过 fixture + 真机 census / Apple Instruments 参考识别后,将经证据确认的 marker 写入固定 profile。
若 S2 被证明,优先作为 Hybrid START。
S3 · host process-control launch request
只作为 diagnostics / experimental comparator:
launch_requested_ns (host monotonic)
除非 C0 建立并验证 host↔device clock bridge,否则:
HybridTotal = devicePresentationNs - hostLaunchNs
是禁止公式。
6. END 语义
目标 END:
首个可证明属于本轮目标 App 的 FrameLifetime presentation end_time
不是“ActivityTrace 的第一条 FrameLifetime”。
一个 frame 至少必须满足:
- event identity =
com.apple.coreanimation / IOMFBServer / FrameLifetime; presentationTimestampNs > startTimestampNs;- reconnectGeneration 与当前 cycle 相同;
- targetGeneration 与当前 launch round 相同;
- sourceEpoch 仍属于当前有效窗口;
- target PID 已 verified;
- target foreground 状态成立;
- decoder 无 source-gap / discontinuity / out-of-order;
- displayID 合法且未发生 display boundary change;
- 不来自上一轮残留、App rebind 前目标或 reconnect 前 source。
6.1 surfaceID 归因门禁
surfaceID 是 Phase C 的关键研究字段,但当前代码没有证明:
surfaceID -> target PID / target bundle
因此 C0 必须做正/负样本验证:
- 目标 App 冷启动;
- 启动前系统界面帧;
- 启动后切入其他 App;
- 系统 overlay / keyboard / notification;
- App A→B rebind;
- reconnect 后新 round。
只有出现可重复的 surface / process 映射证据,才能把 END 标为 real。
若只能证明“目标 App verified foreground 时窗口内的第一条 foreground-device frame”,可以进入 candidate,但不能叫 real。
若同时存在多个无法消歧的 surface,结果必须 missing: ambiguous-first-presented-surface。
7. 时钟域与正式公式
7.1 唯一首选公式:device ↔ device
当 START 与 END 都是同一 device mach clock 的 ticks:
deltaTicks = endMachTicks - startMachTicks
deltaNs =
floor(deltaTicks * timebaseNumer / timebaseDenom)
hybridTotalMs =
deltaNs / 1_000_000
必须满足:
endMachTicks > startMachTicks
timebaseNumer > 0
timebaseDenom > 0
sameReconnectGeneration == true
sameClockEpoch == true
noClockReset == true
算法版本建议:
startup-hybrid-total.device-mach-framelifetime.v1
7.2 Cross-source clock proof
CoreProfile lifecycle mach ticks 与 ActivityTrace FrameLifetime mach ticks虽然都表现为 mach ticks,C0 仍必须证明:
- 相同 timebase;
- 同一 device session 中数值处于同一 epoch;
- 30s 观测窗口内相对 host elapsed 的 slope 无异常漂移;
- reconnect 后重新建证据,禁止沿用上一代 clock proof。
未证明时:
quality = missing
reason = cross-source-clock-unverified
7.3 Host ↔ device clock bridge
如果研究 S3 host launch request fallback,必须独立形成 clock bridge:
deviceNs ≈ a * hostMonotonicNs + b
并记录:
- calibration sample count;
- slope
a; - offset
b; - residual median / p95 / max;
- calibration generation;
- valid-from / valid-until。
未达到 C0 误差门槛时,该 bridge 只能进 diagnostics,不能参与产品 Total。
8. 质量与失败语义
Phase C 新增 Hybrid provenance,至少区分:
| quality | 含义 |
|---|---|
real | START 与 END 均为直接 device timestamp,target attribution 与 clock gate 全部通过 |
candidate | timestamp 本身真实,但至少一项归因仍依赖已验证但非直接的 foreground/surface 规则 |
repaired | 使用 S1 或其他明确批准的 endpoint repair;必须带 repair reason 与 error bound |
missing | 无法在不猜测的前提下得到值 |
禁止:
- missing → 0;
- ambiguous → candidate;
- repaired → real;
- host/device 混钟 → repaired;
- source gap 后跨 gap 取 frame;
- 跨 generation/reconnect 拼端点。
报告建议字段:
hybridTotalMs
hybridTotalQuality
hybridTotalReason
hybridTotalAlgorithmVersion
hybridStartSource
hybridStartMachTicks
hybridStartQuality
hybridEndSource
hybridEndMachTicks
hybridEndSurfaceID
hybridEndDisplayID
hybridEndQuality
clockDomain
clockProofGeneration
sourceEpoch
targetGeneration
reconnectGeneration
Legacy 字段继续保留:
legacyTotalMs
legacyTotalQuality
legacyTotalSource
C1 shadow 阶段禁止覆盖现有 totalDurationMs。
9. ActivityTrace 生命周期与资源模型
Phase C 不得重新引入 CoreProfile unified recording。
FrameTime 已选择
复用现有唯一:
ActivityTraceFrameLifetimeRawAdapter
Hybrid evaluator 只注册一个 one-shot observer,不创建第二条 ActivityTrace subscription。
FrameTime 未选择但 Startup 已选择
允许建立最小 Startup FrameLifetime observer,但必须:
- 复用 long-lived DTX owner / activitytracetap transport;
- 只订阅 FrameLifetime whitelist;
- 不启用 CoreProfile;
- 不影响 Dedicated Startup owner;
- 首个合格 END + reorder grace 后若无其他消费者立即 stop;
- 最晚在 launch 后固定 timeout 自动 stop;
- Session Stop / reconnect / App rebind 必须清理。
建议初始上限:
startup frame observer timeout = 10s
reorder grace = 250ms
bounded pending candidates <= 64
duplicate ActivityTrace streams = 0
这些是 C1 初始预算,不是无条件写死;E2/E4 可收紧,但不得为提高成功率无限延长。
10. 性能 / 轻量化门禁
Phase C 实施必须给出变更前后基准。
至少测:
- Start click 同步新增耗时;
- first FrameLifetime decode latency;
- CPU;
- RSS;
- ActivityTrace bytes/sec;
- event queue high-water;
- raw/session 增量大小;
- 10s timeout 最差路径。
目标预算:
| 项目 | 门槛 |
|---|---|
| 新增 CoreProfile/kperf recording | 0 |
| 重复 ActivityTrace subscription | 0 |
| Startup observer pending rows | ≤ 64 |
| Startup observer lifetime | ≤ 10s |
| Start 同步路径额外 CPU wall time | p95 ≤ 5ms |
| Hybrid evaluator 单 event 处理 | p95 ≤ 1ms |
| 额外 RSS | ≤ 2 MiB |
| ActivityTrace 持续带宽 | 实测应保持约当前 FrameLifetime 量级;若 > 0.10 MiB/s 则 FAIL |
| Session 磁盘增量 | 需实测;不得因 shadow raw duplication 翻倍 |
如果现有 FrameTime adapter 已 active,Hybrid evaluator 必须零复制或近似零复制消费 normalized event;禁止再次序列化整条 raw 流只为计算一个首帧。
10.1 执行路径、复杂度、吞吐与并发边界
Hybrid evaluator 的热路径固定为:
normalized FrameLifetime event
→ O(1) identity / generation / clock / quality gates
→ append bounded candidate metadata
→ first-qualified END seals result
→ observer short-circuit / detach
效率约束:
- 时间复杂度:正常单事件处理必须为
O(1);不得在每个 frame 到来时回扫整轮历史。最多只允许在 seal / diagnostics 时扫描有界候选集合,复杂度O(K),其中K <= 64; - 空间复杂度:
O(K),K <= 64;不得保留第二份 FrameLifetime raw history; - 并发策略:沿用现有单事件流/单 writer 顺序,Hybrid evaluator 不为每个 frame 新建 task、thread 或 process;若需要异步 teardown,只允许最多 1 个受控 cleanup task;
- 吞吐目标:E2 synthetic benchmark 至少覆盖 240 FrameLifetime events/s,在该负载下 evaluator p95 单事件处理仍需 ≤ 1ms,队列不得持续增长;
- 首结果延迟目标:从合格 normalized FrameLifetime event 进入 evaluator 到 shadow result seal,p95 ≤ 10ms;
- 超时边界:Startup-only observer 最长 10s;reorder grace 250ms;所有 teardown 必须 bounded,不得用无限 wait;
- 短路策略:一旦结果 sealed,后续 frame 只做常数级 no-op / observer detach,不允许改写首个 END;
- 复用策略:FrameTime 已选时直接消费已有 normalized event;禁止为 Hybrid 重复 decode、重复序列化或重复订阅 ActivityTrace。
这些效率门禁必须在 C1 的 before/after benchmark 中单独出表,不能只以“全量测试通过”代替性能证据。
11. C0 必做实验
C0-1 · Clock-domain proof
同一真机、同一 session 记录 CoreProfile lifecycle mach ticks 与 ActivityTrace end_time:
- 核对 numer / denom;
- 检查数值 epoch 是否同域;
- 至少 30s;
- reconnect 后重新做一次;
- 若出现 offset/epoch 不可解释,Phase C 停止。
C0-2 · First-presented-frame attribution
至少:
- Fixture A;
- App A / B / C 三类真实 App;
- 每个 10 次冷启动;
- 额外 10 个负样本(系统界面、切其他 App、overlay、rebind)。
输出每轮:
launch round identity
PID
foreground transitions
FrameLifetime candidate sequence
displayID
surfaceID
frameSeed
presentationTimestamp
selected END / reject reason
legacy initial-frame endpoint if present
要求 negative controls 0 次错误归因。
C0-3 · START candidate census
对 S0/S1/S2/S3 同场记录:
- availability;
- 与 launch request 的相对时序;
- generation/PID attribution;
- 与 clean legacy START 的 delta 分布;
- source loss 时行为。
没有证据不得把某个 kernel event 命名为“process start”。
12. E2 反例矩阵
Implementation AI 必须补确定性 fixture / integration tests,至少覆盖:
- clean S0 + clean target FrameLifetime;
- S0 missing、S1 repaired;
- S0/S1 都 missing → Hybrid missing;
- END missing;
- frame 在 START 之前;
- duplicate same timestamp;
- out-of-order frame;
- ActivityTrace source-gap;
- discontinuity;
- wrong display;
- multiple ambiguous surface;
- target not foreground;
- stale previous-round frame;
- targetGeneration mismatch;
- sourceEpoch mismatch;
- reconnectGeneration mismatch;
- App A→B rebind 不得使用 A 的 frame;
- reconnect 后不得使用上一代 clock proof;
- PID rebind 后旧 PID frame 不得通过;
- lifecycle source-buffer-loss 但 ActivityTrace 正常;
- ActivityTrace loss 但 legacy Total clean;
- launch ack 失败;
- host launch request 存在但 clock bridge 未验证 → 不得计算 host/device Hybrid;
- FrameTime 已选 → 必须复用唯一 ActivityTrace subscription;
- FrameTime 未选 → startup one-shot observer timeout 后释放;
- Startup 未选 → 不得因为 Phase C 偷开 observer;
- Stop before first frame → missing + clean teardown;
- first qualifying frame 到达后后续 frames 不得改写 END;
- source restart 后 first frame 必须重新 warmup/归因;
- legacy
totalDurationMs在 C1 shadow 阶段逐字保持现有语义。
13. C1 代码边界建议
优先新增独立组件,避免继续膨胀 RealtimeLaunchTraceDecoder:
perfowl_collector_core/startup_hybrid_total.py
HybridStartupTotalEvaluator
HybridEndpoint
HybridClockProof
HybridTotalReport
职责:
- 接受已标准化 START candidate;
- 接受 FrameLifetime normalized event;
- 做 identity / clock / ordering / source-quality gate;
- 只保存有界候选状态;
- 输出 shadow report。
RealtimeLaunchTraceDecoder 继续负责 lifecycle; FrameLifetimeDecoder 继续负责 FrameLifetime 解码; realtime.py 只负责 wiring / lifecycle / observer refcount。
禁止把 Hybrid endpoint 规则散落在多个 if 中。
14. E4 真机验收矩阵
C1 E2 通过后才打包进入 E4。
最低矩阵:
| 对象 | 冷启动轮数 |
|---|---|
| Fixture A | 20 |
| App A | 20 |
| App B | 20 |
| App C | 20 |
| negative controls | ≥10 |
| reconnect/reboot 扩展 | ≥5 |
每轮必须同时保存:
- hybrid shadow;
- legacy Total;
- lifecycle endpoint 完整性;
- FrameLifetime candidate/reject sequence;
- source/reconnect/generation;
- startup activation timings;
- resource telemetry。
14.1 稳定性 Gate
publishableHybridRate >= 95% overall
publishableHybridRate >= 90% per app
falseAttributionCount = 0
crossGenerationJoinCount = 0
clockDomainViolationCount = 0
publishable 可以统计 real/candidate/repaired,但必须分层单列,不能只给一个混合百分比。
14.2 与 legacy clean subset 的同场对照
只在 legacy clean endpoint 同时存在的轮次比较:
deltaMs = hybridTotalMs - legacyTotalMs
absDeltaMs = abs(deltaMs)
此处是语义一致性比较,不把 legacy 自动当绝对真值。
初始 Gate:
median(absDeltaMs) <= max(25ms, 2 * refreshIntervalMs)
p95(absDeltaMs) <= max(80ms, 5 * refreshIntervalMs)
若存在稳定偏差,必须解释该偏差是否来自“lifecycle Initial Frame END”与“实际 presentation END”的定义差异,而不是简单通过加常数校正。
禁止用经验 offset 把结果“调到更像 legacy”。
14.3 资源 Gate
同设备、同 App、同采集项配对比较 C1 vs baseline:
- CPU 不出现持续明显抬升;
- RSS 增量 ≤ 方案预算;
- FrameTime continuity / FPS 不因 Hybrid observer 下降;
- Dedicated Startup source-ready / lifecycle capture 不退化;
- Session 大小不因重复 raw 流显著放大。
15. C2 晋升条件
只有以下全部成立,Lead AI 才能提出 C2:
- C0 Clock / Start / End attribution 全部 PASS;
- C1 E2 counterexample 全部 PASS;
- E4 稳定性达标;
- false attribution = 0;
- paired delta 满足 Gate 或有可证明的定义差异;
- 无 FrameTime / Startup / kperf / reconnect 回归;
- 资源预算 PASS;
- knowledge / formula / provenance 已更新;
- Project Owner 明确批准晋升。
否则:
legacy Total = 主值
hybrid Total = shadow / diagnostics
16. 明确停止 / 回滚条件
出现任一条件立即停止 Phase C 产品化:
- FrameLifetime 无法可靠归因到目标 App;
- ActivityTrace 与 CoreProfile clock epoch 无法证明一致;
- 为获取 END 必须重新让 CoreProfile 同时承担 frame filter;
- 需要双 kperf recording;
- 必须依赖截图轮询;
- false attribution > 0 且无法通过确定性规则消除;
- hybrid coverage 提升依赖放宽 generation/source-gap 门禁;
- E4 差值呈无界长尾;
- Hybrid 让 FrameTime / Startup source stability 明显退化;
- host/device clock bridge 只能靠经验常数而无法校准。
回滚必须只关闭 Hybrid evaluator / one-shot observer;Phase A/B 架构保持不动。
17. 开发与提交要求
Project Owner 已于 2026-09-22 明确批准本方案;Implementation AI 现在可以开始 C0/C1,但不得自行进入 C2。
推荐拆分:
test(startup): add hybrid total feasibility fixturesfeat(startup): add shadow hybrid total evaluatordocs(startup): record phase c shadow validation
若一个批次可保持范围清晰,也可以合并,但产品代码与 E4 结论不得混成一个“自证 PASS”提交。
Implementation AI 交付必须包含:
- exact HEAD;
- diff summary;
- 实际实现与本方案偏差;
- focused tests;
- full Sidecar regression;
- benchmark / resource evidence;
- site build;
- 可安装签名/公证包(进入产品代码交付时按 AGENTS.md);
- 同一 devlog 的 Implementation Update;
- 停止在 C1,不自行宣布 C2。
18. Lead AI 验收顺序
Implementation delivery
↓
Lead static/code audit
↓
E2 focused + full regression
↓
resource benchmark review
↓
signed/notarized test build
↓
E4 matrix
↓
Lead PASS / PARTIAL / FAIL
↓
Project Owner decides C2 promotion
19. 当前结论
> 本节的「当前状态」是 2026-09-22 方案批准时的快照,按 D2 不回改;其后的进展以文末 > Implementation Update · 2026-09-22 为准(C0 已 implemented · gate-open,C1 未开始,E4 未执行)。
Phase C 的正确第一步不是“把 launch command 和第一帧相减”,而是先证明:
START 可归因
+ END 可归因
+ 两者同一 device clock
+ round identity 不串代
因此本方案把 C0 Attribution/Clock Gate 设为硬前置。
当前状态:
- Phase C formal plan:已制定并获 Project Owner 批准;
- C0/C1 实施授权:已开放;
- 产品实现:等待 Implementation AI 按本方案开始;
- E4:未进入;
- C2:仍未授权;
- 下一动作:Implementation AI 先执行 C0 Feasibility & Attribution,再实施 C1 Shadow Hybrid Total;完成后交 Lead AI 独立审计,不得自行宣布 C0/C1 PASS 或进入 C2。
---
Implementation Update · 2026-09-22(C0 取证与归因仪表化)
> 审计基线:5a06929c22db912db22866ed2c29109761f1b797(docs(startup): approve phase c c0 c1)。 > 本段只记录 C0。C1 未开始,C2 未进入。 本批未宣布任何 C0 PASS。
1. 本批范围与角色边界(重要)
Project Owner 在开工前明确裁定:C0 的正式真机证据不由 Implementation AI 自证。因此本批交付收缩为 「C0 仪表化 + E2 反例矩阵 + 真机取证能力」,然后停在 C0,交回 Lead AI:
Implementation AI(本批) → C0 instrumentation + E2 + census/benchmark 取证工具 ← 已完成并停止
Test AI(下一步,未开始) → 真机执行 C0-1 / C0-2 / C0-3,产出 E4 原始证据
Lead AI(未开始) → C0 Gate 独立判定(PASS / PARTIAL / FAIL)
因此本批没有:自跑真机 E4、自建验收包/公证、把 C0 写成 validated、把 shadow 提升为正式 Startup Total。
2. 实际修改范围
| 文件 | 性质 | 说明 |
|---|---|---|
MacApp/Sidecar/perfowl_collector_core/startup_c0_feasibility.py | 新增(离线模块) | C0 三闸仪表化 + Session 只读投影 |
MacApp/Sidecar/tests/test_startup_c0_feasibility.py | 新增 | §12 E2 反例矩阵,96 项 |
MacApp/scripts/startup-c0-census.py | 新增 | 真机/回放 census 入口(Test AI 用) |
MacApp/scripts/benchmark-startup-c0-feasibility.py | 新增 | 时间/空间/吞吐 benchmark + 归因探针 |
产品活线零改动(逐字节断言,非承诺):
git diff HEAD -- MacApp/Sidecar/perfowl_collector_core/{realtime,recording,display_performance,launch_trace,adapters}.py
→ 空
startup_c0_feasibility.py 是新增的离线模块,没有任何调用方:它是一个纯 reader,不注册 tap、不订阅 ActivityTrace、不拥有 CoreProfile/kperf、不写 Session、不触碰 totalDurationMs。把「C0 只有离线读取能力」 这件事说清楚,是本批最重要的一条诚实边界——离线回放结构上无法满足下面 §5 列出的 5 项。
3. 关键实现决策与相对方案的偏差
偏差 1(最重要):END 侧 timebase 只能标 assumed-constant,这是发现而非缺陷。 FrameLifetimeDecoder.__init__(timebase_numer=125, timebase_denom=3) 是编译期常量,产品路径没有任何地方 把设备回报的 timebase 注入进去;adapters.py 里 CoreProfile 的 envelope 会带 device_mach_timestamp / mach_numer / mach_denom,ActivityTrace 的 envelope 不带。所以 END 侧的 125/3 只是「恰好对 arm64 成立 的假设」。仪表化据此把 provenance 显式分层(device-reported / assumed-constant / missing), Clock Gate 遇到非 device-reported 一律 fail-closed。
偏差 2:S2 从「Start Gate 要求」降级为「capability gap」。 方案 §5 把 S2(设备侧 process-start / resume 标记)列为 START 优先级之一。实现时确认:干净的 S0 已经满足 Start Gate,S2 是研究偏好而非门槛,且方案本身禁止在未做 census 前命名任何内核标记。因此 S2 缺仪器记入 capabilityGaps(不是 failures),报告 s2Status="not-instrumented",绝不猜测 debugID。把 「还没有仪器」写成「经验性否定结论」是不可接受的,两者在报告里保持分离。
偏差 3:Clock Gate 的速率/单调性按 (generation, source) 分组,不跨源合并。 两条流的时间锚点都打在到达时刻上,各带自己的 transport delay。最初把两源锚点合并成一个有序列表, 系统性地制造出假 clock-reset-detected。修正为按源分组后,test_source_arrival_offset_is_not_a_clock_reset 钉死该行为。
偏差 4:显式的人工标注通道。 方案 C0-2 要求 App A/B/C ×10 冷启动 + 10 个负对照。负对照「哪个 Session 是系统 UI / 别的 App / 覆盖层」 是工具无从推断的事实,因此 census 提供 --labels 注入;未标注时 falseAttributionCount 保持不可测, 不许猜。
偏差 5:有界保留(§10.1)与一处显式例外。 方案 §10.1 要求空间 O(K), K <= 64 且「不得保留第二份 FrameLifetime raw history」。30s/60fps 一轮是 1800 帧, 逐帧保留 14 个字段实测把单轮投影的常驻推高到超出预算,因此:
- 帧诊断按 presentation 时间保留最早的 64 帧(K 大小的 max-heap,
O(K)而非O(F)); - 被丢弃的帧不静默:
framesObserved/frameRejectCounts/framesTruncated/
framesFrameLevelQualifiedBeyondSample 把全量人口压成 O(1) 计数;
- 边界不得伪装成结论:若被丢弃的帧本可合格,报告
end-observation-truncated,
绝不允许写成 end-no-qualifying-frame(那是对从未看过的帧下断言)。
显式例外:clockAnchors 仍按源完整保留(约 2 个标量/帧,~130 B/帧 → 30s ≈ 268 KB、120s ≈ 1.0 MB)。 Clock Gate 的速率证明依赖相邻锚点对的中位斜率与逐点单调性,抽稀锚点等于削弱 C0 唯一要建立的时钟证明。 本批选择保留证明强度、把成本显式记账(benchmark 单独出表),并在遗留项里挂 C1 复核; 这不属于「第二份 raw history」——它是 2 个标量,不含 payload / 身份 / surface / quality。
4. C0 E2 证据(96 项,全绿)
cd MacApp/Sidecar && PYTHONPATH=. <py> -m unittest tests.test_startup_c0_feasibility
→ Ran 96 tests in 0.240s / OK
§12 反例矩阵逐项覆盖(左为矩阵编号):
| # | 项 | 用例 |
|---|---|---|
| 1 | clean S0 + clean frame | test_s0_clean_start_is_real、test_warmup_first_frame_is_accepted、test_fully_proven_observation_reports_all_gates_passed |
| 2 | S0 missing / S1 repaired | test_s0_missing_s1_repaired、test_repaired_is_never_promoted_to_real、test_s1_phase_out_of_range_rejected、test_s1_not_surviving_rejected |
| 3 | S0/S1 全缺 | test_all_start_sources_missing |
| 4 | END missing | test_no_frames_observed_is_missing、test_end_never_claims_repaired |
| 5 | frame 早于 START | test_frame_before_start_rejected、test_frame_equal_to_start_is_not_after_start |
| 6 | 重复时间戳 | test_duplicate_timestamp_selects_exactly_one |
| 7 | 乱序 frame | test_selection_is_invariant_to_input_order |
| 8 | source-gap | test_source_gap_frame_rejected |
| 9 | discontinuity | test_discontinuity_frame_rejected |
| 10 | 错 display | test_display_boundary_change_rejected |
| 11 | 多 surface 歧义 | test_ambiguous_surfaces_within_grace_collapse_to_missing、test_ambiguity_is_not_downgraded_to_candidate、test_single_surface_is_not_ambiguous、test_surface_proof_resolves_ambiguity、test_without_surface_proof_the_ceiling_is_candidate、test_surface_proof_reaches_real |
| 12 | 非前台 | test_not_foreground_frame_rejected |
| 13 | 上一轮陈旧帧 | test_stale_previous_round_frame_rejected |
| 14–16 | target/source/reconnect 代际错配 | test_target_generation_mismatch_rejected、test_source_epoch_mismatch_rejected、test_reconnect_generation_mismatch_rejected |
| 17 | App A→B rebind | test_app_rebind_frame_from_previous_target_rejected |
| 18 | 跨代复用 clock proof | test_previous_generation_proof_cannot_be_reused、test_reconnect_generation_gets_its_own_proof、test_proof_missing_for_a_second_reconnect_generation、test_each_generation_needs_both_sources |
| 19 | PID 重绑后旧帧 | test_unverified_pid_blocks_selection |
| 20 | lifecycle 丢源但 END 正常 | test_lifecycle_buffer_loss_does_not_promote_the_start |
| 21 | ActivityTrace 丢失但 legacy clean | test_a_clean_legacy_total_cannot_rescue_a_missing_end |
| 22 | launch ack 失败 | test_failed_launch_ack_never_becomes_a_start_or_a_device_delta |
| 23 | host↔device 桥未验证 | test_host_device_delta_attempt_is_forbidden、test_host_clock_delta_is_recorded_and_labelled_not_a_device_delta |
| 28 | seal 后不得改写 END | test_frames_beyond_grace_cannot_change_the_sealed_end |
| 29 | 源重启后首帧重新归因 | test_source_restart_may_not_inherit_the_previous_rounds_first_frame |
| 30 | legacy Total 逐字保持 | test_c0_reports_the_legacy_total_without_rewriting_it、test_no_hybrid_total_value_is_emitted |
| §10.1 | 有界保留 6 项 | test_diagnostics_are_capped_at_the_documented_bound 等 |
另有 Clock Gate 8 项(时间基不一致 / provenance 未证 / 锚点不足 / 窗口 <30s / 速率漂移 / 时钟回拨 / epoch 无配对 witness / 跨代复用)与 Start Gate 质量分层 2 项。
§12-24 / 25 / 26 / 27 本批不覆盖,且不是遗漏:这四项描述的是活线 observer 的接线行为 (FrameTime 已选时复用唯一 subscription、未选时 one-shot observer 超时释放、Startup 未选时不得偷开 observer、首帧前 Stop 的干净 teardown)。C0 没有活线路径,无法用离线 fixture 产生有意义的反例。 它们由 C1 实现后以活线测试覆盖——本批只把结构性约束作为断言钉住:benchmark 输出 realtimePyUnchanged=true / newActivityTraceSubscriptions=0 / newCoreProfileRecordings=0。
5. C0 离线结构性无法满足的 5 项(必须真机/人工补)
census 每次运行都会把这张表原样打印出来,防止下游把「离线测不到」读成「已经测过」:
- C0-1 配对同事件 witness:产品路径不记录任何「同一事件同时出现在 CoreProfile lifecycle 流与
ActivityTrace FrameLifetime 流」的 witness ⇒ cross-source-epoch-unproven 无法靠回放清除。 必须真机产出该 witness,或由 Lead AI 重新规定跨源时钟证明。
- C0-1 30s 窗口 + reconnect 复证:需要「选了 FrameTime 指标」(否则 ActivityTrace 根本不跑)
且「同一次采集内含 reconnect」的 Session。
- C0-2 负对照:哪个 Session 是系统 UI / 别的 App / 覆盖层只有操作者知道 ⇒ 需要
--labels,
否则 falseAttributionCount 保持不可测。
- C0-2 surfaceID → 目标 PID 映射:产品代码不证明该映射 ⇒ END 侧
real不可达,
最强只能到 candidate。
- C0-3 S2 内核 process-start 标记:无仪器,且方案禁止未做 census 就命名 ⇒
not-instrumented。
6. 性能与资源 benchmark(§10 / §10.1)
/py MacApp/scripts/benchmark-startup-c0-feasibility.py --rounds 20 --json internal/startup-c0-benchmark.json
→ exit 0(三项预算全过)
合成 Session 规模 300 / 900 / 1800 / 3600 帧 × 5 轮,1800 帧 ≈ 真实 30s/60fps。
| 项目 | 预算 | 实测 | 判定 |
|---|---|---|---|
| 单帧投影耗时 p95 | ≤ 1 ms | 0.3685 ms(中位 0.1757) | PASS |
| 吞吐 | ≥ 240 events/s | min 2501.3 / median 5698.0 | PASS |
| C0 自有增量工作集 | ≤ 2 MiB | 0.379 MiB | PASS |
| 单次完整评估(含 1891 事件 Session 投影) | — | 6.752 ms / 次 | 记录 |
| 诊断帧保留数 @300/900/1800/3600 | = K ≤ 64 | 64 / 64 / 64 / 64 | 与 F 无关 |
| 报告字节 @300/900/1800/3600 | 与 F 无关 | 18083 / 18084 / 18085 / 18085 | 与 F 无关 |
| 新增 CoreProfile/kperf recording | 0 | 0 | PASS |
| 重复 ActivityTrace subscription | 0 | 0 | PASS |
RSS 归因必须分开说,否则是错告。逐阶段高水位拆分出:
harness 造 Session 0.047 MiB
投影(含读 Session) 5.348 MiB ← 其中 validate_recorded_session 单独实测 4.980 MiB
闸评估 0.012 MiB
validate_recorded_session 会把整份 Session 的每个事件物化进内存(本批 1891 事件),因此任何建立在它 之上的离线分析器都继承一个 O(session) 底噪。该函数属产品既存代码、本批逐字节未改。所以 §10.1 的 2 MiB 空间预算按 C0 自有增量判定(0.379 MiB,PASS),同时把未归因的 5.359 MiB 原样写进 rssAttributionMiB.analyzerIncrementalPeak 与 observations.analyzerIncrementalPeakWithinBudgetUnattributed=false, 任何人可复核这次拆分,不存在「把预算偷偷改小」。
7. 全量回归(E2)与 flake 归因
测试文件同一性(先证明失败文件与基线逐字节相同):
IDENTICAL tests/test_sample_publisher_decoupling.py
IDENTICAL tests/test_realtime_consumer_fairness.py
IDENTICAL tests/test_pmd3_subscription_fairness.py
IDENTICAL tests/test_pump_filter_active_fairness.py
IDENTICAL tests/test_display_ingress_backpressure.py
基线探针:git archive 5a06929 MacApp fixtures | tar -x -C /tmp/probe。base = 853 项;ship = 949 项(+96)。
交错配对 ≥3 轮(本机多 AI 共用,负载摆动可达 20 倍,单次通过无意义):
| 轮 | ship | base |
|---|---|---|
| 1 | 944 / 3 fail | 853 / 2 fail |
| 2 | 949 / 6 fail | 853 / 2 fail |
| 3 | 949 / 5 fail | 853 / 3 fail |
失败集合逐轮漂移,且同一个名字在基线也失败:
test_pump_filter_active_fairness…test_g_rejected_flood_keeps_message_bound_fairness—
base 三轮全失败、ship 三轮全失败 ⇒ 基线既有;
test_display_ingress_backpressure…test_b1_b2_synthetic_120hz_benchmark— base r1/r3、ship r2/r3;test_pmd3_subscription_fairness…(三轮内换了 3 个不同用例)— base r2/r3;test_pump_filter_active_fairness…WallVersusActiveTimeBenchmark— base r3、ship r2;test_realtime_consumer_fairness…(DisplayPipelineBenchmark / HeartbeatFairness)— base 同文件 ship r1/r3。
对「只在 ship 出现的 3 个名字」做隔离配对复测(base/ship 各 4 轮,6 个用例):
cases: spcad hbeat pmd3 wall-vs-active flood backpressure
ship-r1 :: P P P P F P base-r1 :: P P P P F P
ship-r2 :: P P P P F P base-r2 :: P P P P F P
ship-r3 :: P F P P F P base-r3 :: P P P P P P
ship-r4 :: P P F F F F base-r4 :: P P P P F P
flood两侧在隔离下同样失败(base r1/r2/r4)⇒ 排除回归;- 其余各项在两侧压倒性通过(
spcad8/8 全 P),仅 ship-r4 一轮集中掉落 —— 该轮处于 48 次测试调用后的
负载/热漂移尾部,同一轮 base 也并非干净。
结论(不过度声明):可归因回归 = 0。所有失败都落在已知计时/调度敏感家族,且基线同场同样失败; spcad / hbeat / pmd3 三个名字仅在 ship 出现,隔离复测 7/8–8/8 通过、测试文件与基线逐字节相同、 本批改动是新增离线文件且 realtime.py 未改,判定为负载敏感而非本批引入;但「无真机、无负载隔离的 单主机」不足以对这三个名字做出统计显著性结论,因此它们保留为未归因项,交独立验收复核,本批不据此 声称「0 失败」。
8. 当前状态与遗留项
- C0 状态:
implemented · gate-open(E2 完成 / E4 待执行); - C0 Gate 判定:未做。
judgement恒为pending-lead-ai,报告不输出任何 Hybrid 值
(test_no_hybrid_total_value_is_emitted 钉死),也不写 validated;
- C1:未开始;
realtime.py未改(用户明确要求 C1 需接活线时再做); - C2:未进入;shadow 未提升为正式 Startup Total。
遗留项:
clockAnchors仍为 O(F)(~130 B/帧)。C1 复核是否把 Clock Gate 的速率证明改成流式摘要(同时保住
单调性与中位斜率),以严格满足 §10.1;
scpad / hbeat / pmd3三个未归因名字需独立验收在隔离主机复核;- C0-1 配对 witness、C0-2 surfaceID→PID 证明需要可能存在的产品侧记录能力——若真机也无法产出,
建议由 Lead AI 重新规定跨源时钟证明与 END 归因的可达上限(当前 END 最强只能是 candidate);
FrameLifetimeDecoder的 125/3 仍是编译期常量。若真机 proof 显示设备回报值与常量不同,
需要一次公式入档的正式变更,而不是就地改常量。
Lead AI Independent C0 E2 Audit(2026-09-22)
独立验收对象:exact commit 80b0b162cdfb44ba480bff45b882fe4f718e33ac,审计基线 5a06929c22db912db22866ed2c29109761f1b797。该 range 只有 1 个提交、9 个文件;对 realtime.py、recording.py、display_performance.py、launch_trace.py、adapters.py 做 exact diff,结果均为空,确认 C0 未接产品活线。
独立复核证据:
- C0 focused:96 / 96 OK(Lead 独立运行)。
- C0 benchmark:单帧投影 p95 0.127 ms,吞吐 min 7346 events/s,C0-owned RSS 增量 0.418 MiB;K=64 保留与预算均成立。
- Sidecar 全量:949 tests / failures=1 / errors=0 / 167.580s。唯一失败仍为既有负载敏感
test_g_rejected_flood_keeps_message_bound_fairness(3103 未大于 3123);该测试不在本批 diff,且此前基线/ship 已多轮证明为既有 flake,本批不修改其阈值。 - Clock Gate 对
assumed-constantFrameLifetime timebase fail-closed;recorded-session projection 不生成 paired witness,因此cross-source-epoch-unproven不会被离线回放伪清除。 - END 在没有
surfaceID -> target PIDproof 时最高仅为candidate;C0 不输出 Hybrid Total,judgement保持pending-lead-ai。
阻塞缺陷 C0-FIX-01 · round foreground verification 未进入 End Gate
evaluate_c0_end_gate() 读取了 foregroundVerified,但没有把它用于 frame selection。Lead 构造反例:
foregroundVerified=false + frame 自身 foreground=true + 无 surface proof,当前结果仍为:
status = passed
endQuality = candidate
selectedEnd = true
failures = []
这违反正式方案 §6 的「target foreground 状态成立」硬门禁。尤其当前 ActivityTrace adapter 的 foreground 来自 target 配置,缺省可为 true,因此不能用单个 frame 的 foreground=true 替代 round-level verified foreground 证据。
修复要求:
foregroundVerified is not true时 End Gate 必须 fail-closed,禁止产生 selected END;- 新增独立 failure/reject provenance(建议
target-foreground-unverified),不要把“未证明”伪写成“明确不在前台”; - 增加确定性反例:round foreground 未验证、frame foreground=true 仍必须 missing / failed;
- 明确
foregroundVerified的证据来源,禁止从待选 FrameLifetime 自身循环证明。
阻塞缺陷 C0-FIX-02 · recorded-session 不能自动等价 E4
当前 evaluate_c0_feasibility() 仅凭:
origin == "recorded-session"
就把 evidence 标为 E4。Lead 使用 synthetic observation 独立复现:只要设置 origin="recorded-session",报告即输出 E4。这违反项目证据等级红线:E4 必须是真机证据,序列化 Session 形态本身不是 E4。
修复要求:
- recorded/replay/synthetic Session 默认不得自动升级 E4;
- 默认保持 E2,只有明确且可信的 true-device capture provenance 才可标 E4;
- provenance 必须可审计并随 census 输出,不能由任意 observation 字符串自报即升级;
- 增加测试:synthetic recorded session = E2;具备正式 true-device provenance 的 Session 才可 = E4。
Lead Gate 结论
- C0 离线架构方向:接受。
- C0 E2 实现验收:BLOCKED / REPAIR REQUIRED。
- 正式 Test AI 真机 C0-1/C0-2/C0-3:暂缓。 当前工具存在 attribution 与 evidence-level 两处会污染 Gate 证据的缺陷,不能先采再解释。
- C1:继续 BLOCKED。
- C2:继续禁止。
最小修复范围仅限 C0 离线模块、C0 tests、census/provenance 输出及本 devlog/handoff;不得修改 realtime.py 或借此进入 C1。修复提交后由 Lead AI 复验上述两个反例、focused/full regression 与 benchmark;通过后才把 C0 状态切为 READY FOR FORMAL E4,再交 Test AI 真机执行。
Implementation Fix Update · 2026-09-22(C0-FIX-01 / C0-FIX-02)
Lead AI 对 80b0b162cdfb44ba480bff45b882fe4f718e33ac 的独立验收判定 C0 E2 = BLOCKED / REPAIR REQUIRED。本批只修上述两个阻塞缺陷:不进入 C1,不改 realtime.py 等产品活线,不解决 S2 / surfaceID→PID / clock witness,不启动真机 C0,不宣布 C0 PASS。
1. 缺陷与修复
C0-FIX-01 · round foreground verification 未进入 End Gate
修复前行为(Lead 复现,本机复跑确认):foregroundVerified=false 且 frame.foreground=true 时,evaluate_c0_end_gate() 仍返回 status=passed、endQuality=real、selectedEnd 非空、failures=[]。本机复跑比 Lead 记录更严重——得到 real 而非 candidate。
根因两处,缺一不可:
evaluate_c0_end_gate()读入了foreground_verified,但它从未参与选择;选择只受surface_proven影响。collect_c0_observations_from_session()用first_frame["quality"] != "not-foreground"反推foregroundVerified——循环证明:待选的那一帧同时充当"允许选中它"的证人。
修复:
| 位置 | 变更 |
|---|---|
evaluate_c0_end_gate() | round 级 foregroundVerified is not True → 追加 fail-closed failure target-foreground-unverified;选择分支由 else 收紧为 elif foreground_verified:,无 proof 时 selectedEnd=null、selectionReason=null、endQuality="missing" |
FAILURE_CLASSES | 新增 "target-foreground-unverified": unverified |
FRAME_REJECT_ORDER | 不新增:这是 round 级事实,不是帧级 reject。帧级 not-foreground 仍表示"该帧自证不在前台",两者不得互相冒名 |
| round row | 新增 foregroundVerified / foregroundVerifiedSource |
| End Gate 汇总 | 新增 foregroundVerifiedRoundCount / foregroundUnverifiedRoundCount / foregroundVerificationAvailable |
collect_c0_observations_from_session() | 删除循环推导,改为 foregroundVerified=False + foregroundVerifiedSource="missing",并追加 note foreground-verification-unavailable-in-recorded-session |
| 常量 | 新增 FOREGROUND_SOURCE_MISSING / FOREGROUND_SOURCE_INDEPENDENT_WITNESS |
foregroundVerified 的证据来源(Lead 要求 4):只能来自独立于待选 FrameLifetime 的证人。Session manifest 只记录 identityStatus(身份),不记录前台;产品当前没有任何路径产生该证人。因此离线投影恒为 false、End Gate 恒报 target-foreground-unverified——这是缺仪器,不是"已证明不在前台"的经验负结论。
C0-FIX-02 · recorded-session 不得自动等价 E4
修复前行为:evaluate_c0_feasibility() 中 "evidence": "E4" if observation.get("origin") == "recorded-session" else "E2"。任意 origin="recorded-session" 的 observation(含全合成 fixture)即输出 E4。
修复:新增 resolve_c0_evidence_level(observation) -> (level, provenance),等级只由显式可审计的采集 provenance 决定,不再由字符串自报:
| 输入 | 等级 | provenance |
|---|---|---|
origin ∈ {replay, synthetic} | E2(origin 否决,即使携带完整 witness) | declared-non-capture-origin |
无 captureProvenance | E2 | not-a-device-capture |
kind != "true-device-capture"、witness 非 Mapping、缺 captureId、capturedAtNs 非正 | E2 | capture-witness-unusable |
kind="true-device-capture" + trueDeviceWitness{captureId, capturedAtNs} 完整 | E4 | true-device-capture-witness |
报告同时输出 evidence 与 evidenceProvenance;投影 observation 显式声明 captureProvenance: null("声明",不是"缺字段")。replay / synthetic 即使带 witness 也恒 E2:声明与证人自相矛盾的记录不构成可审计证据。
2. 实际修改范围(4 个文件)
| 文件 | 变更 |
|---|---|
MacApp/Sidecar/perfowl_collector_core/startup_c0_feasibility.py | 两项修复主体(常量 / 解析器 / End Gate fail-closed / 投影去循环 / 汇总字段) |
MacApp/Sidecar/tests/test_startup_c0_feasibility.py | +13 / −1 测试(含 2 个 Lead 反例) |
MacApp/scripts/startup-c0-census.py | row/aggregate 保留 evidence、evidenceProvenance 与 foreground 计数;新增第 6 条 UNCOVERED;控制台新增证据列 |
site/devlog/p4-startup-phase-c-hybrid-total-plan.md | 本段 |
未改:MacApp/scripts/benchmark-startup-c0-feasibility.py(§10.1 空间语义未变,逐字节未动);internal/startup-c0-benchmark.json 为重跑产物。
3. 5 个产品活线文件隔离证明
realtime.py / recording.py / display_performance.py / launch_trace.py / adapters.py 相对 C0 原实现 80b0b16 与基线 13c913b:git diff --stat 为空,SHA-256 逐一 IDENTICAL。
| 文件 | SHA-256(前 16) | 结论 |
|---|---|---|
realtime.py | 8ee779bdf1e6d49d | IDENTICAL |
recording.py | 886d4b4c2fcf6391 | IDENTICAL |
display_performance.py | 12e5daf3019d3bc0 | IDENTICAL |
launch_trace.py | 78c8e215fbd16117 | IDENTICAL |
adapters.py | 2eead9d1e14f1cae | IDENTICAL |
benchmark 另独立断言 productionHotPath.realtimePyUnchanged = true、新增 ActivityTrace 订阅 0。
4. C0 E2 证据(108 项全绿)
cd MacApp/Sidecar && PYTHONPATH=. <py> -m unittest tests.test_startup_c0_feasibility
Ran 108 tests in 0.343s
OK
新增 13 项 / 移除 1 项:
- Lead 反例 1(C0-FIX-01)
C0EndGateTests.test_lead_counterexample_unverified_foreground_never_selects_an_end:断言status=failed、selectedEnd=null、endQuality=missing、selectionReason=null、publishableEndRate=0、failures含target-foreground-unverified,且帧级rejectedBy=[]且not-foreground不出现在frameRejectCounts(证明未把"未证明"伪写为"证明非前台")、target-foreground-unverified不在FRAME_REJECT_ORDER、failureClasses含unverified不含unsupported;并以foregroundVerified=true的同帧对照证明断言非空泛。 - Lead 反例 2(C0-FIX-02)
C0FeasibilityReportTests.test_lead_counterexample_recorded_session_does_not_upgrade_to_e4:origin="recorded-session"→E2/not-a-device-capture。 - E2/E4 契约 4 项:
test_origin_alone_never_promotes_past_e2(枚举 5 种 origin 恒 E2)、test_declared_non_capture_origins_veto_even_a_full_witness、test_only_a_complete_true_device_witness_reaches_e4、test_a_partial_witness_stays_e2(8 种残缺 witness 各自被拒)。 - 投影/写入路径 3 项:
test_projection_never_claims_foreground_verification(含"帧确实声称前台"的非空泛反证)、test_a_projected_session_is_e2_and_fails_the_end_gate_closed、test_deriving_the_session_report_keeps_e2_and_the_foreground_blocker。 - census provenance 2 项:
C0CensusProvenanceTests.test_census_rows_carry_level_and_provenance、test_census_never_prints_e4_for_a_recorded_session。 - 其余 2 项:
test_round_row_reports_its_foreground_provenance、test_resolver_is_the_single_source_of_the_level。
被移除的 1 项:C0FeasibilityReportTests.test_origin_drives_the_evidence_level。该断言钉死的旧值 origin="recorded-session" → E4 正是 Lead 判为缺陷的契约本身,必须随契约变更而改变;其覆盖由 test_origin_alone_never_promotes_past_e2 与两项反例测试接续,覆盖面只增不减(详见 §7 偏差 3)。
5. 全量回归与 flake 归因(E2,未放宽任何阈值)
| 轮次 | 树 | 用例数 | 结果 |
|---|---|---|---|
| R1 | ship(本批) | 961 | 4 FAIL + 1 ERROR |
| R2 | ship(本批) | 961 | 6 FAIL |
| R1 | base 13c913b | 949 | 2 FAIL |
| R2 | base 13c913b | 949 | 1 FAIL |
证据一 · 失败集合逐次漂移(同一代码两轮):ship R1 挂 xctrace reap(ERROR)、pmd3×3、flood;ship R2 挂 display_ingress_backpressure、pmd3×2、flood、r8fix11、DisplayPipelineBenchmark。xctrace reap 与 pmd3 burst 在 R2 消失,三项新名字出现——失败集合不是代码属性。
证据二 · 基线同样漂移:base R1 挂 flood + DisplayPipelineBenchmark,base R2 只挂 flood。
证据三 · 交错隔离配对(3 轮 ship/base 交替,每轮 8 名全跑,ran=8/8):
name ship base attributable?
xctrace-reap PPP PPP no
pmd3-filter PPP PPP no
pmd3-pump PPP PPP no
pmd3-burst PPP PPP no
flood-bound FFF FFF no <- 两树 6/6 全挂:稳定宿主 flake
ingress-bp PFP FPF no <- 两树都挂,但轮次不同
r8fix11-par PPP PPP no
display-bench PPP PPP no
interleaved ship/base pairs: 3
attributable regressions: 0
flood-bound(test_g_rejected_flood_keeps_message_bound_fairness):ship 3/3 挂、base 3/3 挂——message-limit 逼近 3123 阈值线,属已知计时敏感族;两树同挂,不可归因本批。ingress-bp(test_b1_b2_synthetic_120hz_benchmark):shipPFP、baseFPF——两树都失败、轮次不同,属负载上下文 flake。- 仅在 ship 侧出现过的名字(
xctrace reap、pmd3三项、r8fix11、DisplayPipelineBenchmark)在隔离配对下两树各 3/3 通过。 xctrace reap结构上不可达本批改动:其 import 闭包只依赖launch_trace/xctrace_capture/ 标准库;且该测试以 200 ms 真实墙钟超时拉起子进程并断言其被回收,本机负载下子进程来不及写出 pid 文件即报 ERROR(非 FAIL),与观测一致。
失败率:ship 10/1922 = 0.52%,base 3/1898 = 0.16%(两轮全量合计)。结论:0 项可归因回归;6 个名字曾在 ship 侧出现但经交错配对排除,已登记在案供 Lead 独立复查。
证据四 · 用例集合归属(loader 口径,非 grep -c 'def test_'):base 949 / ship 961,net +12。comm -23(ship 独有)= 13 项,全部位于 test_startup_c0_feasibility;comm -13(base 独有)= 1 项,即被契约变更取代的 test_origin_drives_the_evidence_level。
6. C0 benchmark 重跑(§10 / §10.1)
| 指标 | 预算 | 实测 | 判定 |
|---|---|---|---|
| collect 每帧 p95 | ≤ 1.0 ms | 0.0650 ms | PASS |
| 吞吐 | ≥ 240 帧/s | min 14719 / median 21690 帧/s | PASS |
| 额外 RSS | ≤ 2 MiB | C0 自身增量 0.4258 MiB | PASS |
| 诊断帧保留 | O(K), K ≤ 64 | 300/900/1800/3600 四档恒为 64 | PASS |
| 报告体积 | 不随 F 增长 | 18407 → 18409 B | PASS |
| 产品活线 | 逐字节不变 | realtimePyUnchanged=true,新增订阅 0 | PASS |
rssAttributionMiB:sessionReaderBaseline 4.973 MiB(产品自身 validate_recorded_session 的 O(session) 下限)、analyzerIncrementalPeak 5.398 MiB、c0OwnedIncremental 0.426 MiB。未经分摊的峰值仍原样输出(analyzerIncrementalPeakWithinBudgetUnattributed=false)以保持可审计;重跑未修改 benchmark 脚本。
7. 相对原方案的偏差与过程记录
- 新增
foregroundVerifiedSource字段:Lead 要求"明确foregroundVerified的证据来源",原 observation schema 无此字段,故新增并随 round row 输出。 - 投影行为收紧(显式登记):投影
foregroundVerified由"从帧反推"改为恒false,直接后果是所有离线 Session 的 End Gate 现在都记为target-foreground-unverified、selectedEnd为 null。这是去掉伪造 proof 的必然结果,不是回归;census 已把该现象登记为第 6 条 UNCOVERED,防止被误读为"已证明不在前台"的经验负结论。 resolve_c0_evidence_level保留一条升级路径:origin="recorded-session"+ 完整真机 witness 仍可为 E4。理由:recorded-session 是真机采集的序列化形态,Lead 要求 2 允许"明确、可信、可审计的真机采集来源";而replay/synthetic属声明性非采集中间态,恒 E2。Lead 反例(合成的 recorded-session)因无 witness 而恒 E2。- benchmark 未改动:§10.1 空间语义未变,仅重跑产出新 JSON。
- 过程偏差(工具):对同一文件连续两次批量写入时观察到前一次编辑静默丢失,改为逐次写入 + grep 复核,并在提交前以测试结果与 SHA-256 校验兜底。
8. 当前状态与遗留项
- C0 两处阻塞缺陷:已修复并在本地验证(E2)。 两项 Lead 反例均已闭环。
- 本 AI 不宣布 C0 PASS。 报告
judgement恒为pending-lead-ai。 - C0 状态:待 Lead AI 复验两项反例 + focused/full 回归 + benchmark 后,再切 READY FOR FORMAL E4。
- 正式 Test AI 真机 C0-1/C0-2/C0-3:本 AI 未启动、未自证。
- C1:BLOCKED(不因本修复自动开启)。C2:禁止。
- 遗留(不属本批范围,均未改动):S2 kernel marker 无仪器;surfaceID→PID 无证明(
real不可达);paired same-event witness 无产品路径;foregroundVerified的独立证人尚无产品路径。
Lead AI C0 Fix Revalidation(2026-09-22)
独立复验对象:exact commit 66dfbb847fcd16d1acbe12f2d67108a3e2890428,父提交 13c913b44e90fba37af1932690985c7d6e539b4a。
1. 独立验证结果
- C0-FIX-01:PASS。 Lead 重新构造原反例:
foregroundVerified=false+frame.foreground=true,当前输出为status=failed / endQuality=missing / selectedEnd=null / selectionReason=null / failures=[target-foreground-unverified]。round-level foreground proof 已真正成为 selection 前置条件;recorded-session projection 也不再从待选 FrameLifetime 自身反推该 proof。 - C0 focused:108 / 108 OK(Lead 独立运行)。
- benchmark:单帧投影 p95 0.0693 ms(≤1 ms)、吞吐 min 13959 events/s(≥240)、C0-owned RSS 0.473 MiB(≤2 MiB)、诊断帧 K=64 恒定、报告字节 18407→18409,预算全过。
- 5 个产品活线文件相对
80b0b16..66dfbb8exact diff 为空,确认仍未进入 C1。 - Sidecar 全量:961 tests / failures=1 / errors=0 / 134.473s。唯一失败仍是既有负载敏感
test_g_rejected_flood_keeps_message_bound_fairness(3117 未大于 3123);不在本批 diff,且此前 base/ship 多轮已同场复现,继续归为既有 flake。
2. C0-FIX-02 仍未闭合:结构化 witness 仍可自证 E4
当前 resolve_c0_evidence_level() 把以下普通 observation 字段视为足以升级 E4:
captureProvenance.kind = "true-device-capture"
trueDeviceWitness.captureId = 任意非空字符串
trueDeviceWitness.capturedAtNs = 任意正时间戳
但这些字段没有绑定到产品采集事实、Session 内容哈希、设备侧不可伪造事实或外部 Test/Lead attestation;仓库中除 C0 自身测试与该解析器外,也不存在另一个可信 producer。测试 _true_device_witness() 本身就是手工构造普通 dict。
Lead 独立反例:对 synthetic fixture 生成的 observation 直接注入:
captureProvenance = {
kind: "true-device-capture",
trueDeviceWitness: {
captureId: "forged-by-fixture",
capturedAtNs: 1700000000000000000
}
}
当前机械报告立即输出:
evidence = E4
evidenceProvenance = true-device-capture-witness
因此原缺陷只从“一个 origin 字符串自报 E4”变成了“一个三字段字典自报 E4”。这仍不满足 Lead 审计要求中的“明确、可信、可审计的 true-device provenance”,也违反项目 E4 是执行/采集事实而非输入 JSON 形状的证据等级红线。
将该剩余缺陷命名为 C0-FIX-02B · mechanical report must not self-promote to E4。
3. C0-FIX-02B 修复决策
最小且最诚实的语义为:
- C0 离线 evaluator 自身永远不得把 observation 内容升级为 E4。
evaluate_c0_feasibility()的机械报告最多标 E2;结构化captureProvenance可以保留为“capture claim / diagnostic provenance”,但不能决定证据等级。 - E4 属于 Test AI / Lead AI 的外层执行证据等级。 真机 C0-1/C0-2/C0-3 实际运行后,由 Test AI 交付 session/命令/设备采集上下文,Lead 再判该批证据为 E4;不得让被分析的数据自己给自己发 E4 证书。
- 如确实希望产物内携带 E4,应另设外部、hash-bound 的 test attestation 层,由正式 E4 harness 对输入 Session/labels 做内容绑定;该层不能是 observation 内可任意赋值的普通字段。本轮为最小修复,不要求先实现该扩展。
- 新增反例:即使 observation 带“结构完整”的
true-device-capturedict,机械 C0 report 仍必须为 E2;原test_only_a_complete_true_device_witness_reaches_e4必须删除或改写,因为它当前钉死的就是剩余缺陷。 - census 可继续显示 capture provenance claim,但必须与
evidence=E2明确分列,禁止把 claim 渲染成 E4。
4. Lead Gate 结论
- C0-FIX-01:PASS。
- C0-FIX-02:PARTIAL;C0-FIX-02B REPAIR REQUIRED。
- C0 E2 总门禁:仍 BLOCKED。
- READY FOR FORMAL E4:NO。 Test AI 暂不启动正式 C0-1/C0-2/C0-3。
- C1:继续 BLOCKED。C2:继续禁止。
下一次 Implementation AI 只允许修 C0-FIX-02B 与对应 tests/census/docs;不得改产品活线、不得补 foreground 产品仪器、不得进入 C1。
Implementation Fix Update · 2026-09-22(C0-FIX-02B)
Lead AI 对 66dfbb847fcd16d1acbe12f2d67108a3e2890428 的独立复验给出 C0-FIX-01 = PASS / C0-FIX-02 = PARTIAL / C0-FIX-02B = REPAIR REQUIRED / C0 E2 = BLOCKED / READY FOR FORMAL E4 = NO(见上文《Lead AI C0 Fix Revalidation》)。本批只修 C0-FIX-02B,在基线 9ba46322ff00fda4e22fab1a49a6b3e35164a86f 上开工。不进入 C1,不改 5 个产品活线文件,不解决 foreground 产品仪器 / surfaceID→PID / paired witness / S2,不启动真机 C0,不宣布 C0 PASS,不宣布 READY FOR FORMAL E4。
1. 缺陷与修复(冻结原则)
修复前行为(本机在工程基线上复跑证实):resolve_c0_evidence_level() 由 observation 自带的 captureProvenance 结构决定等级——只要 kind="true-device-capture" 加任意非空 captureId、任意正 capturedAtNs,机械报告即输出 evidence=E4 / evidenceProvenance=true-device-capture-witness。仓库内除该解析器与自身测试外不存在第二个可信 producer,所以这只是把「字符串自证」升级成「结构化字典自证」。
本机用完整伪造 dict(captureId="forged-by-fixture"、capturedAtNs=1700000000000000000)对 6 个 origin 扫描:基线 9ba4632 上 4/6 个 origin 拿到 E4,修复后 0/6。
| origin | 基线 evidence | 修复后 evidence | 修复后 claim |
|---|---|---|---|
synthetic | E2(origin 否决) | E2 | declared-non-capture-origin |
replay | E2(origin 否决) | E2 | declared-non-capture-origin |
recorded-session | E4(缺陷) | E2 | true-device-capture-claim-unverified |
true-device-capture | E4(缺陷) | E2 | true-device-capture-claim-unverified |
unknown | E4(缺陷) | E2 | true-device-capture-claim-unverified |
""(空 origin) | E4(缺陷) | E2 | true-device-capture-claim-unverified |
冻结修复原则(逐字遵守):C0 离线 evaluator 自身不得根据 observation 内容把报告升级为 E4;机械 evaluate_c0_feasibility() 报告最高只能标 E2;captureProvenance 可继续作为 capture claim / diagnostic provenance 保留,但不能决定 evidence=E4。真正 E4 属于外层执行事实:Test AI 在真机执行 C0-1 / C0-2 / C0-3 并保留 Session、执行命令、设备采集上下文与 labels,随后由 Lead AI 根据该真实执行证据判定「这一批验收证据」为 E4。即被分析的数据不能给自己签发 E4;执行真机实验的人与验收者决定其证据等级。若未来确需报告文件内部携带 E4,须另设 hash-bound external test attestation——本轮为最小修复,不实现、不扩范围。
修复:
| 位置 | 变更 |
|---|---|
resolve_c0_evidence_level() | 删除:等级语义整体撤出该函数 |
resolve_c0_capture_claim() | 新增,返回 str:只回答「payload 的 capture claim 有多完整」,返回 CAPTURE_CLAIM_* 命名空间——与 E* 等级命名空间刻意不相交 |
evaluate_c0_feasibility() | evidence 恒为 EVIDENCE_RECORDED(E2);新增 evidenceAssignedBy="offline-evaluator"、evidenceTrueDeviceAssignedBy="test-ai-execution+lead-ai-verdict"、captureProvenanceClaim、captureProvenanceClaimPromotesEvidence=False;notes 增加一条明示 E4 归属 |
| 模块 docstring | 新增第 4 条设计规则「本模块不给自己评级」 |
| 常量 | 新增 EVIDENCE_ASSIGNED_BY_OFFLINE_EVALUATOR / EVIDENCE_TRUE_DEVICE_ASSIGNED_BY / CAPTURE_CLAIM_{NONE,DECLARED_NON_CAPTURE,INCOMPLETE,TRUE_DEVICE_UNVERIFIED} / CAPTURE_CLAIM_PROMOTES_EVIDENCE=False;删除 4 个带等级语义的 PROVENANCE_* 常量 |
| census row | evidenceProvenance → captureProvenanceClaim,与 evidence / evidenceAssignedBy 分列 |
| census aggregate | 新增 evidenceAboveE2Count(机器可检的 0,非 0 即回归)、captureProvenanceClaimCounts、captureClaimPromotionCount |
| census 控制台 / notes | 证据等级与 claim 分两行打印;notes 明示 claim 不是等级、E4 归 Test/Lead |
EVIDENCE_TRUE_DEVICE = "E4" 常量保留但本模块从不赋值:它作为「刻意缺席者的名字」存在,让读者与测试都能断言机械报告里绝不出现该等级。
2. 实际修改范围(3 个代码/测试文件 + 2 个文档)
| 文件 | 变更 |
|---|---|
MacApp/Sidecar/perfowl_collector_core/startup_c0_feasibility.py | 修复主体 |
MacApp/Sidecar/tests/test_startup_c0_feasibility.py | −1 / +4 → 净增 3 项,共 111 项 |
MacApp/scripts/startup-c0-census.py | 等级/claim 分列 + 两个机器可检计数 |
site/devlog/p4-startup-phase-c-hybrid-total-plan.md | 本段 |
internal/current-ai-handoff.md | C0 状态与 Lead 复验要点更新 |
未改:MacApp/scripts/benchmark-startup-c0-feasibility.py(逐字节未动);internal/startup-c0-benchmark.json 为重跑产物。
3. 5 个产品活线文件隔离证明
realtime.py / recording.py / display_performance.py / launch_trace.py / adapters.py 相对复验对象 66dfbb8、当前 main HEAD 9ba4632 与工作区:git diff --stat 三次均为空,SHA-256 逐一 MATCH。
| 文件 | SHA-256(前 16) | 结论 |
|---|---|---|
realtime.py | 8ee779bdf1e6d49d | IDENTICAL |
recording.py | 886d4b4c2fcf6391 | IDENTICAL |
display_performance.py | 12e5daf3019d3bc0 | IDENTICAL |
launch_trace.py | 78c8e215fbd16117 | IDENTICAL |
adapters.py | 2eead9d1e14f1cae | IDENTICAL |
benchmark 另独立断言 productionHotPath.realtimePyUnchanged = true、新增 ActivityTrace 订阅 0、新增 CoreProfile 录制 0。
4. C0 E2 证据(111 项全绿)与测试集合差异
| 对象 | 结果 |
|---|---|
| C0 focused(ship) | 111 / 111 OK |
C0 focused(基线 9ba4632) | 108 / 108 OK |
用 unittest loader(非 grep -c 'def test_')取测试 ID 集合做差:108 → 111。
- 删除 1 项:
test_only_a_complete_true_device_witness_reaches_e4——它钉死的正是剩余缺陷,按要求删除/改写。 - 改名 4 项(语义从「等级」改为「claim」):
test_census_rows_carry_level_and_provenance→..._and_capture_claim_separately;test_declared_non_capture_origins_veto_even_a_full_witness→..._report_a_contradictory_claim;test_a_partial_witness_stays_e2→test_a_partial_witness_is_reported_incomplete_and_stays_e2;test_resolver_is_the_single_source_of_the_level→test_capture_claim_is_never_an_evidence_level。 - 新增 4 项(含核心反例与非空泛保护探针):
test_lead_counterexample_complete_witness_still_stays_e2——核心反例:即使 observation 内人为提供完整kind=true-device-capture+ 非空captureId+ 正capturedAtNs,机械报告仍必须 E2,且 claim 分列、captureProvenanceClaimPromotesEvidence=False;test_a_full_witness_cannot_promote_any_origin——对多个 origin 逐一附加完整伪造 witness 扫描,全部 E2;test_evidence_ownership_is_reported_not_self_assigned——断言报告声明 E4 归属,而不是默默省略;test_lead_counterexample_forged_witness_cannot_promote_through_the_census——census 层反例,并带非空泛探针:把evidence="E4"硬塞进 row 时evidenceAboveE2Count必须变成 1,证明前面那些 0 有意义。
保留(未放宽):recorded-session 无 provenance → E2;replay → E2;synthetic → E2;partial / invalid provenance → incomplete-capture-claim 且 E2。
5. 全量回归与 flake 归因(E2,未放宽任何阈值)
按门禁纪律做 base(9ba4632) → ship 交错配对 3 轮(共 6 次全量)。
| 轮次 | 树 | ran | 失败集合大小 (F/E) | 耗时 |
|---|---|---|---|---|
| 1 | base | 961 | 7 (4/3) | 241.7s |
| 1 | ship | 964 | 10 (6/4) | 315.9s |
| 2 | base | 961 | 17 (8/9) | 370.5s |
| 2 | ship | 964 | 18 (9/9) | 410.2s |
| 3 | base | 961 | 19 (10/9) | 501.6s |
| 3 | ship | 964 | 22 (10/12) | 835.1s |
ran961 → 964 恰好等于本批 C0 净增 3 项;6 次运行中没有任何 C0 测试进入失败集合。- 耗时 241.7s → 835.1s 单调上升(主机负载持续抬升),失败集合大小基本与之同行;base-r3(19)比 ship-r1(10)/ ship-r2(18)都差,主导变量是负载而非树。
- 机制性归因:全仓库只有
test_startup_c0_feasibility.py引用 C0 模块与 census 脚本(Grep 确认);本批失败的每个测试都住在别的文件里,机制上不可能由本改动引发。 - 3 轮全量并集中仅出现在 ship、从未出现在任一 base 轮的 ID 有 7 个。对其中 5 个直接做 base/ship 交错定点探针(各 3 轮),base 同样复现:
| 争议 ID | base 命中 | ship 命中 |
|---|---|---|
test_11_stop_during_drain_does_not_start_new_tap | 3/3 | 3/3 |
test_14_startup_only_mode_does_not_start_frame_continuation | 3/3 | 3/3 |
test_benchmark_shipping_policy_steady_120hz_7200_frames | 2/3 | 2/3 |
test_benchmark_real_cadence_120hz_10s | 1/3 | 2/3 |
test_pump_yields_after_burst_limit | 1/3 | 0/3 |
其中 test_11 / test_14 在两棵树上都 3/3 必挂(负载下确定性失败),test_pump_yields_after_burst_limit 在基线上反而更容易挂。这 5 项与既有 flake 家族一致(test_pmd3_subscription_fairness、DisplayPipelineBenchmark、test_frametime_continuation)。
- 余下 2 个 ID(
test_filter_stream_yields_on_unmatched_flood、test_f_blocked_wire_does_not_delay_process_exit)各只在最烫的一轮(ship-r2,410.2s,约为干净基准 134s 的 3 倍)出现过一次;随后另做 4 轮 base/ship 交错定点探针(共 8 次隔离运行)同样 0 命中——说明它们只在全量套件的高争用上下文里偶发,而不是在树上稳定失败。它们同样住在不引用 C0 的文件里,本报告不声称它们是已证明的既有 flake,只记为「无机制、单次、仅出现在峰值负载轮、隔离后不可复现」的未定项——与往批同类 flake 的处理一致,且不构成本批改动引入回归的证据。 - 结论:0 项可归因于本批 diff 的回归(未放宽任何阈值)。
6. C0 benchmark 重跑(§10 / §10.1)
| 指标 | 本次 | 预算 | 结果 |
|---|---|---|---|
| 单帧投影 p95 | 0.0955 ms | ≤ 1 ms | PASS |
| 吞吐 min | 10334 frames/s | ≥ 240 | PASS |
| C0-owned 增量 RSS | 0.3906 MiB | ≤ 2 MiB | PASS |
| 诊断帧保留 K | 恒定 64 | 与帧数无关 | PASS |
| 报告字节 | 18409 → 18783 | — | 增 374 B(新增归属字段与 note) |
realtimePyUnchanged | true | — | PASS |
7. 相对原方案的偏差与过程记录
- census 字段改名而非并存:
evidenceProvenance→captureProvenanceClaim。改名而非并存,是因为旧名字本身在暗示「等级的依据」,并存会留下一个仍可按等级解读的入口。 captureProvenanceClaimPromotesEvidence=False作为字段输出:把「claim 不升级等级」从注释变成可断言的机器事实;census 侧同样落到captureClaimPromotionCount。- 两个过程性坑(已修,记录以备后人):
git archive探针最初只导出MacApp/Sidecar,漏掉MacApp/Sources,导致基线树上test_metric_contract_exactly_matches_swift_registry因找不到 Swift Registry 而 ERROR。这是探针工件、不是真实失败;已改为导出完整MacApp+fixtures,单独验证该文件 4/4 OK 后重跑全部 6 轮。- 探针脚本先
chdir再按相对路径写结果,把 2 个 JSON 落进了真实工作区的MacApp/Sidecar/。已用显式路径删除(未用任何通配符或清理命令),并复核脏文件计数回到基线 23 + 本批 5 个改动。
- 首轮探针整体作废重跑:因上一条第一项,第一次的 6 次全量运行中基线侧数据不可信,已全部作废、未用于本报告;§5 表格全部来自重跑。
8. 当前状态与遗留项
- 本批状态:
implemented(E2),等待 Lead AI 复验 C0-FIX-02B。 - 未自判:不宣布 C0 PASS / PARTIAL / FAIL,不宣布 READY FOR FORMAL E4;真机 C0-1 / C0-2 / C0-3 未启动。
- C0 总门禁:仍 BLOCKED;C1 继续阻塞;C2 继续禁止。
- 遗留(均不属本批范围、未改动):S2 kernel marker 无仪器;surfaceID→PID 无证明(
real不可达);paired same-event witness 无产品路径;foregroundVerified的独立证人无产品路径。 - 若 Lead 复验通过并明确放行,下一阶段为 Test AI 在真机执行 C0-1 / C0-2 / C0-3 采集 E4 证据;E4 与 C0 Gate 的判定权仍在 Lead AI。