Skip to content

Stream scoped local authority migration backups - #6620

Merged
huangruiteng merged 6 commits into
mainfrom
codex/size-unbounded-local-migration-20261016
Oct 16, 2026
Merged

huangruiteng merged 6 commits into
mainfrom
codex/size-unbounded-local-migration-20261016

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Reviewed local Goal migration reused complete disaster-recovery inputs, so a project with large .loopx/research artifacts could turn a small authority cutover into a multi-gigabyte transfer and then hit the generic recovery browser's 128 MiB archive / 512 MiB expanded limits.

This change gives trusted local authority migration its own profile. prepare-import now creates or replays a Goal-scoped allowlist archive containing only source-owner-selected state, registry, referenced evidence, and lease facts. .loopx/research, experiment artifacts, caches, and unrelated runtime content remain in place. Existing complete backups remain valid explicit inputs and are witnessed as streams with a disk-backed member index, without fixed total archive, expanded-byte, or member-count ceilings. Path/collision/digest/source-change checks remain fail-closed, selected links cannot escape or cycle, and TypeScript accepts both legacy v0 and streaming v1 witnesses. Target provider writes receive a real free-space preflight and typed capacity diagnostics.

Validation:

  • ruff check on all changed Python and test files
  • npm run typecheck:control-plane
  • tests/control_plane_ts/cold_source_import.test.ts: 16 passed
  • cold backup/import/state focused Python suites: 68 passed
  • final HTTP/App cold-import matrix: 44 passed
  • real read-only witness: 1,048,771,702 compressed bytes, 2,492,886,200 logical bytes, 53,798 members; 19.156 seconds, 20 MiB disk index, unchanged SHA-256
  • loopx check public-boundary scan: 8 changed files clean

Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>
Signed-off-by: huangruiteng <huangrt01@163.com>

@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.

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

REQUEST_CHANGES — head eef564598dbc47324346c18c426b94fc884fc2c9。方向有价值;当前 head 先修复下面两项问题再复审。

动机

旧 Markdown Goal 的操作者需要先保留可核验的来源,再明确确认迁入 canonical authority。过去这条路径会把无关研究产物一起打包,使小范围迁移承担整个项目的复制和核验成本。这个 PR 希望让普通迁移只备份选定 Goal 的协调来源,并继续接受操作者显式提供的完整旧备份。可观察的结果应是迁移备份不再包含无关研究目录,完整 Todo、归档、源字节和原操作恢复保持可核验。这不是完整灾难恢复、Host 重新激活、SQLite 默认发布或 D2 长期资格化。

尚待补齐的是合法旧备份的兼容性和大归档扫描的成员缓存约束;其余完整恢复与长期资格化仍按原 RFC 推进。

改动思路

备份与导入继续复用现有 owner,职责划分合理:Python 选择来源并执行归档 IO,TypeScript 校验源覆盖、决定目标、管理 writer fence 和原操作回执。没有新增平行的 Python 导入状态机。普通 backup-state 仍是完整备份;CLI prepare-import 不提供 manifest 时,和 App 预览一起改用 scoped archive,显式 manifest 则继续走完整旧备份兼容路径。

入口 本次行为 保持的边界
CLI 无 manifest / App 预览 只复制来源 owner 选出的文件;重用原备份发布器 预览不导入、不授予执行、不停止 Host
CLI 显式旧 manifest 扫描完整归档,向 TS 传递所需成员摘要 无关内容不能冒充源覆盖;合法旧备份应继续可用
apply / recover 核验原计划、原 source witness、原目标;容量预检 恢复原操作,保留后续 canonical 写入

这份改动的当前边界应保留为“可信本地迁移的来源备份与核验”,不把通用跨平台提取的名字折叠策略引入只读 witness,也不为其另建迁移框架。磁盘索引是合理机制,但必须连底层 tar 元数据缓存一并检查,才能达到减少大归档内存累积的目的。

具体改动

