diff --git a/CHANGELOG.md b/CHANGELOG.md index a607d5e..8f71e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 0.17.0 (unreleased) +- Reorganize the four managed Skill documents by task. Keep the entry short, place executable UI recipe examples inside the installed Skill, and remove duplicated instructions and historical iteration notes. + +- Add the readonly sibling-range client recipe for flat lists whose headings and items are siblings. Read one explicit container, validate unique ordered boundaries and readable row names, and retain native identities and property gaps. Results describe only the observed logical range; missing/ambiguous boundaries and incomplete reads stop without inventing an empty or complete business group. No scrolling or item actions are performed. +- Add optional scopePath to client readonly inspect/review and checkbox-audit. Summary and detail reads share one explicit ancestor path, resolve it separately, and retain scopeResult in step evidence. Off still skips details; invalid paths, scope failures and unsupported or unconfirmed scope results stop without whole-window fallback. Unscoped calls retain their behavior and call count. +- Let checkbox-audit complete when only className, bounds or isOffscreen properties are unavailable, while preserving the gaps in step evidence. Required identity/state and coverage checks still apply. The client-only reader.inspect option allowAuxiliaryPropertyGaps is opt-in; ordinary reads remain strict by default and review behavior is unchanged. - Add opt-in client workflows through the persistent JSON Skill session: bounded readonly-ui steps, checkbox-audit over an explicit set of controls, and expand-ui for one explicit or selected Group. Preserve live observations, conditional reads, target identity, cancellation and owned-process cleanup; unknown or incomplete evidence cannot produce successful findings. No model API or background workflow runs unless explicitly invoked. - Add wincode_ui_set_expanded and optional scopePath for inspect/review/setExpanded. Paths resolve each observed ancestor uniquely, and failures never fall back to whole-window operations. Scoped requests require inspectionVersion 5; older Hosts are rejected, and scoped actions check capability before dispatch. - Carry scopePath through expand-ui discovery, selection, expansion, readback and nextRequest. Explicit parent requests inspect the child inside that parent from the first read; final child readback resolves the parent again. Duplicate names or IDs in another region cannot supply the result. Each request expands at most once; an already expanded parent needs no extra action. Keep missing, ambiguous, incomplete and unknown outcomes visible without automatic replay or recursive navigation. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 034cb72..c736571 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,13 +37,13 @@ Keep changes bounded to the problem. Tool registration, schema and validation be For a version change, align `package.json`, the root entries in `package-lock.json`, `src/Core/Config.ts`, the Host project version, README and Skill. Do not edit dependency metadata to change the product's Node requirement. Dependency changes need an explicit maintenance scope; update npm/NuGet locks there and validate locked restores. `.gitattributes` and `.editorconfig` define formatting for touched files; do not reformat the repository in a functional PR. -Before merging a topic branch, record the problem, changes, failures and actual verification in the existing work log. Require successful Node 22/24 and CodeQL results for the exact PR head. Keep independent review and author self-review distinct, and identify unverified integrations. Changes to repository protection require an owner decision; the verified policy is recorded below. A successful CodeQL run also does not prove existing alerts are closed. +Before merging a topic branch, record the problem, changes, failures and actual verification in the PR description. Require successful Node 22/24 and CodeQL results for the exact PR head. Keep independent review and author self-review distinct, and identify unverified integrations. Changes to repository protection require an owner decision; the verified policy is recorded below. A successful CodeQL run also does not prove existing alerts are closed. On 2026-09-08, the owner authorized applying main protection. The read-back confirmed required pull requests, strict Node 22/24 regression and three CodeQL analysis checks bound to GitHub Actions, enforcement for administrators, resolved conversations, and disabled force pushes/deletions. The single-maintainer policy requires zero GitHub approvals; this does not constitute an independent review. Re-read GitHub settings when verifying current enforcement. SDK policy follows [Microsoft global.json guidance](https://learn.microsoft.com/en-us/dotnet/core/tools/global-json); dependency locking uses [NuGet locked restore](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#locking-dependencies). -Current implementation is described in the [architecture guide](WinCode-架构与数据流说明.md). Keep pending tests and unresolved issues in the [remaining test plan](WinCode-下一轮工程化迭代计划书.md); completed work belongs in CHANGELOG and the append-only work log. For documentation-only changes, verify local links, commands, version claims and evidence boundaries; do not claim a new runtime regression without running it. The four managed Skill files are delivery inputs, so regenerate/verify the manifest after updating them; this does not synchronize an installed client Skill or reconnect its MCP process. +Current implementation is described in the [architecture guide](WinCode-架构与数据流说明.md). Keep unfinished tasks and unresolved issues only in [the iteration plan](docs/next-iteration.md), and current client contracts in [the workflow guide](docs/hybrid-readonly-orchestration.md). Remove completed tasks from the plan; record user-visible changes in CHANGELOG and verification in the PR. Do not add chronological work logs or duplicate roadmaps. Read historical evidence from Git or the relevant PR only when needed. For documentation-only changes, verify local links, commands, version claims and evidence boundaries; do not claim a new runtime regression without running it. The four managed Skill files are delivery inputs, so regenerate/verify the manifest after updating them; this does not synchronize an installed client Skill or reconnect its MCP process. Node 22 CI runs `npm run test:error-contracts` and uploads its bounded report. It exercises protocol errors, matching tool-error text/structured payloads, real generated-file trash failures, structured WORKSPACE_MISMATCH and same-root workspace recovery; injected UI images test serialization only. Run it locally after changes to these boundaries. diff --git a/README.md b/README.md index dedda0a..41d9210 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ WinCode is a local server implementing the Model Context Protocol (MCP) for AI c - **Project and code analysis:** Explore declared solution and project references, search text and symbols, read selected code, and assess change impact. Built-in text analysis works by default; optional Roslyn integration provides compiler-backed C# symbol and reference analysis. - **Semantic UI actions:** `wincode_ui_click` and `wincode_ui_type` act on exactly one control that a single bounded search proves unique. They use UI Automation patterns and keyboard input only—never coordinate mouse simulation—and do not explicitly activate, restore or raise the target window. Control events and navigation may still bring the application forward. Disabled controls, ambiguous or incomplete matches, read-only values and unconfirmed keyboard focus are refused rather than guessed. -A recorded test with a 222-node window reduced response text from approximately **62 KB to 1.6 KB** by querying a specific control instead of returning the full tree. See the [test record](docs/codex_worklog.md). +Use a query and an observed parent scope to retrieve the relevant controls, and request compact output when full geometry is unnecessary. Check completeness and ambiguity before drawing conclusions. ### Example: Investigate a disabled Save button @@ -185,7 +185,7 @@ npm run benchmark:agent -- 1 # Run one iteration of the optional scripted bench - [Contributing](CONTRIBUTING.md): builds, test suites and delivery requirements. - [Architecture and data flow](WinCode-架构与数据流说明.md): components, interfaces, resource limits and lifecycle management. - [Skill and MCP setup](WinCode-Skill制作与MCP配置指南.md): installation and client configuration. -- [Work log](docs/codex_worklog.md) and [remaining test plan](WinCode-下一轮工程化迭代计划书.md): historical verification, known issues and pending validation. +- [Iteration plan](docs/next-iteration.md): unfinished improvements, known limitations and acceptance criteria. --- @@ -202,7 +202,7 @@ WinCode 是面向 AI 编程智能体的本地模型上下文协议(Model Conte - **实际使用中更快、更准确:**在日常 Windows/.NET 开发中,使用 WinCode 检查 UI 和定位源码,比基于截图的 Computer Use 工作流更快、更准确。通过直接获取结构化的控件属性和源码位置,可以减少对图像识别的依赖和反复交互;配合定向查询与精简响应,还能减少模型需要处理的数据量。 - **项目与代码分析:**查看解决方案和项目中声明的引用关系,搜索文本与符号,按需读取代码,并评估变更影响。默认提供内置文本分析,可选的 Roslyn 集成支持基于编译器语义的 C# 符号与引用分析。 -在包含 222 个节点的窗口测试中,仅查询指定控件即可将返回文本量从约 **62 KB 减少至 1.6 KB**。详见 [测试记录](docs/codex_worklog.md)。 +通过 query 和实际观察到的父范围只读取相关控件,不需要完整几何信息时使用 compact 输出。得出结论前仍需核对完整性与歧义。 ### 使用示例:排查“保存”按钮被禁用的问题 @@ -357,7 +357,7 @@ npm run benchmark:agent -- 1 # 执行一轮可选的脚本化基准测试 - [贡献指南](CONTRIBUTING.md):构建、测试套件和交付要求。 - [架构与数据流](WinCode-架构与数据流说明.md):组件、接口、资源限制和生命周期管理。 - [Skill 与 MCP 配置指南](WinCode-Skill制作与MCP配置指南.md):安装与客户端配置。 -- [工作记录](docs/codex_worklog.md) 与 [后续测试计划](WinCode-下一轮工程化迭代计划书.md):历史验证结果、已知问题和待验证事项。 +- [迭代计划](docs/next-iteration.md):未完成的改进、已知限制与验收条件。 --- diff --git "a/WinCode-Skill\345\210\266\344\275\234\344\270\216MCP\351\205\215\347\275\256\346\214\207\345\215\227.md" "b/WinCode-Skill\345\210\266\344\275\234\344\270\216MCP\351\205\215\347\275\256\346\214\207\345\215\227.md" index 2b04da3..caf0e60 100644 --- "a/WinCode-Skill\345\210\266\344\275\234\344\270\216MCP\351\205\215\347\275\256\346\214\207\345\215\227.md" +++ "b/WinCode-Skill\345\210\266\344\275\234\344\270\216MCP\351\205\215\347\275\256\346\214\207\345\215\227.md" @@ -8,7 +8,7 @@ - **磁盘交付物**包含 Gateway、原生 UIA/Code Host 及可选 Tray 的完整发布目录、构建身份和交付清单。 - **MCP 连接实例**是客户端已经启动的进程;更新源码、构建或复制 Skill 都不会自动更新这个进程。 -架构与数据流见 [架构说明](WinCode-架构与数据流说明.md)。待办见 [当前计划](WinCode-下一轮工程化迭代计划书.md),不要按历史计划重复安装和升级。 +架构与数据流见 [架构说明](WinCode-架构与数据流说明.md)。未完成任务只维护在 [迭代计划](docs/next-iteration.md),不要按历史计划重复安装和升级。 ## 2. 构建与交付核对 diff --git "a/WinCode-\344\270\213\344\270\200\350\275\256\345\267\245\347\250\213\345\214\226\350\277\255\344\273\243\350\256\241\345\210\222\344\271\246.md" "b/WinCode-\344\270\213\344\270\200\350\275\256\345\267\245\347\250\213\345\214\226\350\277\255\344\273\243\350\256\241\345\210\222\344\271\246.md" deleted file mode 100644 index 8731fee..0000000 --- "a/WinCode-\344\270\213\344\270\200\350\275\256\345\267\245\347\250\213\345\214\226\350\277\255\344\273\243\350\256\241\345\210\222\344\271\246.md" +++ /dev/null @@ -1,29 +0,0 @@ -# WinCode 后续测试计划 - -更新:2026-10-07(北京时间)。当前源码版本为 **0.17.0**,hybrid 客户端父范围接入与合并收口见 [专项计划](docs/hybrid-next-iteration-plan.md#main-合并验收线)。本文继续跟踪原有覆盖限制和条件性测试;历史数量不作为当前项目重新测量结果。版本与实际 CI/交付状态见 [README](README.md) 和 [工作日志](docs/codex_worklog.md)。 - -固定工作区、保持已加载的 Host、请求数量限制、独立的设计时输出目录、共享缓存回归,以及 PR #40/#41 的代码导航、UI 精简输出和测试修正已合并,Node 22/24 和 CodeQL 检查通过。后续本地验收已完成 TavernDesk 的 8 个上下文场景、6 个 UI 产品任务、两个独立 Gateway 的审计争用及恢复,以及实际 Codex 连接中的单项目 Roslyn 流程。对应 PR 的交付状态见工作日志;这些测试完成项从待办移除,不扩大为所有项目或所有 UI 环境均已验证。 - -## 待完成的测试与问题排查 - -| 事项 | 已知情况 | 下一步 | -| --- | --- | --- | -| 已安装 Codex 的冷启动确认 | Skill 按需入口已通过真实终端连续 Roslyn 查询、关闭和所属进程退出;Node 22.23.2 与 24.19.0 本地核心回归均为 462 通过、1 项可选夹具跳过。Node 24 已在 I:/New-tarven 完成后台 UI 和真实图片附件验证,Node 22 补验了该项目的实际终端声明/引用流程。原生 wincode 已设 enabled=false,已安装 Skill 四文件一致 | 正常刷新/重新启动 Codex,确认未调用时没有新建 WinCode 进程。当前任务未重启宿主;保存配置不会清除旧连接。Node 22 证据见 test-tmp/node22-validation/report.json | -| Tavern 的生成源码覆盖 | 单目标引用框架被入口覆盖的问题已在本地修复;实际 Host 不再报告 Microsoft.Data/Microsoft.ML 依赖缺失。当前 Codex 终端验证了 Tokenizer、SQLite 参数类型及具体引用,导航两处引用仍正确。完整 Host 诊断现为 11 项 CS8795,均涉及生成正则;24 个分析器/生成器引用仍排除 | 单独评估生成源码的受控支持、定位和快照失效,不直接启用全部生成器。queryComplete=false/incomplete 仍是保守契约,局部零引用不能证明删除安全。最新证据:test-tmp/roslyn-dependency-debug/tavern-report.json;原验收报告作为历史保留 | -| UI 和托盘的间歇性失败 | PR #41 将真实端点启动检查移到顺序阶段,main 两个 Node 版本均通过,3000 ms 生产超时未变;隐藏/唤出、截图/提示窗及原生启动延迟的根因仍未全部定位 | 再现时保存错误码、原生日志和步骤耗时,再针对原因修复;不把调度修正及重跑通过称为已解决所有 UI/托盘问题 | -| SDK 突发请求警告 | main 的 128 请求阶段仍记录 11 个 drain 监听器警告,阶段结束后为 0;此次实际客户端连续查询未出现调用失败,但未采集该客户端的监听器统计 | 若普通负载也出现问题,再检查发送背压及监听器释放。不提高监听器上限来隐藏警告,也不据此认定内存泄漏 | - -## 遇到相关需求时再测试 - -- **长期运行和大项目:**观察 Node、Code Host 及其子进程的内存、CPU、句柄、队列和启动次数,测量大附件哈希计算的开销及磁盘占用。现有短时测试还不能判断长期运行是否泄漏。 -- **特殊 MSBuild 项目:**使用实际项目测试自定义 target 动态生成的项目引用、复杂 source generator、外部输入,以及多个进程同时修改不同源码文件的情况。现有测试已覆盖两个 TFM、显式项目引用修改和两个读取实例对源码修改的响应。 -- **异常退出后的文件清理:**尚未测试写入中途强制退出、Gateway 与 Host 同时强制退出或断电后的遗留文件。当前缓存附件可能被后续清理,磁盘限额通过定期清理实现,跨文件读取也不是原子快照;是否调整这些行为,应由实际需求决定。 -- **其他 Windows 配置:**跨权限或登录会话、不同 DPI、Explorer 重启后的表现仍需测试。托盘的八实例容量和连接恢复已验证,不重复列为开发任务。 - -## 当前功能范围 - -继续使用独立 Gateway、每连接固定项目,默认使用 local-text,按需配置 Roslyn。Roslyn 不自动释放,用户可手动释放;本地开发和测试以 Windows 11 x64 为准。 - -自动空闲释放、pause/resume、自启动、全局停止、审计日志自动轮转、跨实例清理缓存、共享 Host 或数据库、通用租约状态机、跨平台移植均暂不开发。语义 click/type 已实现,0.17.0 增加显式展开和有限客户端导航;父路径暂不扩展到 click/type,自动递归导航按实际需求另定任务。 - -测试完成后,删除对应待办并将结果记入工作日志。GitHub Release 尚未发布,源码合并和 CI 通过不能替代发布或实际客户端测试。 diff --git "a/WinCode-\346\236\266\346\236\204\344\270\216\346\225\260\346\215\256\346\265\201\350\257\264\346\230\216.md" "b/WinCode-\346\236\266\346\236\204\344\270\216\346\225\260\346\215\256\346\265\201\350\257\264\346\230\216.md" index 4237f83..a482e44 100644 --- "a/WinCode-\346\236\266\346\236\204\344\270\216\346\225\260\346\215\256\346\265\201\350\257\264\346\230\216.md" +++ "b/WinCode-\346\236\266\346\236\204\344\270\216\346\225\260\346\215\256\346\265\201\350\257\264\346\230\216.md" @@ -1,8 +1,8 @@ # WinCode 架构与数据流 -**适用源码版本:0.17.0;更新:2026-10-07(北京时间)。本轮基于 main `038c0ccc` 的实验分支收口;0.17.0 最终本地核心、桌面与实际 CLI/隔离应用验收均通过,远端 CI/合并状态以 [PR #52](https://github.com/linnnn89/WinCode/pull/52) 为准,验证范围见 hybrid 计划及工作记录。尚未发布 GitHub Release。** +**适用源码版本:0.17.0;更新:2026-10-08(北京时间)。[PR #52](https://github.com/linnnn89/WinCode/pull/52) 已合并为 `4789771f`,PR 检查及合并后 CI 成功。尚未发布 GitHub Release。未完成任务见[迭代计划](docs/next-iteration.md)。** -本说明描述当前源码中已实现的结构。GitHub 分支保护的历史只读核查日期为 2026-09-08;本轮核对 PR 检查状态,不把它等同重新审计全部保护设置。历史实测结果见[工作记录](docs/codex_worklog.md)。源码版本、磁盘构建和客户端当前连接是三个不同对象,不能互相替代。 +本说明描述当前源码中已实现的结构。历史实测结果按需从对应 PR 或 Git 历史查询。源码版本、磁盘构建和客户端当前连接是三个不同对象,不能互相替代。 N4 实现:`DesignTimeBuild` 使用已有 SDK 的 ProjectCollection 做原项目求值,保留原中间目录的 Compile 排除规则及自定义导入;目标运行仍交给 MSBuildWorkspace,使用每 Host UUID 的私有 IntermediateOutputPath。Configuration/TargetFramework 必须为字面目录段,规范化后的输出必须位于所属 UUID 内;原求值的无效项目异常保持 `PROJECT_LOAD_FAILED`。输入扫描仅过滤已判定不参与默认编译的原中间产物,实际文档/显式输入仍校验,自定义 Compile 保守处理。`OwnedBuildOutputs` 记录原生所有权清单,正常关闭回收;`RoslynHostClient` 在实际退出后调用 `DesignTimeArtifacts` 回收所属命名空间。内部 inputPolicy 为 2,缺失/旧策略 Host 的拒绝和进程回收已有专项测试。当前发布目录通过交付身份核验;复杂 target、任意动态项目图及断电后的孤儿产物仍不在本地通过范围内。 @@ -45,7 +45,7 @@ flowchart TB Codex 可选择 Skill 按需入口:首次使用才创建交互执行会话,`SkillSessionCli` 使用现有 MCP SDK 连接上图中的 Gateway,后续查询复用同一连接。入口启动或读取本地状态不会创建 Gateway;首次工具请求完成工作区、构建和提供方核验后才派发业务。完整 MCP 结果和原始图片作为本地附件交付,入口不重写工具契约或自动重放请求。关闭/所属入口死亡沿用 Gateway 与原生 Host 的清理链路。该模式需禁用客户端自动启动的 WinCode 条目,不能通过 Gateway 内部延迟初始化实现客户端侧零进程;使用方法见 [Skill 会话手册](skills/wincode/references/diagnostics.md#skill-按需会话)。 -0.17.0 的客户端可显式调用 readonly-ui/checkbox-audit,或调用独立的 expand-ui 导航。后者接受实际祖先 scopePath,在所选父范围内定位、最多展开一次,并通过再次解析父路径读回子控件状态;不是服务器批处理、原子快照或自动递归引擎。标准 inspect/review/setExpanded 的路径需 UIA Host inspectionVersion 5,旧 Host 不可忽略范围继续操作。click/type 的定位契约保持原样;应用事件可能带来前台变化。具体字段与结果边界见 [UI 手册](skills/wincode/references/ui.md) 和 [合并验收计划](docs/hybrid-next-iteration-plan.md#main-合并验收线)。 +0.17.0 的客户端可显式调用 readonly-ui/checkbox-audit,或调用独立的 expand-ui 导航。后者接受实际祖先 scopePath,在所选父范围内定位、最多展开一次,并通过再次解析父路径读回子控件状态;不是服务器批处理、原子快照或自动递归引擎。标准 inspect/review/setExpanded 的路径需 UIA Host inspectionVersion 5,旧 Host 不可忽略范围继续操作。click/type 的定位契约保持原样;应用事件可能带来前台变化。具体字段与结果边界见 [UI 手册](skills/wincode/references/ui.md) 和 [客户端工作流](docs/hybrid-readonly-orchestration.md)。 | 层 / 模块 | 负责什么 | 设计边界与源码入口 | |---|---|---| @@ -292,7 +292,7 @@ flowchart LR 本说明的架构图、数据表与关口表共同描述当前实现;新增功能应说明接入哪条数据流、使用哪个现有契约、在哪个关口拒绝或降级,以及如何留下真实验收证据。 -工作区失败恢复、trash 部分完成、有界负载和基础错误迁移已落实;当前待办见[计划](WinCode-下一轮工程化迭代计划书.md)。实际 Codex 连接已完成单项目 Roslyn 的搜索、引用、影响分析、重构建议及源码变化后的重新定位;新建 stdio 连接的自动化测试与此次客户端验收分别记录。条件性性能研究不表示已发现泄漏。 +当前待办见[迭代计划](docs/next-iteration.md)。单项目 Roslyn 的实际客户端证据不代表所有动态项目图均已验证;条件性性能研究也不表示已发现泄漏。 ## 2026-09-09 职责拆分 diff --git a/docs/Hermes_worklog.md b/docs/Hermes_worklog.md deleted file mode 100644 index bb68ca7..0000000 --- a/docs/Hermes_worklog.md +++ /dev/null @@ -1,64 +0,0 @@ -# Hermes 协作工作记录 - -记录 Hermes Agent 在本机(Windows / HERMES_HOME=O:\hermes)围绕本仓库的安装与配置变更。只记与本仓库相关的持久变更;工具语义与恢复手册以仓库 `skills/wincode/` 受管文件为准。 - -## 2026-09-15 — wincode 技能安装到 Hermes + 不固定工作区的 MCP 使用方式 - -**目标**:把本仓库的 wincode 技能安装到本机 Hermes;在 Hermes 建立 WinCode MCP 使用方式,不在配置中固定 workspace,每个项目在使用时由会话自行定位到项目文件夹。 - -**关键变更** - -1. 技能安装(受管四份文件):`npm run skill:sync -- O:/hermes/skills/wincode`。 - - 安装前 `skill:check` 退出码 2(四份均缺失);安装后退出码 0,哈希一致。 - - 首次安装无旧文件可备份,脚本按既有行为留下空的 `.wincode-backup-` 目录(不影响发现,未处理)。 -2. Hermes MCP 条目:`mcp_servers.wincode`(`command=node`,`args=[I:/WinCode/dist/index.js]`,`enabled=false`)。 - - 不传 `--workspace`;禁用条目不参与连接/工具注册(源码核对 `mcp_tool_discovery.py:_enabled`、`mcp_tool_registration.py:_server_enabled`),不会自动启动。 - - 原生 stdio 连接在启动时固定工作区且 `workspace_open` 不能改根,因此不配置固定工作区的常驻连接。 -3. Hermes 适配技能:`O:/hermes/skills/wincode-hermes/SKILL.md`——按需会话的 Hermes 执行方式(pty + process(submit))、每项目 `--workspace` 定位、按项目注册原生连接的方法。非受管文件;受管技能手册不手改。 - -**验证(真实执行,非推测)** - -- 会话 1 @ `I:\WinCode`:`SkillSessionCli --workspace I:/WinCode` → `ready` → `wincode_hello_world`:0.15.0、build verified、revision c229198、17 工具、`workspaceBinding source=argument`;`wincode_search_text` 命中 `skills/wincode/references/diagnostics.md:12`;`close` 返回 `closed:true` 且进程退出(exit 0)。 -- 会话 2 @ `I:\New-tarven`:同一入口换工作区 → `wincode_list_directory` 返回该项目根 22 个条目(`app/`、`Directory.Build.props`、`global.json` 等);`close` 同样干净退出。两会话 instanceId 不同、buildId 相同。 -- `skills_list` 即时可见 `wincode`、`wincode-hermes`(无需重启)。 - -**边界与未决** - -- 未验证:Hermes 客户端实际启用原生条目时的连接行为(当前禁用);Roslyn/UI 能力未涉及(默认 local-text)。Hermes 的 venv 已含 `mcp` 2.0.0 SDK(启用原生连接无额外安装前提)。 -- 如需为某项目启用常驻原生连接:`node I:/WinCode/dist/index.js --print-connection --workspace <项目绝对路径>`,并按不同条目名注册;改配置后需重启/刷新 Hermes。 -- 结果文件保留在 `I:/WinCode/test-tmp/skill-sessions/run-*/`,按本地附件管理,不提交。 - -## 2026-09-27 — 同步远端 main 并把受管技能分发到本机各 AI 目录 - -**目标**:把 `I:\WinCode` 同步到 `origin/main`,并把仓库受管技能(`skills/wincode/` 四份文件)更新到本机已安装 wincode 技能的各 AI 目录。 - -**关键变更** - -1. 仓库同步:`git pull --ff-only origin main`,`c229198` → `39e097d`(`#48` Roslyn 诊断/框架引用、`#49` UI 语义点击与输入),39 files changed;工作区仅剩既有的未跟踪 `docs/Hermes_worklog.md`。 -2. 受管技能同步(0.15.0 → 0.16.0,四份文件:`SKILL.md`、`references/{code,ui,diagnostics}.md`): - - `C:\Users\40218\.agents\skills\wincode`、`C:\Users\40218\.gemini\config\skills\wincode`:全量覆盖,改后 `skill:check` 均 `matched: true`;旧内容备份于 `%TEMP%\wincode-skill-backup-20260927-091727`。 - - `O:\hermes\skills\wincode`:走受管脚本 `node scripts/sync-skill.mjs "O:/hermes/skills/wincode" --apply`,四份均变更,备份目录 `O:\hermes\skills\.wincode-backup-354031b4-…`;复核 check 退出码 0。 - - 全盘扫描 `C:\Users\40218`(排除 AppData/缓存/工具链,深度 7)只发现上述两处含 wincode 技能;`wincode-hermes` 为非受管 Hermes 适配技能,未改。 -3. 验证:四处(仓库源 + 三处安装)逐文件 md5 一致;`skill_view('wincode')` 返回 0.16.0 内容。 - -**边界与未决** - -- `dist/` 仍是 2026-09-12 构建产物(对应 0.15.0),而仓库源码已是 0.16.0:手册新描述的 UI 语义动作(`click`、`mode:"setValue"/"type"`)需重新构建后才在运行实例生效。本次未构建(未获授权)。 - -## 2026-09-27(续)— 重新构建到 0.16.0 并验证 UI 语义动作生效 - -**目标**:让手册描述的 UI 语义动作(`click` / `type` / `setValue`)在运行实例真正可用——`dist/` 与已发布原生组件需重建到 0.16.0。 - -**执行与结果** - -1. `npm run build`:3.6 秒,dist 0.16.0,buildId `2729d75b146dca6b19…`,sourceHash `9f71d70ac6a5…`,artifactHash `c25b1ba0e2a2…`,revision `39e097daca5a…`。 -2. `npm run check`(core):18 阶段全部 exit 0,45.0 秒。报告 `test-tmp/check/2026-09-27T01-30-09-531Z-core/report.json`。 -3. `npm run check:desktop`:7 阶段全部 exit 0,194.9 秒;desktop-tests 40 用例、0 失败/错误。报告 `test-tmp/check/2026-09-27T01-31-55-310Z-desktop/report.json`。 - -**证据(真实执行)** - -- 端到端用例 "clicks, refuses ambiguous/disabled targets and writes text without echoing it" 通过(16.6 秒,真实 WPF 夹具),即语义点击与文本写入真机可用。 -- 已发布 Host 实跑只读 `health` 探针:`hostIdentity` version 0.16.0、informationalVersion `0.16.0+39e097daca5a…`、configuration Release、framework .NET 10.0.11、`inspectionVersion 3`(3 才含语义操作)。绕过 Gateway 单独启动被 `OwnerProcessGuard` 拒绝(`WINCODE_OWNER_PID` 校验,预期行为,非缺陷)。 -- 交付清单 `verify-delivery` matched(contentId `74b3075a5443…`);运行时 `toolCount 19`(0.15.0 为 17),dist 新增 `wincode_ui_click`、`wincode_ui_type`;`dist/build-manifest.json` 与 `dist/delivery-manifest.json` 均为本次构建产物。 - -**边界**:本次未提交、未推送远端,构建产物仅在本机。 diff --git a/docs/codex_worklog.md b/docs/codex_worklog.md deleted file mode 100644 index fbb4345..0000000 --- a/docs/codex_worklog.md +++ /dev/null @@ -1,1461 +0,0 @@ -# Codex 工作记录 - -## 2026-09-07(北京时间)— 后台取证模式与游戏前台实测 - -- 用户明确同意将后台模式加入本轮,并要求在游戏运行时实测。新增 inspect/review 可选 backgroundOnly=true,必须同时指定 PID/HWND;TS/Gateway/Host 均校验或透传。模式保持现有不激活/不还原行为,仅允许 PrintWindow,失败时禁止进入屏幕 BitBlt/CopyFromScreen 回退。正常捕获失败返回 imageOmitted 与原因并保留树;原生调用挂起导致整个 Helper 超时仍返回 TIMEOUT,不能宣称该路径也保留树。 -- 夹具:新增 --background-fixture,ShowActivated=false、WS_EX_NOACTIVATE、无任务栏按钮、NOACTIVATE 置底,不操作游戏。每 100ms 采样前台 PID/HWND/进程名;脚本只取夹具图片与控件树,不采集游戏画面。取证结果保存在 Git 忽略的 test-tmp/background-*,专用脚本不加入默认回归。 -- 实测一(20:27:50 起):test-tmp/background-1788784070411/report.json;5/5 次读取 32 个控件,5/5 返回 printWindowDwm 图片,UIA 根 AutomationId 与夹具一致。已目视首张图片,确实是后台 WPF 夹具,没有游戏画面。72 次前台采样中,60 次为 Client-Win64-Shipping,另有 ChatGPT/Explorer/空句柄;测试夹具从未成为采样前台。用户随后明确确认主动切换过,因此不把变化自动归因于测试;也不宣称这轮游戏始终前台。 -- 实测二(20:30:05 起):test-tmp/background-1788784205075/report.json;增加等待游戏连续 10 次采样为前台的前置条件。15 秒等待中始终为浏览器,未满足条件,断言拒绝继续,0 次取证;标为未执行而非成功。测试窗口已清理,不再反复等待或强制切回游戏。报告原始 foregroundUnchangedInSamples=true 只描述该次浏览器采样,不能解释为游戏验收成功;随后脚本修正为至少已有一轮取证才允许该字段为 true,保留原始失败报告。 -- 验证:C# Host/夹具 Release publish --no-restore、TypeScript build/typecheck 通过;后台策略与源码审查两个无前台窗口套件合计 12/12 通过,覆盖显式身份校验、MCP/Helper 参数传递与缺图保留树契约。图片失效降级为模拟协议测试,尚未在真实窗口强制 PrintWindow 失败。前一阶段窗口发现专项 3/3 通过;本轮未运行会激活窗口的全量 npm test。 -- 证据边界:后台 WPF 取证在游戏运行场景有效,不能外推所有应用/最小化/独占全屏渲染行为;100ms 采样无法排除更短暂的焦点变化。PrintWindow 非零返回也不保证内容正确,图片仍需检查。严格全程游戏前台验收尚未完成。没有改游戏、全局配置或个人数据;未提交/推送。 - -## 2026-09-07(北京时间)— v0.8.0 第一阶段:只读窗口发现 - -- 授权与范围:用户要求开始迭代,按草案先交付窗口发现;没有实现局部子树查询或控件模式状态。新增 wincode_ui_list_windows,复用 Host/Adapter/Router/MCP 边界,无新增依赖、常驻进程或窗口缓存。 -- 实现:PID、进程名精确匹配、标题字面量子串可组合筛选;默认 30、上限 100 个候选;输出 PID/HWND、标题、进程名可用性、窗口状态、采集时间和枚举完整性。Host 软预算 2 秒,Adapter 排队与执行总期限 3 秒,清理时间另计。达到上限时探测额外匹配再标记截断,不虚构总数。中文标题使用 Unicode Win32 接口;输出超过 256 字符标记 titleTruncated。 -- 生命周期:新增请求复用现有串行锁、超时取消与进程退出确认,配置禁用时不启动 Helper。只枚举可见顶层窗口,不激活、截图或遍历 UIA;与工作区源码的归属不作推断,发现后使用 PID/HWND 重新校验目标。 -- 验证:Host Release publish --no-restore、WPF 夹具 win-x64 Release publish --no-restore、TypeScript build/typecheck 通过。新专项 3/3 通过:参数/禁用/排队取消;真实 Helper 挂起后的取消与超时(OS PID 退出断言);真实 SDK stdio MCP 两进程四个同名中文窗口、组合筛选、数量上限、目标关闭后的失效处理、目标存活与 Helper 被动状态回收。 -- 失败与修正:首次夹具发布未指定 win-x64,验收启动的是旧路径产物;改用显式运行时发布。随后端到端断言发现 Adapter JSON 序列化未透传新增筛选与 maxWindows,已补齐。hello_world 成功时 isError 可省略,测试改为明确禁止 true,并增加实际 Helper 空闲状态断言;未放宽窗口数量/过滤/回收断言。 -- 用户新增约束:用户正在全屏游戏,后续暂停会打开窗口的测试。新专项已经执行完毕;本轮尚未运行全量 npm test,避免夹具窗口影响前台。Codex 原生 WinCode 工具本会话尚未接入,不将 SDK stdio 测试冒充原生接入验收。未操作游戏或修改客户端设置,未提交/推送。 -- 新发现及 USER_DECISION_REQUIRED:截图现有 PrintWindow 失败后回退 BitBlt/CopyFromScreen,目标被游戏遮挡时可能采集游戏。已建议并询问本轮是否加入后台模式:固定 PID/HWND、不激活/还原窗口、禁止屏幕回退,截图失败保留树;等待用户选择。PrintWindow 成功返回也不等于图像内容必然有效,不承诺所有后台窗口可截图。 - -## 2026-09-07(北京时间)— v0.7.2 可选界面关键词源码检索 - -- 授权与范围:用户确认开始迭代。复用 wincode_ui_review、显式 candidateFiles 和现有扫描预算,新增可选 textQueries;保留所有既有未提交修改,没有引入依赖或修改目标应用。 -- 实现:UiTextSearch 提供最多 5 个、每个 80 字符的显式关键词;按区分大小写的原始属性子串检索,最多返回 40 项文件/属性行号/片段/SHA256。评论、CDATA 和属性内伪声明不产生命中。资源引用与绑定表达式单独标记,保持 identityMatch=false;不展开资源、不解码实体、不扫描元素正文。 -- 生命周期与预算:复用同一次 UI 快照与同一次 XAML 读取;没有新进程、缓存、队列或扫描入口。MCP 在 128 KiB 文本预算内优先裁剪可选文本命中,再执行原有源码证据降级;保留 UI 树、图片和 ID 候选的原有边界。 -- 反证验证:运行时 ID=Save 的源码 Content=Cancel、另一按钮 Content=Save 时,文本结果独立返回,原 ID 候选完全不变。补充重复命中、注释/CDATA、动态资源、绑定、字面量特殊字符、行号、长片段裁剪、40 项上限和入参拒绝;MCP 测试验证可选参数贯通、实际 UTF-8 预算和图片分离。 -- 验证结果:typecheck/build 通过;源码审查专项 10/10 通过;完整 npm test 为 154 项,153 通过、0 失败、1 跳过,约 30.5 秒。git diff --check 通过,仅现有 CRLF 转换提示。原始测试输出:test-tmp/v072-regression.log。 -- 真实项目重放:使用 v0.7.1 保存的隔离语言弹窗快照及当前 FirstRunLanguageDialog.xaml,命中标题资源引用第 5 行与 TavernDesk 字面量第 38 行;简体中文无字面量命中,16 节点仍为 0 身份匹配。报告:test-tmp/v072/replay.json。此项为离线快照/当前源码重放,不是新增实时 GUI 验收;未读取个人数据库或重开应用。 -- 文档与版本:README/CHANGELOG、package/config 版本更新为 0.7.2。未提交、推送或发布;独立对话窗口(用户已选择跳过)、多 DPI、4K 和长期稳定性仍未验证。命中计数仅代表已扫描文件内容,应结合 fileScanComplete 与 truncated 解释。 - -## 2026-09-07(北京时间)— v0.7.1 真实隔离验收与可诊断性 - -- 授权:用户要求开始 v0.7.1 迭代并验收;保留既有未提交内容。再次核实 New-tarven 的 IsolatedTestStartup 和 Start-IsolatedTest.ps1:全新 test-root 隔离 data/config/logs,不附着已有实例;Release --no-restore 构建 0 警告/错误后,创建 PID 34008,receipt 校验通过。 -- 隔离根:I:/New-tarven/work/isolated-test-20260907-193735-88d913aebeba492aabec1d729a64cf4a。未复制个人数据、配置 Provider 或发送模型请求;没有修改 TavernDesk 源码。首次语言弹窗状态 initialized,用户选择语言后变为 window-shown。 -- 桌面操作工具将 TavernDesk 窗口错误归属 AiPPT,重绑定后仍报 window ownership 错误,停止自动输入并请用户手动选择语言。WinCode MCP PID 定位及截图正常。初次候选文件路径误写 Views/FirstRunLanguageDialog.xaml,返回 fileScanComplete=false,已记录为错误路径验证,不宣称完成弹窗源码扫描;实际文件位于 App 根目录。 -- 第一性原理验证:主窗口 66 节点,仅 4 个带 ID(MinimizeButton/MaximizeButton/MaximizeGlyph/CloseButton),显式候选 MainWindow.xaml/ConversationWindow.xaml 内无字面量声明匹配;因此没有增加模糊匹配或推断 x:Name 身份。截图编号位置已查看,密集控件的编号可能覆盖文本;不等于截图视觉可读性已全面验收。 -- 代码:新增 per-node reason,区分缺 ID、疑似裁剪、ID值不支持、文件扫描不完整、字面量无匹配/存在不支持声明、歧义;增加声明扫描与节点覆盖计数,MCP 缩减源码结果后同步 returnedNodes。保持候选语义和旧 status 字段。 -- 诊断:FlaUI 被动状态含 activePid/运行/关闭/最近错误,成功缓存探针不抹掉取证错误;WorkspaceWatch 失败保留时间/原因并输出日志,不自动重启。新增 4 项针对性测试,并补预算裁剪后计数断言。 -- 实测:scripts/verify-ui-runtime.ts 限制显式 PID、1–20 轮,不启动目标。主窗口 20 次 review、4 次取消后恢复、3 次工作区往返切换;每轮 managedChildProcesses=0,缓存始终 0 内存条目/1 磁盘条目/66 字节;RSS 首 107.71 MiB、末 95.23 MiB、范围 92.92–122.00 MiB;网关退出确认,目标保持存活。只能说明这轮短测未见持续增长。 -- 验证:typecheck/build 通过;专项 15 项通过;全量 150 项,149 通过、0 失败、1 跳过,约 28.4 秒。报告在 test-tmp/v071/main/report.json,首次/末次 PNG 与 JSON 保留;test-tmp 新增 Git 排除以防运行时截图随代码发布。 -- 待完成:独立对话窗口手动打开后的取证、正确候选路径的弹窗覆盖、最终目标关闭确认。未提交、推送或发布。未做多 DPI 切换、4K 超限或长时测试。 -- 收尾:用户明确选择跳过独立对话窗口,列为未验证;对已保存语言弹窗快照使用正确 FirstRunLanguageDialog.xaml 做离线源码重放,fileScanComplete=true、16/16 节点均 missing-automation-id,文件无字面量 ID 声明(不冒充第二次实时窗口取证)。检查目标 PID 的可执行路径与 launch receipt 一致后,仅结束本次隔离实例,targetExited=true;报告保存 language-source-replay.json 与 teardown.json。 -- 最终边界:全量回归 149/0/1,主窗口短批量测试通过;没有虚构源码命中。当前运行时 ID 缺失是实际覆盖限制,独立对话、多 DPI 和长期稳定性没有验收结论。保留隔离测试根与截图/报告,不删除证据。验收脚本增加退出终态断言后类型检查通过;未新增模型、服务或依赖。 - -## 2026-09-07(北京时间)— 工作区浏览降噪与项目类型摘要 - -- 用户确认“开始改”:仅调整目录浏览/统计与项目摘要,保留先前未提交变更;不修改符号扫描、缓存指纹、目标项目或工具链。 -- WorkspaceManager 新增共用目录判断:默认忽略规则沿用;仅当 .dotnet 内同时存在 dotnet.exe/dotnet 普通文件、sdk 与 host 目录时认定本地 SDK。只查元数据,不运行 SDK、不跟随标记链接。树和统计均返回省略原因;统计标明 filtered-depth-limited 与深度 6,省略详情最多 100 条并附总数。 -- ArchitectureAnalyzer 复用现有项目图新增 projectSummaries:文件、项目类型与 UseWPF/OutputType/Sdk/ProjectReference 声明依据。不扩大目录名启发式,不将目录分层解释为已验证职责;未显式声明 OutputType 时保持未知,不模拟 MSBuild 导入/条件求值。 -- 验证:typecheck/build 通过,cleanup 专项 11 项通过(含新增 SDK 与普通同名目录反例、任意目录名 WPF 识别)。真实 MCP 再读 New-tarven:.dotnet 不再出现在树;过滤后的文件数由前次记录 4635 降至 1180、统计字节由 1079181742 降至 397281787;这是浏览范围统计变化,不是磁盘删除或严格性能基准。 -- 实际摘要正确识别 TavernDesk.App 为 WPF executable、AgentHost 为 Console executable;Core/Infrastructure 未显式声明输出类型,保留说明。结果保存 test-tmp/new-tarven-browse-after.json。未启动 TavernDesk 或读取个人数据库内容。 -- 全量初跑:144 通过、1 失败、1 跳过;失败为原 MCP 取消测试仅等待固定 200ms 即断言 helper 清空。改为最多 4 秒等待终态,保留退出断言及后续调用验证。重跑全量 146 项:145 通过、0 失败、1 跳过,约 33.4 秒;类型检查通过。日志分别保存 test-tmp/browse-regression.log 和 browse-regression-recheck.log。未提交/推送。 - -## 2026-09-07 19:13(北京时间)— 真实 MCP 只读访问 New-tarven - -- 按用户要求,以 SDK StdioClientTransport 调用本地 dist/index.js,先以 WinCode 目录启动,再 workspace_open 到 I:/New-tarven;缓存保留在 WinCode 内。未启动 TavernDesk、未读取个人数据库内容、未修改目标项目。 -- 4 次 MCP 调用均无 isError:workspace_open 约 1722ms、analyze_workspace 25ms、prepare_context 381ms、hello_world 75ms。识别 TavernDesk.sln、4 个项目、WPF/.NET 10、5 条 ProjectReference 边。 -- ChatReplyExecutor 任务返回实现文件第 14 行及测试文件第 10 行,2 个证据片段;工具估算 780 tokens(预算 2000,非完整 MCP 输出 token 数)。Serena 未连接,Repomix CLI 不可用,明确使用本地降级;FlaUI health 可用,但本次未进行窗口取证。 -- 结束前 managedChildProcesses=0;关闭 SDK transport 后操作系统 PID 检查确认本次网关已退出。结果保存在 test-tmp/new-tarven-read.json。 -- 实机观察:工作区目录输出包含 .dotnet 工具链目录,元数据规模有噪声;路径分层启发式未把 TavernDesk.App 等带前缀目录归入 Presentation,但 csproj 依赖图正常。此轮仅记录,不扩展修改范围。 - -## 2026-09-07(北京时间)— 基础架构、生命周期与缓存复查 - -- 授权:用户要求复查架构合理性、生命周期、缓存并打好基础。保留上轮 v0.7 未提交内容,只修复与基础边界直接相关的问题。 -- 架构结论:保留 Gateway 协议、Router 工作区/请求协调、Adapter 上游所有权、CompositeTool 证据组合分层;保持全局磁盘淘汰及命名空间隔离;UI 快照/源码关联不进入长期代码缓存。未引入额外队列框架、服务、数据库或依赖。 -- 生命周期修复:Router 对每个清理所有者逐个尝试,聚合失败,仍执行缓存排空、session 关闭与 ResourceManager 释放;重复 dispose 保留失败结果。taskkill 子命令增加 2 秒上限,避免清理工具自身无限等待。工作区元数据读取失败恢复 root/trash;指纹在修改工作区状态前计算。 -- 缓存修复:超大覆盖失效旧键的内存/磁盘值;内存总限额小于单条限额时不突破上限;从磁盘读取时重新估算大小,不信任 byteSize;淘汰前先检查文件大小再解析;写入、prune、clear 共用原有写队列;shutdown 排空已接受写入。目标目录根据入队前固定的文件路径创建。 -- FlaUI 传输修复:StringDecoder 保留跨 chunk 的 UTF-8 中文;stdout 超限后立即停止存储后续数据;stderr 严格截断,stdin 早退 EPIPE 有错误处理。 -- 针对性测试:增加缓存覆盖/总配额/伪造元数据、清空与排队写入、退出单点失败、工作区读取失败回滚、真实 Node helper 中文拆字与输出洪泛用例;前一轮 13 项专项通过,全量回归进行中。 -- 边界说明:磁盘配额仍为初始化及每 20 次写入时的周期淘汰,不是每次写入后的硬配额;本次串行化仅覆盖单 CacheManager,不承诺多个独立网关共用一个目录时的跨进程事务。WorkspaceWatch 自动重启仍后置,未做长时压力测试或多 DPI 肉眼验收;未将这些限制报告为已解决。 -- 验证完成:全量 npm test 144 项,143 通过、0 失败、1 跳过,约 25 秒;随后补充重复 dispose 失败结果保持断言并整理局部注释/缩进,typecheck/build 再次通过,受影响的 cleanup/hardening 14 项专项全部通过。无依赖安装、外部发布、提交或推送。 - -## 2026-09-07(北京时间)— v0.7 方案复核与实施 - -- 授权:用户要求再次结合 GitHub 经验和第一性原理推敲方案后开始迭代,并要求代码注释与 LOG。范围锁定为 WPF、显式候选 XAML、源码候选证据;本次不推送或发布。 -- 复核依据:[Onlook](https://github.com/onlook-dev/onlook#how-it-works) 使用代码插桩建立元素映射;[FlaUI](https://github.com/FlaUI/FlaUI) 提供 UIA 访问;[AutomationId 文档](https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/use-the-automationid-property) 提醒 ID 不具备全树唯一性。因此 WinCode 无插桩观察只能返回声明候选,不能宣称精确运行时源码身份。 -- 方案收敛:复用 inspect 单次快照、MCP 请求槽位与图片分离;新增 UiSourceMapper 和 UiReview。仅检索 UTF-8 XAML 起始标签的显式字面量 AutomationProperties.AutomationId,返回文件、起始行、片段、哈希与 Command/Binding 等原始属性声明。不是完整 XML/XAML 解析器,不推断 DataContext、模板实例或禁用原因。 -- 边界:最多 16 文件,单文件 256 KiB、总读取 1 MiB,2 秒检查点;最多关联 100 节点、每节点返回 5 候选;保留候选总数与截断。真实路径限制工作区内;文件变更/缺失/编码不支持显式记录;被 Host 裁剪的边界长度 ID 不匹配。 -- 输出:源码查询失败保留 UI 快照;最终 128 KiB 文本预算先缩减源码证据,不改变已有树/图片对应。注释说明候选语义、固定长度读取、路径限制和生命周期所有权。 -- 验证进行中:新增 6 项源码/协议测试已通过,随后执行真实 WPF 端到端和全量回归。未进行 4K/多 DPI 肉眼验收,不改变显示设置或读取用户真实窗口。 -- 验证结果:npm run typecheck、npm run build 通过;npm test 137 项,136 通过、0 失败、1 跳过,约 24.9 秒。真实 WPF disabled 控件成功关联本次快照 ID 与 XAML 起始标签行,并读到 IsEnabled 声明;没有据此自动判定缺陷。git diff --check 通过(仅换行风格提示)。 -- 反证自审:文件缺失时“单候选”不代表全范围唯一,补充 fileScanComplete;长起始标签的片段可能不含命中属性,候选独立返回 automationId 保留依据;极限大小 UI 快照连省略说明都容不下时优先保留原快照。修改后重新执行类型/构建及专项测试,结果随后补录。 -- 最终复核:上述补强后 typecheck/build 再次通过,ui-source-review 与 ui-hardening 共 9 项全部通过。全量 136/0/1 为补强前记录,补强后只重跑受影响专项。未安装依赖、修改 Host、提交或推送;版本统一为本地未发布 0.7.0(同时修正锁文件旧版本元数据)。 - -## 2026-09-07(北京时间)— v0.6 加固版本提交与合并授权 - -- 用户明确要求将当前版本推送至 linnnn89/WinCode 并合并。已确认 origin 为目标仓库,默认分支 main,本地 HEAD 与 origin/main 无分歧。 -- 本次交付包含 Gemini 加固改动与后续快速修复,沿用上一节记录的 129 通过、0 失败、1 跳过及构建验证;通过 codex/v0.6-ui-hardening 分支创建 PR 后合并,不额外修改功能。 - -## 2026-09-07 18:38(北京时间)— v0.6 验收边界快速修复 - -- 授权:用户要求直接快速修复退出确认、排队期限、裁剪后编号一致性及最终文本预算四项问题;保留 Gemini 现有未提交改动。 -- FlaUiAdapter:inspect 使用包含排队时间的统一截止时间;区分用户取消与截止时间耗尽。helper 只在退出事件确认后清除活动引用和资源登记;清理超时明确返回错误并保留所有权,后续健康探测/取证拒绝启动新 helper,shutdown 可再次清理。 -- UIA Host:先限制字段长度及裁剪控件树,再基于保留节点标注图片;重算节点数、深度和标注列表,为响应元数据预留空间。 -- MCP:输出紧凑 JSON 并按最终 UTF-8 字节检查 128 KiB;异常 helper 输出超过预算时返回有界错误,不附带失去对应证据的图片。 -- 测试:新增 tests/ui-hardening.test.ts;WPF fixture 增加独立 --budget-fixture 模式,实际验证长字段和大树裁剪后的文本预算及节点计数;取消测试必须取得 helper PID 并确认 process.kill(pid, 0) 返回 ESRCH,不再仅依赖 isRunning。 -- 验证:npm run typecheck、npm run build 通过;Host Debug 构建零警告/错误;Host 和 WPF fixture Release win-x64 本地 publish(--no-restore)通过;npm test 共 130 项,129 通过、0 失败、1 跳过,约 25.4 秒;git diff --check 通过。 -- 限制:未人为制造操作系统拒绝终止进程,未进行 4K/多 DPI 图片的肉眼标注验收。对未确认退出状态的阻断做了模拟回归,真实正常取消后的 PID 退出已验证。未提交、推送或发布 GitHub 变更。 - -## 2026-09-07 11:56(北京时间)— Stage 1 快速修正 - -- 目标:修复审查确认的 overflow 内存失效、删除边界及 shutdown 排队请求问题;保留既有 Stage 1 实现,不接入 FlaUI 或新增依赖。 -- CacheManager:统一 overflow 删除入口;删除前失效关联内存条目,限制为本缓存 overflow 目录中的普通文件,拒绝目录联接/符号链接越界。TTL、容量淘汰、孤儿清理及 clear 复用此入口。 -- ToolRouter / McpServer:槽位等待前后检查关闭状态;关闭通过既有 workspaceLock 等待进行中的切换完成后再释放资源;槽位获取失败进入 MCP 错误响应,只有实际取得槽位才递减计数。 -- 新增 tests/stage1-cleanup.test.ts 并纳入 npm test:覆盖容量淘汰后的读取、TTL/容量清理外部路径保护、Windows junction 保护、关闭与切换/排队请求并发。全部测试使用新建临时目录,结束清理。 -- 验证:新增 4 项通过;原 v05-stability 35 项通过;npm run typecheck、npm run build 和 git diff --check 通过。 -- 全量 npm test 未通过:旧 tdd-suite 中硬编码外部 Tavern 路径的切换测试、固定项目名 WinCode MCP 的 MCP 架构测试失败;套件输出失败后未退出,已中断。本轮未修改这两个旧测试,也未把全量验证报告为通过。 -- 限制:未做真实 Serena 子孙进程树端到端验收;未变更 Stage 2 范围。文件边界校验不承诺抵御恶意进程在校验与删除之间并发替换目录。 - -## 2026-09-07 12:12(北京时间)— 保存 v0.6 参考方案 - -- 按用户要求在根目录新增 [WinCode v0.6运行时UI取证实施方案](https://github.com/linnnn89/WinCode/blob/67239e3bcdad2ed6572b7407925906f521567dba/WinCode%20v0.6%E8%BF%90%E8%A1%8C%E6%97%B6UI%E5%8F%96%E8%AF%81%E5%AE%9E%E6%96%BD%E6%96%B9%E6%A1%88.md),保存完整范围、架构、协议、生命周期、截图预算、测试与后续路线。 -- 文档明确为参考方案,v0.6 仅 UI 取证,v0.7 源码联动后置;未实施功能、安装依赖或控制真实应用。 -- 验证:文件已生成,核对章节结构;本次仅文档变更,不运行代码测试。 - -## 2026-09-07 14:00(北京时间)— WinCode v0.6 运行时 UI 取证全量实施交付 - -- 目标:按照 [WinCode v0.6运行时UI取证实施方案](https://github.com/linnnn89/WinCode/blob/67239e3bcdad2ed6572b7407925906f521567dba/WinCode%20v0.6%E8%BF%90%E8%A1%8C%E6%97%B6UI%E5%8F%96%E8%AF%81%E5%AE%9E%E6%96%BD%E6%96%B9%E6%A1%88.md),完成 Windows 桌面应用程序 UI 自动化取证功能(C# FlaUI.UIA3 Host、TypeScript UiContracts & FlaUiAdapter、MCP 工具 `wincode_ui_inspect` 及 ToolRouter 深度集成)。 -- 架构设计与关键改动: - 1. **Step 1: C# UIA Host 与独立测试夹具 (PR #2)**: - - 新建 `tools/WinCode.UIA.Host`(.NET 10 console, `net10.0-windows`, `win-x64`, FlaUI.UIA3 5.0.0)。 - - 显式调用 `SetProcessDpiAwarenessContext(PerMonitorV2)` 解决高 DPI / 屏幕缩放坐标物理像素对齐问题。 - - 三级健壮截图管线:`PrintWindow(PW_RENDERFULLCONTENT)` -> `BitBlt` -> GDI+ `CopyFromScreen(Format32bppRgb)`。 - - UIA3 有界遍历:`maxDepth` 与 `maxNodes` 截断,`TruncateReason` 精准标记;`EnumWindows` 过滤输入法及系统浮层;支持仅 HWND 查询时调用 Win32 `GetWindowThreadProcessId` 自动解析并回填归属 PID。 - - 物理像素编号徽章渲染:将 UIA 全局屏幕物理坐标转为窗口相对坐标,在截图上绘制对应 `UiNode.id` 的圆形数字标识徽章。 - - 新建 `tests/fixtures/wpf-ui-review` WPF 独立测试夹具(含普通/禁用按钮、输入框、7 层深度嵌套及启动就绪信号)。 - 2. **Step 2: TypeScript UiContracts 与 FlaUiAdapter 封装 (PR #3)**: - - `src/Core/UiContracts.ts`:定义 `UiRect`, `UiNode`, `UiInspectRequest`, `UiInspectResult`, `UiErrorCodes`(含 `CANCELLED`, `VERSION_MISMATCH`, `PAYLOAD_TOO_LARGE` 等完备错误码),`UI_INSPECT_DEFAULTS`。 - - `src/Core/Config.ts` & `package.json`:升级至 0.6.0,添加 `flauiInspectMs` 及 `adapters.flaui` 配置支持。 - - `src/Adapters/FlaUiAdapter.ts`:实现 `IAdapter`,多级解析 Release/Debug 路径,非侵入健康检查,`Mutex` 串行化防 UIA COM 线程冲突,`killProcessTree` 严格限制仅杀死 Helper 自身进程树(绝不触碰目标应用 PID),支持 AbortSignal 取消与 shutdown 优雅释放。 - - `tests/flaui-adapter.test.ts`:10 项单元与生命周期自动化测试全部通过。 - 3. **Step 3: MCP 工具 `wincode_ui_inspect` 与 ToolRouter 集成 (PR #4)**: - - `src/Gateway/Protocol.ts`:注册 `wincode_ui_inspect`,严格限定入参类型(`integer`、`anyOf` 互斥要求、枚举校验与默认值)。 - - `src/Core/ToolRouter.ts`:实例化 `FlaUiAdapter`,纳入 `initialize()`、`disposeOnce()`、`getRuntimeHealth()`(含 `lastError` 聚合)与 `inspectUi()`。 - - `src/Gateway/McpServer.ts`:注册 `wincode_ui_inspect` 工具调用,按 Grok 4.6 审查意见执行**文本与二进制分离策略**:`content[0]` text 仅包含干净结构化控制树 JSON(剥离 base64 负载并标记 `hasScreenshot: true`,防止模型上下文被巨型字符串撑爆);`content[1]` 独立以 MCP `image` ContentBlock 交付 Base64 PNG。同时升级 `wincode_hello_world` 暴露 `adapters.flaui` 与 capability。 - - `tests/ui-inspect-mcp.test.ts`:12 项端到端 MCP 协议与并发测试全部通过。 -- 结对审查与调试(Grok 4.6 high): - - Step 1、Step 2、Step 3 均按规范调用 Grok 4.6 进行深度结对审查。在 Step 3 审查中,Grok 明确提出 Base64 污染 text JSON 会导致上下文膨胀,指导完成文本与图像分离,并补充严格入参校验与并发互斥验证,确保高质量交付。 -- 验证证据: - - `npx tsx --test tests/ui-inspect-mcp.test.ts`:12/12 全部通过(耗时约 4.3s)。 - - `npx tsx --test tests/flaui-adapter.test.ts`:10/10 全部通过(耗时约 1.8s)。 - - `npm test` 全量测试:106 通过,1 跳过(外部可选夹具),0 失败。 - - `npm run build && npm run typecheck`:0 错误。 - - C# UIA Host 发布编译(Release win-x64):0 错误,正常输出可执行文件。 -- 限制与说明: - - 目前仅支持 Windows 10/11 x64 桌面环境。 - - 目标窗口必须可见且未最小化(最小化窗口 DWM 停止渲染,返回 `WINDOW_MINIMIZED`)。 - - 非管理员权限 Helper 无法读取高权限(UAC 提权)窗口。 - - UI 元素与 XAML 源码精准关联按规划在 v0.7 实施。 - -## 2026-09-07(北京时间)— v0.7.2 GitHub 发布同步 - -- 用户明确授权快速推送并合并。核对 origin 为 linnnn89/WinCode,main 与 origin/main 无分歧;发布包含本轮累计的基础加固、v0.7–v0.7.2 源码审查、诊断与测试。 -- 复用上一轮 typecheck/build 与全量 153 通过、0 失败、1 跳过结果,不重复运行测试;test-tmp 截图、隔离运行数据与报告由 Git 排除。通过独立 codex 分支和 PR 合并,最终合并状态以 GitHub PR 为准。 - -## 2026-09-07(北京时间)— 浏览器遮挡 Codex / QQ 的后台截图验收 - -- 用户明确指定截取浏览器后方 Codex 与左侧 QQ。通过 wincode_ui_list_windows 查找进程并固定各自 PID/HWND,再调用 backgroundOnly=true、capture=original;没有移动、激活、还原目标窗口或操作聊天内容。 -- 两个目标均返回成功,captureMethod=printWindowDwm;Codex 1951×1264,QQ 1796×1497。目视确认两张均为完整目标应用界面,包含用户附件中被浏览器覆盖的区域,无浏览器或游戏像素混入。未同步采样本次前台/Z序,不宣称持续遮挡状态已经独立监测。 -- UIA 树分别为 12 与 7 节点,只证明本轮返回规模,不代表这些 Chromium 界面的可访问性信息完整。 -- 证据保存 test-tmp/occluded-apps-1788784386282/{codex,qq}.{png,json};包含用户界面内容,仅保留在 Git 忽略目录,日志不抄录聊天内容。未推送截图或提交代码。 -## 2026-09-07(北京时间)— GitHub 推送与截图内存优化 - -- 用户先授权推送已验收版本,再检查资源占用与优化。已将 v0.8 第一阶段及后台模式提交 c16f062,推送 codex/v0.8-background-inspection 并创建 https://github.com/linnnn89/WinCode/pull/9;PR 为 OPEN,未执行合并。本节内存优化在推送后实施,仍为本地未提交状态。 -- 审查发现:2 MiB PNG 上限不限制原始 Bitmap 分配;标注 Clone 造成第二份全分辨率位图;MemoryStream.ToArray 在判断 PNG 大小时复制全部缓冲。 -- 最小修正:分配前检查 16,777,216 像素/单边 16,384 像素,超限省略图片并保留树。主 32bpp 位图最多约 64 MiB,不是进程总内存上限。原图/标注互斥输出,因此直接原图标注;PNG 先检查 Length,再通过 GetBuffer 转 Base64。Bitmap 分配移入异常处理;窗口发现复用字符串缓冲。没有新依赖或常驻池。 -- 既有控制:Helper 串行化并逐次退出,UI 快照不缓存;传输 6 MiB、文本 128 KiB、PNG 2 MiB;缓存默认序列化估算内存 32 MiB、磁盘 128 MiB、单条 2 MiB。这些预算不是整个进程的硬内存上限。 -- 验证:Host Release publish --no-restore、TypeScript typecheck 通过;后台/源码专项 12/12、管道/预算/退出边界专项 5/5 通过。后台夹具 10 轮交替原图与标注,10/10 成功,末次标注图已目视确认;每轮 managedChildProcesses=0,缓存均 0 内存条目、1 磁盘条目/66 字节。网关 RSS 首 100.61、峰 102.53、末 91.94 MiB;heapUsed 首 17.56、末 16.85 MiB。报告 test-tmp/background-1788784638642/report.json,输出 test-tmp/v080-memory-run.log。 -- 边界:测的是网关请求后 RSS,不含 Helper/GDI 瞬时峰值;没有长期/并发洪峰测试。超大窗口分配前分支经静态检查,未创造超大真实窗口实测。全量 GUI 回归仍暂缓,避免打扰游戏。没有改无关缓存与架构,私人界面截图仍由 Git 忽略。 -## 2026-09-07(北京时间)— 强制 WinCoding UI 访问指示器 - -- 用户要求 UI 调用时在屏幕右上角强制显示红色录制标志与 WinCoding。新增 RecordingIndicator.cs,复用现有 Win32/GDI+,无新依赖;内置 Host 窗口枚举、inspect/review 的实际 UI 取证先创建标志并确认首次绘制,失败拒绝访问,health/ping 不显示,无请求级关闭开关。主屏物理右上角、DPI 缩放、红点 REC 和下方 WinCoding,NOACTIVATE/TOOLWINDOW/layered/topmost,不抢焦点。每次短请求至少保留 600ms;异常/强杀依靠 Helper 所有权回收,没有独立悬浮进程。 -- 运行时:专用消息线程避免 UIA 阻塞拖住指示器绘制;每次小尺寸缓冲原子绘制,正常 Dispose 关闭并等待线程退出;自身窗口不进入本 Host 的窗口发现结果。此前内存优化未提交内容保持不变。 -- 验证与纠偏:首轮 PowerShell FindWindow 的空类名查询未找到窗口,改为精确 Host PID 类名;探针未设 DPI 感知导致边界虚拟化,修正探针与指示器线程 DPI 后确认物理边界 x=4940..5108、y=12..108。早期探针过早 WM_PRINT 干扰首次绘制,Host 按失败拒绝访问,探针改为等待可见与绘制完成。窗口定向 PrintWindow 探针未完整呈现标签,因此额外只采集指示器所在屏幕小矩形,目视红点/REC/WinCoding;不以不完整探针图冒充视觉通过。 -- 实测 scripts/verify-recording-indicator.ps1 四项通过:inspect、listWindows 显示;强杀 Helper 标志移除;health 不显示。每项采样前台均未改变。Host Release publish --no-restore 通过;TS typecheck 与后台/资源边界 7 项通过。最终增加标志后的无激活后台夹具 2 轮成功,记录 test-tmp/indicator-final-background.log。前一轮 3 次报告为 test-tmp/indicator-background.log。没有运行会激活普通夹具的全量 GUI 测试。 -- 可见性边界:当前游戏性能 HUD 覆盖标志顶部部分,红点、REC 与 WinCoding 仍可见;不通过反复抢前台解决。普通 topmost 不保证盖过独占全屏、安全桌面或更高层浮层,首次绘制确认也不证明未受遮挡。功能只约束内置 Host,不能阻止恶意程序独立截屏、替换程序或使用其他 Host;未宣称防恶意软件安全边界。用户已授权提示标志,未操作游戏或个人数据。未提交/推送本轮修改。 - -- 用户要求黑色背景半透明:指示器 layered alpha 从 255 改为 160(约 63% 不透明度)。这是窗口整体透明度,文字与红点同样受影响;位置与生命周期保持原逻辑。Host Release publish --no-restore 通过。 -## 2026-09-07(北京时间)— 极简审计日志、容量上限与双渠道提醒 - -- 用户确认推荐阈值与渠道并要求测试:采用 1 MiB 提醒、2 MiB 停止新 UI 访问,不自动删证据;MCP 原结果返回文本提醒,显式桌面模式弹窗。新增 UiAudit.cs、check-ui-audit.ps1,复用 BCL 和现有 Host,无新 NuGet 包、数据库、后台服务或全局策略。 -- 记录:UI 操作前 durable start,结束时追加 end;关联 ID、UTC 毫秒时间、操作、Helper/目标 PID、规范化 HWND、捕获模式、结果码、耗时及标志状态。不写窗口标题、消息文本、截图、Base64 或原始异常详情。典型两条约 300 字节,notice.state 固定 32 字节;本轮真实两次调用后目录共 620 字节。 -- 限制:日志在 LocalApplicationData/WinCode/logs/ui-audit,按逻辑文件字节计算。开始前预留 384 字节结束空间,故可略早于 2 MiB 停止;没有自动删除或悄悄停止记账。命名 Mutex 跨 Helper 保护同目录,整个操作占用锁并立即拒绝其他 Helper(AUDIT_BUSY),进程终止后允许接管 abandoned mutex。只有 start 的记录明确视为结果未知。目录扫描限制 128 个平面文件,拒绝子目录/链接。 -- 提醒:自动提醒在完成记录后检测,同级 30 分钟冷却;停止等级立即升级提醒。MCP auditNotice.message/目录/字节数保留在返回中,包括载荷超限错误分支;不建立额外任务。--desktop-notice 在记录标志结束后弹窗。独立检查 --audit-check 可显式检查路径,不能改变实际访问的审计目录。手动检查不受自动冷却限制。 -- 测试:C# 测试夹具直接链接生产 UiAudit.cs,不引入测试框架包;17 项断言涵盖极简体积、关联、内容排除、阈值/冷却、容量预留、禁止自动删除、缺少 end、只读写入失败、结束容量失败、跨进程并发、异常目录。首次源文件相对路径少算目录层级导致编译失败,修正后通过。TS 审计/MCP 传递、后台与资源边界合计 8 项测试通过;Host Release publish --no-restore、TS build/typecheck 通过。 -- 桌面实测:针对 test-tmp/audit-acceptance-01/warning 的弹窗显示 1.001 MiB、建议清理与正确完整路径,原生控件文本核验后自动关闭;报告 test-tmp/audit-dialog-result.json。未填充真实日志目录。MCP 提醒测试使用隔离目录的真实 Host 检查结果透传,不冒充满阈值真实生产调用。 -- 实际流程:加入审计后无激活夹具 2 次后台取证成功,日志默认目录 620 字节;报告 test-tmp/audit-live-background.log,具体样本目录 background-1788786428746。全量 GUI 回归未运行,避免打扰前台;默认 npm test 已纳入审计测试。 -- 边界:日志为本地追溯材料,不防同权限删除/篡改;第三方写入可使目录超过预算,但 WinCode 新操作会拒绝。文件系统分配空间不等于逻辑文件大小。缺结束记录不能推断成功;强杀后提醒只能在后续请求/显式检测时送达。本轮与此前未提交的指示器/内存优化均保留,未提交/推送。 - -## 2026-09-07(北京时间)— 快速提交与双语 README 同步 - -- 按用户要求整理英文/中文独立章节,同步窗口发现、后台截图、半透明强制标志、内存与输出预算、审计阈值及 MCP/桌面提醒;补齐两个工具表,纠正 v0.7 源码候选仍被列为未来功能的旧说明。 -- 本次提交包含此前已验证的指示器、截图内存优化、极简审计与相关测试。沿用上节真实验证结果,本次仅做文档与 Git 差异检查,不重复启动 GUI 测试;test-tmp 私人截图、构建产物不纳入提交。 - -## 2026-09-07(北京时间)— 精简 WinCode Skill 制作与本机安装 - -- 用户确认制作:仓库 skills/wincode 保留源文件,安装到 C:/Users/40218/.agents/skills/wincode。17 行入口按任务选择 code/ui/diagnostics 三份手册;不复制 README、工具 Schema、源码、构建产物,不引入脚本桥接或依赖。默认自动发现,不改变 MCP 配置。 -- 参数对照 Protocol.ts;建议小预算、后台 PID/HWND 定向取证,保留候选/降级/截断边界。明确审计提醒与授权清理,不绕过提示标志或审计。 -- 验证:校验器首次因 Windows 默认 GBK 无法读取 UTF-8 中文失败,改用 python -X utf8 后源文件与安装目录均通过;四文件逐字节一致、引用链接存在。未启动 UI、不读取个人数据。客户端是否已重新发现技能尚未验证;仅安装技能不保证 MCP 工具已连接。本轮未提交/推送。 - -## 2026-09-07(北京时间)— 面向用户的 Skill 与 MCP 独立指南 - -- 按用户要求在根目录新增 WinCode-Skill制作与MCP配置指南.md,说明渐进读取结构、复制安装、路径适配、STDIO 界面/CLI 二选一配置、验收和审计故障处理。明确 Token 边界与已有技能不直接覆盖。 -- 核对现有技能、Protocol/启动入口、README 构建命令和已查证的 CLI 帮助;检查相对链接与 Git 空白差异。本轮仅写文档,不安装依赖、不修改客户端配置、不启动 UI。 - -- 用户随后授权推送并合并:README 中英章节均加入指南链接;将技能与指南提交至既有 v0.8 分支,经 PR #9 发布。推送前 diff --check 通过;本次文档安装内容未重复执行 GUI 测试,功能验证沿用上文记录。 - -## 2026-09-07(北京时间)— v0.9 有界局部取证、状态与完整性 - -- 用户确认上一轮计划后实施。基于已合并 main 新建 codex/v0.9-local-ui;保留既有两份文档删除。README 在执行期间出现并行改版,保留其新版结构,仅追加本轮双语说明,不将整份改版归为本次独立产出。 -- 完整性:遍历异常计数并标记 enumerationFailed;OperationCanceledException 继续传播;深度边界仅确有子节点才标记截断。新增 treeComplete(仅结构/输出覆盖)、propertyIssues(不支持/错误/裁剪)和计数。UiPropertyEvidence 保证未读到布尔值不补 false。 -- 查询:既有 inspect/review 加可选 query,automationId/name/controlType 精确区分大小写 AND;默认扫描 1000/最多 5000 节点、返回 10/最多 20 候选、2 秒及 50 层软边界。独立于展开子树预算,完整唯一才展开;多个匹配保持歧义,未完成不判唯一。请求内只保留有界候选 COM 引用,不跨请求缓存;达到硬停止条件后不再逐层请求兄弟节点。整个 Helper 截止时间及清理保持原机制。 -- 状态:readStates 显式读取 Toggle/SelectionItem/ExpandCollapse 的值,不执行动作、不读取输入框内容;unsupported/unknown 明确。截图仍为整个目标窗口,仅标注返回子树。候选与子树共同计入 128 KiB 文本预算,源码证据继续使用剩余预算。 -- 兼容:Host inspectionVersion=2;请求局部查询/状态遇旧 Host 时返回 VERSION_MISMATCH,避免参数被忽略后整窗结果冒充定向结果。版本同步为 0.9.0,没有新增第三方依赖。 -- 失败纠偏:首次 Python 读取遇默认 GBK,改为 UTF-8;首次夹具 publish 未带 RID,补用 win-x64 发布到脚本实际目录。实机第一次将 Button 错当叶节点,实际有 Text 子节点,保持真实截断并增加 Text 叶节点断言。另一次将 TryGetValue=false 当成读取异常导致搜索误报不完整;核对 FlaUI v5.0.0 AutomationProperty 源码确认其含义是不支持,改为该属性无字面量可匹配,真实异常仍标记不完整(https://raw.githubusercontent.com/FlaUI/FlaUI/v5.0.0/src/FlaUI.Core/AutomationProperty.cs)。 -- 性能:增加响应准备阶段 helperPeakWorkingSetBytes(系统峰值 RSS,不含后续最终序列化,不是硬内存上限)。222 节点夹具全树约 62 KB,局部按钮约 1.6 KB,调用约 0.78 秒,没有证明提速。故不引入 UIA CacheRequest 或跨请求快照缓存;只落实输出缩减与峰值观测。 -- 验证:Host Release 发布、TS build/typecheck 通过。非交互默认回归 128 pass、1 skip、0 fail;随后兼容保护及相关变更的五个专项文件 22/22 通过,最终有界搜索停止逻辑及属性语义专项 4/4 通过(内含生产 C# 16 项断言)。前一轮全量结果不冒称包含随后新增的旧 Host 测试。测试脚本分为 npm test 非交互、test:ui 交互、test:all 两者,局部实机另用 test:ui-query。 -- 实机:test-tmp/ui-query-1788788783290/report.json,16 次取证全通过,覆盖完整/唯一/歧义/扫描与候选上限/无匹配/叶节点/三类状态/源码联动/标注/重复调用。另两次真实 query 取消和超时确认记录到 Helper PID,结束后 OS 查询为 ESRCH、目标 PID 仍存活;末次 MCP 健康 activePid=null。156 个 100ms 前台采样一致,不证明更短暂变化不存在。review.png 已目视确认窗口图与 #1/#2 局部标注对应,未读取个人应用。 -- 维护:README 双语、CHANGELOG、独立安装指南、Skill UI/诊断手册同步;替换安装手册前比较 HEAD 基线,确认无用户编辑,源副本/安装副本一致,技能校验通过。未增加 Skill 入口或默认预读范围。 -- 未验证:真实目标控件动态消失的实机注入(搜索/属性失败用生产函数故障注入断言)、本轮新的浏览器遮挡实机、超大 4K/多 DPI 和长期资源走势。未运行会激活窗口的旧 GUI 全量套件。保持本地修改,未提交、推送或合并。 - - -## 2026-09-07(北京时间)— README 双语复核与独立替换稿 - -- 对照现有源码、配置、脚本和此前验收证据,参考 Playwright MCP、Repomix、FlaUI 的价值说明与快速上手结构,以及 MCP Filesystem 的路径示例。纠正零 Token、绝对免疫、防篡改、MSBuild 求值、即时失效和全进程内存硬上限等超出证据的表述;同步 v0.9 查询、源码候选边界、资源预算与真实测试入口。 -- 两次原 README 写入遭工具策略拒绝(blocked by policy,无具体原因),没有执行写入。用户随后明确要求新建 reamdeV2.md 自行覆盖;已按此文件名生成完整中英替换稿,原 README 保留。按用户要求维持 ~ 示例,并明确整段替换为实际绝对路径;修正 Serena 上游链接,不改变代码或客户端配置。 -- 验证:15 个本地文档链接存在、4 个 JSON 示例解析通过、所有 npm run 命令与 package.json 一致。反证核查包括后台截图不保证所有渲染器可用、局部查询截图仍覆盖整窗、test:all 不含独立 test:ui-query。仅文档变更,未重复构建或 GUI 测试,未提交或推送。 - -## 2026-09-07(北京时间)— v0.9 提交与合并 - -- 用户授权提交合并,并确认删除旧的 v0.6 实施方案与初步构思;两项删除纳入版本记录。用户已将独立替换稿覆盖到 README,提交当前双语版本。 -- 本轮验证:typecheck、build、局部查询 4/4 通过;npm test 共 130 项,129 通过、1 跳过、0 失败。未重复运行交互 GUI 套件。提交前确认远端 linnnn89/WinCode、分支 codex/v0.9-local-ui 与 origin/main 无分歧,忽略的临时截图及构建产物不提交。 - -## 2026-09-07(北京时间)— Agent 效率第一轮 - -- 目标与范围:用户在迭代方案后要求开始,先落实第一轮现有工具修正;从干净 main(1dca8ab)建立 codex/agent-efficiency-round1。未增加依赖、外部服务或进程常驻机制,未提交/推送。执行期间发现 README 有并行改动,保留且未编辑,本轮文档集中在 CHANGELOG 和仓库 Skill 代码手册。 -- 响应:prepare_context 默认 compact 单一 JSON;responseFormat=legacy 保留 JSON + Markdown。新增 ContextResponse 负责最终序列化,完整文本预算包含 JSON 转义、所有文本块及计量字段本身。maxTokens 为 512–65536 的整数,口径为 UTF-16 字符数÷4,显式声明并非模型 tokenizer 或整个协议传输字节上限。兼容模式仍有重复表示,使用方可显式选择。 -- 证据:优先同文件中与任务精确匹配的符号,统一 reason/line/startLine/endLine;前缀过长或后续裁剪时保留目标声明。locationKind 区分符号附近、文件开头和全文片段;evidenceInsufficient=false 只表示获得片段,不证明覆盖任务。紧凑全文只返回 packedContent 一份正文,空 pack 保持不足,部分 pack 保留 truncated。 -- 参数:candidateFiles 仍为优先候选,未改成排他范围。focusAreas 对齐为最多 5 个字面量文件/目录,不支持 glob,并在检索前拒绝通配符和非法输入;流式读取目录直属项,最多追加 8 个文件,每目录最多检查 1000 项。缺失/不可读/过大/越界/选择上限进入 fileIssues;源码读取校验工作区真实路径,单文件上限与内置打包器的 500000 字节阈值一致。 -- 完整性:小预算下保留 queryComplete/truncated/evidenceInsufficient;元数据裁剪显式标记 metadataTruncated、limitationsOmitted,omittedFileCount 保留完整遗漏计数。重构计划返回影响分析的风险、来源、歧义与局限;先提示具体待核证据,取消固定提取接口和零回归承诺。 -- 验证过程:先增加 11 个专项场景,旧实现 11 项失败;修复后逐项通过。一次整文件补丁因同路径重复操作被 apply_patch 格式校验拒绝,没有文件变更,改用单文件增量补丁完成。反证自审增加空打包、长前缀挤掉目标、外部 junction 与元数据遗漏计数场景。 -- 验证结果:完整默认非交互 npm test 为 145 项,144 通过、1 跳过、0 失败,包含真实 stdio MCP 紧凑/兼容调用;随后补充遗漏列表标志及第 16 个专项场景,最终专项 16/16、typecheck、build 均通过。该全量结果先于最后两项局部补充,不冒称是最终全量重复验收。未启动交互 GUI 或读取个人应用数据。 -- 同证据对比:隔离夹具 compact 1493 字符、legacy 2281 字符,证据与局限数组一致,文本量减少约 34.5%。这是本版本两种输出模式的单夹具对照,不是旧版本整体基准,不证明真实模型 Token、调用次数或时延下降。 -- 交付与后续:已构建 dist;既有 MCP 进程未重启,需要重新加载连接取得新 Schema/实现。本机安装目录的 Skill 副本未改动,只同步仓库源手册。第二轮精确定位参数/排他范围、第三轮跨调用复用仍未实施;后续效果需固定端到端任务集评估。 - -## 2026-09-07(北京时间)— Agent 效率复核修补与精确取证 - -- 授权:用户在四项复核缺陷与第二轮方案后同意继续;保留 candidateFiles 优先语义,新增可选排他范围和精确定位。继续使用现有依赖,未提交、推送、重启 MCP 或更新本机安装 Skill;README 并行修改未触碰。 -- 修补:小预算先缩减辅助列表与说明,避免缺失候选元数据挤掉有用正文;修复前缀恰好截至声明前换行时遗漏目标;focusAreas 使用逻辑工作区路径展示、真实路径校验,支持目录联接;打包器提供内部正文位置,分别报告 selectedFiles、packedFiles、returnedFiles 与 bodyStatus,部分打包/最终裁剪后不再把未返回文件标记为已包含。旧缓存键升级以免复用缺少位置清单的结果;无清单的外部结果保持 unknown/null。 -- 精确取证:scopeFiles 限定最多 20 个文件并跳过全仓库符号搜索;lineRanges 接受最多 8 个文件、每文件一个 1 起始闭区间、最多 500 行,跳过符号查询,越界返回缺口。symbol 必须结合 scopeFiles,复用本地 C#/TS/JS/Python 声明解析,精确区分大小写;读取内容在本次符号解析和片段生成间复用。重名/未找到/语言不支持不返回误导的文件头,始终声明 queryComplete=false 与非语义局限。未新增语义后端调用,也未把正则匹配称为 Serena 语义解析。 -- 参数边界:scopeFiles 不与 focusAreas 混用;candidateFiles/lineRanges 必须在 scopeFiles 内;symbol 与 lineRanges 互斥;lineRanges 不与 includeFullText=true 混用。参数错误在检索前返回,路径仍经真实路径边界校验。 -- 反证自审:检索到了一个同名声明并不能保证唯一;覆盖单文件重载、跨文件重名和一文件重名同时另一文件唯一的组合。全文打包成功也不保证预算裁剪后仍有正文;覆盖 Markdown/XML 正文位置、部分 pack、截断至正文之前以及未知位置结果。500 行上限并不保证全部返回,compact/legacy 的 512 预算均保留实际范围与截断状态。 -- 验证:专项 25/25;最终 typecheck、build 通过;完整默认非交互 npm test 共 155 项,154 通过、1 跳过、0 失败(约 31.2 秒)。测试中有意注入的 Serena 失败/超时和 watcher 失败被对应断言覆盖,不是未处理故障。git diff --check 通过,仅提示现有 Windows 行尾转换。未重复运行交互 GUI 或读取个人应用数据。 -- 效率证据:精确行号及排他文件夹具均记录全仓库符号查询为 0;相同证据夹具 compact 1553、legacy 2341 字符,减少约 33.7%。这是当前两种格式对照,并非旧版本端到端基准;未测真实模型 Token、复杂项目时延或用户任务成功率。 -- GitHub 对照复核:Serena(https://github.com/oraios/serena)提供符号级语义检索,Repomix(https://github.com/yamadashy/repomix)提供仓库打包;本轮只复用现有工具边界实现按需取证,没有引入它们的新后端或依赖。不得将当前局部正则定位等同于上游语义能力。 -- 后续建议(未实施):先用固定任务集记录工具调用数、重复范围读取、返回字符量、耗时和完成率;若重复取证占比足以抵消维护成本,再讨论带内容变化校验的跨调用证据复用。公共接口、失效语义和额外资源预算待方案确认。当前 dist 已更新,既有客户端需重新加载 MCP 才能取得新实现/Schema;仓库 Skill 手册已同步,本机安装副本未更新。 - -## 2026-09-07(北京时间)— 效率改动推送与第三轮测量 - -- 用户要求先推送,再按建议开始迭代。核对 origin=https://github.com/linnnn89/WinCode.git、分支 codex/agent-efficiency-round1;fetch 后 origin/main 与本轮起点 1dca8ab 一致。仅暂存上一轮 13 个效率相关文件,保留另一任务的 README 修改。 -- 推送完成:ff8b7232518157ff2ec586e9c6dd7a7fb4de9c20(feat: bound agent context output and add precise evidence retrieval),已推到 origin/codex/agent-efficiency-round1,ls-remote 核对远端提交一致。没有合并 main。沿用上一轮最终 typecheck/build 和 154 pass、1 skip 的全量结果;推送前 staged diff --check 通过。 -- 新迭代:新增 scripts/benchmark-agent-efficiency.ts、tests/agent-efficiency-benchmark.test.ts,提供 npm run benchmark:agent -- 1/3 与 npm run test:benchmark。6 个固定场景:已知符号、已知行号、重名、缺失、重复读取、修改后复查。两种策略具有相同初始位置知识;候选优先仅在证据断言不满足时精确补取,精确优先直接定位。每个场景/策略/轮次独立合成工作区,真实 MCP handler 和本地符号解析,未模拟生产返回;关闭语义上游和 GUI,无新依赖。 -- 测量边界:记录 MCP 调用数、内部符号查询调用(包括缓存命中,不能当作磁盘扫描数)、返回 UTF-16 字符、MCP 调用墙钟时间、跨调用重复显示的非空源码行。重复行按文件、实际行号、内容 SHA-256 判定;修改行不计重复。未计模型思考、真实 Token、启动/清理或真实用户任务成功率。重复与编辑场景是人为构造,不据此估计真实用户频率或缓存收益。 -- 小样本后正式测量:单轮 12 场景运行均达标,约秒级成本;随后三轮交替策略顺序,共 36 场景运行全部达标。候选优先/精确优先各 18/18 通过最终证据断言,首调用达标 9/18 vs 18/18;MCP 33 vs 24 次(-27.3%),符号查询 21 vs 0 次,返回字符 50760 vs 29841(-41.2%),重复显示行 141/513 vs 15/111。异质场景调用耗时中位数约 25.47 vs 1.99 ms,只作夹具描述,不对真实项目声称同比提速。 -- 反证复核:已知符号场景两者均为一次 MCP 调用,精确路径 1791 字符反而比候选路径 1771 字符略多,因为保留了非语义局限;不能宣称所有场景同时省调用、省文本。重复显示行集中于人为重复/修改后复查,不能把所有重复行都认定为无效;文件修改后的核验必须保留。 -- 依据结果的实际调整:仓库 Skill 代码手册默认例子改为 scopeFiles,明确行号优先、文件+符号其次、需要发现其他文件才用 candidateFiles;补充取证满足后的停止条件,以及修改、截断、换工作区时的重取条件。尚未部署到本机安装 Skill,不改变当前 MCP 协议或增加缓存。 -- 验证:最终 npm run typecheck 通过;新增重复度计量与资源上限测试 2/2;三轮基准的 36 个证据断言通过;git diff --check 通过(仅 Windows 行尾提示)。本轮未修改 src 生产代码,未重复全量回归或 GUI 测试;前轮全量结果不冒称本轮重复执行。 -- 报告:[三轮 JSON](../test-tmp/agent-efficiency/1788793544719-3736/report.json),包含请求参数、逐次耗时/文本量/断言、环境、基线提交、工作区脏标记和脚本 SHA-256;生成目录按既有约定被 Git 忽略。新基准/手册/日志改动目前仅本地,未再次提交或推送。 -- 后续建议:先在真实开发任务中沿用同一口径测量实际重复请求,再评估带内容变化校验的复用。当前证据支持调整取证路由,不能证明新增跨调用缓存的维护成本值得承担;缓存接口、失效条件和资源预算仍需具体方案后确认。 - -## 2026-09-07(北京时间)— 基准审核修补、C# 分层与 README 同步 - -- 授权:用户同意执行审核建议,同时要求同步 README。本轮修正未提交的基准、测试与手册,保留 README 其他已有改写;没有修改生产 src 接口、安装依赖、启用语义上游或增加跨调用缓存,未提交/推送。 -- 成功判定:新增 validateEvidence,核对当前夹具的真实文件、正整数范围、片段行数、正文(含截断前缀)、请求范围、符号所在行、诊断对应文件以及 evidenceInsufficient/queryComplete 状态。单独核对实际响应字符计量与预算。错误响应不再以正文包含标记而通过;正向定位和负向诊断继续使用明确场景断言。 -- 失败报告:transport-error/tool-error/invalid-response/scenario/setup/各清理阶段分别记录;无效响应与异常不会被一次成功重试掩盖,只有合法但证据不足才进入有限补取。每个场景结束依次尝试所有清理动作,清理失败保留且场景不通过;后续场景继续运行。CLI 写出含成功与失败场景的 JSON,并依据失败状态设置非零退出码。 -- 场景与公平性:从 6 类扩为 10 类,复制既有 dotnet-mini 的 MemoryService.cs、SaveManager.cs 到各自隔离夹具,不修改原件或启动程序。新增 C# 未知文件、已知文件、已知文件与符号、已知行号四层;两种策略只使用各层共同提供的信息。未知文件两者均进行发现,精确策略也不会得到预先泄露的文件路径。 -- 停止与新鲜度:repeat-unchanged 中候选策略再次请求,精确策略在受控夹具明确无修改时复用已验证片段并记录 reuse-after-known-no-change;read-after-edit 两者均修改夹具版本、更新校验源并重新请求。此处依赖所有夹具写入受控,不是生产文件新鲜度探测或缓存;实际外部变化、工作区切换仍须重新取证。 -- 测试过程:初次新增故障套件 5 pass/1 fail,旧正文注入错误地同时改变了行数,先被 invalid-range 拒绝,没有走到目标 stale-or-wrong-body 断言。改为保持行数、只改正文字符的注入,未降低断言;最终专项 6/6 通过,涵盖原审核的错误范围/错误诊断文件、旧正文、JSON 损坏、传输与工具错误、混合结果报告、无变化复用、编辑重取及清理失败。新增测试接入默认 npm test。 -- 最终验证:typecheck/build 通过;默认非交互完整回归 161 项,160 pass、1 skip、0 fail,约 28.9 秒。随后单独运行三轮基准,避免与回归并发干扰耗时;两组各 30/30 场景运行通过(10 类各重复 3 次,并非 30 个独立任务)。MCP 调用 45 vs 33,符号查询调用 30 vs 3,返回字符 67413 vs 42345,重复显示行 141/609 vs 6/186。耗时中位数约 24.67 vs 1.68 ms,仅为本地夹具调用时间。不能将新场景总量与旧六场景报告直接比较,也不能声称真实用户或模型的通用提速。 -- 报告:[Schema v2 三轮 JSON](../test-tmp/agent-efficiency/1788794274918-25708/report.json)。仍按既有 test-tmp 忽略规则保存;不覆盖旧报告。场景结果新增 knowledge、errors、actions、reusedEvidence 等字段,保留原始请求和逐调用状态。没有收集个人应用内容。 -- README:同步中英文代码上下文路由、compact/legacy、字符预算估计、生产新鲜度限制、基准命令、10 类场景与失败报告说明;明确旧/新版总量不可直接比较,修正本轮涉及的“严格模型 Token 预算”表述。仓库 Skill 与 CHANGELOG 同步;本机安装 Skill 未更新。 -- 文档验证:README 6 个 JSON 示例可解析,8 个不同本地链接均存在,git diff --check 通过(仅 Windows 行尾提示)。未运行 GUI 测试。后续如需生产跨调用复用,仍需单独设计可验证的内容变化、工作区隔离、容量与失效规则;本轮没有实现这些接口。 - -## 2026-09-08(北京时间)— 汇总后续迭代路线图 - -- 授权与资料:用户要求结合「WinCode迭代路线图」及「优化 Agent 使用效率」的 TavernDesk 实测,新增根目录 Markdown;随后明确要求一个 GPT-6 High 子智能体检索 GitHub 经验。已读取两段对话,按要求使用 gpt-6-astra/high 独立只读研究,综合官方仓库/文档,无额外委派或依赖安装。 -- 基线:分支 codex/agent-efficiency-round1,HEAD 6fba44a7404960257e5c749210892477ad26845c,源码版本 0.9.0,SDK 锁文件 1.30.0;不把旧对话 main@b492491 当作本地基线。开始时工作区干净。 -- 当前核查:workspace_open 仍默认附带深度 2、宽度及整响应未设预算的目录树;hello 返回 0.9.0 和工具名,缺少构建/schema 身份。当前会话已经暴露 scopeFiles/symbol/lineRanges,本次限定 483–705 行实际只返回 483–576、truncated=true;不能用 queryComplete=true/evidenceInsufficient=false 代替任务覆盖。历史约 5 万 token 为用户提供的实测概括,本次未重测 TavernDesk 或复算原始响应。 -- 路线决定:按用户指定将工作区摘要、运行实例/能力核对、精准取证实机验收排在前三;随后 Serena 身份/解析正确性、MCP SDK v2、UI→XAML→C#,MSBuild/WPF 深检/Repo Map 按真实需求进入。应用导航可访问性、computer use 错归窗口与测试环境初始化分别归因,不算成 WinCode 三类新增缺陷。固定测试目录的既有完成状态引用原工作记录,不冒称本轮验证。 -- 交付:[WinCode-迭代路线图.md](https://github.com/linnnn89/WinCode/blob/d51f3e105b07b50b1e2535ca541f532ea77b3fc5/WinCode-%E8%BF%AD%E4%BB%A3%E8%B7%AF%E7%BA%BF%E5%9B%BE.md)。包含 R1–R9 目标、最小范围、代码落点、验收反例、GitHub 五项经验、暂缓项和后续 USER_DECISION_REQUIRED。补充同会话诊断与真实宿主验收区别、最终正文范围覆盖、Node 20+ 迁移门槛;不为 Inspector 额外安装或升级环境。 -- 文档验证:UTF-8、13 个不同本地链接、占位/冲突标记与围栏检查通过;初次空白检查发现 Markdown 换行末空格,已改为段落分隔。最终复核仅新增路线图并增订本日志。未修改生产代码、配置或锁文件,未运行代码回归、GUI/Serena 实机验收,未提交、推送或更新发布包。 - -## 2026-09-08(北京时间)— R1 工作区摘要 0.9.1 - -- 授权:用户要求按路线图逐版本复测、Debug、推送 PR 并合并;随后接受 R5 最低 Node 20。实施前 fetch 核对 origin/main=bae4a87(PR #13 已合并),从该提交新建 codex/r1-workspace-summary,带入本任务路线图和日志;不回退其他任务工作。 -- 改动:workspace_open 默认仅工作区身份、项目摘要和最多 8 个入口,完整 JSON 默认 8000 UTF-16 字符(可设 2048–32768);不再计算全仓文件数/字节数或构造目录树。项目发现限制 2000 项、深度 3、描述文件总读取 256 KiB;未知统计为 null,缺口明确记录。includeTree 为有界兼容选项;新增无状态 wincode_list_directory,遍历与序列化分别限额,不改变源码搜索/缓存过滤。 -- Debug 与反例:检查链接逃逸、非法目录参数、宽目录、长 Unicode 项目列表、超大 solution;修正 .slnx 命名/数字实体路径及非法实体处理。真实 TavernDesk 入口曾被多语言 README 挤占,缩减通用 README 入口选择后保留 TavernDesk.App/Core/Infrastructure 等项目。workspace 切换异常仍恢复原根和 trash。 -- 验证:typecheck/build 通过;完整非交互回归 170 项,169 pass、1 skip、0 fail;最后 README 入口筛选修正后 build 与专项 9/9 再次通过。报告保存在 test-tmp/r1/(既有忽略目录)。未安装依赖或运行 GUI;版本同步为 0.9.1,README/Skill/CHANGELOG 更新真实契约。 -- 真实目录复测:新编译生产 Gateway + SDK InMemoryTransport 的独立 MCP 会话打开 I:/WinCode 和 I:/New-tarven,断言响应预算、无默认树、入口上限及 TavernDesk.sln/项目身份。WinCode 3554 字符;TavernDesk 最终实测见 test-tmp/r1/real-workspaces.json。深度缺口明确为 partial,不把它当全仓盘点。该会话关闭 Serena/FlaUI/Repomix CLI,不代表当前 Codex MCP 连接或真实应用 GUI 已更新。 -- 合并:最终 TavernDesk 3705 字符;PR #14 的 C#/TypeScript CodeQL 全部成功后按提交 32b2385 锁定合并,远端确认 MERGED,main=7ce737f1923b0dd31935cd453e39b114ce960091。 - -## 2026-09-08(北京时间)— R2 运行实例与工具契约 0.9.2 - -- 从 R1 已合并 main 新建 codex/r2-runtime-contract;继续用户已授权的逐版 PR 流程。R4 在 test-tmp/r4-worktree 独立准备,避免混入本版构建/测试/提交。 -- 构建:新增 scripts/build.mjs,复用本地 TypeScript,无依赖安装;指纹包含 WinCode 自身源码、构建配置和锁文件以及实际 JS 产物,编译前后源码变化则失败。manifest 缺失、无效、版本/产物失配或源码模式明确 unknown;RuntimeIdentity 模块初始化时快照并冻结,不读取被分析项目 HEAD,不因重编译磁盘文件而改写旧实例身份。该校验是本地一致性诊断,不是签名或供应链认证。 -- 契约:tools/list 与 hello 使用同一已注册定义快照,默认只返回 schemaHash/toolCount;toolName 按需返回单个 inputSchema 和 hash,capabilities 直接源自注册工具。hello/context 未知参数明确失败,避免拼错后静默成功。原手写 JSON-RPC 脚本换成现有 SDK stdio 客户端,隔离夹具且 finally 清理。 -- 验证:typecheck/build 通过,身份/契约专项 8/8;完整非交互回归 178 项,177 pass、1 skip、0 fail。test:e2e 启动独立生产 Gateway stdio 进程:initialize、tools/list、hello 参数/hash 对照、scopeFiles+symbol 与 lineRanges 的第 50 行实际正文、未知参数拒绝及末次同实例核对均通过。报告 test-tmp/r2/,该测试关闭上游/GUI。 -- 宿主边界:实际调用当前 Codex 的 WinCode hello,仍返回 0.9.0、无新 build/schema 身份,证明重新编译不自动刷新既有连接。未重复尝试新参数;未改全局 MCP 配置或终止宿主进程。后续真实宿主 R3 验收仍需要客户端重连,不能用独立 stdio 成功替代。 -- 合并:PR #15 C#/TypeScript CodeQL 全部成功后锁定 9467ede 合并,远端确认 MERGED,main=2cbf443cc983828dd9c9cdb066f2c03f5aecaada。 - -## 2026-09-08(北京时间)— R3 精准范围覆盖 0.9.3 - -- 从 R2 合并基线新建 codex/r3-context-coverage。显式 lineRanges 读取使用剩余响应预算,不再固定截为 4000 字符;500 行/500KB 等既有限制保留。序列化每次裁剪后重算最终 coverage,包含请求/完整行数、实际范围、半截尾行、未返回原因和有界补取;超预算可省明细但保留总计。非范围请求 coverage=null、taskCoverage=null,不把声明附近片段当方法本体覆盖。 -- 反证:223 行足预算、512 token 半行、newline 边界、重复证据去重、多文件明细裁剪、缺失/EOF、最大预算无进展重试均覆盖;nextRequest 从半截整行补取并在需要时提高预算,达到最大值且不前进时停止建议重复请求。 -- 测试组织失败与修复:首次完整回归因 test-tmp 下两个临时 worktree 被既有符号搜索纳入,ToolRouter 出现三处定义而触发 UNKNOWN。没有削弱断言或改搜索语义;核对绝对路径后用 git worktree move 移到 I:/WinCode-worktrees,完整复测恢复正常。该问题归于本次测试环境组织。 -- 验证:typecheck/build 通过,专项及既有上下文 34/34;最终完整非交互回归 187 项,186 pass、1 skip、0 fail;独立 stdio 契约与正文探针通过。真实 TavernDesk 源码验收脚本 scripts/verify-tavern-context.ts 通过 8 场景并逐片段与实际源文本核对;当前 ShowCharactersAsync 位于 309–324 行,已知方法首调命中。1–223 行请求实际完整返回,9942 字符;该新增场景没有旧版配对测量。原 7 个场景的基线与新版请求相同,新增覆盖信息使部分响应更长,不主张普遍 token 降低。 -- 报告:test-tmp/r3/baseline-1788823132218.json、acceptance-1788823577798.json、regression-final.log、stdio.log;源码只读,哈希和实例身份记录于报告。仅知文件时仍返回文件头,不能替代已知方法时显式传 symbol;未知文件的当前例子首调找到目标,不能推论所有未知任务成功。 -- GUI 与归因:复用 I:/New-tarven/work/TAVERN-TEST/profile,经既有脚本 --no-restore 构建 0 警告/0 错误并启动本轮 PID 24964,启动回执核对专用数据/配置/日志路径。当前旧版 WinCode 按该 PID/HWND 查询 NavCharacters,完整遍历 68 节点唯一命中,名称“角色”、isEnabled=true,说明应用可访问性修复有效。后台 PrintWindow 图像几乎空白,不能用于视觉验收;computer use 本轮未调用,不能将空图归为其窗口归属问题。 -- 宿主限制:用户表示无法重连当前 Codex WinCode MCP;保留真实宿主新版验收待办,继续以新启动生产 MCP 进程完成可执行复测与已授权 PR 流程。没有终止旧 MCP 或修改全局配置,不能将本版源代码/stdio 成功写成旧连接已升级。 -- 合并:PR #16 CodeQL 全部成功后锁定 8bda24d 合并,远端确认 MERGED,main=0351111772a3265418d04bd3d0e35116644f5b0a。 - -## 2026-09-08(北京时间)— R4 Serena 身份与解析 0.9.4 - -- 从 R3 合并 main 建立 codex/r4-serena-correctness,整合独立 worktree 的两个生产文件和专项测试。原始 namePath/容器/重载索引完整保留;简单名称先完整唯一语义定位,歧义或查询不完整不再查询第一项引用。完整身份使用前导 / 的精确模式,防止同后缀重定向;ImpactAnalyzer 透传身份且歧义不发引用查询。 -- parser 区分合法空 []/{}、未知结构、损坏 JSON、上游缩略输出;部分合法条目不能变成完整唯一结果。find_symbol 使用官方 name_path_pattern,不拿需要具体文件的 overview 替代全局搜索。上游零基坐标改为一基,引用的声明起点另标 lineKind=containing-symbol;现有四个测试坐标断言按真实转换规则对齐。 -- 反证修补:同一文件内两个容器/重载按身份计数,不能按文件去重为唯一;歧义候选上限 20,保留 candidateCount/candidatesTruncated,41 个候选也不选择首项。源码片段包含摘要字样不误判为上游摘要。 -- 验证:新增专项 27 项(子智能体 26 项加主智能体候选上限测试);typecheck/build、默认完整回归 214 项(213 pass、1 skip、0 fail)通过;生产新 stdio 契约/正文与 TavernDesk 8 场景再次通过。报告 test-tmp/r4/;未安装/启动真实 Serena LSP,受控响应/现有 mock 握手不能替代真实语言服务器验收。 -- 用户新增要求:另开 gpt-6-astra/xhigh 专用只读子代理诊断旧版本连接。初步对照:该新代理实际 Codex MCP hello 已为 0.9.4 且 build.status=verified,主代理同时仍为 0.9.0、旧启动时间不变;配置/进程根因继续核对。新代理连接成功不代表父连接已重启。 -- 合并:PR #17 CodeQL 全部成功,锁定 8276992 合并并确认 MERGED,main=95446c3e06628561501eb31a70bc6da6bea1ccd7。 - -## 2026-09-08(北京时间)— R5 SDK v2 / Node 20,0.10.0 - -- 用户已明确接受 Node 20 最低版本。核对官方迁移文档和 npm,server/client/core 均有稳定 2.0.0,SDK engines>=20;沿用当前 Node 24.19.0 执行,不下载/切换系统 Node,不宣称单独在 Node 20 跑过。 -- 迁移:生产 Gateway/Serena 客户端、stdio/in-memory、测试与脚本全范围更换公开 import;handler 改方法字符串与 ctx.mcpReq.signal,callTool 取消/超时改为 v2 第二参数。直接依赖仅 server/client 2.0.0,移除旧 SDK 和未直接使用的 Zod3,v2 传递依赖统一 Zod4.5.4;没有安装 codemod/Inspector,没有启用新协议默认行为或改变工具参数。schemaHash 与 R4 同为 b640821440bfe6f961683ce22ce6266a3ca6c74f363ceb54d3442bdbca12f191。 -- 安装失败与修复:首次并存安装被旧 SDK 依赖链 body-parser@2.3.0 请求未发布 iconv-lite@^0.8.0 阻止;只读 npm 日志及公共 registry 核对后,移除已计划退出的 v1 声明和未用直接 Zod 声明再安装成功,未加不满足上游范围的 override。首次 typecheck 查出三处旧 callTool 三参用法,按 v2 API 修正,保留信号/超时断言。 -- 验证:typecheck/build、独立 stdio initialize/list/schema/目标正文、TavernDesk 8 场景通过;完整非交互回归 214 项(213 pass、1 skip);真实隔离 WPF 的 UI/协议回归 34/34,含图片独立 block、真实客户端取消传递、helper 退出、工作区切换和重复窗口筛选。日志 test-tmp/r5/;GUI测试为专用夹具,不证明 TavernDesk 的后台空白截图已修复。 -- 旧连接诊断结论:专用 GPT-6 xHigh 子代理核对同配置同绝对启动路径,旧主连接与新子连接返回不同版本/启动时间;旧进程持续运行是直接解释,不是新版磁盘无法被 Codex 加载。新实例0.9.4与当时manifest一致。官方 config/mcpServer/reload 存在但当前工具未暴露且无单服务器无扰动保证,未调用。详见根路线图第13节;该取证在R5依赖迁移前完成。 -- 合并:PR #18 CodeQL 全部成功,锁定 fe78222 合并并确认 MERGED,main=9d9053b87fea1dc9accd7965b8bbcedb0c0782b3。 - -## 2026-09-08(北京时间)— R6 UI 到源码候选 0.11.0 - -- 从 R5 合并 main 建立 codex/r6-ui-code-navigation。UiReview 仍只采一次快照,增加可选 candidateCodeFiles(1–8 个明确 .cs 路径);从已有 XAML 的 Click/简单 Binding 提取文字声明/赋值候选、哈希和下一步限定读取。不提供 DataContext/模板求值,不推定 CanExecute 原因,runtimeSourceVerified=false、runtimeBuildSourceIdentity=unknown。 -- 预算与隔离:单文件 256 KiB、总计 1 MiB、40 线索/200 匹配/每线索最多 5 个输出、代码 JSON 16000 字符,受整体 UI 文本 128 KiB 二次约束。非法候选参数在采集前拒绝;无该参数不读 C#,显式候选外不扫描;Gateway 优先省略新增代码元数据,保留 UI 节点与图片标号。 -- 独立反证发现并安排修补:插值表达式内嵌字符串泄漏为代码、多赋值伴声明未标歧义、null/超长构造参数被作为 symbol 建议、最后一次文件关闭期间取消未传播。为避免变量遮蔽导致跳错方法,nextRequest 改为赋值所在精确行,relatedSymbol 仅线索;调用方读取赋值后再明确请求候选方法。 -- 新 Codex 连接早期验收:构建 990ab3cc9f22ba454f7867fc1013414650bad5c054560a50084eae8ac29101bb(边界修补前),0.11.0/verified、schema 确有 candidateCodeFiles。TavernDesk PID24964/HWND0x100800 完整查询 68 节点、NavCharacters 唯一;XAML116 行 Command → C#127 行赋值/192 行声明 → 实际 ShowCharactersAsync309–324 正文。7 次 MCP 调用含工作区恢复预算2000被拒后改2500成功;本连接最后恢复 I:/WinCode。该证据不能替代后续最终构建,父连接仍旧。 -- 隔离 WPF 测试在仓库外复制 6 个源码文件,空本地 NuGet feed 只复用 SDK/缓存;已观察 false→源码副本单行 true→重新编译与启用,首次结束清理因 EXE 短暂占用失败,补退出等待与有限重试后复测。两个旧失败目录的后续 PowerShell 清理被自动审批拒绝(仅 blocked by policy);未绕过,保留 C:/Users/40218/AppData/Local/Temp/wincode-ui-code-runtime-0oJpM9 与 wincode-ui-code-runtime-EybwBw 作为故障遗留,不影响工作区。 -- 最终修补与验证:上述 4 项均修复,普通/verbatim 插值支持有限嵌套屏蔽(深度 12);复杂插值原始字符串明确不支持。专项 15/15,typecheck/build/独立 stdio 通过;非交互回归 229 项(228 pass、1 skip);重编译专用 WPF 后 UI/协议 34/34。新源码修复测试 1/1:PID27644 disabled → 读取第38行赋值 → 显式请求谓词正文 → 外部副本 false 改 true → 重新编译 → PID8236 enabled;源码与程序集哈希均变化,精准读取不含旧 false,本次新目录已正常清理。 -- 最终构建 aa1bb067cc79e5cab41ed17961a5c4a11c1d2948acee1c908356cf6b22acf49d,在新生产 stdio 上通过 TavernDesk 原有 8 场景及可选真实 UI 验收:UI review、赋值行 nextRequest、显式方法正文共 3 次调用,分别 6164/1877/2181 字符;赋值行覆盖完整,方法正文实际核对通过。报告 test-tmp/r3/acceptance-1788825262426.json;其余日志 test-tmp/r6/。没有将截图空白、旧主连接或真实 Serena LSP 未验收写成已解决。 -- 提交与交付:实现 dfeeab1 已推送 [PR #19](https://github.com/linnnn89/WinCode/pull/19),本次记录补充后按最终提交的远端检查结果合并,实际合并状态以该 PR 为准。R1–R6 均已完成版本实现与复测;R7–R9 尚无新进入证据,继续按路线图条件安排,不擅自启动 MSBuild 求值、进程内 WPF 深检或 Repo Map。 -- 最终真实 Codex 身份验收:07:56:53 新代理实例 a90b9128-2d0b-4c02-9676-9512f9c36b6d 实际 hello 返回 0.11.0、verified,buildId 与上述最终 aa1bb067… 完全一致;实际 ui_review schema 含 candidateCodeFiles,整体 schemaHash=2837d4e1e2b0b35bdae5bd9ec451ea7bea9396df26aa4446cbe2d4020c4316b7。仅调用一次 hello,没有重启/修改配置;它确认最终构建可由 Codex 加载,不代表父连接更新。 - -## 2026-09-08(北京时间)— 当前主连接 TavernDesk 实测验收完成 - -- 用户要求完成实测问题下一轮迭代。基线 main@8f997d8 已包含 R1–R6,工作区干净;此前对话“下一轮尚未开始”已被当前代码和历史合并记录纠正。本次建立 codex/tavern-host-acceptance,仅更新 README、路线图及本日志,不重复实现已有功能。 -- 直接使用本主任务当前连接的 WinCode MCP:首末 hello 均为实例 1b949eb7-14e4-45c6-9cbd-6e6c21eef32a,startedAt=2026-09-08T00:25:54.527Z,version=0.11.0、build.status=verified、revision=8f997d8427301f97cd8d4b4da80b688e83f95c54。buildId=8510232a8a407b93464fbc59a7db9a0dfa51c6eeb5545dca06a666fa4de5ec52;schemaHash=2837d4e1e2b0b35bdae5bd9ec451ea7bea9396df26aa4446cbe2d4020c4316b7,实际参数含 scopeFiles/symbol/lineRanges。不是用独立 stdio 或其他代理替代本连接验收,也没有主动重启 MCP。 -- workspace_open(I:/New-tarven) 默认单块 JSON 3705 字符,无目录树、入口最多 8 项;work/.publish-verify 等作为省略项报告,未测总数为 null,项目扫描不完整明确标记。按需列出 ViewModels:访问/返回各 10 项,1193 字符,entry-budget、scanComplete=false、truncated=true。 -- 精准取证:ShowCharactersAsync 首调返回第 309 行声明及 301–324 行证据,2137 字符,保留本地模式匹配的语义不完整提示。1–223 行请求返回 223 个完整行、9934 字符,allRequestedCovered=true;512 token 估计预算返回 1510 字符,仅 9 个完整行、第 10 行半截,allRequestedCovered=false。小预算覆盖明细被省略时仍保留总计,不把 queryComplete=true/evidenceInsufficient=false 误判为完整覆盖。 -- 原始响应逐条核对 UTF-16 字符预算、实际源码片段/行号、目录截断和同实例身份;源文件 SHA-256=40c34ef90dc3746ab002e9dbcf17e1ff5287faacaa0da71942a59218ae7ff450。证据:[当前宿主原始响应及校验](../test-tmp/tavern-host-acceptance-20260908.json),按既有 test-tmp 规则忽略。历史约 5 万 token 缺少同口径原始配对,不计算百分比提速。 -- 执行 node node_modules/tsx/dist/cli.mjs --test tests/workspace-summary.test.ts tests/runtime-identity.test.ts tests/runtime-contract.test.ts tests/context-coverage.test.ts:26/26 通过,0 失败/跳过,日志 test-tmp/live-tavern-targeted.log。没有生产源码变更,不重复全量回归或重新构建。 -- 共 8 次真实 MCP 调用:首末身份 2 次、打开/恢复工作区 2 次、精准取证 3 次、有界浏览 1 次。最终活动工作区恢复 I:/WinCode。仅只读 TavernDesk 源码,未启动 GUI、读取数据库、调用 Provider 或新增依赖。此前后台空白截图、computer use 窗口归属以及真实 Serena LSP 未验收仍保留,不纳入本次完成结论。 - -## 2026-09-08(北京时间)— 0.11.1 证据边界与使用手册修补 - -- 授权:用户要求修复重新评估指出的缺口,推送 PR 并合并。从 main@8f997d8 建立 codex/evidence-usage-fixes,包含此前未提交的主连接验收文档;范围限于安装手册一致性、符号片段/补读及截图质量提示。R7–R9 和真实任务/原生工具成本对照仍按进入条件另行评估。 -- 片段:保留旧 bodyStatus 枚举以兼容调用方,增加 bodyStatusScope 区分 displayed-snippet 与 packed-file;符号窗口始终 symbolCoverage=unknown。fileLineCount 来自同次源码读取;最终序列化后按实际尾行生成最多 80 行的 nextRequest,半截尾行重读、预算不足提升预算、EOF 不补读,不增加方法解析器或跨调用缓存。 -- 反证:113 行方法包含窗口外的错误处理,片段可以 complete,但不能报告整方法覆盖;compact/legacy 两种输出及补读正文均验证。首轮新增测试 18/20,发现 compact 输出被旧 evidence 字段覆盖,修复后上下文定向 36/36 通过。类型检查首次发现 mjs 测试导入缺声明,按仓库既有 build 测试方式使用明确文件 URL 动态导入后通过。 -- 截图:标注前至多采样 32×32 个原始像素,RGB 各通道范围不超过 3 时仅标 suspect-low-variation;正常纯色/低对比画面也可能触发,其他情况仍为 unknown,均不证明视觉可用。不丢弃原图/UIA、不激活窗口或自动改用屏幕截图。8 个像素/取消/错误边界检查通过;真实隔离 WPF 的 original/annotated MCP 响应均保留质量字段与图片块。 -- 手册:增加 skill:check / skill:sync,仅处理四份受管文档,默认只校验;显式同步先备份再替换并逐文件哈希核对,拒绝链接目标,保留其他文件及 MCP 配置。本机安装四份手册已更新。自审发现备份若仍叫 SKILL.md 可能被再次发现,改为 .bak 后缀;本次早期备份也已原位更名保留。最终同步专项 2/2、安装内容匹配,无重复 Skill.md 备份。 -- 版本与文档:package/lock/WINCODE_VERSION 为 0.11.1;README 中英文、Skill、CHANGELOG 及路线图最新状态已同步,原路线图历史章节保留。没有新增依赖或修改全局 MCP 配置。 -- 验证:typecheck、Gateway build、Host publish --no-restore 通过;默认非交互回归 234 项,233 pass、1 skip、0 fail;UI/协议 34/34 通过;生产新 stdio initialize/list/契约/目标正文/未知参数拒绝/同实例核对通过;TavernDesk 只读源码 8 场景通过。全量回归在最后 .bak 备份后缀调整前完成,该调整后仅重跑相应同步专项。日志 test-tmp/evidence-*.log,源码报告 test-tmp/r3/acceptance-1788828481850.json,均按既有规则排除。 -- 运行边界:本任务现有连接 hello 仍返回 0.11.0(实例 6e7b45f6-2662-43cf-a75c-f0884b1e72d7),没有把新 stdio 的 0.11.1 验证冒称为旧连接升级;后续客户端重连后加载新构建。截图提示修复不等于所有应用的空白截图已消除,computer use 窗口归属及真实 Serena LSP 仍未验收。 - -## 2026-09-08(北京时间)— PR 自动回归与真实源码审查对照 - -- 授权:用户同意执行重新评估建议。基线 main@57d6d68 / 0.11.1,工作区干净,建立 codex/ci-real-task-baseline;范围为 CI、升级验收及小规模真实源码审查,据实测调整取证手册。无网关公共接口、运行版本或依赖升级;R7–R9 未启动。 -- 当前连接验收:本任务实际 hello 返回 0.11.1、verified,实例 690efd0b-200e-4b15-bd08-a3d608a777cf,启动于 09:17:46(北京时间);buildId=483b15166ff2f1bb1905d8d6eec2a360c9aee39b82a669be41f7831d008aaea1、revision=57d6d682b6141c72171df176fb57bc5ce5d44efe,与磁盘产物一致。本轮没有重启客户端或修改 MCP 配置。真实连接正文同时验证 bodyStatusScope、symbolCoverage 和 nextRequest,Serena 仍降级。 -- CI:Windows 2025、Node.js 20/24 小矩阵、.NET 10;固定 GitHub Actions 提交,contents:read,禁用保留凭据,15 分钟超时并取消同分支旧运行。先发布原生 Host、预构建两个控制台夹具,再运行 npm test 与生产 stdio 契约。沿用现有检查且没有降低断言;交互 UI 不进入无桌面验收。分支保护未修改,远端结果以实际 PR 检查为准。 -- 对照设计:四项实际 TavernDesk 源码维护问题,起始信息均为明确文件及声明名。原生路径先用 rg 获取前 8/后 79 行;MCP 路径先按旧 Skill 使用 scopeFiles+symbol、4000 估计 token 预算,缺少分支后执行有界补读。数据根任务两侧继续读复制实现;导入任务两侧增加字段赋值搜索,MCP 路径此步转用原生工具。原始结果、参数、耗时、源码哈希见 test-tmp/maintenance-source-study-20260908.json(按既有规则忽略,不上传 TavernDesk 正文)。四份文件哈希未变化,所有 MCP 正文逐行等于源码。 -- 对照结果(调用数 / UTF-16 返回字符):导入错误条件:原生 2 / 3929,MCP 起步 3 / 7797;回复取消:1 / 3979 对 2 / 7531;数据根迁移:2 / 11397 对 3 / 16135;固定测试根复用:1 / 4430 对 2 / 8121。任务取证共 6 / 23735 对 10 / 39584,MCP 起步一侧含一次原生回退;身份核对与打开/恢复工作区另计。四次初始 24 行窗口均未包含问题关键分支。该结果是同一 Agent 的非盲源码审查、固定窗口策略对照,不是独立模型实验、完整开发任务完成率、真实 Token 或总任务耗时比较;不计算通用提速百分比。 -- 审查结论:ImportAsync catch 仅在会话引用未变时写状态,页面离开/书架切换到会话变更的完整生命周期仍未核实,此项保留部分验收。ExecuteAsync 中 Interrupted 和空正文分支均先于持久化;数据库复制/发布先于配置保存,路径修复是独立入口,配置保存失败后的跨文件系统事务回滚不能由本方法保证;测试根需显式 reuse、匹配非链接标记,并拒绝祖先/子项链接。均为源码结论,不冒称 GUI、真实取消或数据迁移运行通过。 -- 最小修正:README 中英文与 Skill 明确区分声明预览和异常/取消/释放分支审核;后者有现成文件工具时优先有界原生读取,小文件可按预算取完整正文。保留 MCP 证据边界,没有为长方法增加解析器、工具或配置。 -- 本地验证:typecheck、build、默认回归 234 项(233 pass、1 skip、0 fail,28.5 秒)、新生产 stdio 契约通过。日志 test-tmp/ci-regression.log、test-tmp/ci-stdio.log。未重复运行交互 UI;Node.js 20 和全新 Windows runner 的结果待本 PR 实际 CI。 -- 已推送 [PR #21](https://github.com/linnnn89/WinCode/pull/21)。首轮 [CI 34176857681](https://github.com/linnnn89/WinCode/actions/runs/34176857681) 在两个版本暴露实际失败:Node 20 在模拟上游挂起时事件循环提前结束,207 pass、26 cancelled、1 skip;Node 24 在 workspace-summary 触发 libuv fs-event 短路径断言,231 pass、1 fail、1 skip。原生 Host 与控制台夹具构建均已通过,失败不是缺少 .NET 环境;CodeQL 通过不能替代这些回归。 -- 定位与修复:withTimeout 的 unref 使孤立等待在到达期限前退出,新增真实子进程用例以退出码 13/无超时结果先复现;移除该期限定时器的 unref,保留 finally 清理,并验证已完成操作不会挂到 30 秒期限。WorkspaceWatch 将传给 fs.watch 的路径经 realpathSync.native 规范化,状态仍保留请求路径;真实 junction 写入用例核对规范路径、变化通知和关闭。两项新用例修复前失败、修复后通过。依据:[Node timers](https://nodejs.org/api/timers.html#timeoutunref)、[libuv #5010](https://github.com/libuv/libuv/issues/5010)。未修改测试数据根来隐藏短路径问题,也未放宽断言或屏蔽失败套件。 -- 范围更新:CI 发现的问题直接阻碍本轮验收,按既有授权修复两个运行时局部缺口,版本增至 0.11.2;无新依赖或公共参数变化。前述 0.11.1 实际连接验收仍有效,但不能冒称该连接已加载随后新增的 0.11.2 修复。 -- 补充计量:两条取证路径的跨调用重复非空源码行均为 0;本样本中的额外调用来自窗口分片和逐次元数据,不据此引入缓存。MCP 身份核对及工作区打开/恢复共 4 次另外记录。安装 Skill 在首次文档修正后同步一致,最终 0.11.2 版本待交付前再核对。 -- 修复后本地验证:两个新增回归 2/2、typecheck、0.11.2 build、新生产 stdio 契约通过;默认回归 236 项(235 pass、1 skip、0 fail、0 cancelled,27.6 秒)。日志 ci-deadline-red.log / ci-watch-red.log 保留修复前反例,ci-regression-fixed.log / ci-stdio-fixed.log 保留修复后结果。最终远端验证与合并记录统一见 PR #21 对应提交的 CI/CodeQL 检查,不用本地通过推断远端成功。 - -## 2026-09-08(北京时间)— 下一轮整体工程化审查与计划 - -- 用户要求读取「WinCode迭代路线图」最后一次分析,重新审视整体软件工程设计并交付明确计划。实际读取该对话最新分析;当前本地与远端 main 均为 3be2c49 / 0.11.2,开始时工作区干净。本轮是方案编制,没有沿用旧迭代授权直接修改实现或推送。 -- 远端复核:CI 34177655445 的 Node 20 在 runtime-contract.test.ts 清理 other 目录时报 EBUSY,234 pass、1 fail、1 skip,stdio 跳过;Node 24 通过,CodeQL 通过。GitHub main protected=false、required checks 为空、branch rules=[]。未修改分支规则;不把 CodeQL 或本地通过当作 CI 全绿,也没有将 EBUSY 直接归因于永久 watcher 泄漏。 -- 隔离复现:直接调用当前 Serena 实现,三个目录各 210 处引用返回 202 条却 queryComplete=true/truncated=false;指定 a/Uses.cs 仍读取 a/b/c 三份源码。有效 JSON 的源码包含中英文“未激活”文字均触发错误降级。Repomix useCli=false 在受控 spawn 替身下仍请求 CLI 版本探测,并在模拟成功后进入 CLI packing;没有真正启动外部 CLI。 -- 新契约反例:通过实际 SDK InMemoryTransport 调用 find_code_symbol,query 对象未报错,底层收到 "[object Object]";当前 schema 要求 string。核对已安装 SDK 的公开 AjvJsonSchemaValidator,可直接拒绝错误类型和 schema 明确禁止的未知字段,无新增依赖。探针、结果及日志位于 test-tmp/engineering-review-20260908/,采用专用临时源码且正常清理,没有读取个人数据库或运行真实上游。 -- 整体审查覆盖 Gateway/ToolRouter/Adapter 依赖、查询完成/覆盖状态、取消与资源所有权、TS/.NET 构建、CI/测试入口、运行 Host 身份、README/SECURITY 与历史记录。新增计划不是通用插件或大型框架改造;冻结已完成的 R1–R6 和当前不具备进入证据的 R7–R9。 -- 交付:[WinCode-下一轮工程化迭代计划书.md](../WinCode-下一轮工程化迭代计划书.md),并在原路线图增加入口。五个工作包依次为稳定性、工具契约、资源/取消、构建交付规范、真实集成;包含证据表、目标依赖图、修改边界、版本 PR、验收/回退和 9–15 工程日的非承诺估计。Node 支持、严格未知字段政策、分支保护及缺失上游资源均明确为待决定项。 -- 本轮未运行完整回归、Windows GUI、Node 20 本机复现或真实 Serena,不宣称这些已经完成。只新增/更新三份 Markdown 与忽略目录内的分析探针;没有修改生产代码、安装依赖、改全局配置、提交或推送。 - -## 2026-09-08 19:08(北京时间)— WP1 / 0.11.3 稳定性与降级可信度 - -- 授权:用户要求根据计划逐级实施,延续每版本复测/Debug/独立审查/PR/合并。D1 已同意 Node 升级,先保留 20/24 修复证据,WP4 改 24 主支持与 22 兼容;D2 明确保持未知字段容忍模式,WP2 必须在 Skill 列明规范字段;D3 分支设置仍待决定。 -- 实现:Serena 流式目录遍历、有界 UTF-8 文件读取,全局 200 引用/500 符号/5000 遍历项/8 MiB、单文件 256 KiB,限定文件提前生效;超限、截止、读错、编码和跳过链接均不报告完整,v3 缓存隔离旧结果。合法 JSON 中的中英文未激活文字不再触发协议错误。 -- Repomix:禁用优先健康缓存、pack 缓存和进行中请求;冻结入口策略防异步变更污染缓存,进程启动前再次检查配置。8 项受控回归不启动真实 npx。 -- Watcher:stop 等待 close 事件并有 2 秒故障上限,旧 owner 事件不污染新 owner;Router 切换和释放显式等待。所有关闭用 allSettled 收齐后保留失败。此次没有给 runtime-contract 的 fs.rm 增加重试,没有将 EBUSY 预设为永久泄漏。 -- 反例与独立审查:Serena 首轮 12 例有 10 失败,原引用 202、符号 510;关闭首轮 3/3 失败。独立审查找到“旧关闭失败导致新关闭提前结束”和“false→true 配置切换污染 builtin 缓存”,均补测试先红后绿;另修 mkdir 异步边界后禁用仍启动 CLI。复审无未解决实质问题;引用 JSON 测试改为已支持的 preview 字段,没有扩展生产解析器迎合错误 fixture。 -- 本地验证:Node 24.19.0,typecheck、build、默认回归 268 项(267 pass、1 skip、0 fail、0 cancelled,28.8 秒),生产 stdio 契约通过。skip 为既有可选 TavernDesk 集成项。10 次顺序及 10 次并发完整 Router 初始化/切换/停止/清理均通过,实际 native watcher close 全部确认,spawn 边界观察为 0。现有实际 Windows 子进程 PID 清理测试也通过。日志:test-tmp/wp1-regression.log、wp1-stdio.log。 -- 构建身份:本地 buildId=238d4a12a6a702feb3d422a988567556b2cd33842a5c2e3181f2b3342d9a0a5e,schemaHash=751b916ea659b888da414f2f0696b853962ef38b7570c5b80a992716211af9c7(本包无 schema 修改)。这是提交前源码构建,revision 仍记录基线;最终 PR 按精确提交 CI 验证。 -- GitHub 借鉴:[Node watcher](https://github.com/nodejs/node/blob/main/lib/internal/fs/watchers.js) 的真实 close 事件和 [VS Code lifecycle](https://github.com/microsoft/vscode/blob/main/src/vs/base/common/lifecycle.ts) 的 owner/幂等/错误语义,落实到既有机制,没有新增框架或依赖。 -- 边界:截止检查仍为协作式,不能中断已经提交 OS 的单次 I/O;端到端取消留 WP3。恰好达到结果上限保守标截断。未运行本轮真实 Serena/交互 GUI,不把 mock 通过视为 WP5 完成。远端 Node 20/24 与 PR/合并结果随后增订。 -- 远端首轮 PR #22 / CI 34219091516:Node 20/24 各 266 pass、1 fail、1 skip、0 cancelled。唯一失败为新 scoped-file 测试将 TEMP 的 RUNNER~1 短路径与 scanner 的 runneradmin realpath 直接计算相对路径。生产读取只有目标文件,测试改成比较唯一打开路径与目标 realpath,保持零额外文件读取断言;定向 19/19 通过。两版各 10 次顺序+10 次并发生命周期均通过,原 EBUSY 未出现;CodeQL 通过。未据此跳过整套复测。失败日志 test-tmp/wp1-ci-first-failure.log。 -- WP1 最终提交 202e3bd:CI 34219393246 的 Node 20.20.2 / 24.19.0 均 267 pass、1 skip、0 fail、0 cancelled,生产 stdio 与 CodeQL 通过;各版 10 顺序+10 并发生命周期关闭证据确认。2026-09-08 19:14 按精确 head 合并 PR #22,main=31b7dd1fb4d93cf13897ae68498f1fbd52e7e55e。远端日志 test-tmp/wp1-ci-passed.log;未修改分支保护。 - -## 2026-09-08 19:15(北京时间)— WP2 / 0.12.0 工具契约与模块边界 - -- 基于已合并 WP1 开始 codex/wp2-tool-contracts。用户选择容忍未知字段,要求 Skill 明确规范字段;因此统一允许额外字段但不转发生效,已声明字段仍类型/必填/范围校验,保留合法旧调用与别名。 -- 范围:按工作区/代码/UI 静态分组的工具权威定义与执行;复用既有 SDK Ajv;Gateway 仅访问 ToolRouter 用例。业务查询/打包/健康类型放在小型 Core 契约,Adapter 路径兼容导出,保留原有降级与旧数组查询兼容;不引入插件框架或新依赖。 -- Skill 在原有四份手册内补规范字段表、类型、约束和示例,强调 candidateFiles 优先但不排他、scopeFiles 排他、relativePath 为引用定义文件、未知字段不代表功能已生效。 -- 新安全事实:用户截图中的 code scanning #1 经 GitHub API 核对,在 main=31b7dd1 仍 open/medium,规则 js/shell-command-injection-from-environment,Repomix 的 cmd 启动入口尚未修复。CodeQL job 成功不等于告警清零;已向用户明确说明。单独只读调查无 shell 启动方案,未关闭/忽略告警,未用 WP1 的禁用 CLI 修复冒称解决启用路径问题。 -- 验证完成:Node 24.19.0 下 typecheck/build 通过;默认回归 278 项(277 pass、1 个既有可选 TavernDesk skip、0 fail、0 cancelled);生产 stdio 验证 15 工具、schema/hash 同源、精准行正文、未知字段忽略与已知类型拒绝。受控真实 WPF / MCP UI 测试 34/34 通过,包含图片独立 block、串行并发、客户端取消后下一请求恢复、跨工作区与实际 helper 退出。没有操作个人应用数据库。日志 test-tmp/wp2-regression.log、wp2-stdio.log、wp2-ui.log。 -- 独立审查发现契约矩阵以基准调用自身作比较可能漏掉错误路由,已改为独立列明 16 个名称的期望方法和实参;主代理复核最终矩阵、共享路径校验及既有 realpath/junction 防护,定向 19/19 通过。原测试中三个“未知字段应报错”的旧断言按用户决定改为验证忽略且不改变行为;错误类型与路径边界断言保留。早期集成的 type-only 导出错误已修复,没有将其冒称用户政策反例。 -- 构建 buildId=471ba757156a69a8e89d1a7cc2d3d8f6ca4aa4739c71ea5fc90034ff09620063,schemaHash=417b9ad1deafe21006e10ffdc4d17f1d325630b3ddcee9bedf7e1fdd2e5908d3。这是提交前新进程验证,不证明当前 Codex 旧连接已更新。未运行真实 Serena;资源关闭错误保留/端到端代码取消仍属于 WP3;CodeQL #1 仍 open,未修改启动兼容策略。 -- 用户补充偏好:尽量避免分出子 agents。现有协作已经收尾,后续默认主代理直接实施和复核,避免额外代理调用;独立审查与自审证据分别记录。 - -## 2026-09-08 — WP2 合并与 WP3 / 0.12.1 - -- WP2 最终 head fac36dd8a4bd2ec377ab690926c14d558e9f1688 的 Node 20/24 与 CodeQL 全部通过,PR #23 于北京时间 19:45 合并,main=39d2b20c52ba70ffc9770a5e841107b37c1c17f4。 -- 用户明确选择降低健康查询开销:hello 返回被动身份/已知状态;diagnose_project 主动探测。实现 healthObservation 的 known/unknown 与 observedAt,未探测 available/commandFound 为 null,配置禁用保持已知。强制诊断不复用 Repomix/FlaUI 的短时健康缓存,Serena 检查复用现有诊断调用,避免重复。 -- 代码操作使用内部 signal/deadline,覆盖符号/引用/上下文/影响/重构和打包。Serena RPC、握手接收 SDK 取消参数;本地读文件及扫描循环协作取消,打包的取消归属不共享。工作区排队/排空及提交前可取消;提交开始后完成一致性收尾。Shutdown 中止活动代码操作并保留排空失败。 -- ResourceManager 保留关闭失败、最多 100 条 owner/kind/outcome 与 1024 字符错误、历史省略数;释放期间新登记资源也等清理结束。失败不跳过其他 owner,重复关闭不伪报成功。Gateway 仍尝试关闭传输,初始化失败释放已获取资源。Windows taskkill 使用独立 argv,并核对自有 PID 退出。 -- Debug:实际上游取消测试先发现已完成 reset 结果误用于新连接;修正按新连接开启关闭生命周期。完整回归又暴露旧测试断言失败后漏关 watcher、共享 config 未恢复导致连带失败,改 finally 清理而不削弱断言;仅终止了已核对属于本轮的测试进程树。ImpactAnalyzer 的旧双参数断言改为同时验证完整 namePath、定义文件及 operation 转发。 -- 更早握手取消测试证实 SDK 关闭过程中清空 transport.pid,进程尚存;现于 transport.start 返回 Promise 后立即记录真实 PID,在握手取消路径回收。曾试验固定 legacy 握手,单独不能解决问题,已撤回该试验、保持默认协议协商;没有改变上游协议兼容政策。定向最终 14/14 通过,包括真实 mock 上游挂起握手/RPC 的 PID 退出与重新连接;fixture 不代表真实 Serena C# 语义。 -- 方法依据:[MCP cancellation](https://modelcontextprotocol.io/specification/2024-11-05/basic/utilities/cancellation)、[Node fs](https://nodejs.org/api/fs.html) 与已安装 SDK 2.0.0 的 public ConnectOptions/CallToolRequestOptions/StdioClientTransport 源码。没有新增依赖、全局配置或真实应用数据操作。 -- 边界:不能中断已经提交 OS 的单次 I/O;Serena 共用连接重置可影响其他上游请求;不把进程计数归零视为退出。UI 测试 34/34 通过,stdio 通过;最终源码全套结果和 PR 检查随后增订。按用户减少子 agents 的偏好,本包由主代理实施与反证自审,未宣称完成独立模型审核。F12 shell 启动兼容变化与 D3 分支保护仍未获单独确认。 -- WP3 本地最终验收:Node 24.19.0,typecheck/build 通过;默认回归 292 项(291 pass、1 个既有可选 TavernDesk skip、0 fail、0 cancelled,36.98 秒),生产 stdio 通过;UI 34/34 通过,后续修正只涉及 Serena 握手。buildId=6a79eb1cf16d42f635b69155b48520559b8e4f22690be87a42a9a74fc1fbd16a,schemaHash=feffc1d2bd2c898d3d339b14b3287da0192a52d7b29741cc577cd029f3211b3a。日志:test-tmp/wp3-final-check.log、wp3-stdio-release.log、wp3-ui.log。PID 提前登记引起旧稳定性测试的活动 PID 残留断言失败,已在真实退出后清空并注销;相关 50/50 复测通过。 - -## 2026-09-08 — WP3 合并与 WP4 / 0.12.2 交付规范 - -- WP3 head=0115642f82d0aeec806095599c03191065194ba3 的 Node 20/24 与 CodeQL 全部通过,PR #24 于北京时间 20:16:48 合并,main=227463ed13cacb3f6ea37f17bb560a4d5ac442cd。两版回归各 291 pass、1 skip、0 fail/取消。未增加子代理;自审不冒称独立审查。 -- 按用户已批准的 Node 升级,WP4 改为 24 主支持、22 兼容、engines >=22。此前已先修复并验证 Node 20 故障,没有靠删除失败矩阵掩盖缺陷。Host 与三个实际构建夹具加入 NuGet lock;global.json 固定本机已有 SDK 10.0.303、rollForward=disable,遵循 Microsoft 对锁文件的建议。未升级声明的 NuGet/npm 依赖、未安装全局工具。 -- 新增 check / check:desktop / test:inventory / delivery:verify。核心入口包含锁定恢复、原生/控制台构建、全部明确归类的非交互测试和新 stdio 进程;桌面入口单独运行隔离 WPF 和 UI→源码闭环。CI Node 22/24 使用同一入口并上传有界报告;不是托管 runner 桌面验收。报告记录命令、环境、构建/schema hash 和关闭前资源观察,后者不冒称进程退出证明。 -- 交付清单覆盖 Gateway JS、Host 整个发布目录(含 DLL/sidecar)、四份受管 Skill、版本/SDK/锁文件配置;校验丢失/新增/修改文件与版本不一致。时间戳和绝对检出路径不参与内容身份,Git revision 单独关联。哈希只证明本地内容一致性,不是签名,也不验证另一个 Codex 连接。 -- 原生 Host 用程序集 version/informationalVersion/configuration/framework 替代写死版本;生产只选 Release 发布文件,Debug/dotnet-run 仅在显式 --development/npm run dev 模式允许,customHostPath 显式覆盖保留。 -- 增补 CONTRIBUTING、行尾/编辑约定,修正 SECURITY 过期支持表和固定 48 小时响应承诺。GitHub private vulnerability reporting 已只读核验开启。保留未使用 ExtensionManager 兼容入口,只修正过期注释,不扩展插件架构。 -- 首轮本地核心 check 通过:301 tests,300 pass、1 既有可选 TavernDesk skip、0 fail/取消;桌面 35/35 通过,含真实 WPF 修复闭环。报告 test-tmp/check/2026-09-08T12-30-46-296Z-core/report.json、2026-09-08T12-32-57-103Z-desktop/report.json。新增交付反例 9/9 通过,覆盖 JS/源文件/Skill/SDK/Host DLL 变化、缺失 sidecar、新增 DLL、版本不一致、禁止隐式 Debug 回退。 -- 文档批量写入首次因当前 Set-Content 不接受 -NoNewline 未写入三个目标,随后改用明确补丁完成并检查差异;没有把失败写入记为完成。原生编译与锁定恢复首轮成功。后续增加报告字段及文档后仍须以干净检出复核最终版本。 -- 尚未完成:干净检出与重复内容身份、最终 CI/PR 合并。D3 分支保护与 F12 无 shell Repomix 兼容取舍仍待用户决定,不修改仓库权限、不关闭旧 CodeQL 告警。 -- 干净检出 I:/WinCode-worktrees/wp4-clean-20260908 的首次 npm ci 成功,但回归 299 pass/1 fail/1 skip:旧 tdd-suite 将项目名写死 WinCode/WinCode MCP;实际 identifyProject 按 path.basename(root) 命名,任意其他合法检出目录都会触发假失败。改为精确断言实际目录名,未更改产品行为、未重命名目录绕过。失败报告保留在该检出的 test-tmp/check/2026-09-08T12-37-31-404Z-core/。 -- 干净检出修正后完整 check 两次通过(均 300 pass、1 skip、0 fail/取消),报告 2026-09-08T12-39-02-455Z-core 与 12-39-56-230Z-core。两次 Gateway buildId=88c96147c9fc9430c5d5f39f0c27cf17d9b1a2ad5323c548c46fc817a35fe9ec;交付 contentId=5795657ffac19488b24503baf9ca2d867f5d1f24fa2a5deaa3c461783066c043,时间戳变化而内容身份相同。依赖来自该检出的 npm ci 与共享 NuGet 下载缓存,没有借用原工作区 dist/bin;这不是空机器离线构建证明。 -- 从系统 Temp(非仓库 cwd)直启该干净检出的发布 Host,health 成功,真实 version=0.12.2、configuration=Release、informationalVersion=0.12.2+b6901cd960da460fa91bb2eeba81e63eac716ae4、framework=.NET 10.0.11。生产 stdio 测试也在独立临时工作目录运行。 -- 反证自审:只覆盖 Host 主 EXE 会漏掉依赖 DLL 混用,清单实际枚举整个发布目录并用新增/缺失/替换反例验证;同版本旧内容仍通过哈希检测。未提供签名真实性证明,也未更新父 Codex 连接。最终 PR 的 Node 22/24、CodeQL 结果仍须在提交后核对。 - -## 2026-09-08 — WP4 合并与 WP5 / 0.12.3 真实应用验收 - -- WP4 PR #25 的精确 head=8d36bc7727fa5f85ba72ff44992927d760c0614d,Node 22/24、CodeQL 三语言分析及汇总均 success,于北京时间 20:44:51 合并,main=ba94c003a1b4ed130cbd0bd73389506f3f348481。CI 34227530379、CodeQL 34227527264,日志 test-tmp/wp4-ci-passed.log。强制 Host Rebuild 后再次验证,完整交付哈希保持相同;不仅依赖增量构建跳过。 -- WP5 增加显式 test:product,复用既有 Client、stdio、EvidenceOverlap;六个固定导航 ID,输入不预先提供 XAML/C# 文件名。每项先用有界 rg 发现 XAML 与 C# 候选,再调用 ui_review、赋值精确行和方法声明窗口。按源码行与 SHA-256 核对正文;错误、耗时、实际返回字符和清理失败留在本地报告。没有新增依赖、插件层、生产缓存或源码映射推断。 -- 固定 TavernDesk profile=I:/New-tarven/work/TAVERN-TEST/profile;第一次本轮启动 PID=6512、HWND=0x140B0E,回执核对测试 DB/config/logs 路径,无 Fresh、无重复导入参数。六项通过:NavDashboard、NavChat、NavCampaigns、NavCharacters、NavWorldbooks、NavSettings。每项 2 原生+3 MCP,合计 30 次(12 原生、18 MCP)、67103 UTF-16 字符;第二次最终脚本实测调用耗时合计 5145 ms,连接/打开另计 2 次、8099 字符。显示源码 127 行、重复 42 行。结果 test-tmp/product-tasks/1788871732133-22296/report.json、1788871829665-10888/report.json;单应用、单操作者、脚本固定任务,未做盲试验或纯原生等价对照,不能推算通用提速或模型 token。 -- 同一实例上的 test:tavern-context 八种范围场景通过;精确行 2219 字符、allRequestedCovered=true,符号窗口 2453 字符定位方法但全方法覆盖未知,已知整文件请求 2387 字符仍未定位目标。范围正确与任务充分性分开。三次 UI→赋值→方法请求通过;没有把 snippet 非空升级为完整方法。报告 test-tmp/r3/acceptance-1788871741500.json。 -- 当前 Codex 的实际 hello 确认为 0.11.2,实例 9706c136-9daf-44e8-9811-2c0f3b755f6d,启动于 2026-09-08T10:33:06.633Z,buildId=186f4bfc8f4d3cbc5ac2f7fe9f96ec98fa28c6fca1e6b862916923f523d778c7。它仍调用旧 Gateway;其新启动的磁盘 Host 实际报告 0.12.3/Release。通过当前旧连接做了一次后台原图检查,printWindowDwm,捕获质量提示 unknown;实际查看可读到 V3 固定样本角色和六个导航,没有仅凭像素变化宣称成功。此证据属于旧 Gateway+新 Host 组合,不混入新 stdio 版本通过项。没有重载客户端、修改 MCP 配置或杀死该旧连接。 -- 已按用户“Skill 明确规范字段”的要求,将四份受管手册备份后同步到 C:/Users/40218/.agents/skills/wincode;随后 skill:check matched=true。备份 .wincode-backup-f76fb13c-62fb-4259-bec8-ed514885efa5。规范字段、容忍未知字段以及固定测试 profile 复用指引已进入实际安装手册;它不证明当前 MCP 已重连。 -- 本机 PATH 未发现 serena/serena-mcp-server;uv tool list 为空,受限检查 uv archive 中未发现 Serena 包;当前实际 hello 也报告 commandFound=false/handshakeOk=false。真实 Serena/C# 语义集成未运行。上游官方配置说明 C# 使用 .NET 10 与自动下载的 Roslyn Language Server(https://github.com/oraios/serena/blob/main/docs/02-usage/050_configuration.md);未因此自动下载/安装,已询问可用现有路径,保留未验证项。 -- 最终本地 check 再次通过:300 pass、1 个既有可选 TavernDesk skip、0 fail/取消,报告 test-tmp/check/2026-09-08T12-53-08-086Z-core/report.json;交互桌面 35/35,包括真实隔离 WPF 缺陷→源码修复→构建→运行复查,报告 2026-09-08T12-49-49-117Z-desktop。后续改动为文档、测试脚本计数/关闭日志,未改 UI 生产实现。真实业务源码未修改。 -- 自审反例:六次命令赋值落在同一构造函数,逐项 nextRequest 仍重复显示 42 行。Skill 因此提醒复用已核对且未变化的正文,必要时合并有界范围;测试继续报告原始重复量,不先删除重复结果美化指标。保留 runtimeSourceVerified=false、wholeMethodCoverage=unknown;不重开 R7–R9 或引入缓存层。 -- 用户随后说明导入真实角色卡;先前自有 PID 6512 已正常 CloseMainWindow 退出。复用原 profile 重启为 PID=33672、HWND=0x330A40,后台查看当前仍显示 V3 固定样本;尚未确认用户新卡所在位置,已询问名称/是否在 [TEST] 窗口导入。没有将此重启误称真实角色卡验收通过,没有扫描个人数据库。新卡状态验收待定位后补充。 -- F12 API 在 main=ba94c003a1b4ed130cbd0bd73389506f3f348481 仍 state=open;D3 分支保护也未获单独确认。真实 Serena、当前 Codex 连接重载、新角色卡定位均未完成;这些限制不以受控测试替代。遵循减少子代理偏好,本轮只有主代理自审,无新增独立审核。 - -### WP5 验收矩阵(2026-09-08,后续增订) - -| 能力 | 环境 / 数据 | 证据类型 | 结果与范围 | -| --- | --- | --- | --- | -| 构建、契约、取消/资源 | Node 24、本机隔离夹具 | 受控进程与故障注入 | 核心 300 pass、1 skip;远端最终 head 结果待 PR | -| WPF UI→源码修复闭环 | 隔离真实 WPF 窗口 | 真实运行+合成源码 | 35/35 桌面套件,含修复后运行复查 | -| 六项导航→源码 | 固定 TavernDesk / V3 测试样本 | 真实应用+字面源码候选 | 6/6;赋值与方法声明验证,运行时绑定/完整方法未知 | -| 精准范围 | TavernDesk 当前源码、独立新 stdio | 真实源码只读核对 | 8 种场景与 UI 候选闭环通过;整文件请求不保证定位 | -| 后台截图 | 当前旧 Gateway 0.11.2 + Host 0.12.3 | 实际查看原图 | 可读;captureQuality=unknown,不冒充新 Gateway GUI 验收 | -| Serena/C# 重载→引用→正文 | 未找到可用本地命令 | 未验证 | 未安装上游/语言服务器,WP5 语义集成保持未完成 | -| 用户新导入真实角色卡 | 待确定名称及测试 profile | 未验证 | 当前画面仍为固定样本,等待定位信息 | -| 当前 Codex 重连 | 存活 0.11.2 连接 | 实际 hello | 已识别旧实例,未重载 | -- 用户已确认新角色卡在书架中;Computer Use 的 list_windows 再次把测试 HWND=3344960(0x330A40)归给 AiPPTAddin.App.exe,get_window 报“window id 3344960 no longer belongs ... current owner ...”,刷新列表后唯一一次重试同样失败。未构造伪造窗口对象或绕过驱动注入输入,已请用户在该测试窗口打开新角色卡。WinCode 按 PID/HWND 的同窗读取和截图仍正常,故这项障碍归因 Computer Use,不归因 WinCode;真实卡显示验收继续等待界面切换。 -- 对最终隔离 WPF 修复测试补充反证检查:临时项目在仓库之外会脱离 global.json,因此现在明确复制已有 SDK 策略与 packages.lock.json,并采用 --locked-mode+原有离线源恢复。没有下载新依赖。test:ui-code 真实修复闭环单项复测通过,typecheck 通过,日志 test-tmp/wp5-locked-ui-code.log。 -- 真实角色卡补充验收:用户指明书架中的角色后,实际截图已显示其封面及名称。仅对固定 profile/data/taverndesk.db 使用 SQLite URI mode=ro 与 PRAGMA query_only,精确不区分大小写名称匹配为 1 条;原始卡片 JSON 与导入报告 JSON 可解析,描述 2896 字符、开场白 670 字符均与原卡字段一致。头像路径按 TavernDesk AppDataPaths 规则相对 data 根解析,文件存在且位于专用数据根内;未修改数据库、未公开角色卡正文/图片/原文件路径。 -- 该角色名在界面出现为两个 TextBlock,完整搜索 84 个节点后返回 ambiguous、2 matches、不展开 tree。当前连接的截图可读;另用新载入的 0.12.3 生产 Gateway/Host 经 InMemory MCP 验证相同歧义行为,结果 test-tmp/wp5-user-card.json。它不表示重复入库或查询失败,也不证明动态角色卡已有稳定唯一 AutomationId。详情页动作/聊天生成未验收,Computer Use 窗口归属错误仍存在。 -- 真实角色卡存在的书架状态下,新 stdio 六导航任务再次 6/6 通过:30 次调用、67083 UTF-16 字符、5134 ms 调用时间合计,重复显示 42 行。报告 test-tmp/product-tasks/1788872623250-36420/report.json。此证据替代上表“新卡位置未知”的当前状态,保留此前历史记录;已确认入库/书架显示,未声称聊天或全部卡片语义兼容。 -- 临时探针曾调用不存在的 connectTransport(失败日志 wp5-user-card-probe.log),核对既有 runtime-contract 测试后改为公开 SDK InMemoryTransport 与 Gateway 内已有 server.connect,复测通过;没有为测试添加生产连接 API。头像初检把相对路径误按 WinCode cwd 解析,随后查明 AppDataPaths 的 data 根规则并正确复核;初检 false 不属于应用文件丢失。 -- 为让远端检查报告直接提供实际测试数量,check 使用 Node 内置 TAP reporter 并收集 tests/pass/fail/cancelled/skipped 摘要;不只留下“命令退出 0”。缺少完整测试摘要仍报错,不把未运行套件记为通过。 -- TAP 报告补充实测:核心 check 通过,report.tests={tests:301,pass:300,fail:0,cancelled:0,skipped:1}(2026-09-08T13-05-15-874Z-core);桌面 check 通过,report.tests={tests:35,pass:35,fail:0,cancelled:0,skipped:0}(2026-09-08T13-06-36-861Z-desktop)。更新 PR 后重新核对精确 head CI,不沿用先前 head 的成功状态。 - -## 2026-09-08 21:37 — 0.12.4 安全启动与剩余验收 - -- 用户要求尝试落实四项检查,按已说明方案推进 F12/D3;随后明确批准 Serena 隔离安装与验收,上限新增磁盘 1 GB、安装 15 分钟,不改全局 PATH。 -- main 保护已通过 GitHub API 写入并回读:PR 必需、strict Node 22/24 与 CodeQL csharp/javascript-typescript/actions 五项检查,绑定 GitHub Actions app=15368;enforce_admins=true、禁止 force push/删除、要求解决讨论。单维护者 required_approving_review_count=0,不将其写作独立审核。请求/回执在 test-tmp/security-verification/main-protection-*.json;本轮未新增子代理。 -- Repomix 健康与打包改为 process.execPath+独立 argv+shell:false。读取本地 package.bin,显式 customCliPath 仅接受绝对 JS 入口;不再调用 npx 缓存/PATH shell 包装器,不安装 Repomix。保留禁用无进程、降级、缓存策略隔离;随机输出名避免并发碰撞,清理超时文件和取消监听器,持续排空并限制输出。 -- 初次新增测试错误使用 CacheManager 构造参数/dispose,修正为现有 API 后 15/15 通过;这些是真实 Node 子进程夹具,不是真实 Repomix 包验收。覆盖特殊字符 argv、安装 bin 发现、无效显式路径、超时与取消后 PID 退出。失败证据保留 repomix-targeted.log。 -- 完整检查第一轮暴露两项旧测试依赖 npx 恰好较慢;改为真实挂起脚本,不削弱超时/清理断言。第二轮因隔离 Serena 安装扩大工作区,旧全仓影响分析触发真实 total-byte-limit 并正确返回 UNKNOWN;将该测试改为固定两文件夹具,保留有引用风险评估及置信度约束,未放宽生产扫描上限。第三轮完整 check 通过,报告/日志见 test-tmp/security-verification/full-check-3.log。 -- Serena 固定 v1.7.0 commit=949a27ef1e5fda1a6e7b561e777bcece345c6ffd,复用现有 Python 3.13.7,venv/uv-cache/SERENA_HOME/语言服务位于 test-tmp/serena-real;安装完成约 632 MB,.NET 10 复用已有环境。C# Roslyn=5.5.0-2.26078.4,下载校验采用上游固定 SHA-256。握手真实成功,第一次查询发现 structuredContent.result 包裹 JSON 字符串被当成符号对象;上游 content 内实际返回三个完整身份。此新缺陷作为后续兼容修复,不把本 PR 写成 Serena 已验收。 -- 当前 Codex 管理 CLI 只有 list/get/add/remove/login/logout,没有受支持的 reconnect 命令。保存配置指向正确 I:/WinCode/dist/index.js;没有用重复注册或杀进程替代重连,也未宣称旧父连接更新。 -- 作者反证自审:无 shell 不等于已安装脚本可信或有沙盒;缺失显式 CLI 不应悄悄执行另一安装。CodeQL 任务成功仍需合并后读取 alert #1 的 fixed 状态;PR/远端验收待后续回执。 - -## 2026-09-08 21:43 — 0.12.5 真实 Serena 兼容闭环 - -- 0.12.4 PR #27 精确 head=b912343 的 Node 22/24 和三语言 CodeQL 均通过,2026-09-08 21:40:05 在启用保护后正常 squash 合并,main=41602e0af4a1738f2542ae513eec1dda1485df29。未使用 admin bypass。main 扫描后 alert #1 已自动 fixed,fixed_at=2026-09-08T13:41:50Z;未手动 dismiss。 -- 真实 Serena 原始返回同时包含 content JSON 和 structuredContent={result:JSON字符串}。旧实现优先序列化整个 structuredContent,误判为不支持的符号结构。现在仅解开唯一 result 字符串包装,其余结构仍执行原有校验;不吞掉未知元数据,不将畸形 structuredContent 的 text 作为成功替代。 -- 新增六项回归与既有身份/降级套件合计 52/52:真实包装的符号/引用、合法空数组、未激活错误、截断、非 JSON、额外 metadata。引用坐标仍明确 containing-symbol,不伪装为精确调用行。 -- 新增显式 test:serena-real,不安装组件、不修改 Codex 注册。复用已批准隔离安装的 Serena 1.7.0 与 Roslyn,按生产默认连接/调用超时执行专用 C# 项目;七项通过:三个同名/重载身份、歧义不猜测、两个重载分别命中各自调用、直接上游正文与源码逐字一致、合法空符号/零引用、终止自有上游后将测试重启命令设为缺失并确认降级、真实未激活项目。保留 direct upstream body oracle 和 WinCode adapter 查询的证据边界,不宣称新增全方法正文产品接口。 -- 报告 test-tmp/serena-acceptance/1788874762075-31176/report.json 与 1788874881144-38796/report.json;每次记录真实结果、PID 退出、dispose 成败,测试 C# 源码保持一致。进一步自审加强为核对引用预览中 > 标记所在行,避免“周围文本同时有另一重载”造成假阳性;最终复测回执另存本地。查询已隔离安装目录对应 python/dotnet 进程,没有残留匹配进程。Serena 环境最终约 632 MB,复用 Python 3.13.7,无全局 PATH 修改、无全局 Serena 启用。 -- 首轮 0.12.5 全量回归和 stdio 通过,交付阶段因检查运行期间补充 package.json 维护命令触发源码/构建指纹不一致而失败;这是有效的一致性保护。冻结变更后完整重跑,不绕过交付校验。失败报告 2026-09-08T13-40-56-234Z-core。 -- 当前 Codex 旧连接仍需客户端重连;没有提供可调用的重连接口,不用结束 Codex/强杀 Gateway 冒充成功。作者自审,未新增独立审查或子代理。 - -## 2026-09-08 21:56 — 当前架构、数据流与检查关口说明(北京时间) - -- 根据 0.12.5/main 10496e0 源码核对启动、Gateway/Registry、Router、Context/Response、Serena/Repomix/FlaUI、原生 Host、工作区/缓存/资源生命周期和交付脚本,新增根目录 WinCode-架构与数据流说明.md,README 增加入口。 -- 五张 Mermaid 图覆盖分层、请求时序、代码证据、桌面取证、构建到连接;配套数据存放表与 G1–G11 检查关口表。区分运行时校验、测试约束、远端保护、客户端授权,明确未知字段容忍、内部/外部 candidateFiles 语义、最终序列化覆盖、候选与运行时绑定、缓存与进程 RSS 等边界。 -- 本轮 GitHub 只读回查 main 保护仍生效:strict Node 22/24 与三项 CodeQL,对管理员生效,approval=0;未改变远端设置。架构边界以源码为据,不用历史 README 或测试总数代替实现。 -- 自审补充当前限制:Router 职责集中、错误响应尚非单一格式、不同文件路径各自校验、重构计划不执行修改、客户端需自行重连。文档为独立架构参考,不扩展生产功能。 -- 验证:文档本地链接、代码围栏及五个图块检查通过;git diff --check 通过。未运行代码回归,未安装 Mermaid 渲染器,未把文本结构检查写成视觉渲染验收。 - -## 2026-09-08 22:12 — Markdown 当前状态同步(北京时间) - -- 按用户要求核对 14 份项目 Markdown,将两份根目录计划收敛为 0.12.5 基线的未完成工作;移出已实现的 R1–R6/WP1–WP5 步骤,历史留在版本记录、工作日志和 Git 中,不另建归档计划。 -- 新待办为工作区切换失败一致性、trash 部分完成恢复、有界混合负载、错误契约渐进整理。前两项是静态风险、尚待故障注入;重要恢复策略和公共接口列出待决事项,没有实施或承诺全局重构。 -- 更新双语 README 导航及锁定构建入口、Skill/MCP 安装维护指南、Host 协议和截图限制、贡献指南、架构说明与 0.12.5 验收记录。修正 Host“零副作用”和截图成功保证等过度表述。 -- 回查本地最终回执:main 10496e0、核心回归 313 通过/1 可选跳过、真实 Serena 七项通过、告警 #1 fixed、分支保护已启用。保留客户端重连、真实 Repomix 包验收及完整成本对照的证据缺口;未重新把旧宿主连接记作最新版本。 -- SECURITY 与四份规范 Skill 已符合当前版本和容忍策略,保持正文;skill:check 实测四份安装文件一致,无需重复部署。补修历史日志中两处已删除 v0.6 方案的链接,改指 Git 中已确认存在的原提交,仅修链接、不改当时事件结论。 -- 验证:14 份 Markdown 的本地链接与围栏检查、规范命令对照、旧状态扫描、git diff --check;复核既有测试报告,不将其计作本轮新回归。文档外链未做在线存活检查,Mermaid 未重新渲染;本轮没有代码、依赖、环境或远端变更。 - -## 2026-09-08 — 文档更新上传授权(北京时间) - -- 用户要求上传至 linnnn89/WinCode;沿用既有 PR、必需检查通过后合并流程。本次仅提交当前九份 Markdown 变更,不变更软件版本或运行环境。 -- 上传前确认 origin 地址正确、本地 main 与 origin/main 一致、无其他打开的 PR,git diff --check 通过。实际远端检查及合并结果以该 PR 回执为准。 - -## 2026-09-09 09:57 — 拉取最新版与架构建议计划对照(北京时间) - -- 用户要求拉取 linnnn89/WinCode 最新仓库并根据“架构分析优化建议”总结下一步计划。确认精确根目录 D:/CODEX PROJECT/WinCode MCP、origin 地址和干净 main 后,执行 git pull --ff-only,从 580e75a 更新至 bbc20ffe99d34842bc68aac213d5fe6989327118(0.12.5);HEAD 与 origin/main 差异计数 0/0。 -- 读取引用对话的两轮完整问答,对照 Registry、CI、代码证据、项目图/影响分析、UI 源码候选、缓存 watcher 和既有 E1–E4 计划。发现多项建议已有基础实现,更新既有计划书的核对基线并增补差距表、执行顺序、验收与待决范围。 -- 建议先做 E1–E3 可靠性验证,再通过 E4 渐进整理契约,之后验证有限语义关系与更深 UI 候选链;增量索引按性能证据决定。明确候选映射不等于运行时绑定、引用不等于调用图,未把历史测试数字作为本轮结果。 -- 本轮仅同步仓库和整理计划;没有修改生产代码、安装依赖、运行构建/回归、控制目标应用、重连客户端或推送远端。计划实施和重要路线仍待后续授权。 - -## 2026-09-09 — 启动 E1/E2 故障注入,依赖同步待确认(北京时间) - -- 用户要求开始进行。新增 scripts/verify-failure-recovery.ts,设计 11 个工作区切换阶段/取消用例及 2 个 trash 失败用例;禁用外部适配器,使用 test-tmp 下唯一生成目录,记录根、会话、watcher、请求准入与文件实际位置,不将缺陷行为固化为通过的回归断言。 -- 实际执行 node node_modules/tsx/dist/cli.mjs scripts/verify-failure-recovery.ts,在模块导入阶段因 ERR_MODULE_NOT_FOUND: @modelcontextprotocol/client 退出;13 个用例均未执行,未复现或修复任何故障。Node 实测 v24.19.0;已有 node_modules 不满足拉取后的 0.12.5 依赖。 -- 锁文件为 v3,含 46 个包条目(包括平台可选包),核心 client/server 均为 2.0.0;安装脚本标记出现在 esbuild/fsevents。建议在项目根执行 npm ci --no-audit --no-fund,同步锁定依赖;需要网络及本地 node_modules 重建,实际下载量未核实,不涉及全局安装。 -- USER_DECISION_REQUIRED:用户协作契约第四节要求安装依赖事先确认,本次开始实施不明确包含依赖同步;先请求上述操作授权。生产代码未变更,故障脚本运行验证未完成;git diff --check 通过。 - -## 2026-09-09 10:22 — E1/E2 修复及 E3 小样本(北京时间) - -- 用户授权项目内重建依赖;npm ci --no-audit --no-fund 成功安装 20 个当前平台适用包。npm 提示 esbuild postinstall 未获 allowScripts 授权,本轮未修改其脚本授权;现有平台包足以执行 tsx 和编译。package-lock.json 未变更。 -- 修复前故障报告 test-tmp/failure-recovery/run-MaSxLX/report.json:13 个用例、15 条症状记录(不是 15 个独立缺陷)。用户随后明确选择:变更前失败保留旧根,变更后失败阻止业务请求、重新打开恢复;trash 部分完成保留实际位置,不自动移回,并保留旧响应字段。 -- ToolRouter 记录恢复状态,覆盖根准备后的取消和各绑定阶段失败;MCP 返回 WORKSPACE_RECOVERY_REQUIRED,hello 被动可读,同根重新打开也完整初始化,恢复失败保持阻止。Workspace 返回 completed/not_moved/partial 与失败阶段,目录准备/移动失败不声称已有目标;元数据失败保留实际路径。同名文件加入 UUID 避免同时间戳目的路径碰撞,不实施自动回滚。 -- 新增 tests/failure-recovery.test.ts 并纳入 npm test 清单;更新仓内代码/诊断手册、既有计划与路线图,未同步全局 Skill 或重连客户端。13 个原故障用例修复后报告零问题(test-tmp/failure-recovery/run-zRs9nX/report.json)。回归测试首次 8 项失败来自错误预期 resetConnection 只调用一次;核对 Serena.initialize 会 dispose/reset 后,修正为现有两次调用事实,未改变生产生命周期。随后 37 项针对性测试通过;增补同名碰撞测试后的故障恢复与 Skill 测试 16/16 通过,最终 typecheck 通过。 -- scripts/verify-mixed-load.ts 小样本:test-tmp/mixed-load/run-cE5x6P/report.json;80 次记录的 Core 操作、10 轮、10 个真实 Node 模拟上游进程,963 ms,结束时无自有子进程残留。未发现被断言检查的跨根证据/占用泄漏;RSS 134→137 MiB、heapUsed 34→42 MiB,未观察稳定平台,不宣称无内存泄漏。真实 Serena/Repomix、Windows 句柄和子进程 RSS 未覆盖。 -- npm run check 实际通过 typecheck、build-gateway,restore-host 因 SDK 10.0.303 缺失退出;报告 test-tmp/check/2026-09-09T02-18-41-301Z-core/report.json。未修改 global.json,本机已装 10.0.302。生产 stdio 实测通过(15 工具、契约及构建身份一致),不代表 Codex 当前连接或 UI Host 已更新。 -- 全量 npm test 实际为 327 项、324 通过、3 失败(日志 test-tmp/recovery-regression-20260909-101859.log;此时尚未添加最后的同名碰撞用例)。两项 C# 夹具明确受 SDK 缺失阻塞;FlaUI 缓存健康测试依赖 Release Host 的路径解析,当前产物缺失,断言 available=true 失败。保留失败,不跳过或弱化测试。E1/E2 生产代码已实现,但完整交付关口尚未通过。 -- USER_DECISION_REQUIRED:为完成交付检查,已询问是否可在项目内隔离安装锁定 SDK 10.0.303 并恢复 NuGet 依赖;等待答复。没有全局环境修改、版本发布、Git 提交/推送或远端变更。E4 其余契约统一及语义/UI 深化仍未实施。 - -## 2026-09-09 10:34 — 项目隔离 SDK 与核心交付关口完成(北京时间) - -- 用户明确授权项目内隔离安装 SDK 10.0.303 并恢复锁定 NuGet 依赖。先按 [微软安装脚本文档](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script) 获取官方脚本,指定版本、x64、项目 .deps/dotnet-10.0.303 和 NoPath;脚本长时间未进入可观察的下载阶段,停止该自有安装进程,未反复重试。 -- 官方 releases.json 确认 ZIP 地址 https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.303/dotnet-sdk-10.0.303-win-x64.zip。使用 curl 有界下载 297570534 字节,SHA-512 与官方元数据一致:ad4ef6202e55babde1c65e1be7b468c3ecb738ccc2fb8bd3c2bb408a4f45d247c8c5a5f57fedc54ebee7cb5fc4f487772997f2d053010d2e1903b974cc64216d;解压至同一项目目录,dotnet --version 实测 10.0.303。 -- 验证子进程环境:DOTNET_ROOT/DOTNET_ROOT_X64 指向项目 .deps/dotnet-10.0.303,PATH 仅在当前 PowerShell 子进程前置该目录;DOTNET_CLI_HOME、NUGET_PACKAGES、NUGET_HTTP_CACHE_PATH 分别指向项目 .deps/dotnet-cli-home、.deps/nuget-packages、.deps/nuget-http-cache。设置 CLI 遥测退出、关闭 ASP.NET 证书生成与全局工具 PATH 添加;未修改系统 SDK、持久 PATH、global.json 或包锁文件。 -- 执行 node scripts/check.mjs(npm run check 的实际入口),12 个阶段全部通过:typecheck、Gateway 编译、3 项 locked restore、Host publish、2 项夹具 build、核心回归、生产 stdio、交付清单生成及校验。报告 test-tmp/check/2026-09-09T02-32-50-227Z-core/report.json;328 tests / 328 pass / 0 fail / 0 skipped,耗时约 60 秒。此前 3 项环境相关失败均消失。 -- 交付清单 matched=true,contentId=d70c6fe34050340486b82b79a2c8a232e4d0f8e54818cf5e18e1b25ebe77b35e。更新既有计划与路线图,E1/E2 标记本地核心交付验收完成;E3 真实上游/更长采样、E4 其余契约及语义/UI 方向仍待后续工作。 -- 本轮未修改生产代码或测试,未运行不相关的真实桌面闭环、全局 Skill 部署、客户端重连、远端 CI 或 Git 提交/推送。普通新终端仍默认使用系统 SDK;重跑检查须在其子进程中使用上述项目 SDK 环境。git diff --check 通过;核心验收不等同真实桌面、上游兼容性或长时间稳定性证明。 - -## 2026-09-09 10:54 — 复核问题获准修复与完整复验(北京时间) - -- 用户先要求只读复核,再明确“同意修复”。隔离复核报告 test-tmp/review-d92e747f05e04a0e8ca7d17bd66db159/observations.json 证明:内部 client.close 一次失败后,3 次 workspace_open 均失败且 close 只执行一次;190 字符文件名移动后元数据失败、200 字符无法移动,旧命名均成功;底层 fs.watch 创建失败仍曾允许切换提交。复核阶段未修改生产代码。 -- 获准后新增 GatewayRestartRequiredError 表达被保留、无法在原实例恢复的清理失败。Serena 内部关闭失败及 watcher 关闭失败返回 restart_gateway;ToolRouter 对永久状态直接拒绝重复 workspace_open,保留原会话与失败信息。提示用户先检查 Gateway 自有资源清理后按客户端正常流程重启;未自动重启、清除失败记录或终止目标应用。一般绑定失败仍允许重新打开恢复。 -- bindWatch 及切换提交前检查实际 watcher 绑定;原生创建失败和初始化期间异步 watcher 错误均进入恢复状态。原 watcher 关闭失败与创建失败分别测试,不以替换整个 bindWatch/resetConnection 代替底层异常验证。 -- trash 保留 UUID 防碰撞,展示用 basename 按完整 Unicode 码点及 UTF-8 字节预算截短,为 .meta.json 预留空间;去掉截短后尾部点/空格,完整原路径继续保存在响应与元数据。测试覆盖 183、184、190、193、194、200、220、255 字符 ASCII、中文及 emoji,逐项验证正文、元数据和源文件位置。 -- 改写 verify-mixed-load.ts:在真实自有 Node 模拟上游 RPC 已开始后发起切换,用受控调度确认旧根与请求占用,再触发运行中取消或观察上游退出。5 轮取消、5 轮退出,共 70 次 Core 操作、10 个自有进程、1934 ms,全部交错断言通过且无自有进程残留。报告 test-tmp/mixed-load/run-ftSuQB/report.json;只证明受控交错,不宣称耐久性、真实 Serena/Repomix 兼容或无内存泄漏。 -- 针对性恢复/watcher 测试 23/23,typecheck 通过。第一次完整检查 test-tmp/check/2026-09-09T02-52-33-897Z-core/report.json 为 333 项中 332 通过、1 失败:stage1-cleanup 的关闭时序夹具把 bindWatch 置空,不符合新增实际绑定校验。移除空 mock、使用隔离目录真实 watcher,保持原时序/拒绝/清理断言;该文件 11/11 通过,未弱化生产检查。 -- 最终完整检查 test-tmp/check/2026-09-09T02-54-08-398Z-core/report.json:333/333、0 失败/0 跳过,生产 stdio、锁定构建和交付清单均通过。contentId=0b2dd10f93c67daee51a764429d7c9d30028c113b0f31d22a162f87471c91bbb,matched=true。沿用项目隔离 SDK/缓存环境,未增加依赖或修改版本锁。 -- 更新仓内代码/诊断手册及现有计划、路线图,未部署全局 Skill、重连客户端、提交/推送或运行真实桌面检查。E1/E2 复核补修完成;E3 真实上游与长期趋势、E4 其余统一契约及语义/UI 深化仍属后续工作。git diff --check 通过。 - -## 2026-09-09 11:20 — E3 环境与真实验收、E4 盘点(北京时间) - -- 用户要求继续完成待办,随后明确“补齐环境”。保留现有架构与 E3 100 次/5 分钟预算;没有启动新架构、独立 Agent 或长期压力服务。E4 涉及公共字段,先完成 10 个响应样例和兼容方案,再通过问题请求确认,当前仍待答复。 -- verify-mixed-load 增加可选间隔、指定自有 PID 的 Windows Get-Process 句柄/工作集/私有字节采样及逐轮进度。实际 70 次 Core 操作、10 轮、97235 ms,报告 test-tmp/mixed-load/run-NUo0VL/report.json;采样全部可用、无缺失 PID,每轮结束句柄 234,dispose 后 233,无自有上游残留。工作集前两轮从约 119.8 降至 107.2 MiB,第 2–9 轮约 103.4→105.2 MiB;小幅增长不能判为泄漏,也不能证明长期平台。自有子进程工作集约 53.9–56.4 MiB,采样进程同步结束;不是持续高负载或峰值 RSS。 -- 隔离安装:uv 复用已有工具,下载 Python 3.13.15 到 .deps/python;Serena v1.7.0 tag 与 commit 949a27ef1e5fda1a6e7b561e777bcece345c6ffd 一致,uv sync --frozen --no-dev --no-editable 安装 75 包到 .deps/serena-venv,缓存 .deps/uv-cache。Repomix 1.18.0 使用项目子目录 npm install --save-exact --ignore-scripts --no-audit --no-fund,171 包;真实压缩验证证明本次不需要执行安装脚本。两个依赖锁及版本/目录大小回执在 .deps/environment-receipt-20260909.json;主 package-lock、global.json 未改。 -- 首次 Serena 直接 exe 连接后查询 15 秒超时,报告 test-tmp/serena-acceptance/1788923155570-17692/report.json。查明 SDK 子进程默认白名单不传 SERENA_HOME/DOTNET_CLI_HOME 等;上游在用户目录新建 .serena。新增 scripts/serena-isolated-launcher.py,在导入前明确设置项目路径;只改变测试启动环境,不扩大全局环境继承或生产公共接口。根据上游日志“configuration file not found, autogenerating”、全部文件的创建时间与精确两文件清单核对,将本次 .serena 归档 .deps/serena-first-attempt;确认用户目录路径不再存在。未删除或覆盖用户已有配置。 -- 通过该启动器执行 project index 预热专用生成 C# 项目。Serena 使用其固定 Roslyn 5.5.0-2.26078.4 和 SHA-256 校验下载;预热成功。后续测试保留生产默认超时,没有为通过验收加大超时。新组件加缓存逻辑文件大小 751061663 字节,约 716 MiB(包括可能重复计数的硬链接,不是物理磁盘占用;不含此前 SDK/NuGet)。 -- 真实 Serena 原 7 项通过后增补 Router A→B→A。初版用 Marker 查询 Marker0 时命中文件节点,改为不同文件中的同名精确类;下一版把回到 A 的合法缓存命中错误地要求产生第 3 个进程,改为同时检查重复查询及每轮新的 Probe 查询,保留跨根文件名断言并确实触发 3 次连接。最终 8/8 通过,报告 test-tmp/serena-acceptance/1788923903497-28124/report.json;自有 PID 均退出。 -- 新增 verify-repomix-real。首个生成根受到父仓 test-tmp 的 Git ignore 影响导致空包,增加独立 git init 后确认两个文件确实打包;发现生产使用 /File: |`,有界读取用户显式指定的配置;需 enabled/项目求值许可、根内入口 csproj、明确 Configuration/TFM、已有 dotnet/Host 路径。只在配置启用时选 Roslyn,hello 不求值;不自动采用仓内配置,也不通过普通工具参数选择可执行程序。Host 子进程单独设置匹配的 DOTNET_ROOT/DOTNET_HOST_PATH,不改全局环境;Roslyn 路径不初始化/查询外部 Serena,显式文件范围的文本能力仍复用本地解析。 -- 保持 15 个 MCP 工具名。符号搜索返回 source=roslyn、location;引用工具新增可选 symbolLocation={snapshotId,project,file,position},原 symbolName 保留并核对匹配。重载/同名返回候选,旧 Serena 序号身份明确拒绝。Host v2 新增 symbols,按实际语义符号去重 partial;指定文件范围时选择该文件中的声明,避免跳到范围外的另一半声明。两类查询提供 semanticContext 的范围、检查点与排除数;准确调用 span 不等于全仓完整性,影响报告保留 UNCERTAIN/UNKNOWN。 -- 生命周期:编辑后旧身份失败,下一次显式搜索才重载,失败业务请求不自动重放;工作区同根重开/A→B→A 关闭旧 Host。传输有界帧、ID/信封校验、取消监听清理与 1 秒协作宽限,超时/初始加载未响应可硬回收。真实关闭/协议错误在进程退出后仍保留清理失败,进入 E1 restart_gateway 阻止后续业务;纯关闭超时在确认硬回收成功后可完成释放。初次项目加载失败按预期失败启动清理,不能把退出码 1 错判成永久恢复失败;损坏项目修复后可显式搜索恢复。 -- Windows Host 在初始化 MSBuild 前加入自有匿名、不继承句柄的 Job,KILL_ON_JOB_CLOSE 在 Host 退出/崩溃时回收继承的后代;句柄保持到进程结束,避免在关闭确认前误杀自身。依据微软 [Job Objects](https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects) 与 [JOBOBJECT_EXTENDED_LIMIT_INFORMATION](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-jobobject_extended_limit_information),未增加 P/Invoke 包或第三方服务。此为自有资源机制,不能约束自定义 targets 通过外部服务创建的进程,也不是项目执行沙盒。 -- 失败与修正:首批 62 项定向回归有 1 项失败,ImpactAnalyzer 把额外 undefined 传给旧 Serena 三参数调用;改为仅有 Roslyn 定位时使用第四参数,原断言保持,相关 40/40 后通过。MCP 首两轮 [run-3dIsQZ](../test-tmp/roslyn-gateway/run-3dIsQZ/report.json)、[run-yb8YjT](../test-tmp/roslyn-gateway/run-yb8YjT/report.json) 误把 Gateway 自己的 conhost 计入切换时必须退出的 Host 树;核对父进程后,改为切换检查 Code Host 子树、最终退出检查整个 Gateway 树,仍逐一核对 PID/创建时间。之后 12 场景中间回执通过,最终增加加载失败修复场景至 13;不把中间数量相加。 -- 最终 `npm run test:roslyn-gateway` [回执](../test-tmp/roslyn-gateway/run-nP7SpF/report.json) 13 场景全部通过。使用生产 dist/index.js 与新的 SDK stdio 客户端,生成 A/B 两套项目夹具,覆盖精确重载、partial 文件范围、TS 显式上下文、错误位置/旧身份、编辑、A→B→A、初始加载损坏与修复、真实 MSBuild 中取消/崩溃/超时和关闭。后三项各观测 7 个实际自有进程(Host、BuildHost、受控 target 的 cmd/node 及控制台),均确认退出,恢复搜索成功;无 Serena/Python 启动。受控 target 只运行夹具自有等待脚本,不执行目标应用。 -- 最终 `npm run check` [回执](../test-tmp/check/2026-09-09T05-41-07-067Z-core/report.json) 12 阶段成功,344/344、0 失败/取消/跳过;锁定恢复、UIA Host 构建、生产 stdio、15 工具 schema 与现有交付清单验证通过。buildId=1c62d2c8e4b10ff345eba1721745a81998368ef3413eef399b000b9fbdac1aa6,delivery contentId=7bafc6bc5d8eb872618bc4cc2b25352491a5a43c1c0d6fe22d0304429e8084ee。现有清单仍是 Gateway/UIA Host/受管 Skill,不把其成功冒充 Code Host 正式交付。构建使用项目内 SDK 10.0.303 与缓存,仅当前命令环境生效。 -- 本轮早期独立 Host [42 场景回执](../test-tmp/roslyn-host/fixture-whR9f2/report.json) 通过;其后的 scoped partial 与接入修订由上述最终 MCP 场景和核心回归验证,未声称旧 42 项在最终状态重跑。代码/接口新增中文说明,按 skill-creator 同步 SKILL.md、references/code.md、references/diagnostics.md;Skill/工具/Roslyn 定向测试 18/18 与 Python UTF-8 quick_validate 通过。未找到既有受查 Skill 安装目标,没有新建全局安装或声称当前 Codex 已加载新版。 -- 反证自审:引用位置准确但加载图缺少反向依赖/生成器时仍不能判安全删除;因此 semanticContext 与 queryComplete=false 保留,ImpactAnalyzer 只提供已证实的有界证据。Host 崩溃时只确认父进程退出不足以证明释放,因此测试在真实 MSBuild target 阻塞期间采样 BuildHost 和后代再验证退出。初始加载失败也不能误触发永久清理失败,最终 MCP 增加损坏/修复实证。以上是作者自审,未进行独立模型/人工审核。 -- 更新既有计划、路线图和本日志。下一阶段为 E4 公共错误迁移、Code Host/Roslyn/BuildHost 正式打包与指纹、无 Serena/Python 干净环境及实际客户端验收,再推进默认后端迁移。当前 SDK 风格单入口/配置/TFM、ProjectReference 可达图和生成夹具证据,不涵盖任意 `.sln`、外部自定义输入/服务创建进程、非 Windows 或真实用户项目。E4 文本兼容及真实项目求值政策仍需在对应范围确认;本轮已有接入授权无需重复申请。 -- 收尾核对生产 ToolDefinition 中的实际工具名,并修正计划新增段的简称;3 份计划/日志的代码围栏、18 个本地链接检查通过,`git diff --check` 通过。仅补记非受管文档后再次 `npm run delivery:verify`,上述 contentId 仍 matched=true;没有因文档记录重复执行全套构建或扩展测试范围。 - -## 2026-09-09 — 当前工作版本提交与推送准备(北京时间) - -- 用户明确要求将当前版本推送 GitHub;纳入当前 README、稳定性/恢复机制、Roslyn Host/Gateway 接入、验证脚本与既有文档改动,不升级版本号。目标 origin/main。 -- 推送前 npm run typecheck 通过,git diff --check 通过;43 个待提交文件的常见凭据标记及大文件筛查未发现命中,构建产物和测试临时目录由现有忽略规则排除。 -- npm test 执行失败:日志报告当前可发现的 .NET SDK 为 10.0.302,缺少 global.json 锁定的 10.0.303,ui-query-check 无法启动。未安装 SDK、放宽版本锁或将历史通过结果作为本轮验证。日志位于本地 test-tmp/pre-push-tests.log。本次推送保存当前工作版本,不表示完整回归或发布验收通过。 - -## 2026-09-09 — Pro 最新分析对照复核与下一轮规划(北京时间) - -- 目标:结合用户提供的网页版 Pro 全文、当前实现和 GitHub 一手经验,复核架构与下一轮优先级。会话读取工具不可用后由用户补充附件全文,现已完成对照;没有把预览截断处当成完整结论。本轮只修改既有计划/路线图/本日志,并在 test-tmp 创建隔离探针与回执,没有修复生产代码或替用户批准公共接口/输入政策变化。 -- 基线:[PR #30](https://github.com/linnnn89/WinCode/pull/30) 已于 13:56:43 合并,远端 main 为 `2235a4200c117a1c1389afce1fecd90c45908a73`。本轮开始时本地 bbc20ff 加工作区修改,收尾复查已为 `codex/current-version-20260909@a2d76f6` 且本轮编辑前干净;保留上节提交准备记录。本轮未运行提交/推送/分支切换。两次核对 13 个关键生产/构建文件 Git blob,均与远端基线一致;[比对回执](../test-tmp/review-20260909/pro-baseline-comparison.json)保留初始 HEAD 与各文件哈希,不声称整个工作区完全相同。 -- Pro 路径问题已用实际 ImpactAnalyzer 加受控提供方复现:[探针](../test-tmp/review-20260909/impact-identity-audit.mjs)、[回执](../test-tmp/review-20260909/impact-identity-report.json)。四个外部引用文件全部出现在 affectedFiles,但同名 Service.cs 和后缀 NewService.cs 被组件摘要遗漏,两个 Handler.cs 合为一个;不同启动目录下工作区内绝对目标无法解析。此证据验证实际聚合逻辑,不冒充真实 Roslyn 提供方验收。 -- [真实 Roslyn/MCP 探针](../test-tmp/review-20260909/roslyn-post-integration-audit.mjs)及[回执](../test-tmp/review-20260909/roslyn-audit-ebBSx0/report.json)仅使用现有构建、项目内 SDK 10.0.303 和生成项目;夹具 NuGet 源清空,未新增持久依赖或执行用户真实项目。正常 Save 引用有一处、source=roslyn,但重构建议误称文本降级和查询中断;新增无关 README 使旧定位 SNAPSHOT_STALE,生成无关 33 MiB 文件使查询 INPUT_BUDGET_EXCEEDED,该文件已移除。 -- 新增正确性发现:显式 CodePage=1252 的 Café 类在 dotnet build 中 0 警告/错误,Host 搜 Café 得到零结果和非法字符诊断,搜 Caf 却返回错误类名。源码核对指向 WorkspaceSession 冻结文档时强制 UTF-8,故提升为优先修复;不把这种精确位置结果视为正确符号的充分证据。 -- 反证结果:原先怀疑 Gateway 非正常退出可能留下 Code Host/BuildHost。受控 MSBuild 阻塞期间,Gateway 强制退出和客户端关闭两场景各观测 9 个相关进程,3 秒后均无残留,无额外清理动作才通过的情况。此次没有复现孤儿进程问题,不能据上游故障帖子宣称 WinCode 存在该缺陷。源代码显示上游互斥等待未受 Host 队列上限覆盖,但尚未负载验证,只登记为有界探针候选。 -- 实际 Codex 连接于 14:39 被动 hello:[回执](../test-tmp/review-20260909/current-client-1439.json)。版本 0.12.5,instanceId=`26920008-5f2c-40d5-85c9-ef63a3e41e6d`,buildId=`1c62d2c8e4b10ff345eba1721745a81998368ef3413eef399b000b9fbdac1aa6`,schemaHash=`d685329e95f1cc93087ea3f62ff96d1647e93dc0c4ad9f683d476ce79b8f3bdc`,15 工具;当前 codeProvider=serena、mode=degraded、semanticQueryUsable=false,Roslyn 未启用。更新旧客户端 0.11.2 待办,区分身份已验证与 Roslyn 功能未验收,未修改用户客户端配置。 -- 架构意见:保留现有 Registry/Router/CodeQueries/Adapter/独立 Code Host,不新建能力注册平台、全局图或数据库。清理对象限定为旧 Serena 专用判断/中性依赖命名、纯文本解析耦合、无用注入。ArchitectureAnalyzer 的 `_queries` 尚未使用,不能把静态项目声明图称为已经实现的 Roslyn 语义架构图;不同用途的 watcher 暂不合并。 -- GitHub 参考:[Serena #1718 维护者限缩故障范围](https://github.com/oraios/serena/issues/1718#issuecomment-5033051492)与[同步归属讨论](https://github.com/oraios/serena/issues/1718#issuecomment-5032705578)、[csharp-ls #401](https://github.com/razzmatazz/csharp-language-server/issues/401)、[VuDZ/RoslynMcpServer](https://github.com/VuDZ/RoslynMcpServer)、[MadQ 作者实测](https://github.com/MadQ/RoslynMcp/blob/dev/docs/battle-test-results.md)。仅采纳具体职责和验证方法;不照搬 AddDocument、单一版本失效或他人的性能数字,也不把模型诊断/源码对应关系当成构建或运行时因果证据。 -- 计划更新:A1 先修路径与来源/覆盖说明;A2 修编码及无关大文件阻断,明确输入集合并保留真实依赖失效;B 把真实脚本接入 CI、统一 SDK 选择、补 Code Host/BuildHost 清单及实际客户端验收;C 贯通可选 symbolLocation,并同步中文注释、Skill 和契约。E4 独立待决;全面性能优化与 WPF 精确导航在这些步骤后按实测需要进入。 -- 验证边界:本轮运行上述隔离诊断并完成清理,没有重跑整套 npm check。344/344 和 MCP 13 场景仍是前一接入阶段回执;另一个提交准备流程的默认 npm test SDK 发现失败已核对原日志,纳入 B,不删历史失败、不放宽 global.json、不宣称本轮默认环境通过。Pro 与本轮为不同证据来源,本轮反证自审仍不等于独立模型/人工审核。 -- **USER_DECISION_REQUIRED:** 本次为复核与规划,新的修复尚未实施。A2 输入覆盖政策、C 公共参数扩展、E4 文本策略、B 的可选交付形态及实际项目求值范围须在对应实施前明确;已批准直接 Roslyn 方向及项目内依赖权限不重复申请。下一步推荐从 A1/A2 开始,不为完成规划额外申请安装、客户端控制或发布权限。 -- 文档收尾:三份文件的代码围栏、新增的 10 个本地链接和 git diff --check 通过,13 个关键实现/构建文件哈希仍与复核基线一致,受管改动仅这三份文档。范围检查首次因 Git 默认将中文路径转义而误报;改用 NUL 分隔的原始路径输出后通过,未放宽范围断言。测试探针/回执仍在既有忽略目录 test-tmp,不列入发布包。 - -## 2026-09-09 — A1/A2 正确性与可补充输入实施(北京时间) - -- 授权与基线:用户要求开始,随后选择“编译相关输入之外允许显式补充文件”。只读 fetch 更新 origin/main 到 2235a42,确认其树与本地原 a2d76f6 一致后建立 codex/roslyn-correctness,保留三份已有计划修改。没有提交、推送、全局安装、客户端配置修改或真实用户项目求值。 -- A1:ImpactAnalyzer 改用工作区完整文件身份、可用项目身份作聚合键,名称只展示;修正绝对路径、分隔符/大小写、同名/后缀文件及链接源码组件计数。RefactorAssistant 不把正常 Roslyn 有限结果称为文本回退或中断;未解析的 Roslyn 结果不再附加正则回退说明。Roslyn 已知加载/查询/清理失败按现有 AdapterLastError 汇总,清理失败保留不可恢复标记,不主动探测上游。 -- 编码:WorkspaceSession 的冻结文本使用 document.GetTextAsync 所提供的 Encoding,遵循 MSBuild CodePage/BOM;四种生成夹具验证 Café 符号、引用和 UTF-16 偏移,未改写用户源码或引入编码依赖。新增函数/接口与关键身份、缓存语义补充中文注释,Program 的“尚未接入 Gateway”过时说明已修正。 -- A2:WorkspaceInputs 采用约定输入候选 + 实际文档/AdditionalFiles/分析配置/程序集 + 祖先配置 + 显式 additionalInputs。非标准 Import 和隐式数据须显式补充,未声称自动识别所有依赖。自动发现 .cs 后仍由 MSBuild Compile 决定加载;未加载的普通二进制和 README 不计输入字节。目录枚举和输入预算保留,必要输入超限不截断。每个文件流式散列,仅冻结源码保存正文,global.json 签名单独保留,不把 Files.Count 当成总输入数。 -- 配置与边界:additionalInputs 最多 32 个根内相对文件,JSON 最长 4096;Node 与 Host 均校验字面路径、重复及边界,Host 验证链接和每次存在性。缺失阻断重载,恢复后显式搜索;切换根后按新根解释。只经显式启动 JSON/原生 argv 传递,不接受 MCP 工具参数或自动读取目标仓库配置。ready 的 inputPolicy.version=1 和实际列表必须匹配,旧/漏配 Host 拒绝并清理,协议 v2 和 15 个工具名不变。 -- 针对性失败:首轮 36 项有 1 项因 Windows 大写文件名推导符号名而失败,修正匹配后 36/36。中间 [Host 回执](../test-tmp/roslyn-host/fixture-fif5GD/report.json)在恢复补充文件后于第 23 场景返回 INPUTS_CHANGED;当时信息未区分指纹与事件,不能事后断言唯一根因。[八轮隔离恢复](../test-tmp/review-20260909/input-race-kqPHuT/report.json)未再次复现。保留原失败,拆分错误说明,增加每次加载前最多四个 50 ms 的事件稳定观察窗;没有增加业务重放或放松加载后检查。 -- 反证自审:非标准 custom.rules 真正作为 MSBuild Import 改变条件与引用数;AdditionalFiles 的非标准后缀仍被跟踪;丢失补充项后 reload 不得通过过滤旧文件而丢掉要求。实际 MSBuild Touch 在内容哈希不变时仍触发拒绝,证明稳定等待没有吞掉求值期间的事件。移除该写入 target 后可显式恢复。此为作者自审,不冒充独立审核或全磁盘原子一致证明。 -- 验收:[核心回执](../test-tmp/check/2026-09-09T07-20-21-008Z-core/report.json)12 阶段通过,350/350、0 失败/取消/跳过,覆盖最终 TypeScript、类型检查、锁定构建、生产 stdio 和现有清单。其后 Host 等待补修由[最终 Host 58 场景](../test-tmp/roslyn-host/fixture-sDJxDM/report.json)及[最终真实 MCP 16 场景](../test-tmp/roslyn-gateway/run-fLDNDI/report.json)验证,构建 0 警告/错误;MCP 覆盖补充配置端到端传递、无关 33 MiB 文件、缺失/恢复、绝对目标、有限重构说明、A→B→A 和真实 MSBuild 取消/崩溃/超时后已观测自有进程退出。中间 46/56/14/16 结果不累加为终验数。 -- 环境与交付:复用现有项目内 SDK 10.0.303、锁定 NuGet 缓存及已有 Node 依赖,仅当前测试子进程设置 SDK 路径,不改系统 PATH/global.json。先前默认入口发现不到 SDK 的失败仍保留,B 才统一普通入口/CI 选择。仓内 SKILL.md、code/diagnostics 手册、计划和路线图同步;未部署全局 Skill 或声称当前 Codex 已加载新实现。正式清单仍不含 Code Host,不能用现有 delivery matched 代替 B。 -- 资料核对:[MSBuild 增量构建](https://learn.microsoft.com/en-us/visualstudio/msbuild/incremental-builds?view=vs-2022)与[Exec](https://learn.microsoft.com/en-us/visualstudio/msbuild/exec-task?view=vs-2022)用于解释自定义输入无法凭扩展名穷尽;编码 API 根据已安装 Roslyn 5.9.0 XML 契约和真实结果核对。没有下载新工具或增加依赖。 -- 本阶段 A1/A2 已完成;B/C、默认迁移、E4 仍未实施。输入补充方案已经批准,不再重复询问;后续 E4 文本策略、实际项目求值范围和正式交付形态按对应阶段对齐。持续写入仍可能拒绝,未列入的自定义输入、生成器、运行时动态调用和全规模性能不在本轮保证范围。 -- 收尾检查:六份变更文档的围栏及新增 19 个本地链接有效,Skill quick_validate 和 git diff --check 通过。重新生成清单时默认 dotnet 路径再次复现锁定 SDK 发现失败;改为本轮已使用的项目内 SDK 命令环境后生成/验证成功,未声称修好了默认入口。最终 contentId=`a0298c62eaf3c51710befaed42d1a96087902fe78371947645ad74b98184b4ab`,matched=true,回执 `test-tmp/roslyn-input-delivery.json`;仍只证明既有清单范围。最后的文档/注释更新没有改变已验收的功能逻辑,不重复扩展测试预算。 - -## 2026-09-09 — 交付、去 Serena 与职责拆分实施(北京时间) - -- 用户要求将前轮 A1/A2 后续的 B/C 与巨型模块问题逐项处理;没有把握时暂停对应事项询问。 -- 已明确三项产品决定:彻底退役外部 Serena;未提供 Roslyn 项目配置时以本地文本模式启动并说明语义未配置;文本 source 改为 local-text,旧调用方需迁移。版本拟同步为 0.13.0。 -- 实施顺序:统一 SDK 与脚本辅助函数 → Code Host 发布清单、构建身份及真实 CI → 本地文本能力独立/移除外部接入 → 精确符号贯穿影响与重构 → 工作区/UIA/上下文/历史测试职责拆分 → 真实回归与文档同步。保留前轮全部未提交改动;不发布远端、不修改真实客户端配置。 -- 当前 SDK 入口已用项目内现有 10.0.303 验证,无新安装。Code Host 身份、可选完整交付组件与 CI 步骤已编写,尚待整体验证。 -- 已提取原有文本扫描预算/路径/编码/取消规则及纯解析函数。删除外部连接实现及专用启动/协议夹具;外部上游格式/握手专属测试随接口退役,通用身份、UNKNOWN 风险、文本边界和工作区恢复测试迁移保留,最终测试数量会相应变化。 -- 初次针对性运行 58 项中 57 通过,1 项仍修改旧适配器私有 timeouts 字段;正在迁移该测试注入位置,未削弱扫描截止断言。完整回归、发布目录语义验收与桌面验证尚未完成。 - - -## 2026-09-09 — B/C 与职责拆分本地验收更新(北京时间 17:02) - -- 用户已明确选择:彻底退役外部 Serena;缺少 Roslyn 配置时默认 local-text 并提示语义未配置;旧 source 改为 local-text,同步契约、测试与 Skill。版本标记 0.13.0 为本地待发布。旧配置/启动器/外部专用夹具删除;本地文本扫描和明确退化边界保留。 -- B 已实现:维护脚本统一选择已安装 SDK(精确 global.json;无下载/全局改动);check 发布 Code Host 全目录,交付清单覆盖 deps/runtimeconfig/Roslyn/BuildHost 并验证版本、Release、协议。CI 的 Node 22 增加真实 Host/MCP;尚未推送,因此没有本轮远端运行结果。 -- C 已实现:影响/重构接受可选 symbolLocation,保留简单名称入口及别名。先验证已选快照再搜索该声明,拒绝过期/错名,保持 UNKNOWN/不完整语义;本地文本模式在扫描前拒绝精确定位。 -- 职责拆分:Workspace 保留根变更/Git/trash,分出 Browser/Discovery/Contracts;本地文本分出扫描/声明解析;Cache 分出 WorkspaceFingerprint;Context 拆出符号收集/格式化,响应拆出纯范围覆盖。UIA Host 分出 Win32、窗口定位、抓图、树读取及契约;FlaUiAdapter 提取纯协议解析,保留集中进程生命周期。删除 Architecture/Refactor 未使用的查询注入。Router 锁、排空、恢复仍集中,未机械拆散事务状态。 -- 测试和脚本:两份历史大测试按功能拆成 13 份并隔离缓存;退役外部协议测试,保留通用取消/恢复/边界证据。验收脚本共享 SDK/进程观察,Host 和 Gateway 场景分组。没有为追求测试数量保留不存在的连接接口。 -- 本轮失败与修复:首轮核心 302/303,TTL 50ms 被并发 I/O 提前耗尽,改用可控时钟保留前后断言;中文异地 Host 查询正常,但 PowerShell 进程观察非 UTF-8 导致匹配失败,已明确编码并通过完整复验。新增版本测试发现旧输入策略 mock 缺少 id:null,纠正该夹具,避免提前协议错误形成假阳性。Skill 校验器默认 GBK 解码失败后以 Python -X utf8 通过,未改全局设置。 -- 当前已通过:[核心 307/307](../test-tmp/check/2026-09-09T08-57-16-594Z-core/report.json),[桌面夹具 35/35](../test-tmp/check/2026-09-09T08-58-14-312Z-desktop/report.json),[Host 58 场景](../test-tmp/roslyn-host/fixture-sPovtl/report.json),[真实 MCP 19 场景](../test-tmp/roslyn-gateway/run-NqA7IS/report.json),[混合负载 70 调用/10 轮](../test-tmp/mixed-load/run-BcmGpg/report.json)。MCP 使用完整发布目录的中文/空格异地副本和不同 cwd,观测真实 BuildHost/targets 子进程退出;仍复用本机 SDK,不能称为干净机器安装验收。末尾无用构造参数清理另由类型检查与针对性测试验证,最终清单将在收尾重建。 -- 反证自审:不仅断言成功;验证缺失/混合 BuildHost 文件清单、错误版本/Debug Host、旧重载定位不可被自动搜索替换,以及被拒绝的文本定位不触发扫描。此为作者自审,非独立审核。 -- 文档:README、贡献指南、架构图、仓内 Skill 的默认后端/定位/错误/发布目录说明同步,保留 Serena 致谢及历史日志。 -- USER_DECISION_REQUIRED:E4 普通错误文本迁移仍未批准,已请求具体选择;本轮不改变该行为。实际 Codex 连接尚未变更:启用 Roslyn 需明确目标项目、Configuration、TFM 和求值授权;发布/推送及全局 Skill 部署尚未执行。 - -### 同轮最终核对(北京时间 17:05) - -- 无用注入清理后[最终核心 307/307](../test-tmp/check/2026-09-09T09-01-27-993Z-core/report.json)通过;此前 53 项针对性回归亦通过。 -- 修正 Gateway 验收发布输出写入本轮 fixture 目录,避免改写正式交付 publish;[最终 MCP 19 场景](../test-tmp/roslyn-gateway/run-R15auS/report.json)通过,随后原交付清单仍 matched=true(contentId=53fcfb01f044dd0acb71b9d086397f30d960208d678308ae48e0bd3697e1910c)。 -- 48 个本地 TypeScript 模块静态 import/export 图无环;43 个测试文件各登记一次;Skill 校验、相对链接/代码围栏及 git diff --check 通过。桌面验收后仅 UIA Program 尾部空白整理,最终 check 已重新发布。 -- 用户回复要求先详细解释 E4 两方案利弊,尚未选择;已保持现有普通错误 content/isError,不提前新增 structuredContent。后续等待明确选择。 - - -## 2026-09-09 — 当前开发状态上传 GitHub(北京时间 17:13) - -- 用户确认尚未广泛分发,可以直接采用 E4 方案二;普通 Gateway 异常已开始统一 JSON 文本与 structuredContent,错误码/恢复动作不从消息猜测,UI/trash 领域载荷保留。随后用户要求先上传当前状态,因此停止扩展实现,以草稿 PR 保存当前完整相关变更。 -- 本次验证:[错误契约 10 场景通过](../test-tmp/error-contracts/run-Daj6o7/report.json);[最新核心检查](../test-tmp/check/2026-09-09T09-11-00-925Z-core/report.json)的类型检查、Gateway 构建及 .NET 构建通过,核心回归 306/307。失败为 tests/resource-cleanup.test.ts 的中文文件原地修改指纹未变化,根因尚未定位;不以此前 307/307 覆盖此失败。此轮 check 在 regression 失败后停止,未执行后续 stdio 与清单阶段。 -- E4 待完成:错误/恢复分支专项测试、当前 UI 错误双载荷的验收及完整手册核对。旧的桌面 35、Host 58、MCP 19 场景属于 E4 之前的成功基线,不表示该开发快照已全部复验。 -- 上传范围为工作分支 codex/roslyn-correctness 的源码、测试、脚本、仓内文档与 CI;.deps、node_modules、dist、test-tmp、缓存继续忽略,不上传本地依赖或生成证据。Serena 专用目录仍未实际删除。main 未合并,实际客户端与全局 Skill 未改动。 - -## 2026-09-09 20:40 — 结合最新架构评估收敛下一轮计划(北京时间) - -- 用户要求读取“架构分析优化建议”最新分析并结合本地实际更新计划;已读取引用聊天,核对本地/远端 main@a23740c、当前声明解析/扫描、Gateway 错误/影响输出、CI、Skill 与既有验收记录。此次规划不修改生产代码、依赖、实际客户端或远端配置,不派生子代理。 -- 直接调用当前 parseTextDeclarations 做六个最小观察:C# `// class Ghost {}`、TS `const note = "class Ghost {}";`、Python 三引号内 `class Ghost:` 都错误返回 Ghost;真实 UserCard 函数在 .ts 返回声明,在 .tsx/.jsx 返回空。源码确认符号/引用扩展名集合不一致。将其列为 LocalText 优先修补,同时保留文本引用、复杂词法和降级完整性的边界。 -- 本轮实际执行 `npm run test:error-contracts`,10 场景通过;[报告](../test-tmp/error-contracts/run-uZMb3F/report.json)。它证明当前实现的已测分支,不证明标准一致性:unknown tool 的现有断言本身要求 isError,需按协议层修订。持续 CI 和恢复/领域分支仍待补齐。 -- 只读核对 [CI 34339009649](https://github.com/linnnn89/WinCode/actions/runs/34339009649):Node 22/24 成功,Node 22 真实 Host/MCP 步骤成功。CodeQL 34339009257 的三个有效语言成功、Python 历史 job 失败,失败注释明确 exit 32/没有 Python 源码;附带增量缓存提示不能替代该直接证据。 -- 与聊天结论的一处更新:GitHub default-setup 当前语言列表已排除 Python,updated_at=2026-09-09T10:13:02Z。将“修改 Python 配置”从待做事项撤下,改为下一次新扫描确认;本轮未改远端设置或触发扫描。当前仓库没有本地 CodeQL workflow,不为历史红叉另建一套流程。 -- 对照 MCP 2025-11-25 工具规范确认未知工具属于协议错误、业务输入值校验属于工具错误;保留已批准的 E4 方案二,不重新要求用户选择旧方案。影响报告去掉第二文本块与 unknown tool 的对外兼容调整列为实施前明确项,暂不扩展全工具响应抽象。 -- 重写根目录计划和路线图,只保留 0.13 修补方针、实际客户端验收及条件性 UI→Roslyn/性能研究。移除 E1–E3、A1/A2、B/C、Serena 退役和职责拆分的已完成步骤;所有历史实现/失败仍保留本日志与 Git,未把旧失败静默删成成功。 -- 本轮未执行完整核心、桌面或真实 Roslyn 套件,也未声称当前客户端已启用。仅用已有远端结果建立版本基线;计划保留隔离项目、求值范围与实际消费者验证关口。 -- 文档终验:两份计划的 15 个本地链接、围栏、版本和维护命令核对通过,git diff --check 通过。校验脚本首次按系统默认 GBK 读取含中文 JSON 失败;改为显式 UTF-8 后回执校验通过,未改全局环境。 - - -## 2026-09-09 21:18 — 0.13.1 LocalText 与 E4 稳定化实施(北京时间) - -- 用户要求按计划实施,沿用既有逐版本复测/debug、PR 与合并授权。针对客户端验收的询问,用户明确选择“先完成代码与 CI,客户端验收暂缓”;未修改真实 MCP 配置或已安装 Skill,未安装新依赖/SDK,未分出子代理。 -- LocalText:复用并提取现有 C# 非代码区屏蔽纯函数;增加有深度/取消边界的 JS/TS/Python 屏蔽。修复注释、字符串、模板、正则及 JSX 展示内容产生假声明;增加 TSX/JSX 扫描与指定文件符号取证。行号/展示签名保留原文;词法边界不确定时文件结果不完整且不缓存,旧 v1 声明缓存失效。插值/JSX 表达式省略,复杂语法仍可能漏检,不主张完整语法或精确引用。 -- E4:正常受理时未知工具走 SDK ProtocolError -32602;已知工具保留 isError/领域载荷,关闭/取消的入口优先级不变。专项由 10 扩展为 16 场景,覆盖真实隔离 trash metadata partial 及重试位置、工作区提交失败/阻断/恢复、注入 UI 失败与独立图片。后者只验证序列化,不冒充真实截图。Node 22 CI 新增专项及有界回执上传。 -- 影响输出:删除第二份 Markdown 文本,保留 JSON 中 formattedReport、证据字段和别名。固定 dotnet-mini/MemoryService 的同一结果文本从 3194 减至 2511 个 UTF-16 字符,减少 683;[量化回执](../test-tmp/impact-0131-size.json)。这不是实际 token 测量或跨任务性能结论。 -- RED/DEBUG 记录:各类 Ghost、TSX、未知工具和输出去重先复现失败后修复。第一次完整 check 的两个失败来自仍要求旧 stdio 结果的断言,按新契约迁移后通过;测试编写中的 evidence.content/impact.data 错误访问由类型/运行检查指出并更正。自审又复现代码块后的正则字面量泄漏,补充反例并修复后重跑完整检查。未删减生产边界或用旧成功覆盖失败。 -- 最终本地验收:[核心检查](../test-tmp/check/2026-09-09T13-15-21-646Z-core/report.json)318 项、317 通过、0 失败、1 跳过(未配置固定 TavernDesk 工作区的可选集成);覆盖类型、锁定构建、生产 stdio 与完整交付。独立[桌面夹具 35/35](../test-tmp/check/2026-09-09T13-09-20-368Z-desktop/report.json)、[真实 Host 58 场景](../test-tmp/roslyn-host/fixture-cB6uto/report.json)、[真实 MCP 19 场景](../test-tmp/roslyn-gateway/run-j1oS63/report.json)、[E4 16 场景](../test-tmp/error-contracts/run-6c5rEY/report.json)通过。后续仅手册/换行整理,最终核心已重新构建核对交付;无实际客户端验收。 -- 文档:版本统一 0.13.1;README、CHANGELOG、SECURITY、架构/配置指南与仓内 Skill 对齐;计划仅保留暂缓的客户端关口和条件性后续工作。77 个变更文档本地链接、代码围栏核对通过。历史日志追加而非改写,生成证据仍在忽略目录内。 -- 作者反证自审覆盖旧缓存假阳性、词法不确定的假零结果、长字面量取消、未知工具后的连接可用、impact 字段/别名保持,以及部分移动后真实文件位置;不等同独立模型或人工审核。剩余边界:有限词法、文本引用、干净机器/长期耐久性及实际消费者均不扩大声明。 -- 发布流程:工作分支 codex/local-text-e4-stabilization;本地通过后推送本版本 PR,等待对应 head 的 Node 22/24、真实 Roslyn/E4 及有效 CodeQL 全部通过再合并。远端完成情况以 PR/Actions 回执为准,此条写入时尚未推送,不提前声明 CI 已绿。 - - -## 2026-09-09 21:28 — 本地 WinCode Skill 与远端同步(北京时间) - -- 用户恢复客户端验收后,明确要求先修复远端手册与本地 Skill 脱节。fetch 核对 origin/main 与本地均为 76b343c,四份仓内受管手册与远端一致。 -- 已安装位置为 C:/Users/40218/.agents/skills/wincode,原版本 0.12.5;使用现有 sync-skill 脚本更新 SKILL.md、references/code.md、references/diagnostics.md 到 0.13.1 契约,ui.md 原已一致。修改前完成备份,位置:C:/Users/40218/.agents/skills/.wincode-backup-9404b286-f360-42e9-b6a7-13cd35dbe39d;备份采用 .bak 后缀,不注册为重复 Skill。 -- 更新后四文件 SHA-256 与仓内一致,skill check 无差异;delivery:verify matched=true。详见[本地同步回执](../test-tmp/skill-sync-0131.json)。无新增依赖,未改 MCP 配置、未重启客户端。 -- 实际连接 hello 仍返回 0.12.5,instanceId=17625857-dbe1-48b8-9305-946629711532,wincode_find_references Schema 只有 symbolName/relativePath、没有 symbolLocation。配置仍指向 I:/WinCode/dist/index.js,当前工作区 I:/WinCode,尚未提供 --roslyn-config。因此已完成手册同步,尚未完成实际连接升级或 Roslyn 验收;后续须准备隔离验收配置并正常重连,不能向旧接口传新字段后宣称生效。 - - -## 2026-09-09 21:33 — Skill 功能指引与 0.13.1 实现对照(北京时间) - -- 用户要求确认指引与实际项目是否一致。本轮对照四份受管手册、Gateway Registry/Schema/验证器、LocalText/Context、RoslynAdapter/Host、UI 查询和恢复路径;不使用旧连接行为推定新版源码,不派生子代理。 -- 发现并修正文档差异:scopeFiles+symbol 段落漏写 TSX/JSX;简单名引用示例未提示 Roslyn 只返回候选;health.text.semanticConfigured 被误读为整个实例配置状态的风险;维护脚本实际还原/构建 Host 及生成夹具,不能称为只还原夹具或完全离线。补充不同提供方 kind/大小写与 queryComplete 边界、目录参数默认值、lineRanges.file 长度,以及 diagnose 不主动加载 Roslyn、MCP 搜索代替内部 reload 的具体操作。 -- 验证:源码与 dist 的 15 个公布工具 Schema 哈希一致,四份手册包含所有顶层规范字段;两种别名与恢复规则按源码核对。33 项现有 tool-contracts/local-text 测试全部通过;这不证明未覆盖语法或实际客户端语义。范围及字段清单见[核对回执](../test-tmp/skill-contract-audit.json),[针对性测试输出](../test-tmp/skill-contract-audit-tests.log)。 -- 仅修订仓内 references/code.md 与 references/diagnostics.md,保持公共接口/代码不变。通过现有脚本备份并同步本地 .agents/skills/wincode,四文件相等,Skill 校验、链接/围栏、git diff --check 通过。备份为 .wincode-backup-9fd16a60-2ff6-4bb3-980e-f88fe7f01460。重新生成并验证交付清单 matched=true,contentId=517363f6087c4c8e7976f7d810ae0e70ca290ed2bf6e12a7bf2f60650a1e1694。 -- 反证自审:Roslyn 已配置时 text.semanticConfigured 仍为 false;简单名返回 references=[] 实际可能尚未查询引用。修订指引要求读取 codeProvider/health.roslyn 和 resolution/candidates,并使用真实搜索 location,避免把这两种情况误判为后端不可用或零引用。 -- 再次实际 hello:连接仍是 0.12.5、instanceId=17625857-dbe1-48b8-9305-946629711532;引用 Schema 仅 symbolName/relativePath,没有 symbolLocation。故“手册与本地 0.13.1 实现对齐”不等于“当前连接可使用新版接口”。本轮未改 MCP 配置、未执行客户端 Roslyn 验收;文档修订尚未提交远端。 - - -## 2026-09-09 21:34 — 将核对问题同步到项目 Skill 指南(北京时间) - -- 用户明确要求把发现的问题同步写入项目指南。确认前轮修订已存在于仓内 skills/wincode/references/code.md 与 diagnostics.md,并补齐根目录 WinCode-Skill制作与MCP配置指南.md 的对应说明。 -- 根指南同步健康字段归属、Roslyn 简单名候选/精确定位、过期定位恢复、TSX/JSX 与 kind 差异、维护脚本还原/网络边界;更新核对日期并补齐 Code Host 交付目录说明。参数细节继续引用受管代码手册,不另建重复字段表。 -- 本次仅增加根指南和工作记录,受管四文件未再变化,本地 Skill 与仓内一致;链接/围栏及 git diff --check 核对通过。沿用前轮已通过的 33 项针对性测试,不因说明同步重复运行代码回归。未修改 MCP 配置或推送远端。 - - -## 2026-09-09 21:42 — 恢复实际客户端验收并准备隔离配置(北京时间) - -- 用户要求继续已安排工作,沿用恢复隔离 Roslyn 验收及配置修改的明确授权。保留此前未提交文档修订;不修改生产代码、个人应用数据或真实 TavernDesk 项目。 -- 现场环境与旧记录不同:项目内 .deps/dotnet-10.0.303 已不存在,现有系统 C:/Program Files/dotnet/dotnet.exe 经维护脚本核对 SDK 10.0.303 可用;未安装或修改全局工具链。现有 wincode 参数已由此前的含 workspace 改成仅 dist/index.js,本轮以实际读取的版本备份,未恢复过时配置。 -- 生成 test-tmp/client-roslyn-20260909/workspace 的 App/Lib 项目:Debug、net10.0、C# 13,两个 Api.Save 重载及 Other.Save 同名方法;整型重载基线引用为 App/Use.cs 第 7、10 行,字符串第 8 行、其他类第 9 行。NuGet.Config 清空包源;还原/构建通过。Code Host --identity 返回 0.13.1/Release;delivery:verify matched=true。[准备回执](../test-tmp/client-roslyn-20260909/preparation.json)。 -- 仅修改 C:/Users/40218/.codex/config.toml 的 mcp_servers.wincode.args,加入隔离工作区及 --roslyn-config;其他配置字节保留。原服务器块备份于 C:/Users/40218/.codex/backups/wincode-before-client-roslyn-1b3f90fd-059b-4617-a651-42691eeaadd2.toml,未将完整宿主配置复制进项目。codex mcp get wincode --json 已读出正确新参数。[变更回执](../test-tmp/client-roslyn-20260909/client-config-change.json)。验收完成后恢复此前参数,不保留默认指向测试夹具。 -- 实际连接在配置更新前后均返回 0.12.5、instanceId=17625857-dbe1-48b8-9305-946629711532,引用 Schema 只有 symbolName/relativePath,当前工作区仍 I:/WinCode。没有调用旧版不支持的新参数,没有另起脚本模拟实际消费者通过。当前工具没有重连能力,需用户正常重启 Codex 后继续本任务;不强杀宿主或 Gateway。 -- 当前完成的是夹具构建、配置准备和读取校验;实际搜索/引用/影响/重构、stale 与恢复尚未执行。计划和路线图同步为“进行中,等待客户端重连”,不再沿用暂缓状态。 - - -## 2026-09-09 22:29 — 0.13.2 连接退出与限时清理(北京时间) - -- 用户已确认按顺序实施退出、异常终止验证及空闲回收;沿用逐版本测试、PR、合并授权,单智能体执行,无新依赖。保留并同步此前 Skill/配置指南修订。 -- RED:正式 dist/index.js 在客户端 EOF 后 9 秒仍存活;初始化期间 stop 后仍执行后续阶段;卡住的适配器令其他清理无法执行。 -- GREEN:统一 EOF/close/管道错误与退出入口;先连接传输、业务请求等待初始化;关闭取消所有 MCP 操作及初始化,晚到资源立即回收;共用 8 秒预算并为所有权清理预留时间。 -- 针对性 27 项通过。旧测试预期退出时继续完成工作区重绑,已按批准的取消行为更新为拒绝切换、只执行一次 dispose;保留失败后刷新缓存的既有行为。完整检查、PR/CI 尚待后续记录。 -- 纠正前记录:无需一概重启整个 Codex,刷新对应 MCP 连接即可;当前工具未提供已验证可调用的重连入口,未强杀用户宿主。异常进程终止和空闲回收未在本阶段宣称完成。 - -- 完整检查第一次因已有 40ms TTL 测试在并行负载下先过期而失败(保留 2026-09-09T14-29-49-098Z-core 报告);改用受控 Date.now,保持过期前/后断言,不延长 TTL。重跑核心 322 项:321 通过、1 可选场景跳过;桌面 35/35。正式入口及生命周期最新针对性 27/27,通过交付清单校验。 - -## 2026-09-10 08:19 — 本地同步 GitHub 昨晚最新版(北京时间) - -- 用户要求对齐本地落后版本。fetch origin 后确认远端 main 为 6e27e06(2026-09-09 22:39,0.13.2)。同步前工作区干净,本地 a2d76f6 与远端 #30 的 2235a42 为补丁等价提交,实际缺少 #31、#32、#33 三次更新。 -- 先创建 codex/backup-before-sync-20260910 保留旧 main,再通过 git reset --keep origin/main 对齐;HEAD 与 origin/main 完全一致。无依赖安装、全局配置修改或远端推送。 -- npm run typecheck、npm run build 通过;生产入口 EOF 针对性测试 2/2 通过。dist 从旧 0.13.0 刷新为 0.13.2。 -- 反证核对:源码同步不能证明旧构建或已运行 MCP 已更新,因此补做 Gateway 构建;本轮未重建 .NET Host、未验证完整交付清单、未执行完整回归或刷新现有客户端连接。上述边界不作为已完成的运行时部署报告。 -- 同步后仅本条工作记录为本地未提交变更,源代码保持与 GitHub 一致。 - -## 2026-09-10 08:29 — 结合网页版讨论更新下一轮迭代计划(北京时间) - -- 用户要求读取昨晚本地迭代文件并结合“架构分析优化建议”形成更具体计划。通过 read_thread 取得 9 轮对话(无更多分页),区分早期建议、GPT 后续撤回与用户最终的客户端无关/托盘偏好;只更新既有计划书、路线图和本日志,没有实施生产代码或客户端配置变更。 -- 基于 main@6e27e06/0.13.2 核对 Router、RoslynAdapter/Host、UIA、Cache/UiAudit、Gateway、CI 和交付入口。确认可逆释放尚缺、Code Host 初始加载先于 EOF 循环、UIA EOF 是输入边界;已有 Job 不能直接当作 Gateway 死亡即全树退出的证明。 -- 纠正跨机器就绪状态:旧记录涉及 C:/Users/40218 与 I:/WinCode,本项目没有 test-tmp/client-roslyn-20260909 的 preparation.json/client-config-change.json;不沿用“本机只等重连”。状态统计会枚举磁盘缓存,现行审计为 1 MiB 提醒/2 MiB 阻断且不自动删除,分别纳入托盘开销与存储政策边界。 -- 新计划按 M0 本机基线、M1 异常所有权、M2 实测后局部延迟、M3 Roslyn 可逆释放、M4 托盘 MVP、M5 策略/暂停/存储细化模块、验收、停止与回退条件。版本号为建议,不当作已发布。GPT 已撤回的通用 Lease/FSM、全面工作区休眠不继续安排。 -- USER_DECISION_REQUIRED:独立 WinForms/Named Pipe 与启动方式、idle 默认策略、暂停范围、日志保留,以及本机配置/安装/外部操作仍在相应阶段确认;本次未修改真实设置、安装依赖、运行进程强杀或推送。 -- 官方只读核查:Microsoft Job Objects、WaitForSingleObject、NotifyIcon、PipeOptions 与 Node net/timers,链接置于计划相关段落;没有沿用网页聊天的隐藏引用标记。 -- 反证自审纳入:管道断开不等于进程退出、仅 inFlight 不覆盖切换等待、后台 idle 错误不能绕过 E1、托盘状态不能持续扫盘、配置保存不等于所有实例生效、缓存目录可能被多实例共用。属于作者自审,不是独立模型或运行验收。 - -- 2026-09-10 08:31 用户补充明确平台范围:README 中英文新增 Windows 11 x64 本地开发/测试基准,说明其他操作系统、Windows 版本与依赖版本不保证一致效果,建议 macOS/Linux 用户 fork 后本地适配;同步平台徽标、计划书和路线图,不安排本轮跨系统移植。 -- 文档验证:四个变更文件 UTF-8 可读,README/计划书/路线图代码围栏闭合,51 个本地 Markdown 链接目标存在,中英文平台/依赖/fork 说明完整,git diff --check 通过。没有运行代码测试、跨平台验证或未来功能实验;保留前一轮同步日志,最终变更仅 README、既有计划书、路线图及本日志。 - -## 2026-09-10 09:17 — 按计划实施 M0/M1,自有原生 Helper 的所属进程退出保护(北京时间) - -- 用户授权“开始按计划进行迭代和测试”。保留之前四份文档修改,在 codex/m1-parent-ownership 本地分支实施;使用已有 Node 24.19.0、项目内 .NET SDK 10.0.303 和锁定依赖,没有安装/升级或修改客户端配置。 -- M0 原始 0.13.2 完整 check 322/322、生产 stdio 和完整 delivery 校验通过,回执 [core baseline](../test-tmp/check/2026-09-10T00-42-56-585Z-core/report.json)。本机 App/Lib 和后续回执由 scripts/verify-roslyn-gateway.mjs 重建,不使用旧机器路径。 -- M0 当前真实 Codex 连接两次 hello 均仍为 0.13.0/local-text,instance fcff4dad-c746-48ed-ac78-c995ea47d54e,build f9860e16528a104cb939d8a53c4e64d9411561287197e531a89860c78bd1b23f。隔离 stdio 的新交付不等于该实例更新;实际消费者语义闭环、客户端重连及已安装 Skill 同步仍未完成。 -- M1 新增 tools/Shared/OwnerProcessGuard.cs,由两个 Host 链接共享源文件。Gateway 只向自有子进程传 WINCODE_OWNER_PID;Host 在项目求值/UI 访问前校验最多八层真实祖先、创建时间和存活状态,再持有 owner 进程对象句柄。没有客户端进程名称判断、周期全机监控、新依赖或新增 MCP 工具。 -- owner 死亡先 CancelAsync 广播;独立线程宽限两秒后仅 TerminateProcess 当前 Helper,避免原生调用/取消回调卡住兜底。Code Host 继续由既有 Job 覆盖其后代;UIA EOF 保留输入结束语义。Code Host 初始加载与请求取消接入 owner token,内部协议仍 v2。 -- 新增 owner-guard-check 原生夹具、13 项隔离测试,涵盖原生阻塞、取消回调阻塞、正常协作退出、父进程在 Attach 前退出、启动包装链、非法/无关 owner、自指 owner、重复释放句柄、两个实例隔离及生产 UIA EOF。夹具 Helper 使用 detached 以防 Windows 控制台连带退出掩盖测试;生产启动方式未改。 -- 真实 Code Host/MSBuild 初始加载和 UIA 读取分别通过目标写入握手进入故障阶段,再仅强杀测试 Gateway。按预先记录的每个 PID/创建时间查残留,不能依赖已断开的祖先链。失败清理持有实际 Process.SafeHandle 并核对创建时间;不按名称终止。0.13.4 最终 [MSBuild owner receipt](../test-tmp/owner-death/run-hnEJ0Y/report.json) 无观测残留;[UIA owner receipt](../test-tmp/owner-death/run-6OH4q7/report.json) 无 Helper 残留、目标进程仍在,随后才单独关闭测试目标。 -- Repomix 使用真实 RepomixAdapter + 生成 Node CLI 审查,在本机未观测残留,见 [controlled Repomix receipt](../test-tmp/owner-death/run-liokKo/report.json)。没有安装/运行真实 Repomix;这个结果不等于它继承了 .NET guard 或其全部第三方后代受到保障。CLI 路径未改。 -- 失败过程:首次强杀后 transport.pid 已清空导致脚本断言失败,已改为强杀前保存 PID;随后发现残留查询不能只从死亡的根递归,改为匹配全部预观测身份。旧 0.13.2 在本机初始加载样本中也未观测残留,因此没有把该样本记作已复现产品缺陷。早期清理脚本把预期的“PID 不存在”当错误退出,修正错误处理后重跑。句柄重复测试原有 CLR Thread 对象延迟回收和 JIT 最后引用滞留,采用测试侧预热、NoInlining 分组与终结器回收后原阈值通过;生产不调用 GC。 -- 0.13.3 阶段完整 core 335/335、desktop 35/35、[直接 Roslyn Host 58 场景](../test-tmp/roslyn-host/fixture-6JyqoD/report.json)、Gateway 19 场景及 E4 16 场景通过;CI 文件加入 owner-death 与受控 Repomix 场景/报告上传,但没有推送或运行远端 CI。 -- 作者反证自审:正常关闭通过不能证明原生阻塞也退出,因此加入独立阻塞夹具;Helper 退出不能证明全部后代退出,因此按身份逐类报告;目标窗口存活与测试收尾分开记录。真实 PID 复用/权限差异及其他 Windows 版本未实测;仍存活但卡死的 Gateway 不触发本机制。 -- 原生接口核对采用 Microsoft [Process32FirstW](https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32firstw)、[PROCESSENTRY32W](https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32w)、[GetProcessTimes](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes) 与 [WaitForSingleObject](https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject);这些文档不是本机运行验收的替代。 - -## 2026-09-10 09:19 — M2 按启动测量延后 UIA 探测,0.13.4 本地交付(北京时间) - -- 在 M1 后做三次 stdio 和源码 Router 分段样本,[改前报告](../test-tmp/runtime-baseline/run-msaLjT/report.json):连接到 hello 426.7–434.8 ms;Router 初始化 165.7–168.1 ms,其中 FlaUI 158.9–161.2 ms、每次创建一个健康探测进程。缓存约 2–3 ms、指纹约 1–2 ms、watcher 小于 1 ms,没有证据支持把它们一并休眠/延迟。生成夹具规模不能代表大仓库。 -- M2 仅修改 FlaUiAdapter 初始化为平台/配置/发布文件校验,不运行原生 probe。尚无运行观察保持 available=null/source=unknown;首次 UI 请求直接执行且成功响应更新已知身份;主动 diagnose 保持探测,非强制并发 probe 在既有 mutex 内复查 memo。ToolRouter 单独保留首次 UI 失败,即使健康仍 unknown;未改 watcher/cache/Roslyn 的启动策略或 MCP Schema。 -- 新增 3 项测试,覆盖初始化不探测、并发首次非强制诊断共享观察、缺失文件报告/恢复后首用,以及 first-use timeout 的 unknown/lastAdapterError 边界。作者反证自审修正:失败响应不能自动把未探测 Host 标为已确认不可用,也不能因 health=null 丢掉错误。桌面 hello 测试按真实未探测状态断言 null/unknown,随后原有真实 UI 首用测试照常执行。 -- 最终 [core 338/338](../test-tmp/check/2026-09-10T01-13-24-684Z-core/report.json)、[desktop 35/35 + UIA owner-death](../test-tmp/check/2026-09-10T01-15-11-141Z-desktop/report.json)、[真实 Gateway 19 场景](../test-tmp/roslyn-gateway/run-orlRgW/report.json)、[E4 16 场景](../test-tmp/error-contracts/run-CRw4pt/report.json) 全部通过。直接 Host 的 58 场景在 M1/0.13.3 阶段运行,此后 Code Host 仅同步产品版本;没有把它记作再次运行。 -- 首组改后采样与其他测试竞争资源,单独保留 [并行负载样本](../test-tmp/runtime-baseline/run-CUJxNy/report.json),不用于安静对照。待重测试结束后,[改后独立三样本](../test-tmp/runtime-baseline/run-6cXWUA/report.json):连接到 hello 273.8–289.8 ms,Router 6.7–6.9 ms,FlaUI 静态校验约 0.32 ms,启动原生 probe 为 0。语义冷查询 3.58–4.29 s,热查询 102–108 ms,客户端关闭约 28–32 ms;全部观测进程在关闭后退出。样本共享 OS/SDK 缓存,不给 p95/跨机器性能承诺,未声称改善 Roslyn 语义查询耗时。 -- 改后进程 working-set 合计:未用语义约 84.6–85.2 MiB,语义操作后约 193.9–211.9 MiB;进程求和可能重复计算共享页,只是瞬时快照。启动统计只覆盖被包裹的 Node 异步文件方法/spawn,不含全部原生或内核 I/O。Roslyn 可逆释放仍未实现,不能把上述数据当作已节省驻留内存。 -- Node 24.19.0/Windows 11 本机最终源码、Gateway 和两个 Host 版本 0.13.4;buildId 07ee0681a89cdc8035d3038f8710a43e923f7e44385a2aaa3ce38eadab90ae19;delivery contentId 7131f0943f56b81aa5033458765c5cb320cb0b37723a1054c8fee1e8f83f14d2 再次校验 matched=true。工具数 15,Schema 哈希与基线相同。test inventory 45 个文件完整覆盖,git diff --check 通过。 -- README 保留用户的 Windows 11/其他平台 fork 说明,同步源码版本;CHANGELOG、受管 Skill 源文件、既有计划/路线图和 CONTRIBUTING 更新。未提交/推送、未安装全局 Skill、未改客户端/自启动、未删除用户审计记录。Node 22/远端 CI、跨权限/系统版本、真实 Codex 新版本语义消费尚未验证。 -- USER_DECISION_REQUIRED:已发出 D2(建议默认关闭 idle,显式开启后比较 120/300/600 秒)和 D1(建议独立 WinForms/Named Pipe、首版手动启动)的选择题,尚未收到答复。按计划第 9 节及用户协作契约的重大路线边界,M3 自动释放策略和 M4 托盘不据沉默启动;M5 暂停/存储仍按 D3/D4 决定。当前本地增量可评审,当前连接升级仍单独待验。 - -## 2026-09-10 10:31 — M3 手动释放与 M4 最小设置,0.14.0 本地交付(北京时间) - -- 用户已确定“默认关闭自动释放、设置内手动释放;优先 Agent 工作流畅度,同时平衡后台内存;其他细节按最小方案”。D1/D2 已确认,M3 手动路径与 M4 合并为 0.14.0;本轮没有 idle timer/自动释放开关、开机自启动、全局配置写入、暂停、停止全部或存储清理。沿用现有 SDK/框架,没有新增 NuGet 包或安装依赖。 -- RoslynAdapter.releaseWarmState 复用现有 mutex/关闭链,覆盖操作排队与异步清理;释放只清 Host/snapshot,保留配置、诊断并保持可再次加载。ToolRouter 保护 MCP 在途、直接语义操作、待执行切换、shutdown/recovery;忙碌直接拒绝,不排队延后释放。被接纳释放之后的新请求等待其完成;关闭失败进入现有 restart_gateway 恢复门。缓存/watcher/Gateway 保留。 -- 新增可选 WinCode.Tray,独立 WinForms NotifyIcon 与设置窗口,手动启动;Gateway 仅显式 --tray 时接入。读取内存快照,不启动 Host 或扫描磁盘缓存,隐藏窗口不轮询。八个实例槽加一个唤出窗口槽,控制绑定活连接及 instanceId,管道受当前用户/会话、本机/实际客户端 PID 和额外 User SID 校验保护;限制帧、待决操作、连接退避,断开/超时保留未知,不重放控制。退出 Tray 后 MCP 独立运行。 -- 首批失败包括测试夹具相对 Host 路径、管道地址转义、时序断言、测试变量类型标注和文档脚本语法。明显路径/语法/类型问题直接修正;初始超时测试按本轮已记录 PID/创建时间关闭自有测试进程,随后确认无观测残留,没有按客户端名称清理。所有失败回执保留在 test-tmp,不改写为成功。 -- 用户新增要求:非简单语法/object 等错误,首次测试失败即主动查官方文档和真实 GitHub 实现/问题记录。已写入 CONTRIBUTING。时序断言依据 [Node net 回调契约](https://github.com/nodejs/node/blob/main/doc/api/net.md)改为等待真实 shutdown 回调,不以额外固定 sleep 粉饰通过。管道本机连接实际返回 229,核对 [Microsoft ERROR_PIPE_LOCAL](https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-) 后只接纳这一明确本机结果;其他失败或远程查询成功均拒绝。 -- 另查 [dotnet/runtime #123903](https://github.com/dotnet/runtime/issues/123903),CurrentUserOnly 的历史 Owner SID 行为不能替代实际 User SID,故在读取首帧后按 [RunAsClient 官方用法](https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes.namedpipeserverstream.runasclient?view=net-10.0)同步核验客户端 User SID。重复唤出使用 Identification 身份级别并等确认后关闭短连接;不模拟客户端操作文件或执行程序。依据 [构造函数说明](https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes.namedpipeclientstream.-ctor?view=net-10.0)核对默认 None 与显式 Identification 的差别。 -- [核心 check 356/356](../test-tmp/check/2026-09-10T02-26-26-551Z-core/report.json)通过:包含 8 项手动释放、9 项 TrayClient 和可选 Tray 交付校验;TypeScript、锁定还原/构建、生产 stdio 均通过。核心之后的 Tray 发送前失联复查和重复唤出细化已重新 Release 发布,并以最终 Tray 验收验证;没有把此前核心回执中的旧 delivery ID 称为最终交付 ID。 -- [桌面 check 35/35 + UIA owner + Tray](../test-tmp/check/2026-09-10T02-28-08-797Z-desktop/report.json)通过;最终 [Tray 7 个场景](../test-tmp/tray/run-z65Kr8/report.json)再验证真实 WinForms、同用户安全管道、两个独立 stdio MCP、忙碌拒绝/仅选中实例释放/重复释放/隐藏与退出、重复唤出和实际 dist/index.js --tray 的确认后退出。UI 使用模拟 Roslyn 生命周期,真实 Roslyn 由下述独立验收覆盖;不把截图或模拟后端说成真实项目 UI/Roslyn 全链路验证。 -- [真实 Roslyn 十轮报告](../test-tmp/manual-release/run-umaOsZ/report.json)通过:每轮查询及引用正确、新 snapshot、旧定位在无 Host 时明确拒绝、所有预观测 Code Host/BuildHost 按 PID/创建时间确认退出,资源登记始终为 3;缓存标记和 namespace、watcher 保持。另覆盖冷态新增源码、释放后 A→B→A。没有观测残留。未重跑未受修改的纯 Host 58 场景;本次 [真实 MCP/Roslyn 19 场景](../test-tmp/roslyn-gateway/run-F36HcB/report.json)和 [E4 16 场景](../test-tmp/error-contracts/run-USNVaz/report.json)已重跑通过。 -- 十轮生成小项目:冷查询 3.717–4.112 秒,热查询 109–148 毫秒,手动关闭 21.6–27.2 毫秒;进程工作集合计约 200.2–206.9 MiB → 67.7–69.6 MiB,被关闭的 Host/BuildHost 合计约 132.0–137.5 MiB。共享页可能重复计数,不等同精确回收的独占 RAM;样本共用系统缓存,非大型仓库/p95/跨机承诺。独立 Tray 打开时单次工作集约 51.5 MiB、private bytes 12.7 MiB;Tray 自身有成本,因此继续采用可选手动启动/退出,无自动高频监测。 -- 最终 Gateway buildId=458dc48f0d60f0d2cebf98e3b989efafddfcf74646da46c8c871ed475b1bb6bd;完整 delivery contentId=0f4bd8f371eb74d040b64fd7683ca033e6aaaa6d4c1fe0972b79aa058141cefa,0.14.0 matched=true。15 个 MCP 工具和 Schema 哈希保持原值。最终托盘截图已人工视检,无当前截图范围的裁切/重叠;默认系统 DPI 下的程序化渲染不代表全部 DPI 或人工鼠标操作验收。 -- 作者反证自审:Agent 两次调用之间可能仍在规划,界面空闲不等于整个任务结束,因此只提供用户手动释放且提示旧定位失效;状态陈旧时后端仍重新判断。畸形帧与随后合法帧同批到达必须停止处理,已有回归;断线重连前的旧待发控制重新检查连接状态。普通 Node 和 .NET 唤出客户端均已本机联调,同权限/跨用户拒绝矩阵、Explorer 重建、八实例上限压力及长期驻留仍未全面实测。 -- README 中英文、既有计划书/路线图、CHANGELOG、Skill 源文件及 CONTRIBUTING 同步;真实 Codex 连接、已安装 Skill、客户端 --tray 接入没有改动或升级。本轮没有提交/推送或运行远端 CI;Node 22 与其他 Windows/依赖版本仍未实测。M5/自动策略为延后范围,实际消费者接入保持待验。 -- 文档收尾:8 份相关 Markdown 为有效 UTF-8、代码围栏闭合;现行文档及本轮新增日志的 66 个本地链接目标存在,版本/锁文件一致,git diff --check 通过。全历史日志扫描另发现 15 个旧机器 test-tmp 回执未随源码来到本机;保留历史记录,不据此引用其结果作为本轮验证。首次链接脚本把 chatgpt-conversation URI 当成本地路径,已按 URI 类型修正校验器。 - - -## 2026-09-10 11:32 — 0.14.0 稳定性收尾:工作流连续性、状态可信度与原生交付(北京时间) - -- 用户确认按分析继续,再次强调优先工作流畅、避免反复启停。保持无自动释放/自动加载策略;仅用户手动释放,忙碌不排队。未增加依赖、全局配置、自启动或 M5 能力,未修改实际 Codex 启动参数、重启客户端或推送远端。 -- 设置改为“暂无在途请求”,明确不代表 Agent 整个任务结束。连接保留但刷新失败、观察超过 30 秒时标为未知,保留上次观察时间;释放前重新获取被动状态,超时不继续控制。可见窗口的一秒计时器只重绘时效,隐藏时停止,不向 Gateway 轮询或改变 Host 生命周期。操作结果绑定实例,切换选择不会显示另一实例的结果;调整说明文字换行及表格宽度,避免 PID 列/页脚裁切。 -- 本地管道新增注册接纳/拒绝反馈及可见原因;修复 InvalidDataException 未纳入 IOException 过滤导致监听任务退出的问题。连续 12 次不兼容注册后继续服务。拒绝和唤出回复均有界等待客户端读取/关闭,避免立即 Disconnect 丢掉未读回复,不使用不可取消的 WaitForPipeDrain。安全身份校验及正常 MCP 退出入口保持。 -- 原生发布改由 scripts/publish-native.mjs 在构建前采集输入、构建后复核并绑定完整产物,check 自动使用该入口。覆盖项目目录(排除 bin/obj)、仓内 Shared、仓内 props/targets/NuGet 配置和现有构建脚本;delivery:verify 复查原生输入/产物,不能通过重新生成交付清单接纳改过 .cs 的旧 DLL。范围是仓内已知输入,不是任意外部 MSBuild 导入/SDK 二进制的签名证明。加入源码/共享源码变化、继承配置变化、构建中变化和输出排除的回归。 -- [核心检查 360/360](../test-tmp/check/2026-09-10T03-20-58-314Z-core/report.json)通过,包含类型检查、锁定构建、生产 stdio 与交付验证;此前 [失败回执](../test-tmp/check/2026-09-10T03-19-06-872Z-core/report.json) 保留。旧 watch-invalidation 测试在整个仓库写探针并固定等 500ms,受并行写入/尾沿 debounce 影响;改成隔离 Git 工作区并等待真实 noteFilesystemChange 回调,原失效逻辑继续执行且仍断言指纹不同,未改生产 watcher/缓存或绕过 memo。针对性 5/5 通过后完整重跑通过。 -- [桌面 35/35、UIA owner、托盘与贯通验收](../test-tmp/check/2026-09-10T03-22-29-403Z-desktop/report.json)通过。[真实贯通回执](../test-tmp/tray-workflow/run-Ho43hD/report.json)使用正式 dist 的 Router/MCP/TrayClient、原生设置处理函数、同用户安全管道与两个实际 Roslyn 项目:实际并发语义请求拒绝释放且不延后执行;七次间隔查询保持 A/B snapshot 及 Code Host 身份;只释放 A、B 保持热态;旧定位失败不预热,重新搜索后精确引用正确;退出 Tray 后两个 MCP/语义快照继续可用,关闭后全部预观测进程无残留。测试只替换了隔离管道的启动接线,未模拟 Roslyn 生命周期;不是当前 Codex 连接验收。 -- 同一贯通运行含冷态、双热态、隐藏驻留、只释放 A、退出 Tray 的进程样本。七次隐藏热态样本的工作集合计约 484.2–491.0 MiB,句柄合计 1981–1988,进程数保持 9,无自动启停。所有工作集合计可能重复计算共享页;这是短时、小项目样本,不能外推大型项目、长期泄漏或唯一物理 RAM 回收。 -- 核心之后的托盘显示/回复等待修订已通过上述桌面检查。其后仅实例结果显示隔离及验收诊断修订重新 Release 发布,最终本机 Tray 九场景连续通过三次:[1](../test-tmp/tray/run-mKPJrp/report.json)、[2](../test-tmp/tray/run-rdZu4z/report.json)、[3](../test-tmp/tray/run-xohNR5/report.json)。没有将较早 core/desktop 的交付 ID 称为最终 ID,也未重复未受影响的纯 Host/E4/十轮测试。最终 Gateway buildId=5d8c46b2d68c4c0c490633b0bb4fb8ec56787e9105b756917c94652f7997763f;delivery contentId=132e047e7d81a73a26b3b1cc24623ee464fa653f718049f30af1d1873c54fa7d,matched=true。 -- 失败及外部参考:管道两次 EOF 失败回执为 [Apop33](../test-tmp/tray/run-Apop33/report.json)、[WMQdEX](../test-tmp/tray/run-WMQdEX/report.json)。先依据 [DisconnectNamedPipe](https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-disconnectnamedpipe) 和 [dotnet/runtime Windows 管道实现](https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs)处理未读回复,再由 [InvalidDataException 类型定义](https://learn.microsoft.com/en-us/dotnet/api/system.io.invaliddataexception?view=net-10.0)定位异常过滤遗漏;修正后重复拒绝验收通过。贯通首次 [nHnQyk](../test-tmp/tray-workflow/run-nHnQyk/report.json) 错把至少两个长期 Host 进程作为条件;查询真实进程树、检索 Roslyn 案例后改为定位实际 Code Host 及预观测子树,不把 conhost 或已退出的临时求值进程误记为常驻 BuildHost。watch 测试参考 [Node fs.watch 契约](https://nodejs.org/api/fs.html)和 [Node 测试指南](https://github.com/nodejs/node/blob/main/doc/contributing/writing-tests.md)。布局参考 [WinForms 布局约束](https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/layout)。 -- 待观察失败:[VcfIp2](../test-tmp/tray/run-VcfIp2/report.json) 在原生验收时超时,未留下 UI 回执,根因尚未证实。参考 [WinForms 异常处理](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.application.setunhandledexceptionmode) 和 [官方实现](https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms/System/Windows/Forms/Application.cs),在仅验收模式启用异常直出、输出阶段和退出/超时原因,后续三次未复现;不能将诊断改进称为已修复该偶发超时。 -- 当前 Codex 被动 hello 实测为上一轮 0.14.0、buildId 458dc48f…、local-text;codex mcp get 的实际参数只有 dist/index.js,未启用 Roslyn 或 --tray。[实际客户端验收参数预览](../test-tmp/tray-workflow/run-Ho43hD/client-configuration-preview.json) 与 [Roslyn 配置](../test-tmp/tray-workflow/run-Ho43hD/client-roslyn.json) 已准备,仅指向隔离夹具,applied=false。实际消费者启用/重连、Node 22/远端 CI、跨权限/系统/DPI 和长期驻留仍未验证。当前已构建本地增量可评审,未提交/发布。 -- 作者反证自审覆盖:连通但观察失效、失效状态下直接进入释放处理函数、拒绝注册耗尽监听、切换选择混入别的实例结果、全部分层测试通过但真实设置/Roslyn未串联、C#改过而旧DLL仍被清单接纳,以及刷新/隐藏期间意外重启。README、CONTRIBUTING、CHANGELOG、既有计划/路线图同步;不把作者自审称作独立审核。 -- 收尾校验:最终 delivery 再次 matched=true;git diff --check 通过;本次六份 Markdown(日志只检查新增段落)的 70 个本地链接均存在,UTF-8 与代码围栏通过。最终 settings.png 已目视核对,PID 列和页脚完整、切换实例不显示其他实例的操作结果;仅覆盖本机默认 DPI。 -- VcfIp2 证据补充:旧验收脚本有 35 秒兜底终止,但当时未保存退出码/超时标志,回执只记录缺少原生 UI 报告。因此“超时”属于基于旧脚本行为的推测,不能据此认定具体根因;现已补齐这两个诊断字段,后续三次正常退出且未复现。 - -## 2026-09-10 11:45 — 多项目、多 Agent 并发诊断(北京时间) - -- 用户要求测试多个项目或不同软件 Agent 同时调用 MCP 是否混淆、溢出。本轮只增加 [隔离诊断脚本](../scripts/verify-multi-agent.mjs) 和本日志,未修改生产代码、接口、依赖、实际消费者配置、自动释放策略或远端。使用当前 dist/index.js 真实 stdio、已发布 Code Host、项目内 SDK 和两个离线还原的生成项目;同一测试驱动内的三个 SDK Client 各自启动独立 Gateway,分别代表 A、B、另一软件的 A。不是三个实际第三方软件的接入认证。共享实例情景通过同一合法 stdio Client 交错发送两个逻辑 Agent 的调用,不声称 stdio 本身支持多个独立连接。 -- [第一轮八场景回执](../test-tmp/multi-agent/run-9uve7M/report.json)全部完成:三个进程并发冷加载、同名方法引用分别为 A=1/B=2;跨实例定位(包括同项目两个实例)返回 SNAPSHOT_STALE 且不改变健康快照;96 次交错精确引用;单进程 128 个并发搜索和其他实例查询;64 个请求中取消 16 个,余下 48 个正确;共享工作区复现;同路径重开复现;关闭一个客户端后另外两个继续工作。预观测进程最终均无残留。此处 success 指诊断场景完成,不代表没有发现缺陷。 -- **已复现:共享进程的多调用任务不具备项目隔离。** A 打开 A,另一个逻辑 Agent 打开 B 后,A 只按 Save 搜索成功返回 B.Api.Save(int),相对目录查询也返回 only-B.txt;没有自动 WORKSPACE_MISMATCH。传旧精确 symbolLocation 则明确 SNAPSHOT_STALE,说明精确定位有保护,普通名称/相对路径没有任务级绑定。ToolRouter.config.workspaceRoot 是实例全局状态,切换锁只保护在途请求与切换,不覆盖完整 Agent 任务。不能把这个结果泛化为独立进程串项目;另一 A 实例保持原快照和查询结果。 -- **已复现:同路径 workspace_open 会主动关闭健康 Code Host。** 明确观察旧 Host PID/创建时间退出、hello 的 processAlive=false、下一次搜索获得新 snapshot,另一个实例仍热态。此行为来自 ToolRouter.ts 的同路径显式 resetConnection,原意是恢复入口,但多个 Agent 重复初始化会造成冷启动,与优先连续工作存在矛盾。第二轮该重新搜索约 3.915 秒;这是小项目单次观察,不是性能承诺。 -- **负载边界:有限突发成功,但准入队列缺少长度上限。** 第一轮单实例 128 请求时被动 hello 观察 inFlightRequests=129(包括 hello 本身);请求结束回到只有 hello 的 1。代码中的 acquireRequestSlot 和 Mutex 计数/串行等待没有队列容量限制,仍有单次操作超时。第二轮集中搜索最长约 13.948 秒;不能把单帧上限、32 MiB 单实例缓存预算或超时等同于进程总内存/队列容量上限。本轮没有制造 OOM,也未证明持续洪峰下不会耗尽内存。Roslyn 精确查询不走语义磁盘缓存,本轮 cache entries=0,不能据此声称已验证跨进程共享缓存写入/清理安全。 -- 128 请求场景两轮出现 drain MaxListenersExceededWarning。按用户要求先查 [MCP SDK 真实问题 #842](https://github.com/modelcontextprotocol/typescript-sdk/issues/842)、[官方客户端 stdio 实现](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/client/src/client/stdio.ts)和 [Node 流背压规则](https://nodejs.org/api/stream.html),再增加测试诊断。第二轮 [完整警告堆栈与前八场景](../test-tmp/multi-agent/run-ffxcyO/report.json)确定警告来自本机 @modelcontextprotocol/client 2.0.0 的 StdioClientTransport.send,事件数超过默认 10,场景结束后监听器为 0;三个 Gateway stderr 没有同类警告。外部 issue 场景是服务端批量通知,只作为相似背压机制参考,不能视为本机相同根因的证据。不抬高监听器阈值、不隐藏警告、不声称已证实持续泄漏。 -- 第二轮新增 SDK 帧测试曾失败:测试错误地期待超限后同一连接还能读下一条合法消息,实际为 0 条。核对 [官方服务端 stdio.ts](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/server/src/server/stdio.ts) 和本机包后确认超限应报错并关闭传输,是测试假设错误;生产代码未改。修正为明确断言错误、关闭、后续不执行,并只重跑受影响边界,未反复重跑已通过真实 Host 场景。[边界两场景回执](../test-tmp/multi-agent/run-PxHPQo/report.json)通过:按 64 KiB 分块发送 10 MiB + 64 KiB 后 SDK 发出 10485760 字节上限错误并关闭;该测试用已安装真实 transport 和内存流,未向实际 Codex 连接发大包。 -- 同一边界回执通过真实原生托盘安全管道的八注册接纳、第九注册明确拒绝、满员后 show 仍可用、断开一个连接后新注册成功。九条注册通道来自一个自有 Node 测试进程并使用各自 UUID,状态是夹具;这验证原生容量及恢复,不等同九个真实 Roslyn MCP 的资源压力,也不将托盘八槽称为 MCP 全局进程上限。测试托盘使用隔离命名空间,最终无预观测残留。 -- 补跑既有 [混合负载十轮 / 70 调用](../test-tmp/mixed-load/run-HT12AB/report.json),5.754 秒通过,覆盖 Router 在途请求、交错切换、取消、命名空间和释放;该脚本有控制门且不启动外部适配器,作为 Router 层补充,不能冒充真实三客户端 Roslyn 测试。 -- 验证:新脚本 node --check、git diff --check、delivery:verify 通过;delivery contentId 仍为 132e047e7d81a73a26b3b1cc24623ee464fa653f718049f30af1d1873c54fa7d,matched=true。生产未变,未重复无关核心/桌面全量构建。可复现命令:node scripts/verify-multi-agent.mjs;仅帧/托盘容量为 node scripts/verify-multi-agent.mjs --boundaries-only。 -- 建议次序:目前让并发独立项目使用独立 Gateway,任务期间固定工作区,避免每次查询前重复 workspace_open;下一轮先解决同路径重开的幂等与显式恢复语义,再设计请求工作区绑定和有界排队/取消/可观察等待。**USER_DECISION_REQUIRED(后续实现)**:是否改变现有 workspace_open 恢复契约、增加每请求工作区身份或固定实例模式、采用什么超载拒绝/等待规则;本轮不擅自改变这些公共行为。不建议为了省内存直接合并成全局多项目 Host 或引入自动启停。 -- 作者反证自审:单次请求全部正确仍可能在两个调用之间串项目;实例隔离仍可能有共享磁盘缓存或共同源文件的竞争;64 个取消测试只保证指定取消与余下查询正确,不涵盖任意 Host 硬故障;10 MiB 帧防护不能限制大量小请求的总队列;警告消失与进程回收也不能证明长时无泄漏。并发源码写入/共享缓存清理、真实软件接入、UIA 多 Agent 操作同一窗口、长期大项目驻留尚未实测,不能给出“任意多 Agent 并发绝对安全”的结论。 - -## 2026-09-10 11:57 — 回顾并精简下一轮规划,形成并发治理最终推荐(北京时间) - -- 用户要求结合 GitHub 优秀案例给出最终推荐,并写入原规划书、删除已经确定实现的部分。本轮仅修改 [详细计划](../WinCode-下一轮工程化迭代计划书.md)、[简版路线图](https://github.com/linnnn89/WinCode/blob/d51f3e105b07b50b1e2535ca541f532ea77b3fc5/WinCode-%E8%BF%AD%E4%BB%A3%E8%B7%AF%E7%BA%BF%E5%9B%BE.md) 和本日志,没有修改生产代码、测试、依赖、实际客户端配置或远端。 -- 复核当前 0.14.0 未发布工作树及现有回执:核心 360/360、桌面 35/35、真实托盘/Roslyn 贯通、最近托盘专项、三实例诊断、第二轮失败、修正后边界和混合负载报告。delivery:verify 再次 matched=true,contentId=132e047e7d81a73a26b3b1cc24623ee464fa653f718049f30af1d1873c54fa7d;不是重跑这些代码测试,不把旧回执套到未经验证的新生产修改。 -- 从未来待办删除旧 M0–M4 已完成的本机重建、owner guard、UIA 延迟探测、手动释放、托盘/安全 IPC、状态可信度和原生交付绑定步骤;已验证托盘八注册/拒绝/空位恢复也移出开发队列。删除已过期版本建议、相互矛盾的“没有托盘/缺少可逆释放/仍主动启动探测”等基线,以及 M5 中 2/5/10 分钟自动释放设置的旧执行方案。只保留简短基线和历史链接,不删除既有日志与失败回执。 -- 最终推荐为独立 Gateway、连接固定项目、健康 Host 保持热态、有界排队。N1 固定根和错误目标零副作用;N2 同根确认与必要恢复分流;N3 有界受理/公平等待/取消收尾/明确过载;N4 真实共享缓存/源码变更和窗口边界验证;N5 实际消费者、原生未定位失败及交付验收。每项列关键模块和可反证验收。 -- GitHub 一手参考:[Playwright MCP](https://github.com/microsoft/playwright-mcp/blob/main/README.md#user-profile)明确同项目并发 profile 需额外隔离;[rust-analyzer reload.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/reload.rs)区分相同工作区、构建数据变化和强制重载;[.NET ConcurrencyLimiter](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/ConcurrencyLimiter.cs)及 [测试](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Threading.RateLimiting/tests/ConcurrencyLimiterTests.cs)提供队列容量/FIFO/取消归还竞态参考;[SDK #842](https://github.com/modelcontextprotocol/typescript-sdk/issues/842)与 [客户端 stdio](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/client/src/client/stdio.ts)用于区分发送积压警告。仅借鉴机制,不声称这些仓库证明了 WinCode 的实现安全;链接为查阅当日 main/master,不宣称固定发布版本。 -- 32 个未完成业务请求、4 个状态槽、64 KiB 参数预算仅为明确的实测起点,需合法调用兼容性与资源验证;传输帧、队列、缓存和进程 RSS 分开。固定根不自动解决同项目共享磁盘或同窗口操作;N4 未预判需要新缓存架构,也不预先复制每个实例的持久缓存。默认自动释放关闭及手动释放保持既有用户选择。 -- 保留的真实未完成项:当前消费者最新构建/Roslyn 闭环、两种目标软件项目连接兼容性、共享缓存并发清理/编辑、VcfIp2 根因未定位、Node 22/远端 CI、长期/大项目及其他权限/DPI/Explorer 范围。特别修正旧“超时已定位”式措辞:VcfIp2 原始回执只证明缺少 UI 报告,旧超时归因仍为推测。 -- USER_DECISION_REQUIRED 为后续实现的固定根迁移、同根重开语义和过载错误/预算这组推荐契约;本次文档更新不代表已经实现或批准真实客户端操作。若目标软件只支持全局单连接但必须多项目切换,应先重新选择每请求工作区身份,不悄加不安全兼容模式。 -- 作者反证自审:固定根后选错工具连接仍需客户端尊重错误;同根快捷返回不得绕过恢复门或输入新鲜度;取消逻辑容量降低不等于等待节点释放;独立 PID 不等于共享存储或 UI 隔离;本地交付身份不等于活动客户端已升级。规划按用户要求删除已完成待办,历史只追加不改写。 -- 文档收尾:详细计划 148 行、简版路线图 42 行;两计划及本次新增日志的 UTF-8、代码围栏、27 个本地链接、1 个标题锚点通过,旧 M0–M4 实施段落已移除,N1–N5 两文档一致,git diff --check 通过。仅文档修改,未重复运行代码全套测试。 - -## 2026-09-10 12:00 — 按用户要求快速推送 GitHub 检查点(北京时间) - -- 用户明确授权快速推送当前状态。提交范围为当前 0.14.0 的 owner guard、延迟 UIA 探测、手动释放、原生托盘、交付校验、并发诊断及精简后的 N1–N5 规划;发布到 origin/codex/m1-parent-ownership,不改 main,不创建 Release。 -- 推送前 git diff --check、delivery:verify matched=true 和测试 inventory(47 个测试文件)通过;复用此前核心 360/360、桌面 35/35 及专项回执,没有为快速检查点重复运行全套测试。源码与锁文件、原生工程和测试源码一并提交;忽略的 dist/bin/obj、.deps、test-tmp 回执、真实客户端配置和凭据不上传。 -- 已知限制随检查点保留:共享实例跨项目会影响后续普通查询,同根重开会重置健康 Host,等待队列缺少容量限制;N1–N5 仅为推荐计划,尚未实现。VcfIp2 原生验收失败未定位,实际消费者最新构建/Roslyn 接入、共享存储竞争及长期资源验证未完成。远端 CI 在推送后独立运行,提交本身不表示 CI 已通过。 - -## 2026-09-10 13:05 — N2 热态保留与 N3 互斥取消基础(北京时间) - -- 用户已明确要求按确认的计划继续实施,N1–N3 方向不再待审批。本轮在干净的 bf220a9 / codex/m1-parent-ownership 基线上先交付可独立验证的 N2 和互斥取消基础;N1 固定根需要同步迁移原有跨项目及完整故障注入覆盖,仍为下一开发项。没有修改实际客户端配置、已安装 Skill、依赖或自动释放策略,也未提交/推送本轮修改。 -- `ToolRouter.openWorkspace` 健康同根不再无条件 `resetConnection`;Windows 大小写和分隔符别名沿用现有根的拼写,保持 session/watch 身份。持工作区锁后读取 Adapter 已有的类型化 `restartRequired/cleanupFailed`,需要时才执行重置;十个并发恢复确认只重置一次。`reloadRequired` 保留给显式搜索;既有 `workspaceRecovery/restart_gateway` 门禁保持。Configuration/TFM/可执行文件参数在 Adapter 构造时冻结,修改客户端启动配置仍须重建连接,重复打开不热应用配置文件。 -- `Mutex` 的 Promise 尾链改为有序可删除等待集合;取消立即删除等待闭包和监听器,存活请求保持 FIFO。已执行的任务在实际操作及异步清理结束后才交还执行权。增加只读 `pendingCount` 供真实等待节点断言;128 个受控等待者中取消 64 个后,在队首仍阻塞时实际节点已降为 64,剩余依序完成并归零。**没有增加 MCP 总受理上限、参数预算或 SERVER_BUSY;本项不等于完成 N3。** -- 针对性测试 [63/63](../test-tmp/n2-targeted.log);最终核心 [364/364](../test-tmp/check/2026-09-10T04-58-35-445Z-core/report.json),包含类型、构建、既有原生锁定还原/发布、stdio 与交付检查;E4 [16 场景](../test-tmp/error-contracts/run-RcGwrj/report.json)通过。新增 UI 失败诊断后另跑 typecheck 通过。首次 typecheck 的 `never[]` 断言收窄错误属测试代码的低级类型问题,改用长度断言,没有改变生产行为;失败回执保留在 `test-tmp/check/2026-09-10T04-54-35-495Z-core/report.json`。 -- 真实 Roslyn MCP [22 场景回执](../test-tmp/roslyn-gateway/run-uffy9G/report.json)通过:十次同根打开、四个并发确认夹杂引用保留实际 Host/已观测后代 PID、snapshot、watcher/session;真实 SDK 选择输入变化要求 `HOST_RESTART_REQUIRED → workspace_open` 并回收旧 Host;冷加载损坏、热态旧证据拒绝/重载失败、修复后显式搜索,以及真实 MSBuild 取消/崩溃/超时均验证。它运行在源码逻辑相同、工具描述更新前的构建;最终描述/Schema 由后续核心与三实例回执覆盖,不声称旧消费者已经更新。 -- 第一次真实 MCP 回执 [run-2WfcZA](../test-tmp/roslyn-gateway/run-2WfcZA/report.json)在第 16 个场景失败:旧测试借同根打开制造冷态,改动后实际为热态输入失效,返回 SNAPSHOT_STALE 而不是预期 PROJECT_LOAD_FAILED。按用户要求先检索并阅读 [Roslyn #54796](https://github.com/dotnet/roslyn/issues/54796)、[rust-analyzer reload.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/reload.rs),再结合本地 `WorkspaceSession.EnsureFresh/ReloadAsync` 确认层次。上游 SDK 加载案例只是参考,不是本地错误码的来源。修正前置状态并增加冷、热两种严格断言;生命周期故障先证明旧定位失效,再显式搜索进入真实阻塞 target,未把多种错误合并放行,也没有加入自动重放。 -- 最终构建的 [三实例十场景](../test-tmp/multi-agent/run-hnl1r7/report.json)完成:独立 A/B/A 冷加载、96 次交错引用、128 次搜索、64 请求取消 16 个、跨实例定位拒绝、单连接退出及帧/托盘边界。相同路径重开缺陷 `observed=false`,Host PID 15772 保留,snapshot 前后均为 `84e8f7c18ad44533963eeecad5b75b43`;观察过的进程最终 survivors=[]。**共享连接跨项目问题仍 `observed=true`,普通搜索仍可返回 B.Api.Save(int)**;success 代表诊断完成,不能解释为多 Agent 风险全部解决。客户端 SDK drain 警告再次出现,堆栈仍在 `@modelcontextprotocol/client/dist/stdio.mjs:205`,阶段结束监听器归零;未提高阈值或隐藏警告。 -- 因 Mutex 同时用于 UIA,补跑桌面回归。首次 [34/35](../test-tmp/check/2026-09-10T04-59-31-835Z-desktop/report.json)的完整树/截图场景只留下 `success=false`,旧断言无领域错误码。检索 [FlaUI #394](https://github.com/FlaUI/FlaUI/issues/394)、[FlaUI AutomationElement](https://github.com/FlaUI/FlaUI/blob/main/src/FlaUI.Core/AutomationElements/AutomationElement.cs) 和 [微软 UIA 等待接口](https://learn.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationwindowpattern-waitforinputidle) 后,先在测试断言中补 requestId/errorCode/errorMessage/captureMethod,不改生产时序或超时。单套件 [13/13](../test-tmp/n2-desktop-diagnostic.log)未再现。首次失败缺少底层信息,窗口就绪、捕获环境、Mutex 均不能确定为根因;此项与旧 VcfIp2 失败分别保留在 N5。 -- 最终本地交付 matched=true,contentId=`9de57af9a81d9966457b0eb8e1a1320d75d04fe76fd8ca3a79ee5504674a8834`;Gateway buildId=`0987e341f543efdd78305937878d722a85cada178ff11e9156cc578f984a7d78`,Schema hash=`e1f7a396188e3bd4ba905935d2e896465a38526b219e54d9047b79bdd85d4411`。对应 revision 是基线 bf220a9,未提交增量由 source/artifact hash 区分。同步更新工具描述、仓库 Skill 诊断源文件、CHANGELOG、原规划和路线图;删除已完成 N2 待办,保留 N1、完整 N3、N4、N5 及新旧未定位失败。 -- 作者反证自审:保温不能掩盖 SDK/输入失效,已用真实 SDK 变更与冷热损坏反例验证;取消早返回不能允许新任务进入尚未清理的 Host,单测与真实 UIA/Roslyn 取消分别覆盖。短时小项目及 SDK 模拟不证明长期无泄漏、真实多软件接入、共享缓存/窗口完全隔离;互斥节点可删除不证明业务队列有界。自动释放继续关闭,设置内手动释放行为保留。没有独立模型/人工审查,本轮不把作者自审当独立审核。 -- 13:10 补充桌面收尾:第二次完整桌面运行 [35/35 和 owner-death 通过](../test-tmp/check/2026-09-10T05-02-26-806Z-desktop/report.json),但托盘 [xaa7Wf](../test-tmp/tray/run-xaa7Wf/report.json)确认 35 秒保护超时、退出码为空、最后在隐藏/唤出阶段;不能外推旧 VcfIp2 同因。先查 [微软 WinForms 线程安全调用](https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-make-thread-safe-calls) 与 [dotnet/winforms #4631](https://github.com/dotnet/winforms/issues/4631),再只在隔离 `TrayAcceptance` 加毫秒/线程和 Hide、延迟、ShowExisting 工作线程/确认、VisibleChanged、报告写入的阶段日志,未改线上行为或增加超时。两次专项 [ZvxMdQ](../test-tmp/tray/run-ZvxMdQ/report.json)、[XVjI4K](../test-tmp/tray/run-XVjI4K/report.json)通过;第三次随最终完整桌面运行的 [sTDf8v](../test-tmp/tray/run-sTDf8v/report.json)通过。三次未再现仍不构成根因修复,N5 保留。 -- 最终完整 [desktop 检查](../test-tmp/check/2026-09-10T05-07-25-412Z-desktop/report.json) success=true:35/35、owner-death、托盘及真实托盘/Roslyn 工作流全部通过。[工作流四场景](../test-tmp/tray-workflow/run-3jNah3/report.json)包括忙碌释放拒绝、七次间隔热态观察、仅释放 A 后显式查询恢复/B 保持热态,以及退出托盘两个 MCP 继续查询,最终 survivors=[]。托盘诊断源码按标准流程重新发布后,最终 delivery contentId 更新为 `1b5a924800a412c6fd2fffa82663b43c46d29095c1e703d4678ade473a9baeb4`,matched=true;Gateway buildId/Schema 未再变化。UTF-8、本轮文档本地链接和 git diff --check 通过。工作树保持本地可审阅修改,N1/完整 N3 的既有缺口及桌面未定位失败不作为本轮已解决事项。 - -## 2026-09-10 13:47 — 潜在实际运行缺陷验收(北京时间) - -- 用户要求查验既有测试未检测到的实际运行漏洞并给出建议。本轮只读审查生产实现,使用 `test-tmp/runtime-audit/` 下被 Git 忽略的隔离脚本和夹具;保留 `codex/m1-parent-ownership`、基线 `bf220a9` 上原有未提交增量。没有修改生产代码、已有测试、规划书、实际客户端配置或依赖,没有发布或推送。以下 `observed=true` 表示缺陷被复现,不是产品验收通过。 -- **P1:缓存成功返回旧代码。** [已有提交的 Git 仓库回执](../test-tmp/runtime-audit/git-uj1Ucy/report.json)中,新建但未暂存的 `src/Target.cs` 从 Gone/OLD_VALUE 改为 PresentNow/NEW_VALUE_WITH_MORE_CHARACTERS;变更前后 `git status` 都为 `?? src/`,显式 watcher 失效通知和 fresh 指纹计算后指纹仍相同,公开 MCP prepare_context 返回旧全文、find_code_symbol 返回已消失的 Gone,且 queryComplete=true、truncated=false。仅暂存夹具文件后,对照查询返回新全文、Gone 数量归零、fromCache=false。另一个 [深层非 Git 文件场景](../test-tmp/runtime-audit/run-jIeKUX/report.json)同样复现旧全文;目录指纹的深度 3/文件数 100 上限未覆盖实际选中文件。当前证据覆盖 local-text 和内置上下文打包,不宣称复现 Roslyn 语义快照失效。根因位置:WorkspaceFingerprint.ts 的 Git 状态目录粒度、前 100 项统计与有界目录扫描;LocalTextAdapter/RepomixAdapter 信任此指纹的缓存命中。建议对实际输入建立依赖/内容身份,利用已读取的有限文件内容校验,watcher 只作加速失效信号;不要每请求全仓哈希,也不要把要求用户 git add 当成修复。 -- **P2:共享磁盘缓存清理后返回悬空附件。** [两进程回执](../test-tmp/runtime-audit/run-jIeKUX/report.json)使用两个真实 Node 进程、当前 CacheManager/RepomixAdapter 和同一缓存目录。为确定性触发淘汰,把合法阈值缩为 maxDiskEntries=1、maxEntryBytes=8192;A 打包产生全文附件,B 淘汰磁盘条目和附件,A 的内存命中仍返回 fromCache=true、contentOmitted=true,但 overflowPath 已不存在。未模拟文件系统或修改 TTL。确认的是缺失上下文附件,不是源文件丢失或跨项目污染。建议命中时校验附件存在性/身份,缺失则失效重建,并明确多进程清理所有权;原子写入不能单独解决附件生命周期。 -- **P2:取消同根确认误入恢复并重启健康 Roslyn。** 同一回执在实际同根元数据读取完成、Router 的 await 后取消检查前设置可控取消窗口,使用真实已发布 Roslyn Host。未改变根目录或源码,却出现 recovery-required(roslynLoaded=true)、业务请求被阻止;按提示重新打开后变冷,下一查询 Host PID 从 18476 变为 6384,snapshot 也变化。触发来自 rootPrepared=true 后统一进入恢复分支。建议区分无状态变更的同根确认与真实重绑定/清理失败,保留后者的强制恢复保护。此前正常同根保温测试通过仍成立,但 N2 的取消边界验收未通过,应重新列入后续修复范围。本轮是可控 AbortSignal 窗口,不称为真实第三方客户端手势复现。 -- **P2:同根确认等待慢请求,连被动状态也阻塞。** [默认超时回执](../test-tmp/runtime-audit/drain-JdeB0m/report.json)通过真实 MCP 处理器及受控慢查询,保持默认 shutdownMs=8000。同根打开在约 8010.7ms 后 TOOL_EXECUTION_FAILED;期间 hello 也等待约 8011.4ms 才成功。放行原查询后查询正常、工作区健康。根因是判断 sameWorkspace 前先设置切换屏障并等待 drain,状态请求也经过屏障。建议尽早分类同根确认,并让被动状态保持有界响应;真正切换/释放仍需排空,不能只增加超时。这是确定性模拟慢业务耗时,不是实测大型项目冷加载耗时。 -- 发现非低级逻辑失败后按用户要求查阅真实上游资料:[Git status 官方行为](https://git-scm.com/docs/git-status.html)、[VS Code 文件监听限制](https://github.com/microsoft/vscode/wiki/File-Watcher-Issues)、[VS Code 监听实现说明](https://github.com/microsoft/vscode/wiki/File-Watcher-Internals)、[npm cacache 内容读取实现](https://github.com/npm/cacache/blob/main/lib/content/read.js)。资料支持目录枚举/监听局限和读取缓存实体时校验的设计参考,本地缺陷结论来自本轮回执;未引入这些项目的依赖。 -- 验收判断:新确认四类缺陷、五个触发场景;证据正确性与异常时工作连续性尚不满足稳定多 Agent 使用标准。优先修复旧代码缓存,并与已有 N1 任务级项目绑定一起作为正确性门槛;随后处理同根取消/状态阻塞、缓存附件生命周期和完整 N3 准入预算。将本轮反例转为正式回归后,再进行真实消费者及长期驻留验证。既有共享连接跨项目问题、无准入容量上限、未定位的 WPF/托盘偶发失败仍未解决,不重复计为本轮新增发现。 -- 限制:本轮没有重复未修改生产代码的完整 364 核心/35 桌面套件;这些数字属于上一轮基线。本轮隔离探针使用真实文件系统、真实缓存进程、真实 Roslyn Host,但部分竞态采用确定性调度、缓存阈值缩小;不能估计生产发生率,不能证明长期无泄漏/OOM,也不是跨权限安全渗透或三个真实软件客户端接入认证。脚本入口为 [主探针](../test-tmp/runtime-audit/probe.mjs)、[Git 输入探针](../test-tmp/runtime-audit/git-input-probe.mjs)、[状态延迟探针](../test-tmp/runtime-audit/confirmation-latency.mjs)。本次为作者反证审查,不是独立模型或人工审核;未实施修复,不静默改写之前的成功记录。 - - -## 2026-09-10 14:01 — 验收缺陷修复与 PR 交付准备(北京时间) - -- 用户明确授权继续修复,完成后推送 PR 并合并。本轮收敛为验收新增四类缺陷,保留上一轮未提交的 N2/Mutex/诊断增量;未实施 N1 固定项目和完整 N3 准入限制,也未修改实际消费者配置、安装依赖或开启自动释放。拉取发现 origin/main 已通过 #34 合并同内容基线,确认树与 bf220a9 无差异后,在 origin/main dda0203 创建 codex/runtime-cache-continuity,避免重复引入已合并基线。 -- 文本检索每次沿用现有 8 MiB/5000 项等扫描预算枚举并读取输入,避免新建/删除文件和监听遗漏被结果缓存隐藏;声明解析以实际内容 SHA-256 复用,使用 CacheManager 既有 LRU/条目与字节预算,仅存解析结果,不新增独立内存池或持久 AST。简单文本引用直接扫描。旧有界 WorkspaceFingerprint 保留作为变更提示,不能再决定这些源码结果是否有效。 -- 内置打包读取实际选中文件后,以有顺序的路径/内容元组计算身份,再复用相同内容的打包/附件;不额外执行一次全仓内容哈希。没有可核验输入清单的 CLI 打包不复用旧结果;显式禁用 CLI 和取消隔离继续生效。缓存读取检查 overflow 实体,peer prune 后内存/磁盘命中均降为未命中重建。本轮未建立跨调用附件租约,不承诺返回的临时文件永久存在或跨文件编辑具有原子快照。 -- 健康同根确认在切换屏障之前执行,保留会话、Host 和新鲜度提示;不重复改写相同根的 trash/config,取消确认不进入部分重绑定恢复。已知重启/清理失败、真实 A→B 切换继续使用排空和恢复路径;没有无限放行真正切换期间的状态请求,完整状态准入仍属 N3。 -- 新增 [正式回归](../tests/runtime-cache-regressions.test.ts) 8/8,通过公开 MCP 的未跟踪目录/深层文件/超过提示预算、同长度且还原 mtime 的修改、新建删除文件、引用失效、内存预算、缺失附件的内存/磁盘重建、真实双进程淘汰,以及取消/慢查询交错。实际双进程使用 [既有工具链夹具](../tests/fixtures/cache-peer.mjs),缓存阈值缩小但文件系统/进程均真实。已加入 package.json 的核心 inventory;原有相关测试 87/87。新夹具 windowsHide 在 Node ForkOptions 类型中不受支持,移除该多余属性(子进程 stdio 为管道),属于低级类型错误;之后完整 typecheck 通过。 -- [完整核心 372/372](../test-tmp/check/2026-09-10T05-56-37-721Z-core/report.json)通过类型、构建、原生锁定构建、stdio 与交付。E4 [16 场景](../test-tmp/error-contracts/run-mgJSec/report.json)通过;[完整桌面](../test-tmp/check/2026-09-10T05-57-42-090Z-desktop/report.json) 35/35、owner-death、托盘和贯通检查通过。[实际托盘/Roslyn 贯通](../test-tmp/tray-workflow/run-bImEg6/report.json)包含忙碌拒绝、七次间隔保温、仅释放 A、退出托盘不影响 MCP,最终无观察到的残留。这次通过不宣称已定位历史 WPF/托盘偶发失败。 -- 修复前后使用同一隔离审查探针:[修复后主回执](../test-tmp/runtime-audit/run-5F2F0c/report.json)中深层旧代码、双进程悬空附件、取消导致健康 Host 重启均 observed=false;真实 Host PID 19200 和 snapshot 79046bfdeee342e99a784779daf617aa 均保留、业务可用。[Git 对照](../test-tmp/runtime-audit/git-p1dvGU/report.json)仍有相同旧提示指纹和 ?? src/,但已返回新源码、Gone 符号消失,证明没有依赖改提示算法或要求用户暂存。报告 observed=false 本身不是通用无缺陷证明,正式断言见新增回归。 -- 真实 Roslyn 专项首次 [nWBtRL](../test-tmp/roslyn-gateway/run-nWBtRL/report.json)在第 19 个场景报 Owned process survived: 11848。按要求先查 [MCP 取消规范](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/cancellation)、[Process.Kill 文档](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.kill?view=net-10.0)及 [dotnet/runtime #107992](https://github.com/dotnet/runtime/issues/107992),再检查客户端取消与真实 Gateway 收尾。定位到旧测试把同根打开当作 drain 屏障,与本轮只读确认语义冲突;现场稍后 PID 已不存在。改为在原有 8 秒预算内观察 Gateway inFlightRequests 回到只有 hello 的 1,再严格核对所有已记录进程退出,未延长预算、主动杀进程帮助断言或放宽退出条件。该调整只在验收脚本,生产清理代码未改。 -- 作者反证自审:相同文件长度/mtime、监听遗漏、新增文件、磁盘条目尚存但正文附件缺失,以及取消客户端先结束而服务端仍在收尾。保留真实修改后的输入失效/SDK 重启/清理失败恢复测试;不因同根保温跳过实际故障。规划仅移除已取得证据的完成项,N1、完整 N3、N4 其他存储/UI/源码竞争、N5 真实消费者和长期样本仍保留。 - -- 14:02 收尾:调整观察方式后的 [真实 Roslyn 22 场景](../test-tmp/roslyn-gateway/run-wNNpAi/report.json)全部通过,包括主动 MSBuild 取消/崩溃/超时及进程退出。最终本地 delivery matched=true,contentId=ace3a662543d5c6df01bda9f23bea28199031c420f976e6a5f4fdb39d3ba19fb;Gateway buildId=717352bcd195475eed76cab88e7c1f96701c8fbaaab8e105a1d34dcccdc34b75。该本地构建在切换等内容基线前产生,revision 元数据为 bf220a9;不把它冒充最终 PR 提交构建,远端 CI 将核验具体提交。git diff --check 通过;推送/合并状态以随后 GitHub 回执为准。 - - -## 2026-09-10 14:10 — 全项目 Markdown 同步(北京时间) - -- 用户在 PR #35 等待 CI 时要求同步更新本项目各个 md。核对 Git 管理的 14 份 Markdown,按相关范围更新 README 中英说明、架构和时序图、配置/Skill 指南、四份受管 Skill 文档、贡献与安全说明、UIA Host README、CHANGELOG、规划/路线图及本日志。没有同步已安装手册、修改实际客户端配置或连接。 -- 纠正旧 0.13.1 架构/指南/Host 当前版本标题;安全策略保留既有维护承诺,补充 main 的 0.14.0 开发线。源码版本、磁盘产物和运行实例继续分别核验,不把本次 PR 合并写成已发布版本。补充独立托盘/Helper 所有权与手动释放边界,自动释放仍关闭。 -- 统一缓存文案:namespace 不等于跨进程物理隔离;有界 fingerprint 不是源码身份;声明按内容复用、内置打包按实际候选内容复用、CLI 无输入清单不缓存、附件失效可重建但没有永久租约。纠正“监听一定及时失效”和“已有生产缓存等于 UI 证据跨调用有效”的过度表述。 -- 同根健康确认不排空、不重启、取消不制造恢复状态;真实重绑定/已知故障仍需恢复。N1、完整 N3、N4 剩余交错/UI/源码边界及 N5 消费者/长期验证继续保留。此前 364/360 等历史结果不改写,当前基线明确为核心 372/372、桌面 35/35、真实 Roslyn 22/E4 16。 -- 四份受管 Skill 文档属于交付输入,本轮会重跑完整核心检查并重建交付清单;不因只改 Markdown 便沿用旧清单冒充匹配。旧 CI 结果只属于 bcd2fe5,文档提交后的合并等待对应新提交检查。 - - -## 2026-09-10 14:47 — Node 22 远端进程归属误判修复(北京时间) - -- PR #35 首个提交 bcd2fe5 的 [CI 34443549607](https://github.com/linnnn89/WinCode/actions/runs/34443549607)中,Node 24 与全部 CodeQL 通过,Node 22 核心/E4/独立 Roslyn Host 58 场景通过,但 Roslyn Gateway 的 timeout 退出断言失败。随后 owner-death、Repomix owner-death、manual-release 因脚本立即退出而没有执行,不能把这些项目写成该轮已通过。 -- 下载有界回执后定位:所谓残留 PID 752 是 csrss.exe,创建于 05:59:48;其 ParentProcessId=744,而真正测试中 PID 744 是 06:08:39 才创建的 conhost.exe。旧 ownedProcesses 只用 ParentProcessId 展开,沿这条错误边把 138 个进程纳入 Code Host 子树。此证据支持验收观察器 PID 复用误判,不支持 Roslyn 清理失败;不能通过无限重跑隐藏。 -- 按用户要求先查 [Win32_Process 官方 ParentProcessId/CreationDate 说明](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process)及 [MicrosoftDocs 原文](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src/CIMWin32Prov/win32-process.md)。修复 scripts/lib/owned-processes.mjs:同一 CIM 快照中按父子创建时间校验每条边,缺少相关创建身份时报错,不能悄悄省略未知后代。重放远端回执,138 项缩为实际 7 项,保留 dotnet Code Host、BuildHost、cmd、blocker node 及对应 conhost;没有增加清理宽限或修改产品清理代码。 -- 新增确定性 PID 复用/缺少创建身份回归,专项现为 9/9。修改受管文档后的 [核心 372/372](../test-tmp/check/2026-09-10T06-10-43-228Z-core/report.json)通过;加上 PID 回归后的最终 [完整核心 373/373](../test-tmp/check/2026-09-10T06-14-00-192Z-core/report.json)通过类型、构建、原生交付、stdio 和清单校验。修正观察器后的 [真实 Roslyn Gateway 22 场景](../test-tmp/roslyn-gateway/run-qoTd5O/report.json)通过。新增规则依旧严格断言真实子进程退出,不把只读过滤回放当成新的清理证据。 -- 全项目 14 份 Markdown 已核对,93 个本地链接、UTF-8、代码围栏通过(历史工作日志只核对新增段,不静默改写旧失败)。文档检查初次因 Git 对中文文件名引用导致路径解析失败,改用 git ls-files -z 取得真实文件名后通过,属于检查脚本路径处理错误。 -- 用户补充确认:修复观察器之后,必须在最新提交上完整运行 Node 22 专项链,若真实 Code Host/BuildHost/cmd/blocker 仍存活才继续查产品生命周期。在该链与全部必需检查通过前,PR #35 保持未合并;不沿用 bcd2fe5 的部分 CI 结果。 - -## 2026-09-10 17:00 — 架构审查第 1–2 批修复(北京时间) - -- 用户要求“按照你的设计开始进行迭代or修复”。在已合并 #35 的 main `7d53fdac3017560dbecb138eee0f8bb6f6361af3` 上建立 `codex/architecture-boundaries`,先落实执行/磁盘边界和默认读取可用性。此前 #35 最终提交 `5d1ae37` 的 Node 22/24/CodeQL 已通过并合并,属于历史基线;本轮本地修改尚未提交、推送或应用到实际消费者。 -- A1/B1/B2:新增 `GitClient`,从启动时工作区外安装候选解析真实绝对路径,使用 execFile/argv,禁用 shell、可执行 fsmonitor 和可重定向 Git 工作目录的环境变量。要求 Git 2.36+;旧版把 false 当 hook 路径的行为依据 [Git 官方配置文档](https://git-scm.com/docs/git-config#Documentation/git-config.txt-corefsmonitor),不自动安装或升级。状态由 Git 自身查询,支持 linked worktree;查询不可用/失败为 unknown,缺少 isClean,不把异常报告为干净。生成仓库的 git.cmd 反例和真实 worktree/损坏配置回归通过;fsmonitor 先用直接 Git 阳性对照确认会生成标记,再确认 Workspace/Fingerprint 两条生产路径都不生成标记。 -- A2/A3:新增共用 `FileSystemBoundary`。Cache 初始化、写入、维护和 Repomix overflow 都拒绝 cacheDir/祖先中的链接或 junction,并校验已打开目录的 dev/ino 身份;trash 目标实际位置必须在工作区内,在 mkdir、rename 和 metadata 前复查。JSON 文件名及格式头采用 wincode-v1 所有权标记;清理只处理本版本可识别条目和保留命名的 overflow。旧版/无法识别的 JSON、临时文件及无关文本保留,不计入受管配额,不做迁移。保留移动后元数据失败的 partial、实际位置与既有恢复门。 -- A5/A8:旧 identifyProject 归并到有界 ProjectDiscovery;架构目录树和图入口枚举共用 WorkspaceBrowser。项目描述符检查词法/实际根边界;不另读 .sln 外部引用,不求值 MSBuild。发现最多 2000 项、树最多 500 项;图最多 16 个项目文件、单文件 64 KiB、合计 256 KiB,入口枚举合计最多 2000 项。maxDepth 限整数 1–5,整份格式化 JSON 上限 32768 UTF-16 字符,返回 scanComplete/omissions/outputOmissions。Router、Context、结构扫描与目录浏览传递实际取消/deadline;取消后在底层读取返回和句柄关闭前继续持有活动请求资格。 -- A4:TextDeclarations 折叠屏蔽后的空白,移除 C# 重叠可选空白匹配;规范化单行超过 16384 字符返回词法不确定/不完整。独立 Node 子进程受外部 5 秒截止约束,并通过真实 MCP 查询与心跳,避免用主线程计时器证明主线程未阻塞。原 2084 字符反例不再超时;最终全量负载中,2048 空格解析约 1.05 ms,65536 空格约 12.56 ms,20 ms 心跳在约 23.86 ms 响应。这是合成输入的实测,不是普遍性能保证。 -- 正式新增 [12 项边界回归](../tests/architecture-safety.test.ts)和[独立声明探针](../tests/fixtures/declaration-budget-probe.mjs),纳入 package.json 的完整 inventory。最初 6 个缺陷场景在修复前均失败;第一批与既有相关测试 67/67 通过。首次完整检查 [382/384](../test-tmp/check/2026-09-10T08-48-35-105Z-core/report.json)保留两项失败:旧“非 Git”夹具实际位于 Git 仓库内;旧 overflow 夹具把任意 txt 当受管附件。依 [rev-parse 官方行为](https://git-scm.com/docs/git-rev-parse)改为真正独立的非 Git 目录;附件改由生产 writeOverflow 创建。未放宽非 Git、过期删除、容量驱逐或内存引用保护断言。 -- fsmonitor 阳性夹具首次未生成标记,保留 [失败回执](../test-tmp/architecture-boundaries-recheck.log)。对照 [Git 实现](https://github.com/git/git/blob/master/fsmonitor.c)的 shell 调用以及本地 stderr,确认带空格的 hook 配置未引用,实际尝试启动 D:/CODEX;修正生成夹具的命令引用后,[25/25 相关复测](../test-tmp/architecture-boundaries-recheck-final.log)通过。没有更改真实仓库/全局 Git 配置。 -- 作者反证自审发现“紧凑 JSON 已符合预算,格式化 MCP 文本仍超限”:137 个生成项目声明返回 32847 字符,见[修前失败](../test-tmp/architecture-output-budget-before.log)。按实际格式化序列化计数后为 32238,见[修后回执](../test-tmp/architecture-output-budget-after.log),新增端到端预算回归。不用仅检查内部对象大小代替客户端实际响应。 -- 最终 [npm run check](../test-tmp/check/2026-09-10T08-58-28-915Z-core/report.json)通过:Windows x64 / Node 24.19.0,**385/385,0 fail、0 skip**;包含类型检查、Gateway 构建、既有原生组件锁定还原/发布、完整回归、新 stdio 实例、交付清单核验。[E4 16 场景](../test-tmp/error-contracts/run-WnFBU1/report.json)通过,包括真实 partial trash 及恢复行为。最终 buildId=`ce1d898b90951d0419797ab316dfe7172527d939b49c9638b7d7a80935452352`,Schema=`304d4030ad9e3ba8ad55159273a7b9b892bd8f6366980d3b2f9ce2d75a0fe2a9`,delivery contentId=`ddbb283087dcb1e69f632eae7e9cc8db9263b231565a8cf2bb45ec3f87c54d78`、matched=true。revision 仍是基线,未提交增量由 sourceHash/artifactHash 标识。 -- 同步 README 中英双语、CHANGELOG、架构说明、计划/路线图及仓库 Skill 源文件;本机安装副本和真实 MCP 连接未更新。剩余 N1 固定项目、完整 N3 准入、A9 生产清理身份、N4/N5 实际消费者/交错/长期样本继续保留。静态链接与目录身份复查不构成对抗并发替换的原子沙盒;合作取消不保证强制中断永久挂起的 OS I/O;overflow 返回后没有永久租约。自动释放仍关闭,默认 local-text 与显式 Roslyn 求值授权不变。 -- 17:03 补充验证:[真实 Roslyn MCP 22 场景](../test-tmp/roslyn-gateway/run-NDrihh/report.json)全部通过,包括 Host 热态/精确身份、实际 MSBuild 取消/崩溃/超时后的已观测 Host/BuildHost/后代清理及恢复、最终 Gateway 退出。只生成/求值 test-tmp C# 夹具并使用既有 SDK;不是当前 Codex 连接或干净机器验收。其后 `delivery:verify` 仍 matched=true、contentId 不变;本轮未改原生/UIA/托盘生产代码,未重复桌面验收,历史未定位桌面失败继续保留。当前代码、预算、Schema 与手册经作者自审,未进行独立模型或人工审核。 - -- 用户随后明确要求先提交目前版本并合并。发布范围为本轮第 1–2 批修复和对应测试/文档;发布前交付清单再次 matched=true,远端 main 仍为 7d53fda。沿用相同生产源码的 385/385、真实 Roslyn 22 和 E4 16 回执;新提交的必需 CI 完成后才合并,真实消费者配置不在本次发布范围内。 - -## 2026-09-10 19:04 — 下一轮第一批:失败证据、清理身份与客户端预检(北京时间) - -- 用户要求“按照你的规划开始工作”。核对 I:/WinCode 初始为干净 main `fb3cd48df3f38b209565b906fbfe3485df48461d`(#36),建立 `codex/runtime-baseline-and-cleanup`。先实现失败证据保留与 A9,真实客户端项目配置验证前置到 N1;保持现有公共切换行为,固定根、完整准入及后续竞争验证尚未实施。本轮未提交、推送、安装依赖或改变实际客户端配置。 -- CI 证据:从 `scripts/check.mjs` 抽出小型 `scripts/lib/check-stage.mjs`,在抛出失败前写入阶段退出码/信号、TAP 总数、日志路径与采集完整性;用 [Node 官方多报告器](https://nodejs.org/docs/latest-v24.x/api/test.html#multiple-reporters)同时输出 TAP 与原生 JUnit。CI 的 always 上传保留报告、阶段日志与 XML 七天。维持原有 8 MiB 捕获预算,ENOBUFS、启动失败及超时明确标为采集不完整;缺少完整 TAP 汇总即使退出码为零也失败。JUnit 文件存在不等于该运行完成。 -- [四项日志回归](../tests/check-reporting.test.ts)调用真实 Node 测试子进程,覆盖早期失败被大量后续输出挤出旧尾部摘要、JUnit 转义/断言、成功汇总、零退出但缺汇总,以及输出溢出/启动失败。失败测试夹具的 62 项中 60 通过、1 失败、1 跳过,是有意构造的报告验证数据,不是产品回归结果。 -- A9:`ResourceManager.disposeOnce` 在实际调用清理函数的微任务内重查注册归属;等待前一清理期间或进入微任务前已经注销的资源不再执行,也不生成虚假的 closed 记录。`killProcessTree` 在入口及异步终止后的检查点优先使用 ChildProcess 的 exitCode/signalCode,不再探测已知退出对象留存的数字 PID;自然退出移除 exit/close 两个归属监听器。保留现有进程树清理、实际退出验证与总时间预算。 -- [四项身份回归](../tests/resource-identity.test.ts)在生产修改前均失败,见[修前日志](../test-tmp/baseline-cleanup/resource-identity-before.log),修后全部通过,见[修后日志](../test-tmp/baseline-cleanup/resource-identity-after.log)。PID 复用反例拦截了 OS 动作,未对真实复用 PID 执行信号。与既有生命周期回归合并[22/22 通过](../test-tmp/baseline-cleanup/targeted.log),其中真实已拥有子进程退出覆盖仍保持。未增加 Windows 原子进程句柄身份校验,不能把此修复说成消除了全部 processExists/taskkill 之间的 PID 竞争。 -- [完整核心检查](../test-tmp/check/2026-09-10T10-52-38-371Z-core/report.json)通过:Windows x64、Node 24.19.0、.NET SDK 10.0.303;共 **393 项,392 pass、0 fail、1 skip、0 cancelled**。跳过项为未提供固定工作区的可选 TavernDesk 集成,未自动访问个人项目或数据库。类型、锁定构建、全量回归、新 stdio 实例和交付清单均通过;不是正在使用的 Codex 连接验收。 -- [真实 Roslyn Gateway 22 场景](../test-tmp/roslyn-gateway/run-5FREl5/report.json)、[E4 错误契约 16 场景](../test-tmp/error-contracts/run-D1fPmY/report.json)通过。涉及实际 MSBuild 取消/崩溃/超时后的已观测进程清理及恢复,均使用生成夹具与既有工具链。 -- [桌面完整检查失败](../test-tmp/check/2026-09-10T10-55-28-895Z-desktop/report.json):35 项中 34 通过、1 失败;新 [TAP 日志](../test-tmp/check/2026-09-10T10-55-28-895Z-desktop/desktop-tests.log)及 [JUnit](../test-tmp/check/2026-09-10T10-55-28-895Z-desktop/desktop-tests.xml)保留 `FlaUiAdapter` 第 4 项的 `HOST_ERROR: Recording indicator could not be displayed; UI access refused.`。原生 `RecordingIndicator.cs` 未修改;失败发生在提示窗确认阶段,不能据此断言是截图、WPF 就绪或 Mutex 问题。 -- 对该非平凡失败查阅 [Microsoft UpdateWindow](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-updatewindow)、[WM_PAINT](https://learn.microsoft.com/en-us/windows/win32/gdi/wm-paint)及其链接的 [Windows Classic Samples 源码](https://github.com/microsoft/Windows-classic-samples/blob/18cbd05ee44455cd7552804dcf2c9d6db619b412/Samples/Win7Samples/begin/LearnWin32/HelloWorld/cpp/main.cpp)。UpdateWindow 是否发送绘制消息取决于更新区域,资料只能说明要观察绘制/消息循环,不能证明本次失败原因。增加仅在 test-tmp 的原生 stderr 采集预载器后[单套件 13/13](../test-tmp/baseline-cleanup/flaui-native-diagnostic.log)通过,[原生进程记录](../test-tmp/baseline-cleanup/uia-native-stderr.log)无该异常;保留首次失败,不加 sleep、不增加总超时、不放宽断言。根因仍未定位,也未认定与历史桌面失败同因。 -- 首次 check:desktop 在失败后未进入后续阶段;随后分别运行[真实 UIA owner-death](../test-tmp/owner-death/run-zFyy30/report.json)、[原生托盘](../test-tmp/tray/run-7T1I4d/report.json)、[真实 Tray/Roslyn 工作流](../test-tmp/tray-workflow/run-WXEXn1/report.json),均通过。owner-death 的已观测 Helper 子树无存活者且目标夹具仍活着,验收后另行关闭夹具;工作流覆盖两实例、七次间隔观察中的热态保留、忙时拒绝、定向释放/恢复和托盘退出。这些专项不能覆盖掉完整桌面检查的失败,短样本不能证明长期无泄漏。 -- 当前磁盘 buildId=`3faff18fa1baf36b130867a1a466837d41314372ea389ea647d8309eaffb60c9`、Schema=`304d4030ad9e3ba8ad55159273a7b9b892bd8f6366980d3b2f9ce2d75a0fe2a9`、delivery contentId=`938bb0acd84d4b65ed1489d3eea806236bf80182cd9c97eab89e741964dc1df4`。文档收尾后再次 `delivery:verify` matched=true。revision 元数据是 fb3cd48,未提交源码增量由 sourceHash/artifactHash 标识,不能把它称为该提交的纯净构建。 -- 19:00 前置客户端核验:[预检回执](../test-tmp/baseline-cleanup/client-configuration-preflight.json)记录当前真实 hello 为 **0.13.2**、instance=`2b4f5ded-525e-45bf-a84d-0bf041132345`、buildId=`b3b4024ac8f367e429cc32b7951a16b2d4cec4716a95402911091c154863cc5d`、schema=`ede768d54559a7d33b582ee13fcdb7a29173597eddad10ceceb24fcc940fc84e`、provider=roslyn、根为 `I:/WinCode/test-tmp/client-roslyn-20260909/workspace`。安全读取实际 Codex MCP 配置确认它启动仓库 dist、绑定该旧夹具;未改全局配置或重连。 -- Codex CLI 对两个隔离目录的 `.codex/config.toml` 实际解析通过:同名 `wincode_project_preflight` 分别使用 A/B 绝对根,仓库根下查询不存在;未运行模型任务或产生新用户任务。配置样本保存在预检回执同目录,仅作用于两个生成目录。[Codex 官方文档](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)与[配置优先级](https://learn.chatgpt.com/docs/config-file/config-basic)支持受信任项目配置;[Claude Code 文档](https://code.claude.com/docs/en/mcp)说明项目 `.mcp.json` 及 Desktop Code tab 同名用户级 stdio 优先级例外。CLI 解析通过不等于真实桌面或第二种软件接入通过。 -- `USER_DECISION_REQUIRED`:第二种实际客户端已集中询问 Claude Code / Antigravity / Grok,等待选择;当前 PATH 未找到 Claude CLI,不据此推断整个机器未安装。真实客户端配置变更/重连与必要安装仍需对应授权。固定根改造需先完成这一兼容性前提;N3 完整容量、N4 其余竞争及 N5 长期/大项目证据仍保留。 -- GitHub 连接器读取当前 CI 时被服务端 HTTP 403 阻断,未改走其他鉴权路线或重复尝试;#36 最新 CI 及 Node 22 本轮验证均未核实。不把本地通过写成已修复聊天中未取得完整日志的远端失败。更新现有 CONTRIBUTING、CHANGELOG、详细计划和路线图,不修改旧工作日志,也未同步四份受管 Skill 或已安装手册。 -- 作者反证自审保留两类“看似通过”的风险:晚些出现的大量成功输出掩盖早期失败;资源已注销但旧清理快照仍持有对象。新增测试分别通过真实失败子进程和受控微任务交错验证。实际连线仍旧、完整桌面失败未定、Node 22/远端未验均明确保留;未进行独立模型或人工审核。 - -## 2026-09-10 19:28 — 第二客户端改为 Grok,完成一次真实查询(北京时间) - -- 用户先选择 Antigravity,随后要求“你测一次 GROK 吧”“换成 GROK,先不动 agy cli”。19:04 记录中的第二客户端选择问题已解决;按最新指示以 Grok 继续验收,不再启动或修改 agy CLI。本节追加后续事实,保留前一时点的记录。 -- Antigravity 的 A/B 生成目录使用各自 `.agents/mcp_config.json`。`mcp list` 未列出项目项,但两个实际 TUI 的 `/mcp` 均发现 15 工具,观察到 Node PID 30232/28484 分别绑定 A/B。一次 A 模型请求在 MCP 业务调用前失败:`FAILED_PRECONDITION (code 400): User location is not supported for the API use.`;未改账号、代理或模型绕过。B 只做握手,两会话均正常退出。摘要保存在[客户端预检回执](../test-tmp/baseline-cleanup/client-configuration-preflight.json);客户端日志包含无关历史片段,不复制到文档或外部服务。 -- Antigravity 首次启动过程中,其内置后台更新器自行将 CLI 1.1.27 替换为 1.2.0;日志记录启动更新进程,后续文件时间和 B 会话版本一致。没有发出安装/更新命令,但确实发生了环境变化,已向用户明确报告。[官方故障排查](https://antigravity.google/docs/cli/troubleshooting)说明内置后台更新机制。按用户后续指示停止 agy 操作,没有为处理更新而再改配置。 -- Grok 使用现有 `C:/Users/40218/.grok/bin/grok.exe`,版本前后均为 1.0.13(5e9a58528b76),SHA-256 均为 `BF43DC75F5478A106EAB1E86D422C963E4DBE9666CF14DAB363733D27BF1E672`。依据[官方无界面运行文档](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/14-headless-mode.md),仅为测试进程设置 `GROK_DISABLE_AUTOUPDATER=1`、`GROK_MEMORY=0`;未写全局环境变量或选择新模型/服务。既有 A/B 生成目录各自初始化空 Git 根和 `.grok/config.toml`,用于限定项目配置发现范围;没有改真实仓库或全局 MCP 定义。 -- 首次 Grok doctor 因文件夹未受信任拒绝启动;提示建议的 `--trust` 被本机 1.0.13 参数解析器拒绝。没有反复尝试该参数或手写信任配置,改用正常 TUI 在两个生成目录分别确认信任,然后退出。后续 [A doctor](../test-tmp/baseline-cleanup/grok-doctor-a.json)和 [B doctor](../test-tmp/baseline-cleanup/grok-doctor-b.json)均为 healthy=1/failing=0,确认绝对启动根、协议 2025-11-25、15 工具。受信任目录记录及测试会话历史是本次真实客户端运行产生的状态。 -- 只运行一次 A 项目模型任务,保持 default 权限模式,以[官方权限规则](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/22-permissions-and-safety.md)定向允许候选服务的 hello 和 find_code_symbol,关闭子代理与 Web 搜索。实际共 5 次工具调用:读取已安装 WinCode SKILL、两次工具发现、两次 MCP 调用;`--tools ''` 没有将客户端能力严格裁剪为仅两个 MCP 工具,不能据参数声称隔离了全部本机能力。没有出现 shell、编辑、workspace_open 或子代理调用;B 不追加模型任务。 -- [Grok 验收回执](../test-tmp/baseline-cleanup/grok-acceptance.json)及[原始 MCP 结果](../test-tmp/baseline-cleanup/grok-tool-results.json)经过本地断言核对:hello 为 0.14.0、instance=`2eddc10a-9d01-48f6-a521-c9d937249ca3`,buildId/schemaHash 与本节之前的磁盘构建一致,workspace 为 project-a;Api 查询唯一返回 `Api.cs:2`。provider 为 local-text,结果明确为降级文本声明扫描。本次没有验证 Grok Roslyn、A/B 同时模型查询、同名全局/项目配置冲突或当前 Codex 新构建重连。 -- 模型任务 exit=0、stopReason=end_turn,客户端报告默认模型 `grok-4.6-build`、4 个模型回合、累计 74572 tokens(含 37632 cache-read tokens),费用 **0.01651856 USD**;金额为客户端回报,未独立核对账单。两个 Grok TUI 和模型任务均已退出;收尾查询没有发现绑定这两个生成目录的 Node Gateway 残留。临时回执与断言脚本留在既有 test-tmp,不写入已安装 Skill。 -- 同步现有详细计划和路线图:第二客户端已选定且最小真实调用通过,N1 固定根、完整 N3 仍未实施;当前 Codex 0.13.2、新构建消费者 Roslyn、完整桌面 34/35 的未定位失败、Node 22/远端 CI 等缺口继续保留。此次客户端验收未修改生产代码,不重复完整测试,也未提交或推送。 -- 文档收尾:本轮改动中的 29 个本地 Markdown 链接均存在,`git diff --check` 通过;源码和新增回归经作者复核,没有扩大已通过测试的结论。 - -## 2026-09-10 20:27 — N1 固定工作区实现与验收(北京时间) - -- 用户确认继续,执行已接受的 N1 → 真实客户端检查 → N3 顺序。第二客户端使用 Grok;未再启动或修改 agy CLI。 -- 源码契约升级为 0.15.0:Gateway 启动时固定根,显式 --workspace/-w 需要已有目录的绝对路径,缺省固定 cwd 并报告来源。Router 和 WorkspaceManager 共同拒绝其他根;config.workspaceRoot 在运行期不可写。WORKSPACE_MISMATCH 同源文本/structuredContent 附 active/requestedWorkspace 和 select_workspace_connection。拒绝在排队、指纹读取、排空、watcher/cache/trash/Host 变更前发生;保留同根健康热态与故障恢复。此规则不是 OS 原子安全隔离。 -- 迁移原切换测试为独立 A/B 实例及同根实际停止监听后的恢复;没有删除清理、取消、故障注入门禁。新增 8 项固定根测试覆盖核心入口、普通文本/相对路径/组合工具、中文空格、Windows 大小写与分隔符、父子目录及 junction、CLI 缺省/非法路径;另补 2 项恢复准备阶段失败门禁。诊断脚本退役未对应当前 LocalText 实现的 text-reset 注入项,保留真实 text-initialize,并在诊断报告注明。 -- 最初 5 项新增回归在旧实现全部失败,修改后 5/5;第一轮相关迁移 99/99。首次完整检查 [403 项中 401 通过、1 失败、1 跳过](../test-tmp/check/2026-09-10T12-13-29-501Z-core/report.json):监听已停止时,排空超时现在保留恢复门,旧断言仍匹配顶层文本。补强断言验证 recovery.phase=drain、原始原因、持有请求未被释放及新业务仍被阻止;[最终核心](../test-tmp/check/2026-09-10T12-14-51-064Z-core/report.json) 403 项中 402 通过、1 项可选 TavernDesk 跳过,0 失败。类型检查、三个 native Release 发布、stdio 及交付校验通过。 -- [真实 Roslyn 22 场景](../test-tmp/roslyn-gateway/run-JYtTC8/report.json)、[E4 17 场景](../test-tmp/error-contracts/run-1gdOpi/report.json)、[十次手动释放/重载](../test-tmp/manual-release/run-ArU1cg/report.json)通过。真实 A/B 连接验证拒绝错误根后 A 的 Host/PID、快照、session/watch/cache 不变,B 可继续查询,精确定位跨连接拒绝。故障诊断 12 项无未解决发现;[混合负载](../test-tmp/mixed-load/run-HLx4ss/report.json) 10 轮、70 调用、0 自有存活进程。 -- [本次完整桌面检查](../test-tmp/check/2026-09-10T12-21-13-763Z-desktop/report.json)通过,含 35 项 UI 测试和 owner-death、Tray、真实 Tray/Roslyn 工作流。本轮 native 仅同步版本,未修改提示窗绘制;19:04 的提示窗失败及既有托盘偶发失败根因仍未定位,不能以本次通过宣布修复。 -- [真实客户端回执](../test-tmp/fixed-workspace/client-acceptance-n1.json)核验 Grok 的 0.15.0 build/schema、固定 A 根、Roslyn Save(int) → 1 引用 → B 根 WORKSPACE_MISMATCH → 原定位仍为 1 引用 → 影响分析。客户端重复读取旧已安装手册及参数 schema,触及预设 12 模型回合后退出;重构步骤未执行,不能称完整八步脚本成功。Grok 默认模型未更换,报告费用 0.03867738 USD;可执行文件哈希前后一致,进程环境禁用自动更新和 memory。 -- Codex 首次 CLI 测试无法解析桌面 cua_repl transport;后用官方命令级 --ignore-user-config,仅加载测试 MCP,保留现有 gpt-6-astra/medium 和登录。首次 hello 被权限系统拒绝:MCP tool call requires approval, but approval policy is never。立即停止,没有更改审批策略或绕过;已询问继续 Grok/SDK 并保留待验,或由用户在客户端批准后补验。当前 Codex 桌面连接仍未重连。项目 .codex/.grok 测试配置修改前各保留 config.pre-n1.toml;不修改全局 MCP 或已安装 Skill。 -- 同步 README、架构说明、配置指南、CHANGELOG、CONTRIBUTING、安全边界及仓库内四份受管 Skill;历史报告不改写。尚未提交/推送;Node 22、远端 CI、长时大项目、多模型同时 A/B 及完整当前 Codex 消费者闭环仍未验。接着实施 N3,后续源码变化需新的验证报告,不能沿用本节构建身份声称新代码通过。 - -### 2026-09-10 20:30 — Codex 验收设置澄清 - -用户选择继续 Grok/SDK,并提供当前 WinCode 配置截图质疑是否为本次 CLI 设置问题。只读核对 codex mcp get 与截图一致:node + dist/index.js + 旧 client-roslyn-20260909 工作区/配置,参数排列正常。用户全局 sandbox_mode 为 danger-full-access,而验收命令显式采用 --ignore-user-config 和 read-only;这不是用户截图配置造成的工具审批失败。OpenAI 官方 MCP/配置参考将工具审批模式与运行审批策略分开,CLI 返回的 never 拒绝只证明本次隔离启动的授权设置不满足调用条件,不能归为 WinCode 兼容性或用户设置错误。已向用户澄清,停止 Codex 补验,不更改其策略。参考:https://learn.chatgpt.com/docs/extend/mcp?surface=cli 及 https://learn.chatgpt.com/docs/config-file/config-reference 。 - -## 2026-09-10 21:10 — N3 有界准入完成,Grok/SDK 验收与 N4 实际失败(北京时间) - -- 按已确认 N1 → 实际客户端验收 → N3 顺序完成当前增量。用户选择继续 Grok/SDK、Codex 单列待验,并指出本次 CLI 设置问题;20:30 澄清继续有效:截图参数未发现错误,隔离 CLI 的 MCP 审批要求与生效 never 策略冲突,不能归为 WinCode 兼容性失败。未再启动 Codex 验收、未改全局配置,先不动 agy CLI。 -- 新增 [RequestAdmission](../src/Core/RequestAdmission.ts):32 个未完成业务请求、4 个 hello/tools/list 状态槽、64 KiB 原始 UTF-8 JSON 参数。容量在准备/执行前获取;复用既有 Mutex 的 FIFO 与物理取消节点,启动共享等待不保留连续取消的 Promise 链。排队/准备/适配器共用总截止时间;SERVER_BUSY 只用于尚未执行的满额请求,REQUEST_TIMEOUT 不自动重试。实际清理完成后才释放占用。workspace_open 占业务容量但不计入其自身等待排空的 inFlight;关闭/手动释放保留等待与清理约束。 -- 状态路径不等候慢业务;hello 只读已知缓存状态,未观察的磁盘统计为 null,主动诊断才刷新。统计包含 accepted/completed/rejected/cancelled/timedOut、active/executing/waiting 和等待/执行耗时;executionMs 是非队列墙钟时间(含 I/O/清理),不是 CPU 时间。FlaUI/诊断/Repomix 健康检查沿用调用剩余预算和取消信号。 -- [14 项准入回归](../tests/request-admission.test.ts)覆盖原始参数预算、4/8/16 正常突发、128 请求、连续取消补入、执行取消未完成收尾、启动与恢复等待、关闭和状态容量。首轮核心 [415 通过/1 失败/1 条件跳过](../test-tmp/check/2026-09-10T12-47-55-000Z-core/report.json)被架构规则抓到 Gateway 直接访问 workspace;改为 Router.assertWorkspace 后完整重跑通过,没有弱化规则。此前错误契约夹具同步固定根与诊断 signal;真实 Roslyn 脚本的旧“心跳占 1 个业务请求”假设造成 [run-1gklkM 提前检查 PID](../test-tmp/roslyn-gateway/run-1gklkM/report.json),改为 inFlight=0、business.active=0、waiting=0 后保留严格进程退出断言。新取消场景实际等待 1082 ms 后归零,不能以客户端 Promise 先返回当作清理完成。 - -| 当前构建的实际验证 | 结果与回执 | -| --- | --- | -| Node 24.19.0 核心/构建/交付 | [417 项,416 通过、1 项可选 TavernDesk 条件跳过](../test-tmp/check/2026-09-10T12-50-13-167Z-core/report.json);typecheck、三原生组件、stdio 与 delivery 均通过 | -| 真实 Roslyn | [22 场景通过](../test-tmp/roslyn-gateway/run-ystDNe/report.json),含实际 MSBuild 取消/崩溃/超时及自有后代退出 | -| E4 错误契约 | [17 场景通过](../test-tmp/error-contracts/run-SZG6AN/report.json) | -| 桌面完整检查 | [35/35,后续 owner-death、托盘与双实例工作流全部通过](../test-tmp/check/2026-09-10T13-02-41-136Z-desktop/report.json) | -| 手动释放 | [10 轮通过](../test-tmp/manual-release/run-WQi8n9/report.json),实际退出、旧定位拒绝/新搜索恢复,已观测 survivors=[] | -| 三实例 SDK | [11 场景通过](../test-tmp/multi-agent/run-IVKMMY/report.json),限定 A/B 并发冷启动后再启动第二 A Host;已观测 survivors=[] | -| Grok Build 1.0.13 | [7 次真实 MCP 调用通过](../test-tmp/request-admission/grok-acceptance.json),9 个模型回合正常结束;当前 build/schema/Roslyn 一致,拒绝 B 后 A 原定位仍返回 1 项引用,最终业务占用/等待为 0;客户端报告费用 USD 0.03721436 | - -- SDK 具体结果:普通 4/8/16 突发和 96 次交错精确引用通过;128 次单实例搜索受理 32、SERVER_BUSY 96,兄弟实例仍可查询且 Host/快照保持;64 次突发中成功 32、客户端取消 16、过载 16。队列结束归零。采样 Node RSS 约 92.66–111.05 MiB,三实例完成 96 请求时累计最大等待约 12.68 秒;这是离散小项目样本,不是实时峰值或长期趋势。手动释放回执另有 Node/已观测原生进程工作集、私有字节与累计 CPU 快照;并发原生峰值/句柄趋势仍未测。客户端 SDK 发送端仍出现 11 个 drain 监听器警告,阶段后监听器为 0;未抬高阈值或增加客户端限流。 -- N4 新失败必须保留:[默认三个 Host 并行冷启动 run-zJc2aM](../test-tmp/multi-agent/run-zJc2aM/report.json)在同物理 A/App.csproj 上竞争 obj/Debug/net10.0/App.GeneratedMSBuildEditorConfig.editorconfig,返回 PROJECT_LOAD_FAILED;结束后已观测 survivors=[]。这是实际共享构建输出冲突,尚未修复。verify-multi-agent 默认继续同根并发;--serialize-same-root-startup 仅分离 N3 与 N4,报告显式记录其限制。未添加跨进程锁、改 MSBuild 属性或复制持久缓存。此前 [run-fgPCWl](../test-tmp/multi-agent/run-fgPCWl/report.json)另因 Tray 夹具硬编码旧 0.14 版本失败,已改读 package.json,不能把该失败和 MSBuild 冲突混同。 -- 交付:version=0.15.0;buildId=be08ba26c3d0f82e596161057a595ddb152b7d253d46adc86273ff7c841e151e;schemaHash=4f8a6424c23978ebffe77111f4687c87336de64320f7d24cde8bdeede8ab804f;delivery contentId=24a58650420658f71f3a88c5d2b91bb4a69d5846affccbb3da30c511fdc7376c,收尾 delivery:verify matched=true。版本仍为本地未发布增量,revision 元数据 fb3cd48 不代表干净提交。更新既有 README/架构/配置/Skill 源与计划路线图;未同步已安装 Skill,未安装依赖、提交或推送。 -- 作者反证自审:取消响应快但清理尚未结束,可能错误放入新工作;受控测试与真实 MSBuild 退出检查分别覆盖。另一个反例是实例/PID/快照隔离均正确,但 MSBuild 输出仍共享,已用实际失败证据保留 N4。未做独立模型/人工审核。Codex 完整新构建消费、模型 A/B 并发/编辑闭环、同根并发冷加载、共享存储与 UI 其余交错、原生峰值/长期资源、Node 22 和当前增量远端 CI 仍未完成;历史录制提示窗/托盘根因没有因本轮通过而关闭。 -- 收尾文档链接检查发现 4 份旧回执在当前工作区缺失(Ho43hD 配置预览、VcfIp2/xaa7Wf 托盘、04-59 桌面检查);计划改为注明缺失并指向保留的历史工作记录,未补造文件或修改历史日志。当前 SDK/释放 50 个已观测进程身份重新只读核验 survivors=[]。 - -## 2026-09-10 22:05 — N4 两种隔离原型对照完成,正式方案仍待修订(北京时间) - -- 授权来源:用户质疑“是否最优”后,已同意先比较私有设计时输出与跨进程加载锁,并回复“开始吧”。本轮只新增 [比较入口](../scripts/verify-design-time-concurrency.mjs)、[构建/夹具辅助](../scripts/roslyn/design-time-prototypes.mjs)和 [C# 原型插桩](../tests/fixtures/design-time-comparison/PrototypeCoordination.cs),复制当前 Host 源码到 test-tmp 编译。复用锁定 SDK 10.0.303、已有 NuGet 缓存和生产 RoslynHostClient;没有引入依赖、模型调用或共享服务。正式 Host 源码、Gateway、dist、当前原生构建、全局客户端设置和 agy CLI 均未改动,没有提交/推送。 -- 两种原型的实际范围:private 仅覆盖每项目相对 IntermediateOutputPath 为 `.cache/wincode-msbuild////`,保持 restore 的 BaseIntermediateOutputPath/MSBuildProjectExtensionsPath;lock 仅在 ReloadAsync 从加载到快照冻结期间持有工作区根下零字节文件的 FileShare.None,25 ms 可取消等待,仅重试 sharing/lock 错误。正常语义查询不获取该锁;进程结束由 OS 关闭句柄。两者都不是已经进入生产的通用并发协议。 -- [首轮 run-elfe3J](../test-tmp/design-time-comparison/run-elfe3J/report.json)为 **验证脚本错误**:从符号顶层读取 project/position,实际定位字段在 location;Save 子串查询还匹配 WPF HandleSave。修正为精确名称/签名及实际 location,并要求基线有效后继续比较。其 12 项失败不计入候选结论,原始回执保留。 - -| 实际对照 | 结果与证据 | -| --- | --- | -| 语义 12 项 | [run-UcG4RR](../test-tmp/design-time-comparison/run-UcG4RR/report.json):11 通过、1 候选失败;原实现与锁在普通 C#、项目引用、WPF、自定义目录全部通过;private 在已有自定义 intermediate 目录时 CS0579 特性重复 | -| 并发/退出 13 项 | [run-HdGxkl](../test-tmp/design-time-comparison/run-HdGxkl/report.json):11 通过、2 候选失败;同根基本项目两种候选通过;根锁在不同入口共享 Lib 时使先加载快照失效,父/子根打开同一实际项目则越过协调;等待取消、实际 MSBuild 后代存在时取消/崩溃、兄弟 Host 继续查询均通过 | -| 外部干扰/编辑 14 项 | [run-k5CDWZ](../test-tmp/design-time-comparison/run-k5CDWZ/report.json):8 通过、6 未达到候选要求,含原实现的 2 个反例;外部文件占用、真实 dotnet build、先后加载共享项目、不同配置、源码修改及重载期间热查询分别记录 | -| 并发 6 项复核 | [run-BkHrBq](../test-tmp/design-time-comparison/run-BkHrBq/report.json):修正一处同值自比的快照断言,改比实际引用响应的 snapshot,并保留已关闭 Host 的 trace;4 通过、2 失败。原实现再次在同项目冷加载发生 PROJECT_LOAD_FAILED,这次是 `.NETCoreApp,Version=v10.0.AssemblyAttributes.cs` 写入竞争;根锁共享入口的 SNAPSHOT_STALE 再现。两种候选基本并发通过,private 在关闭/回收兄弟实例后仍保留真实快照及引用 | -| 原精确定位 2 项复核 | [run-zB6QMV](../test-tmp/design-time-comparison/run-zB6QMV/report.json):两种候选均在编辑后及重载后拒绝原 references 定位,返回 SNAPSHOT_STALE;重新定位后引用由 1 变为 2,编译无错误 | - -- [最终汇总](../test-tmp/design-time-comparison/run-zB6QMV/comparison-summary.json)包括原始回执 SHA-256、各场景结果、进程记录和剩余私有产物统计。首批是 39 个不同场景(30 通过、9 未达要求),另有 8 次针对性复核;completed=true/命令正常结束表示诊断实验完成,不能当作候选通过或加入生产核心 417 项计数。原始失败未改写,两个候选均未完成全部验收。 -- 私有输出的具体反例:普通项目、跨项目引用、WPF 的 InitializeComponent/事件/精确引用编译通过;自定义 `artifacts/int/...` 已有生成文件后,覆盖 IntermediateOutputPath 改变默认 Compile 排除规则,旧目录生成的特性文件与新目录一起进入编译,CS0579。没有关闭 GenerateAssemblyInfo、删除原构建产物或放宽 compilationErrors 断言以求通过。 -- 根锁的具体反例:父根与子根生成不同锁文件,trace 显示同一个实际 csproj 在前一个 Host 的 MSBuild 阻塞期间被另一个 Host 完整加载。对相同工作区,先加载 App 再加载 Peer 或先 Debug 后 Release,新 obj 下的 `.cs` 被现有 WorkspaceInputs 自动候选枚举纳入指纹,前一个 Host 返回 SNAPSHOT_STALE。原实现固定先后加载同样再现,说明它还涉及既有输入判定,不能全部归因于锁本身。 -- 外部干扰分开解释:PowerShell 子进程持有默认 editorconfig 的独占句柄时,原实现/锁都 PROJECT_LOAD_FAILED,private 能加载;这是受控故障注入,不是真实 Visual Studio 验收。另一个真实 `dotnet build --no-restore -p:UseSharedCompilation=false -nodeReuse:false` 在指定 target 暂停,Host 加载后放行;private 的快照因默认 obj 新增特性 `.cs` 失效,显式重载后恢复正确 1 项引用;锁原型在该基本项目的共享生成内容未变,本次通过,但未协调外部构建。所有这些拒绝都保留既有输入检查,没有接受陈旧引用。 -- 生命周期与成本:两个原型都通过真实 MSBuild 后代的取消/强杀及兄弟继续工作;等待根锁的 Host 取消前尚无 BuildHost 后代。全部 5 份有效报告共记录 207 个进程身份,结束时均 survivors=[]、cleanupFailures=[]。比较父进程在确认实例退出后回收指定私有 UUID 前缀,基本项目 7 文件/1994 字节、图项目 14 文件/4001 字节;兄弟仍能查询。其他私有产物作为取证文件留存,**生产自动回收未实现**,不能用进程退出代替磁盘生命周期验收。工作集约 130–147 MiB 为语义调用时的离散采样;基本双冷加载中锁的后一个 ready 约 6.17 s(含等待约 2.96 s),private 约 3.15–3.59 s。运行顺序/JIT/缓存未控制,不主张性能最优、实时峰值或长期稳定。 -- 当前判断:优先继续完善私有输出原型,其隔离覆盖比“按工作区根加锁”更适合现有多连接目标;但不将其直接落入正式代码。下一步需要保留原项目 Compile 排除语义、准确划分实际编译输入与其他构建产物,并明确实例产物回收。反证验收必须含“实际被编译的生成文件改变后旧定位仍失效”,不能直接忽略整个 obj。**USER_DECISION_REQUIRED:本轮授权的两原型比较已完成,正式改变 MSBuild 求值、输入判定或产物生命周期前确认具体修订方案。** 不扩展为全局 Host 池、项目复制或通用跨进程 Lease/FSM。 -- 验证范围:脚本语法检查与 git diff --check 通过(另有既存 CRLF 提示);delivery:verify 再次 matched=true,contentId 仍为 `24a58650420658f71f3a88c5d2b91bb4a69d5846affccbb3da30c511fdc7376c`,报告 productionChanged=false。没有重复完整核心/桌面测试;本轮直接调用隔离 Host,不冒充 Grok/Codex 新 MCP 验收。未测真实 VS、大型项目、多目标框架、自定义 source generator、长期磁盘回收和故障峰值,也未作独立模型/人工复审。详细计划和路线图同步这些实测边界,N4 未关闭。 - -## 2026-09-10 22:28 — N4 修订原型与正式接入中途交接(北京时间) - -- 用户随后以“开始吧”“继续”确认继续修订私有输出方案,22:05 的方案确认项因此已获授权;最新指令为结束今晚工作、写好文档与待办、提交当前进度到 PR。按该指令停止开发和进一步运行测试,保存草稿检查点,不合并、不发布。 -- 修订方案复用已安装 SDK 的 Microsoft.Build.dll,通过原项目求值保留自定义 intermediate 的 Compile 排除及原 CustomBefore hook,再为每个 Host 分配 UUID 私有设计时输出。自动候选过滤同时保留实际编译输入、显式 Compile 和无法可靠判定的保守路径,未整体忽略 obj。Native 使用所属 UUID 的清单和活动句柄记录产物,正常关闭回收;Gateway 在确认自有 Host 退出后补偿清理,逐项校验根、UUID、清单及链接边界,不能删除兄弟实例产物。未新增依赖安装或全局服务。 - -| 修订原型的实际验证 | 结果与本地回执 | -| --- | --- | -| 普通 C#、项目引用、WPF、自定义 intermediate 的三种实现对照 | [run-PpetSI](../test-tmp/design-time-comparison/run-PpetSI/report.json):12/12 通过,私有输出的原自定义目录 CS0579 反例恢复 | -| 私有输出外部干扰与编辑 | [run-LHMyyy](../test-tmp/design-time-comparison/run-LHMyyy/report.json):6/6 通过,含外部文件占用、真实构建、共享项目先后加载、不同配置、编辑和重载期间查询 | -| 输入判定反例 | [run-ejjK26](../test-tmp/design-time-comparison/run-ejjK26/report.json):3/3 通过,实际生成文件修改、新显式 obj/Manual/*.cs 均拒绝旧快照并可重载恢复,原 CustomBefore hook 保留 | -| 并发与产物生命周期 | [run-hJPqME](../test-tmp/design-time-comparison/run-hJPqME/report.json):5/5 通过,含基本/共享图/嵌套根并发、取消与崩溃;正常及父进程补偿清理后所属私有文件为 0,兄弟仍可查询,survivors=[]、cleanupFailures=[] | - -- 正式源码已接入 [DesignTimeBuild](../tools/WinCode.Code.Host/DesignTimeBuild.cs)、[OwnedBuildOutputs](../tools/WinCode.Code.Host/OwnedBuildOutputs.cs)、[DesignTimeArtifacts](../src/Adapters/DesignTimeArtifacts.ts),并连接 WorkspaceSession、WorkspaceInputs、RoslynHostClient。输入策略协议升为 2,适配器和已有契约夹具同步;旧策略 Host 的专项拒绝测试尚待补充。最后加入的重叠输出目录候选合并、从实际 Imports 解析原 hook 两项修改在本轮原型回执之后,尚未构建验证。 -- 已实际执行 `node node_modules/tsx/dist/cli.mjs --test tests/design-time-artifacts.test.ts tests/roslyn-contracts.test.ts`:**23/23 通过**,含仅回收所属 UUID、全量预校验、越根/其他 UUID/链接/损坏清单拒绝及 Roslyn 契约。该命令未生成持久报告;不得将这些 TypeScript 测试当作正式 Native 编译或 MCP 并发验收。 -- **当前最终源码为 WIP**:尚未执行正式接入后的 typecheck、Native Release 构建、完整核心检查、默认三 Host 并发冷加载和交付身份核对。磁盘 dist/Native 及前文 417 项、真实客户端等结果对应 N4 接入前构建,不覆盖最后源码;AssemblyAttributes.cs/editorconfig 并发问题仍不能在生产验收层面关闭。 -- 明日首先处理两个明确的验证入口问题:旧 buildPrototype 文本锚点已不匹配正式接入后的 WorkspaceSession;原型 WINCODE_N4_INSTANCE 与正式 WINCODE_BUILD_INSTANCE 必须统一到被测 Host 的真实 UUID,避免 blocker/产物清理检查错位。不得把当前源码作为原实现基线。然后正式构建,并按 [2026-09-11 恢复顺序](https://github.com/linnnn89/WinCode/blob/d51f3e105b07b50b1e2535ca541f532ea77b3fc5/WinCode-%E4%B8%8B%E4%B8%80%E8%BD%AE%E5%B7%A5%E7%A8%8B%E5%8C%96%E8%BF%AD%E4%BB%A3%E8%AE%A1%E5%88%92%E4%B9%A6.md#2026-09-11-%E6%81%A2%E5%A4%8D%E9%A1%BA%E5%BA%8F)完成并发、输入、生命周期与交付验收。 -- 反证自审保留:原型通过不代表最后两个源码修订正确;动态 ProjectReference、多目标框架、自定义 Compile 仍需检查保守失效行为;Gateway 与 Host 同时硬退出或断电后的孤儿目录未实现自动回收。尚未独立审查、Node 22 验证或当前 PR CI 验证。Codex 继续单列待验,先不动 agy CLI,没有再次更改客户端或权限策略。 -- 同步 README、CHANGELOG、架构说明、路线图和计划的当前状态,保留历史失败日志。test-tmp 原始回执仅保留本机、受 Git 忽略,不上传原始模型配置、日志或运行产物;远端 PR 提供结果摘要及可继续执行的待办。当前进度按用户明确授权提交并推送为草稿 PR,等待明日继续。 - -## 2026-09-11 08:47 — PR #37 本地续作:错误契约修复与私有输出生产验收(北京时间) - -- 授权与范围:用户先要求下载昨晚 PR 的进度、与本地对齐并分析计划缺口,随后明确“继续开展工作”。在 `D:/CODEX PROJECT/WinCode MCP` 对齐 `codex/runtime-baseline-and-cleanup` 的 `aa6fc7f457f5a18b122fd791aec2824ed121195d` 后继续本地修复及验证;原 `codex/architecture-boundaries` 分支保留。本轮没有提交、推送、修改 PR 状态、合并或发布,没有安装新依赖、调用模型或改动真实客户端/已安装 Skill/agy CLI。PR #37 实际为 open、draft=false;昨晚“草稿”描述的是 WIP 检查点,不是 GitHub draft 状态。 -- 证据对齐:通过 GitHub 连接器读取 [PR #37](https://github.com/linnnn89/WinCode/pull/37) 和 [CI run 34489311570](https://github.com/linnnn89/WinCode/actions/runs/34489311570)。Node 22 核心为 422 项、421 通过/1 条件跳过,构建已完成;真实 Host 在 39 个已完成场景后因损坏项目重载预期 `PROJECT_LOAD_FAILED`、实际 `QUERY_FAILED` 而失败,后续同一步网关/owner-death/释放未执行。Node 24 构建与核心通过。CI 合成 merge 的 tree 与 PR aa6fc7f 相同,这些是原 PR 的证据,不是本轮未推送增量的远端结果。下载的受控 CI JSON 保留在 `test-tmp/pr37-audit-20260911`。昨晚四份修订原型 `run-PpetSI/run-LHMyyy/run-ejjK26/run-hJPqME` 及其他历史 test-tmp 未随 Git 下载到本机,历史工作日志不改写。 -- 生产修复:[DesignTimeBuild.cs](../tools/WinCode.Code.Host/DesignTimeBuild.cs) 在实际 `ProjectCollection.LoadProject` 边界将 `InvalidProjectFileException` 映射为 `PROJECT_LOAD_FAILED`,不改变 Program 的 MSBuildLocator 先后顺序。Gateway 与 Native 同时验证 Configuration/TargetFramework 的字面目录段,拒绝点段、尾部点/空白、分隔符、MSBuild 属性/列表/转义字符;私有输出只生成一次并核对规范化后仍在所属 UUID 内。新增缺失/旧 inputPolicy 的拒绝与进程回收测试,错误码保持 `HOST_PROTOCOL_ERROR`,不能持有成功快照。 -- 验证入口:[verify-design-time-concurrency.mjs](../scripts/verify-design-time-concurrency.mjs) 改为当前正式发布 Host 和生产 RoslynHostClient 的验收,不再复制/插桩当前源码作为原基线。运行前后核对 delivery/source 身份,blocker 与 owner.json 使用 client 的实际 `WINCODE_BUILD_INSTANCE`;每个选中场景、编译/引用结果、退出及产物清理分别留证。取消/强杀后由生产关闭逻辑清理,验证脚本不通过手工删除私有目录替代被测回收。完成全部选中场景、非空选择、无失败/交付变化/清理失败/已观测残留才 success=true,否则非零退出。旧原型辅助和 C# 夹具保留作历史材料;正式入口仅接受 `--phase`、`--filter`。 -- [verify-multi-agent.mjs](../scripts/verify-multi-agent.mjs) 增加交付前置核验和 `--roslyn-only`;该选项保留真实 A/B/A 三 Host 同时冷启动、全部 Roslyn/准入/传输验证,只排除原生 Tray 容量项,默认完整模式不变。CI Node 22 已加入该入口及完整私有输出矩阵,失败报告按 always 上传,15 分钟作业预算未提高;本轮未推送或触发 CI。 - -| 本轮实际验证 | 结果与本地证据 | -| --- | --- | -| 核心/类型检查/构建/交付 | [core report](../test-tmp/check/2026-09-11T00-17-41-069Z-core/report.json):425/425,0 跳过;现有锁定 SDK 10.0.303、Node 24.19.0,包含 Gateway 与三 Native 组件的正式发布和交付核验 | -| 产物归属及 Roslyn 契约专项 | `test-tmp/n4-production/contracts-after.log`:26/26;目录段测试先在修改前实际失败,`contracts-before.log` 保留 | -| 真实 Native Host | [fixture-3Rpx5Q](../test-tmp/roslyn-host/fixture-3Rpx5Q/report.json):59 场景通过,含损坏项目失败/修复链路、原生 16 组非法配置/框架组合在私有目录副作用前拒绝 | -| 真实 MCP Roslyn Gateway | [run-Rse2Ni](../test-tmp/roslyn-gateway/run-Rse2Ni/report.json):22 场景通过,含实际 MSBuild 取消/崩溃/超时及恢复 | -| E4 错误契约 | [run-bWLzfr](../test-tmp/error-contracts/run-bWLzfr/report.json):17 场景通过 | -| 真实三个 SDK 客户端 | [run-qwMOna](../test-tmp/multi-agent/run-qwMOna/report.json):10 场景通过,startupMode=all three hosts parallel,survivors=[];精确引用 A/B/A=1/2/1,128 请求受理 32、SERVER_BUSY 96,结束占用归零 | -| 完整私有输出生产矩阵 | [run-1toEyr](../test-tmp/design-time-production/run-1toEyr/report.json):20/20,228.291 秒;语义 5、并发/退出 5、构建/编辑干扰 6、输入反例 4,productionChanged=false、cleanupFailures=[]、survivors=[] | -| 验收入口负例 | [run-GkatiK](../test-tmp/design-time-production/run-GkatiK/report.json):无匹配场景的 filter 实际退出 1,success=false、cases=[]、observed=[],该失败符合预期 | -| 手动释放 | [run-TG4ZUS](../test-tmp/manual-release/run-TG4ZUS/report.json):10 轮通过,已观测残留为空 | -| Gateway 在加载中死亡 | [run-MayNi6](../test-tmp/owner-death/run-MayNi6/report.json):9 个已观测进程身份全部退出,survivors=[]、cleanup=[] | -| RepomixAdapter owner 死亡 | [run-DS7jFN](../test-tmp/owner-death/run-DS7jFN/report.json):通过,survivors=[]、cleanup=[];使用受控 Node CLI,不声称真实 Repomix 或完整 Gateway 集成 | - -- 矩阵反证:已有自定义 intermediate 先按与 Host 相同的 Configuration/TargetFramework 连续普通构建两次,再检查生产 Host 编译无错误;双 TFM `net10.0/net10.0-windows` 按条件分别得到 1/2 项引用,关闭兄弟后保留原快照。源码编辑使旧定位失效,重载后引用从 1 变为 2,旧定位继续 `SNAPSHOT_STALE`。项目文件中显式引用 App→Lib 改为 App→Peer→Lib 后,先更新生成夹具的 restore 输入,再重载为 3 个项目/2 项引用;新 Peer 输出进入所属清单并随关闭回收。这不是任意 target 动态生成引用的证明。另按报告内容复核 65 份记录的 compilationErrors 均为空。 -- 失败过程保留:`run-Lux8mM` 的输入反例首次返回 `INPUTS_CHANGED`,符合 watcher 事件落在 Capture 期间的既有契约;另一个引用变更场景尚未 restore 更新项目图。修正夹具与断言后要求首次拒绝且不返回证据、紧接着严格 `SNAPSHOT_STALE`,显式重载后引用正确。`run-WDKrAC/run-Y20hsP` 暴露验证脚本遗漏 `runDotnet` 导入,补齐后分别针对性通过,最终由完整 20 项再次覆盖。`run-KuzB7O` 的预构建未显式传入配置/TFM,早期 Directory.Build.props 求值到另一目录;原 MSBuild 求值同样将旧 `.cs` 纳入 Compile。这是夹具条件不一致,未通过改生产排除规则、删除旧产物或关闭特性生成解决;参数统一后 `run-LZbC6v` 及最终矩阵通过。临时筛选变量拼写错误 `run-d411ZJ` 也以失败保留。所有日志位于 `test-tmp/n4-production`,未改写失败回执。 -- 交付身份:version `0.15.0`;buildId `65102c51f7d53138fe7874ba656d7a5e9938168dc9f32c5e2c54ad400387baaa`;schemaHash `4f8a6424c23978ebffe77111f4687c87336de64320f7d24cde8bdeede8ab804f`;delivery contentId `0fded67d16aed5d7ca3c98b566fd6ebe57cdaabda8ea7d18ece422de8dc3c522`;发布 Code Host DLL SHA-256 `631814d5b9fd0997b6c952d56a05c412a989123225b8a4fbb35326418d45f5fd`。收尾 `delivery --verify` matched=true。revision 元数据仍为 aa6fc7f,当前源码是该提交上的本地未提交增量,不能称为干净提交构建。同步来源与客户端部署继续区分,codexConnectionVerified=false。 -- 剩余范围:具体同项目 MSBuild 输出竞争可以在本地生产回归层面关闭,N4 整体未关闭。共享缓存并发写入/清理/读取、双实例源码编辑、UI 窗口交错、任意 target 动态项目图、自定义生成器及双重硬退出/断电孤儿产物继续开放。当前增量未做 Node 22、远端 CI、真实 Grok/Codex 消费、完整桌面/托盘复验、长期资源或独立模型/人工审核。SDK 在 128 请求突发中仍有 11 个 drain 监听器警告,阶段结束为 0;未调整阈值,短时归零不证明长期无泄漏。本轮无新的 USER_DECISION_REQUIRED;后续若需新的清理政策、依赖、真实客户端配置或外部交付,应明确范围后按有效授权执行。 -- 文档同步:更新既有 README、CHANGELOG、架构说明、详细计划和路线图,把已完成的恢复工作移出待办,标记本机缺失的历史报告。历史日志保持原文;本轮回执是本地可核验文件,不随源码提交,也不冒充已上传 CI artifact。 -- 最终检查:四个改动的 `.mjs` 入口/辅助文件 `node --check`、`git diff --check` 和当前文档/本节报告链接存在性检查通过。HEAD 与已抓取远端分支提交差异 0/0,工作区保留 14 个文件的本地增量。交付与完整矩阵结束后未再修改生产源码,也未重复无关完整测试。 - -## 2026-09-11 09:18 — 借鉴维护者经验,补齐缓存完整性与双实例编辑验收(北京时间) - -- 授权与范围:按用户“吸取网友的优秀经验,继续工作”,继续 PR #37 检查点上的本地修复。只核查公开一手资料,使用现有 Node 24.19.0、锁定 SDK 10.0.303 和已安装依赖;没有新增存储架构/依赖、调用模型、修改真实客户端、提交、推送、合并或发布。上一节的本地修改全部保留。 -- 经验落地:[npm cacache 的读取实现](https://github.com/npm/cacache/blob/main/lib/content/read.js) 将大小和内容摘要纳入校验,启发本轮把“附件还存在”改为可验证的内容完整性。[write-file-atomic 实现](https://github.com/npm/write-file-atomic/blob/main/lib/index.js) 的 activeFiles 排队只在单进程内;[Windows 多进程 #28](https://github.com/npm/write-file-atomic/issues/28) 和 [锁冲突 #227](https://github.com/npm/write-file-atomic/issues/227) 是报告/提议,不当作已合入保证或 WinCode 已复现的故障。保留现有唯一临时文件后 rename 和实例内队列,用真实 Gateway 验证跨进程交错,没有因此增加锁或重试。 -- 先复现再修复:[integrity-before.log](../test-tmp/n4-cache/integrity-before.log) 的四个负例在原实现全部失败:同大小且恢复 mtime 的损坏 overflow 被内存/磁盘读者继续当作命中;合法 JSON 正文被修改,或另一个键的整份 JSON 复制到当前文件名,在相同 fingerprint 下返回错误正文。这说明输入身份正确、文件存在和 JSON 可解析都不足以证明缓存正文正确。 -- 生产修复:[Cache.ts](../src/Core/Cache.ts) 增加绑定命名空间键、时间/TTL、fingerprint、正文和附件身份的 SHA-256 元数据,内存/磁盘命中都核验;附件通过同一文件句柄,以 64 KiB 缓冲区在既有磁盘预算内流式校验大小/摘要。缺失、损坏或旧条目没有摘要时重算。JSON 读取按已打开大小加一个探测字节限定,读取期间检测到增长/缩小即未命中;新增文件增长反例验证读取量没有随追加内容膨胀。目录格式、MCP 公开契约和清理所有权未改变。 -- 修复过程保留:[cleanup-integrity.log](../test-tmp/n4-cache/cleanup-integrity.log) 暴露了第一版补丁提前放弃元数据写入,使超出缓存预算的附件无法立即由原容量清理识别。修正为拒绝缓存复用但保留有界受管元数据,既有 TTL/容量回收恢复;没有扩大孤儿扫描或清理权限。相关反例和有界读取测试共新增 5 项,写入既有 [runtime-cache-regressions.test.ts](../tests/runtime-cache-regressions.test.ts)。 -- 新增 [verify-shared-cache.mjs](../scripts/verify-shared-cache.mjs) 和 [cache-gateway.mjs](../tests/fixtures/cache-gateway.mjs):由测试入口载入正式发布的 ToolRouter/WinCodeMcpServer/Cache 模块,使用真实 SDK stdio 公开工具调用,每个 hello 核对版本、buildId、固定工作区和独立实例。小预算生成夹具用来触发真实写入与自动清理,不是替代 Cache 实现,也不代表实际消费者或标准 CLI 启动配置已验收。关闭回执要求业务占用归零和资源已释放;清理失败、已观测残留或交付变化使验收失败。 -- 共享缓存 8 场景:同键 8 个并发请求;不同键 8 个并发请求;兄弟 20 次实际写入触发容量清理并重建已返回附件;两个热 Gateway 拒绝同大小损坏附件;同项目源码编辑后两端回读更新;A/B 共享物理目录无跨项目正文;一端退出时兄弟继续命中;两个写者退出后全新 Gateway 命中已验证的持久缓存。报告没有损坏 JSON、错误正文、遗留临时文件、关闭后占用或已观测残留;四个客户端 stderr 仅有正常启动消息,没有错误或警告。 -- 验收入口失败保留:[run-5iBtOC](../test-tmp/shared-cache/run-5iBtOC/report.json) 首次在 hello 断言使用错误字段 runtime.version,尚未进入业务场景;实际版本在 hello.version,构建在 hello.runtime.build。按公开返回结构修正断言后重跑完整 8 场景,没有削弱版本/build 核验;失败运行也取得正常停止回执。 -- [verify-design-time-concurrency.mjs](../scripts/verify-design-time-concurrency.mjs) 新增 inputs/peer-source-edit:同项目两个正式 Host 同时加载,初始引用数各为 1;实际编辑生成夹具后,两端旧定位均返回 SNAPSHOT_STALE 且无证据;同时重载后引用各为 2,旧定位继续无效,关闭一个 Host 后另一个保留新快照。默认完整矩阵由 20 增为 21,未改动生产 Roslyn 源码。 - -| 本次缓存增量后的实际验证 | 结果与本地证据 | -| --- | --- | -| 缓存/预算/运行回归专项 | [integrity-after.log](../test-tmp/n4-cache/integrity-after.log):24/24;含内容损坏和错误键反例 | -| 清理/边界相关专项 | [cleanup-integrity-corrected.log](../test-tmp/n4-cache/cleanup-integrity-corrected.log):44/44;包含原容量回收回归与有界读取反例 | -| 完整核心/类型/构建/交付 | [core report](../test-tmp/check/2026-09-11T00-59-55-956Z-core/report.json):430/430,0 跳过;typecheck、Gateway、Native 发布、stdio、delivery 全部通过,日志为 test-tmp/n4-cache/core-check.log | -| 真实 SDK/Gateway 共享缓存 | [run-xTRbJS](../test-tmp/shared-cache/run-xTRbJS/report.json):8/8,9.501 秒,8 个已观测进程身份,cleanupFailures=[]、survivors=[] | -| 完整正式 Host 生产矩阵 | [run-CJF7mW](../test-tmp/design-time-production/run-CJF7mW/report.json):21/21,229.448 秒;89 个已观测进程身份,productionChanged=false、cleanupFailures=[]、survivors=[] | -| E4 错误契约 | [run-pD7oeb](../test-tmp/error-contracts/run-pD7oeb/report.json):17/17,通过 | - -- 当前交付:version=0.15.0,buildId=`09f71cba0339b2bf9f3f9e7d28cd727df7815aa230af1565cda3a04bce6d3187`,sourceHash=`1bd9aa91805d05f3b04955ed4786cc84dffe8e2ea7a2b49f8036a64aadec84c1`,artifactHash=`0924b27f2ce659f5e30cd677513c3fc1c8fedc65f6f388b37e7afe19766d1a6b`,delivery contentId=`8ec5572d5b85ecbc25601208e6f115038d57b2572e1a0fc36ae4303d52b56f5e`。revision 仍为 aa6fc7f,本地生产修改由源码摘要区分;不把当前构建说成干净提交或已部署连接。15 工具/schema 保持,codexConnectionVerified=false。 -- 反证自审:两端校验通过仍不能保证已返回附件永远存在。实际容量清理删除了原附件,后续请求重建;这保留现有可过期引用契约。磁盘条目/字节限制是定期清理目标,不是跨进程瞬时硬配额:本次配置 4 条时一度 14 条,全新 Gateway 启动清理后回到 4 条。没有用测试通过掩盖这个边界,也没有擅自新增租约或全局锁。 -- 未验证事项:写入中断/掉电持久性、超大附件摘要读取成本、长期缓存/原生资源趋势、多文件多写者原子快照、任意动态项目图、UI 并发取证仍未覆盖。上一节 59/22/SDK 10/释放 10/owner-death 是同日上一构建证据,未在缓存增量后逐项重跑;此前 SDK drain 警告也没有因这 8 个小场景无警告就视为修复。Node 22、远端 CI、独立审核和真实消费者仍待验。当前局部修复没有新的 USER_DECISION_REQUIRED;长期附件保留、硬配额或新清理政策需要先明确需求。 -- 文档与持续验收:同步既有 README、CHANGELOG、架构说明、路线图和详细计划,加入一手经验来源并把已完成的存储/编辑验收移出待办。CI Node 22 的并发步骤新增共享缓存入口及 always 报告收集,15 分钟预算不变;当前没有推送触发。test-tmp 回执仅保留本机,历史日志原文保留。 -- 最终核对:3 个本轮验收入口/夹具的 node --check、git diff --check、75 个当前文档/本节相对链接存在性检查通过,交付再次 matched=true。HEAD 与已抓取 PR 分支仍为 aa6fc7f、提交差异 0/0;当前保留 16 个已跟踪文件修改和 2 个新增文件,含上一节的未提交增量。完整验证后未再修改生产源码。 - -## 2026-09-11 09:31 — TDD 红—绿重放、退化检验与完整回归(北京时间) - -- 目标与范围:用户明确要求“进行TDD测试验证代码”。针对本轮缓存完整性及双实例编辑进行验证,保留全部已有工作区修改;没有新增依赖、改动生产实现、操作真实消费者或推送外部变更。由于实现已经存在,本次采用隔离副本重放修复前后行为,并刻意移除关键保护检验用例能否发现退化,不声称这是从零开始的测试先行开发。 -- 新增 [runtime-cache-regressions.test.ts](../tests/runtime-cache-regressions.test.ts) 的 3 项行为回归:inline/overflow 两类旧缓存缺少 integrity/backingFile 元数据时必须重算,重算后正文正确且再次命中;JSON 在路径大小检查之后追加合法空白,超过 maxEntryBytes 时必须未命中。追加空白不改变 JSON 数据,专门验证读取预算,而非借助正文损坏间接失败。原有文件句柄检查后增长的读取量断言继续保留。 -- 可重放实验:[replay.mjs](../test-tmp/tdd-cache/replay.mjs) 复制当前 55 个 TypeScript 源文件(460581 字节)及该测试文件到 test-tmp 独立目录,仅替换 Cache.ts 为 PR aa6fc7f 的版本。其余模块保持当前代码,用相同 7 个反例验证差异;这不是完整历史 PR 或 Native 的重建。测试仍使用现有 tsx 和本机依赖。每个阶段核对实际 TAP 用例/通过/失败/跳过数量及退出码,失败类型均为 ERR_ASSERTION,没有以编译、导入或环境错误充当红阶段。 - -| 红—绿/退化验证阶段 | 实际结果 | -| --- | --- | -| PR 原版 Cache.ts | 7/7 按预期失败:内存/磁盘同大小附件损坏 2,JSON 正文修改/换键 2,旧缓存两类 2,路径检查后文件增长 1 | -| 当前 Cache.ts | 同一组 7/7 通过,0 跳过 | -| 移除正文完整性校验 | 对应 2 个反例均失败 | -| 摘要不再绑定缓存键 | 换键反例失败 | -| 移除附件摘要比对 | 内存/磁盘两个损坏附件反例均失败 | -| 把有界 JSON 读取改为 readFile | 大小检查后增长反例失败 | -| 恢复当前实现 | 同一组再次 7/7 通过,主工作区 Cache.ts 哈希始终不变 | - -- [完整红—绿报告 run-kLuIQ8](../test-tmp/tdd-cache/run-kLuIQ8/report.json):success=true,7 个阶段,6.627 秒;四种选定退化全部被检出,不作为全项目 mutation coverage。报告包含每阶段日志、实际失败名称、源码/测试 SHA-256 和 productionSourceUnchanged/testsUnchanged=true;副本最后恢复当前实现。正式 Cache.ts 文件摘要为 bbae056552bb3a3eba3a1362fa3ce3dcdb5cd4be3957cee033dbed41ea8212a6,PR 原版为 e84c10c07c856d2b3d389930f5d56adada2ce991023b92f19924f52e7eb1111c。 -- 测试自身的失败也保留:[run-XfrwD0](../test-tmp/tdd-cache/run-XfrwD0/report.json) 首轮当前实现 6/7 通过,失败是新增 overflow 用例把包含随机附件路径的 preview 文本要求完全相同。重建会产生新路径,因此改为验证新旧路径不同、实际附件正文逐字节内容相等,inline 仍比较完整正文;继续要求首次未命中和重建后命中。该失败不归为生产缺陷,未通过修改实现迎合测试。 - -| 追加的当前实现验证 | 结果与证据 | -| --- | --- | -| 完整核心、类型、Gateway/Native 构建、stdio、delivery | [2026-09-11T01-27-26-632Z-core](../test-tmp/check/2026-09-11T01-27-26-632Z-core/report.json):433/433,0 失败/取消/跳过,45.874 秒;日志 test-tmp/tdd-cache/core-check.log | -| 真实 SDK/Gateway 共享缓存 | [run-awRGKn](../test-tmp/shared-cache/run-awRGKn/report.json):8/8,7.541 秒,8 个已观测进程身份,cleanupFailures=[]、survivors=[] | -| 双正式 Roslyn Host 编辑/重载专项 | [run-xhNUyA](../test-tmp/design-time-production/run-xhNUyA/report.json):1/1,10.990 秒,4 个已观测身份;编辑前引用 1/1,旧定位均 SNAPSHOT_STALE,重载后 2/2,关闭一端后兄弟仍为 2;cleanupFailures=[]、survivors=[] | - -- 交付与边界:buildId 仍为 09f71cba0339b2bf9f3f9e7d28cd727df7815aa230af1565cda3a04bce6d3187,sourceHash、artifactHash 和 delivery contentId 均与上一节相同,matched=true。没有发现需要修改生产实现的新缺陷;源码只新增上述 3 项测试,并同步 README/路线图/计划当前计数与证据。上一节完整 Host 21 场景和 E4 17 是同一生产构建的既有结果,本次 Host 只重跑 peer-source-edit,不冒充再跑完整 21 场景。 -- 反证自审:仅断言未命中可能让“禁用所有缓存”错误实现通过;用例同时要求其他键仍可读取、旧缓存重建后再次命中,并比较实际正文,保留成功路径。隔离副本中的四种刻意退化只证明对应保护被这些测试覆盖,不外推掉电一致性、跨调用附件租约、磁盘瞬时硬配额、UI 并发、长期资源、Node 22、远端 CI 或真实消费者。 - -## 2026-09-11 09:58 — 整体架构复核与 PR #37 合并就绪判断(北京时间) - -- 任务边界:用户要求再次复核整体代码架构、确定下一步并判断是否可以合并。本次检查源码、调用链、已有回执和 GitHub 实时状态,并在 test-tmp 生成小型反例;没有修改生产源码、已有测试或远端 PR,也没有提交/推送/合并。这里只追加复核记录。 -- 架构结论:继续保留单连接固定工作区的 Gateway,由 ToolRegistry/McpServer 统一参数与准入,ToolRouter 组织恢复/释放,现有 Adapter 隔离 Roslyn 与 UIA,Native 以快照和 UUID 归属管理语义及输出。静态扫描 55 个 TypeScript 文件、139 条本地非显式 type-only 导入边,没有发现循环;该扫描不覆盖动态依赖或运行时正确性。重点复查了 RequestAdmission/OperationContext/Mutex、Workspace 固定根、Router drain/恢复、Roslyn Host 协议及退出、私有输出输入策略、缓存、UI 请求截止和交付/CI。现有方向可保留,下一步应先收口并发正确性与交付,不需要为了合并扩大架构。 -- 新发现 [P2]:[Cache.ts](../src/Core/Cache.ts) 的 get 在保存 memEntry 后 await backingFileMatches,再无条件执行 memoryCache.delete/set。等待期间,set、淘汰或 clear 已可能改变同一 Map;恢复时旧对象会覆盖新值或复活已删除条目,且没有相应恢复 memoryBytes。失败校验分支同样需核对自己删除的是否还是原条目。origin/main 也存在同样 await 后 delete/set 结构;本次是发现此前未覆盖的操作交错,不归因于上一节新增测试。 -- 当前正式构建上的确定性反例:[repro-cache-race.mjs](../test-tmp/architecture-review/repro-cache-race.mjs) 直接使用已验证的 dist/Core/Cache.js,只在生成的缓存目录执行公开 CacheManager 方法,无 mock、无生产文件回滚。[run-XoxWYs/report.json](../test-tmp/architecture-review/run-XoxWYs/report.json) 记录三个 reproduced=true:①读旧值与 set(new) 交错,set 已完成后内存仍返回 old,冷读磁盘为 new;②maxMemoryEntries=1 却保留 2 条,报告 2 字节而两个字符串按既有估算合计 4 字节;③clear 已完成后仍返回 old,内存 1 条而统计 0 字节。这是模块行为反例,没有声称已复现跨项目 MCP 错误正文或整个进程 RSS 失控。 -- 测试结论修正:上一节 433/433、7 个红—绿反例和四种退化检验仍是其实际覆盖范围内的通过结果;本次新增反例证明它们没有覆盖异步读取与内存状态修改交错。因此不能据旧测试通过直接判定当前本地代码可合并。建议在本 PR 收尾中先将上述 3 个反例纳入正式回归,异步边界后校验条目身份/状态代次,再修改缓存状态;同时检查磁盘回填的同类交错,避免仅修成功内存命中这一条分支。 -- GitHub 实时状态:通过 GitHub 连接器及现有 gh 的只读查询核对 [PR #37](https://github.com/linnnn89/WinCode/pull/37)。head=aa6fc7f457f5a18b122fd791aec2824ed121195d,base=fb3cd48df3f38b209565b906fbfe3485df48461d,state=open、draft=false、merged=false、mergeable=MERGEABLE,但 mergeStateStatus=BLOCKED;review/review thread 均为空。Git 无冲突不能代替必需检查通过,本地未提交修复也不是 PR 当前内容。 -- 必需检查:gh pr checks --required 返回 5 项,其中 Windows regression (Node 22) 为 FAILURE;Node 24、Analyze (actions/csharp/javascript-typescript) 为 SUCCESS。另一个汇总 CodeQL 也为 SUCCESS。[Node 22 作业 102911583651](https://github.com/linnnn89/WinCode/actions/runs/34489311570/job/102911583651) 的实际日志确认:真实 Host 第 39 个已完成场景后,预期 PROJECT_LOAD_FAILED、实际 QUERY_FAILED,退出 1;该错误映射的本地修复尚未推送。GitHub 状态与昨晚 PR 描述只是远端检查点事实,不作为新的用户指令。 -- 当前交付再核验:delivery matched=true,contentId 仍为 8ec5572d5b85ecbc25601208e6f115038d57b2572e1a0fc36ae4303d52b56f5e;读取现有核心 433/433、完整 Host 21/21、共享缓存 8/8 和 TDD 回执确认其成功/清理状态。本次没有重复无关全套测试,三个新的缓存时序反例才是本轮新运行的验证。 -- 合并前建议顺序:①修复缓存状态交错并以反例驱动回归,确认正常命中、替换/清空结果和容量计数;②重建/核验最终源码,按影响复跑核心、共享缓存及必要 Host 用例;③在获得提交/推送授权后,把当前增量纳入同一个 PR,更新过时的 WIP 描述与证据;④以新的实际 PR head 核对 5 个必需检查、最终差异和分支保护,再决定合并。当前既有 BLOCKED 状态也有尚未修复的相关缺陷,不建议立即合并。 -- 后续验收分层:UI 多实例只读取证、真实消费者/模型闭环、长期/大项目资源和任意动态项目图可以作为后续明确范围的验收;已公开的附件可过期、定期磁盘清理和掉电孤儿产物限制不自动转成此次合并必须完成的新功能。发布或对外承诺相关能力时仍需对应证据。本次为当前助手的架构复核与反例验证,不替代独立模型/人工评审。 - -## 2026-09-11 10:28 — 缓存状态交错修复、共享截止退化修复与 TDD 验收(北京时间) - -- 授权与范围:用户明确要求“好,你开始修复吧”。在原工作区保留全部已有增量,修复上一节缓存 P2;完整检查又暴露一个直接阻碍验收的 N3 超时分类问题,以受控反例确认后局部修复。此次生产代码只新增修改 [Cache.ts](../src/Core/Cache.ts) 和 [ToolRouter.ts](../src/Core/ToolRouter.ts),未改变公共 MCP 参数/工具数量、缓存布局/清理所有权或并发参数,未新增依赖、服务、跨进程锁或模型调用。没有提交、推送、合并、发布或切换真实消费者。 -- Cache 修复:异步附件校验后核对 Map 中是否仍为原条目,失效则未命中,成功刷新及失败删除都不能作用于替换后的条目;旧写入的附件校验失败也只删除自身。磁盘读取先排空已接受写入,使用单一状态代次阻止旧读回填到新值、清空后的内存或新的 namespace/目录。写入、内容记忆更新、prune 和工作区重置使正在进行的磁盘读失效;无每键永久 tombstone。过期/超大记录的删除进入原写队列,在队内再次核对代次,避免旧读删除新落盘值。并行有效读取继续返回数据并遵守同一 LRU 字节/条目预算;发生其他键的修改时,磁盘读可以保守未命中。 -- 正式回归位于 [runtime-cache-regressions.test.ts](../tests/runtime-cache-regressions.test.ts):先添加 12 个交错用例,在修改生产实现之前运行 [red.log](../test-tmp/cache-state/red.log),12/12 为断言失败;修复后相同 12/12 通过。再补已接受 clear/disk-only write 的排空顺序 2 项及正常并行命中 1 项,共 [15/15](../test-tmp/cache-state/green-final.log)。前三类公开方法交错直接复现,无 mock;需要固定磁盘时序的用例只在真实读完/关闭文件后暂停,再执行真实替换/清空。 - -| 缓存 TDD / 反证阶段 | 实际结果 | -| --- | --- | -| 修复前 Cache.ts,隔离重放最终 15 项 | 14 项断言失败,正常并行命中 1 项通过;没有编译、导入或环境错误充当红阶段 | -| 修复后相同 15 项 | 15/15,0 取消/跳过 | -| 移除异步内存条目身份检查 | 5 个对应交错均失败 | -| 移除状态代次更新 | 6 个磁盘回填/删除反例均失败 | -| 移除失败写入的条目身份检查 | 1 个反例失败 | -| 磁盘读绕过已接受写队列 | 2 个顺序反例失败 | -| 强制所有读取未命中 | 正常并行命中反例失败 | -| 恢复当前 Cache.ts | 15/15,主工作区 Cache.ts 和缓存测试文件哈希始终未变 | - -- [隔离重放脚本](../test-tmp/cache-state/replay.mjs) 与 [run-bH8Dhp/report.json](../test-tmp/cache-state/run-bH8Dhp/report.json):success=true,8 个阶段。旧 Cache 使用前次已保存副本并验证 SHA-256=bbae056552bb3a3eba3a1362fa3ce3dcdb5cd4be3957cee033dbed41ea8212a6;只复制当前 TypeScript 源码/目标测试并替换隔离 Cache,不回滚主工作区。五种选定退化全部被检出,不等同全项目 mutation coverage;该缓存实验在下面 Router 修复之前完成,不能当作完整最终 Gateway 的旧版本重建。 -- 完整检查失败过程保留:[第一轮](../test-tmp/check/2026-09-11T02-12-43-939Z-core/report.json) regression 在 300 秒超时,TAP 总结不完整,日志中 MCP architecture/symbol 两项分别触发原有 8 秒超时,最后仍存活的测试子进程属于 resource-cleanup,超时结束后已不存活。未把它报告为完整通过或确定为缓存死锁。随后单独运行 [resource-cleanup 8/8](../test-tmp/cache-state/resource-cleanup-diagnostic.log) 和 [MCP stdio 12/12](../test-tmp/cache-state/mcp-stdio-diagnostic.log) 均通过,首次全套超时的具体根因仍未确认。 -- [第二轮完整检查](../test-tmp/check/2026-09-11T02-19-09-748Z-core/report.json) 正常结束但 **447/448**,唯一失败为排队过期请求预期 REQUEST_TIMEOUT、实际 CANCELLED;15 个新缓存回归均通过。调查发现 RequestLease 与 runCode 对同一截止各设置一次定时器,内层先触发会经 Mutex 转成 AbortError,而准入层尚未标记超时,导致分类和计数错误。 -- 在 [request-admission.test.ts](../tests/request-admission.test.ts) 新增受控时序:准入后、适配器入队前推进观察时钟,使重复的剩余预算定时器确定先触发;仍要求旧 owner 保持、队列节点移除、REQUEST_TIMEOUT、timedOut=1、cancelled=0。[修复前](../test-tmp/cache-state/deadline-red.log) 确定得到 CANCELLED 并失败。ToolRouter 仅在截止与准入租约相同的时候复用其计时与原因,独立更短的操作仍保留定时器。修复后缓存、准入、请求并发和生命周期取消 [67/67](../test-tmp/cache-state/cache-admission-green.log);没有延长超时、放宽错误码或降低计数断言。 - -| 最终源码/构建验证 | 结果与证据 | -| --- | --- | -| 完整核心、类型、Gateway/Native 构建、stdio、delivery | [2026-09-11T02-23-40-026Z-core](../test-tmp/check/2026-09-11T02-23-40-026Z-core/report.json):**449/449**,0 失败/取消/跳过,43.543 秒;原命令 node scripts/check.mjs | -| 真实 SDK/Gateway 共享缓存 | [run-08Twxo](../test-tmp/shared-cache/run-08Twxo/report.json):**8/8**,7.589 秒,8 个已观测进程身份,cleanupFailures=[]、survivors=[] | -| 三个正式 Gateway/Roslyn 同时 A/B/A 冷启动与突发/取消 | [run-aQPfmx](../test-tmp/multi-agent/run-aQPfmx/report.json):**10/10**,12 个已观测身份,survivors=[];--roslyn-only 仅排除托盘容量,未串行同根启动 | -| E4 公开错误契约 | [run-0H1tDE](../test-tmp/error-contracts/run-0H1tDE/report.json):**17/17**;生成输入及现有 SDK,不使用真实 UI/外部适配器 | - -- 最终交付:Node 24.19.0、项目锁定 SDK 10.0.303;buildId=9e644bce6df5c01716938b5f3c923cc2ba80f71f24a2323abda93de4d149bb88,sourceHash=c9dda2872d859b0061a9464ab5e43091e36be589e9d6796c015b12cbfc907589,artifactHash=35b9990e8accd58e64b14e7b6b46c9576432dc887c0fb1e8bd24db8df1a8b587。delivery contentId=1f86b5a058d22be12e60b1cf2a2b0c823069d7bf239bdf29692d000aea7fd1de,matched=true;15 tools 与 schemaHash=4f8a6424c23978ebffe77111f4687c87336de64320f7d24cde8bdeede8ab804f 保持。revision 仍是 aa6fc7f 加本地未提交增量,不是一个已推送新提交;codexConnectionVerified=false。 -- 保留的限制与反证:首次完整运行超时不因随后通过就被解释为已修复;SDK 128 请求突发仍有 11 个 drain 监听器警告,阶段结束为 0,未调整阈值。全套 449 与上述受控场景不证明 Node 22、真实消费者、UI 并发、长期 RSS/原生资源或断电一致性。前次 Host 21/21 仍作为本次 Cache/Router 修复前的结果保留;本次 Native 源码未改,但未再执行完整 21 场景。身份/代次失效允许保守未命中,不增加附件租约或跨进程硬配额。 -- 远端与下一步:本轮 10:17 通过现有 gh 只读核对 PR #37 仍为 OPEN、非 draft、head=aa6fc7f、mergedAt=null、mergeStateStatus=BLOCKED;五个必需检查中 Node 22 FAILURE,Node 24 与三项 Analyze SUCCESS。此次修复已在本地完成,不能声称 PR 当前源码已包含修复或可立即合并。下一步在明确提交/推送授权下整理同一 PR 的新提交与描述,再按新 head 核对检查和最终差异;本轮无新增架构/依赖决策。README、CHANGELOG、架构说明、计划和路线图已同步当前结果及历史证据边界。 -- 最后复核(10:31):远端 head、BLOCKED 与五项必需检查结果保持上述状态。git diff --check、当前交付 matched=true、本节 18 个本地链接存在性通过;保留 18 个已跟踪文件修改及 2 个新增文件,含此前所有未提交工作。最终全套通过后未再修改生产源码或测试,只同步说明与验收记录。 - -## 2026-09-11 11:02 — 截止结果修复、失败收尾验证与合并前本地收口(北京时间) - -- 目标与范围:按用户要求继续自审、修复,达到合理的 PR 合并标准,操作不超出工程项目文件夹。保留上一节全部修改;使用现有 Node 24.19.0、项目 SDK 10.0.303 和已安装依赖,测试进程的 TEMP/TMP 指向工程内 test-tmp/project-temp。没有修改全局配置、下载新运行时、调用模型、操作真实消费者或提交/推送/合并。以已确认的固定工作区、准入、缓存及 Roslyn 输出隔离为本 PR 范围,不要求新增共享服务、通用租约、UI 全覆盖或无限负载证明。 -- 本次自审:复查 RequestAdmission/OperationContext/McpServer/ToolRouter 的预算和恢复调用链,Workspace 固定根,Cache 身份与代次,Native DesignTimeBuild/OwnedBuildOutputs/WorkspaceSession/WorkspaceInputs,以及 Gateway Host 生命周期、ResourceManager、Repomix/FlaUi 取消传播、CI 和交付边界。保留原架构和公开 15 工具契约;上一节缓存修复没有再改动。 -- 新增三个截止反例,均先在正式 [request-admission.test.ts](../tests/request-admission.test.ts) 中失败,再修生产代码:①准入后推进观察时钟,截止检查先于定时器抛出时仍需计入 timedOut;②状态工具完成时已超出截止,不能返回成功;③更短的适配器排队预算不能经 Mutex 被误报为 CANCELLED,且不得执行已过期回调。原 [deadline-red.log](../test-tmp/merge-review/deadline-red.log) 为 3/3 断言失败,0 跳过,错误和计数断言均未放宽。 -- 生产修复:[RequestAdmission.ts](../src/Core/RequestAdmission.ts) 的 release 接收实际失败原因,补齐同步截止和较短预算的超时计数;[McpServer.ts](../src/Gateway/McpServer.ts) 执行返回后再检查截止,并将失败传入租约收尾;[ToolRouter.ts](../src/Core/ToolRouter.ts) 在保留显式恢复错误之后检查实际 operation,保留适配器超时分类。相同截止继续复用租约定时器,独立较短截止仍有自己的定时器。取消/超时不提前归还尚在清理的容量,不增加自动重放。相关准入、取消、并发和恢复 [57/57](../test-tmp/merge-review/deadline-green.log) 通过。 -- 资源测试失败收尾:[resource-cleanup.test.ts](../tests/resource-cleanup.test.ts) 四个创建 Router/Server 的用例改为创建后立即注册 t.after,在初始化或断言失败时也释放 watcher。隔离故障注入 [verify-cleanup-failure.mjs](../test-tmp/merge-review/verify-cleanup-failure.mjs) 保持正式生产源码,只把目标断言替换为明确的 intentional failure:旧测试 [cleanup-vR6KmS](../test-tmp/merge-review/cleanup-vR6KmS/report.json) 到 4024 ms 仍不能退出/给出完整 TAP;修正后 [cleanup-PUaE5c](../test-tmp/merge-review/cleanup-PUaE5c/report.json) 在 1091 ms 正常以 exit 1 结束,并完整报告 1 个预期失败。正常专项 [8/8](../test-tmp/merge-review/resource-cleanup-green.log)。这证明失败收尾缺陷已修复,不声称首次 8 秒业务超时的全部性能根因已定位。 -- 工程内 TEMP 的一次真实失败:[完整检查 02-43-17](../test-tmp/check/2026-09-11T02-43-17-174Z-core/report.json) 为 451/452,唯一失败是非 Git 夹具在工程内创建后,Git 正确发现了父仓库。[process-failures.test.ts](../tests/process-failures.test.ts) 在该夹具作用域内设置 GIT_CEILING_DIRECTORIES、finally 恢复原值,使测试明确模拟非 Git 工作区;未改变生产 Git 行为或全局环境。相关 [6/6](../test-tmp/merge-review/project-temp-green.log),随后按原命令重跑全套,没有降低并发或延长超时。 - -| 最终源码上的本地验证 | 结果与证据 | -| --- | --- | -| 核心、类型、Gateway/Native 构建、stdio、交付 | [02-46-37 core report](../test-tmp/check/2026-09-11T02-46-37-493Z-core/report.json):452/452,0 失败/取消/跳过;44.088 秒;[完整日志](../test-tmp/merge-review/core-check-final.log) | -| 完整非桌面 CI 命令序列 | [acceptance-5hKQAM](../test-tmp/merge-review/acceptance-5hKQAM/report.json):11 个步骤退出 0,490.255 秒;每项成功回执和前后交付一致,manifest 文件未变化 | -| E4 错误契约 | [run-TW1FjA](../test-tmp/error-contracts/run-TW1FjA/report.json):17/17 | -| Native Host / Roslyn Gateway | [fixture-o4h1VG](../test-tmp/roslyn-host/fixture-o4h1VG/report.json):59/59;[run-HR1YxZ](../test-tmp/roslyn-gateway/run-HR1YxZ/report.json):22/22 | -| 两类 owner-death | [Roslyn 加载](../test-tmp/owner-death/run-dUQFCo/report.json)、[Repomix 工作](../test-tmp/owner-death/run-aRHJzX/report.json):各 1/1 | -| 手动释放 | [run-RsbRP1](../test-tmp/manual-release/run-RsbRP1/report.json):10 个 cycles、2 类 scenarios;每轮及最终已观测 survivors=[] | -| 同时 A/B/A SDK/Roslyn | [run-WfBmSB](../test-tmp/multi-agent/run-WfBmSB/report.json):10/10;survivors=[];128 突发受理 32、拒绝 96,业务 active/executing/waiting 最终均 0 | -| 完整正式 Host 生产矩阵 | [run-Q5tOWZ](../test-tmp/design-time-production/run-Q5tOWZ/report.json):21/21,201.575 秒;89 个已观测身份,productionChanged=false、cleanupFailures=[]、survivors=[] | -| 真实 SDK/Gateway 共享缓存 | [run-wJWGon](../test-tmp/shared-cache/run-wJWGon/report.json):8/8,8.381 秒;8 个已观测身份,cleanupFailures=[]、survivors=[] | - -- 报告口径:原聚合脚本的 scenarios 字段只处理 scenarios/observations,因 Host 使用 cases 而显示 0;实际执行及成功判断由原 Host 回执的 21 个 passed cases 证明。手动释放按 10 cycles 和 2 scenarios 分别报告,不混用计数。原始回执保留,不静默改写历史。 -- 最终交付:buildId=`ff308c972a7296bce88891287958c73c1c06fd4766c9c6ecab075f302272dad1`,sourceHash=`0a125d874b28457e70e8d231ac65a72b874236798ae5b659eefe88d077adf273`,artifactHash=`308066c6e065f303f85d539c7eaf84ce6a021a4201e5aabab465f83f03c0b519`,delivery contentId=`9ccaeb481c2f716deeb624b2a7de6363663ca3f0f86fed122680a35926af9096`。version=0.15.0 未发布,revision 仍为 aa6fc7f 加本地未提交增量;不能把它当作已推送新提交或已更新消费者。 -- 反证与合理边界:SDK 客户端 @modelcontextprotocol/client 的 StdioClientTransport.send 在 128 请求突发时仍出现 11 个 drain 监听器警告,阶段结束为 0;没有抬高阈值或改依赖来掩盖。已返回附件可被未来容量清理删除,磁盘预算是周期清理目标,现有超时不提供 OS I/O 强制终止。短时受控验收不证明长期原生资源、UI 并发、掉电持久性或任意动态项目图;这些不自动成为本 PR 的新增实现条件。 -- 合并判断:当前自审发现的代码缺陷已修复,本地 Node 24 核心及非桌面验收完成,未发现需要进一步扩展架构的阻塞缺陷。PR 当前仍不能据此认定可直接合并:工程目录没有现成 Node 22,未下载新运行时;最新本地修复尚未进入远端。最后远端证据仍是前序 10:31 的旧 head aa6fc7f、BLOCKED/Node 22 FAILURE,本段未重新查询远端。后续只需在新 head 上完成仓库必需检查与最终差异复核,不额外设定完美目标。 -- USER_DECISION_REQUIRED:依据用户提供的 AGENTS.md 第四节,需要明确提交/推送授权,才能把本地增量送入同一个 PR #37、更新说明并使用现有 Node 22/24 和 CodeQL CI。推荐直接用仓库现有 CI 获取兼容性证据,避免为了这一步新增本地运行时。合并、发布和真实客户端更新不包含在该建议授权中。README、CHANGELOG、架构、计划及路线图已同步本次结果;所有原始 test-tmp 证据仅保留本地。 -- 可审阅交付:[PR 说明草案](../test-tmp/merge-review/pr-body.md) 已按最终范围准备,尚未发送;建议标题为 feat: isolate workspaces and Roslyn builds with bounded request admission。[最终汇总](../test-tmp/merge-review/final-receipt.json) 从原始回执独立读取并断言 452 项、各集成场景数和同一交付身份,保留 pending 与 mergeReady=false;没有修改原始报告。汇总脚本为 [summarize.mjs](../test-tmp/merge-review/summarize.mjs)。 -- 收尾核对:git diff --check、delivery matched=true、当前文档及本节 59 个本地链接存在性均通过。HEAD 仍为 aa6fc7f;保留 22 个已跟踪修改和 2 个新增文件,包含前序已完成的本地增量。最终核心/非桌面验证之后仅同步说明和本地证据汇总,没有再改生产源码或正式测试。已提出上述提交/推送授权申请,尚未执行外部变更。 - -## 2026-09-11 11:39 — PR #37 提交与 CI 清理修正(北京时间) - -- 用户已授权提交当前版本 PR,随后只读评估近几个版本的工程复杂度。提交 2d4ee56 已推送同一 PR #37,标题和说明已更新;没有合并。 -- 该 head 的 Node 24 与三项 CodeQL 通过;Node 22.23.2 为 450 通过、1 失败、1 可选 TavernDesk 跳过。失败位于 owner-process-guard.test.ts 的 finally:Helper 正常退出后仍启动 PowerShell 查询清理,命令超过 5000 ms,被 SIGTERM 终止;JUnit 明确记录 killed=true、code=null、空 stdout/stderr。原始 [CI report](../test-tmp/pr37-ci-2d4ee56/node22/check/2026-09-11T03-29-15-957Z-core/report.json) 与 regression.xml 保留在本机。未把该失败归为生产 OwnerProcessGuard 故障。 -- 对直接阻塞交付的问题只修测试收尾:先用 signal 0 检查 PID,只有 ESRCH 才跳过;存活或未知 PID 仍进入原有持句柄/创建时间核验,保持清理预算。正常/repeat 两个既有用例增加 Helper 已退出断言,没有新增测试。依据 [Node process 文档](https://nodejs.org/api/process.html#processkillpid-signal) 的无副作用存在性检查及 [child_process 文档](https://nodejs.org/api/child_process.html) 的 timeout/killSignal 行为。 -- 修正后 owner-guard 原 13 项全部通过(13.757 秒),类型检查和 git diff --check 通过;没有重复无关全套或修改生产源码。新的远端 head 仍需取得自己的必需检查结果。复杂度评估不新增设计文档或直接重构,结果在本次回复交付。 - -## 2026-09-11 — PR #37 共享缓存验收契约修正 - -- head 3a3d176 的 Node 22 在 13 分 55 秒后因共享缓存第 3 场景断言失败;未触发总超时。核心、真实 Roslyn/清理、多实例和正式 Host 21 项矩阵均已通过。原始 [CI 回执](../test-tmp/pr37-ci-3a3d176/node22/shared-cache/run-ho390i/report.json) 报 No overflow record for A_CURRENT_0,调用内容断言已通过,清理失败及残留均为空。 -- 旧验收要求每次有效内存命中后仍能查到磁盘 JSON 索引,超出现有可淘汰缓存契约。Cache.get 在验证内存条目及附件后直接返回;同类 [npm/cacache get 实现](https://github.com/npm/cacache/blob/main/lib/get.js) 也先返回 memoized 数据再查询索引。该参考仅用于核对层次关系,不替代本地验证或引入依赖。 -- 在原场景内确定性删除自有索引、保留有效热条目,旧断言复现同一失败([red](../test-tmp/shared-cache/run-sO4Dlm/report.json))。修正为验证实际旧附件淘汰、至少一次重建、索引删除后的正确热命中;其余损坏/修改/跨根/退出场景保留,没有新增自动化测试。 -- 原共享缓存验收修正后 8/8 通过([green](../test-tmp/shared-cache/run-F11W1L/report.json),8.303 秒;observedRebuilds=1,warmReadAfterIndexEviction=true,cleanupFailures=[]、survivors=[]);node --check 和 git diff --check 通过。生产源码和 CI 超时预算未改,下一 head 的远端必需检查仍待运行。 - -## 2026-09-11 — CI 冷启动与完整验收预算核对 - -- head 211799f 首轮 Node 22 的 Tray --endpoint 子进程超过生产代码的 3000 ms 预算,被 SIGTERM 终止,stdout/stderr 为空;同一用例本地 375.322 ms 通过。该轮 restore-host/publish-host 分别 33.773/38.214 秒,前一轮为 6.009/8.338 秒,因此只重跑一次相同 head 的失败任务验证环境/冷启动波动线索,没有修改 Tray 或重复重跑已成功的检查。重跑核心、真实 Roslyn/清理及多实例通过;这不证明 Tray 首次超时的根因已经解决。 -- 重跑的 [逐场景回执](../test-tmp/pr37-ci-211799f-retry/node22/design-time-production/run-1wXhX2/report.json) 显示 WPF 等待就绪触发了验收脚本独有的 20000 ms 限制;生产 RoslynAdapter 默认加载预算为 120000 ms。外层 job 同时被 GitHub 的 15 分钟限制取消,检查注释明确为 The job has exceeded the maximum execution time of 15m0s,完整矩阵未完成。不能只把取消解释为业务用例全部正常,也不能用旧 head 的通过替代此次失败。 -- 根据上述新证据,将设计时语义验收加载预算对齐既有生产默认 120 秒;Windows CI 整项预算设为 20 分钟容纳冷启动和完整矩阵。共享缓存移至构建后优先运行,SDK 并发和设计时输出各占独立步骤。依据 [GitHub workflow timeout 定义](https://github.com/github/docs/blob/main/content/actions/reference/workflows-and-actions/workflow-syntax.md#jobsjob_idtimeout-minutes) 区分 job 总预算与业务截止;生产请求/查询/清理的超时和取消行为均未修改,没有新增测试或依赖。 -- 验证:YAML 解析通过,并比较确认所有验收命令及 Node 条件、job 名称、报告保留设置与修改前一致;脚本语法与 git diff --check 通过。正式发布 Host 的 WPF 定向场景 [run-nKcqEc](../test-tmp/design-time-production/run-nKcqEc/report.json) 1/1 通过,productionChanged=false。新 head 仍需远端完成五项必需检查;当前未合并。 - -## 2026-09-11 — 合并后共享缓存淘汰验收归属修正 - -- PR #37 的 e2bce5a 五项必需检查通过并合并为 9fe9ace;随后 main 的 [CI 34563810434](https://github.com/linnnn89/WinCode/actions/runs/34563810434/job/103151717074) 在 Node 22 共享缓存第 3 场景失败。基础构建和回归已通过,失败断言为 evicted backing content must cause a rebuild。此前交付收尾只确认 PR 检查和合并状态,没有等待该次 main 检查。 -- 原场景从磁盘索引取得同键并发写入的最终附件,却只观察客户端 A 的重建次数;两个客户端可以各自持有不同但有效的附件。固定为 A 写入、删除自有索引、B 再写入的顺序后,旧断言确定性失败:[red](../test-tmp/shared-cache/run-v1macn/report.json) 记录 A 附件存在、B 附件已淘汰、A 重建次数为 0。该行为符合 Cache.get 对内存条目及其实际附件的校验;[npm/cacache](https://github.com/npm/cacache/blob/main/lib/get.js) 的内存命中先于索引查询也支持两者不能等同的判断,不引入依赖。 -- 修正原有场景:由 A 单独创建此前未使用的 Item31 条目,断言磁盘附件就是 A 返回的附件,再执行同样 20 次对端写入和并发读取。仍严格要求实际附件淘汰、至少一次重建、删除索引后的有效热命中,其余损坏、源修改、跨根和退出验收保持原样。仅修改验收脚本及本工作日志,未改生产缓存、超时、依赖或增加测试数量。 -- 本地 Node 24:原完整共享缓存验收 [8/8 通过](../test-tmp/shared-cache/run-WDVkcF/report.json);固定上述双附件顺序后,修正脚本 [8/8 通过](../test-tmp/shared-cache/run-VjL1Cd/report.json)。两次 cleanupFailures 和 survivors 均为空,脚本语法及 git diff --check 通过。Node 22 和本次 PR 的五项必需检查由新 head 的 CI 验证,合并后还需确认 main 新运行;本记录不将待运行检查计为通过。 - -## 2026-09-11 — 清理旧计划并更新 0.15.0 文档 - -- 核对本地代码、测试脚本和 main `d51f3e1` 的 [CI](https://github.com/linnnn89/WinCode/actions/runs/34571066627) / [CodeQL](https://github.com/linnnn89/WinCode/actions/runs/34571066444):PR #37/#38 已合并,Node 22/24 和三项 CodeQL 检查通过,GitHub Release 列表为空。固定工作区、请求限制、独立设计时输出、共享缓存和合并验证已从待办移除。 -- 删除重复的迭代路线图,将原详细计划缩减为后续测试清单。保留实际客户端 Roslyn、双连接 UI、未定位的 UI/托盘失败和 SDK 突发请求警告;大项目、长期运行、特殊 MSBuild 项目和异常退出按实际需求测试。旧计划及研究记录可从 Git 历史查看。 -- 更新中英文 README,修正架构说明、CHANGELOG 和安全说明中的版本及合并状态,保留原安全维护承诺。第一版写完后按用户要求润色:删去“绝对免疫”“杜绝大模型产生误判”等过度承诺,将“业务闭环”“取证路由”等改为具体操作;同时按源码纠正影响分析字段,未知风险为 riskLevel=UNKNOWN,置信度为 confidence=UNCERTAIN。历史工作日志仅将三处旧路线图或已删除章节的链接改为固定提交链接,原记录文字保留。 -- 本机 WinCode Skill 已在前一轮安装并精简入口,Codex 连接已确认运行 0.15.0 / local-text;完整 Roslyn 流程仍需测试。本次只修改文档,未运行代码或桌面测试,未修改受管 Skill、客户端配置或依赖。 -- 文档验证通过:扫描 360 处相对链接和章节链接,本次未新增失效链接;历史文档中原有的 64 处失效链接保留原记录。README 的 8 段 JSON 示例均可解析且与修改前完全一致,12 个 npm 命令均存在于 package.json;源码版本 0.15.0、SDK 10.0.303 与配置一致。Markdown 围栏、UTF-8、历史日志正文保留检查和 git diff --check 均通过。变更仅涉及 8 个 Markdown 文件(含删除 1 个),未提交或推送。 - -## 2026-09-11 — PR #39 的 CI 清理检查超时 - -- 文档提交 `26d0a42` 的 [CI 34575235538](https://github.com/linnnn89/WinCode/actions/runs/34575235538) 中,Node 24 和三项 CodeQL 通过,Node 22 在 owner-death 测试结束时失败。Roslyn Host 59 项、Gateway 22 项均通过;owner-death 主场景记录 9 个已观察进程、survivors=[]、success=true。 -- 失败来自随后执行的兜底清理检查:`terminateObserved` 启动的 PowerShell 子进程触发 8000 ms 超时,报告 `spawnSync powershell.exe ETIMEDOUT`,导致整组测试按既有规则失败。现有记录不能确定超时发生在 PowerShell 启动还是命令执行阶段,也不能证明运行环境抖动就是根因。报告已下载到本地 `test-tmp/pr39-ci-26d0a42-attempt1`。 -- 与已通过的 main `d51f3e1` 比较,生产代码和相关测试脚本完全一致。核对了 Node child_process 超时说明,并检索 GitHub runner-images 的相关记录,未找到可直接确认本次根因的同类案例。先记录失败并重新验证,以检查是否为偶发超时;不更改生产代码、测试断言、清理范围或超时时间,不将重跑通过称为根因已修复。合并仍要求当前 PR 提交的全部必需检查通过。 - -## 2026-09-11 — Agent 导航、上下文恢复与 UI 精简迭代 - -- 按用户要求把实际 New-tavern 审查中的操作摩擦分成四阶段实施:上下文恢复与摘要、限定范围的代码导航、独立连接引导、UI 精简与展开。改动位于 WinCode 工作区;没有增加依赖或修改应用数据库。保留已有文档工作,本轮未提交或推送。 -- 上下文 EOF 错误保留原始缺口,附实际行数及有效交集的续读请求;起点已超过 EOF 或文件不存在时不盲目重试。最终序列化后生成范围明确的 summary,普通文件开头片段也可续读。原有 512-token 字符估算预算、半行覆盖和未知任务覆盖语义保留。 -- 新增 wincode_search_text / wincode_file_outline:字面量搜索、排他文件/目录范围、重叠文件去重、同次读取的行数/字节数和声明概览、可执行的源码续读参数。复用有界 LocalTextScanner;越界范围在读取前拒绝,实际链接再核对真实路径,词法/编码/文件大小等问题按文件报告并保留省略数。导航不启动语义 Host。 -- WORKSPACE_MISMATCH 返回 connectionGuide;CLI --print-connection --workspace 输出同一默认 local-text 配置。真实 STDIO 验证从 A 的错误响应直接启动 B,并分别读到 A/B 标记;打印配置不创建目标缓存,不注册或重启客户端。 -- UI 增加显式 compact 格式,默认 full 保持兼容。控件 ID、层级、状态及图片保留,节点几何和类名可用 expansionRequests 恢复,重复 C# 候选用 candidateIds 共享。同一六控件夹具中 full=13165、compact=8750 字符,约减少 33.5%;图片字节和节点 ID 不变。这不是普遍 token/延迟收益或长期性能结论。 -- 各阶段先执行失败场景再实现:EOF 缺少实际行数/续读、导航工具缺失/词法失败不具名、生产错误缺少可用连接配置、UI 精简仍返回几何及重复候选。相关集合分别通过 36、59、8、37 项检查,集合有重叠,不相加。新增两个 it 场景,其余扩展既有测试及生产 STDIO 驱动。 -- 首次完整 core 检查:452/454,通过的实现之外有两处旧测试仍期望 15 个工具;更新为 17。记录:test-tmp/check/2026-09-11T07-58-37-514Z-core/report.json。第二次:453/454,唯一失败为旧恢复测试把整个仓库扫描计入 4 秒阈值,实际 4108 ms;记录:test-tmp/check/2026-09-11T08-02-02-436Z-core/report.json。将该测试改为隔离项目、真实 MCP 调用和实际恢复状态断言,定向通过 1/1;确认排空时 inFlight=0、监听器恢复、后续健康确认不再次排空。没有放宽时间阈值,也没有第三次全量重跑;原始完整检查报告仍保留失败状态。 -- 首次 desktop 集合 34/35:新增断言误认为按钮只有一个节点,UIA 实际还返回内部文本。改为对照独立原生计数,定向重跑真实 WPF 流程通过 1/1:compact 候选续读、full 展开恢复真实几何、定位判定方法、隔离副本修改/重新编译后同按钮由禁用变为可用;原仓库夹具哈希保持。记录:test-tmp/check/2026-09-11T08-08-23-380Z-desktop/report.json,原始失败未改写。 -- 后续验证通过:最终类型检查;新生产 STDIO 的 17 工具/Schema、搜索到源码、文件概览与 EOF 修正;17 个错误契约场景(test-tmp/error-contracts/run-10DlYB/report.json);桌面 owner-death(test-tmp/owner-death/run-0toZoN/report.json);WinForms/Named Pipe 托盘(test-tmp/tray/run-WQWlW7/report.json);实际 Roslyn 托盘工作流(test-tmp/tray-workflow/run-TuKjVN/report.json)。未重新跑已通过的完整集合,也未把分项通过写成单次全量全绿。 -- Gateway/原生 Release 构建完成,交付清单生成与核验 matched=true,contentId=6bc3f0e6aae0d0726a801897581801b8a27e019e3ad71234f49463f2346af106。生产新实例 buildId=866c93db7b87c8a5a5f5975f8c81ad601b01c02477a562b837414fbd9f344ba3,Schema=96d30bd506af37f08afcc02025ed3254d9ba2b9bd6f71732a31ad1a8f749f876。构建基于 8e4b70c 加当前未提交源码,版本仍为 0.15.0 未发布。 -- 四份受管 Skill 已备份后同步,安装入口保留精简写法,skill check matched=true;备份在 C:/Users/6/.codex/skills/.wincode-backup-948552f4-4d0b-4c9d-b2ce-3ba47349bba0。当前 Codex 连接仍为实例 4a6e5836-c683-4602-8ddf-696ebcd7d94e、旧 buildId=ff308c972a7296bce88891287958c73c1c06fd4766c9c6ecab075f302272dad1、15 工具且无 UI responseFormat。需要正常刷新连接才能加载新 Schema;未修改 MCP 配置或自动重启。Node 22/远端 CI 和当前 Codex 新接口使用尚未验证。 - -## 2026-09-11 — 导航与 UI 功能提交前复核 - -- 按用户确认开始收尾。将原目录 31 个修改文件和 3 个新文件完整保存为本地快照 da2d394,备份分支 codex/backup-navigation-before-delivery-20260911;从 main de14850 建立独立 worktree 和 codex/navigation-delivery-20260911,迁移后 Git tree 与快照一致。原目录保留,依赖和精确 SDK 复用本机现有安装。 -- 复核搜索范围、真实路径、扫描与序列化预算、取消传播、EOF 续读、UI 精简/展开和默认 full 兼容。未发现需要增加产品代码修改的缺陷;两份 Skill 手册的 inputPolicy 仍写 1,与 WorkspaceSession 和 RoslynAdapter 的实际版本 2 不符,已改正。未增加依赖、测试或架构层次。 -- 本地 Node 24.19.0:Gateway 构建和类型检查通过;上下文、导航、扫描、工具契约与 UI 候选集合 86/86;生产固定工作区集合 8/8;修订后的 MCP 恢复场景 1/1;真实 WPF 源码修改与重编译流程 1/1。正式 STDIO 的 17 工具、搜索/概览/EOF 续读、错误契约 17 场景通过。原生三组件经锁定 restore 和正式 Release 发布,交付清单校验通过。原始失败记录保留;本轮未重复完整本地 core/desktop 集合,完整兼容性检查交给最终 PR 提交的 CI。分阶段回执:test-tmp/navigation-delivery/report.json。 -- 实际 Codex 连接已在本次核对前刷新:实例 44103150-ef65-41e1-9f61-db8162ef752f,17 个工具、buildId=866c93db7b87c8a5a5f5975f8c81ad601b01c02477a562b837414fbd9f344ba3,与独立 worktree 构建相同。通过该连接完成限定目录搜索、文件概览、按返回请求读取 25/25 行;针对本轮启动的隔离 WPF 窗口完成 compact 读取及 full 展开,原生查询唯一,控件状态和几何恢复正确。回执:test-tmp/navigation-delivery/live-client.json。未改 MCP 配置;源码和 Schema 无后续变化,不要求再次重启当前连接。 -- 安装的 Skill 已通过既有脚本备份并同步,仅更新两份手册的策略版本,入口仍为 22 行;备份 .wincode-backup-946fa519-9d5c-4008-ba14-e3ac6fdf27dd,校验 matched=true。最终 PR-head 的 Node 22/24、三项 CodeQL 和合并后 main 检查仍待运行;不沿用 PR #39 的成功结果。 - -## 2026-09-11 — 合并后 Node 22 失败与 worktree 浏览修正 - -- PR #40 的最终提交 7ceeeff 在 Node 22/24 和三项 CodeQL 首次检查全部通过后合并为 0944051;随后 main CI 34584238856 的 Node 22 回归失败。唯一失败项为 Tray 端点解析,耗时 3049.8471 ms,原生进程因生产 resolver 的 3000 ms 超时被终止;尚无证据区分 .NET 冷启动、系统负载或其他启动延迟。 -- 将已有的真实端点断言从并行回归移到现有生产 STDIO 阶段,调用编译后的生产 resolver,并将实际耗时写入 check 报告。生产超时、版本与管道断言不变;失败仍阻断 check,无跳过或自动重试。其余九项 Tray 协议测试保留。Node 官方文档说明测试文件默认通过子进程并行运行:https://nodejs.org/download/release/v22.23.2/docs/api/test.html;本调整减少测试启动竞争,不声称已经确定所有端点超时的根因。 -- 本地第一次完整 check 失败于原有目录树断言:worktree 的 .git 是文件,现有目录过滤没有排除它。Git 官方说明此布局:https://git-scm.com/docs/git-worktree。新增一个临时文件回归先复现失败,再仅补充 .git 文件的默认排除;includeIgnored=true 仍能列出该文件。相关浏览测试 17/17 通过。 -- 最终一次本地完整 check 通过:Node 24.19.0,454/454、无跳过;生产 STDIO 的 Tray 端点 251.4701 ms、17 工具与导航/EOF 检查通过;三组件正式构建和交付清单验证通过。报告 test-tmp/check/2026-09-11T09-42-37-700Z-core/report.json。先前失败报告 test-tmp/check/2026-09-11T09-37-13-728Z-core/report.json 保留。未再次运行无关 desktop 集合。 -- 本次修改不增加依赖或架构层次。远端最终 PR-head 和合并后 main 检查仍待运行;当前 Codex 连接已验证 PR #40 的导航与 UI 行为,但最后这条目录过滤需要下一次正常连接加载新构建。 - -## 2026-09-11 — 修正真实项目验收的启动工作区 - -- 复核 main `631f8ba`:PR #41 已合并,[Node 22/24](https://github.com/linnnn89/WinCode/actions/runs/34586761303) 和 [CodeQL](https://github.com/linnnn89/WinCode/actions/runs/34586761201) 均通过。按用户批准的第一步,仅修正两个验收脚本、添加一个回归并更新现有文档;不修改生产源码、依赖或客户端配置。 -- `verify-tavern-context.ts` 和 `verify-product-tasks.ts` 原先把临时目录作为固定工作区,再打开目标仓库,必然返回 `WORKSPACE_MISMATCH`。现在启动时绑定目标仓库,缓存和回收站继续使用独立临时目录,并核对 hello 返回的根目录。新增回归调用真实脚本入口,先复现失败,修复后通过,同时验证源码不变、项目根下未创建测试数据。 -- 真实 New-tavern 的 8 个上下文场景通过,记录 `test-tmp/r3/acceptance-1789122972763.json`。专用 TavernDesk 测试应用 PID 10760 在显示窗口前退出,回执记录 `UnauthorizedAccessException`,因此本轮未执行 6 个 UI 产品任务。失败回执保留于 `test-tmp/r3/tavern-startup-failed-20260911.json`;确认进程已退出,未修改 New-tavern 源码或使用日常数据库,未扩展修复该应用。 -- 本地 Node 24 完整 `npm run check`:类型检查、Gateway 与原生组件构建通过;回归共 455 项,451 通过、4 失败、0 跳过。四项失败均为原有 `mcp-stdio.test.ts` 的 8000 ms 请求超时,新增回归通过。报告 `test-tmp/check/2026-09-11T10-38-39-578Z-core/report.json` 保留失败状态,后续 stdio 阶段未执行。为检查并行负载是否相关,单独运行该文件,12/12 通过,记录 `test-tmp/r3/mcp-stdio-standalone-20260911.log`;这不证明根因已确定,也不替代完整集合通过。未修改超时、生产代码或重复全量测试。 -- 交付清单核验 matched=true,contentId 保持 `290a6d2ca90407260d246b749699d515897461c770bef1ad8057483937bc656c`,本次不改变客户端运行时交付内容。README 更新已验证基线;架构说明修正为 17 个工具并说明 `AUDIT_BUSY`;待办保留双连接 UI、实际客户端 Roslyn 和未定位问题,不把 SDK 突发警告当作已证实的泄漏。 -- 本次 PR 的远端检查尚待运行;当前版本不能报告为全部验收完成或可直接合并。 - -## 2026-09-11 — 固定 stdio 回归输入并简化客户端 - -- 排查上一次完整检查中的四个 8000 ms 超时。加入有界 stderr 后运行六个相关文件,55/55 通过,子进程 stderr 共 97 字节;未复现超时,不把管道阻塞或 Node 版本差异称为已确定根因。诊断记录:`test-tmp/r3/stdio-diagnostic-parallel.log`。Node 官方文档说明测试文件默认并行执行:https://nodejs.org/docs/latest-v24.x/api/test.html。 -- 确认旧协议测试直接扫描整个开发仓库,所创建的独立 cache 配置没有传给实际 CLI。将输入改为三个固定文件组成的临时项目,源码及 trash 均在该项目内;删除无效配置。复用已安装 MCP SDK 的握手、传输与关闭,保留 8000 ms 超时、原有 12 个场景及协议错误断言,错误附最多 8192 字符 stderr。没有新增测试、依赖或生产代码。 -- 定向 12/12 通过;最终完整 `npm run check` 455/455、0 失败、0 跳过,生产 stdio、原生构建及交付清单核验通过。报告:`test-tmp/check/2026-09-11T10-46-38-024Z-core/report.json`。先前失败报告保留;当前结果证明固定输入的测试通过,不宣称已解释历史机器负载。修改后的 PR-head 仍需 Node 22/24 与 CodeQL 检查。 - -## 2026-09-11 — 完成产品任务、双 Gateway UI 与实际客户端 Roslyn 验收 - -- TavernDesk 专用 profile 的目录 ACL 未见明显异常。临时诊断构建将测试回执中的异常类型改为完整调用栈,未复现启动失败;随后按原始字节恢复 App.xaml.cs,用未修改源码重建到 WinCode 的 test-tmp,启动成功。未重置权限、使用管理员提权或接触日常数据库,原始 UnauthorizedAccessException 原因仍未确定,不归因于用户玩游戏或移动窗口。 -- 新构建的专用测试窗口 PID 3132 / HWND 0x1B0C2A 完成六个产品任务,全部通过;回执 `test-tmp/product-tasks/1789123829157-28704/report.json`,启动记录 `test-tmp/r3/tavern-current-startup-passed.json`。验收后关闭该进程,New-tavern 源码无保留改动。 -- 新增两个真实 stdio Gateway 场景,覆盖同一窗口和不同窗口。复用 WPF 夹具中的 UIA provider 握手,增加可释放的测试等待,确保首个 Helper 已进入 UIA 后再验证另一个 Gateway 的 AUDIT_BUSY;释放后检查 PID/HWND、不同标题、PNG 尺寸及 Helper 退出。仅修改测试和夹具,没有修改生产 Mutex、队列或重试行为。测试窗口使用已有后台模式,不主动抢焦点。定向 2/2 通过;随后修正 SDK stderr 静态类型不支持 resume 的类型错误,改为监听 data,类型检查通过。 -- 桌面集合最终 37/37,通过 owner-death(`test-tmp/owner-death/run-Ae2zd0/report.json`)和托盘协议(`test-tmp/tray/run-HmVbOK/report.json`)。用户重启整个 Codex 时,check:desktop 最后的托盘工作流被中断,未生成完整总报告;已完成的日志和 JUnit 在 `test-tmp/check/2026-09-11T10-53-12-759Z-desktop`。仅补跑未完成的托盘工作流,通过 `test-tmp/tray-workflow/run-8FHrV2/report.json`,survivors=[];不将这些分段结果描述为一次完整 desktop 检查通过。 -- 实际 Codex 连接实例 7509c731-baca-4031-a1fe-33a8c722b400,buildId=cbe44dd4e37479ce563c91e5ab9de3439813020de2d4664e3f4e9467e7302741,明确启用 Roslyn,目标为 TavernDesk.Core 的 53 文件隔离副本。完成声明搜索、8 处引用、影响分析和重构建议;副本源码前插入一行后,旧 location 返回 SNAPSHOT_STALE,重新搜索使声明从第 10 行/position 121 移至第 11 行/158,新 snapshot 恢复 8 处引用。报告 `test-tmp/live-roslyn-20260911/report.json`;保留 queryComplete=false、排除 8 个分析器/生成器以及单项目范围,不将 UNKNOWN 风险改称低风险。 -- 原副本已恢复,原项目与副本的 53 个源文件哈希均匹配。项目级临时 `.codex/config.toml` 已移除,CLI 核对默认 WinCode 启动参数已恢复,全局配置未改。用户明确要求后台重启后,核对临时 Gateway PID 30892 与其 Roslyn Host PID 20164 的归属并结束 Gateway,两者均退出;随后实际工具调用返回 Transport closed,说明 Codex 没有自动重建该连接。未启动无客户端连接的替代进程,也未结束其他 WinCode 实例。实际客户端验收已完成;当前连接恢复仍须客户端刷新,不能写成后台重连成功。 -- 此次新增两个测试,更新现有 README、架构说明和待办,删除已完成的验收事项。生产源码、依赖和受管 Skill 均未改变。 - -## 2026-09-11 — 避免进程退出验收后的重复清理 - -- PR #42 的最终提交和合并后 main `0958691` 均通过 Node 22/24 与 CodeQL。PR #43 的最终提交同样全部通过后合并为 `cc4b20f`,但合并后的 CI 34593556243 在 Node 22 的 owner-death 验收收尾失败;Node 24 与 CodeQL 通过。报告已保存于 `test-tmp/ci-main-34593556243/owner-death/run-gXvWAB/report.json`。 -- 原报告确认 9 个受管进程全部退出,`survivors=[]`、场景成功;随后 finally 仍逐个启动 PowerShell 做兜底终止,其中一次 `spawnSync powershell.exe ETIMEDOUT`。这是测试收尾失败,不是本次报告中观察到进程泄漏。尚未确定该 PowerShell 调用为何超过 8 秒;Node 22 文档说明 spawnSync 的 timeout 限制子进程运行时间:https://nodejs.org/download/release/v22.23.2/docs/api/child_process.html#child_processspawnsynccommand-args-options。 -- 仅调整默认 owner-death 脚本:验收已确认全部退出后不再兜底终止;失败路径保留原有 PID/创建时间核验及清理。没有放宽超时、忽略清理错误或修改生产代码。 -- 真实正常路径通过,报告 `test-tmp/owner-death/run-HzZLg9/report.json`。另以临时脚本副本故意保留 Gateway,验证残留仍使验收失败、兜底清理实际执行、随后同身份进程全部消失;故障注入验证通过,报告 `test-tmp/owner-death/run-LTW9Qh/report.json`。未重复无关全量测试;新提交的远端检查尚待运行。 - -## 2026-09-11 — 保留文件移动结果与引用计数,约束引用最终输出 - -- 用户批准实现架构审查中的前两项修复,并同意增加引用输出字符预算;随后要求提交 PR 并合并。基于 main `4720bf8`,本轮只修改相关源码、测试和现有文档,没有增加依赖。 -- 修复 trash 已移动或元数据写入失败后,Gateway 的请求期限检查覆盖实际结果的问题。移动前传递取消并再次检查期限;移动后完成元数据收尾,保留 completed/partial、实际位置及中断计数。客户端取消或断线仍不能保证最终回复送达,没有增加事务日志或自动回滚。 -- 公开 Host 已有的 Roslyn limit(1–1000,默认 100,要求 symbolLocation);影响报告保留已知 totalReferences、referencesTruncated 和实际聚合条数,未知总数仍为 null,UNKNOWN 规则不变。 -- 引用工具最终格式化 JSON 增加 maxOutputChars(2048–32768,默认 8000),两种提供方均适用,计入 UTF-16 字符、缩进、转义及元数据。保留实际返回数、输出省略项、候选和文件问题计数;必要身份及覆盖元数据放不下时明确返回 OUTPUT_BUDGET_EXCEEDED。输出裁剪不修改内部查询对象,不限制 Roslyn 查找阶段的计算成本。 -- 回归先确认旧实现会丢失移动结果/取消信号/引用总数,以及最终 JSON 超预算。实现后定向验证通过:真实临时文件移动与取消、真实 Roslyn Host 的 120 处引用、长路径和转义预览、候选身份及必要元数据边界。本轮累计新增 5 个测试,分属两次授权的局部任务(3 个及 2 个);没有放宽原有超时或断言。 -- 最终完整 `npm run check` 460/460、0 失败、0 跳过,类型检查、Gateway/原生构建、生产 stdio 和交付核验通过。报告:`test-tmp/check/2026-09-11T13-13-40-686Z-core/report.json`;交付 contentId=`7d04eb319ef210868f25ece81496aaaad184e456a2ec3d302a8593e8923537f3`。错误契约 17/17:`test-tmp/error-contracts/run-bAbdGe/report.json`。 -- 自查及本地验证不等同于独立审查;最新 PR 提交仍须通过 Node 22/24 和三项 CodeQL 后再合并。未重新执行无关桌面验收,也未同步已安装 Skill 或重连当前 Codex MCP;本地构建完成不证明活动客户端已更新。 - -## 2026-09-11 — 重组并润色中英文 README - -- 用户要求突出后台 UI 检查、纯文本大语言模型支持,以及实际使用中相比基于截图的 Computer Use 更快、更准确的体验;随后要求规范中英文术语、结合 Gemini 建议润色,并提交 PR 合并。 -- README 按核心功能、使用示例、快速开始、常用工作流、可选配置、适用范围和开发文档重新组织。增加禁用按钮的 UI 与源码联合分析示例,前置推荐的固定工作区配置,将详细历史与内部机制改为现有文档链接。保留后台操作、UIA、源码候选和视觉检查的实际适用范围。 -- 调整中文语序、动宾搭配和链接间距,同步英文。依据微软文档使用“MSBuild 项目评估”,将 rollForward=disable 解释为 SDK 版本须完全匹配;字符串搜索和 Skill 同步说明保持现有接口含义。Computer Use 比较保留为日常开发体验,222 节点窗口的 62 KB/1.6 KB 数据来自既有测试记录。 -- 文档验证通过:10 个本地链接、6 个 JSON 示例、4 个工具调用示例的当前源码 Schema 校验、7 个 npm 命令和 16 个工具名称;中英文工具列表与对应配置一致,git diff --check 通过。未发现文档验证失败;没有修改生产代码、依赖或受管 Skill,也没有新增测试或重新运行运行时测试。 -- 本条按 CONTRIBUTING 的合并要求追加。最新提交的 Node 22/24 与三项 CodeQL 仍待远端检查;本地自查不等于独立审查或活动客户端验收。 - -## 2026-09-12 08:57 — 同步云端 main、本地构建及已安装 Skill(北京时间) - -- 用户要求以较新的云端版本对齐本地,并同步 Skill、评估是否需要更新设计。同步前无未提交修改;获取 origin 后确认 PR #37 已于 2026-09-11 合并。保留原检查点分支,切换 main 并快进到 `af9c1b6a7284cf8fa909badedcc1c05a34cc7ee1`,与 origin/main 一致。该提交远端 CI、Push on main 和 Scheduled 均已成功,分别见运行 34615593278、34615592254、34618221513。 -- 本地原 dist 仍为 2026-09-10 构建,不能仅以同为 0.15.0 判断已更新。使用现有依赖和 SDK 10.0.303 完成 typecheck、Gateway 及 UIA/Code Host/Tray 的标准 Release 发布(--no-restore),未安装或升级依赖。新 buildId=`6fa5431f5e5903928848bc48abb02ac97f9cab259bf8f5b8c3efc958c428f370`;生成交付清单并验证 matched=true,contentId=`081048e0f13e3ea31c24accf1f965d3dc5950f85bf2d2cf672d41258f44b7da0`。 -- 已安装 Skill `C:/Users/40218/.agents/skills/wincode` 从 0.13.1 同步至仓库 0.15.0;使用既有 sync-skill 脚本,先检查、完成备份,再覆盖四份受管文档。备份为 `C:/Users/40218/.agents/skills/.wincode-backup-ba6f56a7-1d51-448b-a837-e2d50fd5d872`;同步后二次检查及四文件 SHA-256 均一致。未发现 `.codex/skills/wincode` 安装副本,没有新建重复安装或改动 MCP 配置。 -- Skill 设计判断:云端已更新固定工作区、限定范围搜索/文件概览、context 续读、UI compact/展开、引用条数与最终字符预算、移动结果及错误恢复规则。继续采用短 SKILL.md 入口加三份按需手册,无需本轮额外改结构或加入抽象层;安装同步已经覆盖旧版指令差异。 -- 实际验证:Skill 同步测试 2/2;工具契约、运行时契约及交付契约测试 30/30;生产 stdio 新连接通过,17 个工具,schemaHash=`6db24ea89d53ad793d7e63e72df418ab0dc031ec651fd93f4dd56708a90e5efb`,覆盖新搜索/概览/精确上下文及边界响应。本地回执为 `test-tmp/sync-20260912/stdio.json`。未重复完整 460 项、桌面或真实 Roslyn 语义验收,构建通过不代替这些流程。 -- 反证检查:当前 Codex 已连接实例 `605af938-fa1c-49d3-b8fc-fbe165a5fd1d` 实际仍为旧 buildId `be08ba26...`、15 工具,并固定在历史 client-roslyn 测试根;磁盘重建和 Skill 同步不会热替换它。需客户端刷新/重启连接后核对新 buildId 与目标工作区;本轮未强杀该进程或擅自改变绑定项目。源码与远端提交已对齐,只有本条本地工作记录为额外修改,未提交或推送。 - -## 2026-09-12 16:54 — 实现 Skill 按需会话并切换本机启动配置(北京时间) - -- 用户批准开始实现首次使用前不随 Codex 启动的方案。基于 `af9c1b6` 新建 `codex/skill-on-demand-session`,保留本文件此前的同步记录;未提交或推送。新增 `src/Client/SkillSession.ts` 和 `SkillSessionCli.ts`,使用已安装的 MCP SDK 2.0.0;没有新依赖、常驻服务、监听端口、Gateway/Host 生命周期重构或公共 MCP 参数变化。 -- 入口由 Skill 首次需要时通过持久交互终端启动;对象构造、入口 ready 和本地 status 不创建 Gateway。首次工具调用连接并核验工作区、buildId 和 provider,多次调用共享连接、Host 与有效 Roslyn 快照。任务收尾显式关闭;连接失败不自动重连或重放。沿用 Gateway EOF 与原生所属进程清理,SDK 发出终止后额外等待实际传输退出。 -- 原计划使用 Node REPL 承载 SDK,实际导入被该工具禁止的 `node:process` 拒绝,因此改用现有 `exec_command(tty:true)`/`write_stdin`;没有绕过限制。普通执行管道会立即 EOF,TTY 输出又可能折行,所以完整 MCP 结果写入 `test-tmp/skill-sessions/run-*/`,图片保留原始数据和解码文件,终端仅返回路径回执。读取结果增加一次文件读取;附件不自动删除。传输失败与已收到结果后的附件交付失败分别报告,均不自动重放业务。 -- 新增恰好 3 个自动测试:未调用状态与连接/结果保持、真实 Roslyn 快照复用和修改后的旧定位拒绝、真实 MSBuild 阻塞时取消并结束入口的所属进程回收。首次进程数量断言把 Windows 创建的 conhost 当成 Gateway,多出的进程经实际命令行核对后修正断言;仍禁止未调用时创建任何 Gateway/业务子进程。三项在最终完整回归中全部通过。 -- 使用当前 Codex 真实交互终端,在隔离 C# 夹具完成搜索声明与后续引用查询。Gateway PID=12376、instanceId=`0da9d0dd-f978-4071-88bc-6c894471969e` 在两次调用间一致,snapshotId=`b32ac81643384f98ad24872d5460e4d9` 直接复用,返回 1 处已知引用。显式关闭后,按 PID/创建时间核对的 5 个所属进程均退出;报告 `test-tmp/skill-session-live/report.json`。这是实际执行工具路径验证,未冒充刷新后的 Codex 原生连接验收。 -- Node 24.19.0 完整 `npm run check` 通过:463 项中 462 通过、0 失败、1 项按原规则跳过(可选 TavernDesk 固定夹具不存在);包含类型检查、锁定还原、Gateway/三个原生组件发布、生产 stdio 和交付校验。报告 `test-tmp/check/2026-09-12T08-51-11-904Z-core/report.json`。buildId=`7491337da2426a3ccc4f2df4a11a5f28982fc86852ae5485305ffeb78bceec7e`,17 工具、Schema 不变;交付 contentId=`66567fe4521c917d27922e3181a5fd4dba99fec9adfe4c537cfafcb71a43fcd0`。未重复全量测试,未运行 Node 22 或真实桌面图片流程。 -- Skill 入口保持短路由,仅在既有 diagnostics 手册首节加入会话流程;同步 README、配置指南、架构说明、贡献指南和 changelog。`quick_validate.py` 以 Python UTF-8 模式通过;默认 Windows GBK 解码曾失败,未改校验器。四份安装文件经既有同步脚本备份/同步/哈希核验一致,备份为 `C:/Users/40218/.agents/skills/.wincode-backup-95536b4a-f736-4d58-8588-e6ea88547904`。 -- 新入口和完整回归通过后,仅将 `C:/Users/40218/.codex/config.toml` 的 `[mcp_servers.wincode] enabled` 从 true 改为 false,其他字节保留;原配置备份 `config.toml.before-wincode-on-demand-20260912-165236.bak`。`codex mcp get wincode --json` 已读回 enabled=false。修改前建立的 4 个 Gateway 仍在,未终止其他任务连接或重启当前 Codex;正常刷新后“未首次调用无新 WinCode 进程”的宿主冷启动观察保留到现有待验表中。 - -## 2026-09-12 — 使用本地 Tavern 验证 Skill 按需会话 - -- 用户要求改用本地 Tavern 项目验证。目标为 `I:/New-tarven`,HEAD=`6eee26031ffe7d01cd676edd5fafc0af1e14dd3d`;开始和结束时 Git 状态均仅有原有 `.publish-verify/` 未跟踪目录。未修改 Tavern 源码、依赖或正常启动配置,也未提交/推送本轮 WinCode 改动。 -- 按当前隔离入口做 Release `--no-restore` 构建。首次 `-Fresh` 已在新建 `work/isolated-test-20260912-165839-478bbf71ef4643dbb290a7784643ae44` 初始化 schema 24 数据库,但停在语言选择对话框,脚本按 30 秒预算终止自有进程。核对代码/回执后,仅对本轮新建且具有专用标记的目录使用既有 `--test-reuse` 参数,选择默认语言并启动窗口。数据库、配置、日志和 API 测试输出路径均由实际启动回执确认在测试根内;未调用 Provider 或使用个人资料库。 -- 通过当前 Codex 的 `exec_command(tty:true)` 和同一 `write_stdin` 会话执行 9 次显式工具请求。首次请求前仅有入口 PID=33064,没有 Gateway 子进程;随后固定到 `I:/New-tarven`,显式配置 App 项目的 Release/net10.0-windows Roslyn。Gateway PID=34856,instanceId=`d014e125-472c-47a7-9207-705c986b383c`,buildId=`7491337da2426a3ccc4f2df4a11a5f28982fc86852ae5485305ffeb78bceec7e`。 -- 文本搜索返回 `ShowCharactersAsync` 的三处匹配;Roslyn 声明位于 MainWindowViewModel.cs 第 315 行,精确引用位于 127、130 行,与实际源码一致。两次上下文响应的所有正文行均与磁盘独立读取一致。UI 取证前后,引用仍使用同一 snapshotId=`2d27e127260b413694440f9b71e7a77b`,重复引用结果相同,Gateway 实例和构建身份保持一致。 -- 在隔离窗口 PID=29192、HWND=0x120B9C 上后台查询 NavCharacters,结果 unique、searchComplete=true,返回启用的“角色”按钮及三节点子树。实际截图 2160×1350、81461 字节,已查看,内容为新资料库的空角色书架;附件与 MCP 原始 image 数据逐字节一致,SHA-256=`e094f01e3b5be5cff85462b1e7f809fcc6732f1f3efdc6e32286253fdb3ffc3f`。XAML 第 116 行与 C# 第 127/197 行候选成功返回,按 nextRequest 续读赋值行;候选文件哈希已核对,runtimeSourceVerified=false 仍保留。 -- 显式关闭 Skill 会话后,按 PID/创建时间记录的 5 个所属进程均退出,survivors=[];Tavern 目标窗口仍存活,随后单独正常关闭该测试窗口并观察到进程退出。第一次收尾身份比较因 PowerShell 将 JSON ISO 时间自动解析为 DateTime 而拒绝执行;实际时间和命令行确认一致后按 UTC ticks 比较,未放宽进程身份检查。 -- 实际项目揭示语义边界:快照排除 24 个分析器/生成器,返回两项 CS8795 及部分 Microsoft.Data/Microsoft.ML 的 CS0234,queryComplete=false、analysisCompleteness=incomplete。上述具体导航和引用查询通过,不代表全项目语义完整;没有通过安装依赖、改项目 TFM 或忽略诊断掩盖缺口。未重复完整回归或新增自动测试;本轮补齐真实按需入口的图片附件验证,但仍未重启 Codex 或运行 Node 22。 -- 完整本地报告:`test-tmp/tavern-skill-JNZ7Wu/report.json`,包含隔离启动、未调用进程状态、查询/快照、图片校验及退出证据;原始 MCP 响应与截图在 `test-tmp/skill-sessions/run-NCIlEv/`。这些源码/图片附件只保存在忽略的本地目录,现有待验表已同步本轮结果和覆盖限制。 - -## 2026-09-12 — Node 22 本地兼容性验证 - -- 用户要求 Node 22 验证,并在确认本机只有 Node 24 后批准下载便携运行时。官方 Node 22.23.2 Windows x64 ZIP 保存到 `.deps/node-v22.23.2-win-x64.zip`(35683585 字节),SHA-256=`1177b4137ba5adaa56354ae40f1080c7450e8ae09cecb47da459d1c52ac99f97`,与官方该版本 SHASUMS256.txt 一致;校验归档路径后解压到 `.deps/node-v22.23.2-win-x64/`。没有全局安装、修改系统 PATH 或切换默认 Node,运行时和归档均由既有 `.deps/` 规则排除 Git。 -- 使用便携版绝对路径先运行既有 `tests/skill-session.test.ts`,3/3 通过、0 跳过,包括未调用状态、真实 Roslyn 快照复用/失效,以及取消后所属进程清理。日志 `test-tmp/node22-validation/skill-session.log`。没有新增测试或修改生产代码。 -- 随后仅在检查子进程环境中前置便携版 PATH,并以同一 Node 执行 `scripts/check.mjs`;父进程和通过 PATH 启动的子 Node 均读回 v22.23.2。完整核心检查通过:463 项中 462 通过、0 失败、1 项跳过(原有可选 Tavern 固定路径不存在),类型检查、Gateway/原生构建、生产 stdio 和交付核验全部成功。报告 `test-tmp/check/2026-09-12T09-20-46-120Z-core/report.json`。Gateway buildId 与 Node 24 构建一致,仍为 `7491337da2426a3ccc4f2df4a11a5f28982fc86852ae5485305ffeb78bceec7e`;记录 Node 22 工具链的新交付 contentId=`7592124791ab0b901a432405cbcdb4b87f2c7312bfe093c4ebca2a553576e1a0`,matched=true。 -- 在当前 Codex 交互终端使用便携版启动真实 Skill 入口,绑定 `I:/New-tarven` 和上一轮显式授权的 Roslyn 配置。4 次显式请求覆盖 hello、声明搜索、精确引用和再次 hello;Gateway 两次自报 v22.23.2,实际入口 PID=26740、Gateway PID=9412 的命令行均指向便携版 Node 22。instanceId=`87e949f7-776c-4380-86ed-3623bcc09f42` 保持一致,snapshotId=`6c1db72b22ea42ec85a3a87959ffdda1` 从声明传到引用;第 127、130 行两处引用按实际源码 UTF-16 位置核验成功。Tavern 已知设计时语义缺口仍返回 incomplete,没有把版本兼容性通过解释为全项目语义完整。 -- 会话显式关闭并退出;记录的 5 个所属进程全部消失,survivors=[]。新执行环境读回系统默认 Node 仍为 v24.19.0。汇总报告 `test-tmp/node22-validation/report.json`,原始 MCP 响应位于 `test-tmp/skill-sessions/run-lRbqki/`。本轮未重启 Codex、未复跑桌面或 CI 的全部附加专项,未提交/推送;现有待验表移除 Node 22 未验状态,保留宿主冷启动观察。 - -## 2026-09-12 18:13 — 修复单目标引用项目的设计时依赖解析(北京时间) - -- 用户批准先修复依赖解析并用 Tavern 实测,生成器支持单独处理。基于已合并的 `c229198` 新建 `codex/roslyn-project-reference-loading`;本轮未提交或推送。改动限定在 Code Host 的 DesignTimeBuild、现有设计时验收脚本和相关文档,未修改 Tavern 源码/依赖/数据库、公共 MCP 接口或分析器策略。 -- 最小复现:Windows App 引用 net10.0 类库,类库使用本机已有且 Code Host 锁定的 System.Composition.AttributedModel 10.0.1。离线正常构建成功,旧发布 Host 却报告 CS0234。隔离诊断副本显示类库带有 WINDOWS 预处理符号且缺少该包的 MetadataReference;只移除工作区全局 TargetFramework 后,依赖和编译诊断恢复正常,输出隔离未变。证据依次为 `test-tmp/roslyn-dependency-debug/case-c7jw7W/report.json`、`case-N4wxIN/report.json`、`case-wDsTsn/report.json`。生产代码没有加入诊断开关。 -- 根因是工作区全局框架覆盖了单目标引用项目。对照安装的 Roslyn 5.9.0 对应源码确认:MSBuildWorkspace 的 properties 等同 MSBuild 全局参数,并传到每个项目。参考:https://github.com/dotnet/roslyn/blob/35d9211b841e7613c1d2f8f5af6d628ace696c4c/src/Workspaces/MSBuild/Core/MSBuild/MSBuildWorkspace.cs 。MSBuild 导入中的 TreatAsLocalProperty 作用域参考:https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-properties#local-properties-and-imports 。最初直接移除全局框架仅作为诊断,正式修复仍保留入口及多目标项目的显式选择。 -- 正式实现先按单目标引用自身声明求值,在所属 Host UUID 目录生成限定到该项目的早期 props 覆盖;保留原 CustomBeforeDirectoryBuildProps 导入、原中间目录排除规则及所有权回收。没有为多目标引用添加自动选择最近兼容框架的规则,也没有扫描 DLL 补引用或自动还原用户项目。 -- 新增恰好一个回归场景 `semantics/mixed/production`:多目标入口选择 Windows、普通类库保留 net10.0、锁定包引用可见、原早期导入保留、跨项目引用正确。旧实现失败报告 `test-tmp/design-time-production/run-PBaQ0n/report.json`;首次修复通过 `run-VdaJNe/report.json`。补充原早期导入断言后的语义阶段 6/6 通过(`run-kQFKMX`),并发/取消/崩溃阶段 5/5(`run-y3fFYK`)、外部构建/源码失效阶段 6/6(`run-V37sRQ`)、输入变化阶段 5/5(`run-VQ4JN1`),合计 22 个实际生产 Host 场景,均无所属进程残留。没有失败的生产修复循环,也没有跳过失败断言。 -- 当前 Codex 的真实 Skill 交互终端在 `I:/New-tarven` 执行 7 次工具调用。Tokenizer 参数签名、SQLite connection/transaction 参数签名均正确解析;EstimateTiktokenMessages 第 25 行、RepairCharactersAsync 第 151 行,以及 ShowCharactersAsync 第 127/130 行引用逐项与磁盘 UTF-16 位置核对一致。同一 Gateway instanceId=`c64b1f1f-3889-45aa-b208-7460c92cf4b6`、snapshotId=`c30b726a198b46ada192eb212ce72d46` 被连续复用,关闭后记录的 5 个进程均退出。实际生产 Host 完整诊断不再含缺失依赖的 CS0234,剩余 11 项均为生成正则实现缺失的 CS8795;原 MCP 简略响应仅展示前五条,不能拿以前显示的两处正则错误当作总数。汇总 `test-tmp/roslyn-dependency-debug/tavern-report.json`;原始响应 `test-tmp/skill-sessions/run-aXAiN9/`。Tavern 前后 Git 状态仅有原有 `.publish-verify/`,本轮未启动目标应用或接触个人资料库。 -- Node 22.23.2 执行一次完整核心检查,通过 463 项中的 462 项,0 失败,1 项原有可选 Tavern 固定夹具跳过;类型检查、构建、生产 stdio 和交付核验通过。报告 `test-tmp/check/2026-09-12T10-10-53-659Z-core/report.json`。Gateway buildId 仍为 `7491337da2426a3ccc4f2df4a11a5f28982fc86852ae5485305ffeb78bceec7e`(本轮仅修改原生加载实现),交付 contentId=`6902e19593c6b55b4712cb2a5e31d8dcecc6adab27a185b87a7d7aacf23a5987`。上述 22 个专项及 Tavern 真实终端使用 Node 24.19.0;未重复 Node 24 全套核心检查或运行本轮云端 CI。 -- 边界:24 个分析器/生成器引用仍排除,queryComplete=false 和 analysisCompleteness=incomplete 的保守契约未变;本次解决依赖加载缺口,不声称全项目语义完整。作者自查不等于独立审查,当前 Codex 宿主冷启动观察也未在本轮补验。诊断副本及原始附件集中保留在忽略的 `test-tmp/roslyn-dependency-debug/`,不作为交付文件。 - -## 2026-09-12 18:45 — 诊断计数与 Skill 防误判规则(北京时间) - -- 用户授权与上述依赖修复一起提交 PR、通过必需检查后合并,并同步本地 wincode Skill。Host 在每项目二十条样例裁剪前统计当前快照编译错误,单列加载诊断;直接符号/引用响应 semanticContext.diagnosticSummary 返回总数、最多各十组的错误码/项目计数及分组省略数,Gateway 给出五条展示样例与省略条数。旧 Host 未提供统计时总数为 null、countsComplete=false;不补写零。limitations 保留简短计数,使组合工具也能带出覆盖边界。 -- countsComplete 仅指已加载快照的编译错误和加载诊断统计,不覆盖编译警告、被排除分析器/生成器或未加载项目。queryComplete=false、analysisCompleteness=incomplete 保持不变。必要元数据超出引用预算仍按原契约拒绝;未增加工具、依赖、服务、自动 restore 或生成器执行。 -- 扩展既有 120 引用集成用例:固定 25 条 CS1029 加一条编译警告,验证计数先于 Host 的二十条和 Gateway 的五条上限,警告不计入错误。旧发布 Host 失败见 test-tmp/diagnostic-red.log;首次发布新 Host 后通过见 diagnostic-green.log,120 个引用仍正确且最终输出遵守预算。另加一个旧 Host 未知/不一致统计拒绝测试;连同上一轮 mixed 夹具,本任务新增两个自动化场景,未超过三项。 -- Node 22.23.2 本轮完整核心检查:464 项、463 通过、0 失败、0 取消、1 项原有可选 Tavern 固定夹具跳过;类型检查、Gateway/原生构建、生产 stdio、交付核验通过。报告 test-tmp/check/2026-09-12T10-39-52-054Z-core/report.json。buildId=2072ef38b631017d6f82fc404a3fa1a75a4619cf53422ea87f6098b3d6f58b8d,schemaHash/17 工具未变;Node 24 的聚焦真实 Host 回归通过。未重复运行无关桌面套件。 -- 当前 Codex 的 Node 24 真实 Skill 终端对 Tavern 搜索 ShowCharactersAsync 并查询引用,返回诊断总数 11、CS8795=11、App=2、Infrastructure=9、Core=0、加载诊断=0、展示=5、省略=6、排除分析器引用=24。第 127/130 行两处引用按磁盘 UTF-16 位置核验;两个响应复用 snapshotId=d394f23675ab42e382d304601f647b72。原始结果 test-tmp/skill-sessions/run-nolmHH/,入口/Gateway instanceId=0cb7610a-f126-4f76-943f-858fb598f7e2。显式关闭后五个记录所属进程全部退出;Tavern Git 仍仅有原有 .publish-verify/,没有业务源码或资料库改动。 -- Skill 保持短入口,只更新 references/code.md 与 references/diagnostics.md:先看完整统计,再区分框架/依赖加载与生成器覆盖;不凭错误码自动安装包或反复重试。skill-creator quick_validate 以 Python UTF-8 模式通过。现有 sync-skill.mjs --apply 已备份后同步 C:/Users/40218/.agents/skills/wincode,备份目录 .wincode-backup-17cc7064-c86b-4973-b869-b0f34d2f1e9d;再次只读检查四份受管文件 matched=true。合并后复核。 -- 尚待本次 PR 的精确提交 CI 和合并;作者自查不是独立审查。生成器覆盖与宿主冷启动观察仍是已有明确边界,不将本轮通过描述为已解决它们。 - -## 2026-10-07 — 0.17.0 客户端父范围与 main 合并收尾 - -- 按 hybrid 计划提交 3d3d019 的 M1–M6 执行:expand-ui 从首次观察到最终子控件读回保留 scopePath,候选 nextRequest 保留祖先范围;同一父选择器与范围根完全匹配时复用,不追加重复严格后代。保持单请求最多一次展开、已有可见候选单次读取、旧失败分类和未知结果不重放。新增 2 项测试,无新依赖、服务或系统安装。 -- 首轮核心 492 通过/0 失败/1 原有跳过;首轮桌面 52 通过/3 失败。三个失败分别为调用次数兼容、内部父路径错误分类、源码夹具漏复制 partial 文件,针对性修复后 3/3 通过。实际隔离 Tavern 另复现范围根重复路径,无动作;修复后同一真实流程通过。首次失败、参考及完整证据边界见[第十七轮记录](hybrid-experiment-findings.md#第十七轮客户端父范围与-main-合并收尾2026-10-07)。 -- 最终完整核心 18 阶段成功,493 项中 492 通过、0 失败、1 原有可选固定 Tavern 夹具跳过;最终完整桌面 7 阶段成功,55/55,含 UIA owner-death、原生托盘及双客户端真实托盘工作流。报告分别为 test-tmp/check/2026-10-07T13-03-35-479Z-core/report.json、test-tmp/check/2026-10-07T13-05-25-034Z-desktop/report.json;首次失败后仅做这一次完整最终复跑。 -- 实际 JSON CLI 同名双区域夹具与隔离 Tavern 通过;Tavern 展开 5 调用/1 动作,独立 On 读回,已展开续接 4 调用/0 动作,可见读取 1 调用/0 动作。18 次 Helper、两个 CLI/Gateway 会话和自有目标应用退出,目标仓库原有改动一致。原始窗口/身份仅在忽略目录,本轮未调用外部模型。 -- 产品与所有原生组件统一 0.17.0,Host inspectionVersion 5;delivery verified。三个既有通用 Skill 位置经备份同步,各 4 个受管文件哈希相同;没有修改 MCP 配置或 adapter。11 份 MD 的 170 个仓库链接、29 个锚点、版本和 whitespace 检查通过,历史本地附件不属于公开交付。完整增量作者自查完成;独立审查未执行。交付 [PR #52](https://github.com/linnnn89/WinCode/pull/52);CI/合并状态以该 PR 的实际回执为准。 diff --git a/docs/hybrid-experiment-findings.md b/docs/hybrid-experiment-findings.md deleted file mode 100644 index 9b949af..0000000 --- a/docs/hybrid-experiment-findings.md +++ /dev/null @@ -1,499 +0,0 @@ -# Hybrid 测试分支:问题与验证边界 - -记录日期:2026-10-06。分支:`codex/hybrid-readonly-orchestration`。 - -本文记录本分支迭代中实际观察到的问题、修复证据和未解决限制。实验方法、接口和原始测量表见 [只读编排实验说明](hybrid-readonly-orchestration.md)。前七轮实现提交包括 `ce8b517`、`a47b6b3`、`7929cb9`、`e798f99`、`5003ad6`、`a27c7b3`、`ccac194`;第八轮为 `e7350fa`,第九轮为 `ef98cb6`/`91e074b`,均在实验分支,main 未合入。 - -**当前接手请先读 [main 合并验收线](hybrid-next-iteration-plan.md#main-合并验收线)和[后续执行顺序](hybrid-next-iteration-plan.md#后续执行顺序)。** 第十六轮已完成标准工具父路径定位,客户端 expand-ui 父范围接入、对应真实应用验收及最终交付/PR 检查仍待执行。2026-10-07 本次更新仅确定计划,没有新增运行时验证。下文“仍未解决”等标题描述该轮结束时的状态,后续结果按时间追加,不抹去旧失败。最新实际方法、失败及范围见本文末节,尚未证明任意导航发现或非 fixture 多候选。 - -## 已确认并修复的缺陷 - -最新实现验收见 [第十四轮辅助属性缺口与错误反馈](#第十四轮辅助属性缺口与错误反馈2026-10-06),第十三轮验收、完成后复核及产品约束复核按历史保留;当前剩余事项见 [交接入口](hybrid-next-iteration-plan.md#当前未实现与待验证事项)。 - -### 1. 总 deadline 已过,错误仍归类为 WORKFLOW_ERROR - -- **现象:** 真实隔离窗口的超时场景中,底层 MCP 的 `Request timed out` 先完成,而工作流自己的 timer 回调尚未执行,结果错误地返回 `WORKFLOW_ERROR`。 -- **原因:** 旧实现依赖 abort 状态判断截止时间,没有同时检查实际时间是否已经达到总 deadline。 -- **修复:** `e798f99` 在 [ReadonlyUiWorkflow.ts](../src/Client/ReadonlyUiWorkflow.ts) 检查实际截止时间,统一分类为 `DEADLINE_EXCEEDED`,保留完成步骤的证据并停止后续分发,不自动重试。 -- **验证:** 在既有预算测试中加入可控时钟场景;修复后的真实短程基准 8 个正常样本与 7 类故障/状态场景通过,实际 helper 与 Gateway 退出检查通过。短程检查不能作为性能结论。 -- **范围:** 客户端只读工作流的错误分类。它不代表所有 MCP 超时都能确定操作是否完成。 - -### 2. 被拒绝的观察仍能支撑“碰巧正确”的最终答案 - -- **现象:** 模型基准宿主接受了不满足证据要求的最终答案:查询歧义、不完整查询、节点属性读取失败、错误目标或失败配方后,只要模型给出 fixture 的正确值,就可能被判成功。 -- **原因:** 旧实现过早根据底层响应登记已观察节点,未以通过完整校验的工作流报告作为登记条件。 -- **修复:** `5003ad6` 在 [hybrid-model.ts](../scripts/lib/hybrid-model.ts) 只从成功的完整报告登记实际节点 AutomationId。整体失败的配方保留步骤报告,但部分步骤不能满足该配方的成功证据要求。 -- **验证:** 测试先行的 12 个场景中,修复前 10 个被误判成功,另 2 个已有守卫;修复后全部符合拒绝要求。真实 WPF 响应经客户端注入歧义后,两条路径均返回 `FINAL_WITHOUT_REQUIRED_EVIDENCE`。 -- **范围:** 实验宿主的结果验收缺陷。不能据此声称原生 UIA 工具读取了错误值,也不能将该守卫推广为所有第三方客户端的行为。 - -### 3. 未完整结束的模型回合仍会分发工具调用 - -- **现象:** 响应已标记截断、中断,或完成原因与工具调用不一致,但工具参数看似有效,宿主仍启动读取并继续对话。 -- **原因:** 旧实现没有在工具分发前校验该模型回合的完成原因。 -- **修复:** `5003ad6` 要求带工具调用的响应以 `finish_reason:"tool_calls"` 完成;无工具调用的最终回答要求 `stop`。不满足条件时返回 `INCOMPLETE_MODEL_RESPONSE`,已收到的 usage 仍保留。规则依据 [DeepSeek 官方完成原因定义](https://api-docs.deepseek.com/api/create-chat-completion/)。 -- **验证:** 两条路径合计 10 个固定回复场景,修复前均启动读取;修复后均为 0 次 MCP 分发。两条路径的真实窗口回放也确认截断回复不启动 UI 读取。 -- **范围:** 当前模型基准宿主的协议验收。其他提供方的兼容性尚未验证。 - -## 已观察到但未作为代码缺陷修复的问题 - -### 4. 真实模型未总能满足最终 JSON 输出契约 - -第三轮真实模型正式测量共 160 个样本:混合路径 80/80 通过,原生路径 77/80 通过。原生 T3 的第 10、16、19 个样本输出自然语言而非要求的最终 JSON。失败样本及其耗时、usage 均保留,没有重跑覆盖。 - -该报告整体为 `success:false`;T3 为 `validComparison:false`,收益百分比为 null。这是模型输出契约失败,不能报告为 UIA 操作失败。第五轮修复没有再次调用真实模型,第三轮的成功率、token 和耗时仍属于旧版验收逻辑下的历史结果,尚未用最新逻辑重新测量。 - -### 5. 默认测试并发下出现一次子进程退出等待超时 - -第五轮首次完整回归为 473/474:既有 `lifecycle-cancellation` 测试在默认并发下超过 1 秒退出等待。该文件单独运行为 14/14;最终采用项目 SDK 10.0.303、测试文件并发数 4,完整回归为 474/474。 - -未修改该测试的断言或无关生命周期代码。较低并发通过与资源竞争相符,但尚未确定根因;不能宣称已修复默认并发下的稳定性,也不能仅凭这次测试失败推断产品进程泄漏。首次失败日志仍保留。 - -### 6. 验证流程曾使用错误 SDK,并与构建发生竞争 - -第四轮首次回归与构建同时运行,读取产物时曾暂缺 build-manifest;该次执行还选中了系统 SDK 10.0.302,而项目固定版本为 10.0.303。这些是实际执行流程错误,不能省略后直接报告首轮全部通过。 - -后续先完成构建,再通过项目已有 `scripts/lib/dotnet.mjs` 验证并选择已安装的 10.0.303,相关回归通过。没有修改 `global.json` 或安装 SDK。此记录不将构建竞争归为已证实的产品缺陷。 - -## 已验证的守卫与实验限制 - -- 工具文字诱导模型调用写操作或改变目标时,实验宿主在分发前拒绝。该守卫在第五轮新增测试的首次运行就通过,属于既有行为的回归证据,不是本轮新修复的缺陷。 -- 第五轮新增 3 项测试,共 24 个固定回复场景;实现修改前 2 项失败、1 项通过,修复后模型测试文件 6/6。真实 WPF 回放 6 个场景通过,4 次合法读取的 helper、Gateway 与 fixture 实际退出均已检查;没有真实模型 API 请求。 -- 真实窗口回放的歧义和诱导文字由客户端注入,模型回复由本地固定序列提供。它验证宿主面对错误回复的行为,不能估计真实模型受诱导的概率,也不代表已经穷尽所有错误模型输出。 -- 混合路径调用预先开发并安装的可信配方。收益包含较小的工具 schema、输出投影和部分任务的条件编排,没有测量模型生成程序或开发配方的成本,也没有隔离各项因素。 -- T2 的最佳原生路径已经一次读取共同父区域,两条路径均为一次工具观察轮次。不能把 T2 的 token 减少解释成减少模型往返。 -- 正式实验前已有试运行,缓存预热没有受控,两条路径的缓存命中率也不同。实际 token 可按 usage 记录,但不能直接推导普遍适用的费用或耗时优势。 -- 目前证据支持继续使用客户端配方;普通 MCP 客户端对服务器 batch 的需求尚未验证,不能据此判定服务器 batch 或通用 runtime 已具备进入条件。 - -## 隐私与可复核范围 - -本记录只使用仓库相对路径、提交标识、合成场景及汇总结果。没有复制个人 API Key、Authorization 值、提供方私人端点、账户用量截图、私人对话内容、邮箱、本机绝对路径或实际 PID/HWND。配置变量名称及协议字段是接口说明,不是凭据值。 - -真实模型实验的凭据通过进程环境读取,仅用于 HTTP Authorization;逐次 transcript 保存请求正文,不保存请求头或完整环境。固定测试中出现的测试密钥属于合成数据。此前审计在其检查范围内未发现真实凭据泄露;这不等于所有原始日志都已匿名化,或能保证任意未来模型/工具响应不包含敏感信息。 - -原始证据位于 Git 忽略的 `test-tmp/`,其中可能保留本机路径、运行标识及原始响应,不随本记录提交,不作为可直接公开的附件: - -- 第三轮真实模型报告、transcript 与进程退出检查:`hybrid-model-*`。 -- 第四轮短程故障验证与会话日志:`hybrid-ui-*`、`hybrid-recipe-checks`。 -- 第五轮红灯基线、实现/测试快照、绿灯结果、首次失败回归、最终回归及真实窗口回放:`hybrid-tdd-checks`。 - -公开仓库提供 [回归测试源码](../tests/hybrid-model.test.ts)、实现及实验入口;本地原始记录不随 Git 分发。需要分享原始记录时应另行审查和脱敏,不能把“没有发现密钥”当作“没有个人信息”。 - -## 第六轮补充发现(2026-10-06) - -### Off 分支的限制曾可被其他选择器绕过:已修复 - -新增 Off 验收时,先限制了直接按 `hybridChecks` 查询的详细读取;进一步补测发现,模型换用名称或更广的根节点查询,仍可启动额外读取。新增测试在修改前确认读取次数为 2,而约束要求只有摘要的 1 次读取。 - -现在摘要已被接受为 Off 后,T3 的原生后续请求只允许再次明确查询摘要,其他区域请求在分发前拒绝。相同测试覆盖名称与根节点选择器,真实 WPF 的名称选择器回放也通过。该修复只限模型实验宿主,没有收窄标准 MCP 的合法查询能力。 - -### 失败反馈已有针对性,最终格式恢复当时未实现(第六轮结束时) - -实验宿主的工具拒绝回执现在为已知错误提供固定提示:允许的工具/配方、固定目标、JSON 参数格式、只读响应选项、源码范围,以及 Off 后应返回的结果形式。回执仍保留 `workStarted`,模型可以在后续回合根据反馈重新提出请求;宿主不自动重放旧工具计划。 - -模型 API HTTP/协议/取消错误仍结束样本。最终回答无法解析成 JSON 时也直接结束,没有再给模型一次格式修正机会;因此不能声称所有 LLM 失败都有纠错反馈,或已经证明这些提示提高了真实模型的下一次调用成功率。 - -本轮正式 On 测量共 160 个样本、359 次模型请求:混合 80/80,原生 61/80。原生 T3 为 1/20,19 次失败均在本地核对为最终 JSON 语法问题;其比较资格为 false,收益为 null。不能把本轮比第三轮更低的 T3 通过率单独归因于架构或验收收紧,模型返回版本/缓存及实际回复路径也影响结果,因果关系未隔离。 - -原生 T3 全部 20 次尝试共 79 次模型请求、40 次 MCP 读取、313898 个实际总 token;失败成本已汇总,不仅列一个成功样本。该问题将“有预算、对两条路径一致的最终格式纠错反馈”提升为随后需要验证的具体事项。现有数据不支持推进服务器 batch。 - -## 第七轮补充发现(2026-10-06) - -### 摘要节点已读取,但状态未知仍可能获得格式纠错:已修复 - -补测在被接受的原生摘要节点中设置 unknown;旧验收只确认节点身份与顺序,最终非 JSON 回答仍可能收到纠错提示。修正格式不能补足未知状态,也不能让模型猜中固定夹具答案后被判成功。 - -现在 T1/T3 最终验收先要求实际摘要为 On 或 Off,并符合独立固定场景,再确认所需的观察顺序。未知、相反状态或缺失证据直接返回 `FINAL_WITHOUT_REQUIRED_EVIDENCE`,不发纠错请求。该场景在修改前确认红灯,修复后通过;范围仍限模型实验宿主,不改变通用 UIA 的合法状态返回。 - -### 最终格式失败现在得到一次反馈,但不能恢复所有调用错误 - -两条路径均可在完整观察后获得一次固定格式反馈。没有从 Markdown 中直接提取答案,也没有提供期望值;必须由模型返回合法 JSON 并通过原有事实验收。纠错期间请求工具立即拒绝,不执行新的读取。初始失败和纠错成本分别保留,整体成本包含两者。 - -新增测试 3 项,先红后绿,最终模型测试 12/12,完整回归 480/480。真实 WPF 的 4 个固定回复场景均恢复,On/Off 的实际读取数保持 2/1,进程退出检查通过。该回放证明宿主规则,不代表真实模型恢复率;真实模型小样本结果另列于实验说明。 - -HTTP/协议/取消错误、错误事实、未完整完成或无有效证据继续结束样本。不能将这次格式恢复描述成通用 API 重试、所有 LLM 失败自愈,或所有第三方 MCP 客户端已有相同行为。 - -T3 真实模型小样本共 40 次尝试、99 次 API 请求。纠错开启的 20 个样本中,7 个初始格式失败都在一次反馈后恢复,总新增 23877 个 token、约 4.58 秒,没有额外读取;纠错关闭的两组仍保留 7 个最终格式失败。该结果给出当前配置下的实际恢复证据,未控制缓存及组别顺序,不能估计普遍效果。四组清理与源码稳定性检查通过,真实模型组审计 64 次 helper start,独立检查无残留。 - -本轮凭据审计在内存中读取既有授权配置,将实际 API Key 的原值、JSON 转义、URL 编码和 Base64 形式与明确的 8 个公开源码/文档文件、本轮本地记录及相关模型记录比对,匹配数为 0。公开差异中的密钥形状、邮箱和路径测试值均为合成样例;公开摘要没有原始对话、目标运行标识或端点。原始本地日志仍可能有本机路径,不作为已匿名化附件;无关的本地 UI 计划文件未加入本次提交。 - -## 第八轮补充发现(2026-10-06) - -### 参数错误文字能读懂,但缺少稳定字段定位:已补齐 - -既有配方在连接前校验输入,但只抛普通 Error;CLI 主要返回 `requestError` 字符串。现在配方校验共享固定代码、合法字段路径和更正提示,CLI 保留旧字段并增加结构化信息,模型宿主复用同一错误。不回显拒绝值或任意键名;范围漂移和实际执行失败不冒充可恢复参数错误。固定回复已验证一次更正、重复错误结束、同轮后续调用延期、HTTP 后无完整 usage 及更正后最终格式失败的独立计量。 - -### 实际选定状态仍可能被“猜对答案”掩盖:已修复 - -参数化原生读取中,节点完整但选定 toggle 为 unknown 时,初版验收仍可能接受 fixture 的正确最终数字;另一次补测确认,实际 toggle 已改变、仍为合法 On/Off 时,两条路径也可能让最终猜测绕过事实差异。两组补测在修复前确认红灯。 - -现在原生对已读区域复用既有配方校验,不再发起 MCP;混合核对已完成配方的实际 findings。未知/不完整控件或实际状态不支持独立固定预期时结束实验,保留已有观察,不追加读取。该规则用于已知合成场景的结果可信性,不是任意生产 UI 的事实 oracle。 - -### 模型打印了正确参数,但没有调用工具:仍未解决 - -首个 On 试运行的条件任务两条路径都提交错误范围。核对请求后明确了 query 和参数角色,保留调整前的源码和失败。随后 30 个独立样本仍只有原生 12/15、混合 3/15;混合 12 次直接打印 `{recipe,parameters}`,没有产生 function tool call,全部按 `FINAL_WITHOUT_OBSERVATION` 拒绝。它们不是 API 传输错误,也不是配方已执行后的统计错误。 - -打印参数与执行任务的区别仍需要改良调用契约,并独立验证;现有数据不足以把原因确定为单一提示、模型版本或提供方行为。本轮没有将最终文本解释为工具调用,也没有为得到通过样本继续追加请求。原生 Off 的另 3 次多余详情请求也保留为失败。 - -三种任务的比较资格全部为 false,收益百分比为 null。模型没有自然产生符合参数更正条件的错误,参数恢复率仍未知;7 次已发生的恢复全是最终格式修正,不能宣称新的参数提示已提高真实纠错率。客户端入口和守卫通过验证,当前模型参数化使用尚未通过整体验收。 - -### 第八轮验证和记录范围 - -新增测试 3 项,初始 3 项均失败;相关工作流/模型最终 18/18,真实 JSON 会话文件 6/6,主回归 482/482(不含该真实会话文件)。回归后提示调整再通过相关测试与类型检查;新会话测试最终扩展条件子集后单独通过。真实子集 P1/P2 分别 1/2 次读取,3 个审计 helper、自有 CLI/Gateway/fixture 实际退出。 - -6 个试运行和 30 个独立小样本共 36 个样本、70 次模型请求、200489 个实际总 token,失败与纠错均计入。模型运行审计 31 次 helper start,独立检查无残留,四组自有 Gateway 与 fixture 均退出;每组实际执行源码稳定,但最初 On 试运行与之后的提示不同,不能合并为同一配置的成功率。详细记录见实验说明,本地附件继续 Git 忽略。 - -本轮隐私检查覆盖明确的 12 个公开源码/文档文件和本轮本地测试、快照、模型记录,将内存中的实际授权 API Key 及 JSON 转义、URL 编码、Base64 形式比对,匹配为 0。公开摘要只含固定类别与数值,不包含原始参数/回答、端点或目标身份。原始日志可能含本机路径,保持本地且不提交;无关 UI 计划文件也未加入交付。 - -## 第九轮补充发现(2026-10-06) - -### 明确调用契约后,打印参数的失败在本轮未复现 - -第八轮的小样本混合侧 12 次最终 JSON 为配方参数,没有 function tool call。本轮只调整参数化提示/工具说明,明确实际函数调用与最终观察结果的职责,并标记 `call-then-findings-v1`。默认 API 工具选择与验收守卫未放宽,最终文本仍不能启动执行。 - -新增 1 项回归覆盖两条路径的 P1–P3:打印参数零读取失败,已有真实观察后仍打印参数则为错误 findings;没有为这种失败启动格式修正或自动重放。初始红灯是提示契约缺少明确调用要求,旧无观察守卫原本就通过,不能虚构其曾存在执行漏洞。相关测试 19/19 和类型检查通过。 - -真实模型试运行混合 3/3、小样本混合 15/15,均首次通过;18 次首次回复全部包含真实工具调用,参数对象作为最终回复的次数为 0。第八轮失败仍保留;跨轮缓存、时间和提供方变化没有随机对照,因此不能将改善唯一归因于某句提示或宣称通用成功率。 - -### 原生参数层级、Off 分支与最终格式仍会出错 - -试运行原生 P2 将区域 AutomationId 放在顶层而非 query,原生 P3 在 Off 后仍试图读详情。守卫分别以范围错误/不需要详情结束样本,各仅有摘要 1 次实际读取,没有追加错误读取。6 个试运行实际 4/6,不改算为全部通过,不重跑。 - -随后 30 个独立小样本最终全部通过,但原生首次仅 3/15,12 次最终格式错误通过一次反馈恢复,新增 55293 个 token、约 9.54 秒;混合首次 15/15,没有更正。两边底层读取各 20 次,成本差异包含 schema/提示、条件任务的模型交互与格式恢复,不能解释为混合少读了证据。两份小样本报告 success=true、三个场景比较资格通过;每路径 5 次仍不满足稳定性能或非 fixture 迁移的验证要求。真实可恢复参数错误机会仍为 0,参数恢复率未知。 - -全部 36 个样本为 34/36,共 89 次 API 请求、264356 个实际总 token,失败与格式修正均计入;试运行与小样本分开。四组执行源码稳定,51 个审计 helper start 的独立退出检查无残留,Gateway/fixture 实际退出;1274 次采样未观察到 fixture 进入前台。详细数值与本地记录位置见实验说明。 - -完整主回归一次 483/483,项目固定 SDK 10.0.303;新增测试仍为 1 项,未重复未修改的真实会话文件。实际窗口行为证据为本轮模型样本,不能仅靠这 483 项宣称桌面验证完成。隐私检查覆盖 6 个公开变更文件及本轮本地基线、测试和模型记录,对实际 API Key 的原值/JSON 转义/URL 编码/Base64 匹配为 0,公开新增行的本机绝对路径/密钥形状扫描为 0。原始记录保持 Git 忽略,无关 UI 计划未加入提交。 - -### 本地交付清单沿用旧指纹:已刷新 - -收尾构建后第一次 `delivery:verify` 返回 `Delivery contents changed or are incomplete.`。逐组件收集核对确认差异在旧清单记录的 Gateway、Skill 和配置内容;当前 Gateway 源码/产物与原生构建回执校验已通过,没有发现需要修改生产源码的原因。备份旧清单后用既有 `scripts/delivery-manifest.mjs` 重新生成本地忽略产物,再验证 `matched:true`。首次失败和诊断记录保留在本轮本地目录,不将清单刷新冒称新功能修复或重复全套测试。 - -## 第十轮前的只读评估与文档交接(2026-10-06) - -第九轮真实模型脚本直接组织提示并调用客户端工作流;JSON 会话有独立真实窗口测试,但两项通过不等于日常客户端完整使用链路已经验收。当前已安装 Skill 与仓库比对:主 Skill/代码手册一致,UI/诊断手册不同;已安装诊断手册没有实验分支的 `readonly-ui`/`checkbox-audit`。这确认了试用说明的差异,不证明默认安装本身有故障,也未执行全局同步或配置修改。 - -第九轮小样本原生 47 次模型请求、混合 30 次,差额中的 12 次是格式修正,5 次是条件任务的模型交互;两边 UI 读取均 20 次。schema JSON 体积为 6043/1117 字节,不是 token。按记录扣除原生格式修正阶段后的 token 差异约为 43.5%/60.8%/49.4%,仅用于账目分解,不是重新获得的首次正确对照。对原有 12 个格式失败作静态分类,均在首个 JSON 对象前有额外内容;未放宽解析、重算通过率或调用模型。旧 T1–T4 提示只在旧入口分支使用,未发现误用于参数化入口,不列为缺陷。 - -计划首页旧状态已更新;最新执行步骤集中到计划前部,历史正文保留。下一轮先做分支使用说明和实际客户端的 6 个功能任务,再确认非 fixture 目标。此交接仅更新文档,没有运行第十轮、同步已安装 Skill、增加 API 调用或重跑历史测试。 - -## 第十轮实际客户端入口与 New-tavern 验证(2026-10-06) - -### 实际入口通过,默认安装仍未切换 - -当前 Codex 按仓库 Skill 使用持久 PTY CLI:读取 ready、确认目标窗口、逐条提交请求、等待匹配 id 回执、读取完整结果附件并检查证据,最后显式 close。请求由当前客户端实际发出,没有使用固定回复驱动或真实模型基准宿主代替。仓库 Skill 补齐调用/回答顺序、业务成功与查询唯一性的区别,以及指定分支构建的试验方式;没有同步全局 Skill 或修改 MCP 配置。 - -已有原生连接根正确,但 Gateway buildId 为较早版本。本轮标准 inspect 与混合配方均通过同一个新分支 CLI 会话顺序调用,旧连接只用于有限窗口列表查询。分支 Gateway buildId 为 `ede8590e616a625d276a3a04fffc9de5ac187dce57433a595ad7b70af7ef2f57`,源码/产物身份校验通过;起始交付清单 `matched:true`。UIA Host 内嵌 revision 较早,其交付源码/产物回执校验通过,不将整个原生 Host 冒称重新构建。本次仅修改说明,生产源码和产物未改。受管 Skill 手册变化后,用既有交付脚本刷新本地忽略清单并验证 `matched:true`;清单刷新不是构建或默认安装切换。 - -| 任务 | 实际原生结果 | 实际混合结果 | 原生/混合 UI 读取 | -|---|---|---|---| -| P1:选定 0/3/6 | checkedCount=2,unchecked=[hybridCheck3],disabled=[hybridCheck6] | 一致 | 1/1 | -| P2:摘要 On,选定 1/2/3/5/6 | detailsRequired=true,checkedCount=4,未勾选/禁用集合同上 | 一致 | 2/2 | -| P3:独立 Off fixture | detailsRequired=false,没有请求详情 | 一致,只有摘要步骤 | 1/1 | - -6 项功能任务均通过,目标唯一、搜索和子树完整,没有截断或属性/遍历问题;参数更正和格式更正均为 0。混合报告本身的执行耗时约为 850/1711/869 ms。整个使用流程还有 CLI 启动、窗口发现、回执轮询和附件读取成本:本地包装观测冷启动约 10.5 秒,9 次完整附件读取各约 0.80–0.91 秒。PTY 等待存在最短轮询时间,这些数值包含工具调度且未包含全部模型思考时间,不能用作两条路径的性能对照。逐次客户端 token 无法取得,记录为未知;未调用额外 DeepSeek API,未重跑历史大样本。 - -### New-tavern:首次缺失保留,新的页面证据后观察成功 - -用户指定 New-tavern 并自行打开语音设置。根据源码的 AutomationId 候选,先在固定 PID/HWND 只读查询 `SpeechNormalize`。原生工具为 `isError=false`、`success=true`,但 `queryResult.status="not-found"`、匹配数 0;不能解释为复选框未勾选。混合配方同范围返回 `isError=true`、`status="stopped"`、`QUERY_NOT_FOUND`,保留失败步骤,没有 findings,实际读取 1 次。该组任务没有取得有效目标,不改算为成功。 - -用户提供语音页上部截图后,针对可见 `SpeechClearKey` 做一次新的原生/混合对照:两者均找到唯一复选框,Off、启用,checkedCount=0,unchecked=[SpeechClearKey],disabled=[];每条路径读取 1 次。用户随后提供“高级合成参数”已展开的截图,再安排原 `SpeechNormalize` 的一组新观察:两者均找到唯一控件,On、启用,checkedCount=1,unchecked=[],disabled=[],每条路径读取 1 次。每次只取该复选框及文字子节点,结果均无截图;不读取输入值、点击、填写、保存或触发语音 API。 - -共 3 组真实页面对照:首组目标缺失,后两组通过。页面状态/查询目标不同,不能合并为成功率或性能样本,也不证明整个语音页、多控件区域或条件摘要已迁移。展开前后结果支持“页面展开状态影响目标可发现性”;首次未独立记录父级的 Collapsed 状态,不能把这一推断写成已完成父子因果证明。 - -### 真实缺口:错误尚不能指导发现和展开父级 - -用户指出目标藏在“高级”菜单时,AI 应更准确发现原因并打开次级界面。当前工作流只给出 `QUERY_NOT_FOUND`、通用错误句和搜索证据,没有关联父级、页面前置条件或具体下一步;这是使用指导的缺口。源码候选也不能单独证明运行时目标藏在某个折叠区域中。 - -当前 [UiTreeReader.cs](../tools/WinCode.UIA.Host/UiTreeReader.cs) 能读取 ExpandCollapse 状态;[UiActionExecutor.cs](../tools/WinCode.UIA.Host/UiActionExecutor.cs) 的 click 只执行 Invoke/Toggle/SelectionItem,没有明确的 ExpandCollapse 操作。部分标题按钮可能支持既有模式,但本轮没有对该菜单执行任何动作,不能宣称现有 click 已验证能展开它。 - -合理的下一步是有界只读诊断、证据支持的父级/导航候选,以及独立的明确展开操作。优先使用 UIA `ExpandCollapsePattern.Expand` 设置目标状态,避免盲目 toggle;动作后读回 Expanded,再重新定位原目标。微软的 [Expand 方法文档](https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.expandcollapsepattern.expand?view=windowsdesktop-10.0) 给出该模式与不可操作/禁用的限制。未知、歧义、disabled 或动作结果不明时停止,不扩成坐标点击或自动重放。此处为后续建议,尚未实现;现有 `readonly-ui` 配方保持只读。 - -### 清理和记录边界 - -3 个 CLI 会话均得到 `closed:true` 并实际退出,各自 Gateway/观测到的自有控制台进程退出;2 只自有 fixture 实际退出。fixture 的 10 个审计 helper start、New-tavern 指定控件的 6 个 helper start 均独立检查无残留。用户的 TavernDesk 继续运行,没有关闭用户应用。fixture 的 2280 次有限采样未观察到 fixture 进入前台;该结论不覆盖用户手动开页或整个 New-tavern 过程。 - -原始参数、PID/HWND、控件附件、PTY 回显、前台进程采样和本地核验脚本保留在忽略的 `test-tmp/hybrid-tenth-checks/` 及本轮 3 个会话附件目录,不提交。原始前台采样可能含其他应用名称,不是匿名附件。公开只保留任务范围、结果与错误类别;用户截图不写入仓库。没有修改 New-tavern、读取其用户数据目录或密钥输入值。最终实际凭据比对覆盖 5 个变更手册/文档与本轮本地记录、附件共 48 个文件:已配置 DeepSeek API Key 的原值/JSON 转义/URL 编码/Base64 匹配为 0;公开新增行的本机绝对路径、密钥形状和邮箱扫描匹配为 0。检查仅覆盖这一已知凭据和所列范围,不等于扫描所有未知个人秘密。相对文档链接和代码围栏核对通过,纯说明改动未新增自动测试、未重跑主回归。 - -## 第十一轮明确展开导航(2026-10-06) - -### 行为与实际父级证据 - -用户批准“父级证据 → 针对性反馈 → 明确展开 → 子控件复查”工作包。本轮只处理 New-tavern 语音页的高级合成参数与 Normalize 复选框。真实 UIA 父级为名称“高级合成参数(可修改)”的 Group/Expander,没有 AutomationId;有完整唯一搜索和明确 Expanded/Collapsed 状态。没有修改 New-tavern 或为验收增加目标应用标识。 - -新增标准工具 `wincode_ui_set_expanded`,需要 inspectionVersion 4 Host;`expanded:true/false` 分别表达明确展开/折叠。目标必须在同一次有界搜索中唯一、启用、支持 ExpandCollapsePattern 且状态明确;已满足所需状态时不操作。不做 Toggle/坐标/焦点回退。旧 Host 成功回执经版本门拒绝,避免把忽略新动作后的只读成功当成动作成功。审计操作名称明确为 `setExpanded`,不包含控件名称或输入内容。 - -客户端 `expand-ui` 与 `readonly-ui` 分开。接收一个明确父级候选和原目标查询,固定 PID/HWND、默认 15 秒总 deadline(上限 30 秒)、最多 5 次调用:保存首次查询 → 有界父级根状态诊断 → 必要时展开一次 → 验证 Expanded 和完整父级树中的唯一子控件归属 → 重新查询原控件状态。初次未找到仍留在步骤中,不改写为成功。诊断根有意仅取一层,允许 maxDepth 截断,仅用于父级身份/状态,不宣称子树完整;验证阶段固定 maxDepth=8、maxNodes=128,截断、属性问题、歧义、未知或超时均停止。动作结果不明保留 actionAttempted,不自动重放。 - -既有只读错误改为区分完整搜索无匹配、歧义和搜索不完整,分别指导检查页面/选择器、增加实际精确条件、取得完整范围证据。完整无匹配不能直接归因为折叠,导航候选在实际归属验证前仍只是候选。 - -### TDD、失败与修复 - -新增恰好 3 项真实 WPF 自动场景:折叠 → 展开 → 读取实际子状态;已展开不关闭;歧义/不支持不操作。先运行主场景,预期红灯为 Unknown tool;实现后因适配器 payload 白名单漏传 expanded,3 项场景首次均失败为 INVALID_ARGUMENT。补齐传递后通过。类型检查曾发现可空字段访问,按明确守卫修正后通过。 - -工具兼容检查另发现新 schema 使用 additionalProperties=false,与本项目“忽略未来额外字段”的已有契约冲突;恢复既有兼容策略并保留规范字段校验后通过。收尾发现 UiAudit 的新动作落入默认 inspect 分支;补显式 setExpanded 映射,相关审计检查和最终实际动作日志通过。这些开发失败保留,不计入最终验收成功率,也没有重跑覆盖历史模型失败。 - -最终类型检查、Gateway 构建及 UIA Host Release 发布通过。核心回归仅完整运行一次,使用项目固定 SDK 和文件并发 4,483/483;最终 3 项导航场景 3/3。审计映射修正后只运行相关既有审计测试,1/1(内部 17 项既有检查),没有第二次完整回归。现有参数拒绝/旧 Host 版本门及工具分发契约也通过。已增加工具的独立分发期望与工具数 20,保留旧 click/type/setValue 行为。 - -### 当前 Codex 客户端与真实 New-tavern - -使用两个显式分支 CLI 会话,均核对实际 Gateway build 身份并读取全部 JSON resultFile。首个候选构建 buildId 为 `7c076f06ace3e5ae0dc1e5c1afe4df834a5db1988fae08f18201de80b8c90e77`;完成兼容策略和分发计数修正后的最终构建为 `adc470a4968eaa581a8c8e8884179d5526d5ee0b8cff2645059e9f928aa8e421`。UIA Host 本轮确实重建为 inspectionVersion 4,交付清单源码/产物验证通过。内嵌 revision 是提交前基线,不拿后续提交 SHA 当作构建身份。 - -| 实际观察 | 初次子查询 | 父级诊断 | 展开调用 | 最终 Normalize | 调用数 | -|---|---|---|---:|---|---:| -| 候选构建,先明确折叠 | complete not-found | Collapsed | 1 | On | 5 | -| 同一会话,保持展开 | unique | Expanded | 0 | On | 4 | -| 最终构建/审计修正后,再明确折叠 | complete not-found | Collapsed | 1 | On | 5 | - -每次折叠只作为已授权导航验收的前置操作,计数表不含该准备调用。展开后的完整父级树为 25 节点,Normalize 单控件树为 2 节点,全部唯一且搜索/树完整,无属性/遍历问题。两次主路径均实际验证父级包含该子控件,再读取其 On 状态。最终保留高级区域展开,没有更改复选框、输入、保存或发起语音请求;用户应用继续运行。 - -这 3 次是功能验收,包括修正后的同一路径重验,不是独立模型统计样本或性能比较;当前客户端逐次 token 未取得,不启动外部模型 API 基准。父级仍需用户/AI 从已有页面证据提供候选;任意菜单自动发现、递归探索、多区域导航与稳定收益未实现/未验证。 - -两个 CLI 均显式 close、closed:true 且实际退出,各自 Gateway 也退出。按目标与时间限定的 17 个审计 helper start 检查未发现原 helper 残留,1 个 PID 已被其他进程复用并单独排除;最终导航的 2 条动作日志均为 setExpanded。没有关闭用户应用。本轮没有专门前台采样,不宣称展开绝不激活目标应用。 - -### 记录与隐私 - -原始步骤、目标 PID/HWND、控件文字、请求/构建结果和本地核验脚本保留在 Git 忽略的 `test-tmp/hybrid-eleventh-checks/` 与两个 CLI 附件目录(`run-pvS5s1`、`run-eHLBNd`),不提交,不当匿名附件。读取只用状态和控件属性,未调用输入值读取,没有访问 New-tavern 用户数据/密钥配置。父级查询返回高级参数区域文字,查询搜索过程仍可能访问其他节点属性,不能声称完全的隐私隔离。 - -实际凭据比对覆盖本轮 21 个变更公开文件及本地记录/附件共 47 个文件:已配置 DeepSeek API Key 的原值、JSON 转义、URL 编码和 Base64 匹配为 0;配置仅在本机内存中用于比对,未调用模型 API。公开新增行(包括新模块)的本机绝对路径、邮箱与密钥形状扫描匹配为 0;6 个文档/手册的相对链接与代码围栏检查通过。检查只覆盖这一已知凭据及所列范围,不能推成所有未知个人秘密均已扫描。公开文档只保留脱敏行为、数量、错误和构建标识,不提交用户截图或本地原始记录。 - -## 第十二轮有限父级发现(2026-10-06) - -### 实现与判断边界 - -用户要求先实现一部分、验证自动发现方向。本轮只修改客户端编排,复用现有 UIA Group 查询、状态读取与明确展开工具,不增加原生协议、依赖、模型调用或服务。`parentQuery` 改为可选;省略时先查目标,唯一可读即返回,否则仅在完整无匹配时搜索当前窗口的 Group。搜索上限 1000 节点/20 匹配,所有返回 Group 的状态与启用证据必须可判定;唯一启用的 Collapsed 候选有实际名称/AutomationId 才继续重新定位。发现阶段的根子树截断不等于 query 搜索截断,两种完整性分别判断。 - -没有把父级名称、高级区域关键字或目标应用写死。多个候选返回 `NAVIGATION_CANDIDATE_AMBIGUOUS` 和候选表;零候选、搜索/属性/状态不完整、无精确身份分别返回针对性错误,不尝试操作。候选唯一仍不能证明它包含目标,因此展开后要求完整父级子树、唯一子控件归属,再重新读取原控件。最多 6 次工具调用、1 次展开;已有显式父级路径保持最多 5 次。失败或结果不明停止,不自动重放,也不自动折回,以免再引入未经验证的动作。 - -### 实验方法与实际结果 - -新增恰好 3 项自动测试。两个防误操作测试先运行,旧实现因必须提供 parentQuery 而失败(2/2 红);实现后两项通过,覆盖多候选/零候选、搜索限额、未知状态/启用属性、属性读取问题及无身份。第三项在真实 WPF fixture 中省略父级选择器,验证折叠 → 自动发现 → 展开 → 子控件 On,并在已可见时验证只有一次读取、零导航。加上既有三项导航兼容回归,共 4/4。类型检查与 Gateway 构建通过,完整核心回归只运行一次,项目固定 SDK、文件并发 4,485/485。 - -当前 Codex 使用一个明确分支 CLI 会话,核对实际 Gateway buildId `80685020c6fa1c69a99529fc1512cc57e21baeefb332eccf1769ca4e3597e179`,逐次读取完整 resultFile。复用第十一轮 inspectionVersion 4 Host,原生源码本轮未改。New-tavern 先确认现有高级 Group,再将其折叠作为前置准备;正式请求仅提供 `childQuery`,不包含父级名称或 ID。 - -| 实际场景 | 发现依据及动作 | 最终状态 | 工具调用数 | 流程内耗时 | -|---|---|---|---:|---:| -| 高级区域折叠 | 目标 complete not-found;完整 Group 搜索找到唯一 Collapsed 候选;重新定位后展开一次;完整子树验证归属 | Normalize On | 6 | 约 4.99 秒 | -| 同一页面已展开 | 初次目标唯一、树完整,直接返回;无导航 | Normalize On | 1 | 约 0.84 秒 | - -调用数与耗时不含窗口定位、冷启动、准备折叠、读取结果文件等成本;每种场景只有一次,不是性能基准或模型成功率统计。折叠时查询遍历 97 节点,展开后 119;发现一个 Group,最终父级树 25 节点、目标树 2 节点,父级与最终目标验证无截断/属性/遍历问题。准备阶段的一次窗口枚举与尚未结束的 fixture 审计冲突,返回 AUDIT_BUSY;等待测试结束后再枚举成功,失败未抹掉,正式导航不重放。 - -CLI 显式 close 并实际退出,独立检查入口与 Gateway 均无残留;目标时间段的 9 次 helper 审计均有成功结束记录且原进程已退出。用户应用继续运行,高级区域留在展开状态。没有填写/保存业务设置、切换复选框或发起语音请求;未专门采样前台变化,不宣称展开绝不激活窗口。 - -### 限制与记录 - -第十二轮结束时能力只覆盖唯一的折叠 Group。第十三轮已接通当前 AI 对多个实际候选的选择与后续验证;其他控件类型、两层以上路径与虚拟化节点仍未实现。本轮未运行外部 LLM API,不证明更便宜模型会正确自主选择目标,也不能把单一真实应用验收推广为任意菜单发现成功。 - -本地原始结果位于 Git 忽略的 `test-tmp/hybrid-twelfth-checks/` 及会话附件 `test-tmp/skill-sessions/run-wOyXEl/`;包含本机路径/目标标识/区域控件文字,不提交或当匿名附件。取证没有读取输入值或访问 New-tavern 密钥配置,Group 查询仍会遍历窗口属性,并非完整隐私隔离。公开文件只记脱敏行为和数量。已配置 DeepSeek 凭据的原值及 JSON/URL/Base64 变体在本轮公开变更、本地记录和会话附件中的匹配为 0;只在内存中比对,不发起模型请求。此结论限于该已知凭据与指定文件,不声称扫描所有未知个人秘密。公开新增行的绝对路径、邮箱及密钥形状检查和文档相对链接/围栏检查通过。 - -## 第十三轮多候选选择(2026-10-06) - -### 行为与范围 - -用户批准接通“多个候选 → 当前 AI 选择 → 程序验证执行”。原实现已有候选列表和显式 parentQuery,但把多候选统一标记为 stopped 错误。本轮增加明确的待选择结果和候选请求,保持原生协议与依赖不变。 - -发现多个完整、启用、折叠的 Group 且存在可定位项时,返回 status=selection-required、success=false、isError=false,保留 NAVIGATION_CANDIDATE_AMBIGUOUS 诊断码。候选包含实际名称/AutomationId/类型/状态,可定位项附 nextRequest,绑定本次 target、childQuery 与 timeoutMs。调用方必须读取完整结果,根据任务与实际候选文字选择一个,补新 id 提交;等待选择不能当作任务成功。没有新模型调用或确定性语义评分,实际选择由当前客户端 AI 作出。 - -新 candidateQuery 与 parentQuery 互斥。选择后的请求先重新观察目标与完整 Group 匹配集,要求选择仍对应唯一、启用、折叠候选,再以当前实际身份重新定位。消失、改名、禁用或重复返回 NAVIGATION_SELECTION_STALE,零动作;搜索不完整保留 NAVIGATION_DISCOVERY_INCOMPLETE。动作失败/结果未知或展开后找不到唯一目标均停止,不重放。目标已可见时直接读取;显式父级及单候选路径保留。每选择请求最多 6 次工具调用、1 次展开,本轮客户端只做一次候选选择,没有自动遍历列表或多层递归。 - -### 验证方法与结果 - -新增恰好 3 项回归:选择必须经实时发现验证(消失、改名、禁用、不完整、重复);动作失败/选错父级不重放、不生成 findings;真实 WPF 两个折叠区域下的发现与选择,仅展开所选区域。另更新已有多候选测试验证新状态与 nextRequest 契约。先运行两个新增单元测试和该既有契约测试,旧实现三项失败(未知 candidateQuery/仍为 stopped),实现后三项通过。真实 WPF 测试在既有 action fixture 中用可选 --navigation-candidates 增加 Speech advanced 与 Display advanced,不修改用户应用。新场景与既有导航兼容回归共 5/5;类型检查、fixture 发布、Gateway 构建通过。完整核心回归仅一次,项目 SDK 与文件并发 4,487/487。 - -当前 Codex 另外通过一个分支 CLI 会话完成实际功能验收,核对 buildId `d88d2d13ccd6dd81e09f2bc60046d3ad6a86abbd466f4a58e9ecc5a7ea565754`,读取全部结果文件。观察两个 Group 后,将显示区域折叠作为前置准备;语音区域初始已折叠。正式任务查询 Normalize,发现阶段两次读取,返回两个实际标题及 nextRequest,零动作。当前 Codex 根据语音 Normalize 任务与 Speech/Display 标题选择 Speech advanced,并提交返回请求:重新扫描后六次调用,明确展开一次、父级完整验证、目标 On;最后独立读取显示 Group 为 Collapsed。发现约 1.75 秒、后续流程约 5.24 秒,均不含启动、准备、AI 决策与结果文件读取成本;单次客户端验收不作为模型成功率/性能统计。 - -本轮窗口枚举、准备、正式请求和独立对照共 12 次目标 helper 审计,均有成功结束记录且原 helper 无残留;自有 fixture、CLI 入口、Gateway 均独立检查退出。仅导航与状态读取,没有输入或业务设置保存。未专门采样前台变化,不宣称界面操作绝不打扰用户。 - -### 限制与隐私 - -多候选实际验收使用独立真实 WPF fixture;New-tavern 此前只证明单候选路径,当前未发现或安排非 fixture 的多候选页面。候选信息本轮仅含实际身份与状态,未增加祖先路径/邻近标签/图像辅助;遇到语义相近候选仍需进一步只读取证或澄清。多层嵌套、任意菜单/Tab/TreeItem 与自动多次尝试未实现。候选有效性验证能拒绝失效选择,但不能保证 AI 语义判断正确,因此展开后的目标归属仍是完成条件。 - -本地记录在忽略目录 `test-tmp/hybrid-thirteenth-checks/` 与 `test-tmp/skill-sessions/run-DRwrfe/`,不提交;可能含本机路径、PID/HWND、窗口控件文字,不当匿名附件。已知 DeepSeek 凭据及 JSON/URL/Base64 变体在本轮公开变更、本地记录和会话附件中的匹配为 0;只在本机内存中比对,未请求模型 API。公开新增行的绝对路径、邮箱和密钥形状扫描,以及文档相对链接/围栏检查通过。此结论限于已知凭据和所列范围,不代表所有未知个人秘密已扫描。main、默认安装和无关文件保持既有状态。 - -## 第十三轮后状态复核(仅文档,2026-10-06) - -按用户要求核对剩余问题并更新 MD。只读检查 `70a378f` 的 ExpandUiWorkflow、SkillSession/CLI 和现有记录,没有修改生产代码、重跑测试或进行 UI/模型实验。487/487、5/5 仍是上一轮结果,不计作本次验收。 - -除既有多层/其他控件/真实应用和模型验证缺口外,本次明确补充以下当前实现限制: - -- 每次 runExpandUiWorkflow 独立创建 deadline 和 dispatchedCalls;发现与候选选择的多次请求没有共享累计动作/调用/时间预算。nextRequest 复制固定 target 与 childQuery,但 CLI 没有校验新请求与上一决策的任务范围一致。手册中的“一次选择、一次展开、不改换目标”目前是调用方规则,不是跨请求的强制状态机。 -- 候选只含身份、类型与状态,没有祖先路径、邻近标签或范围定位;同名/同 ID 候选仍可能无法唯一操作,语义相近项也不保证选择正确。 -- discoverParent 对全部 query.matches 检查未知状态/属性问题;无关 Group 的问题也会阻止发现。没有已证实的局部查询范围时继续保留不完整错误,不能把漏读候选当作唯一。 -- 展开后没有找到唯一目标就以 CHILD_RELATIONSHIP_UNCONFIRMED 停止,尚未区分“选错区域”和“目标还在内层折叠”;最终读回仍只接受明确 toggle 状态。 - -剩余事项的当前清单集中在 [交接入口](hybrid-next-iteration-plan.md#当前未实现与待验证事项)。同步修正三份 docs 的旧轮次入口及第十二轮“当前能力”的历史措辞,保留原失败/数量。两个 Skill 手册明确单请求预算与调用方流程规则的差别;未切换安装 Skill 或 main。 - -## 产品约束设计复核(仅文档,2026-10-06) - -用户要求先评估类似的过度防御/约束,再更新 MD。只读核查混合 UI 工作流、统计配方、客户端请求入口、UI 查询契约、原生动作/属性读取及相关 Gateway 限制;没有修改生产代码或新增测试/UI/模型实验。评估局限于这些调用路径,不是全产品全面审计。源码基线仍为 `70a378f`;487/487 与导航 5/5 是第十三轮历史结果。 - -发现的设计问题及依据: - -- **证据门槛覆盖了任务不需要的字段。** ExpandUiWorkflow 的完整性检查拒绝子树中任意 propertyIssues,discoverParent 拒绝任一返回 Group 的未知状态/属性问题。ReadonlyUiWorkflow 同时检查整树和源码/代码扫描完整性。可能出现所需目标状态已取得、辅助节点或源码缺口仍阻断的情况。这个影响是源码推断,本次没有故障复现;后续需按任务必需证据验证,不能将不完整搜索的一项匹配称为唯一。 -- **错误码混淆了真实禁用与其他属性缺口。** UiActionExecutor.SetExpanded 使用 `IsEnabled != true || PropertyIssues.Count > 0` 返回 TARGET_DISABLED。Describe 的属性包含 className、isOffscreen、bounds 等;因此启用已确认但辅助字段失败也会被此码阻断。应该保留必要状态的拒绝,独立说明辅助缺口;当前行为未改。 -- **候选续接将良性状态变化当作选择失效。** 当前 candidateQuery 从启用的 Collapsed 候选中重新选择。原区域已展开但目标尚未出现时可能返回 NAVIGATION_SELECTION_STALE;目标已可见的直接读取路径正常存在,不能概括成“已经展开都会失败”。原生 setExpanded 已支持同状态 no-op,未来可复用观察与状态判断。 -- **成功导航之后缺少下一步状态表达。** 已确认展开后没有唯一目标,当前统一 CHILD_RELATIONSHIP_UNCONFIRMED;未区分内层折叠、加载、错误区域等原因。已知动作成功且任务未完成,与动作结果未知需要分别处理。前者可依据新证据继续判断,后者不得自动重放。 -- **实验固定值被写得像通用产品规则。** 一次选择/展开、5/6 次调用、父树 128 节点及只读工作流最多一张图是当前实现边界,未测得通用最优值。保留资源限制,等具体工作流需要时调整相应项;不能把它们推为任意多层任务的完成标准。 - -必要检查继续保留:固定实际窗口身份、完整搜索支持的目标唯一性、任务所需状态/归属、动作后读回、取消/超时处理、未知结果不重放、输入协议拒绝未知字段,以及后台任务不自动回退前台操作。checkbox-audit 输出包含二元勾选与禁用统计,确定 On/Off 与 isEnabled 属于该配方所需证据。现有受理容量和文本/图像上限没有本次证据证明需要整体取消。 - -修订决策:跨请求累计预算没有当前必要性,不列为下一轮门槛;不新增令牌、配额框架或逐步授权。任务范围采用现有窗口和用户任务,允许按新观察修正查询;当前 nextRequest 原样续接规则仍保持实际协议要求。后续优先考虑辅助证据与必要证据分离、错误反馈准确性,其次是有效候选已展开后的续接及有依据的内层导航。详见 [修订后的推进顺序](hybrid-next-iteration-plan.md#修订后的推进顺序)。这覆盖此前“先补累计预算”的建议,不改变历史记录或宣称这些方向已经实现。 - -本次仅更新三份 docs 和两份分支 Skill 手册;默认安装与 main 未切换,无关未跟踪文件未读写。检查记录存于 Git 忽略目录 `test-tmp/hybrid-design-review/`。五份修改文档的相对链接文件存在性、代码围栏配对与新增行的绝对路径/邮箱/密钥形状检查通过;已知配置 DeepSeek 凭据及 JSON/URL/Base64 变体在五份公开文件和本次检查记录共九个文件中匹配为零,只在本机内存中比较,未请求模型 API。该结论仅覆盖所列文件和已知凭据,不保证全机所有未知秘密均已扫描。没有将历史回归结果当成本次重新测试。 - -## 第十四轮辅助属性缺口与错误反馈(2026-10-06) - -### 已实现的行为与范围 - -按产品约束复核的第一项推进,限定在 ExpandUiWorkflow 和原生 setExpanded:className、bounds、isOffscreen 缺口不再单独阻断语义展开及复选框状态读取。steps/actionTarget 仍保留原 propertyIssues 和计数,不删除缺口或声称全部属性完整。其他属性问题、无法由返回树解释的属性计数、搜索/遍历/必要状态不完整仍停止。没有引入策略框架、累计预算或新的授权步骤。 - -原生实际禁用仍为 TARGET_DISABLED;身份/启用证据缺失为 TARGET_EVIDENCE_INCOMPLETE,并提示核查缺口。客户端显式父级的启用未知也用该码;根状态诊断仍允许仅 maxDepth 截断,最终归属取证仍要求完整父树与唯一子控件、独立读回真实 toggle 状态。只读工作流和 checkbox-audit 未修改。未知 Group 的必要状态、候选已展开时的续接和多层导航也未顺带实现。 - -### 测试先行与实际窗口证据 - -新增三项自动化测试:两项核心场景、一项真实 WPF 属性故障场景。首先新增回归,两项核心测试均失败:辅助缺口被 NAVIGATION_DISCOVERY_INCOMPLETE 拒绝,启用未知分类不符。真实窗口中旧 Release Host 在唯一、启用的 Expander 仅有 `className:unsupported` 时返回 TARGET_DISABLED,复现了错误阻断;更新 Host 后同一属性故障场景通过。 - -- 核心相关测试 11/11:辅助字段缺口保留,唯一父级展开后正确读到 On;身份缺口、未知属性、未解释的计数、启用未知、实际禁用、不完整搜索/树和未知 toggle 不得完成或执行导航。随后针对实际 isEnabled:unsupported 补足同一测试场景,复核通过。 -- 类型检查、Gateway 构建、Release Host 发布及 WPF fixture 发布通过,复用项目已安装 SDK,没有修改 global.json 或安装依赖。核心完整回归运行一次,489/489 通过;没有重跑外部模型历史样本。 -- 新的真实 WPF 测试 1/1:辅助字段缺口下原生展开及混合归属/子状态读取成功;启用证据未知和实际禁用分别给出对应码,区域保持 Collapsed。六项既有实际动作/导航回归 6/6,包含单候选、多候选、已展开不切换、歧义/不支持,以及既有点击输入路径。 -- 当前 Codex 使用分支 CLI 一次会话实际验证:先观察父级唯一、启用、Collapsed 且类名缺口;expand-ui 经 5 次标准工具调用、1 次展开,父树完整并确认目标归属,独立子控件读回 On,动作与步骤中的类名缺口保留。另一个启用未知区域返回 TARGET_EVIDENCE_INCOMPLETE,随后只读确认仍为 Collapsed。不是外部模型独立准确率实验。 -- CLI 握手核对的 buildId 为 `253029ad209af867b02c01a0e01478d8f337f3a3898c18495e6d2d6d0e876c63`,源码/产物摘要与磁盘构建一致;revision 字段是实现前的 `77f4ab4`,验收期间的未提交实现由 sourceHash/artifactHash 对应,不能只用 revision 断言旧代码。 -- CLI 显式 close 后入口和 Gateway 已退出;独立 fixture 按既有进程树清理方式结束,8 次该窗口的 helper 启动均无残留。关闭前记录的自有进程创建身份复核为 0 残留。fixture 的清理退出码为 1,来自测试结束的进程终止,不记录为应用自然关闭。 - -真实夹具准备中有两项失败,保留事实:首次编译的局部变量名与既有 disabled 控件冲突,改名后发布通过;首次属性故障注入使测试窗口在 READY 前退出。核查 WPF 官方源码发现 IsEnabledCore 也由事件更新调用,而 UIA 属性查询经 ElementUtil.Invoke 将异常传给调用方。夹具仅在明确故障模式下处理自定义 FixturePropertyException 的事件异常,其余异常不处理;属性查询仍实际返回 unsupported,测试确认缺口而不是直接伪造 Host JSON。参考 [WPF AutomationPeer](https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Automation/Peers/AutomationPeer.cs)及[ElementUtil.Invoke](https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/ElementUtil.cs)。这些失败与修正属于夹具准备,没有通过放宽生产守卫规避。 - -### 当前限制与记录 - -这是针对三类辅助属性的局部修正,不是任意字段/任意任务的证据策略。无关节点的必要属性/状态问题仍可能阻断;只读工作流的 UI/源码统一门槛、良性展开状态续接、内层折叠和非 fixture 多候选尚未处理。本轮实际属性故障验收使用隔离 WPF 提供方,没有对 New-tavern 注入故障,也没有外部 API 调用、性能结论或默认安装切换。 - -用户提出非 WPF 兼容性后,核对了变更范围:UiTreeReader、UiPropertyEvidence、inspect/review 返回结构、click/type/setValue 均未改;分类依据 UIA 字段而非框架名称。WPF 夹具只提供本轮故障复现证据,不能推为所有 Win32/WinForms/Electron/WebView2 提供方已验证。按用户指定尝试识别 Open-LLM-VTuber:源码保留 Electron 和 Rust/Tauri 壳;首次实际窗口枚举中“桌宠”无匹配,“Open-LLM”只命中文件资源管理器,尚未定位运行中的目标应用。未读取其配置、凭据或聊天内容,未修改该项目,也未启动应用;已请求用户打开实际版本。窗口未找到不是 UIA 控件读取回归证据。 - -用户随后打开根目录桌宠,实际运行的是 Electron 壳。通过专门绑定该项目的分支 CLI 定位窗口并只读取证:浅树先按 maxDepth 截断,maxDepth=16/maxNodes=128 后取得完整的 15 节点,实际最深 12 层,Window 1、Pane 9、Document 1、Group 2、Text 2,遍历错误为 0。外层两处 automationId:unsupported、一处 isOffscreen:unsupported 保留;收窄为唯一 Document 后只读到 5 节点、4 层,树和搜索均完整、属性问题为 0。完整树没有复选框,两个 Group 的 ExpandCollapse 均为 unsupported;不能把普通 DOM Group 当可展开菜单,也不能把这个结果算作非 WPF 导航验收。没有触发点击/展开、读输入值、更改无障碍设置或修改目标项目。该兼容性观察证明普通读取仍能识别实际 Electron 窗口/文档,未覆盖其他提供方或辅助属性故障下的非 WPF 导航。 - -这个窗口也提供真实的设计证据:整窗很深不代表目标区域同样很深,先缩小到实际 Document 可以取得完整局部证据;不能把固定深度 8 当所有框架的合理完整性门槛。后续若调整,应由明确截断和实际区域决定,而非按 WPF/非 WPF 分叉。Electron 的无障碍树可能由辅助技术或应用 API 启用,参见 [Electron 官方无障碍说明](https://www.electronjs.org/docs/latest/tutorial/accessibility);本次已观察到 Document,未判断其设置关闭或调用启用 API。 - -专用识别会话显式 close 后自有 CLI/Gateway 和该窗口 7 次 helper 启动均无残留;用户打开的桌宠保持运行。记录在 `test-tmp/hybrid-fourteenth-checks/non-wpf*.json` 及 `test-tmp/skill-sessions/run-41JWsi/`。 - -原始记录在上述忽略目录及 `test-tmp/skill-sessions/run-aHsY38/`,可能含本机路径、进程身份和控件内容,不作为匿名附件。只提交实现、三个测试及现有手册/记录的脱敏更新;main 和无关文件保持原状。最终检查五份 MD 的相对链接文件/代码围栏、全部公开新增行的绝对路径/邮箱/密钥形状以及 git diff --check 通过。十个公开变更文件、本轮记录及两个实际 CLI 会话附件共 58 个文件,已知配置 DeepSeek 凭据及 JSON/URL/Base64 变体匹配为零;只在本机内存中比对,未请求模型 API。结论限于已知凭据和所列文件,不延伸为所有未知秘密或全机隐私保证。 - -## 第十五轮前的设计分析与文档整理(仅文档) - -核对基线:本地 HEAD 与远端实验分支均为 `dc3eec3bdc52c3e1754b776ce257eb2cf4717ffd`,远端 main 仍为 `038c0ccc7372948da405546cc7ec0add56eea9de`。本次检查 ExpandUiWorkflow、UiContracts、CLI/会话入口及相关工作流测试;复用第十四轮实际窗口记录,没有重新打开应用、运行代码测试或请求外部模型。 - -源码确认两处待改路径:candidateQuery 经过 Collapsed 过滤后才匹配,正常变为 Expanded 的所选区域可能误报选择失效;展开后的归属不成立统一停止,catch 又覆盖 nextAction,未将已有父树中的候选/歧义/截断整理为下一步。它们是当前源码分析结论,第十五轮的独立复现和修复验收尚未进行。 - -本次建议先做“已展开状态续接与局部诊断”,原因是它直接减少已打开区域的重复处理,并让 AI 使用已经取得的证据;完整方案只维护在[第十五轮计划](hybrid-next-iteration-plan.md#第十五轮计划已展开状态续接与局部诊断)。同时核对到 UiQuery 没有父范围搜索字段,故局部候选不能自动变成有归属保证的递归操作。第十四轮 Electron 记录支持优先收窄实际区域,但不证明已实现非 WPF 导航或任意多层路径。官方 ExpandCollapseState 的直接子级语义及相对元素搜索说明已在计划中注明来源。 - -文档整理限于三份现有 hybrid MD:规划负责当前状态、优先级和下一轮验收;实验说明负责接口及历史方法;本文负责事实、失败和验证边界。规划中的第十四轮前复核、第十至十三轮验收及第六至九轮计划折叠保留,修正旧“当前状态以第十三轮为准”的指引,不删除历史失败,不另建重复计划文件。生产源码、测试和 Skill 手册未修改,尚未实现的行为不写入操作指南。无关未跟踪 UI 计划未读取或改动。 - -本次文档检查:三份 MD 共 44 个相对链接的文件存在性、其中 15 个锚点、代码围栏和折叠标签配对通过;git diff --check 通过。公开新增行仅检查本机绝对路径、邮箱和常见凭据形状,不读取凭据配置或实验原始窗口内容,也不将第十四轮已知密钥扫描的结论延伸到本轮。未启动代码测试,未将历史通过数计作本次验证。 - -## 第十五轮已展开续接与局部诊断(2026-10-07) - -### 交付行为与兼容范围 - -实现前基线 `dc3eec3`。candidateQuery 改为直接重新定位当前 Group,不再枚举所有 Group 或要求所选区域仍 Collapsed。唯一有效的 Expanded 区域继续观察,不关闭、不再次展开;Collapsed 仍只展开一次。完整搜索无匹配或多义才是 NAVIGATION_SELECTION_STALE;搜索不完整、真实禁用及启用证据未知分别沿用 QUERY_INCOMPLETE/TARGET_DISABLED/TARGET_EVIDENCE_INCOMPLETE。 - -动作后复用已有父树,diagnosis.localObservation 增补区域 query、结构完整性、截断/遍历/属性计数、匹配及实际内层 Collapsed Group;列表各最多 20 项,超出数量显式报告。节点身份字段和 propertyIssues 保留,id/parentId 仅说明本次关系,不生成内层可执行 nextRequest。缺失、局部歧义和不完整读取分别给针对性 cause/nextAction;catch 不再覆盖这些诊断。parentState 表示最新父级观察,展开前状态仍在 steps 中;旧测试的 Collapsed 断言已改为 Expanded。 - -completed/selection-required/stopped、success 和 findings 的完成含义不变。父级已展开而任务未完成仍 stopped,不凭候选产生 findings;未知动作结果不重放。客户端新增诊断字段,UiQuery、标准 MCP schema、原生读取器/动作器、普通 readonly-ui 和统计配方均未修改,没有新增依赖、框架、累计预算或授权步骤。 - -### 测试先行与失败记录 - -新增三项自动化测试:两项核心场景、一项真实 WPF 嵌套场景。两项核心测试先 RED:已展开候选拿不到有效父状态,动作后的诊断仍保留旧 Collapsed;实现后 GREEN。相关核心 13/13;测试覆盖可读目标、内层折叠、完整区域无目标、局部同名歧义、截断/遍历失败,以及既有失效选择、禁用和未知动作不重放。 - -真实 WPF 新场景 1/1:专门的外层 Expanded/内层 Collapsed 区域同时存在无关启用未知 Group,所选目标续接仍取得完整局部诊断,3 次调用/0 次动作;依据返回的实际内层 ID 独立定位后,读到 On,只有一次内层展开,外层保持 Expanded。目标随后可见时只读取一次。夹具仅在 --nested-navigation 模式增加这一结构。 - -既有六项桌面场景首次 4/6:一项仍期待旧 parentState=Collapsed,另一项 AUDIT_BUSY。核查原生 UiAudit 发现不同 helper 共用审计目录互斥锁;本轮把桌面回归与人工 New-tavern 验收并行,导致忙锁,不是展开功能的新缺陷。串行桌面回归后 5/6,忙锁未复现,剩余仍为旧断言;更新该断言后仅重跑受影响场景 1/1。首次和中间失败日志均保留,没有把它们改写成一次 6/6。 - -类型检查、Gateway 构建与 WPF fixture 发布通过;原生 Host 未修改,沿用已验证的 Release 构建。完整核心回归运行一次,文件并发 4、项目既有 SDK 下 491/491,通过后未重复完整回归。没有外部模型 API 调用或性能实验。 - -### 当前 Codex 客户端实际验收 - -两次 PTY CLI 会话核对并使用当前分支构建,buildId 为 `e71d12975c2f4d3d3b28fb89e0ad583d10b4fb7fc84de0e55b4ae4f2f369176f`,hello 报 verified,源码/产物摘要匹配磁盘。revision 为实现前 `dc3eec3`,验收期间尚未提交的实现由 sourceHash/artifactHash 识别,不将 revision 当唯一版本证明。 - -- **真实嵌套 WPF**:当前 AI 提交 candidateQuery 续接外层,完整结果为 stopped/CHILD_RELATIONSHIP_UNCONFIRMED;父级 Expanded,实际内层候选 Collapsed,3 次只读调用、零动作。AI 读取完整候选后,按实际 ID 发起独立 parentQuery;5 次调用、1 次内层展开,验证归属并独立读取 On。再次只读观察确认外层仍 Expanded。此处的后续请求由调用方依据实际结果决定,不是工作流内部递归。 -- **真实 New-tavern**:未发现运行实例后,从用户指定项目的现有 app EXE 启动,先实际定位并点击设置/语音导航,再观察高级区域唯一、启用、Collapsed,明确展开一次。没有读取密钥值、聊天正文或调用测试连通性/保存。高级已展开后,candidateQuery 路径读到 SpeechNormalize=On,只调用 1 次、零展开;明确 parentQuery 路径完整验证父树归属并独立读回 On,共 4 次、零额外展开。 -- New-tavern 首次读取被同时进行的桌面回归拒为 AUDIT_BUSY,actionAttempted=false、没有有效 findings;保留该失败。桌面测试全部结束后重新安排只读验证,上述两条路径通过,没有重放展开动作。 -- 两次 CLI 均显式 close 并观察入口退出;自有嵌套 fixture 清理退出,创建身份核查为 0 残留;两个目标的实际客户端审计 helper start 共 20 次,检查为 0 残留。本轮自行启动的 New-tavern 通过正常 CloseMainWindow 退出,没有结束用户既有进程。fixture 清理退出码 1 来自测试进程终止,不算自然关闭。 - -### 文档、Skill 与剩余限制 - -将前一轮规划与整理变更一并保留;更新三份 hybrid 文档的当前入口、完成状态和剩余清单,原方案折叠作为历史。更新仓库 SKILL.md、UI 与诊断手册,说明最新状态续接、局部字段、准确错误分类,以及实验构建/旧连接差异。按用户本次要求,使用现有 sync-skill 脚本先备份旧安装文档、再同步;4 个受管文件校验一致,无关额外文件保留,备份目录不暴露第二个 SKILL.md。MCP 配置、默认连接和 main 没有切换。 - -仍未实现父范围查询、自动递归导航、其他导航控件及虚拟化/滚动。首次自动发现仍可能被其他 Group 的关键状态问题阻断;本轮仅解决已选父级的续接。嵌套交互证明 WPF 局部诊断及调用方后续独立定位,New-tavern 证明实际单层读取;不等于非 fixture 嵌套/多候选或非 WPF 导航已验收。已有 Electron 只读识别记录保持原范围,没有为本轮人为制造展开目标。 - -原始测试与验收记录留在 Git 忽略的 `test-tmp/hybrid-fifteenth-checks/` 和两次 CLI 附件目录;公开只记录方法、失败、状态与数量。没有将原始窗口树或截图提交,也未读取凭据配置或请求外部模型。 - -收尾检查:六份修改 MD 的 51 个相对链接、18 个锚点、代码围栏与折叠标签配对通过;十个公开变更文件新增行的本机绝对路径、邮箱及常见凭据形状为 0。本轮已生成的 40 个本地 JSON/日志文件常见凭据形状为 0。该检查不是已知密钥精确比对,不保证所有未知秘密均已排除;本地附件仍含窗口和进程信息,不作为匿名公开文件。安装 Skill 最终 check 为 matched=true。git diff --check 通过,提交范围仅这十个文件,原始记录及无关未跟踪文件不提交。 - -## 第十六轮父路径内查询与展开(2026-10-07) - -本轮用户授权先实现父范围定位这一部分,沿用实验分支。实现前本地 HEAD 与远端实验分支均为 `1be61ef32e1b93699686cafb701447384af67431`,远端 main 仍为 `038c0ccc7372948da405546cc7ec0add56eea9de`。不合 main,不改默认 MCP 配置,保留无关未跟踪文件。 - -### 实现和依据 - -标准 inspect/review/setExpanded 增加可选 scopePath,每项为精确父选择器;每次原生请求从窗口重新解析,逐项在上一范围的严格后代中确认完整唯一,最后查询/动作留在最后父级。可跳过布局包装,不匹配当前根来充当下一层。无路径请求沿用原流程。最终 query 搜索包含最后父级及其后代;不带 query 时返回最后父级树,截图仍是整窗取证。 - -复用已有 BoundedUiSearch,并加入可选 includeRoot;独立 UiScopeResolver 只负责同请求路径解析,没有递归引擎、跨请求 COM 缓存或新依赖。整个父路径及最终目标共用原有 2 秒搜索时限,整体仍受请求 deadline/取消约束。父路径失败保留 scopeResult 的解析数量、从 0 开始的失败位置、搜索状态和原因,返回 SCOPE_NOT_FOUND/SCOPE_AMBIGUOUS/SCOPE_SEARCH_INCOMPLETE,不退回整窗操作。 - -inspectionVersion 升至 5。旧 Host 可能认识 setExpanded 却忽略父范围,所以带路径动作在现有互斥及 deadline 内先执行只读 health,低于 5 时动作不发送;检查没有新增权限状态机或累计预算。compact expansionRequests 保留原路径。MCP schema、共享 TypeScript 契约、原生 DTO/读取/动作及 Skill 同步;click/type 与客户端 expand-ui 参数未扩展。 - -设计参考 [Microsoft AutomationId 的作用范围](https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/use-the-automationid-property)和当前依赖 [FlaUI 5.0.0 的嵌套定位源码](https://github.com/FlaUI/FlaUI/blob/v5.0.0/src/FlaUI.Core/AutomationElements/AutomationElement.Find.cs)。采用相对父范围逐层定位的思路,保留本项目完整唯一搜索,不复制 FindFirst 的首项选择。 - -### 实验方法、失败和验证 - -新增自动测试共 2 项:核心契约检查字段透传、非法路径、compact 后续查询保留路径、旧 Host 只收到 health 而未收到动作;真实 WPF 场景检查两个并列区域中的同名同 ID、缺失/歧义/不前进路径无操作、指定侧展开、另一侧保持折叠、两层路径读取和重复展开 no-op。夹具只在显式 --scope-navigation 模式增加该场景,两个区域的 Normalize 状态分别固定为 On/Off,不读取实际业务配置。 - -契约测试先失败,实测收到的范围为 undefined,再补共享字段/schema 后通过。首次类型检查发现 scope schema 的 as const 生成 readonly 数组,与 SDK schema 类型不兼容,改为普通对象后通过;首次夹具编译发现局部 label 与已有变量重名,改为 regionLabel 后通过。这两项均为构建问题,不当成功验收记录。 - -首次相关桌面运行为 2/3,新双区域测试缺少 tree。随后通过当前 Codex CLI 读取完整结果,发现 inspectionVersion 为 5 但 queryResult 仍在整窗匹配到两个高级控件且无 scopeResult;根因是 FlaUiAdapter 的原生 JSON payload 字段白名单漏传 scopePath。补实际字段后相关桌面 3/3 通过。契约层 mock 路由曾通过而真实链路失败,说明真实窗口与客户端入口验证不可省略。本轮没有因失败扩大到无关重构。 - -相关核心 39/39;完整核心仅一次 492/492;相关桌面最终单次 3/3。最终 typecheck、TypeScript build、原生 Host Release publish 及 WPF fixture publish 通过。项目继续使用已有 SDK 10.0.303,不安装环境,不改 global.json。 - -### 当前 Codex 客户端实际验收 - -最终 PTY CLI 连接当前实验构建,hello.runtime.build.status=verified,buildId 为 `edcec0497b822d8cf11dae3ddff8a2bdd2ab2e3b83b88b1c4ffd6787c6b8b4f8`。revision 为实现前 `1be61ef`;当前未提交源码由 sourceHash `dfadc9341966fdacb8c8196d0a07c0c1262b744453b0d62054df1cfda96934d8` 与 artifactHash `5938792991812de3a9403e93cf6b2fff1720240379a1a3bb86b0fbbfcca2272c` 识别,不将 revision 误作全部实现身份。 - -在独立实际 WPF 窗口顺序完成:语音父路径内唯一观察 Advanced=Collapsed;在同父路径下 Expand 一次;语音 → Advanced 两层路径内读取 Normalize=On;显示父路径内读回 Advanced=Collapsed。每份完整 MCP 结果已读取,isError=false,query.searchComplete=true、treeComplete=true,无遍历或属性缺口,Host inspectionVersion=5。动作回执之外使用独立读回来确认真实结果。 - -最终入口显式 close 返回 closed=true、进程 exit 0;诊断用的上一入口也已关闭。独立 fixture 自动退出后核对本轮两个入口、Gateway、launcher 和 fixture 等已观察自有进程无残留,UIA Helper 存活数为 0。没有关闭用户自有应用。本轮没有重新验收 New-tavern 或 Electron 的 scopePath;此前只读/单层记录不能代替该能力的非 fixture 或非 WPF 验收。 - -### 文档、Skill 与隐私 - -更新三份现有 hybrid MD:当前入口和状态改为第十六轮,保留第十五轮原方案与历史结果;移除“父范围未实现”的当前结论,同时保留自动生成父路径/递归执行、非 fixture 同名路径和非 WPF 导航未验证的限制。不新增重复规划文件。仓库与已安装 Skill 的 root/UI/诊断三份文档同步,旧安装内容先备份,4 个受管文件最终 check matched=true,无关文件及 MCP 配置保留。 - -原始记录仅在忽略目录 `test-tmp/hybrid-sixteenth-checks/` 及 `test-tmp/skill-sessions/run-Gc6vlr/`、`test-tmp/skill-sessions/run-IXZ3Oj/`。公开只记录方法、失败、合成夹具标识、结果及数量;不提交原始窗口树、截图、进程身份或本机路径。未读取凭据配置、输入真实个人数据或请求外部模型 API。提交前检查公开新增行及这些本地记录的常见凭据形状;这不是已知密钥精确比对,也不承诺排除任意未知秘密,本地附件不作为匿名公开资料。 - -收尾检查:19 个公开变更文件新增行的本机绝对路径、邮箱及常见凭据形状均为 0;22 个本地 JSON/日志文件常见凭据形状为 0。6 份 MD 的 58 个相对链接、21 个锚点、代码围栏及折叠标签配对通过,git diff --check 通过。原始记录和无关未跟踪文件不进入提交。 - -## 第十七轮:客户端父范围与 main 合并收尾(2026-10-07) - -本轮按计划提交 `3d3d019` 的 M1–M6 执行,产品版本统一为 0.17.0。expand-ui 接受显式 scopePath;初始子查询、有限发现/候选重定位、动作和最终读回保留范围,nextRequest 保留调用方祖先路径。父级明确后最终子控件限定在该父级内,不回退整窗。父查询与路径末项三个定位字段完全相同时复用范围根;否则追加父选择器,总长最多 50。单请求仍最多展开一次,未知结果不重放。 - -### 首次失败与修复 - -首轮完整核心检查成功:493 项中 492 通过、0 失败、1 项原有可选 Tavern 固定夹具跳过;报告 `test-tmp/check/2026-10-07T12-44-41-128Z-core/report.json`。首轮完整桌面检查 55 项中 52 通过、3 失败;报告 `test-tmp/check/2026-10-07T12-46-46-565Z-desktop/report.json`,后续阶段当次未执行。失败分别是已展开候选可见读取从 1 调用变成 4 调用、内部父路径歧义改成 SCOPE_AMBIGUOUS 破坏旧 QUERY_AMBIGUOUS 分类、源码修复夹具漏复制新增 partial 文件而报 CS0103。 - -修复保持可见候选的单次读取;调用方祖先范围失败保留 SCOPE_*,内部追加父级失败沿用 QUERY_*/候选失效分类,steps 保留原始 scopeResult;既有源码夹具复制列表补 HybridFixture.cs。失败生成目录内独立重现漏文件的 CS0103,补同一源码后 publish 成功。三项针对性复验 3/3 通过,没有放宽断言或加长等待。 - -实际隔离 Tavern 又揭示范围根边界:父级名称/类型与 scopePath 末项相同,原代码仍追加同一严格后代,合法请求在 failedIndex=1 停止,未执行动作。核心回归先固定该失败,再复用相同末项,一次修复后核心场景及同一真实窗口通过。泛化 Pane 祖先另返回 SCOPE_AMBIGUOUS,保留失败,未改成全窗回退。 - -实现参考了 [Microsoft UIA 查询范围说明](https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-obtainingelements)、[FindFirst 的树范围契约](https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automationelement.findfirst?view=windowsdesktop-10.0)、[SDK 默认 Compile 项](https://learn.microsoft.com/dotnet/core/tools/csproj)和 [partial 类型编译规则](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods);最终判断来自本地复现和复验,未修改环境或安装依赖。 - -### 真实入口与隔离应用 - -新增 2 项自动测试:一项核心覆盖范围传递、根范围复用、非法输入、失败分类和 nextRequest;一项实际 JSON CLI 双区域 WPF 场景,两个区域有重复父名/ID 和子 ID。语音侧一次展开后 Normalize 为 On,显示侧仍 Collapsed;显示侧再独立展开为 Off,语音续接零动作;全窗同名查询失败,缺失祖先无动作。报告 `test-tmp/hybrid-merge-checks/scoped-session.json`。 - -真实 Tavern 使用现有 EXE 的隔离 test-root/test-reuse 入口和合成初始资料,导航到语音页;没有改源码、重建应用、调用 Provider 或保存业务值。实际 Codex PTY 中新建 CLI,hello 为 0.17.0、build verified,UIA Host 为 inspectionVersion 5。一条已观察 Group 路径兼作父范围:折叠时 5 次调用/1 次 Expand,父级 Expanded、目标 On;独立 inspect 再次读到 On;明确父级续接 4 次调用/0 动作,可见目标读取 1 次调用/0 动作。18 次 UIA helper 启动全部退出,两个 CLI/Gateway 会话及自有 Tavern 窗口退出,Tavern 原有 Git 状态一致。完整原始结果和身份仅保存在 `test-tmp/hybrid-merge-checks/tavern-report.json` 及两个 `test-tmp/skill-sessions/` 会话目录。 - -### 验证和范围 - -最终完整回归、受管 Skill 同步、交付清单、公开文档检查和 PR 精确 head CI 在本节后续追加实际结果。首轮失败不覆盖。对 main 的完整增量按 CONTRIBUTING 做作者自查;这不代表独立审查。模型脚本只通过显式 benchmark 命令运行,本轮未请求外部模型或启动性能实验。 - -不新增自动递归、多层路径生成、click/type 范围、其他控件导航或非 WPF 支持。单窗口真实应用证据不代表所有程序不会激活窗口,不证明稳定 token/延迟收益,也不代表当前已加载客户端或默认配置自动更新。原始窗口树、进程身份和本机路径保持在忽略目录,不作为匿名附件。 - -第十七轮最终本地收尾:`npm run check` 18 阶段全部通过,493 项中 492 通过、0 失败、1 原有可选固定 Tavern 夹具跳过;报告 `test-tmp/check/2026-10-07T13-03-35-479Z-core/report.json`。`npm run check:desktop` 7 阶段全部通过,55/55、0 跳过,实际 UIA owner-death、原生 Tray 和双客户端真实 Tray 工作流均成功;报告 `test-tmp/check/2026-10-07T13-05-25-034Z-desktop/report.json`。这是首轮失败后的唯一完整最终复跑,没有再追加 test:all。Gateway buildId=`7c633218b3e7396bb04de03a3f1f80d528a52a5e7fb8f6410b9b58a3753b9872`;delivery contentId=`46291cc1cfe8873e8587c969b3531df6ba57ce3b4cb2b1ef2485ab001ac2e03a`、matched=true。构建 revision 仍为计划提交,未提交的实现由 sourceHash/artifactHash 明确识别,不能只用 revision 宣称精确源码身份。 - -三个既有通用 Skill 安装位置通过受管脚本先备份再同步,各 4 文件最终 matched=true;独立 adapter 和 MCP 配置未改。11 份 MD 的 170 个仓库相对链接、29 个锚点及代码围栏检查通过;243 个历史忽略目录附件链接保留为本地历史记录,不宣称可公开重取。版本字段与 git diff --check 通过。完整 main 增量作者自查覆盖客户端预算/取消/身份、范围校验/逐次解析、旧 Host 动作前能力门、审计/所属进程清理及模型脚本显式入口;未发现未解决的合并阻塞,未进行独立审查。 - -2026-10-07 重新读回 main 保护:严格所需 Node 22/24 Windows regression 和三项 CodeQL(javascript-typescript、csharp、actions),管理员受约束,所需人工审批数为 0。合并必须满足 [PR #52](https://github.com/linnnn89/WinCode/pull/52) 精确 head 的这些检查;成功的本地回归不替代 CI、源码合并不代表 GitHub Release 或默认客户端刷新。 diff --git a/docs/hybrid-next-iteration-plan.md b/docs/hybrid-next-iteration-plan.md deleted file mode 100644 index 001d208..0000000 --- a/docs/hybrid-next-iteration-plan.md +++ /dev/null @@ -1,617 +0,0 @@ -# Hybrid 后续迭代规划与接手入口 - -更新:2026-10-07。**第十七轮客户端父范围接入及真实应用验收已完成;本地核心/桌面回归、交付与作者自查已通过;交付 [PR #52](https://github.com/linnnn89/WinCode/pull/52),远端所需 CI 与合并状态以 PR 实时记录为准,合并须满足下方 M1–M6。** 原规划基线为实验分支 `5989c6b`、main `038c0ccc`;计划提交为 `3d3d019`。标准 inspect/review/setExpanded 和客户端 expand-ui 均已接入 scopePath。第十七轮实际方法、首次失败、修复与限制见[验证记录](hybrid-experiment-findings.md#第十七轮客户端父范围与-main-合并收尾2026-10-07)。历史轮次保留,不代表当前待办。 - -阅读顺序:[main 合并验收线](#main-合并验收线) → [后续执行顺序](#后续执行顺序) → [当前状态](#当前状态与必须遵守的边界) → [剩余能力](#当前未实现与待验证事项)。第十七轮及更早记录保留为实现和历史证据,不作为新的执行清单。 - -## main 合并验收线 - -**本次合并交付“显式窗口与父范围内的有限导航和只读结果”。** 调用方根据实际观察指定 PID/HWND、父选择器路径和目标;客户端能在该范围内定位、必要时展开一个明确父级、读回其子控件状态。已经展开时不重复动作;歧义、缺失、必要证据不足、取消或动作结果未知时保留事实并停止。本次保留已有只读工作流和 checkbox-audit,仍通过显式客户端调用启用。 - -expand-ui 已补齐父范围传递和最终子控件定位,并完成对应真实入口与实际应用验证;完整回归、交付身份和 PR 检查仍按下表核实。**单请求最多一次展开,不自动递归第二层;后续请求可依据新观察继续已授权任务。** 展开属于会改变界面状态的导航动作,只读统计和显式展开在文档中分别说明,不承诺所有应用均不激活窗口。 - -| 合并条件 | 可核对的通过标准 | 2026-10-07 当前验收状态 | -|---|---|---| -| M1:父范围贯穿客户端 | expand-ui 接受可选 scopePath;首次观察、候选发现/重定位、展开、读回和可执行 nextRequest 保留正确范围。父级确认后的最终子控件读取限定在该父级下,同名旁支不被读取或操作;范围失败不回退整窗 | 实现及核心针对性验证通过;范围根与父选择器相同则复用,不追加重复严格后代。真实双区域 JSON 会话通过 | -| M2:兼容性与失败行为 | 无路径旧请求、readonly-ui、checkbox-audit、标准 click/type 保持原有行为;重复展开为零额外动作;缺失、多义、截断、旧 Host、取消和未知动作结果不产生错误成功或自动重放 | 无路径兼容场景、候选可见时单次读取及原失败分类复验通过;本轮只新增 2 项自动测试,首轮桌面失败与修复保留 | -| M3:真实使用闭环 | 当前客户端通过 JSON 会话入口在双区域同名/同 ID 夹具完成路径隔离;另在一个真实应用的隔离测试实例完成带父路径的目标读取、已展开续接和一次合法展开后的独立读回。检查结果、所属进程退出和目标应用状态 | 双区域夹具和隔离 Tavern 均通过;实际展开 5 调用/1 动作、已展开续接 4 调用/0 动作、可见读取 1 调用/0 动作,独立读回 On,所属进程退出 | -| M4:完整回归与 CI | 最终实现通过 npm run check、npm run check:desktop;精确 PR head 的 Node 22/24 和 CodeQL 所需检查成功。任何跳过项说明范围,不能跳过 M1–M3 的必要场景;新增失败查明并处理 | 本地最终核心 493 项:492 通过、0 失败、1 原有跳过;桌面 55/55 及所属进程死亡/托盘/真实托盘工作流均通过。远端 Node 22/24 与三项 CodeQL 由 [PR #52](https://github.com/linnnn89/WinCode/pull/52) 的精确 head 检查和 main 保护强制要求,合并前必须全部成功 | -| M5:交付身份一致 | 当前 Gateway、UIA/Code Host/Tray、受管 Skill 与配置通过 npm run delivery:verify;实际客户端 hello 及 Host 能力与这份产物一致,scopePath 使用 inspectionVersion 5 或兼容新版本 | 0.17.0 Gateway 与 Host 5 的实际 CLI 身份 verified;delivery:verify matched=true;三个既有通用安装位置各 4 个受管文件哈希一致,旧内容备份。未刷新默认客户端连接 | -| M6:文档与审查完成 | README、CHANGELOG、现有计划/架构说明和 Skill 对范围、字段、前台影响、使用入口一致;相对链接及示例参数有效;对 main 的完整增量经过审查,无未解决的阻塞问题,历史失败和未验范围保留 | 公开入口/版本/范围及根计划已同步;完整 main 增量作者自查完成,无未解决阻塞发现;11 份 MD 的 170 个仓库相对链接/29 个锚点有效,历史 test-tmp 附件链接单列不算公开交付,交付 [PR #52](https://github.com/linnnn89/WinCode/pull/52),远端检查及合并回执在该 PR 核对 | - -M1–M6 全部满足后,才标记“可合并 main”。合并沿用仓库 PR 和保护规则,不强推、不绕过所需检查;原规划请求只写文档;后续用户要求执行到当前计划完成,按上述验收线推进,保留真实检查状态。PR 应以完整用户行为描述 21 个既有提交及后续收口,不能只描述最后一轮 scopePath 改动。作者自查与独立审查分别记录,不以多一份审批或新增流程代替问题修复。 - -不设任意日期或测试数量作为完成线,也不要求证明所有应用、模型和 Windows 环境均可用。必要场景因环境缺失未执行时保留待验;已通过的无关检查不反复重跑。若后续生产代码改变,复核受影响证据;仅更新 docs 不必重复桌面验收,但最终 PR head 仍需满足所需 CI。 - -## 后续执行顺序 - -### 工作包一:接齐 expand-ui 的父范围 - -范围集中在 [ExpandUiWorkflow.ts](../src/Client/ExpandUiWorkflow.ts)、既有 [SkillSession](../src/Client/SkillSession.ts)/[CLI](../src/Client/SkillSessionCli.ts) 契约及对应 Skill 用法。优先复用 [UiContracts.ts](../src/Core/UiContracts.ts) 和标准工具已实现的路径校验/解析;不新增导航服务、跨请求令牌、缓存或依赖。 - -1. 明确 scopePath 表示本请求已观察到的祖先范围;parentQuery/candidateQuery 相对于该范围定位。未给父级时,有限候选发现也只在该范围内进行。路径本身不负责展开隐藏的祖先。 -2. 已给父级时,避免初始子查询因为旁支同名而阻断合法父范围定位,或直接把旁支同名状态当成目标结果。父级实际确认后,最终子查询使用包含该父级的路径;父选择器与路径末项三个定位字段完全一致时复用该范围根,避免重复严格后代。实际路径仍无法完整唯一解析时停止。无路径旧行为保持兼容。 -3. nextRequest 保留 target、childQuery、实际父范围和已有预算;返回的局部节点不能直接冒充可跨请求使用的唯一操作身份。每个请求仍重新定位并使用现有 deadline/取消/输出限制,未知结果不重放。无新增调用时不扩大现有调用上限。 - -本工作包最多新增 3 项自动化测试,优先扩展既有测试数据和断言: - -- 核心范围/兼容场景:覆盖有效与非法路径、初始读取、候选续接、最终子查询和 nextRequest,确认无路径有效请求保持原语义。 -- 真实 WPF 双区域会话:相同父名/ID 和子 ID,在指定侧完成读取与一次展开;另一侧状态保持不变。已展开路径零额外动作,嵌套目标的最终读取不回到全窗。 -- 独立失败场景:按受影响缺口选择范围失效/歧义、旧 Host 或取消,核实动作次数、结果真实性和清理;已有测试充分覆盖时不新增。固定期望来自夹具 On/Off 和未操作侧状态,不能从生产算法反推。 - -先运行相关测试及 npm run typecheck,再构建并走当前客户端实际入口。已有主回归和桌面测试留到工作包三集中执行;不通过 mock 透传就宣布实际范围隔离成功。涉及同一问题的修改—验证连续失败三次时停止修改,重新核对证据。 - -### 工作包二:一次真实应用验收 - -优先使用 New-tavern 现有隔离测试入口、合成配置和语音页。先只读确认实际父路径、目标身份及 UIA 能力;场景内完成目标可见时的读取、父级已展开时的零额外展开,以及折叠状态下的一次展开和独立状态读回。动作沿用用户已授权的任务范围;若只授权后台操作,就不能为通过验收擅自聚焦、键盘输入或恢复窗口。无合适隔离实例时保持 M3 未完成,不接触个人资料库凑结果。 - -记录实际 CLI/Gateway/Host 身份、使用的请求、完整 MCP 结果、动作次数、最终状态及自有进程退出;原始窗口数据留在忽略目录,只把脱敏方法、结果和失败追加到 [问题记录](hybrid-experiment-findings.md)。已有非 fixture 单复选框统计证据可以保留,不为同一行为重复跑模型。并列同名控件的隔离用固定 WPF 场景验证,真实应用不具备该布局时不伪造“真实应用多候选已通过”。 - -验收中若实际发生越界读取/操作、重复展开、把未观察状态当成功或清理残留,属于合并阻塞;先定位并修复。仅缺少非 WPF 可展开控件或未做长时性能测量,按后续范围记录。 - -### 工作包三:交付收尾与 PR - -1. 在稳定源码上通过既有构建/检查入口生成分支产物。先核实所需 SDK 和依赖;缺失时遵守仓库安装规则,不自动更换环境。运行一次 npm run check 和一次 npm run check:desktop,二者分别覆盖核心与桌面阶段;若本次改动引发失败,修复后仅补相关阶段或一次最终复跑,不同时追加一遍 npm test 或 test:all。保留首轮失败。 -2. 对 M1–M3 核对实际证据,执行 npm run delivery:verify。清单过期先比较具体差异;源码/原生产物过期按既有构建路径处理,不能只重写清单接受旧 DLL。受管 Skill 最后一次修改后再刷新并核验清单;安装副本同步、实际客户端刷新与仓库构建分别记录。 -3. 更新现有文档和 CHANGELOG:说明客户端入口、显式展开及范围限制;修正根目录旧计划“UI 写操作暂不开发”和过时架构基线;完成的历史 PR 状态以新记录澄清,不抹掉原失败。公开内容排除窗口树、个人路径、模型凭据及本地附件。 -4. 审查完整 main...分支差异,重点检查范围是否在每层保留、旧 Host 是否可能忽略范围、取消后是否重复动作、无关默认行为是否改变,以及实验/模型脚本是否只有显式调用才运行。按 CONTRIBUTING 记录问题处理及审查来源;不开新架构、性能或模型实验支线。 -5. 实施阶段获得提交/PR 授权后,只提交相关源码、测试和文档;创建面向 main 的 PR,等待精确 head 的 Node 22/24 与 CodeQL。全部 M 条件满足并获得合并授权后合并,随后核对远端 main SHA、合并后检查与本地快进状态。若合并授权已在该实施会话给出,沿用授权,不重复询问。GitHub Release 和默认客户端部署单独执行,不由源码合并自动代表完成。 - -### 合并后的候选工作 - -以下不阻塞本次合并,只有实际需求或新失败证据出现时再选一个有界任务: - -- 自动多层导航、祖先路径生成、邻近标签/局部图像辅助定位,以及 scopePath 扩展到 click/type。 -- Tab/MenuItem/TreeItem、滚动、虚拟化和任意文本/数值结果;非 WPF 导航和非 fixture 多候选/条件摘要/多控件迁移。 -- 更多模型、自然发生的参数纠错率、稳定性能和专门前台采样。历史小样本不能支持普遍 token/延迟收益,新外部模型实验需另定范围和预算。 -- readonly 工作流的整树/源码证据门槛及首次发现受无关节点缺口影响的问题,按具体误拒绝复现再改;不预建累计配额或跨请求授权状态机。 -- main 原有的 Roslyn 生成源码覆盖、Codex 冷启动观察、UI/托盘间歇性异常、SDK 突发警告和广泛环境长测继续在[原测试计划](../WinCode-下一轮工程化迭代计划书.md)跟踪。本分支引入的新回归仍必须在合并前解决。 - -达到 M1–M6 后停止扩功能并进入合并流程。新范围需另立具体任务,不能因为剩余能力表仍有条目而无限延长实验分支。 - -## 当前状态与必须遵守的边界 - -- 工作分支:`codex/hybrid-readonly-orchestration`。本次规划核对基线:`5989c6b`。接手先核对当前 Git 根、分支、状态和本地/远端 SHA,使用当前项目,不切换到同名副本。 -- 本次规划时 main 为 `038c0ccc7372948da405546cc7ec0add56eea9de`。后续开发继续在实验分支,达到上方验收线后按 PR 流程进入 main;本次文档更新不执行合并、不强推。若工作区存在无关未跟踪文件 `docs/wincode-ui-action-plan.md`,不提交或改动它。 -- 已实现:客户端只读工作流、`checkbox-audit`、JSON 会话入口、参数拒绝契约、有界参数/格式更正、证据守卫及实验报告。**当前 Codex 通过分支 CLI 完成 fixture 的 6 项功能任务;New-tavern 语音页的两个单复选框范围原生/混合一致。非 fixture 条件摘要、多控件区域及稳定性能仍未验证。** -- 第九轮 30 个独立小样本:原生首次 3/15、最终 15/15;混合首次/最终 15/15。另有 6 个试运行实际 4/6,失败保留。18 个混合尝试都真实调用工具,没有再次打印参数冒充结果。 -- 第十五轮完整核心回归 491/491(运行一次)、相关核心 13/13、新增真实 WPF 场景 1/1、既有六项桌面场景最终分别通过,类型检查/构建通过;实际客户端两个目标的 20 次 helper 启动均检查退出。首次失败及详细方法见第十五轮记录。项目 SDK 使用 `scripts/lib/dotnet.mjs` 选择已安装的 10.0.303,不改 `global.json`,不新增安装。 -- 保留“没有观察不能成功”、实际目标身份、目标唯一性、任务所需状态/完整性校验及未知结果不自动重放。辅助证据缺失是否阻断任务,按下方设计复核判断,不把全部字段完整当作所有任务的共同前置条件。打印参数不触发执行;第十一轮只增加已批准的明确展开/折叠导航,不新增业务输入/保存、服务器 batch、任意 discovery、runtime、服务或依赖。 -- 凭据不写入参数、提示、文档、提交或输出。原始实验记录保留在 Git 忽略的 `test-tmp/`,可能含本机路径和窗口内容,不能当匿名附件。第九轮审计 80 个文件、实际 API Key 及常见编码匹配为 0;该结论不覆盖后来新增文件。 - -## 第十六轮:父路径内查询与展开 - -已实现可选 scopePath:由实际父选择器组成,逐项在上一范围内有界搜索严格后代,每一步必须完整且唯一。最终 query/setExpanded 限定在最后父级内,不带路径的既有请求沿用原流程。inspect 不带 query 时返回该父级的树;最终 query 可以匹配该根及其后代。布局 Pane 无需逐项写入,路径不会自动展开未显示的父级。 - -路径缺失/多义/不完整分别返回 SCOPE_NOT_FOUND/SCOPE_AMBIGUOUS/SCOPE_SEARCH_INCOMPLETE;scopeResult 保留 resolvedCount、从 0 开始的 failedIndex、status、visitedNodes 和 reason。没有退回整窗搜索。每次请求重新定位,无跨请求 COM 缓存、令牌或新增导航服务。父路径及最终查询共用已有 2 秒搜索时限,整体沿用请求 deadline/取消,未加入累计预算框架。此节保留第十六轮历史状态;当前客户端接入见第十七轮记录。 - -需要 inspectionVersion 5;带路径的展开先只读检查当前 Host 能力,旧版未收到动作就返回 VERSION_MISMATCH。compact 的后续查询保留 scopePath。新功能当前只暴露在标准 inspect/review/setExpanded;click/type 与 expand-ui 参数未扩展,自动路径生成和递归执行仍未实现。 - -验证:新增 2 项自动测试(契约 1、真实 WPF 场景 1);相关核心 39/39,完整核心一次 492/492,相关桌面场景 3/3。当前 Codex PTY CLI 的实际双区域路径验证通过,最终构建与原生 Host 5 已核对,自有进程退出。两个并列高级控件同名同 ID,展开语音侧后显示侧仍 Collapsed,两层路径读取 Normalize 为 On。本轮未重新验收 New-tavern 或非 WPF 的 scopePath,也未运行模型 API 或性能比较。 - -主要实现:[UiScopeResolver.cs](../tools/WinCode.UIA.Host/UiScopeResolver.cs)、[共享契约](../src/Core/UiContracts.ts)、[适配器](../src/Adapters/FlaUiAdapter.ts)、[MCP 字段](../src/Gateway/UiTools.ts)。用法维护在 [Skill UI 手册](../skills/wincode/references/ui.md#操作方式与授权),失败和脱敏方法维护在问题记录,不新增重复的规划文档。 - -## 第十五轮计划:已展开状态续接与局部诊断 - -状态:**已实现并完成实际入口验收**。本轮让 AI 在正常页面变化后继续理解任务进度,避免把“父级已经展开”和“仍需查看下一层”一概报成选择失效。最终读取复选框真实状态仍是任务完成标准;动作成功与任务完成分别报告。 - -| 本轮交付 | 实际行为与证据 | -|---|---| -| 所选候选直接续接 | candidateQuery 直接完整唯一定位 Group,不重新普查无关 Group;Expanded 零展开,Collapsed 才操作一次。实际禁用/启用未知/搜索不完整分别分类 | -| 局部诊断 | 复用已返回父树,保留范围、结构完整性、属性/遍历缺口、实际目标匹配和内层 Collapsed Group;不把局部节点变成全局动作请求 | -| 实际嵌套客户端 | 外层 Expanded、内层 Collapsed:3 次只读调用返回内层候选;调用方依据真实候选独立定位内层,5 次调用/1 次展开后读到 On;外层仍 Expanded | -| 实际 New-tavern | 自行启动项目现有 EXE,导航到语音页并展开高级区域;目标可见时 1 次调用读到 On,明确父级归属路径 4 次调用、零额外展开,同样读到 On | -| Skill 交付 | 更新仓库 SKILL.md/UI/诊断手册,并通过既有同步脚本备份后更新安装副本;4 个受管文件逐字节一致。没有修改 MCP 配置或切换 main | - -
-第十五轮实施前原方案与设计依据(已执行,非待办) - -### 从实际任务推导设计 - -用户要的是“读出这个控件的状态”,展开只是到达目标的手段。因此应优先读取已经可见的目标;目标缺失时,反馈必须说明这次看到了什么、还缺什么、哪种下一步有依据。目标未出现本身不能证明父级折叠,也不能证明操作失败。 - -必要证据随结论变化:执行展开需要当前唯一目标、启用证据及展开能力;确认目标属于父级需要实际结构证据;读取状态需要目标的实际状态。证明“没有其他匹配”需要完整搜索,而返回“已经观察到这些候选”可以保留局部结果并说明未完整。不能因辅助属性缺口丢掉已知事实,也不能拿局部结果宣称全局唯一。本轮只调整下表明确的路径,不将该原则扩成全项目证据策略重构。 - -| 当前证据 | 对用户体验的判断 | 下一轮处理 | -|---|---|---| -| `discoverParent` 先筛 Collapsed,再核对 candidateQuery | 所选区域只是正常变为 Expanded,目标仍隐藏时却收到 NAVIGATION_SELECTION_STALE;源码路径可确认,尚无独立实际窗口复现 | 按选择器重新核对该 Group 的当前身份和状态;同一有效区域 Expanded 则零展开动作,继续局部观察 | -| 展开后完整父树没有唯一目标,一律 CHILD_RELATIONSHIP_UNCONFIRMED;catch 又覆盖具体 nextAction | AI 无法直接区分“区域已经打开、内层仍折叠”“局部无目标”和“同名目标不唯一” | 保留动作后观察;用已有父树给出对应诊断和观察建议,不立即重放动作 | -| Electron 实际整窗 15 节点/12 层,Document 5 节点/4 层 | 深度主要来自外壳;增大所有任务的深度或按框架分叉都不是直接解决办法 | 优先已有 query 的局部读取;明确区分选择根节点后读取子树与真正的父范围内搜索 | -| UiQuery 只有精确名称/ID/类型和搜索数量,无父范围;节点 id 仅本次观察有效 | 看见某个内层节点,不等于下次可以按同名选择器在整个窗口唯一操作 | 本轮返回局部候选证据,不伪造祖先选择器、跨请求节点身份或“必然可执行”的内层 nextRequest | - -这里有一个标准语义依据:ExpandCollapseState 描述直接子对象的显示状态,不保证所有后代可见,所以外层 Expanded 与内层仍折叠可以同时成立。[Microsoft 定义](https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.expandcollapsestate?view=windowsdesktop-10.0)。原生 UIA 搜索可相对于指定元素限定范围,但 WinCode 当前尚未暴露相应父范围查询契约;不能把平台能力写成产品已实现功能。[Microsoft FindFirst](https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.automationelement.findfirst?view=windowsdesktop-10.0)。 - -### 一个实现包,两个相连的改动 - -**A. 让选择续接接受有效的 Expanded 状态。** 自动首次发现仍展示可展开的 Collapsed 候选;对调用方已经选择的 candidateQuery,不再用“仍然折叠”代替身份有效性。重新核对它是否唯一、启用、具备已知展开状态:Collapsed 走现有展开一次路径,Expanded 直接观察父区域,目标已经可见则沿用现有直接读取。消失、重复或身份不匹配才是选择失效;确实禁用、必要证据未知、模式不支持分别沿用已有准确错误,不统一伪装成 stale。此处不新增用户确认,不把所有 Expanded Group 自动当作新候选。 - -**B. 将动作后的局部观察整理成可用反馈。** 复用当前已经取得的父树、children/parentId 和状态,优先在客户端完成,不额外扫描整个窗口。建议在现有 diagnosis 上少量增补观察范围、完整性及实际内层候选;具体字段在实施时按现有消费者确定,不预建状态机或通用导航接口。 - -| 动作后实际观察 | 应返回的事实和下一步 | 本次请求中的动作 | -|---|---|---| -| 父级 Expanded,完整范围内唯一目标存在 | 验证归属,独立读取实际 toggle;只有完成这些步骤才 completed | 不重复展开 | -| 父级 Expanded,未见目标,但实际发现内层 Collapsed Group | 列出名称/ID、类型、展开状态、启用情况及本次观察中的父子关系;说明是候选,尚不能证明目标位于其中 | 不自动展开第二层;AI 可依据这些证据安排后续定位 | -| 父级 Expanded,完整范围没有目标,也没有可用内层候选 | 明确“该区域未见匹配”,原因仍未知;建议检查目标查询或页面范围 | 不猜测加载中,不增加固定 sleep 或自动轮询 | -| 父级 Expanded,目标在父树中多次匹配 | 告知具体歧义,提供已有匹配信息供修正查询 | 不选第一个、不扩大动作范围 | -| 树被截断或遍历失败 | 保留已确认的父级状态与已观察节点,说明局部搜索未完成;建议缩小区域,必要时对明确截断调整一次读取参数 | 局部候选不能冒充唯一候选;不重放展开 | -| 动作调用失败/超时,尚未观察到最终状态 | 保留实际错误和动作结果未知;下一步先读取当前状态 | 不因未取得回执而自动重放 | - -状态兼容:保留现有 completed/selection-required/stopped 和 success 含义;任务尚未完成仍不产生 findings。CHILD_RELATIONSHIP_UNCONFIRMED 可保留作为未确认归属的错误类别,在 diagnosis 中区分实际原因,避免为每种提示新造一个错误码。保留完整 steps;不能让统一 catch 文案覆盖已经得到的针对性诊断。已确认父级 Expanded、但任务未完成,不应被描述成“展开结果未知”。 - -本轮的完成体验是:AI 能说明“选定区域已经打开,因此没有再次操作;当前观察到这些内层候选/这个具体缺口”,并得到能用于下一次观察的依据。**本轮不宣称已经能自动穿过多层菜单读到最终目标。** 在当前契约尚无父范围定位时,递归执行会把局部关系丢回全窗查询,尤其容易误处理重复名称;应先消除这一真实接口缺口,再按实际案例决定是否补一条短的多层路径。 - -### 通用性与复杂度取舍 - -- 依据 UIA 能力、当前状态和观察关系处理,不检查 WPF/Electron 名称、不硬编码“高级”或具体应用 AutomationId。当前自动发现仍限 Group、结果仍限 toggle;本轮不把这项配方包装成通用菜单引擎。 -- 复用 ExpandUiWorkflow、现有 CLI/会话和原生 setExpanded;不新增服务、依赖、跨请求令牌、累计预算、内部评分模型或通用工作流框架。现有超时/取消/数据上限继续工作;只有实际新增调用时才调整对应局部调用上限。 -- 不要求所有 Group 的状态都完整才能认定一个已选父级有效:选择续接以被选目标的完整唯一搜索为依据,不能让无关 Group 的属性故障决定它是否可用。首次自动发现的完整性规则不在本轮全面重写。 -- 直接目标读取、原生操作和既有 readonly-ui 保持各自用途。本轮不同时改只读配方的 UI/源码耦合、不增加 Tab/MenuItem/TreeItem、滚动或虚拟化导航。 -- 没有新证据时不重复同一动作;有新的页面/局部观察时允许修正查询继续同一任务,不要求用户逐步批准已授权的导航。完整性要求用于支撑具体结论,不能演变成一串额外 GATE。 - -### 实施落点、验证与停止点 - -主要修改 [ExpandUiWorkflow.ts](../src/Client/ExpandUiWorkflow.ts) 的所选目标重定位、父树诊断和错误整理;复用 [UiContracts.ts](../src/Core/UiContracts.ts) 当前字段。预期无需修改原生读取器/动作器或公共 UiQuery schema;若实际必须增加父范围搜索才能满足更大目标,将其单独说明,不顺带扩入本轮。 - -最多新增三项有价值的自动化测试,按行为组合场景,避免为每个诊断字符串建测试: - -1. **状态续接回归**:选定区域从 Collapsed 变为 Expanded 后不再 stale、不关闭、不再展开;已有目标则读到状态,目标仍隐藏则进入正确的局部诊断。覆盖无关 Group 状态缺口不阻断唯一所选目标。 -2. **局部反馈场景**:有内层折叠、完整范围无目标、同名目标歧义及树截断时,分别返回真实观察和下一步;保持任务未完成,不输出猜测 findings、不进行第二次动作。 -3. **真实 WPF 窗口路径**:复用现有夹具,必要时只增加嵌套区域;在已展开外层、未展开内层的真实 UIA 状态下完成客户端续接和诊断,确认外层不被关闭、内层未被意外操作。 - -优先先跑能复现良性状态变化被拒的测试,再改实现。已有动作结果未知、歧义、禁用与缺失状态测试按改动范围复用。相关测试/类型检查通过后构建,通过当前客户端实际 JSON 入口读完整回执并确认自有进程退出;自动夹具通过与实际客户端验收分别记录。完整回归最多一次,不重跑历史模型样本、不调用外部 API。 - -非 fixture 验收优先已获授权的 New-tavern 语音页:高级已展开时仍正常读取 Normalize,零额外展开。它只能验证实际单层续接,不能代替嵌套场景。第十四轮 Electron 窗口没有复选框或展开模式,不为凑兼容性结论对其执行导航;本轮若没有适合的非 WPF 导航目标,明确保留未验证,不设为整个工作包的阻塞条件。 - -完成后只更新对应 Skill 使用说明和本文件状态,在 findings 追加实际方法、失败和结果;不把计划写成实现。公开记录保留脱敏结果,原始窗口数据留在忽略目录。达到“已展开不误报、未完成有局部证据、无多余动作、真实入口可用”即停止。 - -
- -## 当前未实现与待验证事项 - -以下按第十七轮实现和已有实际证据核对,保留能力全貌;不是全部合并前待办。标准工具及客户端父范围定位已落实;自动多层执行等按上方“合并后的候选工作”处理。 - -| 事项 | 当前状态及用户可见影响 | 依据 | -|---|---|---| -| 自动多层折叠导航 | 未实现自动递归。第十五轮能返回实际内层候选,调用方独立定位内层后完成下一请求已在真实夹具验证;当前请求不会继续展开第二层 | localObservation 及原生唯一目标重定位 | -| 父范围内搜索/操作 | 已实现标准 inspect/review/setExpanded 的 scopePath;调用方明确提供实际父路径即可隔离同名/同 ID。客户端 expand-ui 已接入;click/type 暂不支持路径 | 第十六轮标准契约及第十七轮客户端双区域/隔离 Tavern 实测 | -| 更充分的候选上下文与定位 | 父选择器路径可显式提供;自动提取祖先路径、邻近标签和局部图像未实现。父路径自身仍多义时保留失败;语义相近标题是否相关仍由 AI 判断 | NavigationCandidate 与 scopePath 的能力区分 | -| 跨请求累计预算 | 尚未实现,当前不列为必要改良或多层导航前置条件。单请求已有 deadline/取消和调用/数据限制;未观察到需要新增累计配额系统的问题 | 每次 runExpandUiWorkflow 新建 deadline、计数;CLI 逐请求分发 | -| 任务范围与查询修正 | nextRequest 继承原 target/childQuery,但没有跨请求强制绑定。后续设计沿用用户选定窗口和任务,允许按新观察修正选择器;不增加续接令牌、额外授权状态机或逐步确认 | 请求构造与调用方规则;不是已实现的新校验 | -| 无关节点的不完整证据影响 | 三类辅助缺口已允许。第十五轮的已选父级直接定位不再被无关 Group 的必要状态挡住;首次自动发现仍检查所有返回 Group。父树其他关键缺口、ReadonlyUiWorkflow 的整树/源码门槛未改 | 所选目标路径与首次 discoverParent 路径分别处理 | -| 其他导航类型与任务结果 | 此编排尚未支持 Tab/MenuItem/TreeItem、虚拟化节点实现或滚动导航。完成结果仍要求目标 toggle 状态;不能把它当任意文本/数值/选择项读取的通用导航配方 | Group 发现与 readChild 的 toggle 检查;不否认 WinCode 已有其他原生操作 | -| 非 fixture 多候选验收 | 待验证。New-tavern 证明单候选,两个并列候选的完整客户端流程仅在独立真实 WPF fixture 通过。条件摘要/多控件区域的非 fixture 迁移也未完成 | 第十至十三轮实际记录 | -| 非 WPF 兼容性 | 第十四轮已只读识别用户打开的 Open-LLM-VTuber Electron 窗口:整窗 15 节点/12 层,局部 Document 5 节点/4 层均可取得完整树。未找到复选框,两个 Group 不支持展开;非 WPF 导航及其他提供方仍未验证 | 第十四轮真实项目识别记录;普通读取器未修改 | -| 不同模型、性能与前台影响 | 待验证。本轮没有外部/低价模型准确率实验、稳定性能比较或专门前台采样;已有功能通过不能推为这些结论 | 实验范围与数量 | - -已完成能力不是待办:明确 setExpanded、单候选发现、多候选选择、实时核验、目标归属、失败不重放、辅助缺口分类、有效 Expanded 续接、局部诊断及标准工具父路径定位均有实现和对应证据。第十六轮记录了安装 Skill 同步,默认 MCP 连接与 main 尚未切换;安装状态需在实际交付环境复核。第十七轮已完成客户端范围及隔离应用验证和本地交付收口,下一步仅完成 PR 检查与合并,不自动扩入递归探索或模型测量。 - -
-历史:第十四轮实施前的产品约束复核 - -## 历史产品约束设计复核(2026-10-06,仅文档) - -以下保留 `77f4ab4` 时的评估及当时方案。第十四轮已落实其中“展开导航允许三类辅助属性缺口/原生禁用分类修正”的部分;其余条目仍为待改方向,当前状态见前部清单。 - -结论:当前混合 UI 流程存在部分过宽的证据门槛和试点流程限制;应按任务需要缩小阻断条件,而不是再叠加跨请求配额或授权框架。本次检查 ExpandUiWorkflow、ReadonlyUiWorkflow/Recipes、CLI 请求入口、UI 查询契约、原生 UiActionExecutor/UiTreeReader 及相关 Gateway 限制。以下是当前源码与既有记录的判断,没有新复现或性能测量;不代表已审查整个 WinCode 的所有子系统。 - -| 设计点 | 判断与实际影响 | 后续处理原则 | -|---|---|---| -| 所有节点/属性问题一律停止 | 过宽。发现检查所有 Group;最终取证检查全部子节点的 propertyIssues。无关 Group 或辅助 className/bounds 读取失败也可能挡住所需状态读取 | 分开任务必需证据与辅助缺口,保留原始缺口。只有身份、唯一性、归属或所需状态受影响时阻断相应结论/动作;不把不完整发现中的单个候选算作全局唯一 | -| UI 读取与源码完整性统一阻断 | 任务耦合偏强。ReadonlyUiWorkflow 在配方收到数据前统一拒绝源码扫描/代码证据省略;只需 UI 状态的任务也可能被辅助源码问题挡住 | 由任务决定是否需要源码证据。要求源码对照的任务仍检查完整性;纯 UI 任务明确报告辅助源码缺口,不因此推翻已经取得的必要 UI 证据 | -| 原生展开将所有属性问题报为 TARGET_DISABLED | 错误分类过宽。Describe 同时读取 className、isOffscreen、bounds 等;任一问题都触发该码,未区分真实禁用、启用状态未知和辅助字段缺失 | 依据展开所需身份、启用状态、操作模式和展开状态判断;辅助缺口独立反馈,不误报禁用。尚未修改原生行为 | -| 选择只接受仍为 Collapsed 的候选 | 过严的状态续接。目标已可见时已有直接读取;但目标仍未出现、所选区域已正常 Expanded 时,会被当作失效选择 | 重新观察身份和状态:同一有效父级已展开则继续读取,不再次切换;身份歧义、禁用或必要状态未知仍停止 | -| 成功展开后没找到目标就终止 | 能力缺口和流程终止混在一起。当前 CHILD_RELATIONSHIP_UNCONFIRMED 未区分内层折叠、加载未完成、区域选择错误等 | 已确认动作成功后,可返回实际新证据供下一步判断;没有折叠证据不能猜内层路径。动作结果未知仍先核查,不重放 | -| 一次选择/一次展开、5/6 次调用、完整父树 128 节点、只读工作流最多一张图 | 试点限制,不能当作通用最优值。多层区域或前后图像对照可能受限;当前没有证明各数值已经导致真实失败 | 有具体任务需要时调整对应限制,沿用现有超时、取消和合理数据上限;不一次性解除所有限制或新建预算基础设施 | -| “不得改换查询”及授权表述 | 文档表述风险。固定用户任务合理,锁死查询字符串或将授权说明理解为每步确认会妨碍修正;程序没有新增逐次授权门槛 | 当前 nextRequest 续接仍按现有协议原样提交;新的实际观察可用于同一任务的合法查询修正。已授权的导航不逐步重复询问,扩到新窗口/业务输入保存才重新判断授权 | -| 原生唯一搜索、启用/模式校验、动作后验证、未知结果不重放、协议校验 | 必要的正确性检查。当前二元统计配方的 On/Off 和 isEnabled 也对应其输出需求 | 保留;拒绝拼错字段、未知状态或歧义不是过度防御。后台任务不自动降级为前台键盘/坐标操作 | - -### 修订后的推进顺序 - -1. 优先考虑一个小改动包:缩小辅助属性缺失的阻断范围,并使错误码准确表达缺失证据;先明确当前任务必需的证据,不引入通用策略引擎或新的抽象层。若进入实施,以最多三项有价值的回归和真实窗口路径确认“辅助字段缺失但必要证据有效”以及“必要证据缺失仍停止”。 -2. 再考虑选择续接:有效候选已经展开时继续观察;成功展开但目标未出现时保留未完成状态并提供新证据。用候选上下文解决实际歧义,再按确有证据的内层折叠推进,不遍历全部候选。 -3. 多层导航以原窗口、原任务和观察到的层级为范围;无进展或重复状态应给出针对性反馈。先复用单请求超时/取消与现有资源限制,不把跨请求累计预算列为实施门槛。只有以后实际出现重复运行失控或资源消耗问题,才重新评估是否需要累计限制。 - -以上为设计方向,当前代码仍保留表中的既有限制。此轮只整理 MD,不新建授权/配额系统、不修改生产代码、不重跑历史测试,也不启动真实 UI 或模型实验。具体记录见 [产品约束复核](hybrid-experiment-findings.md#产品约束设计复核仅文档2026-10-06)。 - -
- -
-历史:第十至十三轮客户端与导航验收 - -## 历史验收:第十轮实际客户端入口及第十一至十三轮结果 - -**先确认日常客户端会正确使用现有能力,再选择一个实际应用页面。不要先重复数十次固定 fixture API 基准。** 第九轮提示修正位于实验脚本,不能假定它已经作用于日常客户端。 - -本轮结果:A 的调用/证据顺序已补入分支 Skill;B 为 6/6,原生和混合的 P1/P2/P3 各读取 1/2/1 次。C 由用户指定 New-tavern 并打开语音页:首次 `SpeechNormalize` 两条路径均未找到,失败保留;用户截图确认可见控件后,`SpeechClearKey` 对照通过;随后用户展开高级参数,原目标 `SpeechNormalize` 对照也通过。共 3 组真实页面对照,首组未取得有效目标、后两组各自通过,不将全部尝试改算为成功。未新增模型 API 基准或生产源码修改,默认安装/已有连接未切换。 - -运行 Gateway 为已校验的分支 buildId `ede8590e616a625d276a3a04fffc9de5ac187dce57433a595ad7b70af7ef2f57`。UIA Host 的内嵌 revision 较早,交付清单源码/产物校验通过;不要仅因 revision 不同重建或冒称 Host 已更新。本轮本地记录集中在 `test-tmp/hybrid-tenth-checks/`,完整结果附件仍在 `test-tmp/skill-sessions/`,不随 Git 提交。 - -### A. 接齐分支内的使用说明 - -1. 先读仓库内 [Skill](../skills/wincode/SKILL.md) 和 [诊断手册的会话入口](../skills/wincode/references/diagnostics.md)。本次只读比对发现:当前已安装 Skill 的诊断手册没有 `readonly-ui`/`checkbox-audit`,仓库版本有;UI 手册也有差异。不要盲目同步或把已安装说明当实验分支能力的依据。 -2. 在分支内相关说明补齐实际调用顺序:选择目标 → 提交工具/配方请求 → 等待匹配回执 → 读取完整 `resultFile` → 检查 `isError`、业务状态和完整性 → 根据实际 findings 回答。参数只作工具输入,不能作为完成结果。沿用现有文档,不另建执行框架。 -3. 首次试用显式使用本分支说明和已核实的本分支构建。核对 `dist/build-manifest.json` 的源码/产物身份;文档提交领先构建 revision 不自动表示生产代码过期。已有正确工作区的原生连接先复用;使用 CLI 时避免重复连接,不能自行禁用全局 MCP 配置。默认安装切换另行安排,不在此步骤静默覆盖。 - -### B. 当前客户端走实际入口,共 6 个任务 - -复用已有隔离 WPF fixture;使用当前 Codex 等实际客户端按 Skill 操作。复用现有启动/清理方法,不新建服务,不把测试驱动或固定模型回复冒称实际客户端验收。 - -| 任务 | 给客户端的范围与要求 | 原生/混合各自合法 UI 读取 | -|---|---|---| -| P1 | `hybridChecks` 中 `hybridCheck0/3/6`;无摘要条件,统计选定控件 | 区域 1 次 | -| P2 | 先读 `hybridSummary`,On 才统计 `hybridCheck1/2/3/5/6`;fixture 默认 On | 摘要 1 次+区域 1 次 | -| P3 | 同 P2 的任务范围;fixture 用 `--hybrid-summary-off` 启动,不用 UI 写入切换状态 | 摘要 1 次,详情 0 次 | - -- 每任务原生/混合各一次,合计 6 次。原生走标准 inspect;混合走 CLI `action:"readonly-ui"` 或已核实支持它的客户端入口。`run_readonly_workflow` 是模型实验宿主的函数名,**不是新增的标准 MCP 工具**,不能假设日常客户端已注册它。 -- 通过实际窗口选择固定 PID/HWND,仅针对自有 fixture,使用 `backgroundOnly:true`、`capture:"none"`、scoped query。混合调用参数和原生读取范围保持一致;读摘要后再决定是否读详情,不从文档中的预期值直接生成成功答案。 -- CLI 使用本分支 `dist/Client/SkillSessionCli.js --workspace <已核实的项目绝对路径>`。交互式终端按手册用 `tty:true`,保留同一 session;先读 `ready`,一次发一条 JSON,等待相同 id 回执,读取完整附件,最终显式 close。可直接复用 [现有会话测试](../tests/hybrid-ui-session.test.ts) 中的 fixture、协议和清理方法;自动测试通过与客户端实际使用分别记录。 -- 配方 `timeoutMs` 先用现有 15000 ms;任务执行到 120 秒仍未完成就结束并清理,不追加尝试。修正只允许明确未执行的合法参数问题,最多一次;格式问题最多一次,不重新读取。以上是本轮操作预算,不宣称普通客户端已自动实现实验宿主的所有恢复限制。 -- 只在本地验收时查独立预期:P1 checkedCount=2,P2 detailsRequired=true 且 checkedCount=4,二者 unchecked=[hybridCheck3]、disabled=[hybridCheck6];P3 仅 detailsRequired=false。实际观察必须支持答案,文档答案不能代替证据。 -- 记录入口、实际构建、目标确认、参数、结果、读取次数、错误/修正、连接复用、关闭及自有进程退出。原始参数/目标信息留本地,公开只写脱敏结果。记录冷启动、窗口定位、提交回执及读取结果文件的成本;无法取得当前客户端逐次 token 时记未知。 -- 这 6 次是使用流程验收,不是统计样本;不能宣称随机独立模型成功率。**本轮不另启动 DeepSeek 等外部 API 基准,不因切换客户端模型而重跑第九轮 36 个样本。** 如后续需要测量,先明确新配置和预算。 - -### C. 入口通过后,再选择一个实际应用页面 - -本轮用户已指定 New-tavern 并打开语音设置页,两个明确 AutomationId 的单控件范围已完成对照。应用不需要为本次试验修改;只读取指定复选框,不读取密钥输入值、聊天正文或进行写操作。首次目标缺失后,只有用户提供新的页面证据才重新安排观察,先前失败保留。这里的单控件范围通过不代表整个设置页、条件摘要或多控件配方都适用。 - -当前 WinCode 托盘设置页以表格、按钮为主,不匹配 `checkbox-audit`,不为跑通验收修改该应用或增加配方。没有合适目标就交付 A/B 结果并写明“非 fixture 待指定”;这不妨碍完成实际客户端入口工作,也不能冒称完成实际应用迁移。适用且正确后才另行计划成本测量。 - -### 第十一轮结果:发现与展开次级界面(已实现) - -用户批准本工作包后,已在本分支补充 `wincode_ui_set_expanded`(需要 inspectionVersion 4 Host)及独立客户端 `expand-ui` 入口。父级来自真实 UIA:高级参数 Group 无 AutomationId,使用实际名称+控件类型定位。流程保留首次缺失,确认 Collapsed 后只展开一次,验证 Expanded、完整父级树中的子控件归属,再读取 Normalize 的实际 On 状态;已展开时不操作。3 项新增真实 WPF 场景通过,核心回归 483/483;实际窗口与构建、隐私检查详见第十一轮记录。main 不合入,不覆盖默认安装或修改目标应用。 - -第十一轮当时边界:需要提供一个明确父级候选;第十二轮补充的有限发现见下节。尚未实现自动发现任意导航路径、递归菜单探索或条件摘要/多区域导航。子控件缺失的原因在没有证据时仍为 unknown。父级完整验证固定 maxDepth=8、maxNodes=128,超预算停止;没有性能或低价模型准确率结论。以下条目保留本轮原方案,不能视作待重复执行的任务: - -原方案基于第十轮 New-tavern 的目标缺失,解决页面前置条件反馈和明确展开动作,不重复 P1–P3 大样本: - -- 目标未找到后,先区分完整搜索无匹配、搜索不完整和歧义;在已授权页面内安排最多一次有界只读诊断。只有实际状态及结构支持时,才给出父级/导航候选和建议下一步;否则保留原因未知,不能把每个 not-found 都归为折叠。 -- 导航动作独立于 `readonly-ui`。优先补明确“设为 Expanded”的兼容入口;已有 Expanded 时不切换,Collapsed 且目标唯一/启用/支持该模式时只展开一次。Tab/菜单选择按实际支持模式处理,不默认视为同一种控件;不做坐标回退,不自动输入或保存业务值。 -- 动作后先读回目标状态,再重新定位原控件;共享固定 PID/HWND、总 deadline 和有限步数,保留初次失败。错误反馈需说明已观察状态、已执行/未执行动作及下一步,超时后结果不明不能自动重放。 -- 实现时优先 1 项“折叠 → 展开 → 子控件可见”回归,再用最多 2 项保护“已展开不关闭”及“歧义/不支持不操作”;最后在实际 New-tavern 页面验证。本轮已按实际父级证据落实这些候选,不自动扩大范围。 - -### 第十二轮结果:有限父级发现(已验证方向) - -第十二轮当时的 `expand-ui` 可只传 `childQuery`。目标已可见则一次读取完成;完整搜索无匹配时,搜索当前窗口中的 Group(1000 节点/20 匹配上限),仅对唯一、启用、状态明确的折叠候选尝试一次展开。实际名称/AutomationId 来自本次 UIA,执行前重新定位,展开后仍验证完整父级树中的子控件归属。该轮多候选返回停止错误;第十三轮已改为可选择状态。自动路径最多 6 次调用;显式 `parentQuery` 保持既有最多 5 次行为。 - -新增恰好 3 项测试:真实 WPF 自动发现与目标已可见路径;多个/零候选不操作;搜索或状态证据不完整/无身份不操作。两项防误操作测试先红后绿;相关真实窗口回归 4/4,核心回归一次 485/485,类型检查与构建通过。当前 Codex 通过分支 CLI 在 New-tavern 完成不提供父级选择器的折叠发现(6 次调用、On),以及已可见读取(1 次调用、On);完整结果和进程退出均已核验。 - -这只证明单个 Group 折叠区域的自动发现方向有效。没有加入候选语义排序、递归探索、Tab/菜单/TreeItem 导航、坐标点击回退或新依赖;未开展外部模型 API 实验,不能宣称低价模型成功率或稳定性能提高。默认安装和 main 保持既有版本。本轮到此停止,不自动扩大工作包。 - -### 第十三轮结果:多候选选择(已验证) - -多候选不再统一视作终止错误。有可定位项时返回 `selection-required`、success=false、isError=false,以及候选的 nextRequest。当前 AI 根据任务与实际标题选择一个请求,补新 id 提交;candidateQuery 会重新发现当前 Group,检查选择仍唯一、启用且折叠,再重新定位、展开并验证。选择消失/改名/禁用/重复返回 NAVIGATION_SELECTION_STALE,不操作。显式 parentQuery、单候选及目标已可见路径继续保留。 - -新增恰好 3 项回归,另更新已有多候选契约测试;类型检查、构建、核心回归一次 487/487、真实 WPF 导航 5/5。当前 Codex 通过分支 CLI 实际观察两个折叠区域的标题,选择 Speech advanced:发现 2 次读取+后续 6 次调用,只有一次展开,Normalize On,Display advanced 仍折叠;自有 fixture/入口/Gateway 与目标 helper 已检查退出。 - -本轮是两次请求之间的候选决策交接,不是内部模型评分或自动循环。每请求仍共享固定目标与 deadline;客户端本次只选择一次、展开一次。多层嵌套、任意控件类型及递归尝试未加入。多候选真实验收使用独立 WPF fixture,New-tavern 此前仅验证单候选;没有非 fixture 多候选或低价模型准确率结论。本轮到此停止,详情见第十三轮记录。 - -### D. 遇到问题时的决策与完成标准 - -- 默认保持原生+可选客户端配方。第九轮 47→30 次模型请求的差异中,12 次来自格式修正,5 次来自条件编排;两边 UI 读取均 20 次。工具 schema 体积也不同,不能把全部收益归于减少往返。 -- 原生最终格式问题次于入口验收。已有 12 次失败均在首个 JSON 对象前有额外内容,部分含围栏;不直接从文本截取 JSON 放宽验收,也不继续盲调提示。只有实际使用再次遇到相同问题,才做有证据的最小修正。 -- 旧 T1–T4 错误提示只用于旧入口,未发现误用于新参数化入口;不要把它当已确认缺陷。真实参数更正机会仍为 0,不能报告恢复率提高。 -- 优先复用测试;新缺陷才加测试,最多新增 3 项。代码修改先跑相关测试和类型检查;涉及构建/入口时构建并走实际流程。完整回归最多一次,必要时文件并发 4、使用项目 SDK;并发 4 是已验证运行条件,不代表旧并发超时已修复。纯文档修改不重跑代码测试或模型样本。 -- 同一问题 3 次“修改→验证”失败后停止改代码,重新诊断。HTTP/权限/协议/清理错误、未知或缺失证据结束当前尝试;保留失败和成本,不重跑覆盖,不扩目标。 -- 完成标准:分支说明可用;B 的实际入口过程、结果与退出有证据;C 已验证或明确等待目标;问题和隐私检查补入现有 docs;相关变更提交推送至实验分支并核对 SHA。不要再次自动启动历史大样本或后续架构阶段。 - -
- -## 按需查看的文件与证据 - -| 需要解决的问题 | 优先阅读 | -|---|---| -| 第十五轮状态续接与局部诊断 | [ExpandUiWorkflow.ts](../src/Client/ExpandUiWorkflow.ts)、[工作流回归](../tests/hybrid-ui-workflow.test.ts)、[真实 UI 场景](../tests/ui-action-mcp.test.ts) | -| 实际调用、回执、取消与关闭 | [Skill 诊断手册](../skills/wincode/references/diagnostics.md)、[SkillSessionCli.ts](../src/Client/SkillSessionCli.ts)、[SkillSession.ts](../src/Client/SkillSession.ts) | -| 配方/证据/参数拒绝 | [ReadonlyUiRecipes.ts](../src/Client/ReadonlyUiRecipes.ts)、[ReadonlyUiWorkflow.ts](../src/Client/ReadonlyUiWorkflow.ts) | -| 实际窗口入口回归 | [hybrid-ui-session.test.ts](../tests/hybrid-ui-session.test.ts)、[HybridFixture.cs](../tests/fixtures/wpf-ui-review/HybridFixture.cs) | -| 第九轮模型提示与验收 | [hybrid-model.ts](../scripts/lib/hybrid-model.ts)、[hybrid-model.test.ts](../tests/hybrid-model.test.ts);这是实验宿主,不等于日常客户端 | -| 完整历史方法、失败和数字 | [实验说明](hybrid-readonly-orchestration.md)、[问题记录](hybrid-experiment-findings.md) | - -第九轮本地记录:`test-tmp/hybrid-ninth-checks/`;试运行 `test-tmp/hybrid-model-1791292998362/`、`hybrid-model-1791293033353/`;小样本 `hybrid-model-1791293065996/`、`hybrid-model-1791293169659/`。重点读 `model-results.json`、`delivery.json`、`credential-audit.json`;缺失时使用已提交的脱敏记录,不恢复凭据或重新调用模型来补文件。这些本地记录不随 Git 推送。 - -
-历史:第六至九轮规划与测量结果 - -## 历史记录:第六至九轮规划与结果 - -以下第 1–12 节按历史保留,包含当时的“下一轮”、失败状态和已用完的实验预算;**它们不是现在待执行的任务;第十轮 A–D 也已作为历史验收保留。当前状态与下一步以本文前部为准。** 初始分析基线 `eb6c63f`,第八轮规划基线 `ccac194`。最初《WinCode 改进计划》仅作设计参考,后续提案不等于已实现能力或当前授权。 - -## 1. 初始判断(历史) - -**测量、分支验收和有限格式纠错已补齐;随后按第 3.2 部分验证参数化配方在明确实际场景中的使用。当前不具备推进服务器 batch 的充分证据。** 下表保留初始分析时的事实,更新结果以末节为准。 - -| 已有事实 | 对后续规划的影响 | -|---|---| -| 客户端工作流、参数化 `checkbox-audit`、会话及 JSON 终端入口已实现 | 继续复用,避免再造执行层或再做一次相同的固定 fixture 演示 | -| 第五轮确认并修复了证据误登记、未完整模型回合分发问题 | 第三轮数据只能作为旧验收版本的历史结果,需要明确新版规则下的证据 | -| 第三轮原生 T3 有 3 次最终格式失败 | 继续保留失败,不能仅用成功样本推导整体收益 | -| T2 原生已经一次读取共同父区域 | 该任务没有进一步减少工具观察轮次的空间;不能将 token 减少称为往返减少 | -| 模型实验直接提示使用匹配的 T1–T4 预置配方,真实 fixture 的 T3 摘要为 On | 目前没有充分验证模型自行选配方参数或在 Off 情况下终止分支的端到端行为 | -| 配方层已测试 On/Off/unknown、缺失、重复和不完整状态;终端入口已有真实会话测试 | 后续集中补模型宿主及实际使用层的缺口,不重复堆同类单元测试 | -| 默认并发回归首次 473/474,单文件 14/14,文件并发 4 时 474/474 | 保留待定位状态;不把降低并发等同于修复,也不先扩大成生命周期重构 | - -原始规划中的“T2/T3 轮次至少减少 50%”门槛目前没有整体达成。后续按任务分别判断:T2 评价上下文和完整任务成本;T3 评价条件展开是否减少模型往返。这个划分用于解释结果,不追认原门槛已经通过,也不自动放行服务器 batch。 - -## 2. 下一轮:补齐新版验收与报告 - -目标是让一次新的实验能够回答“失败在哪里、失败消耗多少、哪些结果可比较”,并能把结论追溯到实际参与执行的版本。优先采用现有实验脚本中的小幅改动,不创建通用评测平台。 - -### 2.1 先核对旧证据能验证到什么程度 - -旧记录保存了每轮模型请求/响应和对模型交付的工具结果,没有单独保存统一的原始 MCP 响应流。混合路径交付的是工作流结果及投影,不能假设它包含重放当前实现所需的全部原始数据。 - -- 只读核查旧记录中的完成原因、最终输出格式和可见步骤证据,生成与旧结论分开的复核结果。 -- 对缺少原始输入的样本标记“无法完整重放”,不补造节点、不用最终答案倒推观察。 -- 若不能忠实复用 `runModelUiTask` 的输入,直接停止追求旧样本全量回放;用现有固定回复回归与新的受控测量补证据,避免写一套重复的验收器。 -- 复核不发模型请求、不执行原记录中的工具计划,也不覆盖历史文件。它不能重新测得 token、耗时或模型行为。 - -### 2.2 报告同时保留成功样本指标与全部尝试成本 - -主要落点:[benchmark-hybrid-model.ts](../scripts/benchmark-hybrid-model.ts)、[hybrid-model.ts](../scripts/lib/hybrid-model.ts)。 - -当前比较表主要汇总成功样本,失败样本的 usage 和耗时留在逐次记录中。下一轮将这些已有数据汇总到明确的字段,避免读表时忽略失败代价: - -- 计划样本、实际完成样本、通过/失败数量及失败类别。 -- 成功样本的 P50/P95,以及全部已执行尝试的 token、请求数、耗时;明确分母,不将两类指标混用。 -- usage 不完整时标记未知;可列已知部分与缺失数量,不把缺失计为零。 -- 有失败、样本不足、执行中源码变化或清理失败时,整体状态与任务比较资格分别说明;不因为两条路径各有一些成功样本就生成收益百分比。 -- 缓存命中/未命中的输入 token 按提供方实际 usage 单独统计;未返回则为未知。暂不加入会随价格变化的费用估算。 -- 补齐实验身份:记录验收版本、Git revision/工作区是否有变更,核对配方、工作流及 fixture 等实际依赖的指纹是否已有覆盖;只补缺口,不重复已有构建身份机制。 - -公开摘要只使用固定字段白名单,不复制自由文本错误、transcript、端点、环境、绝对路径或运行标识。原始诊断记录仍留在忽略目录,不能把简单替换路径当作完整匿名化。 - -### 2.3 模型端到端分支场景 - -当前 `modelTasks` 的 T3 预期和最终所需观察都针对摘要 On:要求先观察摘要,再观察详细区域。配方本身支持 Off。需要扩展的是模型基准的任务定义和 fixture 场景,不能把固定 On 基准的限制写成生产配方缺陷。 - -- 用确定的 fixture 场景给出 On/Off 及独立期望值;模型任务提示不提前透露该次状态或正确计数。 -- On 必须先观察摘要,再读取明确详细区域;Off 只需摘要证据,详细区域读取次数必须为零。 -- unknown、歧义或不完整观察继续拒绝;保留第五轮对“碰巧正确答案”和截断工具调用的回归约束。 -- 期望值来自预设 fixture 状态或独立固定样例,不复用配方统计代码作为测试预期。 - -### 2.4 本轮验证预算与完成条件 - -新增自动化测试合计最多 3 项: - -1. 模型宿主的 On/Off 分支证据与调用次数;覆盖固定错误回复,确认未完成观察不能支撑最终答案。 -2. 一组独立固定样本的报告聚合:成功、失败、缺失 usage、未完成计划及无效比较,核对分母和空值语义。 -3. 公开摘要仅输出允许字段;用合成凭据、路径和自由文本验证敏感内容不进入摘要。 - -对这些具体约束先写测试,确认新场景在旧实现下的行为,再做最小修改;已有通过的守卫不为制造红灯而故意改坏。先跑相关测试与类型检查,再构建和真实 WPF 场景验收。顺序使用项目现有 SDK 选择逻辑,构建完成后再测试;完整回归靠近完成时运行一次,由本轮改动引起的失败修复后最多再完整复跑一次。 - -完成标准:旧证据复核范围明确;新报告不遗漏失败代价、不泄露合成敏感内容;模型宿主正确处理 On/Off;相关固定回复测试与真实 WPF 路径通过,取消及关闭后的自有进程退出已确认。该轮先完成离线与真实窗口验证,再进入真实模型测量。 - -## 3. 随后:有限的新测量与真实使用验证 - -### 3.1 重新测量当前验收版本 - -沿用已获授权的模型服务、当前固定目标和只读边界。执行前列出模型设置、样本数、请求/时限预算;不扩展到其他提供方或额外服务。 - -1. 新版固定回复和真实窗口验证通过后,先做每任务每路径 1 次的连通性检查;试运行单独记录,不并入正式样本。 -2. 连通性没有阻塞问题后,先用原 T1–T4 场景进行每任务每路径 20 次、共 160 个样本的可比复测;T3-Off 等新增场景另列组别,不能悄悄混入旧 T3 指标。 -3. 原有每任务最多 8 次请求、120 秒的约束继续生效。160 个样本对应最多 1280 次模型请求的理论上限,不将上限当作预计消耗;认证、协议或连续相同基础设施错误出现时应提前终止并保留部分结果。 -4. A/B 使用一致的任务契约、输出格式要求、目标及观测预算。若为改善 JSON 遵从性调整提示,两侧同时采用同一规则,记录为新的实验配置;不得只修补原生侧失败样本后继续并入旧表。 -5. 每个样本使用新对话、交替 A/B 顺序;预热与正式测量分开。记录实际缓存命中与返回模型身份,不声称能清空服务端缓存或固定模型别名背后的权重。 - -所有失败保留,不重跑覆盖;两边必须满足该任务的完整计划与正确性要求,才能给出该任务的收益结论。结果接近阈值时先报告不确定性,不自动无限增加样本。 - -### 3.2 验证模型能否使用参数化配方 - -现有基准明确告诉模型使用匹配的 T1–T4 配方,因此它证明的是“已知配方的执行收益”。下一步应在同一只读边界内验证模型选择参数的能力,并单独报告这一层增加的成本。 - -- 从已有 JSON 会话入口出发,选择一个参数不同的隔离 fixture 场景;模型根据用户任务给出的明确范围构造 `checkbox-audit` 参数,所有参数仍经现有校验。 -- 与最佳原生调用比较,计入配方选择、参数构造、解释失败和最终回答的成本;不提前给出正确计数。 -- 模型对错误参数的更正应基于新回执重新决定,不自动重放旧计划;记录恢复所需请求,禁止借此扩大目标或写权限。 -- 首个非 fixture 案例在已有项目的专用测试窗口中选择,数据为合成内容。先人工确认 PID/HWND、区域和控件集合,不为完成计划扫描用户任意窗口或上传业务界面内容。 -- 找不到代表性案例时,记录“真实需求尚未验证”,保持当前客户端能力,不为证明需求而扩大产品范围。 - -先完成一个完整案例再判断是否需要更多配方。已有配方和终端会话的基础通过结果可以复用;新增检查聚焦参数选择、原生对照以及可理解的失败反馈。 - -## 4. 并发超时的处理边界 - -该问题与上述功能主线分开记录,只有阻塞验收时才进入本轮修复范围。 - -- 先用已有失败日志和当前调用链区分停止请求、强制终止、操作系统退出以及测试收到退出事件的阶段;必要的本地诊断只记录阶段及耗时。 -- 优先在目标测试上进行有次数上限的并发对照,不反复运行整个套件寻找一次通过。 -- 只有证据证明产品清理缺陷时才改生命周期代码;若是测试调度/超时设计问题,在保持原有清理不变量的前提下修改测试。 -- 在根因未明确时,文件并发 4 只是已验证的运行条件;不得删除原失败、扩大超时后直接宣称修复。 -- 同一问题严格遵守最多 3 次失败的“修改—验证”循环;达到上限后停止修改,重新检查证据并做针对性资料研究。 - -## 5. 架构去留条件 - -| 决策 | 需要的证据与动作 | -|---|---| -| 继续客户端配方 | 新版验收通过,至少一个代表性实际工作流有明确需求;该任务实际总 token 降低至少 40%,或端到端中位耗时降低至少 20%,且失败代价和 P95 未抵消收益 | -| 保留 native/简化配方 | 任务原生已一次完成,或配方参数与恢复成本抵消收益;保留直接工具入口,不强迫所有任务经过配方 | -| 评估服务器只读 batch | 以上证据成立,且有明确的普通 MCP 客户端无法方便使用客户端配方的实际需求;先列出所需有限操作,复核是否能复用执行边界,再提出独立变更 | -| 评估 discovery | 实测工具目录成本仍是主要问题,且客户端可消费渐进发现;独立于 batch 评估发现失败与额外往返 | -| 评估写操作或 runtime | 当前规划不启动。必须先有现有配方/结构化 batch 无法满足的具体任务,以及独立的执行权限、未知结果与恢复设计 | - -条件展开任务另查模型观察轮次能否减少至少 50%;P95 超过 10% 的恶化需解释与针对性复测。任何百分比都只适用于测量过的任务和配置。缓存不受控时不能把费用或延迟优势归因于单一架构因素。 - -## 6. 交付与停止点 - -下一轮交付限制为:实验宿主与报告的必要修改、最多 3 项新增自动化测试、相关真实窗口验证,以及现有实验/问题文档中的脱敏结果。正式模型复测和代表性使用验证按前述条件顺序开展,分别记录,不一次扩张成多个架构项目。 - -所有实现继续提交到测试分支。文档只保留仓库相对路径、版本、合成场景及汇总数字;原始日志保留在忽略目录,发布前检查实际待提交差异。保留无关工作区文件,不将其顺带提交。 - -若客户端配方已经覆盖实际需求且收益可信,就在客户端方案处完成交付;若收益不成立,就保留原生路径并停止该方向的架构扩张。完成验证后给出这一轮明确的去留判断,不以持续增加测试或继续增加层次替代结论。 - -## 7. 第六轮后的调整 - -已完成:旧记录的有界静态复核、报告聚合与公开摘要、模型宿主 On/Off 验收、3 项新增自动化测试、实际 WPF 验收及原 T1–T4 正式模型复测。旧记录缺少原始响应,未做执行重放。参数拒绝增加了针对性提示,但未做新提示与旧提示的模型纠错率对照。 - -正式复测的原生 T3 只有 1/20 通过,其余 19 次最终 JSON 语法失败;该任务的收益仍未成立。因此,在扩大到第 3.2 部分前,先验证有限的最终格式纠错:原生与混合采用同一提示规则、最多一次格式修正、继承原请求数和时限预算,反馈只描述输出契约,不提供正确答案,不重做已完成读取。原失败与恢复请求成本必须分别保留;错误事实、缺失证据和未完整回合不能被格式修正伪装成成功。 - -第六轮结束时该事项尚待实施,第七轮按上述边界完成,没有直接重跑完整 160 个样本。当前默认允许一次格式纠错,也可显式禁用并单独记录配置;原始失败不覆盖。 - -## 8. 第七轮完成后的判断 - -- 新增 3 项测试,先红后绿;进一步补测并修复原生已读取摘要但状态未知仍能进入纠错的问题。最终模型测试 12/12、完整回归 480/480,真实 WPF 的 4 个恢复场景通过,读取次数不变且自有进程退出。 -- T3 的 On/Off × 纠错开/关共 40 个真实模型样本。开启纠错的 20 个样本中,7 次初始格式失败全部在一次反馈后恢复;新增 7 次请求、23877 个 token、约 4.58 秒。禁用纠错的两组保留 7 个失败,不与第六轮正式样本合并。 -- 保留当前一次格式纠错规则,成本计入完整任务;不扩展 HTTP 重试、事实纠错或自动重读。小样本且缓存未受控,不能据此声称生产可靠性已证明。 -- 随后工作仍限第 3.2 部分的一个参数化实际使用案例:有明确固定窗口与合成数据后,比较模型选择参数、原生执行、失败反馈及完整成本。该案例尚未执行,不以本次固定配方成功替代。 - -本轮到实验宿主、报告、基准入口及既有 docs 的交付为止。服务器 batch、discovery、写操作和 runtime 不在本次范围内;普通 MCP 客户端需求与代表性实际工作流的进入条件继续保留。 - -## 9. 第八轮详细计划:参数化配方、可纠正的参数回执与完整任务验证 - -### 9.1 目标及本次源码核对结论 - -目标是回答:模型能否根据明确任务范围正确填写现有 `checkbox-audit` 参数;填错时是否能根据回执修正;经过这些步骤后,客户端配方是否仍有使用价值。本轮先完成这一条任务链,不以继续增加固定 T1–T4 样本代替参数化验证。 - -| 当前源码事实 | 下一轮处理 | -|---|---| -| `ReadonlyUiRecipes.ts` 已接受摘要、区域、明确控件集合和读取预算,1–64 个控件可配置 | 直接复用现有实现;不新增另一套复选框统计器 | -| `SkillSession.readonlyUiRecipe` 与 CLI `action:"readonly-ui"` 已存在,CLI 可在连接前拒绝错误参数 | 用实际入口验收参数错误、更正后执行和会话复用 | -| 模型实验只暴露 `run_readonly_workflow({recipe:T1–T4})`,提示还指定匹配配方;参数由宿主预填 | 增加独立参数化实验配置,让模型填写参数;不宣称已经证明开放式配方选择或工具发现 | -| 模型验收和报告目前硬编码 T1–T4、`hybridSummary`、`hybridChecks` 等身份 | 只抽出本轮需要的场景描述与验收输入,保持旧模式可复现;不建立通用评测平台 | -| 配方输入校验抛出普通 Error,CLI 顶层主要返回 `requestError` 文字 | 增加有限的稳定错误类别、字段定位和更正提示,保留旧文字字段与协议 | -| 第七轮的格式恢复只覆盖“有效观察后的最终 JSON” | 参数错误恢复单独记录;不能用格式恢复成功数替代参数恢复证据 | - -编写本节规划时仅检查并更新文档,第七轮的 480/480 等结果属于该提交的已有验证记录。随后实施的第八轮结果单列在第 10 节,不覆盖历史数字。 - -### 9.2 实施顺序和改动落点 - -按以下三个工作包顺序完成,每包形成可核查的行为结果后进入下一包。 - -**工作包 A:参数回执闭环。** 在现有配方校验中保留明确的失败原因,用一个小型、配方专用的错误表示承载固定代码、合法字段路径和修正提示;CLI 与实验宿主读取同一信息。复用已有 `errorCode`、`errorMessage`、`recoveryAction` 的含义;不通过匹配英文异常文本推断错误类别,也不把客户端输入错误包装成已经执行过的 MCP 调用。先在冷会话中拒绝错误参数,再由调用方提交更正请求,确认只有后者建立连接并读取 UI。 - -**工作包 B:模型构造参数。** 给模型展示现有 `checkbox-audit` 的参数 schema 和任务明确选择的范围;模型调用必须实际提交这些参数,宿主不得默默换成预设正确参数。将经过验证的参数交给现有配方执行路径。场景描述只承载目标约束、允许区域、明确控件集合、独立期望和验收要求;避免扩展为任务插件或任意程序执行接口。 - -**工作包 C:有限测量与交付。** 固定回复和真实 JSON 会话通过后,再做第 9.6 节的独立模型小样本。报告参数提交、拒绝、更正、格式纠错和最终验收的成本;更新现有实验说明与问题记录,提交到当前测试分支。 - -| 文件 | 计划改动及限制 | -|---|---| -| `src/Client/ReadonlyUiRecipes.ts` | 参数错误的稳定信息;保留现有有效参数与统计行为 | -| `src/Client/SkillSessionCli.ts` | 对已知配方输入错误增加结构化字段,保留 `requestError` 和 `wincode-skill-session/1`;普通原生调用、取消、关闭契约不改 | -| `src/Client/SkillSession.ts` | 优先复用现有方法;只有实际接入受阻才作必要小改动 | -| `scripts/lib/hybrid-model.ts` | 参数化工具描述、调用分发、任务范围校验及可追溯验收;保留固定 T1–T4 配置 | -| `scripts/benchmark-hybrid-model.ts` | 显式选择新实验配置,记录实际参数与请求成本;旧命令和默认场景保留 | -| `scripts/lib/hybrid-report.ts` | 以有限场景标识区分新旧样本,增加参数失败/恢复汇总;继续字段白名单 | -| 现有 hybrid 测试文件 | 最多新增 3 项有实际行为约束的自动化测试,见第 9.5 节 | -| 现有 3 份 hybrid 文档 | 记录接口变化、真实结果、失败与剩余验收条件 | - -现有 WPF fixture 能提供以下场景,优先保持其代码不变。只有现有窗口无法表达实际验收需求时才增加明确的合成场景,并记录原因。 - -### 9.3 参数反馈与重试规则 - -下列增量契约在规划中提出,现已按本节边界实施。已知配方输入错误统一为 `INVALID_RECIPE_PARAMETERS`,未知配方为 `UNSUPPORTED_RECIPE`;具体字段使用 `field`、`errorMessage`、`recoveryAction` 描述。错误分类放在参数校验处产生,CLI 和模型宿主不维护两套规则。 - -| 情况 | 给模型的反馈 | 后续动作 | -|---|---|---| -| 控件列表为空、重复或超限 | 固定字段 `checkboxAutomationIds`、数量/唯一性要求 | 可根据原任务重新提交一次合法列表;不得自行删除任务要求的控件 | -| AutomationId 或预算非法 | 对应合法字段路径、允许类型和范围 | 可修正格式/预算;不得自动扩大到整窗读取 | -| 未知字段、配方或写入参数 | 支持的配方/字段集合与只读要求 | 不执行;只有保持任务范围的修正才可继续 | -| 目标、区域或控件集合偏离原任务 | 明确任务范围不匹配 | 本实验结束该样本,不将越界尝试作为普通参数优化继续 | -| 已执行读取但观察歧义、缺失、unknown 或不完整 | 保留已有错误、实际执行阶段与证据 | 结束样本;不通过删控件、猜结果或追加范围使其通过 | -| 已有有效观察,最终 JSON 格式错误 | 第七轮既有格式反馈 | 最多一次格式修正,禁止新的工具调用 | -| HTTP/协议错误、取消或总时限耗尽 | 保留失败类别与已知成本 | 结束样本,不重试 | - -只有校验明确发生在执行之前时才报告 `workStarted:false`;传输失败或结果未知不能一律标为未执行。`recoveryAction` 表示调用方可考虑的下一步,不授权宿主自动重放。 - -参数更正上限为每个样本一次,最终格式更正沿用一次;两者共享最多 8 次模型请求、120 秒总时限和既有上下文/输出预算。更正由模型在读取回执后重新决定,宿主不改写错误参数、不代填答案。达到上限就记录失败。两条路径采用相同的输入错误机会与总预算;原生侧仍可使用最佳合法 scoped query。 - -错误内容只输出固定代码、允许字段名及不含输入值的提示,不回显完整拒绝参数、任意键名、凭据、端点或环境。旧 `requestError` 继续可读;新增信息不要求已有客户端立即升级。 - -### 9.4 固定场景与独立验收 - -只研究“明确选定复选框的状态汇总”这一类任务,使用现有窗口的三种配置。下列期望仅供宿主验收和测试,不放入模型提示;模型只获得任务范围、输出契约和可用工具说明。 - -| 场景 | 用户明确范围 | 独立预期 | 每次合法执行的 UI 读取 | -|---|---|---|---| -| P1:无条件子集 | `hybridChecks` 内的 `hybridCheck0/3/6`,不提供摘要参数 | checkedCount=2,unchecked=[hybridCheck3],disabled=[hybridCheck6] | 原生和配方各 1 次 | -| P2:摘要 On 的另一子集 | 先判断摘要,再统计 `hybridCheck1/2/3/5/6` | detailsRequired=true;checkedCount=4,unchecked=[hybridCheck3],disabled=[hybridCheck6] | 原生和配方各 2 次;模型观察轮次可不同 | -| P3:摘要 Off | 与 P2 相同范围,fixture 启动为 Off | 仅 detailsRequired=false | 原生和配方各 1 次,详细读取为 0 | - -共同区域中的其他控件属于读取上下文,不能进入选定集合的统计。验收检查模型实际提交的目标、区域、控件集合和最终证据,不能只检查结果中的数字;即使 Off 不会读取详情,错误的任务参数也不得因为最终 false 正确而通过。明确集合可允许顺序变化,但成员、条件与目标必须一致。 - -A 为原生 inspect,B 为现有参数化客户端配方。两边收到相同任务范围,不要求原生逐个读控件;原生可一次读取共同父区域。底层仍经过同一标准 MCP、固定 PID/HWND、backgroundOnly、capture:none 与现有结果完整性校验。 - -这三个场景验证参数迁移与条件执行,仍属于合成 fixture。它们不证明模型能从任意自然语言发现控件,也不替代非 fixture 实际应用验收。 - -### 9.5 测试先行与真实入口验收 - -新增自动化测试总计最多 3 项,按具体约束先写测试。已有守卫如本来通过,保留通过事实,不故意改坏实现制造红灯: - -1. **参数拒绝契约与兼容性:** 稳定代码/字段、合法约束提示、没有拒绝值回显;非法参数在连接/读取前拒绝,旧文字回执仍存在。 -2. **模型更正与范围状态转换:** 先错误参数再基于回执提交合法参数;次数与总预算正确;越界、同样错误再次提交、unknown 观察以及“参数错但答案碰巧正确”均不能通过;格式更正不追加读取。只覆盖与新闭环直接相关的场景。 -3. **真实 JSON 会话链路:** 错误请求保持 cold → 更正参数后读取真实 WPF → 同一会话执行另一选定子集 → 正常关闭;确认结果、读取数、连接复用及自有进程退出。复用已有 fixture/CLI 驱动,不另建服务。 - -复用现有取消、关闭、HTTP 错误和格式恢复测试,不为相同行为新增重复测试。运行顺序是最小相关测试、类型检查、必要构建/fixture 发布、真实入口验收,最后完整回归一次。使用 `scripts/lib/dotnet.mjs` 选择项目固定 SDK;默认并发问题未定位前沿用文件并发 4。若本轮修改导致回归失败,修复后最多一次最终完整复跑;同一问题 3 次修改—验证失败就停止修改并重新诊断。 - -真实入口需检查工作流业务结果与 MCP 读取次数,并确认实际 helper、Gateway、CLI、fixture 的退出。原生进程标识只留本地证据;公开记录只列汇总与退出状态。 - -### 9.6 真实模型预算及统计口径 - -先完成上述确定性检查,再使用现有已授权的提供方和设置。开跑前固定源码、场景定义、工具 schema、任务提示及验收版本;不得在一组数据中途修改实现。 - -- 连通性检查:P1–P3 × 两条路径 × 1 次,共 6 个样本,独立记录。 -- 主小样本:P1–P3 × 两条路径 × 5 次,共 30 个样本;交替 A/B 顺序,并轮换场景顺序。总计最多 36 个样本、288 次模型请求的理论上限,不把上限当预计消耗。 -- 每样本最多 8 次请求、120 秒;一次参数更正与一次最终格式更正均从该预算扣除。认证、协议或基础设施阻塞即停止整组并保留部分结果;任务级失败照实计数。 -- 不为得到恢复样本故意篡改真实模型参数。自然发生的输入错误才计入真实恢复率;没有发生则报告“未观察到可恢复参数错误”,固定回复测试仅证明宿主规则。 -- 不自动追加样本或重跑失败。无法完整完成计划、存在任务失败、源码变化或清理失败时,不生成该场景收益百分比;旧 T1–T4 与新 P1–P3 不合并。 - -报告分别保留:首次合法参数比例、参数拒绝数、获得更正机会数、更正后通过数、首次最终答案通过数、格式纠错数、完整任务通过数。分母写明,参数恢复率只在实际发生可恢复输入错误时计算。一个任务既修正参数又修正格式时分别计数,但完整任务不能重复计为两次成功。 - -完整任务成本包含 schema、参数生成、被拒绝请求、工具回执、纠错与最终回答;同时保留各恢复阶段的请求/耗时/usage。usage 不完整则相关总量为 null,并列已知部分;缓存命中按提供方实际返回统计。`firstPassPassed` 的语义变化必须通过新验收版本明确说明,不能悄悄沿用旧“没有格式纠错”的含义。 - -五次重复只用于判断新路径能否工作和是否出现明显成本倒挂。P50/P95 可描述样本,但不能据此宣布稳定性能门槛已满足。原定 token/延迟门槛继续作为后续代表性实际案例的判定条件;本轮不自动扩为 160 个样本,也不估算价格或宣称缓存可控。 - -### 9.7 非 fixture 实际案例的衔接条件 - -当前尚未确认一个带明确目标范围的非 fixture 测试窗口。完成上述闭环后,优先选择用户明确指定应用的专用测试窗口或已有项目公开的专用验收入口,使用合成数据、明确的区域和控件集合;届时核对真实控件是否适用该配方,而不是把固定 fixture 改名为实际案例。 - -这是第 3.2 节仍保留的独立验收条件,不将未指定应用、未核对的窗口或业务数据列为已授权实验输入。没有合适窗口就交付“参数化闭环已验证,实际应用迁移待验证”的明确状态,不扫描任意桌面、不另造应用来替代用户需求。非 fixture 真实模型调用数量在场景确定后另列,不占用或混入上述 36 个样本。 - -### 9.8 隐私、兼容性与交付检查 - -凭据继续只从既有授权环境读取,不写入命令参数、提示、回执或文档。公开摘要新增字段使用固定白名单,参数错误示例使用合成值;本地原始记录保持 Git 忽略,不能直接作为匿名附件。提交前检查实际变更文件和本轮实验产物中的真实凭据原值及常见编码形式,仅公开匹配数量和范围。 - -兼容性要求:旧 T1–T4 配置、现有有效 `checkbox-audit` 参数、JSON 会话协议及原生工具入口继续可用;新增参数错误字段不删除旧字段;取消、时限和证据要求不能因恢复流程放宽。报告中新模式/新验收版本明确标识,保留旧报告原文,不重算并覆盖旧成功率。 - -最终按已授权流程提交、推送到 `codex/hybrid-readonly-orchestration`,核对本地/远端 SHA;不合入 main。无关文件不顺带提交。第 9 节保留执行前规划,实际实现与验证状态见第 10 节。 - -### 9.9 完成标准与去留决策 - -本轮完成需同时满足:参数化任务实际调用现有配方;错误输入能得到可操作且不泄露输入值的回执;更正前未执行读取,更正后的请求保持原任务范围;真实 JSON 会话结果、连接复用及清理正确;3 项以内的新测试和相关回归完成;模型小样本成功与失败、完整成本及限制如实记录。 - -模型样本失败可以形成有用的实验结论,但不能同时宣布该场景已通过验收;基础设施阻塞须明确标为未完成。报告区分实现完成、固定回复通过、真实窗口通过、真实模型通过与实际应用迁移,不能用任意一项代替其余项。 - -- **参数化有效且恢复成本可接受:** 保留客户端配方及有限参数反馈,进入明确的非 fixture 案例验收。 -- **宿主正确,但模型参数/恢复成本抵消收益:** 保留原生路径,将配方定位于适合的明确任务,不增加更多配方来掩盖结果。 -- **发现目标或证据守卫绕过:** 停止模型测量,先修复并验证该具体缺陷;已有失败记录保留。 - -本轮不新增服务器 batch、discovery、写操作、通用 runtime、服务或依赖,不重构无关生命周期。达到上述停止点后交付,不以持续增加实验或架构层次替代结论。 - -## 10. 第八轮实际结果与停止点 - -已实现:共享的参数校验错误、兼容原协议的 CLI 增量回执、模型实际参数分发、任务范围与证据约束、一次参数更正、与最终格式更正分开的计量,以及 P1–P3 独立报告。新增 3 项测试,最初全部红灯;补测并修复未知状态/实际观察不支持固定场景答案的验收缺口。相关测试 18/18,真实会话文件 6/6,主回归 482/482;后来明确参数提示后相关 18 项及类型检查再次通过。新增会话测试最后补齐条件子集和审计 helper 退出,单独通过。没有再次完整回归,也没有追加模型样本。 - -真实模型按预算执行 6 个独立试运行和 30 个独立小样本,全部 70 次请求、200489 个 token;试运行 3/6,小样本原生 12/15、混合 3/15。小样本混合侧 12 次将参数打印为最终 JSON,没有实际工具调用;原生 Off 有 3 次试图读取详情而被拒绝。三种场景均不能给出收益百分比,整体模型验收未通过。7 次最终格式失败恢复属于格式纠错,不能混称参数恢复;本轮真实参数更正机会为 0。 - -本轮完成了实现与确定性/真实入口验证,以及有成功和失败的有限实验;没有完成“模型参数化调用稳定可用”或非 fixture 迁移。当前停止追加模型调用,保留原生入口和实验分支。下一次改良首先针对已经确认的“构造参数被当成最终回答”区分工具调用与最终结果的契约,再以独立配置验证;任何新的样本预算需先列明,不能覆盖本轮失败或自动执行最终 JSON。该缺口解除前,服务器 batch 与进一步实际应用模型迁移的进入条件仍不满足。 - -## 11. 第九轮执行边界(2026-10-06) - -本轮针对第八轮实际出现的“最终答案打印参数,没有工具调用”改良提示,配置名为 `call-then-findings-v1`。两条参数化路径共同明确先发送 function tool call、等待回执、再输出观察结果;混合工具说明和任务尾部明确调用 `run_readonly_workflow`,参数仅作为工具输入。最终 JSON 要求合法带引号的键名和实际观察值。提示不包含固定场景的正确状态/数量,也不代填参数或执行最终文本。 - -范围限实验宿主提示、配置标识、1 项回归测试与现有实验文档;旧 T1–T4 提示、标准 MCP、客户端配方、生产 SDK、依赖和验收规则保持原语义。API 默认工具选择保持不变,不新增强制调用字段或自动重试,不增加最终无观察回答的恢复机会。测试检查 P1–P3 两条路径打印参数均为零读取失败,以及成功读取后把参数当结果仍失败;已有正向、范围、unknown、格式恢复测试继续运行。新增测试初始红灯来自缺少明确调用提示,旧的无观察守卫已经正确拒绝,不能称为新修复的执行漏洞。 - -确定性检查及类型检查通过后,使用既有授权提供方和 WPF fixture:最多 6 个试运行(P1–P3 × 两条路径 × 1),独立记录;只有试运行实际产生混合工具调用且没有基础设施或清理失败,才继续最多 30 个小样本(P1–P3 × 两条路径 × 5)。本轮上限 36 个样本,每样本原有 8 次请求、120 秒、一次参数更正与一次最终格式更正;不为得到成功重跑,不根据试运行再改提示。一组中不修改源码。发现基础设施、证据守卫或清理问题就停止;试运行仍无混合工具调用则不启动小样本。 - -第八轮失败记录保留;新旧配置跨轮对比只能描述观察到的变化,不能把它当同期随机对照或单一提示的因果证据。完整失败成本、首次通过、格式/参数修正和清理分别列出;不完整或有任务失败的场景继续不给收益百分比。固定回复只证明宿主契约,真实模型结果单列。完整回归最多一次,隐私检查只公开数量及范围,最终提交推送到实验分支,main 不变。达到本轮验证与预算边界即交付,不自动进入更多配方、服务器 batch 或未指定的实际应用窗口。 - -## 12. 第九轮实际结果与停止点 - -明确工具调用/最终结果契约与配置标识已实现,新增 1 项自动化测试;相关 19/19、类型检查通过。旧无观察守卫原本正确,本轮没有自动执行最终文本或新增强制工具选择。6 个试运行实际 4/6,其中原生范围错误及 Off 后详情请求各 1 次,保留失败;混合 3/3 产生实际调用,清理正常,因此按预定条件执行 30 个小样本,期间不再改提示。 - -小样本原生首次 3/15、最终 15/15,混合首次/最终 15/15;全部 18 个混合尝试首次产生工具调用,打印参数作为最终答案为 0。原生 12 次格式恢复消耗新增 55293 个 token、约 9.54 秒,没有新读取。全部 36 个样本 34/36、89 次 API 请求、264356 个 token;真实参数更正机会仍为 0。三种小样本比较资格通过,两个试运行失败仍保留,不将所有 36 个样本描述为整体全部通过。 - -四组源码稳定、51 个审计 helper start 无残留,自有 Gateway/fixture 实际退出。完整主回归只运行一次,项目 SDK 10.0.303、文件并发 4 下 483/483。隐私检查覆盖 6 个公开变更文件及本轮本地基线、测试和模型记录,实际 API Key 的原值与常见编码匹配为 0,公开新增行本机路径/密钥形状匹配为 0。无关 UI 计划和原始记录不提交,交付目标仍只限实验分支。 - -当前停止在有限参数化 fixture 的实际调用已验证;非 fixture 应用迁移与稳定性能门槛仍未验证。本轮不再追加模型调用,不扩大配方或服务器架构。 - -交付收尾首次清单验证失败,诊断为既有本地清单的 Gateway/Skill/配置指纹过期;当前源码、产物及原生回执校验通过。仅用现有脚本刷新忽略的本地交付清单,重新验证 `matched:true`,首次失败仍保留;没有修改生产源码或重复完整回归。 - -
diff --git a/docs/hybrid-readonly-orchestration.md b/docs/hybrid-readonly-orchestration.md index 8186e88..792bca2 100644 --- a/docs/hybrid-readonly-orchestration.md +++ b/docs/hybrid-readonly-orchestration.md @@ -1,370 +1,75 @@ -# Hybrid 只读编排测试分支 +# 客户端 UI 工作流 -实验分支:`codex/hybrid-readonly-orchestration`。基线:`038c0ccc7372948da405546cc7ec0add56eea9de`。 +适用版本:0.17.0。工作流由调用方显式启用,使用既有 MCP 连接,不会自动调用模型。后续任务只看[迭代计划](next-iteration.md);本页仅描述当前接口。 -文档分工:本文保留接口用法和历史实验方法;[迭代规划](hybrid-next-iteration-plan.md#main-合并验收线)维护 main 合并验收线、后续工作包和当前状态;[问题记录](hybrid-experiment-findings.md#第十六轮父路径内查询与展开2026-10-07)保存实际失败、验证与限制。第十六轮标准 inspect/review/setExpanded 已支持 scopePath,实际双区域 WPF 与当前 Codex CLI 通过;当轮记录了安装 Skill 同步。第十七轮客户端 expand-ui 已接受父路径并完成实际 JSON 会话与隔离 Tavern 验收;自动路径生成与递归执行未实现。新接手不需要按本文历史命令顺序执行。 +## 选择入口 -当前父路径用法与完整边界见 [Skill UI 手册](../skills/wincode/references/ui.md#操作方式与授权)。inspectionVersion 5 的路径逐层唯一解析、失败不退回整窗;旧 Host 在带范围的展开动作前被识别。第十七轮隔离 Tavern 的 scopePath 已完成一次展开、独立读回和零动作续接;非 WPF 导航仍未验收。 - -**接手入口:先读 [main 合并验收线](hybrid-next-iteration-plan.md#main-合并验收线)、[后续执行顺序](hybrid-next-iteration-plan.md#后续执行顺序)及[第十六轮记录](hybrid-experiment-findings.md#第十六轮父路径内查询与展开2026-10-07),无需从头重跑本文命令。** 客户端父范围和真实应用闭环已完成,合并前继续核对完整回归与交付/PR 检查,单请求仍最多一次展开。第十一至十三轮增加明确展开、单候选发现与多候选选择后验证;第十四轮允许展开导航的 className/bounds/isOffscreen 辅助缺口;第十五轮增加所选 Expanded 父级直接续接及动作后局部诊断,第十六轮补标准工具的父路径定位,第十七轮补客户端范围传递、根范围复用与最终子控件隔离。普通只读工作流/统计配方的证据门槛未改。属性故障与嵌套诊断分别在第十四/十五轮通过独立真实 WPF 窗口和当前客户端 CLI 验证,New-tavern 证明实际单层路径。自动递归、其他控件类型、非 fixture 多候选和稳定性能尚未实现/验证,均不作为本次合并前置条件。跨请求累计预算当前不要求补齐;任务范围沿用用户选定窗口和目标。本文历史结论、命令和预算不能混作新的执行指令。 - -实际发现的缺陷、未解决问题及隐私边界见 [问题与验证边界](hybrid-experiment-findings.md)。失败记录保留在本地,公开文档只提交脱敏汇总。 - -本文初始迭代增加客户端只读编排和真实 Windows UI 基准,当时标准 MCP 工具、schema、Gateway 准入和 UIA helper 生命周期保持既有路径。第十一轮随后新增标准 set_expanded 工具及版本门,最新接口以当前手册为准。只有显式调用客户端模块才启用混合编排;本分支继续独立开发,满足迭代规划中的 M1–M6 后按 PR 流程合并到 `main`。 - -## 构建与验证 - -在此分支的仓库根目录运行,使用现有依赖和 `global.json` 固定的已安装 SDK;命令不会安装或下载 SDK: - -```powershell -npm run typecheck -npm run build -npm run build:hybrid-fixture -node --import tsx --test tests/hybrid-ui-workflow.test.ts -npm run benchmark:hybrid -- 20 -# 已通过进程环境提供获授权的模型配置时: -npm run benchmark:hybrid-model -- 20 -# 新增 Off 场景单独报告;1 次仅作连通性/分支检查: -npm run benchmark:hybrid-model -- 1 summary-off -``` - -需要现有的 Release UIA Host 发布目录和 fixture 已完成的 NuGet restore。缺少这些依赖时按常规项目配置处理;基准不自动安装、重试或降级。`benchmark:hybrid -- 1` 是快速工作流验证,不能替代默认 20 次测量。结果写入 `test-tmp/hybrid-ui-/report.json`,同目录保存背景窗口截图及其步骤证据。实验输出不提交到 Git。 - -## 客户端示例 - -复用 `WinCodeSession`,先通过 `wincode_ui_list_windows` 取得候选,再由调用方明确选择 PID/HWND。整个工作流固定这一身份,不根据标题重新选择窗口。 - -```ts -import { WinCodeSession } from '../src/Client/SkillSession.js'; - -const session = new WinCodeSession({ workspace: selectedWorkspace }); -try { - const result = await session.readonlyUiWorkflow(selectedTarget, async reader => { - const summary = await reader.inspect({ - query: { automationId: 'hybridSummary' }, readStates: true, maxDepth: 2, maxNodes: 8, - }); - const state = summary.tree?.states?.toggle; - if (state === 'Off') return { detailsRequired: false }; - if (state !== 'On') throw new Error('Summary state is unknown.'); - const detail = await reader.inspect({ - query: { automationId: 'hybridChecks' }, readStates: true, maxDepth: 4, maxNodes: 40, - }); - return { detailsRequired: true, inspectedRegion: detail.tree?.automationId }; - }); - // 向宿主返回整个 content,包括原生图像块;同时处理 isError。 - return result; -} finally { - await session.close(); -} -``` - -这是项目内 TypeScript API,示例中的 workspace/target 需要由宿主提供。执行的是受信任客户端程序,没有新增执行模型源码的工具、解释器或 sandbox。包装器限制自己的工具入口,不能限制宿主程序在包装器以外的能力,也不能替任意第三方客户端切断模型轮次。 - -在能执行受信任 TypeScript 的宿主中,优先使用 `session.readonlyUiWorkflow(target, program, options)`。它复用同一个标准 MCP 连接,沿用工作区/构建身份校验、工具验证和完整内容块返回;创建会话及只做客户端处理均不启动 Gateway,首次实际读取才连接。返回值包括 `report`、`content` 和 `isError`,宿主应保留原生 image 内容块。 +| 需求 | 入口 | 当前限制 | +|---|---|---| +| 单次控件读取或源码对照 | 标准 inspect/review | 支持显式 scopePath;字段见 [UI 手册](../skills/wincode/references/ui.md) | +| 自定义只读条件流程 | WinCodeSession.readonlyUiWorkflow(target, program, options) | 可信宿主 TypeScript 回调,reader.inspect/review 均支持可选 scopePath | +| 指定复选框集合统计 | readonlyUiRecipe 或 JSON action: readonly-ui,recipe: checkbox-audit | 可选 scopePath 同时限定摘要与详情 | +| 同级标题之间的列表条目 | readonlyUiRecipe 或 JSON action: readonly-ui,recipe: sibling-range | 显式容器和两个标题,按观察到的兄弟顺序读取;不推断业务分组总量 | +| 目标隐藏在可展开父级下 | expandUiWorkflow 或 JSON action: expand-ui | 显式父路径内最多展开一次;最终结果要求 toggle 状态 | -该入口把会话关闭信号与调用方取消信号合并。`close()` 会取消正在读取、排队及两次读取之间等待的编排,等待已登记工作流形成最终报告,并观察 Gateway 退出。完成步骤的证据保留,后续读取不再分发;关闭后的会话入口拒绝新工作流。调用方仍需保存返回报告;关闭会话不自动替宿主交付附件。客户端回调使用宿主已有的执行能力,signal/race 不能强制终止同步死循环或撤销回调在包装器之外的操作。 +先取得实际窗口并明确选择 PID/HWND。同一任务复用一个 WinCodeSession;首次业务调用才连接 Gateway。结果同时检查 isError、业务 success、步骤与证据;任务结束显式 close。超时、取消或连接丢失不授权自动重放。 -已有连接的其他客户端可继续使用独立的 `runReadonlyUiWorkflow(call, target, program, options)`,由宿主提供取消与连接关闭。终端 JSON 入口 `SkillSessionCli.js` 保留原有单工具协议,测试分支另提供以下已安装客户端配方。 +会话启动、单工具请求和关闭命令见[诊断手册](../skills/wincode/references/diagnostics.md#skill-按需会话)。JSON 回执中的 resultFile 指向完整结果;必须读取该文件并保留全部 MCP 内容块及图片,不能只凭回执路径判定成功。模型输出的参数文本不是工具调用,不能自动执行。 -### 已安装的复选框检查配方 +## 复选框统计 -`session.readonlyUiRecipe(target, 'checkbox-audit', parameters, options)` 与终端入口复用同一配方及会话生命周期。先列出窗口并由调用方选择明确 PID/HWND,然后在同一个终端会话发送: +以下 PID/HWND、名称与 ID 全是示例占位值,必须替换为实际观察: ```json -{"id":"audit1","action":"readonly-ui","recipe":"checkbox-audit","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"summaryAutomationId":"hybridSummary","regionAutomationId":"hybridChecks","checkboxAutomationIds":["hybridCheck0","hybridCheck1","hybridCheck2","hybridCheck3","hybridCheck4","hybridCheck5","hybridCheck6","hybridCheck7"],"maxDepth":4,"maxNodes":40},"timeoutMs":15000} +{"id":"audit1","action":"readonly-ui","recipe":"checkbox-audit","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"automationId":"voiceSettings"}],"summaryAutomationId":"summary","regionAutomationId":"checks","checkboxAutomationIds":["optionA","optionB"],"maxDepth":4,"maxNodes":40},"timeoutMs":15000} ``` -PID/HWND 与 AutomationId 是示例占位值,使用实际选择的窗口和控件。回执仍为 `resultFile`、`isError`、`imageFiles`;读取结果文件的 text JSON 后检查 `success`、`steps` 和 `findings`,不要只根据短回执推断完成。取消和关闭沿用现有 action,`targetId` 指向活动配方请求的 id。 - -配方接受 1–64 个互不重复的复选框 AutomationId,并只读取指定区域一次;maxDepth 默认 4、范围 1–50,maxNodes 默认 300、范围 1–5000。可选的 summaryAutomationId 先作一次状态观察:On 才读取区域,Off 返回 `detailsRequired:false`,缺失或未知停止。省略摘要时直接返回 `{checkedCount,unchecked,disabled}`;提供摘要且为 On 时返回 `{detailsRequired:true,details:{...}}`。勾选和启用是独立状态,禁用但 On 的控件仍计入 checkedCount。 - -每个明确选择的 ID 必须在完整区域结果中唯一对应一个 CheckBox,并有确定 On/Off 与 isEnabled;缺失、重名、错误控件类型或未知状态均停止,不输出部分统计。配方名、参数及终端配方请求拒绝未知字段,不接受源码、表达式、任意工具名或改换窗口。终端 timeoutMs 默认 15000,范围 1–30000;沿用已有步骤/数据预算,捕获模式固定 none。普通单工具请求仍保留原有超时和原生图像返回。 - -本轮 T2/T3 的脚本与模型基准复用 `ReadonlyUiRecipes.ts`,不再维护另一份测试专用统计程序。旧模型测量记录描述当时版本,代码调整后的性能需新测量才能确认。终端配方不需要模型 API key,不保存环境变量;结果文件可包含真实控件文字和节点证据,关闭不会自动删除,应按本地附件管理。 - -## 执行契约 - -- 只提供 `inspect` 和 `review`;始终添加固定 PID/HWND、`backgroundOnly:true`、`responseFormat:"compact"`。不接受 action、替换目标或任意工具名。 -- 即使程序使用 `Promise.all`,请求也按 FIFO 串行分发。默认最多 16 步,总 deadline 15 秒,最大 30 秒;后续调用继承剩余时间。 -- 单步输入不超过 64 KiB,累计中间文本不超过 512 KiB。最终文本默认 32 KiB,最大 128 KiB;最多一张原生图像,沿用 2 MiB 图像上限。 -- 同时检查 MCP `isError` 和业务 `success`;成功 JSON text 无需 `structuredContent`。歧义、未找到、不完整查询、属性读取失败、目标变化或传输异常停止工作流,不自动重试。 -- 条件判断读取真实响应。缺失或 unknown 状态不能按 false 处理。多个步骤是有序的独立观察,节点编号只属于各自请求,不是跨步骤原子快照。 -- 输出保留每步状态、requestId、可用的 capturedAt、节点状态与读取缺口、源码候选及其限制。`completed` 描述已完成读取;`stopped` 不丢弃之前完成的步骤。结果超预算显式返回错误与 `evidenceOmitted`,不报告完整成功。 -- screenshot 只经 MCP image 内容块传递。返回图像不等于宿主或模型已经查看图像。 -- 取消停止后续分发,并把 signal 传入标准 MCP 客户端。调用方负责关闭连接;helper 是否已退出必须结合 Gateway 状态及实际 PID 检查。模块不承诺任意不配合取消的第三方 caller 已结束执行。 - -## 基准方法与证据边界 - -| 任务 | 两条路径共同使用的最佳现有查询 | -|---|---| -| T1 | 单个已知 CheckBox,scoped/compact 一次读取 | -| T2 | 一次读取共同父区域,检查 8 个控件;不人为增加 8 次原生调用 | -| T3 | 读取摘要,根据明确 On/Off 状态决定是否读取详情 | -| T4 | 既有 `ui_review`,限定一个控件和明确 XAML 文件 | - -两条路径执行相同工具工作量、使用同一连接,交替先后顺序。固定 fixture 的预期结果独立定义:8 个控件中 7 个勾选,`hybridCheck3` 未勾选,`hybridCheck6` 禁用。T4 仅验证字面源码候选,保留 `runtimeSourceVerified:false`。 - -报告记录逐次耗时、调用数、中间/交付文本 UTF-8 字节数,以及按 nearest-rank 计算的 P50/P95。连接冷启动单独记录,helper 审计 start 数仅统计本次拥有的 fixture PID,不归因到单个样本。未安装 tokenizer,字节数不换算为 token。 - -`scriptedObservationBatches` 是脚本规定的结果交付次数;T3 可以从 2 次变为 1 次。未调用 LLM,所以 `measuredModelTokens`、`measuredModelDecisionRounds`、`measuredModelLatencyMs` 为 null。原生对照的交付量是各工具文本总量,混合对照是步骤证据和 findings 的最终文本量;两边都未包含最终模型回答、生成程序和规划成本。不能据此宣称真实任务 token 或端到端模型耗时达到规划阈值。 - -故障验证使用隔离 fixture:选择器歧义、查询不完整、控件状态改变、窗口关闭、真实 UIA provider 进入后的客户端取消和工作流 deadline,以及直接适配器的 native helper 超时。检查后续调用不再分发,完成证据保留,并确认自有 helper/Gateway PID 退出。背景模式不主动激活 fixture;前台采样未观察到变化也不保证没有瞬时变化。 - -第一轮默认保持客户端 PoC。只有接入实际模型/宿主后证明收益,并确认普通 MCP 客户端需要同类能力,才考虑共享执行层和服务器只读 batch。UIA 并行、discovery、写操作、持久化副作用恢复和通用 runtime 均未在本迭代开放。 - -## 本地验证结果(2026-10-06) - -Node 24.19.0、SDK 10.0.303:类型检查、Gateway 构建、fixture Release 发布通过;完整回归 467/467 通过,其中新增自动化测试 3 项。最终真实 UI 基准每任务每路径 20 次,共 160 个样本全部通过;7 类故障/状态场景通过,Gateway 和 6 个自有 fixture 的退出均由实际 PID 检查确认。审计观察到 211 次 helper start。前台采样未观察到 fixture 进入前台。 - -| 任务 | 原生 P50 / P95(ms) | 混合 P50 / P95(ms) | 平均交付文本字节减少 | -|---|---|---|---| -| T1 | 825.86 / 890.63 | 827.59 / 887.71 | 28.17% | -| T2 | 828.06 / 926.99 | 828.08 / 968.03 | 17.98% | -| T3 | 1641.38 / 1747.78 | 1646.75 / 1740.14 | 25.40% | -| T4 | 827.11 / 847.74 | 826.74 / 860.57 | 18.16% | - -两条路径的 UIA 调用数相同,中位工具耗时基本持平。T3 的脚本观察结果交付次数从 2 次变为 1 次,其他用例均为 1 次。这些结果验证了客户端编排的执行行为和输出投影;未证明真实模型 token 或完整任务耗时达到原规划的投入阈值,当前决策维持客户端 PoC。 - -第二轮增加会话级入口及 3 项行为测试:冷会话在客户端等待期间关闭;复用真实 Gateway 完成条件读取并交付原生截图;真实 UIA provider 阻塞期间关闭,保留首步证据并停止排队的第三次读取。最后一项确认 Gateway 和 native helper 的实际 PID 已退出,fixture 也显式回收。类型检查、构建及构建指纹核对通过;原有完整回归 467/467、新增会话行为测试 3/3 通过,测试清单共 58 个文件。这轮验证宿主接入与关闭行为,未运行 LLM A/B,第一轮模型指标仍为 null。 - -## 第三轮:真实模型与预置客户端工作流 A/B - -`benchmark:hybrid-model` 使用现有 Chat Completions 服务和隔离 Windows fixture,不启动另一个代理,也不安装依赖、修改客户端配置或新增服务。本轮验证 DeepSeek,请求使用其 `thinking:disabled` 扩展;其他提供方尚未验证。通过进程环境提供 `WINCODE_MODEL_BASE_URL`、`WINCODE_MODEL_NAME` 和 `WINCODE_MODEL_API_KEY`;凭据仅放入 HTTP Authorization,不写入报告或命令参数。端点使用 HTTPS,本地 API stub 可使用 loopback HTTP;禁止携带凭据的 URL 和重定向,不自动重试。缺少配置时在启动窗口前拒绝。模型调用/结果回填采用 [DeepSeek 官方工具调用协议](https://api-docs.deepseek.com/guides/tool_calls/)。 - -默认每任务每路径 20 次,共 160 个样本;显式传入 1–19 次仅作小样本验证。每个任务最多 8 次模型请求、120 秒,单次模型输出上限 2048 token,保持 `temperature:0`、`thinking:disabled`。每个样本使用新对话,交替两条路径先后顺序,共享已校验的连接和明确选择的 PID/HWND。正式运行期间保持基准源码稳定;报告记录脚本 SHA-256、Gateway 构建身份、请求模型名及各响应实际返回的模型/fingerprint。模型别名不保证固定权重版本。 - -- A:模型获得当前注册表中的 `wincode_ui_inspect`/`wincode_ui_review` 完整 schema,使用明确 scoped query、compact、合理预算和既有 review。宿主只允许固定目标、无截图及 fixture 的明确 XAML 候选文件,仍经过标准 MCP 和只读观察校验。 -- B:模型获得 `run_readonly_workflow` 私有基准入口及 T1–T4 的完整配方说明,选择已安装的匹配配方。可信 TypeScript 程序在内部完成相同查询、条件分支与统计,再返回步骤证据和 findings。该入口仅属于基准宿主;没有注册为 WinCode MCP 工具。 -- 两边任务提示给出相同的选择器、最佳查询和独立状态计数规则。预期值只在宿主断言中存在,不发给模型;最终 JSON 除值正确外,还必须有任务要求的真实读取证据。 - -模型发出同一轮多个工具调用时,宿主先执行一项观察,随后对每个尚未执行的 call ID 返回 `DEFERRED_AFTER_OBSERVATION` 和 `workStarted:false`。模型读取观察和延期回执后,自己重新决定后续请求;宿主不把旧计划自动加入队列。这是该基准的保守客户端策略,不能解释成服务器已支持整轮调度。 - -报告写入 `test-tmp/hybrid-model-/report.json`,逐次 JSON transcript 保留请求正文、模型响应、usage 和工具回执。实际 token 按提供方返回的每次 `prompt_tokens + completion_tokens` 累加,包含 schema、配方说明、重复历史、工具结果、最终回答和缓存命中的 prompt token;没有完整 usage 时返回 null,不从字节估算。`modelRequests` 包括最终回答及失败请求,`modelToolRounds` 只计带工具调用的完成响应;耗时统计覆盖整个任务,共同冷连接和窗口发现单列。 - -比较表只用成功样本计算 P50/P95,同时报告失败数量;任一侧未完成全部计划样本,`validComparison:false`,收益百分比为 null。每个样本后检查 helper 已无活动 PID,收尾观察 Gateway 和 fixture 实际退出。前台采样只描述本轮观察。报告、API 凭据和本机配置均不提交到仓库。 - -**解释边界:** B 测量的是预先开发好的领域工作流,包含运行时配方说明的上下文成本;没有测量模型生成程序、开发配方或生产宿主的完整成本。两边给模型的工具目录大小不同,token 收益包含 schema 减少与输出投影,不能全部归因为条件编排。该实验没有隔离这些因素,也未证明普通 MCP 客户端对 batch 的需求。结果用于判断是否继续客户端路线,不直接触发服务器 batch 或通用 runtime。 - -第三轮新增 3 项自动化契约/场景测试:真实本地 HTTP stub 下的 A/B 往返和 usage 计量;依赖调用延期及写操作/目标变化的分发前拒绝;缺失 usage、HTTP 错误与请求期间取消。后者验证不重试、不记录凭据及保留部分计量。 - -### 第三轮本地测量结果(2026-10-06) - -类型检查、构建、源码/产物指纹核对通过;完整自动化回归 470/470 通过,含本轮新增 3 项测试,清单共 59 个测试文件。真实测量使用 Node 22.23.1、`deepseek-flash`、非 thinking、temperature 0。每任务每路径 20 次,共 160 个样本和 345 次真实模型请求。混合路径 80/80 通过;原生路径 77/80 通过,T3 的第 10、16、19 次输出为自然语言,未满足约定的最终 JSON 格式。失败回合保留,不重跑覆盖;因此整个报告 `success:false`,T3 的 `validComparison:false`、收益百分比为 null。这是基准记录的模型输出契约失败,不能报告为 WinCode UIA 操作失败,也不能算成全部任务通过。 - -| 任务 | 通过数:原生 / 混合 | 平均实际总 token:原生 / 混合 | token 减少 | 原生 P50 / P95(ms) | 混合 P50 / P95(ms) | 中位耗时减少 | -|---|---|---|---|---|---|---| -| T1 | 20 / 20 | 5304.60 / 2353.80 | 55.63% | 2713.48 / 3277.36 | 2253.00 / 2534.53 | 16.97% | -| T2 | 20 / 20 | 6863.50 / 3721.00 | 45.79% | 2763.69 / 3176.00 | 2380.80 / 2802.57 | 13.85% | -| T3 | 17 / 20 | 11724.06 / 4219.40 | 不判定 | 4805.06 / 6778.73 | 3316.77 / 3700.01 | 不判定 | -| T4 | 20 / 20 | 5986.30 / 2891.15 | 51.70% | 2934.90 / 3169.75 | 2500.70 / 2754.69 | 14.79% | - -T3 原生行的均值/分位数仅描述 17 个成功样本;未包含 3 次失败的代价,不能用于达标或整体成功率推断。所有逐次记录均保留失败请求的 usage 和完整耗时。 - -T1、T2、T4 每条路径均为 1 次工具观察轮次、2 次模型请求(含最终回答);最佳原生 T2 已一次读取共同父区域,编排没有减少其轮次。T3 混合为 1 次工具观察轮次、2 次模型请求;原生成功样本平均为 2.12 次工具观察轮次、3.12 次模型请求。两条路径的底层调用仍分别为 1、1、2、1,共 200 次 MCP UI 调用。工具目录 JSON 为原生 6043 字节、混合 1802 字节,因此不能将 token 减少全部归因为少一次模型往返。 - -Native audit 观察到 201 次 helper start(含窗口发现)。独立的收尾检查用审计 helper PID/开始时间和当前 Windows 进程创建时间排除 PID 重用,201 次启动对应 186 个不同 PID,未发现残留。Gateway 和 fixture 实际退出,4458 次前台采样未观察到 fixture 进入前台。证据保存在本次 `test-tmp/hybrid-model-1791283297644/` 下的 report、逐次 transcript 和 `helper-exit-check.json`。 - -**当前决策:继续客户端领域工作流。** 完成的 T1/T2/T4 在本配置下达到 token 门槛,但 T2 未减少模型轮次,T3 原生路径存在输出契约失败,且普通 MCP 客户端对 batch 的需求未验证。实验支持保留并使用预置客户端配方;服务器只读 batch、discovery 和通用 runtime 的进入条件仍未满足。 - -## 第四轮:终端接入预置客户端配方(2026-10-06) - -增加参数化 `checkbox-audit` 配方及 `WinCodeSession.readonlyUiRecipe`,现有 JSON 会话通过 `action:"readonly-ui"` 使用同一实现。T2/T3 的两套基准共用此配方;保留标准 MCP、单工具协议、固定目标、步骤证据、取消和关闭行为。没有新增依赖、服务器工具、解释器或写操作。 - -本轮新增 3 项行为测试:明确控件集合与条件分支的完整性;真实终端会话的执行前拒绝、配方及原生调用复用;真实 UIA 阻塞后的取消/关闭和进程回收。配方接入后的完整回归 471/471 通过,清单仍为 59 个测试文件。首次回归与构建并行且未采用项目 SDK,曾因暂缺 build-manifest 与系统 10.0.302 失败;最终回归在构建完成后通过 `scripts/lib/dotnet.mjs` 验证并使用本地 10.0.303 SDK,未调整 global.json 或安装 SDK。 - -短程基准还复现了已有总 deadline 分类竞争:MCP 先抛出 `Request timed out`、而总截止时间已过且工作流 timer 尚未处理时,原来误报 WORKFLOW_ERROR。现按实际截止时间统一返回 DEADLINE_EXCEEDED,并在原有预算测试中加入可控时钟场景,保留完成证据、停止后续分发且不重试;基准在断言前保存实际超时结果便于核查。 - -该修正后的最终复核:类型检查及构建通过;工作流与模型 HTTP 契约测试 7/7,通过真实 Windows 会话测试 5/5;`benchmark:hybrid -- 1` 的 8 个正常路径样本及 7 类故障/状态场景全部通过,审计观察到 21 次 helper start,实际 helper/Gateway 退出检查通过。最终短程报告位于 `test-tmp/hybrid-ui-1791285314223/report.json`,回归及会话日志位于 `test-tmp/hybrid-recipe-checks/`。短程检查不作为性能测量,本轮没有请求真实模型,也没有更新第三轮 token 或缓存结论。 - -## 第五轮:错误模型回复的测试先行验收(2026-10-06) - -本轮只调整模型基准宿主 `scripts/lib/hybrid-model.ts`,不改变原生 MCP 工具、客户端只读配方、Gateway 或 UIA 生命周期。新增 3 项自动化测试,固定回复矩阵共 24 个场景,先运行测试再修改实现;不使用真实模型 API 或个人凭据。 - -| 测试 | 实现修改前 | 修复后 | -|---|---|---| -| 被拒绝或部分失败的观察,随后收到碰巧正确的最终答案 | 失败;12 个矩阵场景中 10 个被误判成功 | 通过;不能用拒绝证据满足任务要求 | -| 响应标记截断/中断或与工具调用不一致,但参数看似可执行 | 失败;10 个矩阵场景都启动了读取 | 通过;0 次 MCP 分发,保留已收到的 usage | -| 工具文字诱导模型换目标或调用写操作,固定回复故意照做 | 已通过;两条路径均在分发前拒绝 | 保留回归,无需为制造红灯修改守卫 | - -红灯基线为 `e798f991126968a71216e6bc994bf3b535d5f01e`。`test-tmp/hybrid-tdd-checks/` 保存 red.log、baseline.json、原始实现与测试快照、green.log、完整回归和真实窗口回放记录。首次运行结果为 3 项测试中 2 项失败、1 项通过,随后修复后该文件共 6 项测试全部通过;测试场景与预期不变,类型检查期间仅补充 MCP text 内容块的类型断言。 - -修复后的有效观察登记以通过校验的完整工作流报告为依据,并读取实际节点 AutomationId;底层响应自报 success 不能直接成为最终答案的证据。配方整体失败时不登记部分步骤作为成功配方证据,步骤报告本身仍保留。带工具调用的模型响应必须以 `finish_reason:"tool_calls"` 完整结束,其他完成原因在分发前拒绝;无工具调用的最终回答仍要求 stop。此约束依据 [DeepSeek Chat Completions 官方完成原因定义](https://api-docs.deepseek.com/api/create-chat-completion/)。 - -真实窗口验收使用隔离 WPF 的实际只读响应,在客户端注入歧义或诱导文字,再以固定回复回放模型犯错的动作;不声称夹具本身产生了恶意文字,也不测量真实模型受诱导的概率。原生/混合路径各 3 个场景,共 6 个全部通过:截断回复 0 次 UI 分发,诱导出的写操作及目标变化均 workStarted=false。4 次合法实际读取的 helper、Gateway 与夹具窗口均已确认退出;本地回复不提供 usage,避免将模拟数字冒充实测 token。 - -类型检查与构建通过。第一次完整回归 473/474,已有 lifecycle-cancellation 测试在默认并发下超过 1 秒子进程退出等待;该文件单独复核 14/14。未修改无关代码或放宽断言,最终采用已验证的项目 SDK 10.0.303、测试文件并发数 4,完整回归 474/474。首次失败日志保留,不能声称默认并发首轮全通过。 - -本轮验证错误执行的拒绝与实验结果可信性,不证明所有第三方客户端具备相同守卫,也不更新第三轮真实模型通过率、token 或缓存收益。原始日志可能包含本机路径,所有实验附件保持 Git 忽略,不自动视为匿名分享材料。 - -## 第六轮:条件分支、失败成本与公开摘要 - -本轮新增 3 项自动化测试:模型宿主的 On/Off 分支;含失败、缺失 usage 和未完成计划的报告聚合;公开摘要字段白名单。首次实现修改前 3 项均失败;补齐实现后模型测试文件 9/9。真实 WPF 配置分别固定为 On/Off,两条路径共 4 个场景通过:On 各读取 2 次,Off 各读取 1 次;6 个实际读取 helper、Gateway 与 fixture 均确认退出。该窗口验收使用固定模型回复,没有模型 API 请求。 - -模型任务依据被接受的实际摘要状态确定所需证据,Off 不要求详细区域。配方继续复用既有实现,原生路径的多余详细请求在分发前拒绝。Off 使用启动参数 `--hybrid-summary-off`,不通过 UI 写操作改变场景;默认 On 及原有 fixture 路径保留。 - -模型报告升级为 `formatVersion:2`、`acceptanceVersion:2`,新增独立的 `public-summary.json`: - -- 原始 `report.json` 和 transcript 继续留在本地,包含诊断身份与响应,不作为匿名附件。 -- 成功样本均值/分位数与 `attempts` 分开;后者包含失败样本的耗时、请求和 MCP 调用数。 -- `totalTokens` 缺少任何样本的完整 usage 时为 null;`knownTotalTokens` 仅累加有完整 usage 的样本,并列缺失样本数,不能当作真实总消耗。 -- 缓存命中来自实际 `prompt_cache_hit_tokens`,未命中按输入 token 减命中 token 计算;任一回合缺失或字段不合法,该样本缓存计量为未知。不估算服务费用。 -- 任一路径不足计划样本、有失败,或执行/清理/源码一致性检查失败,该任务不生成收益百分比。失败原因在公开摘要中只输出有限类别,未知文本归为 OTHER。 -- 补充工作区是否有变更和配方、工作流、fixture 等实际依赖的源码指纹;构建身份仍来自实际 Gateway。 - -旧 160 个样本的只读复核发现 3 次非 JSON 最终回答、0 次错误工具完成标记;全部没有独立原始 MCP 响应流。该复核只检查保存的协议和输出语法,不是新版执行重放,也不能重新测量 token 或耗时。 - -### 第六轮真实模型测量与最终验证(2026-10-06) - -类型检查、Gateway 构建及 WPF fixture Release 发布通过。完整回归在项目 SDK 10.0.303、文件并发 4 下为 477/477;随后补充 Off 的名称/根节点绕过场景,确认红灯并修复,最终回归仍为 477/477。新增测试数量保持 3 项,模型文件最终 9/9。真实窗口最终复核 4/4,确认 Off 后按名称请求详细区域也不会分发;完成的读取 helper 与自有窗口/Gateway 均已退出。 - -真实模型沿用 `deepseek-flash`、temperature 0、非 thinking 和原提示。On 连通性检查 8/8,不并入正式测量。正式 On 每任务每路径 20 次,共 160 个样本、359 次请求、941412 个实际总 token(含失败及缓存命中输入):混合 80/80,原生 61/80。原生 T3 仅 1/20,其余 19 次最终 JSON 语法失败,报告整体 success=false。 +- checkboxAutomationIds:1–64 个互不重复的 ID。每个目标必须在指定区域内唯一,为 CheckBox,isEnabled 已知,toggle 为 On 或 Off。 +- summaryAutomationId 可省略。提供时先读摘要:On 才读详情,Off 返回 detailsRequired:false;未知或缺失停止。 +- scopePath 可省略;提供时包含 1–50 个仅使用 automationId/name/controlType 的精确父选择器,摘要和详情共用该祖先范围。每次读取重新解析,不复用摘要的定位对象。不提供路径时沿用原查询范围和调用数。 +- maxDepth 默认 4,允许 1–50;maxNodes 默认 300,允许 1–5000。详情区域一次读取后在客户端统计,禁用且 On 的控件仍计入 checkedCount。 +- 无摘要时返回 checkedCount、unchecked、disabled;摘要 On 时返回 detailsRequired:true 和 details。字段形状见 [ReadonlyUiRecipes](../src/Client/ReadonlyUiRecipes.ts)。 -| 任务 | 原生/混合通过数 | 平均总 token 减少 | 端到端 P50 减少 | -|---|---|---|---| -| T1 | 20/20 / 20/20 | 55.65% | 19.34% | -| T2 | 20/20 / 20/20 | 45.82% | 15.23% | -| T3 | 1/20 / 20/20 | 不判定 | 不判定 | -| T4 | 20/20 / 20/20 | 51.87% | 19.62% | +非法配方参数在连接/读取前拒绝,CLI 保留 requestError,并提供 errorCode、field、errorMessage、recoveryAction、workStarted:false 等信息。调用方可根据反馈构造更正请求;CLI 不自动更正或重试。传输失败/动作结果未知不能一律解释为未执行。 -T3 原生全部尝试的 79 次请求、40 次底层读取、313898 个 token 已进入 attempts,而非只展示一个成功样本。其成功样本分位数仅有 1 个样本,不能代表性能。其他任务的收益仍包含 schema 和投影差异,缓存未受控,不能全部归因于编排。 +带路径的读取要求 inspectionVersion 5,并确认 scopeResult 已解析全部父级;旧版本返回 VERSION_MISMATCH,缺少范围证据停止为 INCOMPLETE_OBSERVATION。原生 SCOPE_NOT_FOUND/SCOPE_AMBIGUOUS/SCOPE_SEARCH_INCOMPLETE 原样保留,步骤证据包含 scopeResult,不回退整窗。路径不会自动展开隐藏区域,也不裁剪窗口截图;摘要和详情没有合适共同祖先时使用显式单次读取。 -正式报告确认执行期间 9 个依赖源码指纹未变化;审计观察到 201 次 helper start、188 个不同 helper PID,独立退出检查无残留。Gateway 与 fixture 退出通过,4776 次采样未观察到 fixture 进入前台;采样不是不抢前台的绝对保证。 +checkbox-audit 允许 className、bounds、isOffscreen 的辅助属性缺口,仍在步骤证据中保留原始 propertyIssues 和数量;不因这些缺口丢弃已观察到的状态。身份、启用状态、toggle、搜索/遍历完整性、歧义和未分类缺口仍按原规则判断。可信 TypeScript 调用方可在 reader.inspect 中显式设置 allowAuxiliaryPropertyGaps:true 使用同一规则;此选项默认关闭,只在客户端解释,不发送给 MCP,不属于 JSON 配方参数。reader.review 的严格行为不变。 -正式 On 测量在 Off 的其他选择器拒绝及针对性错误提示补充前完成。其真实源码保存在本地 formal-source-snapshot,报告保留实际指纹和当时工作区有变更的状态;不能把最终提交内容冒充测量时的源码。后续修改在最终固定回复、真实窗口、完整回归及独立 Off 模型检查中验证,未重新执行完整 On 测量。 +## 同级标题之间的条目 -最终版本的独立 Off 连通性检查 6/8:两条 T3 路径均只有 1 次底层摘要读取,但最终回答都不是合法 JSON。该小样本报告保持 success=false,不用于性能结论。它进一步确认需要验证最终格式纠错反馈,不能声称混合路径在所有状态下都遵守最终输出契约。 +适用于标题和条目在 UIA 中处于同一层的列表。先通过局部 inspect 和必要截图确认容器、条目顺序,以及两个连续分组标题;配方不自动猜测哪一行是标题。以下仍为占位示例: -本地证据:On 正式测量位于 `test-tmp/hybrid-model-1791288472632/`,独立 Off 检查位于 `test-tmp/hybrid-model-1791289207443/`;红灯、最终回归、源码快照、窗口检查、旧记录复核及隐私审计位于 `test-tmp/hybrid-sixth-checks/`。这些路径是本地记录索引,不是公开附件。 - -启动模型检查时,系统 Python 曾缺少 dotenv;随后使用已存在的 Hermes 环境,未安装依赖,首次失败日志保留。本轮没有为改善模型通过率追加格式恢复或重跑覆盖失败。当前判断继续客户端方案,先处理有预算且公平的最终格式纠错,服务器 batch 的进入条件仍未满足。 - -## 第七轮:一次有预算的最终格式纠错(2026-10-06) - -改动限于实验宿主、报告及基准入口,不改变标准 MCP 或生产客户端配方。原生与混合路径默认采用相同规则:最终回合完整结束、已有全部被接受的任务观察,但内容不是 JSON 对象时,向模型返回固定 `INVALID_FINAL_JSON` 提示,要求只修正格式、复用已有观察、禁止工具调用。最多一次,沿用原来的 8 次总请求、120 秒总时限、上下文及输出预算,不另开样本,不提供正确计数或期望答案。 - -错误事实、缺失/不完整观察、未知或与固定夹具场景不符的摘要状态、截断回复不进入格式纠错。第 8 次请求才产生格式错误时以 `FORMAT_CORRECTION_BUDGET_EXHAUSTED` 结束;修正仍不合法、企图调用工具、HTTP 错误或取消也结束,不再重试。工具调用在分发前以 `FORMAT_CORRECTION_TOOL_CALL` 拒绝,因此修正回合没有新的 UI 读取。 - -逐次 `formatCorrection` 保留首次格式失败、此前请求/耗时/usage、是否提供纠错机会、是否恢复及新增请求/耗时/usage。全部尝试成本已经包含纠错,不能再次相加。HTTP 请求失败且无 usage 时,完整总量与纠错 usage 为 null,已知的纠错前 usage 仍保留;不能将缺失计为零。公开摘要仍是固定字段白名单,分别列首次通过、初始格式失败、提供纠错、恢复及纠错成本,不公开原始回复。`formatVersion:2` 保留,验收规则升级为 `acceptanceVersion:3`。 - -基准入口新增可选的纠错开关和 T3 选择器;原有只传重复数/场景的命令仍可用,默认纠错开启、全部任务: - -```sh -npm run benchmark:hybrid-model -- 5 summary-on repair-off T3 -npm run benchmark:hybrid-model -- 5 summary-off repair-on T3 -npm run benchmark:hybrid-model -- 5 summary-on repair-on T3 -npm run benchmark:hybrid-model -- 5 summary-off repair-off T3 +```json +{"id":"group1","action":"readonly-ui","recipe":"sibling-range","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"name":"Library","controlType":"Document"}],"containerQuery":{"name":"grid","controlType":"Table","maxSearchNodes":500},"startAfter":{"name":"2024","controlType":"DataItem"},"endBefore":{"name":"2023","controlType":"DataItem"},"maxDepth":4,"maxNodes":160},"timeoutMs":15000} ``` -本轮新增自动化测试保持 3 项:On/Off 两条路径的格式恢复与计量;证据、完成标记、错误事实、预算、二次格式失败、纠错工具调用、HTTP/取消及禁用纠错的拒绝矩阵;公开汇总的首次失败与纠错成本。实现修改前 3 项均失败。进一步在第二项中补测发现,只有节点身份而摘要状态未知时仍可能进入纠错,确认红灯后增加实际状态验收。最终模型文件 12/12,类型检查、构建通过;项目 SDK 10.0.303、文件并发 4 下完整回归 480/480。 - -真实 WPF 验收使用实际 UIA 读取和本地固定模型回复,4/4 场景恢复成功:On 两条路径各读 2 次,Off 各读 1 次;纠错各新增 1 次模型请求,未增加读取。6 个读取 helper 及自有 Gateway/fixture 均确认退出;本地回复不提供 usage,没有将模拟数字作为实测 token。 - -真实模型对照仅限 T3:On/Off × 纠错关/开,每组每路径 5 次,共 40 个样本。两条路径交替先后顺序,各样本使用新对话;各组顺序执行,缓存和提供方模型版本未受控,因此只能描述此次小样本及实际同一对话的恢复,不能推断普遍成功率或因果性能优势。第六轮的 160 个样本不与这些新样本合并。 - -### 第七轮真实模型结果与记录 - -沿用已授权的 `deepseek-flash`、temperature 0、非 thinking。40 个样本共 99 次 API 请求、60 次底层 UI 读取、267120 个实际总 token,包含失败和纠错成本,所有样本 usage 完整: - -| 场景/纠错 | 原生首次/最终通过 | 混合首次/最终通过 | 实际总 token:原生/混合 | 新增纠错请求/token:原生/混合 | -|---|---|---|---|---| -| On/关 | 4/5 / 4/5 | 5/5 / 5/5 | 60530 / 21077 | 0/0 / 0/0 | -| Off/开 | 0/5 / 5/5 | 4/5 / 5/5 | 43248 / 14403 | 5/15909 / 1/1639 | -| On/开 | 4/5 / 5/5 | 5/5 / 5/5 | 66766 / 21035 | 1/6329 / 0/0 | -| Off/关 | 0/5 / 0/5 | 4/5 / 4/5 | 27324 / 12737 | 0/0 / 0/0 | - -开启纠错的 20 个样本共 7 次初始格式失败,全部在原对话中恢复:新增 7 次请求、23877 个 token,合计新增耗时 4583.00 ms。对逐次响应核查,7 次修正都只有一个最终回复,没有工具调用;On/Off 的两条路径每样本底层读取仍分别为 2/1。关闭纠错的两组共有 7 次最终失败,均为 `INVALID_FINAL_JSON`,整体 success=false、任务比较资格 false,收益百分比为 null;原始失败保留。 +containerQuery 使用标准 UiQuery;startAfter/endBefore 只接受 automationId、name、controlType 的精确条件,至少一个。两标题必须在容器子树内分别唯一、拥有同一个直接父节点且顺序正确。配方只读取一次,不含两个标题,保留中间所有同级条目的原始顺序,不将条目的子文本重复计数。maxDepth/maxNodes 的默认值及范围与 checkbox-audit 相同。 -开启纠错的两组各为 10/10,报告比较资格通过;这只是每路径 5 次的小样本。纠错的上下文重复成本已进入总 token,不能只用恢复后答案较短来宣称便宜。此次数据支持在实验宿主中保留一次格式纠错;不更新第六轮正式测量的结论,也不证明普遍通过率、缓存收益或服务器 batch 的需求。 +findings 返回 kind:logical-group、basis:sibling-order、container、parent、startAfter、endBefore、observedCount 与 items。节点保留 id、parentId、name、可用的 automationId/controlType 和 propertyIssues;requestId 对应本次取证,节点 ID 不可跨请求用于定位。coverage 固定为 observed-range-only、businessGroupComplete 固定为 unknown:读取的是提供方当前暴露的区间,不能据此宣称完整业务清单;零条目也不证明分组没有隐藏内容。 -四组执行期间 9 个依赖源码指纹均未改变。审计观察到 64 次 helper start,逐组独立检查确认无残留;Gateway 与 fixture 实际退出。合计 1401 次前台采样未观察到 fixture 进入前台,不能解释为绝对保证。 +名称和边界选择器所需属性必须可读;没有 AutomationId 或状态模式不妨碍按名称读取。此配方使用客户端 reader.inspect 的 allowPropertyGaps:true,自行检查必要属性,原始缺口仍保存在步骤证据。该选项不会发送给 MCP,也不允许出现在 JSON 配方参数中;默认读取、review、checkbox-audit 均沿用原有检查。搜索、父范围、遍历完整性和目标身份检查不放宽。 -本地记录依执行顺序位于 `test-tmp/hybrid-model-1791290224985/`、`hybrid-model-1791290289640/`、`hybrid-model-1791290370526/`、`hybrid-model-1791290428123/`。红/绿灯、回归、真实窗口、源码快照、逐组汇总及凭据审计位于 `test-tmp/hybrid-seventh-checks/`。这些记录均保持 Git 忽略,原始报告不是匿名附件。 +标题缺失、歧义、不在同一父节点、顺序错误、条目名称不可读或取证截断时,工作流停止且无成功 findings,已观察部分保留在 steps.evidence;不能将其解释为空组。配方不滚动、不展开、不操作条目,也不从视觉加减号伪造原生展开状态。 -## 第八轮:模型提交参数与有限参数更正(2026-10-06) +## 有限展开与读回 -现有 `checkbox-audit` 继续执行统计,新实验配置 `parameterized` 给模型展示该配方 schema,让模型实际提交 `parameters`。宿主校验区域、条件和明确控件集合与任务一致,再将模型参数交给既有配方;集合允许顺序变化,不能省略或替换成员。旧 T1–T4 配置保留。新配置仍是“明确范围的已知配方参数构造”,没有测量自由发现控件或开放式配方选择。 - -共享 `RecipeInputError` 在配方校验处产生固定 `INVALID_RECIPE_PARAMETERS`/`UNSUPPORTED_RECIPE`、合法字段路径、提示及 `recoveryAction:revise_parameters`。CLI 在原有 `requestError` 之外增加 `errorCode`、`field`、`errorMessage`、`recoveryAction`、`workStarted:false`,保留 `wincode-skill-session/1`。这些已知错误在执行之前产生,提示不回显拒绝值或任意键名;通用传输错误未被冒充为未执行。 - -参数错误在实验中最多允许一次更正,两条路径共享该规则及原 8 次请求/120 秒总预算。第一次拒绝之后,同一轮其他 call ID 只收到延期回执;模型必须先看到错误,再在新回合提交更正。重复无效输入结束样本;范围漂移、失败/未知观察、HTTP 错误、取消及超时不进入参数恢复。第七轮最多一次的最终格式修正继续生效,禁止新读取,两种恢复不能增加总预算。 - -P1 读取指定 3 个控件,独立预期为 checkedCount=2;P2 在摘要 On 后读取另外指定的 5 个控件,预期为 4;P3 使用相同 5 个控件范围而摘要 Off,预期跳过详情。三者分别每路径需要 1/2/1 次底层读取,原生也允许一次读共同父区域。模型提示不含正确计数,期望值仅用于宿主与测试。 - -参数化模式额外核查已有观察是否支持固定场景事实:未知选定状态、缺失/重复控件和不匹配状态不能由猜中的答案掩盖。原生对已读区域复用配方校验,使用不分发 MCP 的只读适配器;混合核对已完成配方 findings。失败的实际观察记录保留,不重新读取或自动删控件。规则限于已知合成场景的实验验收,不声称可以给任意生产 UI 预先指定正确状态。 - -报告验收版本为 4,格式版本仍为 2。新增 `parameterValidation` 记录实际提交/接受/拒绝调用及首次接受情况;`parameterCorrection` 保留初始失败和更正阶段成本。公开摘要分别列参数更正、最终格式更正和完整任务通过。顶层 `firstPassPassed` 表示完整任务没有任何一种更正;既有 `formatCorrection.firstPassPassed` 继续只描述没有最终格式更正。参数已更正但最后任务失败时,只算参数阶段恢复,不能算完整任务成功;两种阶段成本已包含在完整尝试中,不重复相加。 - -```sh -# 6 个独立连通性样本:On 的 P1/P2 共 4 个,Off 的 P3 共 2 个 -npm run benchmark:hybrid-model -- 1 summary-on repair-on parameterized -npm run benchmark:hybrid-model -- 1 summary-off repair-on parameterized -# 30 个独立小样本,不与连通性或旧测量合并 -npm run benchmark:hybrid-model -- 5 summary-on repair-on parameterized -npm run benchmark:hybrid-model -- 5 summary-off repair-on parameterized +```json +{"id":"read1","action":"expand-ui","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"automationId":"speechPage"}],"parentQuery":{"automationId":"advanced"},"childQuery":{"automationId":"normalize"}},"timeoutMs":15000} ``` -新增自动化测试保持 3 项:共享参数错误契约;模型实际提交参数、更正与证据守卫;真实 JSON 会话的冷状态拒绝、更正子集和连接复用。实现修改前 3 项均失败;后续在同一个模型测试中补测并确认原生 unknown、实际状态与场景不符的验收缺口,再修复。相关工作流/模型测试 18/18,真实会话文件 6/6,类型检查与构建通过。完整回归在项目 SDK 10.0.303、文件并发 4 下为 482/482;真实会话文件不在该主回归清单中,单独运行。回归完成后的试运行暴露调用说明问题,仅调整参数化提示并再次通过相关 18 项及类型检查,没有再次完整回归。 - -最终补齐同一项新会话测试的实际条件子集:错误参数保持 cold,P1 子集读取 1 次,P2 子集先摘要后详情读取 2 次,得到独立预期的 2/4;连接复用,3 个审计读取 helper、CLI 与 Gateway 均确认退出,fixture 收尾也检查退出。该更新后的测试单独通过;新增自动化测试仍为 3 项,没有将固定回复计量冒充真实 API token。 - -### 第八轮真实模型结果与明确限制 - -两组连通性检查共 6 个样本,3/6 通过。最初 On 组 P1 两侧通过,P2 两侧提交错误范围,均在 0 次读取时拒绝。检查实际请求后,明确了两条路径的 scoped query 以及混合配方的区域/摘要参数角色;没有提供状态或正确计数。该调整前的 4 个样本和源码快照保留,不并入后续新提示的小样本。调整后的 Off 试运行为 1/2,原生观察 Off 后仍请求详情被拒绝;服务连通与清理正常,任务失败单独计数。 - -随后按计划执行 30 个独立样本,没有增加次数或重跑失败: - -| 场景 | 原生首次/最终通过 | 混合首次/最终通过 | 全部尝试 token:原生/混合 | 实际失败 | -|---|---|---|---|---| -| P1:无条件 3 控件子集 | 5/5 / 5/5 | 2/5 / 2/5 | 34725 / 10291 | 混合 3 次无观察 | -| P2:On 后 5 控件子集 | 0/5 / 5/5 | 0/5 / 0/5 | 86831 / 4895 | 混合 5 次无观察;原生 5 次最终格式失败后恢复 | -| P3:Off 跳过详情 | 0/5 / 2/5 | 1/5 / 1/5 | 35028 / 6544 | 原生 3 次在 Off 后请求详情;混合 4 次无观察;原生 2 次最终格式恢复 | - -原生最终 12/15、混合 3/15。三种场景都为 `validComparison:false`,收益百分比为 null,报告整体 success=false。混合失败的 12 个实际最终响应均为 `{recipe,parameters}` 对象:模型打印了参数,没有产生 function tool call。宿主没有把这个对象解释成程序或自动执行,全部以 `FINAL_WITHOUT_OBSERVATION` 拒绝。参数生成与实际工具调用的遵从性还未满足使用要求;不能因打印出的参数看似正确就算执行成功。 - -30 个样本共 60 次真实 API 请求、23 次底层读取、178314 个实际总 token,所有样本 usage 完整。原生 7 次最终格式修正恢复,新增 7 次请求、35705 个 token、合计 6092.28 ms,没有重新读取。没有发生符合一次参数更正规则的输入错误:参数恢复机会数为 0,真实参数恢复率未知;试运行的范围错误和 Off 后详情请求属于终止条件,不改算为可恢复输入错误。 - -6 个试运行另外消耗 10 次请求、22175 个 token;全部 36 个样本合计 70 次请求、27 次底层读取、200489 个 token。失败样本的成本已包含,混合路径较低的尝试 token 包含大量未执行任务,不能解释为性能收益。 - -四个运行组分别确认执行期间 9 个实际依赖源码指纹稳定;合计审计 31 次 helper start,独立进程检查无残留,Gateway/fixture 实际退出。979 次前台采样未观察到 fixture 进入前台。最初 On 试运行与后续三组的提示源码不同,各自保留实际版本,不能说全部 36 个样本使用同一配置。 - -本地试运行:`test-tmp/hybrid-model-1791291738490/`、`hybrid-model-1791291892148/`;独立小样本:`hybrid-model-1791291942078/`、`hybrid-model-1791292027647/`。红灯、补测红灯、实现/测试快照、初始与小样本源码快照、回归、真实子集会话和凭据审计保存在 `test-tmp/hybrid-eighth-checks/`,均不提交。 - -**本轮判断:参数化客户端入口与拒绝/更正规则已经实现并通过确定性和真实会话验证;当前模型配置的参数化调用没有通过整体验收。** 保留实验分支和原生入口,不据此推进服务器 batch 或非 fixture 模型迁移。随后需针对“打印参数而未调用工具”的实际失败修正调用说明并独立验证,不能放宽无观察验收、自动执行最终文本、继续追加样本或宣称参数纠错率已证明。 - -## 第九轮:明确工具调用与最终结果的契约(2026-10-06) - -配置 `call-then-findings-v1` 针对第八轮已观察到的失败:模型构造参数后直接输出,未调用工具。参数化任务的两条路径共同明确先发出 function tool call、等待工具结果、再用观察值返回合法 JSON;混合工具说明和任务尾部明确调用 `run_readonly_workflow`,配方参数仅为工具输入。最终结果不能是参数对象、代码或占位值。提示只含任务范围、执行方式和结果结构,不含正确状态/数量。旧 T1–T4 提示保持原样。 - -API 字段、默认工具选择、标准 MCP、生产客户端配方、8 次请求/120 秒预算、一次参数更正与一次最终格式更正均未改变。没有强制工具选择、无观察失败后追加反馈或把最终 JSON 自动解释为工具调用。验收版本仍为 4,报告新增固定 `promptPolicy` 标识区分配置,并保留实际源码指纹。 - -新增自动化测试 1 项,覆盖 P1–P3 两条路径:打印参数时零读取、一次模型请求后失败,不进入参数/格式恢复;真正读取后把参数作为结果返回也失败。测试最初红灯来自缺少明确执行提示,旧无观察守卫已正确拒绝,不能把它写成新修复的执行漏洞。相关模型/工作流 19/19 和类型检查通过。真实模型仍执行实际 WPF/UIA,不用固定回复代替。 - -执行前固定最多 6 个试运行加 30 个独立小样本。试运行验证混合实际调用、连通和清理后才进入小样本;不因任务失败重跑,也不再调整提示。每个样本新对话,小样本按重复交替 A/B 顺序并轮换 On 场景顺序。四组使用同一实现,各组执行期间 9 个依赖源码指纹稳定。沿用既有授权模型、temperature 0、非 thinking 和既有 scoped query;失败成本完整保留。 - -### 第九轮真实模型结果 - -试运行 4/6:混合 3/3、原生 1/3。原生 P2 正确读取摘要后将区域 AutomationId 放在参数顶层而不是 `query`,以 `TASK_SCOPE_MISMATCH` 拒绝,没有区域读取;原生 P3 读取 Off 后仍请求详情,以 `DEPENDENT_READ_NOT_REQUIRED` 拒绝。两者各只读取摘要 1 次,未改为可恢复参数错误。这些任务失败不属于提供方/清理失败,保留原记录后按预定规则继续小样本。 - -| 场景 | 原生首次/最终通过 | 混合首次/最终通过 | 全部尝试 token:原生/混合 | 格式修正次数:原生/混合 | 各路径实际 UI 读取 | -|---|---|---|---|---|---| -| P1:无条件 3 控件子集 | 3/5 / 5/5 | 5/5 / 5/5 | 44995 / 19983 | 2 / 0 | 5 | -| P2:On 后 5 控件子集 | 0/5 / 5/5 | 5/5 / 5/5 | 87111 / 22712 | 5 / 0 | 10 | -| P3:Off 跳过详情 | 0/5 / 5/5 | 5/5 / 5/5 | 44953 / 14401 | 5 / 0 | 5 | - -30 个独立样本最终 30/30;原生首次 3/15、最终 15/15,混合首次和最终均为 15/15。原生共 47 次模型请求、177059 个总 token,混合 30 次请求、57096 个总 token;两边底层 UI 读取均为 20。P2 混合一次工具调用在客户端按观察顺序执行两次读取,原生需要两轮模型工具调用;两边没有跳过必要证据。 - -原生 12 次最终格式错误全部通过原对话的一次反馈恢复,新增 12 次请求、55293 个 token、合计 9537.78 ms,没有新增 UI 读取;这些成本已包含在总量中。没有自然发生符合参数更正规则的错误,参数更正机会仍为 0,不能据此宣布真实参数恢复率提高。 +parentQuery 与 candidateQuery 只能提供一个,也可均不提供以有限发现 Collapsed Group。candidateQuery 只能使用返回候选的实际名称/ID/Group 类型。多个候选时先返回 selection-required,由调用方根据任务选择;不默认操作第一个。 -三种小样本比较资格均为 true,两份小样本报告 success=true;各场景实际总 token 降低约 55.6%/73.9%/68.0%,中位耗时降低约 15.6%/39.8%/35.1%。这些是当前每路径 5 次、含最终格式修正成本的描述数据;缓存、组别顺序与提供方返回版本未受控,不能解释为稳定门槛、普遍收益或提示的单独因果效果。第八轮失败及其收益 null 原样保留,不与本轮合并或重新计算。 +scopePath 由实际祖先选择器组成,每项使用 automationId、name、controlType 的组合,1–50 项;逐项搜索严格后代并要求完整唯一,不自动展开祖先。最终父级与路径末项完全一致时复用该根。inspectionVersion 5 才支持父路径;当前 click/type 不支持路径。 -逐次核查本轮所有混合回复:18/18 在首次请求产生真实 function tool call,最终响应打印 `{recipe,parameters}` 的次数为 0。该失败在本轮没有复现;这是有限场景下的实际改善证据,不能声称模型不会再犯同类错误。两组试运行的比较失败仍保留,不能将全部 36 个样本描述成全部通过。 +可见目标直接读取,父级已经 Expanded 时不重复展开;Collapsed 时设置为 Expanded,再核对父子关系并独立读回。completed 才表示取得最终 findings;stopped 可保留已成功动作和局部观察,不能把导航动作成功当作任务完成。局部内层候选只用于调用方决定后续请求,本请求不递归。 -全部 36 个样本合计 34/36、89 次 API 请求、47 次底层读取、264356 个实际总 token,usage 均完整。试运行单独为 12 次请求、30201 个 token;不混入 30 个小样本的成功率。四组审计 helper start 分别为 6/3/31/11,共 51 次,独立进程检查无残留;自有 Gateway 和 fixture 实际退出。1274 次前台采样未观察到 fixture 进入前台,该采样不构成绝对前台保证。 +范围缺失/歧义/不完整不回退整窗。动作失败或结果未知先保留事实,不自动重试;后台语义操作也可能因目标应用事件带来前台变化。完整授权和能力边界以 [UI 手册](../skills/wincode/references/ui.md#操作方式与授权)为准。 -本地试运行记录为 `test-tmp/hybrid-model-1791292998362/`、`hybrid-model-1791293033353/`;小样本为 `hybrid-model-1791293065996/`、`hybrid-model-1791293169659/`。基线、红灯、相关测试、回归、模型汇总及隐私检查在 `test-tmp/hybrid-ninth-checks/`,保持 Git 忽略。公开报告没有原始参数/响应、端点、目标进程身份或凭据。 +## 验证入口与边界 -本轮达到有限参数化场景的调用与结果验证边界;非 fixture 应用迁移仍未验证。本轮不增加配方或服务器 batch,也不以这组小样本覆盖历史失败。 +类型检查用 npm run typecheck;核心与桌面检查分别为 npm run check、npm run check:desktop;磁盘交付身份用 npm run delivery:verify。运行条件和报告位置见 [CONTRIBUTING](../CONTRIBUTING.md)。检查会构建或启动相应测试进程,不因阅读本页就执行。 -本轮完整主回归只运行一次,在项目 SDK 10.0.303、文件并发 4 下 483/483 通过;该清单不包含真实 JSON 会话文件,本轮没有重复运行未修改的会话测试。真实窗口验证来自上述 36 个实际模型样本。新增的 1 项自动化测试中,执行守卫原本通过,明确提示契约先红后绿,不能将固定回复证明当成真实模型成功率。 +工作流、真实 JSON 会话和模型宿主分别由 [hybrid-ui-workflow](../tests/hybrid-ui-workflow.test.ts)、[hybrid-ui-session](../tests/hybrid-ui-session.test.ts)、[hybrid-model](../tests/hybrid-model.test.ts) 覆盖。benchmark:hybrid 是显式 UI 基准,benchmark:hybrid-model 会请求外部模型;后者需先确定授权范围及预算,参数见[脚本](../scripts/benchmark-hybrid-model.ts)。测试夹具通过不代表实际应用迁移或模型性能已验证。 -凭据审计在内存中读取既有授权 API Key,将原值、JSON 转义、URL 编码和 Base64 与 6 个明确的公开变更文件、本轮本地基线/测试/模型记录比对,匹配为 0;公开新增行中的本机绝对路径和密钥形状扫描也为 0。原始本地日志可能有本机路径,保持 Git 忽略且不作为匿名附件。无关本地 UI 计划未提交,交付目标仍只限实验分支。 +真实应用多候选、条件多控件统计和非 WPF 导航仍待验证;自动递归、其他导航类型与任意数值读取尚未由本编排提供。sibling-range 仅整理已观察的同级条目名称,虚拟化列表的完整加载和视觉分组自动识别不在其范围内。原始本地结果可能含窗口内容,保存在忽略的 test-tmp,不直接作为公开附件。 diff --git a/docs/next-iteration.md b/docs/next-iteration.md new file mode 100644 index 0000000..2fcab26 --- /dev/null +++ b/docs/next-iteration.md @@ -0,0 +1,114 @@ +# WinCode 效率与准确性迭代 + +更新:2026-10-08。本文是唯一的后续任务入口,只保留未完成工作。接手时核对当前分支、远端差异和工作区改动。待办包含尚未实施和验收未闭合的项目;当前接口以使用说明为准,尚未取得新的性能比较结果。 + +## 目标与下一步 + +优先提高一次任务得到正确结果的概率,再减少无效读取、模型往返和等待。不能靠忽略必要证据提高成功率,也不能靠少返回关键信息降低 token。 + +**C 的设置复选框实验仍待实际设置页。** 先确认具体控件与独立预期,再进入配对试验;当前没有性能比较结论,不一次实现整张路线表。 + +日常只读本文;调用客户端时查[使用说明](hybrid-readonly-orchestration.md),定位实现时再查[架构](../WinCode-架构与数据流说明.md)。历史计划与过程日志已从工作树移除,需要追查时按 PR 或 Git 历史读取,不默认加载旧版全文。 + +## 需求依据 + +| 用户任务 | 已知事实或需求假设 | 优先处理 | +|---|---|---| +| “实际软件能否像测试窗口一样可靠使用” | 真实应用已有单候选/单控件证据,多候选和条件统计主要在夹具验证 | C:迁移一个真实任务 | +| “能不能少等、少读、少花 token” | 存在小样本记录,尚无代表性任务的稳定收益证据 | C 先测完整成本,再决定 D | +| “检查设置值、列表选择或另一层页面” | 合理但尚未确认频率的需求;当前展开编排最终只接受 toggle 状态 | 先收集具体目标,按一个类型扩展 | + +表中后两项属于需求假设,不自动启动新功能或外部模型实验。其他无关节点是否造成误拒绝,须用具体场景复现后再决定改法。 + +## 借鉴与取舍 + +| 官方资料与成熟实现 | 可借鉴的做法 | 在本项目的应用判断 | +|---|---|---| +| [Playwright Locators](https://playwright.dev/docs/locators) | 逐步缩小定位范围,动作时重新解析目标,单目标操作保留唯一性检查 | 复用现有父路径与新鲜定位;不靠 first/nth 消除歧义,不保存跨请求 COM 元素 | +| [Playwright Actionability](https://playwright.dev/docs/actionability) | 不同操作检查不同前置条件 | 按“读取、统计、操作”判断必要字段;UIA 与浏览器机制不同,不直接移植浏览器等待或 force 行为 | +| [Anthropic 工具设计](https://www.anthropic.com/engineering/writing-tools-for-agents) | 依据真实任务评估,返回相关信息和可操作错误,避免无关上下文 | 优先改已有配方和 compact 输出;保留错误、范围及截断标识,不再增加通用工具目录 | +| [Anthropic Agent 评估](https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents) | 区分能力探索和回归,用可验证结果评估任务 | C 同时核对业务结果和执行轨迹;允许不同合法调用路径,失败成本不能排除 | +| [Microsoft UIA 批量获取属性](https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-cachingforclients) | 一次取得所需属性,减少逐属性跨进程调用;缓存需要刷新 | 仅当 D 证明属性读取是主要瓶颈时,评估单次请求内批量读取;不直接新增全局缓存 | + +真实 issue 的适用边界:[FlaUI #357](https://github.com/FlaUI/FlaUI/issues/357) 中维护者复现了 ScrollViewer 子控件 IsOffscreen 不反映实际可见范围,说明可见性与语义状态不能混为一谈;[FlaUI #552](https://github.com/FlaUI/FlaUI/issues/552) 报告重启应用后属性不受支持,评论中的旧状态猜测没有形成已验证修复,不能据此改 WinCode 生命周期。属性不受支持也不能用 false 代替真实状态,见 [Microsoft 属性读取规则](https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-propertiesforclients)。 + +[FlaUI #216](https://github.com/FlaUI/FlaUI/issues/216) 报告同 ID 的多个视图中 FindFirstDescendant 读到错误元素,报告者改用 FindAllDescendants 后关闭 issue。这里采纳的是“首个匹配不代表目标正确”的教训;本项目继续要求明确父范围内唯一匹配,不能用遍历顺序消除歧义。 + +资料于本次联网查阅。上表最后一列是针对 WinCode 的设计判断,外部成功经验不构成本地验证。无需为采纳这些原则升级 SDK、引入 Playwright、修改运行环境或增加服务。 + +## C.实际任务验收与效率基线 + +### 实验问题与范围 + +由 GPT-6.1 Sol、high 推理强度独立测试:当前未提交构建能否在 Steam 实际设置页面正确读取指定复选框,且配方相对原生读取是否减少调用方负担。测试者重新取得观察和预期,不沿用开发者的成功结论。这里比较两种客户端执行方式,不比较模型能力,不调用外部模型 API。 + +只使用用户已打开的 Steam。允许按实际观察到的唯一可操作控件打开设置和一个相关页签;导航属于准备阶段,至多 3 个语义动作,不更改设置、账号、下载或交易状态。无法通过已支持的语义动作到达时报告需用户打开的具体页面,不新增坐标、键盘或浏览器后端,不重启应用、改启动参数或扫描其他窗口。后台截图保持 backgroundOnly:true;不为取图恢复或激活窗口。 + +当前阻断:独立测试在商店首页未取得复选框案例;一次菜单 Invoke 虽被受理,约 33 秒后的读回未确认导航成功,不能排除菜单曾短暂出现。需用户手动打开相关设置页后重新准备;设置页的 UIA 兼容性仍未知。测试者不得用 WPF 夹具代替 Steam,也不把成功读取 Document 当作条件统计通过。 + +### 准备与独立预期 + +1. 记录实际分支、HEAD、dirty 状态;保存工作区 diff 和本轮涉及文件 SHA-256 到忽略目录。核对磁盘构建、delivery:verify、实际会话 buildId 与 Host inspectionVersion。测试包含本地未提交改动,不能只凭 HEAD 宣称版本相同;不切分支、不重建或修改被测实现。身份不匹配则报告阻断。 +2. 只读接口说明和必要使用手册后开始黑盒测试。重新列出 Steam 窗口,明确 PID/HWND;只用一个当前构建的 WinCodeSession/CLI,会话前后核对身份。父任务和其他测试不得同时操作 UI;旧 MCP 连接不能代替该构建。 +3. 从实际页面选 2–6 个相关复选框,尽量包含当前自然存在的 On、Off、禁用状态。先从清晰截图或用户明确陈述记录每个标签的预期,再将标签与唯一 AutomationId 对应;视觉无法确认的字段保持 unknown,不能复制配方输出作为预期。 +4. 冻结窗口、scopePath、regionAutomationId、所选 IDs、可选 summaryAutomationId、预期和预算至 case.json。只有实际存在有业务意义的摘要开关时才测条件分支;没有摘要则仅测无条件统计,并将条件验收列为未覆盖。缺少稳定 ID、唯一公共父范围或独立预期时不进入计分试验,报告具体能力缺口,不更改产品或造假数据。 + +### 配对执行 + +| 路径 | 执行方式 | +|---|---| +| N:原生基线 | 通过同一会话调用标准 scoped inspect,单次读取必要区域并独立核对所选 IDs 与状态。若有摘要,先只读摘要,On 才读详情,Off 不读详情。不得人为拆成逐控件 N 次读取;不调用配方实现完成统计 | +| R:客户端配方 | 同一会话调用 readonlyUiRecipe(target, 'checkbox-audit', parameters) 或 CLI readonly-ui,读取完整 steps、findings、isError 和 scopeResult,不修改配方 | + +同一冻结案例顺序为 N1 → R1 → R2 → N2(两对,交换先后);每次重新读取,禁止复用前次结果冒充试验。初始准备和连接预热单列,两条路径均使用暖会话。同一组 query/tree 预算与总 deadline,默认每次 15 秒;原生多调用共享剩余 deadline,不能给每一步各 15 秒。预算若不足,只能在冻结案例前基于实际节点调整。 + +每次核对逐项身份、On/Off、isEnabled、checkedCount、unchecked、disabled;禁用且 On 仍计入数量。带摘要时同时核对 detailsRequired 和 Off 零详情读取。前后截图/独立读回若发现页面或状态变化,标记 affected,不进入相同状态的耗时比较,保留已花成本。不会为了补齐 On/Off 组合修改真实设置;未出现的分支明确未覆盖。 + +正例可执行后,额外做一次只读范围反例:在已观察路径后追加明确标注为测试用且本次观察不存在的随机父选择器,预期范围失败、无 findings、无整窗回退。真实布局若有同名旁支可检查歧义;没有则记未覆盖,不构造 Steam 控件。本轮不重跑全部夹具或完整回归。 + +### 记录与判断 + +| 测量项 | 口径 | +|---|---| +| 正确性 | 每个已启动 trial 单独记 pass/fail/affected;未进入试验记 blocked 或 unsupported。N、R 分别给首次成功数/尝试数,记录误报成功、误拒绝、越界及未覆盖项;不得删除失败重算分母 | +| 调用成本 | 分开统计调用方请求、内部 MCP 读取、动作、Helper 启动、结果文件读取和实际模型往返。两路径可能有相同 MCP 次数;一次配方入口不等于一次原生读取 | +| 字节与时间 | 记录原始文本块 UTF-8 字节、配方最终文本字节、图片字节及每次墙钟耗时;内部证据、最终输出不要重复相加当模型输入。准备/冷启动、试验、失败/纠错、人工等待、清理分别列账;完整 token usage 缺失就写 unknown | +| 干扰与清理 | 记录前台变化是否实际观察、设置前后状态、实际动作;close 后验证自有 Gateway/Helper/测试进程退出,保留 Steam。未知项不能填“零干扰” | + +发现结果与独立预期不一致、未经请求的动作、目标改变或清理失败,即停止后续性能试验并保留证据;不边改实现边验收。AUDIT_BUSY 或外部状态变化只有原因已消失且仍在预算内才可重新准备,原失败不删除;超时不自动重放。总量最多 20 次原生 UI 请求(含发现、导航、取图、失败),主动操作时间最多 20 分钟;达到限制就报告实际进展,不继续扩张。 + +仅在四次正例、范围反例、状态保持和清理都成立时,判定该案例通过。无摘要时只能判定无条件案例通过,C 的条件分支仍待验。这是一项适用性与粗略成本试验,不给 P95、统计显著性或稳定收益百分比;发现没有收益时保留更简单的原生路径。实现细节与原始返回只在解释失败时读取,测试者不修改源码、安装 Skill 或本计划。 + +产物集中写到 test-tmp/c-steam-independent-<时间>/:case.json、包含所有尝试的 trials.jsonl、完整原始回执/必要截图、一个 report.md。报告只写版本、任务与独立预期、N/R 逐次结果、成本、失败与限制、清理证据,以及至多 3 条有证据支持的最小建议。个人内容只留本地,公开摘要去标识。父任务审阅后只将仍未解决的问题保留在本文,不新增历史工作日志。 + +## D.测量后才决定的优化 + +| 观察到的实际瓶颈/需求 | 最小候选改动 | 进入条件与边界 | +|---|---|---| +| 原生逐属性读取占主要时间 | UiTreeReader 内按需批量取得属性/状态 | 先分离 Host 启动、定位、属性读取耗时;核对当前 FlaUI API、取消与故障语义。只在请求内用缓存,动作后刷新 | +| 首次发现被无关 Group 的未知状态阻断 | 区分“列出部分候选”和“足以安全执行唯一目标” | 先有误拒绝复现;保留缺口并让调用方明确选择,不能把部分搜索的单个候选当全局唯一 | +| 多层区域迫使 AI 重复拼装已观察路径 | 返回更易使用的局部候选/路径建议 | 路径来自实际观察,下一请求重新验证;先优化反馈,再决定是否需要短路径执行 | +| 真实任务要读选择项或文本数值 | 在现有原生能力上增加一种明确结果读取 | 先定义控件、字段与独立预期;不默认读取输入内容,不同时扩展所有控件类型 | +| 同名按钮/输入框确实妨碍已授权操作 | 给 click/type 补可选父范围 | 独立任务验收:范围贯穿、旧 Host 拒绝、旁支不受影响、结果未知不重放 | + +不预建递归导航引擎、累计配额/授权状态机、全局 COM 缓存或新服务。异步显示问题先复现,再评估有截止时间的只读等待;不增加固定 sleep,不自动重试未知结果的写动作。 + +## 其他未完成项 + +以下按真实需要分别执行,不绑成大改造: + +| 事项 | 当前边界与触发条件 | +|---|---| +| Roslyn 会话刷新间歇失败 | 曾在快照刷新后缺少 references,原测试以 TypeError 掩盖业务响应;测试现已保留完整 MCP 错误。独立、三组并行和相关会话检查未复现,最新完整检查 498 项通过、1 项跳过;底层根因仍未确认。再次出现时先按报告中的实际错误定位,不自动重试、不放宽快照有效性检查 | +| Roslyn 生成源码 | Code Host 仍排除分析器/生成器。真实项目需要生成符号定位时,单独评估受控加载、定位和快照失效;不能用局部零引用证明删除安全 | +| Codex 冷启动 | 尚缺正常刷新宿主后“首次调用前无新 WinCode 进程”的观察;在正常重启机会验收,不打断当前任务 | +| 录制提示窗/托盘间歇失败 | Steam 新配方独立验收首次列窗遇到 HOST_ERROR:Recording indicator could not be displayed,审计耗时约 2.5 秒且未进入 UI 读取。后续主任务只读验收通过;未改提示窗,根因仍未确认。需取得底层异常或初始化阶段耗时再修复,不绕过提示窗或仅增加盲重试 | +| SDK 突发监听器警告 | 历史突发负载后监听器恢复为零,未证明泄漏;普通负载复现时再调查背压与释放,不抬高阈值遮掩 | +| 特殊环境 | 大项目长时运行、动态 MSBuild 输入、异常退出文件清理、跨会话/权限/DPI/Explorer 重启按用户环境触发验证 | +| 发布与部署 | GitHub Release 尚未发布;源码、磁盘产物、安装 Skill 和当前连接分别验收,合并不代表已部署 | + +## 执行与维护 + +每次选一个工作包,先查当前分支、真实源码和相关测试。优先最小复现/相关检查;涉及 UI 的改动必须走实际入口。普通局部任务至多新增 3 项自动化测试;完整回归按影响运行一次,失败后遵守仓库的诊断与重试限制。不得因本文存在待验项就自动安装依赖、运行外部付费实验或扩展任务。 + +任务完成后从本文删除对应任务,把用户可见变化写入 CHANGELOG、验证及失败写入该次 PR;仅保留仍成立的限制。更新使用说明中实际发生变化的契约,不追加“第几轮”流水账,不另建重复路线图。文档改动检查链接、命令、字段和新旧能力边界;文档检查不替代运行时验收。 diff --git a/skills/wincode/SKILL.md b/skills/wincode/SKILL.md index 0c9a480..0e62d1e 100644 --- a/skills/wincode/SKILL.md +++ b/skills/wincode/SKILL.md @@ -1,39 +1,26 @@ --- name: wincode -description: 使用 WinCode MCP 分析 Windows/.NET 项目源码、引用与变更影响,并通过 Windows UI Automation 检查控件、执行语义点击与表单填写、验证桌面交互流程。适用于源码定位、界面问题排查及支持 UIA 的 Windows 应用测试。 +description: 使用 WinCode MCP 定位项目源码、查询 C# 引用和变更影响,或通过 Windows UI Automation 读取、操作和验证桌面界面。用于代码排查与支持 UIA 的 Windows 应用测试。 --- # WinCode -适用于 WinCode 0.17.0。已有正确工作区的 MCP 连接时直接使用;采用按需模式时,首次需要 WinCode 才按[诊断手册的会话入口](references/diagnostics.md#skill-按需会话)启动。Skill 被发现或读取不需要预启动任何进程。 +适用于 0.17.0。优先复用目标工作区已有的 MCP 连接;需要按需启动时,读[会话入口](references/diagnostics.md#skill-按需会话)。仅加载 Skill 不启动进程。 -客户端提供 `readonly-ui`/`expand-ui` CLI action,不是标准 MCP 工具名。展开导航接受实际观察得到的 scopePath,并将父范围保留到最终子控件读取;父路径需要 inspectionVersion 5。每请求最多一次展开,支持已展开续接及局部诊断,不自动生成父路径或递归执行。使用前按手册核对当前连接/入口能力,旧连接不会因 Skill 更新获得新功能。 +## 按任务读取 -只读取与当前任务有关的手册: +| 当前任务 | 手册 | +|---|---| +| 找代码、读上下文、查引用或影响 | [代码与工作区](references/code.md) | +| 找窗口、读控件、统计复选框、读取列表分组或操作界面 | [窗口与 UI](references/ui.md) | +| 启动或关闭按需会话、核对版本、处理错误 | [诊断与恢复](references/diagnostics.md) | -- [代码与工作区](references/code.md):源码搜索、上下文、引用、影响分析和 Roslyn 配置。 -- [窗口与 UI](references/ui.md):窗口与控件定位、语义操作与激活边界、前台键盘输入(需授权)、结果验证和源码候选。 -- [诊断与恢复](references/diagnostics.md):按需会话的启动、复用、结果读取和关闭,以及版本和故障恢复。按需模式先只读该手册首节。 +只读当前任务需要的部分。参数以已连接实例的 Schema 为准;不确定时调用 `wincode_hello_world({toolName:"具体工具名"})`。更新磁盘文件不会更新运行中的连接。 -连接固定到启动工作区;已知根一致时直接查询,不例行重复打开。`WORKSPACE_MISMATCH` 时选择目标项目的连接,可参考 `connectionGuide`;`workspace_open` 只能确认或恢复原工作区。 +## 使用原则 -按需模式在一次任务内保留执行会话 ID,多次查询复用同一连接;读取回执中的完整结果文件,保留所有 MCP 内容块与 `isError`。任务结束或放弃时显式关闭;断线后不自动重放,旧符号定位不能跨新连接使用。工具名和参数以运行实例的 Schema 为准,疑问时使用 `wincode_hello_world({toolName:"具体工具名"})`。 - -默认 `local-text` 提供文本线索;显式启用 Roslyn 才有 C# 语义证据。需要精确引用时先搜索声明,再传回完整 `location`,不猜定位或复用过期快照。 - -日常导航用 `wincode_search_text` 限定目录查字面量、`wincode_file_outline` 查看行数和声明,再把返回的 `nextRequest` 交给 `wincode_prepare_context`。先看 `summary` 的范围和缺口,再核对正文与覆盖率。UI 首轮可显式用 `responseFormat:"compact"`,需要几何或更多信息时按 `expansionRequests` 展开;仅使用本连接已声明的能力。 - -优先按文件、符号和行范围获取小结果。参数遵循手册与实际 Schema;保留截断、降级和歧义,UI 源码候选不等于已验证的运行时映射。`SERVER_BUSY` 或超时后先按诊断手册处理,不自动重放请求或重启连接。 - -用户要求执行或测试界面流程时,可在授权范围内完成定位、点击、输入与结果验证;只要求评估或查看时保持只读。操作前遵循下面的前台边界,字段与流程细节见 [窗口与 UI](references/ui.md)。 - -## 注意事项:后台取证不等于所有操作都不会抢前台 - -“工具不主动激活窗口”与“目标应用不会跳到前台”是两件事。不要因使用 WinCode、UIA 或某个 AI 客户端,就承诺所有步骤都不会打扰用户。 - -- **只看界面或截图时,保持只读。**复用已有实例;对确定的 PID/HWND 使用 `wincode_ui_inspect` 或 `wincode_ui_review`,设置 `backgroundOnly:true`;仅需控件信息时用 `capture:"none"`,需要图像时用 `capture:"original"`。不要为查看而额外点击、换页或填写。 -- **后台截图直接用 PrintWindow,不先把窗口提到前台。**`backgroundOnly:true` 禁止屏幕截图回退;它只约束 inspect/review 的取证方式,不是 click/type 的“禁止激活”开关。截图失败、黑图或窗口最小化时报告实际限制,不为了截图调用 `SetForegroundWindow`、恢复窗口或重新启动实例。 -- **填写必须明确选择模式。**后台写值使用 `wincode_ui_type` 且显式传 `mode:"setValue"`;省略 mode 会走默认的 `"type"`,请求焦点并发送键盘输入。控件不支持 ValuePattern 时,不自动改成 type、SendKeys 或自写聚焦脚本。setValue 不请求键盘焦点,但写值触发的应用事件仍需验证。 -- **语义点击不等于保证不激活。**`wincode_ui_click` 只调用 Invoke/Toggle/SelectionItem,不先聚焦、不模拟鼠标;目标控件的事件、导航、视图装载或弹窗仍可能带来前台变化。已有 WPF 导航按钮的反馈中,自写脚本与 WinCode 对同一按钮使用 InvokePattern 都出现了前台切换,而只读取证未观察到切换。该证据只限具体路径,不能推成“所有 Invoke/Select 都抢前台”,也不能归因于 AI 客户端身份。 -- **只授权后台操作时,不擅自进入前台路径。**启动应用、恢复最小化窗口、Focus/SetFocus、键盘或鼠标模拟都可能改变前台状态,不能当成后台步骤的自动补救。某个动作已知会抢前台,就说明该步骤的限制;没有前台授权不再执行它。若执行中观察到抢前台,停止后续动作,先只读检查状态,不反复重放来“确认”。 -- **区分复现与根因。**动作返回成功只说明调用被接受;先读回业务状态。没有发现显式 Activate 调用不等于已排除应用或框架;有限采样未见前台变化只能报告“本轮未观察到”,不能保证绝不激活。用户未要求追查时,不自行追加对照实验。 +- 连接固定到启动工作区。已知根一致时直接查询;`WORKSPACE_MISMATCH` 时换用对应连接,`workspace_open` 只能确认或恢复原根。 +- 代码先定位再定向读取。默认 `local-text` 提供文本线索;Roslyn 引用需要当前搜索结果中的完整 `location`。 +- 查看界面保持只读:明确 PID/HWND,使用 `backgroundOnly:true`。操作界面按用户授权执行,动作后读回结果。后台写值显式用 `mode:"setValue"`;默认 `type` 会请求键盘焦点。 +- 检查完整结果中的业务状态、范围、截断和缺口。空结果不证明不存在;动作受理不证明业务结果已经发生。结果未知时先观察实际状态,不重发原动作。 +- 按需会话在任务内复用,读回 `resultFile` 的完整内容,结束时显式关闭。`readonly-ui` 和 `expand-ui` 是客户端 action,不能作为 MCP 工具名调用。 diff --git a/skills/wincode/references/code.md b/skills/wincode/references/code.md index 255b63d..bd5b4e6 100644 --- a/skills/wincode/references/code.md +++ b/skills/wincode/references/code.md @@ -1,76 +1,29 @@ # 代码与工作区 -以下为 MCP 工具名和参数;以客户端实际 Schema 为准。 +先按目标取证,再按结果中的缺口补读。连接固定到启动工作区;已知正确根就直接查询。workspace_open 用于确认或恢复同根,换项目需对应连接。版本或工作区不符见[诊断手册](diagnostics.md)。 -0.15.0 起,每个连接固定到启动工作区。推荐显式传入 --workspace <绝对目录>;省略时固定到服务启动 cwd,不能稍后用 workspace_open 改根。hello.health.workspaceBinding 包含 mode=fixed、root 和 source(argument/cwd/configuration);其他项目使用独立连接。Windows 大小写/分隔符及规范化后同根拼写保留原身份,不接受 junction/链接别名。WORKSPACE_MISMATCH 返回 activeWorkspace/requestedWorkspace 与 select_workspace_connection;不要忽略错误继续操作或宣称目标根已改变。 +## 选择读取方式 -0.13.1 的本地声明扫描覆盖 .cs/.ts/.tsx/.js/.jsx/.py,屏蔽注释、字符串以及整个 JSX 元素(含其中的表达式),签名与行号仍来自原文。无法可靠定界、未闭合或嵌套超限的文件标记 lexical-uncertainty,queryComplete=false 且不缓存完整空结果;这不是完整语法解析,复杂声明可能省略。引用搜索仍为文本线索,不提供编译器语义或精确身份。 - -`analyze_change_impact` 及其别名返回一个 JSON 文本块,formattedReport 保留在对象中,不再返回第二份重复 Markdown。没有新增 responseFormat 参数,不要给该工具传 context 专用的格式字段。 - -原始参数按 UTF-8 JSON 共享 64 KiB 上限,包含未知字段。query/symbolName 最长 256,kind 最长 128,路径/target 最长 4096,goal 最长 8192;其他字段按当前 schema。各字段分别合法但合计超限仍拒绝。32 个业务槽包含排队与执行,组合工具不重复占用外层容量;SERVER_BUSY 不自动重试,排队计入整个请求预算。 - -0.14.0 的 local-text 查询每次有界扫描实际输入,按内容哈希复用声明解析;新建、删除或修改文件后重新查询,不依据 Git 暂存状态或监听事件认定旧结果有效。内置打包先读取选中文件,再校验内容身份复用;CLI 输出没有可核验输入清单时不缓存。overflow 缺失会重建,但返回的临时文件不是永久存档,跨调用读取失败应重新获取。以上不提供多文件原子快照,也不把文本定位升级为 Roslyn 精确身份。 - -## 后端与能力边界 - -默认 Gateway 使用 WinCode 内置文本能力,查询结果 `source=local-text`。`hello.health.text.semanticConfigured=false` 只描述文本适配器,即使启用 Roslyn 仍为 false;整个实例的提供方看 `hello.codeProvider`,Roslyn 状态看 `hello.health.roslyn`。显式启用 Roslyn 后使用直接 Code Host,失败会报错,不会偷偷改换提供方。外部 Serena 连接配置、启动器及旧 `serena-adapter-fallback` 来源已退役;旧调用方须适配。`hello.codeProvider` 标明实例选择,不能根据仓库中存在 Host 推断当前连接已更新。 - -Roslyn 调用顺序:用 wincode_find_code_symbol 搜索(query 最长 256 字符),根据 signature、file 和 location.project 选择具体声明;再把该项的 name 作为 symbolName、完整 location 对象作为 symbolLocation 传给 wincode_find_references。location 包含 snapshotId(32 位小写十六进制)、project/file(工作区内相对路径)和 position(非负零基 UTF-16)。不手工猜偏移;同名/重载返回候选,不能自动选第一项。简单名称查询在当前不完整范围下只返回候选,单候选也需明确定位;candidatesTruncated=true 时 candidateCount 可能缺省,不能当作全量计数。 - -受跟踪输入变化、重载、手动释放或连接更换会使 location 失效;无关文件编辑不等于编译输入变化。SNAPSHOT_STALE/INPUTS_CHANGED 后,下一次显式符号搜索执行所需重载;失败请求不自动重放。源码编辑后首次搜索也可能报告过期,再显式搜索恢复。HOST_RESTART_REQUIRED 按诊断手册对同一工作区执行恢复。本地文本实例拒绝 symbolLocation;旧 namePath/重载序号不能迁移为 Roslyn 身份。semanticContext 保留快照、输入检查点、排除生成器数和范围,queryComplete=false 时零引用仍不能证明可删除。 - -维护者可用启动参数 `--roslyn-config <配置 JSON 的绝对路径>` 显式选择;不从目标仓库自动发现执行配置。JSON 对应宿主 WinCodeConfig.adapters.roslyn,最多 16 KiB,示例路径须替换成已安装/已构建的实际文件: - -```json -{ - "enabled": true, - "allowProjectEvaluation": true, - "project": "App/App.csproj", - "configuration": "Debug", - "targetFramework": "net10.0", - "dotnetPath": "C:/dotnet/dotnet.exe", - "hostPath": "C:/WinCode/tools/WinCode.Code.Host/bin/Release/net10.0/publish/WinCode.Code.Host.dll", - "additionalInputs": [] -} -``` - -allowProjectEvaluation 表示允许 MSBuild 设计时求值执行项目 targets,须符合用户授权;不会自动 restore 或下载 SDK。project 是相对固定启动工作区的入口,缺失就报错,不猜其他项目。工作区、配置和 TFM 固定于实例,改变它们需按授权更新启动配置并重新建立连接。dotnetPath/hostPath 必须为绝对普通文件,重解析路径不支持;子进程使用指定 dotnet 安装根,不改系统环境。可选 loadTimeoutMs 为 1–120000(默认 120000),queryTimeoutMs 为 1–60000(默认 30000),不属于 MCP 请求参数。 - -维护验收使用 `npm run test:roslyn-host`(独立 Host)和 `npm run test:roslyn-gateway`(真实 stdio MCP)。维护脚本按显式 WINCODE_DOTNET_PATH、项目 .deps、DOTNET_HOST_PATH、PATH 顺序寻找已安装 SDK,并核对 global.json 的精确版本;不下载安装。当前要求 10.0.303。这些脚本会锁定还原并构建/发布 WinCode Code Host,也会还原生成的测试夹具,保留 test-tmp 报告;不还原用户目标应用。现有 NuGet 缓存缺包时还原可能访问包源,不能将“不下载 SDK”理解成完全离线。Gateway 验收使用完整发布目录的异地副本。此验收不证明当前 Codex 连接已更新或无 SDK 的机器可运行。 - -`additionalInputs` 是可选启动配置,默认空数组。例如构建读取 schema.yaml 和 build-inputs/custom.rules,可填 ["schema.yaml","build-inputs/custom.rules"]。最多 32 个固定工作区相对文件路径,数组 JSON 最长 4096 个 UTF-16 字符;不接受根外/绝对路径、重复项、目录、通配符或链接。缺失项报 INPUT_UNAVAILABLE,不静默删除;创建或恢复文件后再显式搜索。其他项目连接分别配置自己的列表。修改列表需更新启动配置并重启 Gateway,普通 MCP 参数不能添加输入或获取项目执行许可。 - -Host 通过独立进程的 JSON 行协议 v2 工作,非 MCP tools/call:启动参数为 `--allow-project-evaluation ROOT PROJECT CONFIGURATION FRAMEWORK [ADDITIONAL_INPUTS_JSON]`;加载后 ready 帧给出 protocolVersion=2、snapshot 及 inputPolicy={version:2,additionalInputs:[...]}。Gateway 必须核对实际列表;旧 Host 缺少输入策略确认或列表不一致时拒绝接入,即使同为协议 v2 也不能假定兼容。项目求值可能执行 targets,不自动 restore;本维护验收只使用获准的生成夹具。协议及启动方式以源码 `tools/WinCode.Code.Host/Program.cs` 注释为准,尚非稳定公共接口。 - -| 内部 operation | 请求与结果 | -| --- | --- | -| `symbols` | 必填 id、snapshot、query,可选 kind/file;最多返回 200 个声明,totalFound/truncated 说明截断,location 给出可用于引用的当前快照定位。超时与 references 相同 | -| `references` | 必填 id、snapshot、project、file、position;project/file 是工作区内路径,position 为零基 UTF-16 偏移。返回 line/column 一基,start/length 零基 UTF-16。timeoutMs 为 1–60000,默认 30000;limit 为 1–1000,默认 100,只约束返回条数 | -| `reload` | 必填 id;固定根、入口项目、配置和 TFM 内重新求值,成功返回新的 ready/snapshot,调用者须重新定位符号。timeoutMs 为 1–120000,默认 120000;开始重载后失败或取消不会恢复旧身份 | -| `cancel` | 必填 id、targetId;cancellationRequested 仅确认是否向活动目标发出了取消,目标仍有独立结果,不代表立即完成或回滚 | -| `shutdown` | 必填 id;停止接纳、取消并排空请求、释放工作区后才返回成功。stdin EOF 同样清理,但没有 shutdown 确认帧 | - -每帧还须包含 operation;id 为 1–128 字符且活动期间不可重复。队列最多等待 8 项,满时 BUSY;timeoutMs 从接纳起计算,包含排队,Host 本身执行协作取消。Gateway 超时/取消先等待目标收尾,超过 1 秒宽限才回收自有 Host 进程树;初次加载尚不能接收 cancel 时直接回收。Windows Host 在加载前绑定自有 Job,以覆盖普通子进程继承的退出行为;这不是沙盒,也不约束 targets 通过外部服务启动的进程。请求帧最多 65536 个 UTF-16 字符,Node 接收帧最多 1 Mi 字符,超长使通道失效。SDK/global.json、监听或资源释放故障可能要求新进程,不能循环 reload。 - -Host 监听变化并在查询前后比较内容指纹,变化时丢弃结果并要求显式 reload。每次加载尝试前用最多四个 50 ms 观察窗收敛输入事件,受请求取消预算约束;持续写入仍失败。求值期间的内容/事件检查继续保留,不自动重放请求。freshness.scope=compilation-inputs-and-explicit-files,自动候选包括 .cs/.csproj/.props/.targets、.xaml/.resx/.resw/.resources、.config/.ruleset,global.json、project.assets.json、packages.lock.json、.editorconfig/.globalconfig 及 *.nuget.dgspec.json。同时跟踪实际加载的文档、AdditionalFiles、分析配置、程序集引用和祖先常规构建配置;新增源码仍经 MSBuild 的 Compile 规则决定是否加载,不因发现 .cs 就直接加入项目。 - -默认枚举排除 .git、node_modules、.deps、bin、dist、build、.cache、.vs、.packages、test-tmp、trash;实际加载或显式补充的文件优先于目录排除。非标准扩展名导入、排除目录中的自定义配置,以及自定义 targets 隐式读取的数据,应通过 additionalInputs 补充;不能承诺自动发现任意构建依赖。普通 README、视频和未加载的二进制文件不占输入字节预算。文件清单仍需有界枚举,极大目录仍可能超限;预算为最多 20000 个枚举条目、5000 个输入文件、总计 128 MiB、单个输入 32 MiB。必要输入或显式补充文件超限仍失败,不接受截断快照。内容核查流式计算摘要,仅冻结文档时保留源码正文;不支持重解析路径。 +| 已知信息或目标 | 用法 | +|---|---| +| 文件与行号 | prepare_context 的 lineRanges,直接读所需分支 | +| 文件与声明名 | scopeFiles + symbol;仅声明附近片段 | +| 文件但位置未知 | file_outline 后按 nextRequest 续读,或 scopeFiles 预览 | +| 只知道关键词 | search_text 限定 scopePaths,再读命中位置 | +| C# 精确引用 | Roslyn 搜索并消歧,再传完整 location | +| 项目概览或依赖 | workspace_open / list_directory / analyze_workspace | -自定义 targets 的任意外部输入和整个磁盘原子快照尚未验证,所以仍保留 diskFreshnessVerified=false、externalCustomInputsVerified=false。queryComplete 当前为 false;排除的分析器/生成器、加载及编译诊断须保留,零引用不证明安全删除。普通 MCP 请求使用下方规范字段;snapshotId 仅出现在 symbolLocation/semanticContext 内,不单独作为顶层参数发送。未知字段可能被忽略,成功响应不证明新参数生效。TS/JS/Python 的限定文件文本取证仍走 prepare_context,不把 Roslyn 声明搜索当成多语言语义服务。 +已有文件读取工具且位置明确时,可直接有界读取,不必绕经 MCP。默认声明窗口通常只有24行,审查异常处理或资源释放时要覆盖相关分支;不要把片段完整当成整个方法完整。取得足够证据后继续分析,不例行拉全文。 -## 规范字段 +## 工具字段 -先定位再阅读:`wincode_search_text({query:"Save(",scopePaths:["src"]})` 返回每个匹配行的文件、行号、1 起始 UTF-16 列号和预览。`nextRequest` 是下一次 `wincode_prepare_context` 的完整参数,不是自动执行指令。`wincode_file_outline({file:"src/Service.cs"})` 给出同次读取的 `fileLineCount`、`sizeBytes`、声明及续读请求。声明边界仍是文本模式,不代表整个方法或语义身份。 +参数以当前 Schema 为准。名称区分大小写,未知字段可能被忽略;scopeFile 不能代替 scopeFiles,symbolName 不能代替查符号时的 query。原始参数总上限64KiB UTF-8 JSON。 | 导航工具 | 必填字段 | 可选字段及范围 | | --- | --- | --- | | `wincode_search_text` | `query`:非空白单行字面量,最长 256;不接受正则表达式 | `scopePaths`:1–20 个字面文件或目录,默认工作区;`caseSensitive`:布尔值,默认 false;`maxResults`:整数 1–200,默认 50;`maxOutputChars`:整数 2048–32768,默认 8000 | | `wincode_file_outline` | `file`:工作区内字面文件 | `maxSymbols`:整数 1–200,默认 100;`maxOutputChars`:整数 2048–32768,默认 8000 | -导航路径最长 1024,允许工作区内绝对路径,拒绝通配符、父目录逃逸和范围外链接。搜索范围排他、重叠文件去重;单文件 256 KiB、合计 8 MiB、最多 5000 枚举项,并遵守操作取消和扫描时限。默认跳过生成目录及 test-tmp。支持 C#/TS/TSX/JS/JSX/Python 源码,以及 XAML/XML、项目/解决方案、JSON/Markdown/文本、YAML/config/PowerShell/mjs/cjs 文件;声明概览仅解析前六种源码,其余返回 `declarationsSupported:false`。扫描限制和最终 JSON 输出限制都可能使结果不完整。`foundItems` 是有界扫描发现数,`returnedItems` 是实际展示数;`fileIssues` 最多 20 条,`fileIssuesOmitted` 表示省略数。词法失败的文件在 `fileIssues` 中具名;零结果不能证明未扫描范围没有匹配。 - -兼容容忍模式允许额外字段,但会忽略它们,不能据“调用成功”判断参数已经生效。例如 `scopeFile`、`scope_files` 均不是 `scopeFiles`,`symbolName` 不能代替查符号工具的 `query`。未知字段不能补足缺失必填项;已知字段填错类型、空白必填值或违反范围规则仍会报错。下面列出的名称区分大小写,未列出的参数不应发送。 | 工具 | 必填字段 | 可选字段及类型 | | --- | --- | --- | @@ -83,20 +36,15 @@ Host 监听变化并在查询前后比较内容指纹,变化时丢弃结果并 | `wincode_plan_refactoring` | `target`、`goal`: 非空字符串 | `symbolLocation`: 同影响分析 | | `wincode_safe_move_to_trash` | `filePath`: 工作区内相对路径字符串 | `reason`: 字符串;该工具实际移动文件,须符合用户授权 | -`kind` 不是全语言统一的能力承诺:local-text 的 C# 模式匹配 class/struct/interface/enum/method,TS/TSX/JS/JSX 模式匹配 class/interface/enum/type/function/method,Python 模式匹配 class/function;均为有限单行声明模式。Roslyn 返回 class/interface/struct/enum/type/method/property,构造函数归入 method,`type` 筛选也匹配命名类型。Roslyn kind 大小写精确,local-text kind 忽略大小写;建议统一使用上述小写值。两种提供方的 query 都是忽略大小写的名称子串搜索;prepare_context 的 symbol 则是大小写精确匹配。 +导航使用字面路径,不支持 glob 或根外链接;默认跳过生成目录。search_text 单文件256KiB、合计8MiB、最多5000枚举项,输出限制也可能截断。检查 foundItems/returnedItems、fileIssues/fileIssuesOmitted 和扫描完整性。 -Roslyn 精确调用示意(`selected` 必须是本次搜索结果中经消歧选定的项,不能照抄虚构定位): +`kind` 随提供方而异:local-text 支持有限 C#/TS/JS/Python 声明模式;Roslyn 支持 class/interface/struct/enum/type/method/property,构造函数归 method。query 是忽略大小写的名称子串;Roslyn kind 大小写精确,local-text kind 忽略大小写。prepare_context.symbol 则为大小写精确声明名。 -```javascript -wincode_find_references({symbolName: selected.name, symbolLocation: selected.location}) -wincode_analyze_change_impact({target: selected.name, symbolLocation: selected.location}) -``` +wincode_analyze_change_impact 是 analyze_change_impact 的公布别名,wincode_workspace_open 是 workspace_open 的兼容别名;使用实际 tools/list 中的名称。 -local-text 的 queryComplete=true 仅表示该次有界文本扫描完成,仍是 degraded,不证明语义完整;Roslyn 当前 queryComplete 始终为 false,应结合 resolution、semanticContext 和实际返回证据判断。 +## 定向上下文 -`wincode_analyze_change_impact` 是 `analyze_change_impact` 的公布别名;`wincode_workspace_open` 是 `workspace_open` 的历史兼容别名。别名共享参数和执行规则,优先使用本连接 tools/list 公布的名称。其余字段名不接受自动拼写纠正。 - -`wincode_prepare_context` 的完整规范字段如下。路径均为工作区内的字面路径,不是 glob。 +wincode_prepare_context 参数: | 字段 | 类型与数量 | 规则 | | --- | --- | --- | @@ -112,90 +60,60 @@ local-text 的 queryComplete=true 仅表示该次有界文本扫描完成,仍 | `responseFormat` | 可选字符串 `compact/legacy` | 默认 `compact`;两种形式都计入文本预算 | | `maxTokens` | 可选整数 512–65536 | 默认 8000;按 UTF-16 字符÷4估算,并非精确模型 token | -不要用 `"2000"` 代替 `2000`、`"false"` 代替 `false`,也不要把 `{name:"Save"}` 当作搜索字符串。额外字段容忍并不会放宽这些类型规则。返回结果中的 `coverage`、`queryComplete`、`runtime` 等是证据字段,不能作为未声明的请求参数获得相应能力。 - -## 按任务取证 - -上下文先看 `summary`:`scope` 区分 requested-lines、packed-files 和 displayed-snippets,`status` 只描述这个范围的 complete/partial/empty/unknown;`nextAction` 指向续读、文件问题或缩小范围,不承诺整个审查已完成。`line-range-out-of-bounds` 保留原始请求的未覆盖状态,并报告实际 `fileLineCount`;若请求与文件仍有交集,`coverage.details[].nextRequest` 返回有效交集。起点已超过 EOF 或文件不存在时不生成盲目重试。普通文件开头片段也带有界续读请求。 - -怀疑源码与连接不同步时,先调用 wincode_hello_world({toolName:"wincode_prepare_context"}),对照本连接 tools/list 的参数及 schemaHash,并记录 runtime.instanceId/build.buildId。旧实例没有这些字段时明确为旧契约,不再反复尝试新参数。构建后需要客户端重连;build.status=unknown 不能当成当前源码已运行。test:e2e 只证明它自己启动的隔离 stdio 进程。 - -lineRanges 查看最终 coverage.allRequestedCovered、completeLines 和 details 中的 missingRanges/nextRequest。末行 endLineComplete=false 时从该整行补取,不能把“行号落入区间”算作完整正文。nextRequest 可能提高预算;maximum-budget-without-progress 表示不要反复提交同一请求。明细缺省还需检查 omittedItemCount。scopeFiles/symbol 的 coverage=null,taskCoverage=null;queryComplete 或非空片段均不证明整个方法覆盖。 - -| 目的 | 调用 | -|---|---| -| 确认固定项目 | workspace_open({path: "I:/project"});其他项目选择对应连接 | -| 按需浏览目录 | wincode_list_directory({path: "src", maxDepth: 1, maxEntries: 100}) | -| 项目依赖概览 | wincode_analyze_workspace({maxDepth: 2}) | -| 找符号 | wincode_find_code_symbol({query: "Save"}) | -| 文本引用线索 / Roslyn 候选消歧 | wincode_find_references({symbolName: "Save", relativePath: "src/Service.cs"});Roslyn 未传 symbolLocation 时只返回候选,不查询引用 | -| 变更影响 | wincode_analyze_change_impact({target: "Service"}) | -| 重构步骤 | wincode_plan_refactoring({target: "Service", goal: "拆分保存逻辑"}) | - -按目标选工具,不顺序执行整张表。已知文件范围时直接限定: - -选定 Roslyn 重载后,将其 name 和 location 原样传给后续工具:引用使用 symbolName,影响分析及重构使用 target,同时传 symbolLocation。后两者先验证定位再分析,不按名字重选目标;SNAPSHOT_STALE/INPUTS_CHANGED 时须重新搜索。简单名称歧义检查 resolution/candidateCount/candidatesTruncated,不能选第一项。queryComplete=false 不等于零引用。 - -Roslyn 引用默认最多 100 条,显式 `limit` 最大 1000。最终 JSON 文本另受 `maxOutputChars` 限制(默认 8000,范围 2048–32768),计入格式缩进、转义及元数据的 UTF-16 字符数,不是模型 token 数。两项限制先到哪个就在哪截断;提高 limit 不保证能返回全部条目,也不限制 Roslyn 查找引用的计算成本。`totalReferences` 保留当前已加载快照内发现的总数,`returnedReferences` 等于实际返回的 `references.length`;local-text 的 totalReferences 仍只是原有扫描范围内已找到的条数。 - -字符预算造成省略时,`outputOmissions` 标明 references、candidates 或 fileIssues,`truncated=true`、`queryComplete=false`。候选总数不改写,`candidatesTruncated` 和 `fileIssuesOmitted` 如实更新;返回条目的位置与预览不裁半,symbolLocation、semanticContext 和覆盖限制保持完整。必要元数据本身放不下时返回 `OUTPUT_BUDGET_EXCEEDED`,按需增加 maxOutputChars;不会静默裁剪身份。需要更多已知引用时,在同一有效 symbolLocation 上按需提高 limit 或 maxOutputChars,例如 `wincode_find_references({symbolName: selected.name, symbolLocation: selected.location, limit: 120, maxOutputChars: 32768})`。这不是分页,不扩大项目、生成代码或动态调用覆盖。 - -输出预算只作用于引用工具的最终回复。影响分析的 `referencesCount` 继续表示参与内部聚合的返回条数,`totalReferences` 和 `referencesTruncated` 保留其引用查询的计数与裁剪情况。未执行引用查询或无法完整计数时总数为 null,未知截断状态为 null;受影响文件/组件仍只来自实际返回引用,UNKNOWN 判定不变。 - -健康同根 workspace_open 保留 Host/snapshot,不等待业务排空;取消概览确认不会使健康实例进入恢复。已知 SDK 重启要求或清理失败仍遵守诊断手册;另一根在任何重置、缓存、watcher 或 trash 变更前被拒绝,内部 WorkspaceManager 也不能改根。 - -workspace_open 默认返回项目摘要和最多 8 个入口,整份 JSON 默认不超过 8000 个 UTF-16 字符;不生成目录树或统计全仓大小。检查 projectScanComplete,null 统计不等于零。需要目录时用 wincode_list_directory 指定窄路径,查看 scanComplete/truncated/omissions。includeTree:true 可显式取得有界兼容树,不能当成完整仓库清单。maxOutputChars 为 2048–32768;目录 maxDepth 为 1–5,maxEntries 为 1–500。需要生成目录时显式 includeIgnored:true,但不能越过工作区边界。 - -架构分析共用上述有界发现/浏览器:发现最多 2000 项,树预览最多 500 项;图最多 16 个项目文件、每文件 64 KiB、合计 256 KiB,入口目录枚举合计最多 2000 项。`scanComplete=false`、`omissions` 与 `outputOmissions` 表示未取得完整结构,不能将空引用解释成已证明不存在依赖。普通项目识别与图读取都拒绝根外项目描述符;取消沿实际读取循环传播,单次底层 I/O 仍可能延迟取消。 - -本地声明解析先折叠屏蔽后的空白,超过 16384 字符的规范化单行返回 lexical-uncertainty/incomplete;复杂声明仍可能省略。Git 查询需要 2.36+,从工作区外的绝对安装路径执行并禁用 fsmonitor;`git.status=unknown` 或缺少 `isClean` 不等于干净。linked worktree 及 Git 管理的子目录由 Git 本身识别。 +例如,已知保存逻辑所在行: ```json -{"task":"查明保存失败原因","scopeFiles":["src/Service.cs"],"includeFullText":false,"maxTokens":2000} +{"task":"核对保存失败时的资源释放","lineRanges":[{"file":"src/Service.cs","startLine":50,"endLine":80}],"maxTokens":2000} ``` -上例用于 wincode_prepare_context。默认 compact 仅返回一个 JSON 文本块;旧客户端需要 JSON 加 Markdown 时显式传 responseFormat:"legacy"。maxTokens 为 512–65536 的整数,约束所有返回文本的字符数÷4,包括 JSON 转义、元数据和旧格式的两个文本块;这是估算,不是真实模型 Token 硬上限。 +先读 summary 的 scope/status/nextAction,再核对正文与 coverage。requested-lines、packed-files、displayed-snippets 是不同范围;complete 只描述该范围。lineRanges 检查 allRequestedCovered、completeLines、missingRanges;endLineComplete:false 时补读整条尾行。越界可用返回的有效交集 nextRequest;maximum-budget-without-progress 时改用文件读取,不重复原请求。 -candidateFiles 最多 20 个,仅表示优先,仍可能追加符号检索结果。focusAreas 最多 5 个现有文件或目录,仅取目录直属代码文件,最多追加 8 个、每目录最多检查 1000 项;不支持 glob,勿传 src/**/*.ts。路径必须在工作区内,缺失/不可读/选择上限查看 fileIssues,不盲目重复相同参数。 +symbol 只读取声明附近窗口,symbolCoverage:unknown;必要时用 nextRequest 继续。scopeFiles/symbol 的 coverage 可能为 null,不能据 queryComplete 或非空正文认定整个方法已覆盖。candidateFiles 只是优先候选,排他范围必须使用 scopeFiles;focusAreas 仅有限发现目录直属文件。 -已知位置时选择最小取证范围,避免再做全仓库符号查询: +检查 relatedFiles.bodyStatus/bodyStatusScope、truncated、metadataTruncated、omittedFiles 和 fileIssues。selectedFiles、packedFiles、returnedFiles 含义不同,无法计数时可为 null。maxTokens 是 UTF-16 字符÷4的估算,计入元数据和转义,不是真实模型 token。默认 compact 返回一个JSON块;legacy 还返回Markdown,两者都计入预算。 -```json -{"task":"核对保存逻辑","lineRanges":[{"file":"src/Service.cs","startLine":50,"endLine":80}],"maxTokens":2000} -``` +文本结果不构成跨请求原子快照。文件编辑、更换连接或需要新的代码范围时重新取证;临时 overflow 附件失效时重新获取上下文,不删除整个缓存。 -lineRanges 为闭区间、1 起始行号,最多 8 个文件,每文件一个范围、最多 500 行;越界报告缺口,预算不足仍可能截断。它跳过符号搜索,仅返回指定范围;不能与 symbol 或 includeFullText=true 同用。 +## 文本与 Roslyn 引用 -取证路由与停止条件:已知行号直接 lineRanges;只需声明附近片段时用 scopeFiles+symbol。审核已知方法的异常处理、取消或资源释放时,默认 24 行窗口往往不足;若已有文件读取工具,优先用有界 rg 上下文和文件读取一起覆盖所需分支,不必先调用 MCP 再逐段续读。小文件也可用 scopeFiles+includeFullText:true 在预算内读取正文,仍检查截断。仅知道文件用 scopeFiles 预览;需要发现其他文件时才用 candidateFiles/关键词检索。先检查片段是否覆盖问题所需代码,覆盖则继续分析,不例行再拉全文或重复相同范围。重名/缺失时收窄文件或转向已知行号;语义完整性不足需要相应语义工具,重复同一正则请求不能补足。文件修改、更换连接、截断或新问题需要不同代码时重新取证;本工具没有跨调用证据有效期保证,不能把旧片段当成当前文件。 +默认 source=local-text:有限声明扫描与文本引用线索,不提供编译器身份。lexical-uncertainty、degraded、queryComplete:false 表示实际限制;即使文本扫描完整也不能证明语义完整。实例提供方看 hello.codeProvider,不能只看 health.text.semanticConfigured。 -维护者可运行 npm run benchmark:agent -- 1 做单轮检查,或 -- 3 做三轮对照;报告在 test-tmp/agent-efficiency。它比较十类固定脚本场景(含既有 C# 夹具)的调用、返回字符、重复显示行和证据断言,使用真实 MCP handler 与本地回退,关闭外部后端。数据不代表真实用户任务频率、模型完成率或缓存收益,不据此宣称通用提速。Schema v2 校验当前文件、行号、正文和状态,异常保留为失败记录;复用只依赖受控夹具的可信无变化事件,修改后必须重取,不能作为生产环境的新鲜度判断。 +Roslyn 引用流程: -仅知道文件时用 scopeFiles:["src/Service.cs"] 排他限定最多 20 个文件;它跳过全仓库符号搜索,不能与 focusAreas 同用,candidateFiles/lineRanges 必须在其内。需要声明附近片段可加 symbol:"Save":大小写精确匹配,必须提供 scopeFiles,目前复用 .cs/.ts/.tsx/.js/.jsx/.py 本地声明模式,并非语义解析,queryComplete=false。重名、未找到或不支持语言会返回 fileIssues,不用文件开头冒充命中;可用已知行号进一步消歧。 +1. `wincode_find_code_symbol({query:"Save"})`,按 signature、file、location.project 选择声明。 +2. 原样传回所选 name/location: -metrics.selectedFiles 是选择数,packedFiles 是打包器实际处理数(片段模式为片段数),returnedFiles 是返回正文覆盖数;打包器缺少正文位置时为 null。relatedFiles.bodyStatus 表示 complete/partial/omitted/unknown;片段模式的 complete 仅表示该片段完整,不表示整个文件完整。小预算先裁辅助列表,metadataTruncated 提示列表可能不全。 +```javascript +wincode_find_references({symbolName: selected.name, symbolLocation: selected.location}) +wincode_analyze_change_impact({target: selected.name, symbolLocation: selected.location}) +``` -Repomix CLI 的 fileCount 使用独立运行摘要中的文件数,不从正文中的 File 标题估算;空包可以为 0。若已安装 CLI 的摘要格式不受支持或被配置静默隐藏,则明确降级为 builtin-fallback,并在适配器 lastError 记录原因。CLI 快照计数正确不代表其每个正文都有 WinCode 可用的位置映射。 +location 含 snapshotId、project/file 和零基 UTF-16 position;不猜偏移、不选首个同名项。不带 symbolLocation 的 Roslyn 引用请求只返回候选。编辑受跟踪输入、重载、释放 Host 或更换连接后旧定位可能失效;按错误重新搜索。 -bodyStatusScope 明确该字段描述 displayed-snippet 或 packed-file。symbol 请求返回声明附近窗口,symbolCoverage=unknown;即使 bodyStatus=complete 也不能认定整个方法完整。若所需逻辑仍在后方,可使用该证据的 nextRequest 续读最多 80 行;补读从最终尾行之后开始,半截尾行会完整重读。它不推测方法结束位置、不证明调用链完整,fileLineCount 仅为读取时的行数;编辑后重新定位。EOF 不再建议补读,最大预算无法读取完整长行时转用文件读取工具。 +引用的 limit 与最终 maxOutputChars 分别约束条数和文本。totalReferences 是当前快照发现数,returnedReferences 是返回数;检查 referencesTruncated/outputOmissions,必要时在有效 location 上提高预算。这不是分页,也不扩大生成代码、动态调用或未加载项目的覆盖。影响分析只聚合实际返回引用;未知总数或截断可为 null。已有影响报告时直接据其规划,不为通用步骤再次调用 plan_refactoring。 -2000 是首轮建议预算;证据不足再定向补充,确需文件正文才设 includeFullText=true。中文任务优先附上明确符号。startLine/endLine 是本次片段实际覆盖行,line 是其中的符号声明行;locationKind=file-start 只说明读到文件开头,evidenceInsufficient=false 不保证已取得回答问题所需的代码。完整模式的 packedContent 是正文,候选元数据不保证打包结果完整。 +Roslyn 当前排除分析器/生成器,queryComplete 为 false;零引用不证明可以删除。diagnosticSummary 的 compilationErrorCount/loadDiagnosticCount 是快照计数,samplesDisplayed/samplesOmitted 区分样例与省略。countsComplete 不包括未加载项目、分析器或警告,也不提高查询完整性。多个同名组件按路径和项目身份区分,不按显示名合并。 -保留 queryComplete、truncated、metadataTruncated、limitationsOmitted、omittedFiles/omittedFileCount 等字段的含义;预算裁剪后不得把缺失当成不存在。根据缺口收窄候选或增加预算,勿例行拉取全文。 +## 显式配置 Roslyn -检查 source、queryComplete、uniqueResolution/uniqueTypeMatch 与 limitations。source=roslyn 是编译器语义来源,不是文本回退;queryComplete=false 可以表示生成器或加载图等覆盖缺口,不能直接解释为执行中断或要求原样重试。文本回退不保证语义引用完整;零引用、UNKNOWN 或未找到均不证明可安全删除。 +仅在用户允许项目求值时用启动参数 `--roslyn-config <绝对JSON路径>`;不从目标仓库自动发现并执行配置。MSBuild 设计时求值可能执行 targets,不会自动 restore 或安装SDK。 -Roslyn 查询先读 `semanticContext.diagnosticSummary`,再读 limitations 中的诊断样例:`compilationErrorCount` 是当前加载快照的编译错误总数,`loadDiagnosticCount` 单列加载诊断;`samplesDisplayed/samplesOmitted` 区分展示与省略条数。`byCode/byProject` 各最多十组,`codesOmitted/projectsOmitted` 是未展示的组数;总数在裁剪前统计。`countsComplete=true` 只说明该快照的这两类诊断计数完整,不包括被排除的分析器、编译警告或未加载项目,也不提高 queryComplete。旧 Host 无统计时总数为 null、countsComplete=false,不从五条样例推断总数。诊断排查顺序见 [diagnostics.md](diagnostics.md)。 +```json +{"enabled":true,"allowProjectEvaluation":true,"project":"App/App.csproj","configuration":"Debug","targetFramework":"net10.0","dotnetPath":"C:/dotnet/dotnet.exe","hostPath":"C:/WinCode/tools/WinCode.Code.Host/bin/Release/net10.0/publish/WinCode.Code.Host.dll","additionalInputs":[]} +``` -影响分析用完整工作区文件路径及可用的项目身份区分组件,targetFile 和组件 name 仍是展示名称;不同目录同名组件可以分别出现,不要按 name 再合并。提供目录的 target 按工作区解析;只有纯文件名才用于候选匹配。Host 冻结源码沿用 Roslyn/MSBuild 的 CodePage 与 BOM 编码,返回位置仍按解码后的 UTF-16 文本计算,不按原始文件字节偏移定位。 +路径是示例。project 相对固定工作区,dotnetPath/hostPath 为实际绝对普通文件;不支持链接。配置最多16KiB;loadTimeoutMs 默认120000、范围1–120000,queryTimeoutMs 默认30000、范围1–60000。配置、TFM与入口固定于实例,改变后需重建连接。 -若已有影响报告,直接据此规划,不为获得通用清单再次调用 plan_refactoring。该工具仍会做影响分析;它返回的 evidence 保留歧义、降级和 UNKNOWN,不代表已经执行重构。 +additionalInputs 用于非标准构建数据:最多32个工作区内相对文件,数组JSON最多4096个UTF-16字符;不接受目录、glob、重复或链接。缺失输入应恢复或修正配置,不静默删除。自动跟踪已加载源码、项目、引用及常规构建配置,不保证发现自定义 targets 的任意外部输入;diskFreshnessVerified/externalCustomInputsVerified 仍为 false。 -仅在用户授权移除文件时使用 wincode_safe_move_to_trash({filePath:"相对路径",reason:"原因"});它会实际移动文件。重构计划本身不执行修改。 +INPUT_BUDGET_EXCEEDED 时缩小合法输入范围,不接受截断快照。默认发现排除生成/缓存目录,实际加载与显式输入优先。内部 Host 协议不作为 MCP 参数发送;维护时以交付源码为准。交付需完整 Code Host publish 目录,包括依赖和 BuildHost-netcore,不能只复制DLL。 -trash 响应保留 success/trashPath/message,并用 outcome 区分 completed(移动及元数据完成)、not_moved(本次未移动)、partial(已移动但元数据未完成)。partial 的 errorCode=TRASH_METADATA_FAILED、failureStage=metadata,originalPath/trashPath/metadataPath 给出原位置、实际移动位置及预期元数据位置;metadataPath 不证明元数据完整。立即保留并告知用户实际 trashPath,不把 success=false 当作未执行,不重复移动或自动移回。not_moved 的 trashPath 为空,errorCode=TRASH_NOT_MOVED;先检查 failureStage 和文件实际状态。重启不会自动补写元数据或推断原路径;丢失 partial 响应时,本实现不保证自动恢复原目录映射。 +## 文件移除 -取消/截止检查传递到路径准备和移动前;文件一旦移动,就继续收尾元数据并保留真实 completed/partial 与路径,Gateway 不再用通用超时替换该结果,受理统计仍记录超时或取消。客户端主动取消或断连后不保证收到最终响应,也不代表已经回滚;此版本没有新增事务日志或自动恢复。 +仅在任务授权移除文件时调用 wincode_safe_move_to_trash;重构计划本身不执行修改。查看 outcome: -回收站目标名含唯一标识,过长的原文件名展示部分会截短,以给元数据文件名预留空间;完整原路径保存在 originalPath 和成功写入的元数据中。恢复时使用这些路径,不从截短的目标名推断原文件名或扩展名。 +- completed:文件移动和元数据均完成。 +- not_moved:未移动,检查 failureStage 和实际文件状态。 +- partial:已移动但元数据失败。保留 originalPath/trashPath/metadataPath 并告知实际位置,不重复移动或自动移回;metadataPath 不证明内容已写入。 -交付时保留 Code Host 整个 publish 目录,包括 deps/runtimeconfig、Roslyn 依赖及 BuildHost-netcore 子目录。`npm run check` 生成并核对交付清单;Host ready 身份必须与 Gateway 版本一致且为 Release、协议 v2,否则 HOST_VERSION_MISMATCH。不要仅复制入口 DLL,也不要把版本握手等同于运行时文件防篡改。 +取消或断连不代表回滚。恢复依据完整路径和成功写入的元数据,不从截短的展示文件名猜原路径。 diff --git a/skills/wincode/references/diagnostics.md b/skills/wincode/references/diagnostics.md index 8687c0a..f5faa19 100644 --- a/skills/wincode/references/diagnostics.md +++ b/skills/wincode/references/diagnostics.md @@ -1,170 +1,136 @@ -# 诊断与审计 +# 诊断与恢复 -## 设计时语义诊断 - -连接成功、精确符号定位和全项目语义覆盖分别判断。读取 `semanticContext.diagnosticSummary` 的总数、错误码和项目分组,再结合 `excludedAnalyzers`、limitations 样例与实际源码判断影响;展示五条不等于只有五条错误。分组省略数大于零时,未展示的项目或错误码不能当作不存在。引用响应预算不足可显式提高 maxOutputChars;不能容纳必要元数据时会返回 OUTPUT_BUDGET_EXCEEDED,不静默删除统计。 - -出现 CS0234 等缺失命名空间错误,先核对入口及引用项目各自的 TargetFramework、已有 restore assets 与实际 MetadataReference,再判断是否真缺少包。正常构建成功也不证明设计时加载一致;不要仅凭错误码安装依赖、自动 restore、修改项目框架或扫描 DLL 补引用。单目标依赖应保留自身框架,多目标引用仍遵循现有显式配置,本实现不承诺自动选择最近兼容目标。 - -出现 CS8795 等 partial 实现缺失,结合声明、项目生成器配置及排除数量核实原因;错误码本身不是生成器故障的充分证据。WinCode 仍排除分析器/生成器,不自行开启或执行它们。queryComplete=false / incomplete 是保守覆盖契约,即使诊断为零也保留;有效局部引用可继续使用,但不能据此主张全局无引用或安全删除。计数未知时先核对已连接 Host 与本地交付版本,不原样反复调用或擅自重启服务。 +按需启动读[会话入口](#skill-按需会话);连接或版本问题读[连接与交付身份](#连接与交付身份);运行失败按[错误处理](#错误处理)、[UI 取证失败](#ui-取证失败)或[Roslyn 诊断](#roslyn-诊断)查阅。 ## Skill 按需会话 -适用于能保留交互终端会话的 Codex。首次确实需要 WinCode 才启动入口;不要因加载 Skill、开始聊天或查看配置而预热。目标工作区取当前任务的绝对目录,安装目录取已核实的本机交付位置(已有 MCP 配置中的 `dist/index.js` 路径也可用于定位);不照抄其他机器或历史测试项目的路径。 +已有正确工作区的原生 MCP 连接时直接使用。按需模式适合能保留交互终端的客户端;安装切换需禁用原生 WinCode 自动连接并刷新客户端,日常调用不自行改配置。没有持久终端时使用已配置的原生连接。 -按需模式要求客户端不再自动连接同一个 WinCode:安装切换时将其原生 MCP 配置禁用,并让客户端刷新连接。保留原配置以便恢复;日常调用不自行修改客户端配置。有可用且工作区正确的原生连接时直接使用,不同时再启动按需入口。没有持久执行工具时使用已配置的原生 MCP;不要每次调用都临时启动、关闭服务器。 +安装路径从实际客户端配置或已核实交付目录取得,不沿用别人的机器路径。工作区使用本任务的绝对目录。 -**明确指定实验构建的验收。** 若已有原生连接绑定正确根,但其 `runtime.build.buildId` 与所验收的分支构建不同,磁盘更新不会热替换该连接。先核对分支构建的源码/产物身份;在已明确要求试用该构建的任务中,可保留旧连接与配置,仅用一个分支 CLI 会话顺序调用标准工具和配方,完成后关闭。不要同时向旧、新连接提交同一业务任务,也不要为了试验静默覆盖安装 Skill 或禁用全局 MCP。这是一轮指定构建的验收,不表示已经切换默认安装。 +### 启动与复用 -**启动一次。** 通过 `exec_command` 执行以下命令,必须设置 `tty:true`(普通管道会立即 EOF),保留返回的 `session_id`。PowerShell 路径用正确引号转义,不把不可信文本拼成命令: +通过 exec_command 启动,设置 tty:true,保存返回的 session_id: ```powershell node "/dist/Client/SkillSessionCli.js" --workspace "<目标工作区绝对路径>" ``` -仅目标项目已有明确授权的 Roslyn 配置时追加 `--roslyn-config "<配置文件绝对路径>"`;不沿用其他项目的配置,也不从工作区自动发现并执行配置。默认 local-text 不执行 MSBuild。`ready` 回执包含入口 `ownerPid`;此时 `status.state="unused"`、Gateway `pid=null`。首次工具请求才创建 Gateway,并校验工作区、构建和提供方;随后复用同一 Gateway、Host 和有效快照。 +只有目标项目已获准使用 Roslyn 时才追加 `--roslyn-config "<配置文件绝对路径>"`。入口 ready 包含 ownerPid;初始 status.state=unused、pid=null,首次工具请求才创建 Gateway。一次任务复用此会话,不逐次启动新进程。 -**连续调用。** 使用 `write_stdin` 向同一个 `session_id` 发送单行 JSON;Windows 交互终端的 `chars` 以 `\r` 结束。请求 `id` 使用本会话内不同的 1–64 位字母、数字、点、下划线或连字符。示例请求体: +使用 write_stdin 向同一个 session_id 发送单行 JSON。Windows 交互终端的 chars 以 `\r` 结束;一次请求收到回执后再发下一次。工具返回仍在运行时继续等待原会话,不重复提交。 ```json {"id":"q1","tool":"wincode_search_text","arguments":{"query":"Save","scopePaths":["src"]},"timeoutMs":120000} ``` -一次只发一个工具请求,等待该 `id` 的回执再继续;若执行工具先返回正在运行,继续轮询同一个执行会话,不重新发送请求。`timeoutMs` 默认 120000,范围 1–180000,包含本次调用的连接等待;单独握手有 30 秒限制。请求行最多 64 KiB。终端回显或折行不是第二个响应,不能当作完整 MCP JSON。 - -**读取结果。** 回执提供 `resultFile`、`isError` 和 `imageFiles`。用文件/执行工具读取 `resultFile` 中的完整 MCP `CallToolResult`,包括 text、structuredContent、image 等内容块;有图片时按需使用本地图片查看工具读取 `imageFiles[].path`。不要把 `isError:true` 当成成功,也不要只读短回执就推断业务结果。原始 JSON 和图片保存到安装目录的 `test-tmp/skill-sessions/run-*/`,关闭不会自动删除,按本地附件管理;不要提交这些文件。文本结果至少需要一次额外文件读取,这是按需模式的调用成本。 - -**客户端混合只读配方。** WinCode 0.17.0 提供 `action:"readonly-ui"`,无需宿主执行 TypeScript 或提供模型 API key。先按上面的单工具协议列出窗口并明确选择 PID/HWND,再调用已安装的 `checkbox-audit`: - -```json -{"id":"audit1","action":"readonly-ui","recipe":"checkbox-audit","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"summaryAutomationId":"summaryId","regionAutomationId":"regionId","checkboxAutomationIds":["checkA","checkB"]},"timeoutMs":15000} -``` - -示例 PID/HWND 和 AutomationId 必须替换为实际选择结果。summaryAutomationId 可省略;提供时先读摘要,On 才继续,Off 跳过详情,未知停止。明确列出的 1–64 个复选框必须唯一、完整且有确定状态,不从缺失状态推断 false。maxDepth 默认 4、maxNodes 默认 300,可在 parameters 中按原生工具范围调整;timeoutMs 默认 15000、范围 1–30000。只有这一个预置配方,不接受源码、表达式或任意工具名;未知字段在请求执行前拒绝。普通 MCP 工具和旧 JSON 单工具协议继续可用,这不是服务器新增工具。 - -仍读取 resultFile 的完整 content 并处理 isError;配方 text 是步骤证据与 findings,多个步骤是有序观察而非原子快照。结果可能包含控件文字,不是匿名记录。cancel/close 沿用下文协议,targetId 对应配方请求 id。仅在核实使用 0.17.0 或兼容新构建时调用,旧入口没有此 action。 +id 为1–64位字母、数字、点、下划线或连字符,任务内不重复。单工具 timeoutMs 默认120000、范围1–180000,包含连接等待;单行请求不超过64KiB。 -**明确展开导航。** 用户授权这条导航路径后,可调用 `action:"expand-ui"`;它不是只读配方,不加 `recipe` 字段。例:`{"id":"nav1","action":"expand-ui","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"automationId":"实际祖先区域ID"}],"parentQuery":{"name":"实际父级标题","controlType":"Group"},"childQuery":{"automationId":"实际子控件ID","controlType":"CheckBox"}},"timeoutMs":15000}`。scopePath 可省略;每项来自实际观察,parentQuery/candidateQuery 相对于该范围定位。首次子查询和最终读回限定在指定父级内,不读取旁支同名控件。完整结果保留父级诊断、是否尝试动作、后续状态和归属证据;`isError:true` 或 `status:"stopped"` 时不能引用 findings 为成功,也不能自动重放展开。带父路径的读取需要 inspectionVersion 5,旧连接不会热更新。 +### 读取完整结果 -已授权在指定窗口尝试发现折叠 Group 时,可省略 `parentQuery`:`{"id":"nav2","action":"expand-ui","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"childQuery":{"automationId":"实际子控件ID","controlType":"CheckBox"}},"timeoutMs":15000}`。该形式每请求最多 6 次工具调用、1 次展开;目标已可见则只读取 1 次。唯一候选可继续;多候选返回 `selection-required`,由当前 AI 根据任务与实际候选文字选择。省略字段仍表示授权导航尝试,不是只读查询;Group 之外的路径和递归探索不在本轮能力内。 +回执中的 resultFile 指向完整 MCP CallToolResult;读取该文件,核对 isError 和业务 success,保留全部内容块。imageFiles 指向原始图片,可用本地图片查看工具读取。终端回显、折行或仅有路径都不是完整结果。 -**选择并继续。** `selection-required` 的 `isError:false` 表示已取得待选择证据,`success:false` 表示任务尚未完成;不要据此回答目标状态。读取完整结果,选择 `diagnosis.candidates` 中与任务相关且有 `nextRequest` 的一项。将该 nextRequest 原样补上新 id 提交到同一 CLI。合法形状示例:`{"id":"nav3","action":"expand-ui","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"childQuery":{"automationId":"实际子控件ID","controlType":"CheckBox"},"candidateQuery":{"name":"实际所选候选标题","controlType":"Group"}},"timeoutMs":15000}`;实际各字段使用返回请求中的完整值,不能照抄示例占位值。nextRequest 使用 candidateQuery,下一请求会按该选择器重新定位并核验当前 Group;完整搜索无匹配或多义为 `NAVIGATION_SELECTION_STALE`,其他原因分别报告,见下段。续接本请求时保留返回的 target/childQuery,不默认首项或自动遍历列表;同一用户任务获得新观察后,可以另外发起合法查询修正。每个 expand-ui 请求最多一次展开;获得新的实际观察后可继续同一任务的定位。选择不足以确定时先只读核查,确有影响任务选择的歧义再澄清,动作结果未知时先核查状态。 +结果保存到安装目录的 test-tmp/skill-sessions/run-*,关闭后仍保留;可能含源码和窗口内容,不直接公开或提交。每次调用需要额外读取结果文件,这是按需入口的实际成本。 -**状态续接与局部诊断。** 第十五轮的 candidateQuery 直接重新定位所选 Group,不重新发现全窗候选。当前唯一且有效的 Expanded 父级可继续读取,零额外展开;Collapsed 才操作一次。完整搜索无匹配/多义为 NAVIGATION_SELECTION_STALE;实际禁用、证据未知和搜索不完整分别报告准确原因。父级观察 Expanded 后仍缺目标时,读取 diagnosis.localObservation 和 nextAction:它保留本次父树范围、完整性、实际匹配与内层折叠候选,区分缺失、歧义和截断。任务未完成仍没有 findings,不把局部候选当成功答案,也不重放外层动作。局部 id/parentId 只用于本次关系说明;内层项没有可执行 nextRequest,下一步先独立唯一定位,详见[UI 手册](ui.md#操作方式与授权)。 +### 客户端 UI 流程 -**父范围与版本。** inspect/review/setExpanded 接受 scopePath,需要 inspectionVersion 5;expand-ui 的 parameters 也接受可选 scopePath。发现和选择保留祖先范围,父级确认后的子查询追加实际父选择器,每次重新定位。当父查询的 automationId/name/controlType 与路径末项完全相同时,直接复用该范围,避免把范围根再当作严格后代查找。路径最多 50 项;需要追加父级时,调用方路径最多 49 项。调用方祖先失败报告 SCOPE_NOT_FOUND/SCOPE_AMBIGUOUS/SCOPE_SEARCH_INCOMPLETE,不回退全窗;内部追加父级的失败保留原有 QUERY_* 或候选失效分类,steps 留存原始 scopeResult。即使请求未显式提供 scopePath,expand-ui 有父级时也会在首次/最终子查询构造父路径,因此需要版本 5。setExpanded 带路径时在发送动作前检查能力;VERSION_MISMATCH 不自动重试或改用无路径动作。调用前核对实例能力;字段与示例见[UI 手册](ui.md#操作方式与授权)。 +先列窗并确定 PID/HWND,再选择[UI 手册](ui.md#只读配方)中的请求: -**预算与范围的实现边界。** 单次 expand-ui 的 deadline/调用数由程序限制;发现和选择请求各自计时,没有跨请求累计预算或绑定上次任务范围的校验,当前也不要求先新增这些框架。nextRequest 继承原字段属于请求构造,不是不可更改的权限令牌。沿用用户选定窗口与任务,不为已授权的导航逐步重复确认。不要把实验宿主的历史总预算说成日常 CLI 已实现能力。第十四轮允许展开导航中的 className/bounds/isOffscreen 缺口并保留记录;TARGET_EVIDENCE_INCOMPLETE 区分启用/身份证据未知与实际禁用。其他属性、普通只读工作流/源码门槛和自动递归导航仍是实现限制。 +| action | 用途 | +|---|---| +| readonly-ui + recipe:checkbox-audit | 指定复选框统计,可按摘要状态决定是否读详情 | +| readonly-ui + recipe:sibling-range | 读取两个同级标题之间的条目 | +| expand-ui | 最多展开一次父级,再读目标复选框;带状态验证 | -**调用与回答的顺序。** 先确认实际窗口及页面,再提交工具/配方请求,等待相同 id 回执,读取完整 `resultFile`,核对 `isError`、业务状态及证据完整性,最后按实际观察回答。`{recipe,parameters}` 只是输入,打印参数不能代替调用。标准 inspect 的 `success:true` 只说明查询完成;仍须检查 `queryResult.status="unique"`、`searchComplete=true`、`treeComplete=true`、截断/属性问题及所选控件的明确状态。`not-found` 不能推成未勾选或数量为零。配方须为 `status="completed"`、`success=true` 且具有符合任务范围的 findings;`stopped` 或 `isError:true` 应保留错误与已有步骤,不自动重放。 +这些是客户端 action,不是 MCP 工具名,不接受任意源码或表达式。timeoutMs 默认15000、上限30000。非法配方参数在连接/读取前拒绝,回执带 requestError、errorCode、field、recoveryAction 和 workStarted:false;据反馈修正参数,不把修正建议当已执行结果。 -源码中的 AutomationId 是定位候选,先确认运行时页面/展开状态;折叠内容可能不在当前 UIA 树中。缺失后先说明观察范围,不能按源码默认值补答案。用户提供明确的新页面状态后,可重新安排一次有界观察,并将此前失败单独保留;不要反复发送相同请求碰运气。精确查询减少返回内容,但不保证查询搜索过程完全不访问其他节点的属性;日志和原始附件保持本地。 - -**观察、取消、关闭。** 同样发送单行 JSON: +### 取消与关闭 ```json -{"id":"s1","action":"status"} -{"id":"c1","action":"cancel","targetId":"q1"} -{"id":"end","action":"close"} +{"id":"cancel1","action":"cancel","targetId":"q1"} +{"id":"status1","action":"status"} +{"id":"close1","action":"close"} ``` -`status` 只读本地连接状态,不启动 Gateway。取消必须对应当前活动请求;`cancellationRequested` 只证明信号已发送,不能解释为 Host 已退出。任务结束、放弃或准备换工作区时发送 `close`,等 `closed:true` 和入口退出;一轮内仍需后续查询时保留连接。调用异常时也在收尾中关闭。EOF/入口死亡会关闭 Gateway 输入,原有 Gateway/Host 所属进程清理继续生效;当前终端工具持续保留入口时不会自动到期释放。 - -`requestError` 是入口拒绝,`transportError` 是连接/协议/取消失败,不能据此认定业务没有执行;`resultDeliveryError` 且 `toolResponded:true` 表示已收到工具结果、但附件交付失败。出现这些错误先核对实际状态,尤其文件移动,不自动重放。关闭或失联后要显式建新会话并重新搜索声明,不能复用旧 `location`。不要使用 Node REPL 绕过模块限制:当前 SDK 需要 `node:process`,本机 REPL 不允许该导入;支持的入口是上述交互终端。 +cancel 的 targetId 指向当前请求;确认收到取消不表示工作已回滚,仍读取目标的最终结果。任务结束或放弃时发送 close,检查 closed/status 和进程退出。stdin EOF 同样触发清理;关闭失败保留错误,不因为终端结束就宣称清理成功。 -## 原生 MCP 连接与故障诊断 +### 指定构建验收 -跨项目入口:新构建的 `WORKSPACE_MISMATCH` 响应包含 `connectionGuide`,其中 `configuration.command/args` 是独立 STDIO 连接配置,`verification` 给出连接后检查工作区的调用。也可运行 `node /dist/index.js --print-connection --workspace <目标绝对路径>` 输出同一配置;不创建缓存、不注册或重启客户端、不启动项目 Host。配置默认 local-text,不复制已有 Roslyn、开发或托盘选项;目录存在性在实际连接启动时校验。选择已有正确连接优先,建立新连接仍遵守用户授权。刷新连接后再使用新增导航工具或 UI 精简参数,磁盘重建和 Skill 同步不会热替换旧 MCP Schema。 +用户明确要求测试当前分支构建,且现有连接 buildId 不符时,可保留旧配置,用一个分支 CLI 会话顺序调用;核对磁盘与实际 hello 身份,结束后关闭。不同时向旧、新连接提交同一业务任务,也不为测试静默替换默认连接。 -0.16.0 的 WORKSPACE_MISMATCH 是固定工作区拒绝:检查 activeWorkspace/requestedWorkspace,选择对应项目连接。错误发生在工作区资源变更之前,不表示旧根已切换或需要清空缓存。hello.health.workspaceBinding 给出固定根及启动来源;argument 是显式 CLI 参数,cwd 是启动目录回退,configuration 是嵌入式配置。显式 --workspace 必须有绝对目录值;已有连接不会因磁盘重建或配置保存自行更新。 +## 连接与交付身份 -0.16.0 的 health.admission 返回 business/status 的 active、executing、waiting、accepted、completed、rejected、cancelled、timedOut、peakActive,以及累计 waitMs/executionMs 和 maxWaitMs。每实例最多 32 个未完成业务请求、4 个共享轻量状态请求;内层互斥保持 FIFO,运行中取消须在实际清理后归还容量。workspace_open 占用业务容量,但不计入它自己等待排空的 inFlight。状态不等待慢查询或同根恢复;tools/list 满额以协议错误 data.errorCode=SERVER_BUSY 表达。 +工具和参数不符时调用 `wincode_hello_world({toolName:"具体工具名"})`,比较 tools/list、schemaHash、runtime.instanceId/build.buildId 和 workspace。hello 只读已有状态,不启动探测;unknown、available:null 或 not-observed 都表示未知,不能当作不可用或零值。 -计时口径:waitMs/maxWaitMs 按已结束请求累计其显式队列等待;executionMs 是队列以外的墙钟耗时,包含 I/O 和取消清理,不是 CPU 用时。active/executing/waiting 为当前请求数;取消/超时计数是 completed 的子集。 +需要主动检查环境时用 `wincode_diagnose_project({})`;它检查SDK及Repomix/UIA,对Roslyn只读已有状态。不要给 hello/diagnose 传 forceReconnect、toolNames、version 等未声明字段。 -SERVER_BUSY 附 workStarted=false、retryable=true 和容量快照,仅说明该次请求尚未开始。按需稍后重试,不自动重放或重启 Host。REQUEST_TIMEOUT 包括启动、排队和执行预算,retryable=false;核对实际结果和恢复状态。原始参数(含未知字段)按 UTF-8 JSON 限制为 64 KiB,超限为 INVALID_ARGUMENT;该限制不消除 SDK 已解析帧的瞬时分配,也不保证总 RSS 或挂起 OS I/O 的强制终止。 +原生 stdio 启动命令为 node,独立参数为 `<安装目录>/dist/index.js`、`--workspace`、`<绝对工作区>`。不要把整条终端安装命令填入启动命令。配置保存、磁盘构建和 Skill 同步都不会热更新运行实例;按客户端正常方式重连。 -hello.health.cache 仅读取内存及最近显式磁盘观察:diskObservation=not-observed 时 diskEntries/estimatedDiskBytes/diskObservedAt 为 null;incomplete 表示读取不完整。known 及其 diskObservedAt 可能已过时。主动 diagnose_project 才刷新磁盘统计。 +生产使用完整 Release 发布目录。UIA 查询/状态、语义动作、展开、父路径分别需要 inspectionVersion 2、3、4、5。VERSION_MISMATCH 时核对实际 Host 身份及发布文件,不删除范围或状态参数绕过版本检查。 -0.13.0 已退役外部 Serena。默认 local-text 不提供编译器语义,C# 语义需显式配置直接 Roslyn;维护入口为 test:roslyn-host 与 test:roslyn-gateway。 +| 仓内维护命令 | 证明范围 | +|---|---| +| npm run check | 锁定构建、核心回归、生产 stdio、交付清单 | +| npm run check:desktop | 隔离桌面流程,不代表任意用户软件通过 | +| npm run delivery:verify | 磁盘Gateway、Host、四份受管Skill文件一致;不验证现有客户端 | +| npm run test:roslyn-host / test:roslyn-gateway | 生成夹具的语义与进程验证,可能还原夹具依赖 | +| npm run skill:check -- <绝对Skill目录> | 只读比较四份手册;不一致退出码2 | +| npm run skill:sync -- <绝对Skill目录> | 先备份旧手册,再同步并校验哈希;保留其他文件 | -从 0.12.4 起 Repomix 健康探测和打包都由当前 Node 可执行文件直接启动已安装的 JavaScript CLI;不经过 cmd、npx 或 PATH 包装脚本,也不下载包。默认按目标工作区和 WinCode 安装目录的 Node 模块路径读取 repomix/package.json 的 bin 入口;不搜索 npx 缓存或 npm 自定义全局前缀。非标准安装需在宿主 WinCodeConfig.adapters.repomix.customCliPath 提供绝对 .js/.cjs/.mjs 路径;该字段不是 MCP 工具参数,不能传给 hello/prepare_context。显式路径无效时返回 builtin fallback,不执行另一份安装;useCli=false 仍完全禁止探测和启动。执行已安装脚本不提供沙盒或脚本可信性保证。 +维护命令从 WinCode 仓库执行,环境要求以 global.json 和包配置为准。缺少SDK或依赖时按用户授权处理,不自动安装。Skill备份位于同级 .wincode-backup-*,文件用 .bak 后缀;同步不改MCP配置,也不证明当前会话重新加载了Skill。 -`hello` 从 0.12.1 起只读取版本、能力和已知状态,不启动上游、CLI 或 UI Host 探测进程。`health.healthObservation` 区分 `known/unknown` 并给出 `observedAt`;`unknown` 或 `available:null` 表示尚未探测,不能解释为不可用。配置禁用属于已知策略,但观察时间可为 null。已知健康结果可能陈旧,需要当前检查时调用现有 `wincode_diagnose_project({})`,不向 hello 添加未声明的 force/probe 字段。 +## 错误处理 -`wincode_diagnose_project` 会检查 SDK 并主动探测 Repomix/UIA;对 Roslyn 只读取已有加载状态,不会启动 Code Host 或执行项目加载。已授权配置 Roslyn 后,首次明确的符号搜索才触发加载。`health.healthObservation` 当前包含 text/repomix/flaui;Roslyn 的观察时间与快照状态在 `health.roslyn`,不要按旧 Serena 字段判断。 +先保存完整响应、requestId、实际阶段和必要日志,再决定下一步。isError 与业务 success 都要检查;领域工具还可能返回 partial。未知动作结果先读回,不能统一按“未执行”重发。 -代码查询、引用、上下文、影响分析和重构建议接收 MCP 取消信号;停止后续扫描/打包,等待当前读操作或自有上游清理后释放请求占用。Roslyn 取消传播到自有 Host;合作取消未及时完成时按既有超时策略清理自有进程树。磁盘单次 OS I/O 不保证瞬时中断。同根资源恢复在等待和提交前可取消;已开始恢复时保留实际一致性状态,不声称已回滚。 +| 错误/状态 | 处理 | +|---|---| +| WORKSPACE_MISMATCH | 按 activeWorkspace/requestedWorkspace 选择连接,原根未切换 | +| WORKSPACE_RECOVERY_REQUIRED | 查看 recoveryAction;workspace_open 只恢复同根,restart_gateway 则先核查自有资源清理,再重建对应连接 | +| SERVER_BUSY | workStarted:false 表示尚未开始。等在途工作结束后按需重试,不循环请求或重启Host | +| REQUEST_TIMEOUT / CANCELLED | 包含启动、排队、执行或收尾;核对实际结果及恢复状态 | +| HOST_UNAVAILABLE / VERSION_MISMATCH | 核对配置路径、完整发布目录与实际版本 | +| OUTPUT_BUDGET_EXCEEDED | 根据具体工具缩小范围或提高允许的输出预算;保留缺口 | +| OUTSIDE_WORKSPACE / UNSUPPORTED_LINK | 修正为授权范围内的实际路径,不放宽边界 | -`health.resourceCleanup` 是最多 100 条资源关闭记录(owner、kind、closed/failed 与最多 1024 字符错误),`omitted` 表示更早记录被省略。进程数量为零不能替代这些结果或真实 PID 退出证据。关闭失败会向调用方抛出,重复关闭保留失败;初始化失败会尝试释放已取得资源。记录只保存在当前进程内,不是持久审计或防篡改证明。 +每实例最多32个在途业务请求、4个轻量状态请求,UI仍有独立互斥。health.admission 的 waitMs 是队列等待,executionMs 包含I/O与清理,不是CPU时间。health.resourceCleanup 是有界内存记录;需要证明退出时同时核对自有PID,不按客户端名称清理其他进程。 -规范输入:`wincode_hello_world` 仅支持可选 `greeting`(字符串,最长 1024)和 `toolName`(非空字符串,最长 128,选择本实例支持的工具);`wincode_diagnose_project` 没有业务参数。`toolNames`、`forceReconnect`、`version` 不是这些工具的规范请求字段,额外字段被忽略,不会重连、切换版本或批量查询。示例:`wincode_hello_world({toolName:"wincode_prepare_context"})`。未知字段容忍不等于已有字段错误类型也能通过。 +缓存统计也可能是历史观察。overflow附件失效就重新取得上下文;不为修复单个问题全局删除缓存。Repomix未配置或失败时可返回builtin-fallback,查看实际source/lastError;显式customCliPath是宿主配置,不是MCP参数,不用npx临时下载补救。 -更新仓库后,先用 `npm run skill:check -- <已安装 wincode 目录的绝对路径>` 核对四份受管手册;不一致退出码为 2。明确更新时使用 `npm run skill:sync -- <同一路径>`,先在同级 .wincode-backup-* 目录以 .bak 后缀备份旧手册(避免备份被发现为重复 Skill),再写入并校验哈希;其他文件保持原样。此操作不注册 MCP、不改客户端配置、不重启运行实例。检查本机安装内容与仓库一致也不证明当前连接加载了新版。 +## UI 取证失败 -仅遇到故障或用户要求时调用 wincode_hello_world({}) 查看适配器、工作区及 runtime;环境问题再用 wincode_diagnose_project({})。本地文本健康成功不证明 Roslyn 已配置或项目已加载;watcher 停止、最近超时和清理错误如实报告,不自动安装依赖或循环重启。 +Host 必须显示 REC/WinCoding 提示并写入审计。`Recording indicator could not be displayed` 表示提示未就绪,UI访问被拒绝;保留原生错误、审计阶段及耗时,检查自有Helper是否退出。没有根因证据时,不修改提示门槛或把后续成功当成已修复。 -原生模式工具不可用:先确认客户端是否启用了 wincode MCP;已保存配置通常需重新加载客户端/会话。按需模式按本手册首节使用执行会话,不要求注册 MCP。安装路径取实际客户端配置,不沿用历史机器的 I:/WinCode。STDIO 配置结构(占位路径需替换): -- 命令:node -- 独立参数:/dist/index.js、--workspace、<目标工作区绝对路径> +AUDIT_BUSY 表示另一Helper持有审计锁,等其完成;不终止目标应用。日志位于 `%LOCALAPPDATA%/WinCode/logs/ui-audit`,1MiB提醒,接近2MiB时预留结束记录空间并拒绝新访问。出现 auditNotice.message 时简短转告大小、完整路径和建议。 -不要把 codex mcp add 整条终端命令填入启动命令。不要重复注册或静默修改配置。VERSION_MISMATCH 可能表示新网关配了旧 Host,局部查询/状态要求 inspectionVersion=2;按授权重新构建发布。HOST_UNAVAILABLE 时检查已配置 Host 路径/发布产物;构建或环境变更按用户授权执行。 - -出现 auditNotice.message 时把大小、建议和完整路径简短转告原用户。日志目录为 %LOCALAPPDATA%/WinCode/logs/ui-audit:1 MiB 提醒,2 MiB 前预留结束空间并拒绝新 UI 访问;不自动删除。AUDIT_BUSY 表示另一 Helper 占用审计锁,等其完成后再按需要重试,勿杀目标应用。 - -需要手动检查时执行已有只读脚本: +需要检查时使用安装目录的现有脚本: ```powershell pwsh -NoProfile -File "/scripts/check-ui-audit.ps1" ``` -仅用户明确需要桌面弹窗时加 -Desktop;不例行弹窗。日志只有 start 表示结果未知;本地日志不是防篡改证据。清理须获得授权、停止相关调用并保留用户需要的记录,不能为了恢复取证静默删除。 - -从 0.12.2 起,生产模式仅使用发布的 Release Host,缺失时明确不可用;`npm run dev`(`--development`)才允许 Debug/dotnet-run 回退。`customHostPath` 是显式配置覆盖,不是 MCP 请求字段。Host 响应的 `hostIdentity` 来自实际程序集,包含 version、informationalVersion、configuration 与 framework;旧 Host 未提供身份时不能推定版本一致。 - -仓内 `npm run check` 执行锁定构建、核心回归和生产 stdio,生成并校验 `dist/delivery-manifest.json`;`npm run check:desktop` 单独运行隔离桌面闭环。`npm run delivery:verify` 检查 Gateway、发布 Host 全部文件及四份受管手册的一致性,不启动 Host,也不验证另一个客户端实例或签名真实性。构建要求 Node 24(22 兼容)和 `global.json` 中锁定的 SDK;缺少环境时按授权安装,不自动修改环境。 - -WORKSPACE_RECOVERY_REQUIRED 表示固定根内资源恢复尚未完成或自有资源清理失败。业务工具被拒绝;被动 hello 可读取 health.workspaceRecovery,status=recovery_required。recoveryAction=workspace_open 表示对同一绑定路径重试恢复,只有完整重置/初始化及 watcher 绑定成功才恢复请求;不能传另一根绕过恢复门。restart_gateway 表示当前实例保留清理失败,重新打开无法恢复;先检查 Gateway 自有资源清理,再按客户端正常流程重建连接,不自动重启或终止目标应用。永久失败后的同根确认不会反复重建会话。CANCELLED 若附带 workspaceRecovery,按其 recoveryAction 处理;健康概览取消仍保持原状态。 - -0.13.1 中,已知工具执行失败的 JSON 文本与 structuredContent 同源;Gateway 异常含 success=false、errorCode、errorMessage、provider 和 recoveryAction。UI/trash 保留领域字段及实际位置,不要求所有领域错误具有 Gateway 字段;图片保持独立 image 块。未知工具在正常受理状态下返回 JSON-RPC -32602 协议错误,不返回 isError 结果;关闭/取消的入口拒绝优先于工具查找。旧连接不能套用此契约,先核对实际版本。恢复动作不表示已经回滚或允许原样重试。 - -直接 Roslyn Host 与 UIA Host 是不同组件。新 Gateway 的 hello.codeProvider 和 health.roslyn 报告显式选择的提供方、已知观察、processAlive、snapshotId 及重载/重启/清理状态;hello 不启动 Roslyn 或执行项目,进程存活不等于当前磁盘语义已验证。ready 是内部握手帧,UIA 的 VERSION_MISMATCH、inspectionVersion 等不能套到 Code Host。当前 npm run check / delivery:verify 不替代 test:roslyn-host/test:roslyn-gateway;当前交付清单已覆盖 Code Host 完整发布目录,但不证明实际客户端已启用 Roslyn。 - -Roslyn 运行中已观察到的加载、查询或清理错误也纳入 health.lastAdapterError,provider=roslyn;health.roslyn.health.lastError 保留对应观察。lastError 是历史最后一次失败,不表示每次 hello 都执行了健康探测,也不能据此自行重放业务请求。工作区完整重置后观察清空。 - -Code Host 内部协议 v2 的失败包含 success=false、errorCode 和 error,且不附带旧引用。SNAPSHOT_STALE/INPUTS_CHANGED 在内部协议层要求等写入稳定后显式 reload,再用新身份定位;MCP 客户端应重新调用 wincode_find_code_symbol,由适配器执行所需重载,不存在 wincode_reload 工具;PROJECT_LOAD_FAILED 表示结构化 MSBuild 加载失败,先修复项目输入,再 reload,不能继续使用最后一次成功快照。源码的 compilationErrors 可随有用的部分引用返回,不能据此宣称完整。 - -Roslyn 的已知领域错误通过 MCP 的 isError=true 和 JSON 文本 success=false/errorCode/errorMessage 返回;失败的 JSON 文本与 structuredContent 一致,仍保留领域差异。健康状态下同根 workspace_open 保留 Host 和 snapshot;它不是强制冷启动命令,也不是等待在途业务清理完成的屏障。普通同根确认取消不进入恢复;客户端取消先返回时,后台操作仍可能正在清理,不能把客户端 Promise 结束当作 Host 已退出。HOST_RESTART_REQUIRED(SDK/监听状态)应对当前路径执行 workspace_open,此时才关闭旧 Host,再由显式搜索重新选择 SDK。INPUTS_CHANGED/SNAPSHOT_STALE 按 search_again 重新搜索,普通打开不清除已知重载要求。启动时的项目、Configuration、TFM 和可执行文件配置固定于 Adapter;修改客户端启动配置后须正常重建连接,workspace_open 不热应用配置文件。清理失败则按 WORKSPACE_RECOVERY_REQUIRED 的 restart_gateway 处理,不能通过再次打开恢复。HOST_TIMEOUT/HOST_CRASHED 后旧定位不可用,下一次显式搜索才启动新 Host;不会重放失败引用。 - -INPUT_UNAVAILABLE/HOST_UNAVAILABLE 先检查明确的配置文件、SDK/Host/项目路径,以及 additionalInputs 中的文件是否存在;补充文件缺失时,重载也会失败,恢复文件后再显式搜索。不要为恢复查询而静默移除真实构建输入。HOST_VERSION_MISMATCH 先核对 Code Host 与 Gateway 的版本、Release 配置和协议;不要继续使用混合交付。HOST_PROTOCOL_ERROR 同时检查协议 v2、inputPolicy.version=2 和实际补充列表;旧 Host 没有确认新策略时不能绕过。LEGACY_SYMBOL_ID 要求重新搜索 Roslyn 身份;UNSUPPORTED_SYMBOL_LOCATION 表示该实例未配置 Roslyn;SYMBOL_MISMATCH 表示名称和定位不一致。INPUT_BUDGET_EXCEEDED 区分枚举规模与受跟踪输入字节限制,先缩小受支持范围,不能接受截断指纹。内部 BUSY 表示队列已满,DUPLICATE_REQUEST 要求新的 id;CANCELLED 是目标终止结果,取消确认不替代它。OUTSIDE_WORKSPACE/UNSUPPORTED_LINK 拒绝越界或链接路径,不放松校验来恢复。 - -维护接口变更时,同步检查 Gateway 工具定义、相应 references 手册、实际客户端 Schema 和已安装四份受管文件;更新源码手册后运行 skill:sync,再以 skill:check 校验。仍须单独确认 MCP 实例的版本/构建/Schema,不能用手册同步代替重连。公共接口尚未发布时,只记录实验边界,不提前把新参数加入 MCP 规范字段表。 - - -`npm run test:error-contracts` 使用生成夹具验证错误、部分完成与恢复;Node 22 CI 执行该专项并保存有界报告。UI 图片场景使用注入响应,只验证序列化,不冒充真实屏幕验收。 - -## 源码缓存与附件(0.14.0) - -缓存初始化、写入与清理拒绝 cacheDir 及其祖先中的符号链接/junction;已打开缓存目录被替换时拒绝后续磁盘变更。新条目采用 `wincode-v1_` 文件名和格式标记,清理只处理可识别的本版本条目及保留命名的 overflow。旧版 JSON、无法识别的文件和无关附件保留,不计入受管配额;不会自动迁移或删除整个旧目录。回收站目标必须实际位于工作区内,移动后元数据失败仍返回 partial 和实际文件位置。这些检查不等于原生句柄级原子路径隔离。 - -本地文本查询每次重新枚举有界输入,声明解析按实际内容复用;内置上下文打包按实际选中文件的内容复用。工作区 fingerprint 和 watcher 只是变更提示,不能证明完整源码身份。旧连接仍返回过时正文时,先核对运行 buildId,再由客户端正常重连,不以 git add 或反复释放 Host 代替升级。overflow 在命中时复核存在性,缺失则重建;它没有永久租约,后续读取失败可重新请求上下文,不全局删除其他实例的缓存。 - -## 连接关闭(0.13.2) - -正式入口在 stdin EOF/close、传输关闭或管道错误时停止接收请求,取消初始化和活动操作,并按统一 8 秒预算清理自有资源。关闭失败保留非零退出结果;缓存写入不能无限延迟退出。不能把此行为等同于 Codex 当前连接已更新,也不能承诺强杀 Gateway 时所有后代均受同一个 Windows Job 保护。升级后刷新对应 MCP 连接,不必一概重启整个 Codex。 - -## 原生 Helper 所属进程退出(0.13.3) +只有用户需要弹窗时加 -Desktop。日志只有start表示结果未知。清理日志需用户授权并保留所需记录,不为恢复访问静默删除。截图失败、黑图或窗口最小化按[UI手册](ui.md#选择目标与取证)处理。 -Gateway 通过子进程私有环境传递所属 PID;两个 .NET Host 在项目求值或 UI 读取前核验真实祖先链和创建时间,并持有该进程对象句柄。直接运行 Host 时使用实际父进程。无法核验时拒绝开始重操作,不按 Codex/Claude 等客户端名称扫描。所属进程死亡后先取消,独立线程宽限两秒后仅硬退出当前 Helper;Code Host 的既有 Job 处理其覆盖的后代,UIA 不终止目标窗口应用。UIA 的 stdin EOF 仍表示请求输入结束。此机制不检测仍存活但卡死的 Gateway,也不自动覆盖独立 Repomix 子进程。开发启动包装链最多八层;不要手工设置任意 WINCODE_OWNER_PID 绕过核验。 +## Roslyn 诊断 -## UIA 首用与被动状态(0.13.4) +先读 semanticContext.diagnosticSummary 的总数、错误码与项目分组,再看limitations中的样例。CS0234等缺失命名空间先核对入口及引用项目各自的TargetFramework、restore assets、实际MetadataReference;正常编译不证明设计时加载相同。CS8795等partial缺失需结合生成器配置及排除数量;不凭错误码安装包、改框架或开启生成器。 -启动只核验 UIA 平台、配置和发布文件,不运行健康探测进程。文件存在且尚无运行观察时,hello 的 flaui.available=null、source=unknown,不能理解为已安装 Host 不可用。首次 UI 请求直接执行请求;成功响应更新已知 Host 观察,失败保留 lastAdapterError,即使健康状态仍 unknown。需要主动验证时使用现有 wincode_diagnose_project;hello 不补发探测。缺失文件仍可在启动被报告,文件恢复后显式 UI 请求重新解析发布路径,不必重新初始化整个 Gateway。 +| 错误 | 处理 | +|---|---| +| SNAPSHOT_STALE / INPUTS_CHANGED | 重新显式搜索符号,获得新location;失败的引用请求不自动重放 | +| HOST_RESTART_REQUIRED | 按recoveryAction对同一工作区恢复;配置变化仍需重建连接 | +| HOST_TIMEOUT / HOST_CRASHED | 旧location失效,下一次显式搜索才启动新Host | +| PROJECT_LOAD_FAILED | 修复实际项目输入,再显式搜索;不继续使用旧快照 | +| INPUT_UNAVAILABLE | 核查配置、项目及additionalInputs,恢复缺失文件,不静默移除输入 | +| HOST_VERSION_MISMATCH / HOST_PROTOCOL_ERROR | 核对Gateway与Code Host版本、Release发布、协议及输入策略;不混用交付 | +| LEGACY_SYMBOL_ID / SYMBOL_MISMATCH | 重新搜索并传回实际名称和完整location | +| UNSUPPORTED_SYMBOL_LOCATION | 当前实例未配置Roslyn;文本定位不能冒充语义身份 | +| INPUT_BUDGET_EXCEEDED | 分清枚举数量与输入字节超限,缩小合法范围,不接受截断快照 | -## 手动 Roslyn 释放与可选托盘(0.14.0) +queryComplete:false可能是生成器或加载图的覆盖限制,不一定是运行故障;可使用已验证的局部证据,但零引用不证明全局没有引用。 -自动释放关闭,本版不创建 idle timer。用户可按 README 手动启动独立 Tray,并给希望管理的 Gateway 启动参数添加 --tray 后刷新连接。托盘只管理已注册的实例,不扫描/终止外部客户端或目标应用;公开工具名称以当前连接实际暴露的 Schema 为准,不在文档里写死数量;没有让 Agent 自动代替用户释放的管理工具。默认不启用托盘连接、不设置自启动。 +## 关闭与可选托盘 -手动释放遇到业务在途、语义排队/收尾、工作区确认或恢复门时拒绝,不自动延后执行。释放完成后新请求继续;旧 symbolLocation 返回 SNAPSHOT_STALE,显式重新搜索取得当前定位。保留 Gateway、watcher、缓存与最后诊断。清理失败进入 restart_gateway 恢复门,不能靠反复点击清除错误。local-text 没有可释放的 Roslyn。 +close/EOF 会取消活动工作并在统一预算内清理自有资源。所属进程退出保护只针对本实例Helper及已覆盖的后代,不终止目标应用;不要手工伪造所属PID。清理失败保留非零结果,不能靠反复关闭掩盖。 -概览只读内存快照,不为状态启动 Host 或枚举缓存目录。状态是注册/打开/刷新时的观察,不代表 Agent 在两次请求之间已结束整个任务。失联/超时表示未知,控制命令不自动重放;退出 Tray 不停止 Gateway。首版最多八个同用户/会话实例,按同权限级别使用;版本必须匹配。需要停止时由用户确认“停止此实例”,走该 Gateway 既有关闭路径,客户端可能重新建立新实例。 +托盘为显式可选组件,默认不启用、不自启动。它只管理已注册的同用户/会话实例;释放Roslyn保留Gateway,旧location失效。业务在途时释放可能被拒绝,清理失败进入恢复状态。退出托盘不会停止Gateway,概览也不证明Agent当前空闲。 diff --git a/skills/wincode/references/ui.md b/skills/wincode/references/ui.md index 485da26..6de7381 100644 --- a/skills/wincode/references/ui.md +++ b/skills/wincode/references/ui.md @@ -1,14 +1,26 @@ -# 窗口与 UI 取证 +# 窗口与 UI -UI 工具同样占用每实例 32 个业务受理槽;既有 UI/健康探测互斥保留,排队消耗请求预算。SERVER_BUSY 不表示已启动 Helper,不自动重试。hwnd 最长 32 字符;原始参数合计受 64 KiB UTF-8 JSON 预算限制。 +按任务选择:[只读取证](#选择目标与取证)、[复选框与列表分组](#只读配方)、[语义操作](#操作方式与授权)、[有限展开](#展开并读回)。需要启动客户端时读[会话入口](diagnostics.md#skill-按需会话)。 -0.16.0 中,每个 Gateway 的源码范围固定于启动根;换项目选择对应连接。目标 PID/HWND 不是工作区身份,UI 源码候选仍按所选连接解释。可选托盘与 UIA 取证 Host 独立,退出托盘不终止 MCP 或卸载正在使用的 Roslyn;手动释放仅影响选定实例的 Code Host。源码缓存修复不证明 UI 候选对应同一运行时状态,多实例窗口隔离仍需单独验收。 +## 选择目标与取证 -## 规范字段 +1. 已知有效 PID/HWND 就直接使用;未知时用 `wincode_ui_list_windows` 按进程或标题筛选,`maxWindows:10` 通常足够。多个候选先消歧,失效句柄再重新发现。 +2. 只查控件用 `capture:"none"`;需要视觉布局用 `"original"`,需要节点标号用 `"annotated"`。后台取证同时提供 PID/HWND 和 `backgroundOnly:true`。 +3. 首轮可用 `responseFormat:"compact"`、小范围 query 和适当深度。根据相关截断增加预算;需要几何字段时使用返回的 `expansionRequests` 重新读取。 + +以下编号均为占位,须替换为实际观察值: + +```json +{"pid":12345,"hwnd":"0x123ABC","backgroundOnly":true,"capture":"none","responseFormat":"compact","query":{"name":"保存","controlType":"Button"},"maxDepth":4,"maxNodes":100} +``` -混合只读编排实验分支 `codex/hybrid-readonly-orchestration` 提供客户端 TypeScript 入口 `WinCodeSession.readonlyUiWorkflow(target, program, options)`。适用于能执行受信任客户端程序的宿主:先列出窗口并明确选择 PID/HWND,然后在程序内串行调用 `reader.inspect`/`reader.review`,按明确状态决定是否继续,并返回 findings。入口复用会话,关闭时取消编排并等待收尾;同时处理返回的 `isError`、步骤完整性及全部原生内容块。此能力只在该实验分支提供,使用前核对本机交付源码;详细契约见交付目录的 `docs/hybrid-readonly-orchestration.md`。普通 MCP 和终端 JSON 会话按下面的既有工具字段调用。 +`query` 的 automationId/name/controlType 是区分大小写的精确 AND 条件。只有 `queryResult.searchComplete:true` 且 `status:"unique"` 才证明本次范围内唯一;not-found 不表示 Off,incomplete 不证明不存在。搜索预算与返回子树预算分别计算。 -参数名称区分大小写;额外字段仅被容忍和忽略,不会被当成筛选条件。不能把 `automationID`、`AutomationId` 或 `title` 当作下面的规范字段。数字、布尔值必须使用真正的 JSON 类型。 +`treeComplete` 描述本次子树遍历,`propertyIssues` 描述属性缺口。节点 id 只在该次结果有效。需要勾选、选中、展开状态时才传 `readStates:true`;unsupported/unknown 不等于 false。compact 保留名称、身份、层级、状态和缺口,省略部分几何字段;图片直接读取 MCP image 块。 + +截图仍覆盖整个目标窗口,scope/query 只缩小树范围。PrintWindow 可能返回黑图或陈旧内容;核对图片及 captureMethod、captureQuality、imageOmitted。suspect-low-variation 是像素提示,unknown 也不证明图像有效。最小化、取图失败时报告限制,不为截图恢复或激活窗口。 + +## 规范字段 | 工具/字段 | 类型与约束 | | --- | --- | @@ -27,113 +39,99 @@ UI 工具同样占用每实例 32 个业务受理槽;既有 UI/健康探测互 | `wincode_ui_type` | 具备上述 click 的全部字段,另必填 `inputText`(最长 4096;mode=type 必须非空,mode=setValue 允许空字符串以清空值);可选 `clearBefore`(布尔,默认 false,仅 mode=type 有效)、`mode`(`type`/`setValue`,默认 `type`) | | `wincode_ui_review` | 接受上述 inspect 的全部规范字段,另必填 `candidateFiles`:1–16 个相对 `.xaml` 路径、每项最长 512;可选 `candidateCodeFiles`:1–8 个相对 `.cs` 路径、每项最长 512;可选 `textQueries`:最多 5 个非空字面字符串、每项最长 80 | -候选路径必须在工作区内,不得包含通配符或父目录逃逸;参数合法不保证文件存在或运行窗口与源码对应,仍检查结果中的缺口。`query:{automationId:"SaveButton",maxSearchNodes:1000}` 是规范示例;`query:{automationID:"SaveButton"}` 缺少规范定位条件,仍会报错。没有未列出的 UI 工具别名。 - -## 选择目标与取证 - -首轮可以显式传 `responseFormat:"compact"`;默认 `full` 保持兼容。精简格式保留全部已返回控件的 ID、层级、名称、AutomationId、状态和同一截图,省略节点 bounds/relativeBounds/className;`summary` 仅统计已返回快照中的无名称按钮、禁用控件和树缺口,不把这些观察自动判为缺陷。需要坐标或更完整信息时使用 `expansionRequests` 中的 tool/arguments;它们重新观察实时 UI,ID 可能变化,查询仍须检查唯一性和完整性。 +字段名和类型按表传入,不把 automationID 当作 automationId,也不用字符串代替数字或布尔值。原生接口可能忽略额外字段;调用成功不证明未知参数生效。客户端配方拒绝额外字段。 -精简 `codeEvidence.candidates` 是去重后的候选表,`clues[].candidateIds` 引用其 id,候选自身的 `nextRequest` 只保存一份;full 格式仍是 `clues[].candidates`。两种格式都保留运行时/源码身份未验证、歧义、截断与扫描边界。不要把 candidate id 当成源码的持久身份。 +UI 请求原始 JSON 总计上限 64 KiB,hwnd 最长 32 字符。query 搜索另有 2 秒/50 层软限制,仍受 Helper 总超时约束。源码候选路径必须是工作区内字面路径,不能含通配符或父目录逃逸。 -已知准确 PID/HWND 就直接使用;未知时调用 wincode_ui_list_windows,以 processName(不带 .exe)、pid 或 titleContains 缩小范围,maxWindows 建议 10。筛选同时满足。候选歧义时先确认目标,标题不证明源码归属;失效句柄才重新发现。 +## 父范围定位 -对用户指定窗口调用 wincode_ui_inspect,例如(编号必须替换为真实结果): +`scopePath` 来自实际观察。每一步唯一定位上一范围的严格后代,可跳过布局包装,不能重复匹配当前根;每次调用重新解析。没有 query 时返回末级范围树;有 query 时可匹配该根及其后代。 -```json -{"pid":12345,"hwnd":"0x123ABC","backgroundOnly":true,"capture":"none","maxDepth":4,"maxNodes":100} -``` +检查 `scopeResult.status:"resolved"` 和 resolvedCount;失败的 failedIndex 从 0 开始。`SCOPE_NOT_FOUND`、`SCOPE_AMBIGUOUS`、`SCOPE_SEARCH_INCOMPLETE` 分别表示缺失、多义和搜索不完整,不回退整窗。路径需要 inspectionVersion 5,旧连接不能通过删掉范围继续操作。 -只查指定控件时增加 query,例如 {automationId:"SaveButton",controlType:"Button"};automationId/name/controlType 为区分大小写的精确 AND 条件。搜索默认 1000 节点(上限 5000)、最多返回 10 个候选(上限 20),独立于子树 maxNodes;另有 2 秒/50 层软限制,整个 Helper 仍有硬超时。 -queryResult.searchComplete=true 且 status="unique" 才展开子树;ambiguous 返回多个候选,incomplete 不证明唯一,not-found 只限本次搜索范围。treeComplete 仅描述返回子树结构;propertyIssues 单独说明属性不支持、错误或裁剪。节点 id 只在本次结果有效,不作为下次定位凭据。 -工具响应 `success:true` 与任务已得到有效观察分别判断:返回 `not-found` 时没有目标状态,不能当作 Off 或统计零。先确认正确页面及折叠/展开状态,再判断是否需要新的观察;运行源码有 AutomationId 不证明当前 UIA 树已暴露该控件。 -需要勾选/选中/展开状态时传 readStates=true;states 的 unsupported/unknown 不等于 false,不读取输入框值或执行操作。截图仍是整个目标窗口,局部查询只缩减树和标注范围。旧 Host 不支持时应更新构建,不移除查询参数冒充成功。 +路径不会展开隐藏父级。若分组标题和条目是同级节点,用下方 sibling-range 读取逻辑分组,不把标题伪装成原生父节点。 -先取足够的小树,出现相关截断再增预算。需要看视觉布局才用 capture="original";需要对应节点编号用 "annotated"。图片直接消费 MCP image 块,不把 Base64 转贴为文本。 +## 只读配方 -后台取证保持 backgroundOnly=true 并同时传 PID/HWND,避免游戏或其他遮挡窗口污染屏幕回退。不要为截图抢前台、还原窗口或操作游戏。PrintWindow 成功仍可能黑屏/陈旧;检查 captureMethod、captureQuality、imageOmitted 与截断。最小化不支持;整个 Helper 超时可能无树,不无界重试。 +配方在客户端调用既有 MCP 工具。SDK 入口为 `WinCodeSession.readonlyUiRecipe(target, recipe, parameters, options)`;JSON CLI 使用 `action:"readonly-ui"`。固定 target 为 PID/HWND,timeoutMs 默认 15000、范围 1–30000;每次结果检查 isError、report.success、steps 和 findings。它们不执行界面动作。 -captureQuality 在标注前检查原始像素,最多采样 1024 点;suspect-low-variation 表示采样 RGB 各通道范围不超过 3,可能是空图,也可能是正常纯色或低对比界面。unknown 不表示图片合格;采样可能漏掉局部内容。提示不丢弃原图或 UIA、不自动切换截图方式。旧 Host 未提供该字段时按未验证处理。 +### checkbox-audit -需要源码候选时,确认源码工作区后改用 wincode_ui_review,一次取得快照与证据;无需先 inspect 再重复截图。沿用上述参数,增加 candidateFiles:["Views/MainWindow.xaml"],必要时 textQueries:["保存"]。 -- 候选为 1–16 个工作区内相对 XAML 路径;只传相关文件。 -- textQueries 最多 5 个、每个 80 字符,字面量且区分大小写。 -- 行号/哈希/AutomationId 候选不证明运行时版本;runtimeSourceVerified=false,保留 ambiguous、unsupported、not-found 等边界。 -- 已知相关 C# 文件时可加 candidateCodeFiles:["ViewModels/MainWindowViewModel.cs"],1–8 个工作区内相对 .cs 路径。codeEvidence 提供 Click/简单 Binding 的文字声明/赋值候选;按实际线索使用其 nextRequest 续查 prepare_context,并核对目标正文。 -- 不传 candidateCodeFiles 不读取 C#。单文件 256 KiB、总计 1 MiB,线索/匹配/JSON 均有限额;检查 fileScanComplete、searchComplete、truncated 和省略原因。候选文件无匹配不证明全仓不存在。 -- runtimeBuildSourceIdentity=unknown、templateResolution=unsupported;不推定 DataContext 或 CanExecute,也不把构造参数相似当执行链证明。多个同名声明继续保留歧义,不能自动选择首项。 -- nextRequest 先读候选所在精确行;relatedSymbol 仅为构造参数文字线索,可能是变量。核对赋值后再明确请求方法,不能因该字段自动认定为方法或完整调用链。复杂插值原始字符串/超出嵌套限制的文字扫描报告不支持。 +```json +{"id":"audit1","action":"readonly-ui","recipe":"checkbox-audit","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"automationId":"settings"}],"regionAutomationId":"checks","checkboxAutomationIds":["optionA","optionB"]},"timeoutMs":15000} +``` -多个控件若指向同一文件、相邻赋值,可在确认文件未变化后复用当前会话已展示的精确行;有缺口时合并为一次有界 lineRanges 请求。不要因为每个候选都带 nextRequest 就机械重复读取。复用仅限已经核对的正文,不代表这些运行时 UI 证据获得了跨调用有效期保证,也不扩大运行时绑定结论。 +- 必填 regionAutomationId 和 1–64 个不重复的 checkboxAutomationIds。目标须在区域内唯一,类型为 CheckBox,isEnabled 已知,toggle 为 On/Off。 +- 可选 summaryAutomationId:On 才读详情,Off 返回 detailsRequired:false,未知停止。省略则直接统计。 +- 可选 scopePath 同时限定摘要和详情,每次重新解析。maxDepth 默认 4、范围 1–50;maxNodes 默认 300、范围 1–5000。 +- 返回 checkedCount、unchecked、disabled;带摘要时详情位于 details。禁用且 On 仍计入数量。className/bounds/isOffscreen 缺口保留为证据,不阻断已知状态;身份、必要状态和读取完整性仍需成立。 -## 操作方式与授权 +### sibling-range -**第十六轮父范围定位。** 从本次实际 UI 观察构造有序父路径,不把源码中的名称或局部节点 id 当作运行时身份。例:先 `wincode_ui_inspect({pid,hwnd,scopePath:[{automationId:"实际语音区域ID"}],query:{name:"实际高级标题",controlType:"Group"},readStates:true,capture:"none"})`;确认唯一且 Collapsed 后,用同一 scopePath 调用 `wincode_ui_set_expanded({pid,hwnd,scopePath:[{automationId:"实际语音区域ID"}],targetName:"实际高级标题",targetControlType:"Group",expanded:true})`。再将已观察的高级选择器追加到路径,检查其子控件。示例占位值不能照抄。 +先从局部 UIA 树和必要截图确认容器及两个连续分组标题,再调用: -路径每一项可以跳过 Pane 等布局包装,但不能匹配当前范围根节点本身;每次请求重新解析,不缓存 COM 对象。不提供 query 时 inspect 返回最后父级的树;最终 query 可匹配该根及其后代。scopeResult 记录 resolvedCount、visitedNodes,失败时还有从 0 开始的 failedIndex、status 和 reason。SCOPE_NOT_FOUND/SCOPE_AMBIGUOUS/SCOPE_SEARCH_INCOMPLETE 分别表示父路径缺失、多义、不完整;不退回整窗搜索。路径不会自动展开隐藏父级,也不实现菜单探索。compact 的 expansionRequests 保留原 scopePath;capture 仍按整窗取证,不代表局部裁剪,只有树的定位范围改变。 +```json +{"id":"group1","action":"readonly-ui","recipe":"sibling-range","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"containerQuery":{"name":"grid","controlType":"Table","maxSearchNodes":500},"startAfter":{"name":"2024","controlType":"DataItem"},"endBefore":{"name":"2023","controlType":"DataItem"},"maxDepth":4,"maxNodes":160},"timeoutMs":15000} +``` -带路径的展开会先用只读 health 验证 Host 为 inspectionVersion 5,再发送动作;旧版返回 VERSION_MISMATCH,动作不发送。该探测和动作共用本请求 deadline,不增加跨请求预算、权限令牌或逐步确认。标准请求不带 scopePath 时沿用既有流程;展开后仍需独立读回实际状态。 +containerQuery 使用标准 query;startAfter/endBefore 仅接受 automationId/name/controlType,至少一个精确条件。可加 scopePath;深度和节点预算同 checkbox-audit。两标题须在容器内分别唯一、同一直接父节点、顺序正确。一次读取返回中间所有同级条目,不含标题,不重复计入子文本。 -展开导航独立于只读取证。目标缺失时先区分完整搜索无匹配、搜索不完整和歧义;只用用户授权范围内的实际父级名称/标识提出候选,不把所有 not-found 当成折叠。`wincode_ui_set_expanded({pid,hwnd,targetName,targetControlType,expanded:true})` 在完整唯一搜索、启用且明确 Expanded/Collapsed 状态成立后才受理;不支持、未知状态或歧义时拒绝,不回退为 click/坐标/焦点。动作回执只表示调用被接受,之后必须读回展开状态及原子控件。超时或失败不自动重放。 +findings 包含 kind:logical-group、basis:sibling-order、边界、原生父节点、items 和 observedCount;每项保留本次 id/parentId、名称及可用身份。coverage 为 observed-range-only,businessGroupComplete 为 unknown:只描述提供方当前暴露的区间,不推断完整清单或标题计数含义。 -客户端 `WinCodeSession.expandUiWorkflow(target,{parentQuery?,candidateQuery?,childQuery,scopePath?},options)` 接收目标复选框查询,可选明确父级或待重新核验的发现候选;parentQuery 与 candidateQuery 不能同时提供。共用固定 PID/HWND、15 秒默认总预算(上限 30 秒)。提供显式父级时最多 5 次调用;自动发现或候选选择时最多 6 次。保存首次查询,诊断父级根状态;Collapsed 才设置 Expanded 一次,随后要求完整父级树证明子控件归属,再单独读回子控件的真实 toggle 状态。根状态诊断有意只返回一层,允许仅 maxDepth 截断;这不代表父级子树完整,最终验证仍要求完整子树、唯一匹配及必要属性/状态/遍历证据。第十四轮仅允许 className/bounds/isOffscreen 的辅助属性缺口继续,并在 steps/actionTarget 保留原记录;其他属性问题与无法解释的 propertyIssueCount 仍阻断。展开动作启用证据未知返回 TARGET_EVIDENCE_INCOMPLETE,实际禁用返回 TARGET_DISABLED。它不会递归探索任意菜单,且可能因较大的父级树超预算而停止。普通 `readonly-ui` 的门槛未改,也不触发导航动作。 +名称及边界所需属性必须可读,AutomationId 缺失可按名称定位。边界缺失/重复/顺序错误、条目名称不可读或读取截断时停止,无成功 findings;已观察部分保留在 steps.evidence,不解释为空组。配方不自动识别标题、滚动或展开。 -**省略父级的有限发现。** 目标已唯一可见且取证完整时,直接读取,只有 1 次调用。完整搜索未找到目标时,在同一窗口搜索 `Group`(最多搜索 1000 个节点、20 个匹配);搜索完整、状态已知且唯一启用的 Collapsed Group 可自动重新定位并尝试展开。候选唯一不能证明目标在其中,仍须后续归属验证。零候选、搜索不完整、状态未知或候选无可用标识均停止,不执行点击回退。这个试点只覆盖 Group,不遍历菜单、Tab、TreeItem,不证明任意导航发现能力。 +### 自定义只读流程 -**多个候选交给当前 AI 选择。** 多候选且有可定位项时返回 `status:"selection-required"`、`success:false`、`isError:false`,保留诊断码 `NAVIGATION_CANDIDATE_AMBIGUOUS`;这是待决策结果,不是任务完成。`diagnosis.candidates` 每项包含实际 query/state;可定位项附 `nextRequest`(action、target、parameters、timeoutMs)。依据目标任务和当前页面证据选择一个相关项,补一个新 id 后原样提交其请求;不要默认选第一项、编造候选或展开全部。证据不足时追加相关只读观察或澄清。每个请求最多一次展开;后续定位依据实际新观察,不默认遍历其余候选。 +可信 TypeScript 宿主可用 `readonlyUiWorkflow(target, program, options)`,program 内串行调用 reader.inspect/review;两者支持 scopePath。inspect 的客户端选项 allowAuxiliaryPropertyGaps:true 允许上文三类辅助缺口,allowPropertyGaps:true 则由调用方检查任务所需属性。缺口仍留在步骤证据,搜索、范围和遍历检查保留。默认读取与 review 保持严格;这些选项不是 MCP 参数或 JSON 配方参数。 -后续 `candidateQuery` 必须含实际名称或 AutomationId,可带 Group 类型,不能带搜索预算。第十五轮直接按该选择器重新定位 Group,不重新枚举无关 Group:完整搜索无匹配或不唯一为 `NAVIGATION_SELECTION_STALE`,搜索不完整为 `QUERY_INCOMPLETE`,实际禁用为 `TARGET_DISABLED`,启用证据未知为 `TARGET_EVIDENCE_INCOMPLETE`。唯一有效父级仍 Collapsed 才展开一次,已经 Expanded 就继续局部观察,不切换、不再次展开。目标已经可见时仍直接读取。程序校验选择的当前有效性;相关性的语义判断由调用方 AI 完成,没有内部评分模型。 +## 操作方式与授权 -**展开后的局部诊断。** 第十五轮的 `diagnosis.localObservation` 记录实际父区域 query、treeComplete/截断/遍历/属性缺口、匹配数、最多 20 个匹配及 20 个内层 Collapsed Group,超出数量分别用 matchesOmitted/candidatesOmitted 表示。局部项含本次 id/parentId、实际名称/ID/类型、isEnabled、展开 state 和 propertyIssues;id 仅用于解释本次树结构,不能跨请求定位。父级已观察 Expanded、但目标缺失/多义时仍 stopped/success:false、没有 findings;cause 区分 `observed-inner-collapsed-candidates`、`child-not-found-in-parent`、`child-ambiguous-in-parent`,不完整证据为 `local-observation-incomplete`。parentState 表示最新父级观察,原始状态仍保留在 steps。读取相应 nextAction 继续同一任务的观察,不把已确认展开描述成动作结果未知,也不重放外层动作。 +用户要求执行具体流程时,在已授权范围内完成并验证;只要求查看或评估时保持只读。按任务选择模式: -这些内层候选仍不附可执行 nextRequest:标准工具与 expand-ui 提供显式 scopePath,调用方可根据实际父树构造路径并独立核验内层;客户端不自动生成祖先路径或递归执行。路径多义时先修正查询,不默认首项或展开全部。expand-ui 的父/候选查询相对于 scopePath 定位,父级确认后的子查询追加该父选择器;若父选择器三个定位字段与路径末项完全相同则复用该范围,根节点不重复作为后代路径。追加后总路径最多 50 项。nextRequest 保留原 scopePath 和目标。诊断复用本次父树,不额外轮询;树不完整时不能宣称候选唯一或目标不存在。 +| 操作 | 用法与限制 | +|---|---| +| 点击、勾选、选择 | `wincode_ui_click` 使用 Invoke/Toggle/SelectionItem,不先请求焦点;应用事件仍可能激活窗口 | +| 后台写值或清空 | `wincode_ui_type` 显式传 `mode:"setValue"`,使用 ValuePattern;空字符串清空 | +| 逐键输入、IME 验证 | `mode:"type"` 会 SetFocus,需用户授权前台交互;默认 mode 也是 type | +| 明确展开或折叠 | `wincode_ui_set_expanded`,expanded:true/false;需要 ExpandCollapsePattern | -上述调用数和 deadline 由代码按单请求限制;发现与选择是两次请求,未共享累计预算,也未建立强制关联上一请求 target/childQuery 的状态。跨请求累计预算当前不要求补齐。沿用用户选定窗口和任务;新的实际观察可用于合法查询修正,已授权导航不要求逐步重复确认。首次 Group 发现仍可能被其他 Group 的必要状态/非辅助属性缺口阻断,所选父级的直接续接已消除这一耦合。初始候选没有祖先/邻近上下文,局部诊断仅解释观察到的树关系;内层操作仍须独立定位。标准工具的父范围定位已实现,任意任务的证据拆分与自动递归导航尚未实现。 +`backgroundOnly` 只约束取证,不是 click/type 的禁止激活开关。仅授权后台时,不用启动、恢复、聚焦、坐标或键盘模拟补救失败;ValuePattern 不支持也不自动改用 type。已知会激活的动作先取得前台授权。用户正在游戏、聊天或会议时,不索取焦点;需前台键盘验证时由用户选择时机并切到目标窗口。 -先按任务选定模式,再调用。 +动作目标由当前观察的 targetAutomationId/targetName/targetControlType 精确定位。零匹配、多匹配或搜索不完整不操作。动作后按预期检查控件状态、页面或提示;需要等待时做有界只读检查。actionMethod 只说明使用的控件模式,不能代替业务结果。输入内容不回显,inputLength 只报告长度。 -| 任务 | 用法 | -| --- | --- | -| 查看界面、定位控件 | `wincode_ui_inspect` | -| 语义点击、勾选、选择条目 | `wincode_ui_click`:Invoke/Toggle/SelectionItem,不先请求焦点;应用响应仍可能激活窗口 | -| 后台写入或清空输入框 | `wincode_ui_type` 且**显式**传 `mode:"setValue"`:ValuePattern,不请求键盘焦点;应用事件仍需验证 | -| 验证逐键输入、快捷键、IME 行为 | `wincode_ui_type` 的 `mode:"type"`:需要前台授权 | -| 控件不支持 ValuePattern 的后台写入 | 报告该步骤无法后台完成,不自动改走键盘输入 | +| 结果 | 下一步 | +|---|---| +| TARGET_NOT_FOUND / TARGET_AMBIGUOUS | 重新观察、收紧选择器,不逐个试点 | +| TARGET_SEARCH_INCOMPLETE | 缩小范围或调整预算,不能选择首个匹配 | +| TARGET_DISABLED / NO_CLICK_PATTERN / NO_VALUE_PATTERN / VALUE_READONLY | 检查流程前提及受支持操作 | +| TARGET_EVIDENCE_INCOMPLETE | 核对必要属性,不当作实际禁用 | +| FOCUS_FAILED | 由用户处理焦点,不用脚本反复抢前台 | +| ACTION_FAILED、执行中超时或取消 | 可能已部分完成;先读回状态,clearBefore 可能已清空输入,不能重发动作 | -用户要求执行或测试明确的界面流程时,自主完成定位、点击、输入与结果检查,不为每个常规步骤重复确认;用户只要求评估、查看或审查时保持只读。动作会改变目标应用状态,按影响判断是否需要额外确认:超出任务范围的发布、发送、删除或真实业务提交,先说明再执行。 +原生 Host 的 REC 提示与审计不可绕过。提示窗失败发生在 UI 读取之前,先保留错误和日志;诊断方法见[诊断手册](diagnostics.md#ui-取证失败)。 -后台与前台的判断先遵循 [SKILL.md](../SKILL.md) 的独立注意事项:`backgroundOnly` 仅约束 inspect/review 的取证方式,不是动作的禁止激活开关;工具不先请求焦点,不代表目标应用的导航、事件或弹窗不会激活窗口。只授权后台时,不以启动、恢复窗口或聚焦补救失败;已知会激活的动作没有前台授权就停止该步骤。 +## 展开并读回 -`mode:"type"` 会向目标控件索取键盘焦点(UIA SetFocus),可能把该窗口带到前台并中断用户当前输入,因此只在用户授权前台交互时使用: +需要展开目标复选框所在区域时,使用独立的 `expand-ui`,不是只读配方: -- 用户只授权后台测试时固定用 `setValue`,不因为 `type` 更接近真实输入而擅自切换。 -- 授权前确认用户当前没有正在使用该应用。用户正在游戏、聊天(QQ/微信)、会议或演示中时,不索取焦点,也不建议用户为此切走;说明需要前台键盘输入并询问何时方便,由用户决定。 -- 授权后由用户把目标窗口切到前台,工具只做一次焦点确认。`FOCUS_FAILED` 表示当时没有确认到焦点:报告它并等用户处理,不重复重试,也不用脚本、快捷键模拟或窗口置顶代替用户切换。 -- 前台路径只为验证键盘相关行为;业务结果仍用后台 `inspect` 复核。 +```json +{"id":"nav1","action":"expand-ui","target":{"pid":1234,"hwnd":"0x123456"},"parameters":{"scopePath":[{"automationId":"settings"}],"parentQuery":{"name":"高级","controlType":"Group"},"childQuery":{"automationId":"optionA","controlType":"CheckBox"}},"timeoutMs":15000} +``` -## 语义操作 +parentQuery/candidateQuery 二选一,也可都省略以发现候选。默认总预算15秒、上限30秒,每请求最多一次展开;显式父级最多5次工具调用,发现/选择最多6次。目标已可见直接读;父级已 Expanded 不重复展开;Collapsed 才设置展开并读回归属及 toggle。 -`wincode_ui_click` 与 `wincode_ui_type` 只操作同一次有界搜索证明唯一的控件,命中 0 个或多个时不做任何操作。选择器沿用取证时的 `targetAutomationId`/`targetName`/`targetControlType`(区分大小写的精确 AND 条件)与同一个 PID/HWND。 +省略父级时只发现 Group:完整搜索后唯一、启用、状态明确的折叠候选才继续。多个候选返回 selection-required、success:false、isError:false。根据实际任务选择带 nextRequest 的候选,补新 id 提交;不默认首项,也不展开全部。candidateQuery 须含名称或 AutomationId,可带 Group 类型,不能带搜索预算。 -执行一个动作: +选择后重新定位:不存在或不唯一为 NAVIGATION_SELECTION_STALE;查询不完整仍按 QUERY_* 报告。祖先 scopePath 贯穿发现、操作和最终读取;内部追加实际父选择器,若与路径末项三个定位字段完全相同则复用根。总路径最多50项;需追加时原路径最多49项。父级读取因此需要 inspectionVersion 5,带路径动作先检查版本。 -1. 用当前 UI 证据确定目标与预期结果;已有可靠 PID/HWND 就直接用,窗口关闭或句柄失效才重新列窗。 -2. 动作后检查预先确定的结果:控件状态、页面变化、新窗口或提示文本。需要等待时做有界只读检查,不重发原动作。 -3. 结果符合预期才继续;验收条件满足后停止。观察不到就报告未验证,不把 `success:true` 当成测试通过。 +仅 completed/success:true 表示取得最终 findings。父级展开后仍缺目标时,读 diagnosis.localObservation 与 nextAction;它们保留本次树、状态、匹配和内层候选,不授权递归操作。内层候选没有可执行 nextRequest,需根据新观察另行定位。辅助属性缺口可保留;必要身份、启用、状态和完整性仍检查。 -只用 UIA 控件模式与键盘输入:不做坐标鼠标模拟,不以 Shell 方式激活、还原或置顶窗口。结果回报 `actionMethod`(`InvokePattern`/`TogglePattern`/`SelectionItemPattern`/`ValuePattern`/`keyboard:type`/`keyboard:clear+type`)、`actionTarget` 与 `inputLength`;**不回显输入文本**。`clearBefore` 仅 `type` 有效,`setValue` 传空字符串即可清空值。 +首次发现可能受其他 Group 的未知状态影响;大树也可能超预算。每次请求的预算独立,不是整个任务的累计额度。一次失败不把所有 not-found 都解释成折叠,也不自动重放展开。 -| 情况 | 处理 | -| --- | --- | -| `TARGET_NOT_FOUND` / `TARGET_AMBIGUOUS` | 重新观察并收紧选择器,有新证据后再试;不逐个试点 | -| `TARGET_SEARCH_INCOMPLETE` | 扩大预算或收窄范围,不把已有的单个匹配当成唯一 | -| `TARGET_DISABLED` / `NO_CLICK_PATTERN` / `NO_VALUE_PATTERN` / `VALUE_READONLY` | 检查流程前提,或改用受支持的路径 | -| `TARGET_EVIDENCE_INCOMPLETE` | 展开目标的身份或启用证据不足;核查 actionTarget.propertyIssues,不把它当实际禁用,也不自动重放 | -| `FOCUS_FAILED` | 保持后台约束;只有已授权前台交互时才考虑前台路径 | -| `ACTION_FAILED`、执行中超时或取消 | **可能已经部分完成**:`type` 的 `clearBefore` 可能已清空旧内容,`click` 的调用方也可能已执行一部分;先只读观察实际状态,再决定继续、修正或报告 | -| `SERVER_BUSY` 等繁忙拒绝 | `workStarted:false`,未启动 Helper;按诊断手册处理,不自动重放 | +## 界面与源码对照 -只有 `TARGET_*`、`TARGET_DISABLED`、`NO_*_PATTERN`、`VALUE_READONLY` 与 `FOCUS_FAILED` 发生在实际调用之前;`ACTION_FAILED` 不等于未执行。常见失败不必都交还用户:勾选失败后先读勾选状态,已达到目标就不再 toggle。 +需要源码线索时直接使用 `wincode_ui_review`,不必先 inspect 再重复截图。传相关 candidateFiles,必要时加 candidateCodeFiles/textQueries;路径限当前工作区。已知PID/HWND不证明该窗口来自当前源码。 -操作与只读取证共用受理槽、互斥、超时与 Helper 回收;请求在收尾阶段过期时仍返回已完成动作的真实结果。审计 `op` 记为 `click`/`type`/`setValue`。旧 Helper 不具备该能力时按 `VERSION_MISMATCH` 拒绝,不把参数改成普通 inspect 冒充成功。 +XAML/C# 候选是文字匹配,runtimeSourceVerified:false,runtimeBuildSourceIdentity:unknown,templateResolution:unsupported。Click/简单 Binding 候选及 relatedSymbol 不证明 DataContext、CanExecute 或完整调用链。使用 nextRequest 阅读准确正文,再判断关系。 -内置 Host 强制显示半透明 REC/WinCoding 标志并记录极简审计,不绕过。审计提示按诊断手册处理。启动测试应用时使用专用隔离数据模式;已有固定测试 profile 时复用它,禁止使用个人数据库。 +检查 fileScanComplete、searchComplete、truncated 和省略标记。单文件256KiB、总计1MiB,输出另有预算。compact 用候选表与 candidateIds 去重,full 保留嵌套候选。已有相同文件的有效正文可复用;文件改变或相关分支未覆盖时再读。 diff --git a/src/Client/ReadonlyUiRecipes.ts b/src/Client/ReadonlyUiRecipes.ts index 88a25d2..7d410c8 100644 --- a/src/Client/ReadonlyUiRecipes.ts +++ b/src/Client/ReadonlyUiRecipes.ts @@ -1,7 +1,19 @@ import type { UiReader } from './ReadonlyUiWorkflow.js'; -import type { UiNode } from '../Core/UiContracts.js'; +import { validateUiQuery, validateUiScope, type UiNode, type UiQuery, type UiScopeSelector } from '../Core/UiContracts.js'; + +export interface SiblingRangeParameters { + scopePath?: UiScopeSelector[]; + containerQuery: UiQuery; + /** Explicitly observed consecutive group headings. Both bounds are excluded. */ + startAfter: UiScopeSelector; + endBefore: UiScopeSelector; + maxDepth?: number; + maxNodes?: number; +} export interface CheckboxAuditParameters { + /** One common ancestor path for both the summary and detail region. */ + scopePath?: UiScopeSelector[]; /** Omit to read the region unconditionally. On reads details; Off skips them. */ summaryAutomationId?: string; regionAutomationId: string; @@ -34,12 +46,22 @@ function budget(value: unknown, fallback: number, maximum: number, name: string) /** Compile only installed recipes, never expressions or model-generated code. Validate before connecting. */ export function createReadonlyUiRecipe(recipe: unknown, parameters: unknown) { - if (recipe !== 'checkbox-audit') throw new RecipeInputError('UNSUPPORTED_RECIPE', 'recipe', 'Unsupported readonly recipe; use checkbox-audit.'); + if (recipe !== 'checkbox-audit' && recipe !== 'sibling-range') + throw new RecipeInputError('UNSUPPORTED_RECIPE', 'recipe', 'Unsupported readonly recipe; use checkbox-audit or sibling-range.'); + const compiled = recipe === 'sibling-range' ? createSiblingRange(parameters) : createCheckboxAudit(parameters); + return async (reader: UiReader) => compiled(reader); +} + +function createCheckboxAudit(parameters: unknown) { if (!parameters || typeof parameters !== 'object' || Array.isArray(parameters) || - Object.keys(parameters).some(key => !['summaryAutomationId', 'regionAutomationId', 'checkboxAutomationIds', 'maxDepth', 'maxNodes'].includes(key))) + Object.keys(parameters).some(key => !['scopePath', 'summaryAutomationId', 'regionAutomationId', 'checkboxAutomationIds', 'maxDepth', 'maxNodes'].includes(key))) throw new RecipeInputError('INVALID_RECIPE_PARAMETERS', 'parameters', - 'Supply an object with only summaryAutomationId, regionAutomationId, checkboxAutomationIds, maxDepth and maxNodes.'); + 'Supply an object with only scopePath, summaryAutomationId, regionAutomationId, checkboxAutomationIds, maxDepth and maxNodes.'); const input = parameters as CheckboxAuditParameters; + try { validateUiScope(input.scopePath); } + catch { throw new RecipeInputError('INVALID_RECIPE_PARAMETERS', 'scopePath', + 'scopePath requires 1–50 exact parent selectors using only automationId, name and controlType.'); } + const scopePath = input.scopePath === undefined ? undefined : structuredClone(input.scopePath); const region = identifier(input.regionAutomationId, 'regionAutomationId'); const summary = input.summaryAutomationId === undefined ? undefined : identifier(input.summaryAutomationId, 'summaryAutomationId'); if (!Array.isArray(input.checkboxAutomationIds) || input.checkboxAutomationIds.length < 1 || input.checkboxAutomationIds.length > 64) @@ -51,13 +73,15 @@ export function createReadonlyUiRecipe(recipe: unknown, parameters: unknown) { return async (reader: UiReader) => { if (summary !== undefined) { - const value = await reader.inspect({ query: { automationId: summary }, readStates: true, maxDepth: 2, maxNodes: 8 }); + const value = await reader.inspect({ scopePath, query: { automationId: summary }, readStates: true, maxDepth: 2, maxNodes: 8, + allowAuxiliaryPropertyGaps: true }); if (value.tree?.automationId !== summary) throw new Error('Summary observation does not match its AutomationId.'); const state = value.tree.states?.toggle; if (state === 'Off') return { detailsRequired: false }; if (state !== 'On') throw new Error('Summary toggle state is unknown; details were not read.'); } - const value = await reader.inspect({ query: { automationId: region }, readStates: true, maxDepth, maxNodes }); + const value = await reader.inspect({ scopePath, query: { automationId: region }, readStates: true, maxDepth, maxNodes, + allowAuxiliaryPropertyGaps: true }); if (value.tree?.automationId !== region) throw new Error('Region observation does not match its AutomationId.'); const selected = new Map(ids.map(id => [id, [] as UiNode[]])); const visit = (node: UiNode) => { @@ -79,3 +103,65 @@ export function createReadonlyUiRecipe(recipe: unknown, parameters: unknown) { return summary === undefined ? details : { detailsRequired: true, details }; }; } + +function createSiblingRange(parameters: unknown) { + if (!parameters || typeof parameters !== 'object' || Array.isArray(parameters) || + Object.keys(parameters).some(key => !['scopePath', 'containerQuery', 'startAfter', 'endBefore', 'maxDepth', 'maxNodes'].includes(key))) + throw new RecipeInputError('INVALID_RECIPE_PARAMETERS', 'parameters', + 'Supply only scopePath, containerQuery, startAfter, endBefore, maxDepth and maxNodes.'); + const input = parameters as SiblingRangeParameters; + for (const field of ['scopePath', 'containerQuery', 'startAfter', 'endBefore'] as const) { + try { + if (field === 'scopePath') validateUiScope(input.scopePath); + else if (field === 'containerQuery') { + if (!input.containerQuery) throw new Error('Required'); + validateUiQuery(input.containerQuery, false); + } else validateUiScope([input[field]]); + } catch { throw new RecipeInputError('INVALID_RECIPE_PARAMETERS', field, 'Supply valid exact selectors observed in the target container.'); } + } + const { scopePath, containerQuery, startAfter, endBefore } = structuredClone(input); + const fields = ['automationId', 'name', 'controlType'] as const; + if (fields.every(key => startAfter[key] === endBefore[key])) + throw new RecipeInputError('INVALID_RECIPE_PARAMETERS', 'endBefore', 'Start and end boundaries must be distinct.'); + const maxDepth = budget(input.maxDepth, 4, 50, 'maxDepth'); + const maxNodes = budget(input.maxNodes, 300, 5000, 'maxNodes'); + const available = (node: UiNode, key: string) => !node.propertyIssues?.some(issue => issue.split(':')[0].toLowerCase() === key.toLowerCase()); + const matches = (node: UiNode, selector: UiScopeSelector) => fields.every(key => selector[key] === undefined || + (available(node, key) && node[key] === selector[key])); + const describe = (node: UiNode) => ({ id: node.id, parentId: node.parentId, name: node.name, + automationId: node.automationId, controlType: node.controlType, propertyIssues: node.propertyIssues }); + + return async (reader: UiReader) => { + // The recipe needs names and sibling order, not state or IDs that some providers never expose. + const value = await reader.inspect({ scopePath, query: containerQuery, readStates: false, maxDepth, maxNodes, allowPropertyGaps: true }); + if (!value.tree || !matches(value.tree, containerQuery)) throw new Error('Container observation does not match its selector.'); + const starts: Array<{ node: UiNode; parent: UiNode }> = [], ends: typeof starts = []; + const visit = (parent: UiNode) => { + for (const node of parent.children) { + if (matches(node, startAfter)) starts.push({ node, parent }); + if (matches(node, endBefore)) ends.push({ node, parent }); + visit(node); + } + }; + visit(value.tree); + if (starts.length !== 1 || ends.length !== 1) throw new Error('Range boundaries must each match exactly one observed node; missing or ambiguous boundaries do not mean an empty group.'); + const start = starts[0], end = ends[0]; + if (start.parent !== end.parent) throw new Error('Range boundaries must be siblings under the same native parent.'); + const siblings = start.parent.children; + // A sibling with missing selector fields could hide a second boundary; do not silently choose the known one. + for (const selector of [startAfter, endBefore]) { + if (siblings.some(node => !matches(node, selector) && fields.every(key => selector[key] === undefined || + !available(node, key) || node[key] === undefined || node[key] === selector[key]))) + throw new Error('A sibling has insufficient identity evidence to resolve the range boundaries.'); + } + const first = siblings.indexOf(start.node), last = siblings.indexOf(end.node); + if (last <= first) throw new Error('End boundary must follow start boundary in the observed sibling order.'); + const items = siblings.slice(first + 1, last); + if (items.some(node => typeof node.name !== 'string' || !node.name.trim() || !available(node, 'name'))) + throw new Error('A range item has no readable name; inspect the retained partial evidence.'); + return { kind: 'logical-group' as const, basis: 'sibling-order' as const, + coverage: 'observed-range-only' as const, businessGroupComplete: 'unknown' as const, + requestId: value.requestId, container: describe(value.tree), parent: describe(start.parent), + startAfter: describe(start.node), endBefore: describe(end.node), observedCount: items.length, items: items.map(describe) }; + }; +} diff --git a/src/Client/ReadonlyUiWorkflow.ts b/src/Client/ReadonlyUiWorkflow.ts index 77f8ee8..7a97801 100644 --- a/src/Client/ReadonlyUiWorkflow.ts +++ b/src/Client/ReadonlyUiWorkflow.ts @@ -1,11 +1,16 @@ import { randomUUID } from 'node:crypto'; import type { CallToolResult } from '@modelcontextprotocol/client'; -import { UI_INSPECT_DEFAULTS, validateUiQuery, type UiInspectRequest, type UiNode } from '../Core/UiContracts.js'; +import { UI_INSPECT_DEFAULTS, UI_INSPECTION_VERSIONS, validateUiQuery, validateUiScope, type UiInspectRequest, type UiNode } from '../Core/UiContracts.js'; import type { UiReviewResult } from '../CompositeTools/UiReview.js'; export type UiTarget = { pid: number; hwnd: string }; -export type UiReadOptions = Pick; -export type UiReviewOptions = UiReadOptions & { candidateFiles: string[]; candidateCodeFiles?: string[]; textQueries?: string[] }; +export type UiReadOptions = Pick & { + /** Client-only: retain className/bounds/isOffscreen gaps without rejecting a semantic state read. Default is strict. */ + allowAuxiliaryPropertyGaps?: boolean; + /** Client-only: delegate required-property validation to the reader's caller; retain all gaps as evidence. */ + allowPropertyGaps?: boolean; +}; +export type UiReviewOptions = Omit & { candidateFiles: string[]; candidateCodeFiles?: string[]; textQueries?: string[] }; export type UiReadCaller = (name: string, args: Record, options: { signal: AbortSignal; timeoutMs: number }) => Promise; export type UiReader = { inspect(options: UiReadOptions): Promise; review(options: UiReviewOptions): Promise }; export type UiWorkflowOptions = { signal?: AbortSignal; timeoutMs?: number; maxSteps?: number; maxIntermediateBytes?: number; maxOutputBytes?: number }; @@ -31,7 +36,7 @@ function evidence(value: UiReviewResult): Record { children.forEach(visit); }; if (value.tree) visit(value.tree); - return { pid: value.pid, hwnd: value.hwnd, queryResult: value.queryResult, + return { pid: value.pid, hwnd: value.hwnd, queryResult: value.queryResult, scopeResult: value.scopeResult, treeComplete: value.treeComplete, truncated: value.truncated, truncateReason: value.truncateReason, totalNodes: value.totalNodes, traversalErrors: value.traversalErrors, propertyIssueCount: value.propertyIssueCount, nodes, sourceEvidence: value.sourceEvidence, codeEvidence: value.codeEvidence, @@ -39,6 +44,19 @@ function evidence(value: UiReviewResult): Record { captureMethod: value.captureMethod, captureQuality: value.captureQuality, imageOmitted: value.imageOmitted }; } +function blockingPropertyIssues(value: UiReviewResult, allowAuxiliary: boolean, allowAll: boolean): boolean { + let count = 0, blocking = false; + const visit = (node: UiNode) => { + count += node.propertyIssues?.length ?? 0; + blocking ||= !allowAll && node.propertyIssues?.some(issue => !allowAuxiliary || + !['classname', 'bounds', 'isoffscreen'].includes(issue.split(':')[0].toLowerCase())) === true; + node.children.forEach(visit); + }; + if (value.tree) visit(value.tree); + // An aggregate gap without its node detail cannot be classified as auxiliary. + return blocking || (value.propertyIssueCount ?? 0) > count; +} + /** Caller must be a connected standard MCP client (for example WinCodeSession.call). No interpreter or server bypass. */ export async function runReadonlyUiWorkflow(call: UiReadCaller, target: UiTarget, workflow: (reader: UiReader) => Promise, options: UiWorkflowOptions = {}) { @@ -81,16 +99,22 @@ export async function runReadonlyUiWorkflow(call: UiReadCaller, target: UiTar if (stopped) throw stopped; const begin = performance.now(); try { - const allowed = ['query', 'capture', 'maxDepth', 'maxNodes', 'readStates', - ...(tool === 'wincode_ui_review' ? ['candidateFiles', 'candidateCodeFiles', 'textQueries'] : [])]; + const allowed = ['query', 'scopePath', 'capture', 'maxDepth', 'maxNodes', 'readStates', + ...(tool === 'wincode_ui_review' ? ['candidateFiles', 'candidateCodeFiles', 'textQueries'] : ['allowAuxiliaryPropertyGaps', 'allowPropertyGaps'])]; if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !allowed.includes(key))) throw new WorkflowStop('INVALID_ARGUMENT', 'Readonly options contain unsupported fields.'); validateUiQuery(input.query, input.readStates); + validateUiScope(input.scopePath); + const { allowAuxiliaryPropertyGaps, allowPropertyGaps, ...toolInput } = input as UiReadOptions; + if (allowAuxiliaryPropertyGaps !== undefined && typeof allowAuxiliaryPropertyGaps !== 'boolean') + throw new WorkflowStop('INVALID_ARGUMENT', 'allowAuxiliaryPropertyGaps must be a boolean.'); + if (allowPropertyGaps !== undefined && typeof allowPropertyGaps !== 'boolean') + throw new WorkflowStop('INVALID_ARGUMENT', 'allowPropertyGaps must be a boolean.'); if (input.capture !== undefined && !['none', 'original', 'annotated'].includes(input.capture)) throw new WorkflowStop('INVALID_ARGUMENT', 'Unsupported capture mode.'); if (input.capture && input.capture !== 'none' && images.length) throw new WorkflowStop('IMAGE_BUDGET_EXCEEDED', 'A workflow may return at most one native image.'); - const args = { ...input, ...fixed, backgroundOnly: true, responseFormat: 'compact', capture: input.capture ?? 'none' }; + const args = { ...toolInput, ...fixed, backgroundOnly: true, responseFormat: 'compact', capture: input.capture ?? 'none' }; if (Buffer.byteLength(JSON.stringify(args)) > 65536) throw new WorkflowStop('INPUT_BUDGET_EXCEEDED', 'Input exceeds 64 KiB.'); step.status = 'running'; let rejectAbort!: () => void; @@ -115,6 +139,11 @@ export async function runReadonlyUiWorkflow(call: UiReadCaller, target: UiTar // The client program receives the payload too; later edits must not rewrite observation evidence. step.evidence = structuredClone(evidence(value)); if (result.isError || !value.success) throw new WorkflowStop(value.errorCode ?? 'TOOL_ERROR', value.errorMessage ?? 'MCP tool failed.'); + // A connected older tool may discard the new field before the Host sees it. + if (input.scopePath && (value.inspectionVersion ?? 0) < UI_INSPECTION_VERSIONS.PARENT_SCOPE) + throw new WorkflowStop('VERSION_MISMATCH', 'scopePath requires inspectionVersion 5.'); + if (input.scopePath && (value.scopeResult?.status !== 'resolved' || value.scopeResult.resolvedCount !== input.scopePath.length)) + throw new WorkflowStop('INCOMPLETE_OBSERVATION', 'Requested parent scope was not confirmed; do not use unscoped observations.'); if (value.pid !== fixed.pid || !value.hwnd || BigInt(value.hwnd) !== BigInt(fixed.hwnd)) throw new WorkflowStop('TARGET_CHANGED', 'Response does not belong to the fixed PID and HWND.'); if (value.queryResult && value.queryResult.status !== 'unique') @@ -123,8 +152,7 @@ export async function runReadonlyUiWorkflow(call: UiReadCaller, target: UiTar 'Complete search found no match. Verify page/selector and inspect an authorized parent candidate; a collapsed cause is unproven. Readonly workflows never perform navigation.' : value.queryResult.status === 'ambiguous' ? 'Selector matched multiple controls. Add an observed exact identifier; no navigation is permitted.' : 'Search is incomplete. Do not infer absence or state; obtain complete scoped evidence before continuing.'); - const nodeIssues = (node: UiNode): boolean => Boolean(node.propertyIssues?.length) || node.children.some(nodeIssues); - if (!value.tree || value.treeComplete !== true || value.truncated || value.traversalErrors || value.propertyIssueCount || nodeIssues(value.tree) || + if (!value.tree || value.treeComplete !== true || value.truncated || value.traversalErrors || blockingPropertyIssues(value, allowAuxiliaryPropertyGaps === true, allowPropertyGaps === true) || value.queryResult?.searchComplete === false || value.sourceEvidenceOmitted || value.codeEvidenceOmitted || (tool === 'wincode_ui_review' && !value.sourceEvidence) || (value.sourceEvidence && (!value.sourceEvidence.fileScanComplete || value.sourceEvidence.truncated)) || diff --git a/src/Client/SkillSession.ts b/src/Client/SkillSession.ts index a1daa00..9544431 100644 --- a/src/Client/SkillSession.ts +++ b/src/Client/SkillSession.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { withTimeout } from '../Core/ResourceManager.js'; import { runReadonlyUiWorkflow, type UiReader, type UiTarget, type UiWorkflowOptions } from './ReadonlyUiWorkflow.js'; -import { createReadonlyUiRecipe, type CheckboxAuditParameters } from './ReadonlyUiRecipes.js'; +import { createReadonlyUiRecipe, type CheckboxAuditParameters, type SiblingRangeParameters } from './ReadonlyUiRecipes.js'; import { runExpandUiWorkflow, type ExpandUiParameters } from './ExpandUiWorkflow.js'; export interface SkillSessionOptions { @@ -89,7 +89,7 @@ export class WinCodeSession { } /** Installed client recipe; compile/validate parameters before the first tool read. */ - readonlyUiRecipe(target: UiTarget, recipe: 'checkbox-audit', parameters: CheckboxAuditParameters, options: UiWorkflowOptions = {}) { + readonlyUiRecipe(target: UiTarget, recipe: 'checkbox-audit' | 'sibling-range', parameters: CheckboxAuditParameters | SiblingRangeParameters, options: UiWorkflowOptions = {}) { return this.readonlyUiWorkflow(target, createReadonlyUiRecipe(recipe, parameters), options); } diff --git a/tests/fixtures/wpf-ui-review/HybridFixture.cs b/tests/fixtures/wpf-ui-review/HybridFixture.cs index 5acd9eb..742941b 100644 --- a/tests/fixtures/wpf-ui-review/HybridFixture.cs +++ b/tests/fixtures/wpf-ui-review/HybridFixture.cs @@ -6,17 +6,35 @@ namespace wpf_ui_review; public partial class MainWindow { + private sealed class EvidenceCheckBox(bool enabledUnknown = false) : CheckBox + { + private readonly bool enabledUnknown = enabledUnknown; + protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() => new EvidencePeer(this); + private sealed class EvidencePeer(EvidenceCheckBox owner) : System.Windows.Automation.Peers.CheckBoxAutomationPeer(owner) + { + protected override string GetClassNameCore() => throw new FixturePropertyException("Fixture auxiliary property unavailable"); + protected override bool IsEnabledCore() => owner.enabledUnknown + ? throw new FixturePropertyException("Fixture enabled evidence unavailable") : base.IsEnabledCore(); + } + } + // Isolated benchmark mode; existing XAML and acceptance fixture identities remain intact. private void BuildHybridFixture() { + if (Environment.GetCommandLineArgs().Contains("--hybrid-scopes")) { BuildScopedHybridFixture(); return; } var panel = new StackPanel { Margin = new Thickness(16) }; - var summary = new CheckBox { Content = "Details required", IsChecked = !Environment.GetCommandLineArgs().Contains("--hybrid-summary-off") }; + var propertyGaps = Environment.GetCommandLineArgs().Contains("--hybrid-evidence"); + CheckBox summary = propertyGaps ? new EvidenceCheckBox() : new CheckBox(); + summary.Content = "Details required"; + summary.IsChecked = !Environment.GetCommandLineArgs().Contains("--hybrid-summary-off"); AutomationProperties.SetAutomationId(summary, "hybridSummary"); panel.Children.Add(summary); var rows = new StackPanel(); for (var i = 0; i < 8; i++) { - var row = new CheckBox { Content = "Check " + i, IsChecked = i != 3, IsEnabled = i != 6 }; + CheckBox row = propertyGaps && i == 0 + ? new EvidenceCheckBox(Environment.GetCommandLineArgs().Contains("--hybrid-enabled-unknown")) : new CheckBox(); + row.Content = "Check " + i; row.IsChecked = i != 3; row.IsEnabled = i != 6; AutomationProperties.SetAutomationId(row, "hybridCheck" + i); rows.Children.Add(row); } @@ -48,4 +66,30 @@ private void BuildHybridFixture() changes.Start(); } } + + private void BuildScopedHybridFixture() + { + var panel = new StackPanel { Margin = new Thickness(16) }; + foreach (var voice in new[] { true, false }) + { + var content = new StackPanel(); + var summary = new CheckBox { Content = "Details required", IsChecked = voice }; + AutomationProperties.SetAutomationId(summary, "hybridSummary"); + content.Children.Add(summary); + var rows = new StackPanel(); + for (var i = 0; i < 2; i++) + { + var row = new CheckBox { Content = "Check " + i, IsChecked = voice ? i == 0 : i == 1, IsEnabled = voice || i == 0 }; + AutomationProperties.SetAutomationId(row, "hybridCheck" + i); + rows.Children.Add(row); + } + var region = new GroupBox { Header = "Checks", Content = rows }; + AutomationProperties.SetAutomationId(region, "hybridChecks"); + content.Children.Add(region); + var parent = new GroupBox { Header = "Settings", Content = content }; + AutomationProperties.SetAutomationId(parent, voice ? "hybridVoice" : "hybridDisplay"); + panel.Children.Add(parent); + } + Content = panel; + } } diff --git a/tests/fixtures/wpf-ui-review/MainWindow.xaml.cs b/tests/fixtures/wpf-ui-review/MainWindow.xaml.cs index 54f5e57..54875c8 100644 --- a/tests/fixtures/wpf-ui-review/MainWindow.xaml.cs +++ b/tests/fixtures/wpf-ui-review/MainWindow.xaml.cs @@ -73,7 +73,7 @@ private static void ReportForeground() public MainWindow() { - if (Environment.GetCommandLineArgs().Contains("--navigation-evidence")) + if (Environment.GetCommandLineArgs().Contains("--navigation-evidence") || Environment.GetCommandLineArgs().Contains("--hybrid-evidence")) { // WPF also reads IsEnabled during event updates. Only handle our injected event fault; // UIA property queries still propagate it through WPF's ElementUtil.Invoke to the client. diff --git a/tests/hybrid-ui-session.test.ts b/tests/hybrid-ui-session.test.ts index 5ae6798..5c2da55 100644 --- a/tests/hybrid-ui-session.test.ts +++ b/tests/hybrid-ui-session.test.ts @@ -73,6 +73,128 @@ const recipeParameters = { summaryAutomationId: 'hybridSummary', regionAutomatio checkboxAutomationIds: Array.from({ length: 8 }, (_, i) => 'hybridCheck' + i), maxDepth: 4, maxNodes: 40 }; const readCliResult = async (receipt: any) => JSON.parse(await fs.readFile(receipt.resultFile, 'utf8')); +it('JSON scoped checkbox audit separates identical IDs and refuses missing or ambiguous ancestors without fallback', + { skip: process.platform !== 'win32', timeout: 90000 }, async () => { + const output = await fs.mkdtemp(path.join(root, 'test-tmp', 'checkbox-scopes-')); + console.log(JSON.stringify({ checkboxScopeArtifacts: output })); + const f = await fixture(undefined, ['--hybrid-fixture', '--hybrid-scopes']), driver = cli(); + const records: Record = {}; + const auditPath = path.join(process.env.LOCALAPPDATA!, 'WinCode/logs/ui-audit/access.jsonl'); + const auditBefore = await fs.readFile(auditPath, 'utf8').catch(() => ''); + const parameters = { ...recipeParameters, checkboxAutomationIds: ['hybridCheck0', 'hybridCheck1'] }; + const voice = [{ automationId: 'hybridVoice' }], display = [{ automationId: 'hybridDisplay' }]; + const request = async (id: string, fields: Record) => { + driver.send({ id, ...fields }); const receipt = await driver.wait(value => value.id === id); + assert.ok(receipt.resultFile, JSON.stringify(receipt)); + const response = await readCliResult(receipt); records[id] = { receipt, response }; + await fs.writeFile(path.join(output, 'results.json'), JSON.stringify(records, null, 2)); + return parse(response); + }; + try { + await driver.wait(value => value.ready); + const audit = (id: string, scopePath: any[] | undefined, conditional = true) => request(id, + { action: 'readonly-ui', recipe: 'checkbox-audit', target: f.target, + parameters: { ...parameters, scopePath, ...(conditional ? {} : { summaryAutomationId: undefined }) } }); + const on = await audit('voice', voice); + assert.equal(on.success, true, JSON.stringify(on)); assert.equal(on.metrics.dispatchedCalls, 2); + assert.deepEqual(on.findings, { detailsRequired: true, details: { checkedCount: 1, unchecked: ['hybridCheck1'], disabled: [] } }); + assert.ok(on.steps.every((step: any) => step.evidence.scopeResult.status === 'resolved' && step.evidence.scopeResult.resolvedCount === 1)); + const off = await audit('display-off', display); + assert.equal(off.success, true, JSON.stringify(off)); assert.equal(off.metrics.dispatchedCalls, 1); + assert.deepEqual(off.findings, { detailsRequired: false }); + const other = await audit('display-details', display, false); + assert.equal(other.success, true, JSON.stringify(other)); assert.equal(other.metrics.dispatchedCalls, 1); + assert.deepEqual(other.findings, { checkedCount: 1, unchecked: ['hybridCheck0'], disabled: ['hybridCheck1'] }); + for (const [id, scopePath, code] of [ + ['missing', [{ automationId: 'missing' }], 'SCOPE_NOT_FOUND'], + ['ambiguous', [{ name: 'Settings', controlType: 'Group' }], 'SCOPE_AMBIGUOUS'], + ['unscoped', undefined, 'QUERY_AMBIGUOUS'], + ] as const) { + const failed = await audit(id, scopePath ? [...scopePath] : undefined); + assert.equal(failed.success, false, JSON.stringify(failed)); assert.equal(failed.errorCode, code); + assert.equal(failed.findings, undefined); assert.equal(failed.metrics.dispatchedCalls, 1); + if (scopePath) assert.equal(failed.steps[0].evidence.scopeResult.failedIndex, 0); + } + // Independent native reads verify each side's final state, rather than reusing recipe findings. + for (const [id, scopePath, expected] of [['voice-final', voice, 'On'], ['display-final', display, 'Off']] as const) { + const independent = await request(id, { tool: 'wincode_ui_inspect', arguments: { ...f.target, scopePath, + query: { automationId: 'hybridCheck0' }, readStates: true, capture: 'none', backgroundOnly: true } }); + assert.equal(independent.success, true, JSON.stringify(independent)); assert.equal(independent.tree.states.toggle, expected); + } + driver.send({ id: 'status', action: 'status' }); const gateway = (await driver.wait(value => value.id === 'status')).status; + driver.send({ id: 'close', action: 'close' }); assert.equal((await driver.wait(value => value.id === 'close')).closed, true); + await withTimeout(driver.closed, 8000, 'Scoped CLI exit'); exited(gateway.pid); exited(driver.child.pid!); + const auditAfter = await fs.readFile(auditPath, 'utf8'); assert.ok(auditAfter.startsWith(auditBefore)); + const starts = auditAfter.slice(auditBefore.length).trim().split('\n').filter(Boolean).map(line => JSON.parse(line)) + .filter(entry => entry.phase === 'start' && entry.target === f.target.pid); + assert.equal(starts.length, 9); + for (const entry of starts) { assert.equal(entry.op, 'inspect'); exited(entry.helper); } + records.cleanup = { helpersExited: true, gatewayExited: true, cliExited: true, helperStarts: starts.length }; + } finally { + await killProcessTree(driver.child); await withTimeout(driver.closed, 8000, 'Scoped cleanup'); await stopFixture(f.child, f.closed); + records.fixtureExited = true; + await fs.writeFile(path.join(output, 'results.json'), JSON.stringify(records, null, 2)); + } + }); + +it('JSON checkbox audit reads real auxiliary property failures while preserving required-state failures', + { skip: process.platform !== 'win32', timeout: 120000 }, async () => { + const output = await fs.mkdtemp(path.join(root, 'test-tmp', 'checkbox-evidence-')); + console.log(JSON.stringify({ checkboxEvidenceArtifacts: output })); + for (const mode of ['on', 'off', 'enabled-unknown']) { + const f = await fixture(undefined, ['--hybrid-fixture', '--hybrid-evidence', + ...(mode === 'off' ? ['--hybrid-summary-off'] : mode === 'enabled-unknown' ? ['--hybrid-enabled-unknown'] : [])]); + const driver = cli(), records: Record = {}; + const auditPath = path.join(process.env.LOCALAPPDATA!, 'WinCode/logs/ui-audit/access.jsonl'); + const auditBefore = await fs.readFile(auditPath, 'utf8').catch(() => ''); + try { + await driver.wait(value => value.ready); + const request = async (id: string, fields: Record) => { + driver.send({ id, ...fields }); const receipt = await driver.wait(value => value.id === id); + assert.ok(receipt.resultFile, JSON.stringify(receipt)); + const response = await readCliResult(receipt); records[id] = { receipt, response }; + await fs.writeFile(path.join(output, mode + '.json'), JSON.stringify(records, null, 2)); + return parse(response); + }; + const before = await request('before', { tool: 'wincode_ui_inspect', arguments: { ...f.target, ...summary, + backgroundOnly: true, responseFormat: 'compact', capture: 'none' } }); + assert.equal(before.success, true); assert.equal(before.treeComplete, true); + assert.ok(before.tree.propertyIssues.some((issue: string) => /^className:(error|unsupported)$/.test(issue))); + const checked = await request('audit', { action: 'readonly-ui', recipe: 'checkbox-audit', target: f.target, parameters: recipeParameters }); + assert.equal(checked.success, mode !== 'enabled-unknown', JSON.stringify(checked)); + assert.equal(checked.metrics.dispatchedCalls, mode === 'off' ? 1 : 2); + assert.ok(checked.steps[0].evidence.nodes.some((node: any) => node.propertyIssues?.some((issue: string) => /^className:(error|unsupported)$/.test(issue)))); + if (mode === 'enabled-unknown') { + assert.equal(checked.errorCode, 'INCOMPLETE_OBSERVATION'); assert.equal(checked.findings, undefined); + assert.ok(checked.steps[1].evidence.nodes.some((node: any) => node.automationId === 'hybridCheck0' && node.propertyIssues?.some((issue: string) => /^isEnabled:(error|unsupported)$/.test(issue)))); + } else if (mode === 'off') assert.deepEqual(checked.findings, { detailsRequired: false }); + else { + assert.deepEqual(checked.findings, { detailsRequired: true, + details: { checkedCount: 7, unchecked: ['hybridCheck3'], disabled: ['hybridCheck6'] } }); + const independent = await request('independent', { tool: 'wincode_ui_inspect', arguments: { ...f.target, ...detail, + backgroundOnly: true, responseFormat: 'compact', capture: 'none' } }); + const nodes: any[] = []; const visit = (node: any) => { nodes.push(node); node.children.forEach(visit); }; visit(independent.tree); + assert.equal(nodes.find(node => node.automationId === 'hybridCheck0').states.toggle, 'On'); + assert.equal(nodes.find(node => node.automationId === 'hybridCheck3').states.toggle, 'Off'); + assert.equal(nodes.find(node => node.automationId === 'hybridCheck6').isEnabled, false); + } + driver.send({ id: 'status', action: 'status' }); const gateway = (await driver.wait(value => value.id === 'status')).status; + driver.send({ id: 'close', action: 'close' }); assert.equal((await driver.wait(value => value.id === 'close')).closed, true); + await withTimeout(driver.closed, 8000, 'Evidence CLI exit'); exited(gateway.pid); exited(driver.child.pid!); + const auditAfter = await fs.readFile(auditPath, 'utf8'); assert.ok(auditAfter.startsWith(auditBefore)); + const starts = auditAfter.slice(auditBefore.length).trim().split('\n').filter(Boolean).map(line => JSON.parse(line)) + .filter(entry => entry.phase === 'start' && entry.target === f.target.pid); + assert.equal(starts.length, mode === 'on' ? 4 : mode === 'off' ? 2 : 3); + for (const entry of starts) { assert.equal(entry.op, 'inspect'); exited(entry.helper); } + records.cleanup = { helpersExited: true, gatewayExited: true, cliExited: true, helperStarts: starts.length }; + } finally { + await killProcessTree(driver.child); await withTimeout(driver.closed, 8000, 'Evidence cleanup'); await stopFixture(f.child, f.closed); + records.fixtureExited = true; + await fs.writeFile(path.join(output, mode + '.json'), JSON.stringify(records, null, 2)); + } + } + }); + it('JSON navigation isolates duplicate parent and child IDs and keeps scope failures action-free', { skip: process.platform !== 'win32', timeout: 60000 }, async () => { const f = await fixture(undefined, ['--action-fixture', '--scope-navigation']), driver = cli(); diff --git a/tests/hybrid-ui-workflow.test.ts b/tests/hybrid-ui-workflow.test.ts index 4317443..a0645d8 100644 --- a/tests/hybrid-ui-workflow.test.ts +++ b/tests/hybrid-ui-workflow.test.ts @@ -6,6 +6,67 @@ import { createReadonlyUiRecipe } from '../src/Client/ReadonlyUiRecipes.js'; import { runExpandUiWorkflow } from '../src/Client/ExpandUiWorkflow.js'; const target = { pid: 42, hwnd: '0x123' }; + +const rangeParameters = { containerQuery: { name: 'library', controlType: 'Table' }, + startAfter: { name: '2024', controlType: 'DataItem' }, endBefore: { name: '2023', controlType: 'DataItem' } }; +function flatLibrary(): UiInspectResult { + const row = (id: number, name: string) => ({ id, parentId: 2, name, controlType: 'DataItem', + propertyIssues: ['automationId:unsupported'], children: [] }); + return { ...observation(), propertyIssueCount: 6, tree: { id: 1, parentId: null, name: 'library', controlType: 'Table', children: [ + { id: 2, parentId: 1, propertyIssues: ['controlType:unsupported'], children: [ + row(3, '2025'), row(4, '2024'), row(5, 'Game A'), row(6, 'Game B'), row(7, '2023'), + ] }, + ] } }; +} + +test('sibling range reads named rows without AutomationIds and preserves logical boundaries and native identities', async () => { + let calls = 0; + const checked = await runReadonlyUiWorkflow(async (tool, args) => { + calls++; + assert.equal(tool, 'wincode_ui_inspect'); + assert.equal(args.backgroundOnly, true); + assert.equal(args.readStates, false); + assert.equal('allowPropertyGaps' in args, false); + assert.deepEqual(args.query, rangeParameters.containerQuery); + return result(flatLibrary()); + }, target, createReadonlyUiRecipe('sibling-range', rangeParameters)); + assert.equal(checked.report.success, true, JSON.stringify(checked.report)); + const findings = checked.report.findings as any; + assert.equal(calls, 1); + assert.equal(findings.kind, 'logical-group'); + assert.equal(findings.basis, 'sibling-order'); + assert.equal(findings.coverage, 'observed-range-only'); + assert.equal(findings.businessGroupComplete, 'unknown'); + assert.deepEqual(findings.items.map((n: any) => [n.id, n.parentId, n.name]), [[5, 2, 'Game A'], [6, 2, 'Game B']]); + assert.equal(findings.startAfter.id, 4); assert.equal(findings.endBefore.id, 7); + assert.equal(findings.observedCount, 2); + assert.equal(checked.report.steps[0].evidence!.propertyIssueCount, 6); + const strict = await runReadonlyUiWorkflow(async () => result(flatLibrary()), target, reader => reader.inspect({})); + assert.equal(strict.report.errorCode, 'INCOMPLETE_OBSERVATION'); +}); + +test('sibling range rejects uncertain boundaries, unreadable names and incomplete observations without guessing an empty group', async () => { + for (const scenario of ['missing', 'duplicate', 'reversed', 'different-parent', 'unreadable-name', 'name-error', 'truncated', 'search-incomplete']) { + const value = flatLibrary(), rows = value.tree!.children[0].children; + if (scenario === 'missing') rows.pop(); + if (scenario === 'duplicate') rows.push({ ...rows[1], id: 8 }); + if (scenario === 'reversed') rows.reverse(); + if (scenario === 'different-parent') { const end = rows.pop()!; end.parentId = 1; value.tree!.children.push(end); } + if (scenario === 'unreadable-name') delete rows[2].name; + if (scenario === 'name-error') rows[2].propertyIssues!.push('name:error'); + if (scenario === 'truncated') { value.treeComplete = false; value.truncated = true; } + if (scenario === 'search-incomplete') value.queryResult!.searchComplete = false; + value.propertyIssueCount = 1 + rows.reduce((n, row) => n + row.propertyIssues!.length, 0) + (scenario === 'different-parent' ? 1 : 0); + const checked = await runReadonlyUiWorkflow(async () => result(value), target, createReadonlyUiRecipe('sibling-range', rangeParameters)); + assert.equal(checked.report.success, false, scenario); + assert.equal(checked.report.findings, undefined, scenario); + assert.ok(checked.report.steps[0].evidence, scenario); + } + for (const parameters of [{ ...rangeParameters, startAfter: {} }, { ...rangeParameters, endBefore: rangeParameters.startAfter }, + { ...rangeParameters, maxNodes: 0 }, { ...rangeParameters, scopePath: [] }, { ...rangeParameters, unexpected: true }]) { + assert.throws(() => createReadonlyUiRecipe('sibling-range', parameters), (e: any) => e.code === 'INVALID_RECIPE_PARAMETERS' && e.workStarted === false); + } +}); const observation = (requestId = 'read-1'): UiInspectResult => ({ schemaVersion: '1.0', protocolVersion: '1.0', requestId, success: true, ...target, treeComplete: true, truncated: false, queryResult: { status: 'unique', searchComplete: true, visitedNodes: 1, matches: [] }, @@ -13,6 +74,77 @@ const observation = (requestId = 'read-1'): UiInspectResult => ({ schemaVersion: states: { toggle: 'On', selection: 'unsupported', expandCollapse: 'unsupported' }, children: [] } }); const result = (value: unknown) => ({ content: [{ type: 'text' as const, text: JSON.stringify(value) }] }); +test('readonly scopes reach inspect, review and both conditional recipe reads without extra calls', async () => { + const scopePath = [{ automationId: 'voice' }]; + const scoped = () => ({ ...observation(), inspectionVersion: 5, + scopeResult: { status: 'resolved' as const, resolvedCount: 1, visitedNodes: 3 } }); + const calls: any[] = []; + const read = await runReadonlyUiWorkflow(async (tool, args) => { + calls.push({ tool, args }); + return result({ ...scoped(), ...(tool === 'wincode_ui_review' ? { sourceEvidence: { fileScanComplete: true, truncated: false } } : {}) }); + }, target, async reader => { + await reader.inspect({ scopePath, query: { automationId: 'check' } }); + await reader.review({ scopePath, candidateFiles: ['MainWindow.xaml'] }); + }); + assert.equal(read.report.success, true, JSON.stringify(read.report)); + assert.deepEqual(calls.map(call => call.tool), ['wincode_ui_inspect', 'wincode_ui_review']); + assert.ok(calls.every(call => JSON.stringify(call.args.scopePath) === JSON.stringify(scopePath))); + assert.deepEqual(read.report.steps[0].evidence?.scopeResult, scoped().scopeResult); + for (const mode of ['on', 'off', 'unconditional', 'unscoped']) { + const path = structuredClone(scopePath); + const recipe = createReadonlyUiRecipe('checkbox-audit', { regionAutomationId: 'region', checkboxAutomationIds: ['check'], + ...(mode === 'unconditional' ? {} : { summaryAutomationId: 'summary' }), ...(mode === 'unscoped' ? {} : { scopePath: path }) }); + path[0].automationId = 'changed-after-validation'; + const queries: string[] = []; + const checked = await runReadonlyUiWorkflow(async (tool, args) => { + assert.equal(tool, 'wincode_ui_inspect'); + assert.deepEqual(args.scopePath, mode === 'unscoped' ? undefined : scopePath); + const id = (args.query as any).automationId; queries.push(id); + const value = mode === 'unscoped' ? observation() : scoped(); + value.tree = { ...value.tree!, automationId: id, children: id === 'region' + ? [{ ...observation().tree!, id: 2, parentId: 1, controlType: 'CheckBox' }] : [] }; + if (mode === 'off') value.tree.states!.toggle = 'Off'; + return result(value); + }, target, recipe); + assert.equal(checked.report.success, true, JSON.stringify(checked.report)); + assert.deepEqual(queries, mode === 'off' ? ['summary'] : mode === 'unconditional' ? ['region'] : ['summary', 'region']); + assert.deepEqual(checked.report.findings, mode === 'off' ? { detailsRequired: false } : mode === 'unconditional' + ? { checkedCount: 1, unchecked: [], disabled: ['check'] } + : { detailsRequired: true, details: { checkedCount: 1, unchecked: [], disabled: ['check'] } }); + } +}); + +test('readonly scopes reject invalid paths, lost scope evidence and unsupported tools before producing findings', async () => { + let dispatched = 0; + for (const scopePath of [[], [{}], [{ name: '' }], [{ name: 'private', maxMatches: 1 }], Array(51).fill({ name: 'x' })]) { + assert.throws(() => createReadonlyUiRecipe('checkbox-audit', { scopePath, regionAutomationId: 'region', checkboxAutomationIds: ['check'] }), + (error: any) => error.code === 'INVALID_RECIPE_PARAMETERS' && error.field === 'scopePath' && error.workStarted === false && !error.message.includes('private')); + const rejected = await runReadonlyUiWorkflow(async () => { dispatched++; return result(observation()); }, target, + reader => reader.inspect({ scopePath } as any)); + assert.equal(rejected.report.success, false); assert.equal(rejected.report.steps[0].status, 'not_started'); + } + assert.equal(dispatched, 0); + const scopePath = [{ automationId: 'voice' }]; + const cases = [ + ...['SCOPE_NOT_FOUND', 'SCOPE_AMBIGUOUS', 'SCOPE_SEARCH_INCOMPLETE'].map(errorCode => ({ errorCode, + payload: { ...observation(), success: false, errorCode, scopeResult: { resolvedCount: 0, failedIndex: 0, status: 'incomplete', visitedNodes: 3 } } })), + ...[undefined, 4].map(inspectionVersion => ({ errorCode: 'VERSION_MISMATCH', payload: { ...observation(), inspectionVersion } })), + { errorCode: 'INCOMPLETE_OBSERVATION', payload: { ...observation(), inspectionVersion: 5 } }, + { errorCode: 'INCOMPLETE_OBSERVATION', payload: { ...observation(), inspectionVersion: 5, + scopeResult: { resolvedCount: 0, status: 'resolved', visitedNodes: 3 } } }, + ]; + for (const { errorCode, payload } of cases) { + let calls = 0; + const rejected = await runReadonlyUiWorkflow(async () => { calls++; return result(payload); }, target, async reader => { + await reader.inspect({ scopePath }); + return reader.inspect({}); + }); + assert.equal(rejected.report.errorCode, errorCode, JSON.stringify(rejected.report)); + assert.equal(rejected.report.findings, undefined); assert.equal(calls, 1); + assert.deepEqual(rejected.report.steps[0].evidence?.scopeResult, (payload as any).scopeResult); + } +}); + test('navigation keeps the observed parent path through selection, expansion and the final child read', async () => { const scopePath = [{ automationId: 'voiceRegion' }]; const parentQuery = { automationId: 'advanced', controlType: 'Group' }; @@ -396,6 +528,71 @@ test('installed checkbox recipe branches on observed states and rejects ambiguou assert.deepEqual(unconditional.report.findings, { checkedCount: 1, unchecked: ['b'], disabled: ['a'] }); }); +test('checkbox audit retains auxiliary gaps without rejecting known states or weakening default readers', async () => { + for (const scenario of ['on', 'off', 'unconditional']) { + const calls: string[] = []; + const call: UiReadCaller = async (_tool, args) => { + assert.equal('allowAuxiliaryPropertyGaps' in args, false, 'client policy must not reach MCP'); + const id = (args.query as { automationId: string }).automationId; calls.push(id); + const value = observation(); + value.tree = { ...value.tree!, automationId: id, controlType: 'CheckBox', + propertyIssues: ['className:error'], children: id === 'region' ? [ + { ...observation().tree!, id: 2, parentId: 1, automationId: 'a', controlType: 'CheckBox', propertyIssues: ['bounds:error'] }, + { ...observation().tree!, id: 3, parentId: 1, automationId: 'b', controlType: 'CheckBox', isEnabled: true, + propertyIssues: ['isOffscreen:unsupported'], states: { toggle: 'Off', selection: 'unsupported', expandCollapse: 'unsupported' } }, + ] : [] }; + value.tree.states!.toggle = scenario === 'off' ? 'Off' : 'On'; + value.propertyIssueCount = id === 'region' ? 3 : 1; + return result(value); + }; + const checked = await runReadonlyUiWorkflow(call, target, createReadonlyUiRecipe('checkbox-audit', { + ...(scenario !== 'unconditional' ? { summaryAutomationId: 'summary' } : {}), + regionAutomationId: 'region', checkboxAutomationIds: ['a', 'b'], + })); + assert.equal(checked.report.success, true, JSON.stringify(checked.report)); + const details = { checkedCount: 1, unchecked: ['b'], disabled: ['a'] }; + assert.deepEqual(checked.report.findings, scenario === 'off' ? { detailsRequired: false } : + scenario === 'on' ? { detailsRequired: true, details } : details); + assert.deepEqual(calls, scenario === 'off' ? ['summary'] : scenario === 'on' ? ['summary', 'region'] : ['region']); + assert.equal(checked.report.steps.at(-1)!.evidence!.propertyIssueCount, scenario === 'off' ? 1 : 3); + const nodes = checked.report.steps.at(-1)!.evidence!.nodes as Array<{ propertyIssues: string[] }>; + assert.deepEqual(nodes[0].propertyIssues, ['className:error']); + if (scenario !== 'off') assert.deepEqual(nodes.slice(1).map(node => node.propertyIssues), [['bounds:error'], ['isOffscreen:unsupported']]); + const strict = await runReadonlyUiWorkflow(call, target, reader => reader.inspect({ query: { automationId: 'summary' }, readStates: true })); + assert.equal(strict.report.errorCode, 'INCOMPLETE_OBSERVATION'); + } +}); + +test('checkbox audit still rejects required property gaps, incomplete coverage and unknown selected states', async () => { + for (const scenario of ['identity', 'enabled', 'unknown-property', 'unexplained-count', 'truncated', 'traversal', 'search', + 'ambiguous', 'drift', 'unknown-toggle', 'missing', 'duplicate']) { + let calls = 0; + const checked = await runReadonlyUiWorkflow(async () => { + calls++; + const value = observation(); + const check = { ...value.tree!, id: 2, parentId: 1, automationId: 'a', controlType: 'CheckBox', propertyIssues: ['className:error'] }; + value.tree = { ...value.tree!, automationId: 'region', children: [check] }; value.propertyIssueCount = 1; + if (scenario === 'identity') check.propertyIssues.push('automationId:error'); + if (scenario === 'enabled') check.propertyIssues.push('isEnabled:error'); + if (scenario === 'unknown-property') check.propertyIssues.push('newField:error'); + if (scenario === 'unexplained-count') value.propertyIssueCount = 2; + if (scenario === 'truncated') { value.truncated = true; value.treeComplete = false; } + if (scenario === 'traversal') value.traversalErrors = 1; + if (scenario === 'search') value.queryResult!.searchComplete = false; + if (scenario === 'ambiguous') value.queryResult!.status = 'ambiguous'; + if (scenario === 'drift') value.pid = 99; + if (scenario === 'unknown-toggle') check.states = { ...check.states!, toggle: 'unknown' }; + if (scenario === 'missing') { value.tree.children = []; value.propertyIssueCount = 0; } + if (scenario === 'duplicate') { value.tree.children.push(structuredClone(check)); value.propertyIssueCount = 2; } + return result(value); + }, target, createReadonlyUiRecipe('checkbox-audit', { regionAutomationId: 'region', checkboxAutomationIds: ['a'] })); + assert.equal(checked.report.success, false, scenario); assert.equal(checked.report.findings, undefined, scenario); + assert.equal(calls, 1, scenario); + assert.equal(checked.report.errorCode, scenario === 'ambiguous' ? 'QUERY_AMBIGUOUS' : scenario === 'drift' ? 'TARGET_CHANGED' : + ['unknown-toggle', 'missing', 'duplicate'].includes(scenario) ? 'WORKFLOW_ERROR' : 'INCOMPLETE_OBSERVATION', scenario); + } +}); + test('readonly workflow serializes concurrent requests, evaluates actual observations and retains native image evidence', async () => { let active = 0; const calls: string[] = []; diff --git a/tests/skill-session.test.ts b/tests/skill-session.test.ts index 2271e00..ae2faac 100644 --- a/tests/skill-session.test.ts +++ b/tests/skill-session.test.ts @@ -120,7 +120,9 @@ it('one Skill session preserves a real Roslyn snapshot across calls and rejects const stale = await session.call('wincode_find_references', { symbolName: 'Save', symbolLocation: selected.location }); assert.equal(stale.isError, true); assert.equal(payload(stale).errorCode, 'SNAPSHOT_STALE'); const refreshed = payload(await session.call('wincode_find_code_symbol', { query: 'Save' })).symbols.find((item: any) => item.name === 'Save'); - assert.equal(payload(await session.call('wincode_find_references', { symbolName: 'Save', symbolLocation: refreshed.location })).references.length, 2); + const refreshedReferences = await session.call('wincode_find_references', { symbolName: 'Save', symbolLocation: refreshed.location }); + assert.notEqual(refreshedReferences.isError, true, JSON.stringify(refreshedReferences)); + assert.equal(payload(refreshedReferences).references.length, 2); assert.notEqual(refreshed.location.snapshotId, selected.location.snapshotId); processes = ownedProcesses(session.status.pid); await session.close(); await session.close();