Repository navigation
test(chat): lock strict native thread identities in exhausted-answer recovery - #8158
Conversation
|
This pull request has merge conflicts with Choose the remote for the base repository, not an out-of-date fork. git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEADFor a same-repository clone whose Keep the DCO |
loopx-agent
left a comment
There was a problem hiding this comment.
动机 / Motivation
未发现阻塞性缺陷,结论为 APPROVE,仅针对 75ffa6ff25b518e36de3adabc71beed933805d94。
通过原生会话恢复读取既有结果的操作者,以及维护该入口的开发者。 同一个已耗尽恢复预算的请求需要读取原线程结果;旧实现把 unknown 直接传入 string 数组的 includes,完整类型检查失败。此改动在已验证的字符串集合中查找原值,保留读取顺序与严格身份比较。 完整 control-plane 类型检查恢复通过,合法字符串线程仍可读取;数值、空值、布尔值、对象和数组不能冒充原线程。 不增加恢复次数,不重开业务执行,不创建替代线程,也不改变 Stop、权限或预算。
改动思路 / Approach
复用原有 TypeScript 恢复判定和已验证线程集合,直接缩窄类型即可解决编译错误;另建解码器、状态或兼容分支没有独立收益。 本 PR 的边界是两个严格比较及其反例测试;不扩大 read-only review 的执行授权或恢复预算。
已有恢复过程只允许同一原始请求的一次受限结果读取。先检查 Session、Turn、预算、Stop 和上下文版本,再从当前及合法历史线程中确认目标;本次没有另建恢复通道。比较如下,表格描述代码关系,实际证据来自原生测试和真实 Effect RPC:
| 输入 | 旧运行行为与新运行行为 | 本次编译变化 |
|---|---|---|
| 原线程字符串或合法历史字符串 | 保留严格匹配和读取顺序 | 完整类型检查通过 |
| 数值、空值、布尔、对象、数组或外来线程 | 不能冒充已验证字符串 | 不再把 unknown 传入 string includes |
| Stop、额外输入、权限或预算不符 | 维持原有拒绝 | 无新增授权 |
具体改动 / Changes
审查了完整两文件差异(+22/-2),并追到未改动的 Python 原生会话读取入口和 Effect handler。判断依据为 docs/architecture/rfcs/capable-manager-semantic-handoff-v0.md,不可变版本 54e26effd9cc958179b9fe555d50ba22d13538a6;准则 S5/S10 要求保留原 Session/Turn 和不确定情况的拒绝,A23 要求持久化 Stop 阻止迟到执行。两项在当前头上均保持。
关键代码讲解
planExhaustedStewardAnswer(conversation_binding.ts:1114)仍先验证原始失败 Turn、quota attempt、版本、Stop 和附加输入;改动没有移动这些检查,也没有重置恢复预算。priorNativeThread/nativeThreads(同文件 1144–1145)使用find返回集合中真实存在的字符串。合法历史线程仍排在前面;不匹配的未知 JSON 值不会重排集合。这里不需要类型断言或转换。- 原生 proof 的 membership(同文件 1162)使用
some(thread => thread === proof.thread_id)。数字123与字符串"123"保持不同;只有原线程 proof 才能继续走原有nativeRecoveryCapacity。
独立验证:完整 control-plane typecheck 在 base 复现 TS2345、在 head 通过;55 项 TS 测试通过,同一新增测试集运行于旧生产实现也通过,支持运行语义保持。实际 Python 原生恢复入口在 base/head 各 60 项通过。另用同一冻结输入经过真实 collaboration.conversation.request RPC,16 项完整 JSON 观测逐字节一致,无归一化。故意加入 String(proof.thread_id) 的临时 mutation 后,同一路径的独立 oracle 在 proof-123 拒绝;恢复原文件后通过。最终干净 head 的 diff-selected premerge 通过。
对主干的风险 / Risks
主要风险是以“修类型”为名改变线程身份,或者改变合法历史读取顺序,最终让恢复读取到错误会话。上面的负例、完整结果对照及 mutation 针对这项风险;测试只比较字符串成功例会漏掉它。没有新 schema、持久化字段、状态枚举、CLI、提示词或默认开关,既有 Stop、预算、权限和只读限制仍在原 owner 中。
可选 maintainability ratchet 仍然失败:base/head 的完整输出一致,唯一未豁免项是未修改的 Python _admitted_interaction_next_cli_actions 体积债务。它保持 failed,并按独立基线证据记录为无关可选检查;没有修改阈值或把红色结果标绿。该归因依赖完整失败身份、详情、相同 fixture/环境和另行通过的变更不变量。最终 premerge 为通过;早期与临时 mutation 重叠的诊断运行已排除。未查询或等待 CI。
我的整体评价 / Verdict
这是一项与实际编译错误相称的修复,复用了现有 owner,且通过了反向反例而非只证明新代码能运行。两个改动文件只涉及原生恢复判定和测试,检查过的 Chat/Lark 展示入口没有渲染变更,无需新增截图。 本次未验证已安装 App、在线模型行为或 Windows;这些路径的展示代码未变。 本次通过不代表在线模型可用性、长期收益或合并授权;合并 readiness 仍需单独读回。
Reviewer: model_agent · OpenAI · gpt-6.1-sol · runtime_reported · effort=max
English verdict: APPROVE 75ffa6ff25b518e36de3adabc71beed933805d94. The strict native-thread identity and original-request recovery contract are preserved; exact-head local type, native/RPC parity and final premerge checks pass. An unchanged optional ratchet failure remains disclosed; no merge is performed.
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
75ffa6f to
e5bf839
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Reviewer: model_agent | GPT-5 Codex | OpenAI | self_reported
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
维护 exhausted-answer recovery 的开发者需要在后续重构时继续保证 native thread identity 只能来自已验证的字符串集合。此前该入口曾把 unknown 值直接传入字符串数组比较,触发完整 control-plane TypeScript 检查失败;本次 rebase 时 production 修复已经独立进入 main,因此本 PR 的真实剩余价值是补齐身份类型矩阵,防止以后重新引入隐式转换或错误排序。
改动前,测试只覆盖少量 proof 身份;现在同时覆盖 prior 与 proof 的数值、null、布尔、对象、数组和外来字符串,并保留 "123" 作为合法非空线程身份的对照。这样能把“同字面但不兼容类型”与真正字符串身份明确分开。边界仅是回归测试,不改变恢复次数、Stop、权限、预算或用户界面。
Maintainers of planExhaustedStewardAnswer and operators relying on original native-session recovery. Before, incompatible JSON identities had sparse coverage; after, prior and proof values are checked across the relevant JSON types while a valid string identity remains positive. The TypeScript suite passes 57 cases, full control-plane typecheck passes, and the real Python recovery caller passes 60 cases. No production fix, schema, permission, persistence, transport, scheduler, or UI change.
改动思路 / Approach
调用路径是测试中的 planBoundConversationRequest 进入既有 planExhaustedStewardAnswer;权威输入仍是 current Session、已验证的历史 thread IDs 和 typed recovery observation。main 的生产实现先执行 typeof ... === "string" 缩窄,再检查集合成员,因此本 PR 不再重复一套 production 写法,只把负例固定在现有 owner 上。
这项测试与 main 已有的 null/数字 proof 反例有局部重叠,但补上了 prior identity 的完整 JSON 类型矩阵、对象/数组/布尔输入,以及 numeric-looking string 的正向约束。没有发现其他 open PR 在维护同一组断言;代码量仅为 19 行测试,范围与要保护的身份不变量相称。
具体改动 / Changes
完整 diff 为 1 个测试文件、+19/-0,没有 production、docs、generated 或机械移动。规范依据仍为 docs/architecture/rfcs/capable-manager-semantic-handoff-v0.md,审查修订 df97c78693417137fcb1cb961fb840062311f7e3:S5/S10 要求保留原 Session/Turn 与不确定情况下的拒绝,A23 要求 Stop 和迟到执行保持原约束;这些在当前 main 的 recovery owner 中未被本 PR 修改。
新增断言位于 tests/control_plane_ts/conversation_binding.test.ts。它先确认非字符串 prior 不能进入 native_thread_ids 或重排已验证顺序,再确认非字符串 proof 只能得到 recovery_candidate=true 而不能得到 recovery_eligible=true,最后确认字符串 "123" 仍可被优先识别并进入合法 recovery。生产实现无需在本 PR 中再次修改,避免与 main 已有修复形成第二套逻辑。
对主干的风险 / Risks
本 PR 没有运行时路径、持久化格式、schema、CLI、提示词或外部副作用变化,最坏失败只是在测试进程中断言不成立。主要残余风险是它只能保护当前 JS 运行时身份行为,不能替代完整 typecheck;因此我把 npm run typecheck:control-plane 作为必要验证,并同时运行真实 Python recovery caller。
验证结果:57 项 TypeScript 测试通过,完整 control-plane typecheck 通过,60 项 test_steward_interrupted_answer.py 通过,git diff --check 通过。没有阻塞 finding;最强但未覆盖的边界是已安装 App 和在线模型行为,不过该 PR 未触碰这些展示或执行面。视觉审查不适用,因为 diff 只增加测试断言。
我的整体评价 / Verdict
这是一项 goal_achieved 的测试加固:它把 main 已经正确的严格字符串身份契约锁进现有 recovery 测试,既不复制 production decoder,也不需要新的状态或 helper。long_horizon 语义保持,user_experience 不适用;兼容性没有变化。最终评审针对 exact head e5bf839eb5e7da130cb032c184acc9503ac47996,没有发现需要阻断合并的问题。
English verdict: APPROVE e5bf839; test-only coverage now locks strict prior/proof thread identities while preserving valid numeric-looking string identities.
Goal And Delivered Outcome
Keep regression coverage for strict native thread identities in exhausted-answer recovery. The original production type error has already been fixed independently on
main; after rebasing, this PR retains only the tests that exercise unvalidated prior/proof identities.The tests verify that numeric, null, boolean, object, array, and foreign prior identities cannot reorder validated thread IDs, that non-string proof identities cannot authorize recovery, and that a numeric-looking string identity remains valid.
Scope
This is a test-only follow-up. No runtime, authority, persistence, transport, prompt, or user-interface behavior changes.
The production implementation from
mainis preserved: a prior ID must first be a string present in the validated set, and a proof ID must be a string before strict membership is checked.Validation
node --no-warnings --experimental-strip-types --test tests/control_plane_ts/conversation_binding.test.ts- 57 passednpm run typecheck:control-plane- passeduv run --extra test python -m pytest -q tests/test_steward_interrupted_answer.py- 60 passedgit diff --check- passedTested head:
e5bf839eb5e7da130cb032c184acc9503ac47996, rebased onmainatdf97c78693417137fcb1cb961fb840062311f7e3.