规格依据:docs/reference/local-authority-provider-selection.md,spec_revision=f6cf9583794eb8c4cca41fa832a1c2f150059583。完整恢复的独立边界参照同 revision 的 complete-state-recovery-v0.md。逐项对照原文:

  • Inventory and back up the complete supported source:scoped 来源仍包含完整当前/归档记录、registry、evidence 和租约字节;原始来源覆盖由 TS 验证。无关研究产物不进入新归档,现有 CLI 回归通过。
  • Historical ownership inputs, source captures and backups:尚未满足兼容性,见下面 F1。合法的大小写不同成员不应因采用新索引而失去显式旧备份导入能力。
  • Stop source writers and affected Hosts:停写声明、active/expired/orphan lease 拒绝及 outbox 原 owner 均保留;实际 Host 与 runtime 回归通过。备份成功仍不是停写证明。
  • Preview an immutable source/target-bound plan:沿用 digest-bound carrier、目标身份和 source drift 检查;新容量读数不进入计划身份,原操作重放不因空闲空间变化生成另一份计划。
  • Recover the same operation after interruption:File/SQLite 的重启、原回执恢复、后续写入保留通过本地实际入口回归。更大范围的身份接管和完整恢复继续留在既有 RFC,不作为这次 PR 的前置要求。

关键代码讲解

  1. create_cold_source_backup 与 build_scoped_state_backup_plan:给同一 Goal/operation 生成稳定备份名,将 owner 选出的来源交给既有非覆盖发布器。state_backup.py 仅对迁移 profile 关闭重复的大型 file_members 清单;普通完整备份保持原有清单。CLI 与 HTTP adapter 均已接入。
  2. read_cold_source_backup / _scan_archive:对实际归档字节取摘要,扫描成员并用磁盘 dbm 判重,最后核验原文件与 manifest。新增路径、链接和碰撞规则,但 F1 是过强判定,F2 是扫描器底层仍保留线性内存。
  3. verifySourceBackup / executeColdSourceImport:同时读 v0/v1 witness,复核两个完整产物摘要和每个必需源文件的覆盖;scoped profile 不再要求把整个 runtime 纳入归档。容量预检为真实 statfs 观察,异常与历史回执不构成执行许可。

文档说明了新的默认路径、显式完整备份兼容入口以及 complete_goal_backup_verified=false 的含义。两份改动测试覆盖了大于 512 MiB 的展开数据、部分链接拒绝、scoped 排除无关目录及重复 prepare;不能用这些通过结果代替下面的独立反例。

对主干的风险

F1 [P2] — 全归档 casefold() 判重误拒绝合法完整备份。 _member_key(123–124 行)把所有成员先 NFC/casefold,再用同一个键判重;大小写敏感文件系统允许并存的 runtime-root/unrelated/A.txt 与 runtime-root/unrelated/a.txt 因而被判为同名。它们甚至可以完全不属于所选 Goal。使用同一实际 workspace()/prepare-import --backup-manifest 路径,只把两个无关文件由 A/B 改为 A/a,并重新计算归档摘要与大小:基线 bf4a1240532369f63a1d04b142a9f495bafb18d3 为 2 passed,head 为 1 passed / 1 failed,失败为 cold_import_backup_member_unsafe;没有进入导入或新授权。最小修复是让可信本地、只读 witness 按其真实路径身份保留合法区别,继续拒绝真正的重复路径与结构冲突,把通用提取的跨平台限制留在其独立 owner。请把该对照补到现有 scope/CLI 回归,并覆盖 NFC 不同拼写及真正重复的相反案例。

F2 [P2] — r|gz 没有关闭 TarInfo 缓存,磁盘索引之外仍有全量内存索引。 192 行只指定流式压缩读取模式;本次实际 Python 3.14.8 的 TarFile.stream 仍为 false,TarFile.next() 会持续 append 到 tar.members。独立真实归档扫描 25,002 个成员、只返回 1 个所选成员后,仍保留 25,002 个 TarInfo。成员增长时,这部分内存仍线性增长,不能把 20 MiB 磁盘索引当作整个扫描的内存边界;这不是已测出的 OOM。请在支持版本上关闭成员缓存,并为较旧受支持 Python 保留有界的等价策略,增加不依赖机器 RSS 阈值的缓存保留回归。无需引入另一套 tar 解析器。

