Skip to content

test(chat): lock strict native thread identities in exhausted-answer recovery - #8158

Merged
huangruiteng merged 1 commit into
mainfrom
codex/steward-thread-id-20261106
Nov 6, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/steward-thread-id-20261106

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

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 main is 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 passed
  • npm run typecheck:control-plane - passed
  • uv run --extra test python -m pytest -q tests/test_steward_interrupted_answer.py - 60 passed
  • git diff --check - passed

Tested head: e5bf839eb5e7da130cb032c184acc9503ac47996, rebased on main at df97c78693417137fcb1cb961fb840062311f7e3.

@mergify

mergify Bot commented Nov 6, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @huangruiteng.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

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 HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Nov 6, 2026
loopx-agent
loopx-agent previously approved these changes Nov 6, 2026

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

动机 / 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>
@huangruiteng
huangruiteng force-pushed the codex/steward-thread-id-20261106 branch from 75ffa6f to e5bf839 Compare November 6, 2026 14:05
@huangruiteng huangruiteng changed the title fix: preserve typed native thread identities in exhausted-answer recovery test(chat): lock strict native thread identities in exhausted-answer recovery Nov 6, 2026
@mergify mergify Bot removed the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Nov 6, 2026

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@huangruiteng
huangruiteng merged commit a948507 into main Nov 6, 2026
7 of 27 checks passed
@huangruiteng
huangruiteng deleted the codex/steward-thread-id-20261106 branch November 6, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants