Repository navigation
fix(turn): preserve journal numbers with shared sparse transport - #7709
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…urce-fidelity-20261030 Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Exact head: 56f971bed39290d9e0d63f8ce4b9b38ef8fa2eb8;不可变 base:2f544f969236f5dbbe858720108c41d07be60a6c。
动机 / Motivation
需要在中断后依据原始证据继续工作的 Agent,以及审阅 Turn journal 的维护者。
同一份计划含 1.0、-0.0 或非安全整数时,旧写入会改变数值;不同计划还可能被当作同一次重放。当前写入保留这些区别,并在改动不可变计划时拒绝覆盖。
同一真实写入探针在 base 复现四类失真和四种漏拒绝,当前源码与普通 wheel 均通过;普通对象的文件字节及 operation id 不变。
本切片不交付 C1 controller/consumer 整体切换、模型采用、长程净收益、合并或安装升级。
统一生产者、同 capture 消费与完整成本资格仍由既有 C1/#7430 路径继续验证。
改动思路 / Approach
原始 Python 对象继续作为 journal 的主体;只有 JavaScript number 不能区分的来源数值附带稀疏 token。TypeScript writer 在原子写入前恢复这些数值,同一来源区别还进入 operation id 和不可变 plan 的比较。只修落盘、却继续用已舍入的数字判断重放,不能修复这个问题。
复用已有 collector、数值校验器和 journal writer,比新增第二种 journal carrier 或 Python 决策 owner 更小。Python 仍只负责 IO 元数据;TypeScript 保留状态、阶段、来源准入与写入决定。该 PR 交付完整的数值保真前置,能够独立回退,不把 C1 全部迁移作为这一修复的前置条件。相邻重构已将 collector 和 validator 收敛为共享实现,没有保留两份数值判定。
具体改动 / Changes
完整审阅 11 个路径,+333/-72,包括两个共享数值适配文件、两个 context consumer、Python/TypeScript journal writer、两个测试文件及三份协议/RFC 文档。
关键代码讲解
- source_number_tokens 从现有 context adapter 移到共享 IO 边界。整值 float、带符号零和非安全整数带来源 token;普通安全整数、分数 double 沿原对象路径,非有限 float 在请求前拒绝。
- write_turn_journal 是真实 Python caller,只在有 token 时增加元数据,保留原对象、expected effect 和 source admission;仍调用同一个
turn_journal.write。 - sourceNumberTokens 校验路径类型、存在性、重复项、JSON 数值语法及解码值一致性。Context source identity 和 journal 共用这一实现,未引入文字或字段名猜测。
- commitTurnJournal 结合
parseJournal保留当前及既存文件的数值区别。来源、effect、阶段和锁校验保持原 owner;拒绝不能覆盖原文件,恢复审计的浅拷贝也保留顶层数值映射。无 metadata 的普通对象继续原编码。
规范依据:LoopX Turn journal 原契约、C1 原边界。spec_revision 为上述完整 base。journal_source_fidelity、immutable_plan_replay、existing_authority_and_limits 在当前 head implemented;C1_cutover_and_net_benefit deferred。这里以修改前契约和真实反例作 oracle,本 PR 新增说明不证明自己的正确性。
对主干的风险 / Risks
未发现当前阻断性问题。最强风险是“文件中的数字看似正确,但重放仍把两个来源当作同一个计划”。同一公开 writer 探针在 base 丢失四类来源区别,四种计划改动均未被拒绝;当前 head 和普通 wheel 保留全部六组数值,并对四种改动报 plan is immutable,原文件字节不变。普通对象在 base/head/wheel 的字节与 operation id 完全一致,原有分数值及普通字符串也保留。
独立执行:176 项完整七模块 Python 测试、166 项相关 TypeScript 测试通过。覆盖实际 source admission、Goal 实例变化、阶段和 effect 冲突、重放、恢复、context delivery,以及非法 token/缺少 raw-number 支持的拒绝;当前 Node 为 24.21.0,未把它当作最低版本 Node 的新执行。普通 wheel 在空临时环境安装,1,787 个 Python/TypeScript/JSON 源成员与当前源码相等;真实 File/SQLite Goal 均完成 source-admitted 写入、重放、数字改动拒绝及独立 CLI 只读检查,registry 和主状态未变。最初陈旧前端资产的打包拒绝保留,随后按正常构建流程通过,没有绕过来源校验。
成本保留负面结果。同一带引号的 155 万字符正文,合成参数从对象的 1,907,777 字节变为稀疏方案的 1,907,840 字节;整段 JSON 字符串为 2,503,942,超过既有 2 MiB 上限。真实稀疏 journal 以一次 RPC 接收并保留正文,超大请求仍拒绝且不建文件。6,000 个整值 float 的参数则从 30,055 增至 190,971 字节:这是来源保真的成本,未包装成延迟、token 或长程收益。
语义与 CI 对齐
复用 loopx_turn_journal_v0 和既有临时 source_number_tokens 传输,不新增持久状态、权限或 actor lifecycle。两个 RFC 语言版本同改;双语 diff、TypeScript typecheck、diff-scoped advisory 和完整语义检查通过。标准 premerge 第一轮的 6 项直接检查与 14 项选择检查全过,其中语义扫描为 94.835 秒;补齐精确 scope 质量回执后复跑,同一 head 的语义扫描在原 120 秒超时。独立 immutable base 也在该上限超时。当前必需验证与 CQR 已如实保留失败,合并资格仍 hold;此前通过没有被删除,也不据此放行。
评审期间的脚本路径、外部脚本导入来源和不支持的 inspection registry 路由错误均保留;修正后以实际 module 路径、普通 wheel 与独立源数据回读核验。没有将未执行检查记为通过。按当前配置未查询、轮询或等待 CI。
我的整体评价 / Verdict
APPROVE,仅针对该完整精确 head 的数值保真修复,delivery 判断为 justified_increment。long_horizon 为 preserved:后续恢复所依赖的原始事实、不可变计划和一次结算身份得到保留;user_experience 为 improved:原命令和 journal 读回即可看见正确数字,不增加设置或确认回合。视觉审阅不适用:受影响的是 journal 与 CLI/context 传输,没有网页或 App 渲染改动;已安装 App、最低 Node、真实模型采用和长程收益仍未独立认证。
共享 collector/validator 和既有 writer 的边界与问题相称;没有必要增加新 codec、配置或捕获服务。C1 的完整切换和收益验收仍开放,代码批准不授予 merge、dismiss、安装或新的实验权限。
English verdict: APPROVE — 56f971b. Matched real-writer and installed-wheel probes preserve numeric source facts, immutable-plan refusal and ordinary-object identity; 176 Python and 166 TS tests pass. C1 cutover and end-to-end benefit remain separate; the current required semantic-deadline and CQR gates remain held.
Python-to-TypeScript journal writes can turn
1.0into1, lose signed zero, and round unsafe integers in saved evidence. The same numeric collisions can make a changed immutable plan look like a replay.This change keeps the existing journal object carrier and reuses the sparse number metadata already used by context delivery. The Python collector now lives in the existing runtime IO adapter; context projection and the journal share one TypeScript validator. The journal owner restores the numeric spellings for publication and uses them in replay and immutable-plan comparisons. Goal lifetime, admission, effect, phase and lock checks remain with that owner. Ordinary object callers retain their bytes and operation identity; historical receipts are not rewritten.
The whole-JSON-string approach from the closed #7657 is superseded. It preserved numbers but double-escaped evidence text. On the same synthetic workload:
The hard 2 MiB request limit and one-RPC write remain unchanged. The sparse arm preserves all original numeric facts and quoted text, and replay leaves the file unchanged. A number-dense control still grows from 30,270 B to 199,185 B; number-dense sources can reach the same request limit earlier. The incorrect baseline is not an efficiency target. These component checks do not establish a latency or long-horizon benefit.
Validation on final
56f971bed39290d9e0d63f8ce4b9b38ef8fa2eb8, based on main2f544f969236f5dbbe858720108c41d07be60a6c:cat --and are checked against the actual disposable capture references, including the final missing-evidence refusals.Earlier component
aafc2d29passed 203 source cases, 134 quiet CLI cases and 241 context cases (one Linux-only skip). All 11 proposed files are byte-identical in the final main integration; the final package/integration results above qualify the new combination separately. The initial concurrent CLI cohort's 131 passes and three startup/deadline failures are retained. The quiet rerun kept the original time limits. An initial package-audit wrapper also misclassified OS capture readers as Python readers; product tests passed, and the corrected audit follows the actual published commands.This is an independent prerequisite for #7430. Unified composition, writer/consumer cutover, old-reader retirement, and comparable original-quota outcomes and full costs remain open there. The paired controller RFC and Turn protocol record this boundary and the rejected transport result.