容量故障还可顺手 refine:我只注入 statfs 的 0 可用空间读数,仍使用真实 File/SQLite、计划与 fence。apply 返回 cold_import_target_capacity 时 fence 已写入而目标仍 missing;恢复空间后原 recover 能完成,没有丢失数据。建议在首次 fence 前也做当前容量检查,同时保留 commit 前检查,减少可以预先拒绝的中间状态;这不是本次要求改变既有恢复模型。

独立验证:Python 聚焦 54 passed;实际 packaged-compatible HTTP 44 passed;Host/runtime 6 passed;TS 冷导入 16 passed;control-plane typecheck、Ruff、完整 semantic smoke 与 diff check 通过。首次 HTTP 运行因全新 checkout 未构建 bundle 出现 44 个 setup errors;构建本 head 后才重跑得到上述 44 passed,这不是已复现的产品失败。首次误用未安装的 tsx 启动器未执行测试,随后改用仓库原生 Node 命令。没有读取、等待或据此判断 GitHub CI,也没有独立重做作者宣称的 1 GiB 私有产物见证。

语义与 CI 对齐

本 PR 在既有 cold-import witness owner 内扩展版本标签和容量诊断,没有新增执行策略或第二个决策 owner。semantic advisory 不支持这些 unnamed/dynamic 语义,因此空结果不是无新增语义的证明;完整 drift smoke 通过也没有覆盖 F1/F2。硬性的源覆盖、停写、fence 和执行许可界限没有被称为建议。额外拒绝合法旧输入是未披露的语义收窄,应在本 PR 修复;新 scoped 默认已在文档明确说明。没有因这些改动重新宣布 D2 或完整恢复达标。

我的整体评价

这是值得优先修好的一份 PR:它减少迁移对无关研究目录的复制,继续复用原 source/backup/import owner,没有引入泛化框架;源码体量与问题基本相称。对长期使用,磁盘索引是改善方向,但尚有可定位的内存保留缺口;对用户路径,合法旧备份直接失败是实际回归。我的结论是 REQUEST_CHANGES:先修 F1/F2,再按准确新 head 复核整个 PR。容量检查的时机可作为同边界的有限完善;逆序祖先冲突等更广泛结构覆盖仍不应从现有少量用例推定为完整。无需把完整灾难恢复、长期 D2 或我们的其他未完成工作一起塞进这个修复。

English verdict: REQUEST_CHANGES — eef5645. Valuable scoped-migration design, but legal case-distinct old archives now fail through the real CLI, and tar scanning still caches every TarInfo in memory. Independent File/SQLite CLI/HTTP, recovery, typecheck and semantic checks otherwise passed; no merge authorization is implied.

Comment thread loopx/control_plane/coordination/cold_source_backup.py Outdated
Comment thread loopx/control_plane/coordination/cold_source_backup.py
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…itness-repair-20261016

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@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.

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

APPROVE — c0387f39d1ba320cdd25e2ad6e24be2cdb5f30be。前次 F1/F2 已修复;此次重新判断整个 base-to-head PR,没有继承旧结论。没有发现阻塞问题。 我也参与了这次修复,这份复审不宣称独立 reviewer 共识。

动机

旧 Markdown Goal 的操作者需要先保留可核验的来源,再明确确认迁入 canonical authority。过去一次小范围迁移也会复制无关研究产物;现在普通预览只备份选定 Goal 的协调来源,已有完整备份仍可显式使用。可观察的改善是减少无关文件复制,同时保留完整任务、归档、源字节和原操作恢复。这个范围不包含完整灾难恢复、Host 重新激活或 SQLite 默认发布。完整恢复的身份接管、可变 owner 捕获和长期资格化仍由既有 RFC 继续推进。

