fix(authority): stabilize resume evaluation identity - #4831
huangruiteng wants to merge 1 commit into
Conversation
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
45707f5 to
e0ca1b8
Compare
|
Rebased onto current main at exact head e0ca1b8. Fresh local validation: control-plane typecheck passed; focused TypeScript authority/outbox 29/29 passed; Python drain/native E2E 66/66 passed; LoopX premerge gate 17/17 passed with public-boundary clean and no manual holds. Per repository policy this control-plane PR remains maintainer-review required; no self-merge was attempted. |
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
评审 exact head:e0ca1b87a30f7fc0244f693ecc13de5c7a0d573c
动机
这个 PR 修复的是 authority identity 把查询时钟误当成持久事实的问题。resume_condition.evaluated_at 由读路径按当前时钟重新计算;旧实现把它放进 runtime-shadow 的 partition/head digest 后,同一份未发生写入的 Todo 状态会在每次读取时产生新 identity,导致 bootstrap 后的只读 inspect 报 drifted,并可能让下一次合法 capture 以 source_partition_continuity_unproved 停住。
我用相同的隔离 Goal、file-v0 backend 和 public coordination-shadow CLI 对 base/head 做了反事实验证:base 09f10c4e37b89c6ed25f2f2354f89d641662e83c 在 bootstrap 后连续两次 inspect 都是 drifted,且 expected digest 每次变化;本 head 连续两次都是 matched,expected 与 observed digest 稳定一致。这个结果证明修复的是实际入口上的确定性缺陷,不只是让 helper 单测变绿。
改动思路
实现边界是合理的:TypeScript local_authority_shadow.ts 仍是 parity、continuity 和 commit 的决策 owner;Python local_authority_shadow_projection.py 只在 source-owned outbox capture 边界镜像同一个 codec。两边都仅从 Todo identity view 中移除 resume_condition.evaluated_at,不改写原 projection,也不移除 satisfied、readiness、reason、generation、target 或其他 Todo/lease 事实。
这比只修 inspect 更完整:inspect 与 writer continuity 必须共用同一 identity,否则只读状态看似恢复,下一次 writer 仍会失败;也比冻结读时钟更稳,因为语义边界被明确表达为“决策事实”与“查询观察”。完整 prepared outbox bytes、传入 projection、cursor、receipt 与 provider revision 仍按原规则验证。
具体改动
生产代码共两处边界:
- TypeScript 新增
todoAuthorityIdentityView、partitionAuthorityIdentityView和导出的localAuthorityShadowPartitionDigest,并让localAuthorityShadowHeadDigest、validateEntryIdentity、validateSourceContinuity使用同一规范化规则。 - Python 新增
_stable_todos与partition_comparison_view,让partition_digest/head_digest与 TypeScript authority owner 保持一致。
关键代码讲解
todoAuthorityIdentityView:clone 单个 Todo,仅在resume_condition为对象时删除精确键evaluated_at;不会通过 substring/prose heuristics 扩大忽略范围。localAuthorityShadowPartitionDigest:只对partition === "todos"应用稳定 identity,lease partition 保持原语义。partition_comparison_view:Python capture codec 的对应实现;跨语言测试直接比较相同 projection 的 digest,避免双 runtime 漂移。validateSourceContinuity:继续要求 previous digest 精确匹配,并要求非 no-op 的新 digest 不同,因此真实 decision mutation 仍不能伪装成无变化。
测试与文档也覆盖了对应契约:Python/TypeScript 都证明“仅时钟变化相等、satisfied 变化不等”;outbox 测试覆盖时钟变化后的下一次真实 Todo mutation;双语 RFC 与 self-repair pattern 说明这个 identity 和恢复边界。
对主干的风险
最重要的兼容风险是 digest 含义发生变化:旧的 pre-promotion lineage 若历史 receipt 中包含旧的完整 Todo digest,新版本 qualification 可能拒绝它,不能把旧 receipt 静默重解释成新语义。这个 PR 没有隐藏该差异:当前 RFC 仍把真实 promotion 保持在未满足门禁状态;对尚未 promotion 的候选 lineage,现有 revision-fenced rollback 会归档 candidate,再从仍然 authoritative 的 legacy source 做 fresh bootstrap。这里不会改写或损坏原始 Todo/lease 数据,也没有自动 promote。
我验证了以下证据:
npm run typecheck:control-plane:通过;- 绑定受支持 Python 3.13 的 TypeScript authority/outbox suite:29/29 通过;
- Python public CLI/runtime-shadow/cursor recovery E2E:38/38 通过(518.49s);
- 同一 synthetic fixture 的 base/head 真实 CLI 对照:base 漂移,head 连续 matched;
loopx canary premerge --from-git-diff --goal-id finance-research-goal:17/17 selected checks 通过,public boundary clean,无 warning/manual hold。
远端 CI 未拉取或等待,因为当前 capability packet 明确 wait_for_ci=false;本地 required validation 没有失败或跳过。最初用系统 python3 跑跨语言子进程时,因解释器过旧不支持项目使用的 dataclass(slots=True) 导致 2 个环境失败;按项目约定设置 LOOPX_TEST_PYTHON 到 checkout 的 Python 3.13 后同套 29 个用例全部通过,这不是被忽略的产品失败。
语义与 CI 对齐
这是对既有 runtime-shadow authority identity vocabulary 的有意扩展,不是另起一套状态权威。规范化依据是独立事实:evaluated_at 是读时观察,而 evaluated decision 是持久语义。风险 profile、typed-state、domain-neutrality、default-off 与 guidance/obligation lens 均未发现阻塞项;尤其 feature-off 路径不创建 shadow state、不改变 CLI 请求形状,也不影响普通 Goal。
我的整体评价
结论:可批准。这个 PR 在正确 owner 中用最小跨语言改动修复了可复现的真实路径缺陷,同时保留了完整字节/投影验证与所有决策字段的 fail-closed 语义。没有引入第二份 authority、配置开关、状态字段或 speculative framework。
Future-facing pass:当前的两个小 identity-view helper 正好对应真实 Python capture / TypeScript authority 边界,不宜再抽成跨语言生成框架;更大的 promotion/migration 工作仍应留在既有 D2/D3 门禁和显式 cutover 计划中。本 PR 的 residual risk 仅是旧 pre-promotion digest lineage 需要走现有显式 recovery,不能把它当成已 promotion 数据的在线迁移方案。
English verdict: APPROVE - exact head e0ca1b87a30f7fc0244f693ecc13de5c7a0d573c; base/head public-CLI reproduction confirmed the clock-only drift fix, supported by 29 TypeScript/cross-runtime tests, 38 Python CLI/runtime-shadow E2E tests, control-plane typecheck, and 17/17 premerge canaries; old pre-promotion digest lineages still require explicit rollback/rebootstrap and no promotion authority is granted.
Summary
resume_condition.evaluated_atfrom runtime-shadow parity and source-partition semantic digestsWhy
resume_condition.evaluated_atis computed from the reader clock. Re-reading unchanged durable Todo state therefore changed the authority digest, so an immediately matched bootstrap could drift without a writer. The same unstable field also made the next captured write fail withsource_partition_continuity_unproved.The decision fields remain authoritative: readiness,
satisfied, reason, generation, target, and every other Todo or lease fact still change the digest.Validation
npm run typecheck:control-planeloopx canary premerge --from-git-diff --goal-id finance-research-goal: 17/17 selected checks passed, public-boundary clean, no warnings or manual holdsProduct entry points
This changes the existing coordination-shadow CLI/control-plane read and capture path without changing request or projection shape. No frontend or Lark control changes are needed because no user-editable setting or new action is introduced; those surfaces continue consuming the same shared projection and typed receipts.
Upgrade recovery
A pending entry created with the old digest semantics must not be reinterpreted or blindly replayed. Operators should archive that pre-promotion shadow lineage with the existing rollback flow, bootstrap a fresh lineage from the unchanged primary source, and qualify new captured operations before promotion.