| 1 | # 文件读取、证据检查与进度展示改造(单 PR 方案) |
| 2 | |
| 3 | 状态:实施中。分支 `feature/read-evidence-pr1`,基线 `main-v2 / daf18272f`。 |
| 4 | 一个 PR 承载全部改造;内部按可审查提交推进,不再拆分为多个 PR。 |
| 5 | |
| 6 | ## 1. 目标与默认行为 |
| 7 | |
| 8 | 普通用户无需配置开关、选择技术模式或学习新操作,即可获得以下默认行为: |
| 9 | |
| 10 | 1. 普通局部查阅不产生全文读取债务,不要求模型提交机械回执。 |
| 11 | 2. 明确全文任务可正确分页;无法完成时说明缺口,不虚报成功。 |
| 12 | 3. 文件读取受阻只影响依赖它的操作,独立工作仍可继续。 |
| 13 | 4. 修改前检查实际目标范围及内容是否仍有效。 |
| 14 | 5. 重复读取有界退出,正常分页只更新一条进度状态。 |
| 15 | |
| 16 | 不包含:增大默认上下文、放宽修改权限、移除截断标识、重做会话日志、通过读取完成标记证明模型已正确理解代码。 |
| 17 | |
| 18 | ## 2. 基础契约 |
| 19 | |
| 20 | 三个概念必须分开,不能互相替代: |
| 21 | |
| 22 | - **来源身份**:工作区、规范路径、实际数据来源(磁盘文件或编辑器 overlay),以及实际生成输出的原始字节或缓冲区的 SHA-256。不能在执行完成后重新探测来源、再给旧输出补发身份。 |
| 23 | - **读取快照身份**:同一次逻辑读取所使用的稳定内容版本,跨页保持不变,源内容变化时改变。局部读取不为此强制扫描全文。 |
| 24 | - **窗口摘要**:该页实际交付内容的摘要,只证明这一页,不能替代整文件版本。 |
| 25 | |
| 26 | 协议为 v2;v1 字段仅用于历史诊断,不授予修改许可。 |
| 27 | |
| 28 | - 第一次读取创建逻辑 `read_id`;后续分页沿用它,每次工具调用仍保留独立调用 ID 与 `result_ref`。 |
| 29 | - 游标由主机签发,绑定会话、运行代次、读取任务、规范路径、来源快照、请求范围和准确下一位置;校验发生在执行入口,能解码不等于被接受。 |
| 30 | - 拒绝跨会话、跨文件、过期、伪造及不匹配位置的引用;恢复运行时旧游标失效。 |
| 31 | - 模型可以使用续读引用,无需理解其字段。 |
| 32 | - `EOF` 必须携带可信的源结束位置;不能仅因某页出现 EOF 就判定整个 range 完成;空文件必须有明确的空文件事实。 |
| 33 | - 依据最终模型可见输出裁剪证据:被截断的半行、未交付原文、摘要和搜索结果不计作完整原文覆盖。 |
| 34 | - 同一 provider batch 的新读取不能为该批次的修改提供证据。 |
| 35 | - 协调器返回深拷贝快照;取消、版本变化及新一代任务使用 generation fencing。 |
| 36 | - 上下文压缩后的历史证据保留审计价值,修改所需的当前可见片段仍须重新获取。 |
| 37 | |
| 38 | ## 3. 读取语义与统一协调器 |
| 39 | |
| 40 | | 调用 | 默认行为 | |
| 41 | | --- | --- | |
| 42 | | 无范围、无 intent | `inspect`:有界预览,文件有剩余内容不产生全文债务 | |
| 43 | | 有 offset 或 limit、无 intent | `range`:只要求指定范围,按可信源结束位置截止 | |
| 44 | | `intent=full` | 建立不可由模型自行降级的全文义务,用续读游标推进 | |
| 45 | | 冲突参数 | 返回明确参数错误和合法调用方式 | |
| 46 | |
| 47 | 全文义务来源包括明确用户要求、适用项目规则和已确认任务要求;任务契约保留其来源,不能因后续工具改用 `inspect` 而解除。自然语言任务理解仍属模型能力,不宣称主机能形式化识别所有全文审查意图。 |
| 48 | |
| 49 | 协调器接入真正的默认执行路径,统一负责意图、预算、进展、完成与暂停;旧状态机不再同时 enforce。 |
| 50 | |
| 51 | ## 4. 按操作检查证据 |
| 52 | |
| 53 | 主机内部 `CheckOperationEvidence`:真实 writer 声明目标和所需证据,复用其路径、编码、overlay、唯一匹配及区间解析,不信任模型自报的写范围。 |
| 54 | |
| 55 | | 操作 | 处理规则 | |
| 56 | | --- | --- | |
| 57 | | 搜索、列目录及可信只读工具 | 保持可执行,不因其他文件读取未完成而阻塞 | |
| 58 | | 创建新文件 | 不要求读取不存在的内容;执行时仍检查目标不存在条件及授权 | |
| 59 | | 精确编辑 | 要求目标旧文本及必要上下文已交付,执行前重新验证 | |
| 60 | | 范围或符号删除 | 要求完整目标、两端锚点及中间内容;替换旧的“必须无参数重读全文”fallback | |
| 61 | | 覆盖已有文件 | 默认要求完整当前内容证据;只有明确授权完全重建时例外 | |
| 62 | | notebook、移动等其他内置写工具 | 按真实影响声明证据,不静默视为安全 | |
| 63 | | shell/MCP 等未知写范围 | 保持原有授权边界;存在可能相关的未满足修改证据时保守阻止 | |
| 64 | | 最终回答 | 允许报告局部结果或明确阻塞;未满足的明确全文任务不得标记为整体完成 | |
| 65 | |
| 66 | `multi_edit` 仍是单文件原子编辑,本 PR 不改造为多文件事务工具。多目标操作与已声明写范围的工具批次执行统一证据预检;实际写入继续遵守原有原子性语义。 |
| 67 | |
| 68 | ## 5. 有界续读与成本控制 |
| 69 | |
| 70 | `needs_scope` 接入真实上下文预算:复用现有有效上下文、压缩阈值及任务预算,不扩大默认上下文,不自动压缩以强行塞入全文。 |
| 71 | |
| 72 | - 初始内部上限:每个逻辑读取最多自动推进 64 页、累计主动读取执行时间 120 秒;同时受动态 token 预算与任务总预算约束,最先达到者生效。等待模型响应不计入主动读取执行时间。 |
| 73 | - 初始 inspect 页沿用现有行数与输出上限;正常局部读取不自动扫完文件。 |
| 74 | - 连续两次无新增有效内容:切换一次有依据的策略;切换后再连续两次无进展则暂停该依赖链。 |
| 75 | - 重复页、重复搜索、换调用 ID 不算进展;文件变化不重置硬预算。 |
| 76 | - 显式全文任务预算不足时保留未完成范围,继续独立事项,不自行降级。 |
| 77 | - 旧策略回执保留为兼容适配器,依据主机证据验证;新流程不要求调用。 |
| 78 | - 由现有通用进度保护统一决定暂停,避免一次行为被多个计数器重复处罚。 |
| 79 | |
| 80 | 这些上限是内部默认值,不增加用户设置项;调整必须依据资源数据并记录。 |
| 81 | |
| 82 | ## 6. 状态展示 |
| 83 | |
| 84 | 带版本的读取状态事件,以会话、回合、`read_id`、generation 和 sequence 定位: |
| 85 | |
| 86 | - 前端按读取任务 upsert 一条状态;正常分页使用中性进度,不逐页追加警告。 |
| 87 | - 局部页完成只在工具卡展示返回范围及是否还有内容。 |
| 88 | - 全文任务显示已覆盖范围;总量未知时不显示虚假百分比。 |
| 89 | - 暂停时显示原因、缺失范围及可执行恢复动作,默认折叠诊断细节。 |
| 90 | - 活动状态由稳定宿主管理,避免分页更新改变历史消息几何。 |
| 91 | - 覆盖取消、并行读取、会话切换、断线重连、乱序事件和历史恢复。 |
| 92 | - CLI、serve、ACP 使用同一语义;旧客户端只收到有界起止及阻塞 Notice。 |
| 93 | - 完成 en、zh、zh-TW 文案与无障碍通知节流。 |
| 94 | |
| 95 | ## 7. 验收 |
| 96 | |
| 97 | 协议与证据:多页共用稳定读取身份并正确累计覆盖;CRLF/LF、BOM、UTF-16、同大小替换、overlay 修改、符号链接变化均不能错误复用证据;半行截断、搜索摘要、旧游标、跨会话引用及单独 EOF 不能伪造完成;同批 read/edit、取消后结果、压缩后修改及恢复后的旧元数据按契约处理。 |
| 98 | |
| 99 | 实际任务:大文件局部查询不强制全文、不要求回执、不阻塞独立操作;全文审查多页交付后完成、不足时准确暂停;A 文件受阻不阻塞 B 文件且整体任务不虚假完成;覆盖、范围删除、精确编辑和多目标预检分别有通过、缺失、过期及并发变化用例;重复页与重复 grep 按“两次换策略、再两次暂停”结束,微小进展不能绕过总预算。 |
| 100 | |
| 101 | 界面与质量:连续 100 次进度更新只更新一条活动状态、无重复警告与列表跳动;Linux/macOS/Windows 路径与编码行为,以及 Windows WebView2、macOS WKWebView 真实界面;固定任务集比较成功率、介入次数、工具轮数、输入 token、耗时与压缩次数;host-only 元数据在 Chat、Responses、Anthropic 实际序列化中均被剥离;schema 保持确定顺序,动态状态不进入稳定前缀;最终运行 race、lint/vet、根模块与 desktop 测试、前端完整测试计划、生产构建与资源预算。 |
| 102 | |
| 103 | 禁止交付:错误放行、虚假全文完成、无限续读、跨任务证据串用。 |
| 104 | |
| 105 | ## 8. 实施顺序与状态 |
| 106 | |
| 107 | | 步骤 | 内容 | 状态 | |
| 108 | | --- | --- | --- | |
| 109 | | 1 | 来源身份、稳定读取 ID、游标及覆盖判定 | 已完成(19d91b570) | |
| 110 | | 2 | 真实 writer 证据要求与统一预检 | 已完成:writer 声明、统一预检、批次级预检先于执行、未知写范围保守阻止、用户显式重建授权 | |
| 111 | | 3 | 预算、有界续读及旧回执兼容 | 已完成:预算与阶梯、旧策略回执作为兼容适配器保留(仍按主机证据校验,5 个测试覆盖),新流程不要求调用 | |
| 112 | | 4 | 结构化事件、单卡 UI、跨端与本地化 | 已完成:事件、桌面状态行、CLI 状态行与 en/zh/zh-TW 文案 | |
| 113 | | 5 | 默认启用新协调器,移除冲突的旧执行路径 | 已完成:新行为即默认,Options.ReadPipeline 只保留主机内部回退开关 | |
| 114 | | 6 | 方案文档、工具说明、兼容说明与验收记录 | 已完成:工具说明、方案、验收矩阵与固定任务集对比 | |
| 115 | |
| 116 | 默认行为已切换:无范围、无 intent 的读取是有界预览,不再产生全文债务;只有 `intent=full` 会分页到结尾。跨平台界面验收(Windows WebView2、macOS WKWebView)与固定任务集对比需要在具备真实桌面的环境执行,不能由本仓库的单元测试替代。 |
| 117 | |
| 118 | 默认使用新行为;仅保留主机内部、按 turn 固定的回退入口,用于诊断和紧急回退,不暴露普通用户开关。旧活跃状态不能在半个工具批次中转换;恢复时重新验证,回退不撤销用户文件中已完成的修改。 |
| 119 | |
| 120 | ## 9. 验收记录 |
| 121 | |
| 122 | 以下每项都由仓库内的确定性测试覆盖;命令为 `go test ./...`、`pnpm test`(桌面前端)与 `make lint`。 |
| 123 | |
| 124 | 协议与证据: |
| 125 | |
| 126 | | 验收点 | 覆盖测试 | |
| 127 | | --- | --- | |
| 128 | | 多页共用稳定读取身份并累计覆盖 | `TestRangeObligationPagesUntilCovered`、`TestOutOfOrderPagesStillSatisfyAWholeFileRead`、`TestReadContinuationCursorJoinsTheLogicalRead` | |
| 129 | | 不同窗口摘要不被误判为文件变化 | `TestReadEnvelopeNamesTheServingStore`、`TestReadEnvelopeSeparatesSourceIdentityFromWindowDigest` | |
| 130 | | CRLF/UTF-16/编码差异不错误复用证据 | `TestReadEnvelopeKeepsUnicodeWindowsIntact`、`TestReadEnvelopeSeparatesSourceIdentityFromWindowDigest` | |
| 131 | | 半行截断不计作已读 | `TestClipToNarrowsDeliveredRangeToVisibleBytes`、`TestReadShadowRecordsTheCoordinatorVerdict` | |
| 132 | | 单独 EOF 不能伪造完成 | `TestRangeCompletionNeedsATrustworthySourceEnd`、`TestWholeFileRequiresContiguousCoverageFromLineZero` | |
| 133 | | 旧游标、跨会话、跨文件、过期引用被拒绝 | `TestReadContinuationCursorRejections`、`TestReadCursorRoundTripAndMatching` | |
| 134 | | 同批 read 不能为同批修改作证 | `TestEvidenceGateIgnoresSameBatchReads` | |
| 135 | | 跨快照分页不拼接 | `TestEvidenceGateNeverStitchesAcrossSnapshots`、`TestEvidenceGateStitchesPagesOfOneSnapshot` | |
| 136 | | host-only 元数据被剥离 | `TestReadResultEnvelopeDoesNotAffectProviderVisibleBytes`、`TestModelInputMessagesStripsReadResult` | |
| 137 | | 协调器状态不可被外部修改 | `TestReturnedObligationsAreDeepCopies` | |
| 138 | |
| 139 | 实际任务: |
| 140 | |
| 141 | | 验收点 | 覆盖测试 | |
| 142 | | --- | --- | |
| 143 | | 大文件局部查询不强制全文 | `TestImplicitReadIsABoundedPreview` | |
| 144 | | 明确全文任务分页到 EOF | `TestExplicitFullReadContinuesSourcePagesToEOF` | |
| 145 | | 覆盖/删除/精确编辑的证据规则 | `TestEvidenceGateBlocksAnUnreadOverwrite`、`TestEvidenceGateAllowsAfterTheModelSawTheContent`、`TestEvidenceGateRejectsStaleContent`、`TestWriteFileDeclaresWholeFileEvidenceOnlyForOverwrites` | |
| 146 | | 未知写范围不绕过证据阻塞 | `TestEvidenceGateBlocksUnknownScopeWriterAfterABlock`、`TestEvidenceGateLeavesUndeclaredWritersAlone` | |
| 147 | | 批次级预检先于执行 | `TestEvidencePreflightBlocksBeforeTheBatchRuns` | |
| 148 | | 用户显式重建授权(模型不能自授) | `TestEvidenceGateHonorsAnExplicitRebuildInstruction`、`TestParseConstraintsRecognizesAnExplicitRebuild` | |
| 149 | | 重复页与无进展有界退出 | `TestRepeatedPageIsNotProgress`、`TestStalledPagesPivotOnceThenPause` | |
| 150 | | 预算耗尽与内容变化不重置 | `TestPageBudgetStopsContinuation`、`TestActiveTimeBudgetStopsContinuation`、`TestContentChangeDoesNotResetTheBudget` | |
| 151 | | 未知上下文窗口不猜测 | `TestReadShadowNarrowsAnUnboundedFullRead` | |
| 152 | | 固定任务集的新旧策略对比 | `TestFixedTaskSetComparesReadPolicies`(同一脚本任务在新默认与旧回退开关下运行,记录轮数、读取次数、主机续读指令数与放行写入数) | |
| 153 | |
| 154 | 界面: |
| 155 | |
| 156 | | 验收点 | 覆盖测试 | |
| 157 | | --- | --- | |
| 158 | | 连续 100 次更新仍只有一条活动状态 | `read-status-upsert.test.ts` | |
| 159 | | 乱序事件不回退 | `read-status-upsert.test.ts` | |
| 160 | | 新回合清空上一回合状态 | `read-status-upsert.test.ts` | |
| 161 | |
| 162 | 跨平台:`go test` 在 CI 的 ubuntu-latest、macos-latest、windows-latest 三平台矩阵上运行,因此路径、换行与文件替换的确定性用例由 CI 覆盖;本机只验证了 macOS。 |
| 163 | |
| 164 | 仍需真实环境执行:Windows 原生 WebView2 与 macOS WKWebView 的界面与滚动验收(需要真实桌面与交互);真实 provider 下的任务集成功率与 token 对比(本仓库提供确定性 harness 覆盖轮数、读取次数、主机续读指令与放行写入数)。ACP 消费同一结构化事件,终端侧由 CLI 渲染。 |
| 165 | |
| 166 | ## 复核修复与验收补充 |
| 167 | |
| 168 | 以上提交映射是历史记录,不代表仅凭协调器单测即可完成验收。执行链路补测以 `read_pipeline_regression_test.go` 为准:新协调器实际控制续读、暂停和最终回答;旧状态机仅在内部回退模式运行。普通 inspect 继续正常参与上下文压缩,动态预算裁剪仅约束全文读取与自动续读,并在批次有序提交时重新计算并行读取的共同剩余预算。 |
| 169 | |
| 170 | - 身份从同一份不可变内容生成。inspect/range 只对不超过 256 KiB 的磁盘文件捕获全文身份;更大的局部读取仍为有界流式读取。显式 full 及其主机续读的磁盘快照上限为 64 MiB,避免无界内存;超出时仍可局部读取,但不能拼接成未经证明的全文覆盖,未完成的 full 转为 needs_scope。这是内部资源边界,无新增用户配置。 |
| 171 | - 游标必须完整匹配主机最近签发的会话、代次、路径、快照、终点和位置;路径由真实 reader 解析,执行使用当前来源生成快照并核对。不能删除字段或重写位置来通过校验。 |
| 172 | - `edit_file`、`multi_edit`、`delete_symbol`、`notebook_edit` 从真实 Preview 的最终修改推导原始所需范围;`write_file` 覆盖要求当前原始版本的全文证据;`delete_range` 继续由锚点审计拥有。`move_file` 不替换源内容且拒绝覆盖已有目标,不要求全文阅读。 |
| 173 | - 同批读取不提供同批写入证据。依赖前一步新文本的一组修改使用 `multi_edit`,以最初版本统一预检;历史分立调用链的兼容测试显式启用回退模式。写入执行再次检查预检来源,防止预检之后新增的用户内容被覆盖。 |
| 174 | - 未知范围写工具只受真实未解除义务约束;补齐前一轮证据后重新判断并解除历史阻塞。只读和不修改工作区的记账操作保持可用。 |
| 175 | - 重建豁免绑定同一肯定重建语句中的完整目标路径,拒绝否定语句、同名不同目录及文件名子串匹配。 |
| 176 | - 桌面和 CLI 保留不连续区间,使用一基行号并提供暂停后的操作说明;generation/sequence 拒绝过期更新,任务结束清除活动状态。 |
| 177 | |
| 178 | 契约兼容:现有 `read_file` 的 intent/cursor 已属于本 PR 的模型可见 schema;更新 golden 固定该有意变化,升级首次请求可能重新建立工具前缀缓存。内部读取信封、原始来源摘要和写入预检数据不进入模型请求。不改变会话存储格式;旧游标在新运行中失效后须重新读取。真实 provider 成功率、token 费用及原生 WebView 验收仍须单独记录,不能由单元测试代替。 |
| 179 | |
| 180 | ## 真实测试后的收尾修复 |
| 181 | |
| 182 | 历史完成事实、当前请求可见原文、写入操作依据分别由协调器、请求可见索引和 writer 预检拥有。读取结果在有序 finalizer 内按 workspace、规范路径、来源类型、原始身份和快照关联任务;同版本复读保持已满足状态,扩大范围保留覆盖与预算,只补缺失区间。新回合使用新的注册表和游标绑定。 |
| 183 | |
| 184 | 结构化 reader 不再使用通用字符串去重。仅当本轮实际请求仍包含字节一致的原始结果、且区间已经覆盖时才能返回引用;引用不计入新增交付,不允许引用链或同批结果充当模型已见内容。原文被投影移除或被扩展改写后重新交付必要窗口,不撤销过去的完成事实。扩展将正文替换成不可解析文字时也必须清除来源身份。 |
| 185 | |
| 186 | 本轮新增可选 `read_pause` LocalOnly 记录及 `incomplete_read` 回合 outcome,补充上一节持久化边界:无存储迁移,旧会话缺失字段保持原行为,旧客户端通过既有 LocalOnly 工具标识忽略记录。摘要最多包含 32 个文件、每个区间字段 64 段,不保存正文或可执行游标;实时展示与历史回放按同一记录 ID 去重。状态是未完成暂停,不是成功或网络重试;用户通过现有输入框补充要求继续。该记录不恢复旧游标,也不授权写入。 |
| 187 | |
| 188 | 保留原始 64 组真实测试,增加 32 组定向场景及 12 组精确写入测试。HTTP 层限制全部上游请求(含重试)为 600 次,每次预留 128,000 token,未知用量保留预留额,总计最多 300 万 token 或 4 小时。代理不存储凭据和请求正文。模型执行任务失败与宿主不变量失败分别统计。 |
| 189 | |
| 190 | 本轮不改变工具 schema、系统前缀和默认配置;压缩后必要的原文重新交付可能增加单个请求输入量,须以实测报告说明,不宣称普遍降本。确定性测试、真实模型测试、原生 WebView 验证、远端 CI 和发版状态分别记录。 |
| 191 |