改动思路

备份与导入继续复用现有 owner,职责划分合理:Python 选择来源并执行归档 IO,TypeScript 校验源覆盖、决定目标、管理 writer fence 和原操作回执。不会因引入临时 SQLite 索引而增加一个 Goal authority。索引只是一次扫描的可删除工作空间,路径和文件类型按实际归档成员判断;来源覆盖仍由独立的类型化导入规则核验。

当前 PR 的边界是既有 CLI/App 冷迁移的来源备份与核验;普通完整备份和通用跨平台提取继续沿用各自边界。提高通用恢复大小上限无法消除无关复制,另建迁移框架也没有必要。

触发入口 现在的行为 必须保留的条件
CLI 不给 manifest / App 预览 创建或重用选定来源的 scoped 备份 预览不授予执行权限
CLI 显式完整旧备份 扫描全部结构,只传所需来源摘要 合法不同名字保留,真正冲突拒绝
明确确认 / 原操作恢复 原计划、源核验、fence、File/SQLite 回执 停写、租约结算、目标身份和后续写入保护

上表描述本 head 的代码路径;执行证据另列于下文。

具体改动

规格依据是 docs/reference/local-authority-provider-selection.md,spec_revision=f6cf9583794eb8c4cca41fa832a1c2f150059583。使用改动前已接受的契约判断本次文档修改,而非用新文档证明自身正确;同 revision 的 complete-state-recovery RFC 继续区分惰性验证与后续接管。

  • Inventory and back up the complete supported source:已实现。来源 owner 提供当前/归档任务、registry、证据和租约字节;真实 CLI scoped 回归及 HTTP 覆盖证明排除无关目录没有截断来源覆盖。
  • Historical ownership inputs, source captures and backups:已实现。保留已持久化 v0 witness/carrier 和显式完整旧备份;大小写、Unicode 不同拼写在实际 CLI 新回归通过,真正同名和祖先冲突仍拒绝。
  • Stop source writers and affected Hosts:保留并验证。未结算租约、孤儿租约、outbox 拒绝和实际 Host/runtime 路径仍经过原 owner。备份不是停写证明。
  • Preview an immutable source/target-bound plan:保留并验证。重复 prepare、来源漂移、目标身份、错误 digest 和未确认恢复均沿用原不可变计划。预览后新增无关研究/cache 文件的实际 CLI 反例通过,原计划路径与 digest 不变。
  • Recover the same operation after interruption:保留并验证。File/SQLite 的原回执、重启、丢失响应和后续写入保护通过真实入口检查;没有把读回变成再次执行。

关键代码讲解

  1. create_cold_source_backup 与 build_scoped_state_backup_plan 重用非覆盖归档发布器。普通 backup-state 的完整来源和成员清单保持不变;只在迁移 profile 内减少冗余清单。
  2. _scan_archive 使用临时 SQLite 事务索引,保留真实 UTF-8 名字,标记隐含父目录并检查两个顺序的文件/祖先冲突。每个 header 后清空无需随机访问的 TarInfo 缓存。独立 25,002 成员扫描仍只返回 1 个所选摘要,缓存从旧 head 的 25,002 降至 0;这不是整函数恒定内存或 OOM 测量,所选来源本身仍可增长。
  3. read_cold_source_backup 核对实际产物摘要、源映射与稳定字节,临时索引满时返回容量错误并保留原归档。真实 SQLite 页数限制故障验证了清理和原始两份文件未变。
  4. verifySourceBackup / executeColdSourceImport 保留唯一来源覆盖和迁移状态机;v0/v1 是原 witness owner 的格式扩展,没有新执行策略。

对主干的风险

复审分别检查了 eef5645..c0387f3 修复及 ef05bb2..c0387f3 整个 PR:8 个相关文件,827 行新增/134 行删除,没有夹带已合入 main 的其他功能。前次反例先得到 6 failed/4 passed,再修复到当前 targeted 11 passed;Python 3.11 的完整 scope 文件 12 passed。大小写/NFC 兼容性、真正重复、双向祖先冲突、后到目录和磁盘索引容量都包含在内。

全量功能证据来自紧邻父提交 6e4da7fd80f216198bef687f01e6654e84c077cb:70 Python、44 真实 HTTP、16 TS,及两类静态检查、完整 semantic smoke。到当前 head 只删除不受支持 Python 3.10 的容量错误字符串 fallback;项目最低版本为 3.11,类型化 SQLite 错误码分支保留,当前 targeted/3.11 真实容量回归覆盖这项变化。这些父提交结果是经失效分析复用,未宣称全部在 c038 重跑。

当前 head 另已构建并运行打包前端到真实后端的现有 goal-storage scenario:File/SQLite、中文/英文、桌面/窄屏、未结算租约/outbox 拒绝、明确确认、刷新/新窗口原操作读回、提交后丢失响应与持久化取消均通过。外围工作区发现使用合成 fixture;实际存储请求使用隔离的真实服务,没有调用模型或活动 Goal。

canary premerge --from-git-diff 当前 head 通过:5 个直接检查、17 个选择项;其中 DSH 结构 ratchet 的原始失败被保留为 1 项 advisory。相同 Python、不可变 base/head 的完整规范化报告一致,均为未改动函数的 93 statements/51 decision points;未增加例外、放宽预算或把原始失败说成通过。八个公开路径扫描干净,CQR cqr_1cc301cc20852eb577c1 对应准确 base/head 并 verified valid。无手动 hold;未查询或等待 CI。

容量预检在首次 apply fence 后才再次执行,仍有可完善空间:零可用读数会留下 fence/空目标;恢复容量后原 recover 成功,这一真实 File/SQLite 故障路径已验证。它是现有恢复模型内可恢复的资源拒绝,当前不需要另建框架来合并本修复。未独立重复作者的私有 1 GiB 产物,也未宣称其他操作系统、完整 M2/M3 或 D2 达标。初次缺 bundle/错误 TS 启动器的环境失败已保留,并由实际重建与原生启动命令通过结果取代。

语义与 CI 对齐

现有 witness 格式扩展、scoped 默认和容量错误已在原文档/CLI 披露;源覆盖、停写、租约、fence 和显式确认仍是强制条件。临时索引的 F/D/I/L 标记属于局部 tar 结构,不创建共享控制面词汇或权限。开发时 advisory 先于完整 semantic 检查,后者通过;空 advisory 不作为语义等价证明。v0 兼容分支有实际持久化消费者,所以保留;不受支持 3.10 的消息 fallback 已删除。

我的整体评价

长期使用改善:无关文件不再进入默认迁移备份,扫描也不再累积全归档 TarInfo;用户路径改善:合法旧备份恢复可用,原操作恢复和明确确认保留。该增量值得交付,边界连贯、可回退;所选数据本身的成本与完整恢复的剩余范围如上保留。有限 refactor 已应用到同一 reader:消除错误名字折叠、统一临时索引、补全祖先规则并删掉无消费者的版本分支。没有增加新的 authority 或泛化迁移框架。我的准确 head 结论是 APPROVE;后续合并还须单独执行 approval closeout 与 merge readiness。

English verdict: APPROVE — c0387f3. Both previous findings are resolved: legal old archive names remain valid and unused TarInfo retention is bounded. Whole-PR File/SQLite, CLI/HTTP, packaged UI and recovery evidence supports this scoped increment. Parent test reuse, the unchanged ratchet advisory and broader recovery/platform limits are explicitly retained above. This reviewer participated in the repairs; no independent consensus is claimed.

@huangruiteng
huangruiteng merged commit dda6f7e into main Oct 16, 2026
16 of 28 checks passed
@huangruiteng
huangruiteng deleted the codex/size-unbounded-local-migration-20261016 branch October 16, 2026 08:50
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