diff --git a/docs/EVIDENCE.md b/docs/EVIDENCE.md index c846e743..fda49292 100644 --- a/docs/EVIDENCE.md +++ b/docs/EVIDENCE.md @@ -51,6 +51,7 @@ R1 固定总出口的源码为 `ce9b24d`,早期 `2dc8d6d` / `9b16acc` 仍分 | 工作区几何与交互的固定记录 | [#38](https://github.com/JavaZeroo/termforge/issues/38)、[工作区几何与输出验证](../experiments/shell/results/2026-10-26-workspace-geometry/validation.json)、[resize 限定证据](../experiments/core/results/2026-10-26-resize-replay/validation.json) | 固定 27f376d 的客户区/布局/输出、默认/候选恢复及窄屏记录;其完整回归仍有 #36/#38 红项,不代表当前所有配置或 R2 通过。 | | 短窗口选区夹具与 hosted 四 shell 前置(#38) | [固定锚点回归](../experiments/shell/test_shell.py)、[准备入口](../tools/prepare_hosted_shells.py)、[夹具收尾合同](research/hosted-wsl-fixture-cleanup.md)、[云端结果与剩余阻塞](https://github.com/JavaZeroo/termforge/issues/38) | 短窗口保留相同选择断言;仅指定一次性 hosted VM 准备固定 Ubuntu。夹具强制收尾与产品自然退出分列,未知归属/回收仍失败。 | | 工作区恢复的运输完成端点(#38 夹具修正) | [限定合同](research/workspace-restore-transport-oracle.md)、[原失败及默认/候选验证](../experiments/shell/results/2026-10-26-workspace-transport-oracle/validation.json) | OSC 标题中的重复标记与 column-one CUP 误导旧端点;新判定保留完整 UTF-8、期限、负载及恢复断言。仅修夹具,不宣称修复产品恢复。 | +| 工作区恢复的命令编辑前缀(#61) | [BS 因果反例及复验](../experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json)、[运输完成合同](research/workspace-restore-transport-oracle.md) | 原失败中的 PowerShell 命令重绘 BS 污染当前记录;真实 LF 后仍须完整输出与新提示符,回显/拼接/非法封装拒绝保留。仅修完成 oracle,原 30/40 秒与产品断言不变。 | | 候选文字观测与回显端点(#38 夹具修正) | [同源码前后对照及反事实](../experiments/shell/results/2026-10-26-native-observation/validation.json)、[标准文本投影](../experiments/core/text_projection.hpp) | 观测改用标准文本投影,回显完成后才全选;原断言和负例保留。修正夹具时序/投影,不宣称产品选择策略已变或 R2 全绿。 | | 拒绝负例的读取前停止(#46 夹具修正) | [原记录、反事实及 fresh 验证](../experiments/shell/results/2026-10-26-receiver-stop/validation.json)、[公开空数据夹具](../experiments/shell/fixtures/rejected-order-before-read.json) | 旧 oracle 错误拒绝“首读前已停止”;同一空数据/零读原件红→绿,正常读取约束保留。限定夹具修正,不解释 #44 的退出。 | | 候选 GUI 合法空标题(#45) | [真实 GUI 前后对照](../experiments/shell/results/2026-10-26-empty-title/validation.json)、[转换入口](../experiments/presentation/render.hpp)、[持续回归](../experiments/shell/candidate_title_test.cpp) | 真实空 OSC 标题使旧 UTF 转换抛异常;修后两 fresh GUI 正常,非法非空反例保留。不回填旧 normal-1 未知异常的根因。 | diff --git a/docs/FEATURES.md b/docs/FEATURES.md index c1a48bd7..e80c9546 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -24,7 +24,7 @@ |---|---|---|---| | 本地命令输入/输出 | **实现中,未验收**:Windows shell → ConPTY → 有界队列 → 终端核心 → HWND;普通字符、中文字符消息、基础编辑和回收有固定场景。[持续 cmd 输出/Ctrl-C 回归](../experiments/conpty/README.md#持续-cmd-输出与-ctrlc-独立回归)保留完整负载、单次 ETX、原始字节和清理 | [#36](https://github.com/JavaZeroo/termforge/issues/36) 的历史 normal/resize 超时原因未知;旧、新探针的后续成功不是因果修复。原生控制处理函数、完整键盘模式及四 shell 交叉仍需验收;中文字符消息不等于 IME | R1 / R2 | | 历史浏览 | **限定验收**:100 行有界物理行历史、只读投影、滚轮/翻页/原生滚动条与每会话阅读锚点;编号内容、隐藏输出、淘汰、缩放与回收有[固定回归](../experiments/shell/results/2026-10-23-r1-foundations/validation.json),组合出口见八会话工作流 | 候选历史/resize 合同另列;完整 reflow 与大历史规模的内存、搜索和滚动性能未验收 | R1;扩展 R2 / R4 | -| 选择与复制 | **限定验收**:屏幕/100行有界历史的拖选、词/行、全选、自动滚动及每会话高亮;菜单/快捷键和实际 CF_UNICODETEXT 精确往返。[最终系统审计](../experiments/shell/results/2026-10-24-r1-selection-accepted/validation.json)覆盖占用反馈/保持/重试、live覆盖失效、同组标签、原生rename EDIT复制与关闭后目标隔离;[带样式尾空格/软硬行/受控DPI](../experiments/shell/results/2026-10-24-r1-styled-system-copy/validation.json)另有两轮系统证据 | 普通尾空格经 ConPTY 规范化后不保留;显式空白端点策略未对照,词规则与容量为本项目策略。限定动作与参考差异见统一文字动作,完整组合见 #18 的独立验收;矩形选择归 R4,受控 DPI 不等于真实跨显示器 | R1;扩展 R2 / R4 | +| 选择与复制 | **限定验收**:屏幕/100行有界历史的拖选、词/行、全选、自动滚动及每会话高亮;菜单/快捷键和实际 CF_UNICODETEXT 精确往返。[最终系统审计](../experiments/shell/results/2026-10-24-r1-selection-accepted/validation.json)覆盖占用反馈/保持/重试、live覆盖失效、同组标签、原生rename EDIT复制与关闭后目标隔离;[带样式尾空格/软硬行/受控DPI](../experiments/shell/results/2026-10-24-r1-styled-system-copy/validation.json)另有两轮系统证据 | hosted reverse 场景的复合断言失败由 [#66](https://github.com/JavaZeroo/termforge/issues/66) 独立定位;普通尾空格经 ConPTY 规范化后不保留;显式空白端点策略未对照,词规则与容量为本项目策略。限定动作与参考差异见统一文字动作,完整组合见 #18 的独立验收;矩形选择归 R4,受控 DPI 不等于真实跨显示器 | R1;扩展 R2 / R4 | | 粘贴 | **限定验收**:有界传输、异步读取、确认与目标绑定;正常系统粘贴、占用/超限错误与重试、读取和发送途中取消/关闭、隐藏目标、洪流及精确接收前缀均有重复验证。[最终系统证据](../experiments/shell/results/2026-10-24-r1-clipboard-accepted/validation.json)保留晚发布原始 1418 和完整回收 | 默认 Return 等限定参考动作已在统一文字动作验收,全流程交叉见 #18 的独立验收;bracketed paste/应用模式归 R2,未宣称全部系统或任意负载验收 | R1;应用模式 R2 | | 查找与定位 | **限定验收**:有界增量字面量搜索、原生查找框、方向/循环/高亮、中文、追加与淘汰;持续输出时可显式使用有效局部命中,运行中查询替换/取消/关闭均有[托管两轮独立证据](../experiments/shell/results/2026-10-24-r1-find-accepted/validation.json) | 局部结果不表示全局末端/循环;动作与受控 DPI 交叉见统一文字动作,组合与资源见 #18 的独立验收。正则、跨会话、完整 Unicode caseless 和大历史规模归 R4 | R1 / R4 | | 统一文字动作 | **限定验收**:共用目标/可用状态,实际终端右键/快捷键、原生 Find/rename 编辑、busy/closing/失效选区反馈及受控 DPI/zoom 保持;[最终系统审计](../experiments/shell/results/2026-10-24-r1-actions-accepted/validation.json)与[逐项对照](research/r1-action-comparison.md)覆盖 Find 实际 Ctrl+C/V、中文重选、默认 Return 和会话隔离 | 复杂 busy/closing 参考行为仍未知,采用明确项目策略;完整流程与八会话有限资源已由 #18 另验收,真实跨显示器及完整 WindTerm 等价未验收 | R1 | @@ -43,7 +43,7 @@ | 基础窗口操作 | **限定验收**:新建/切换/关闭/重命名、五条分隔线、面板显隐、底部焦点模式 | 完整会话动作/快捷键、命令面板、目标 Focus/SyncInput 行为 | R1 / R4 | | 工作区外观 | **实现中,未验收**:两种逻辑尺寸下部分外框/层级依据直接取证;UI 字体来自独立 OFL 来源,私有加载、实际 GDI face/em/行高与缺失资产失败有自动检查 | 不是像素等价;图标、主题/透明度、多语言及所有状态仍缺。Explorer/Filer/Outline 当前为空区域 | R2 / R3 / R4 | | DPI 与文字缩放 | **限定验收**:每会话 50%–200%,受控 96→144→192→96;[字体/显示合同](milestones/R2-font-display.md)明确 12×26 基准网格。两台真实 96-DPI 显示器间往返两次保持窗口、rename 焦点、隐藏标签和网格,[证据](../experiments/shell/results/2026-10-24-r2-monitor-font/validation.json)明确 `distinct_dpi=false`;v2 会话恢复另覆盖 100%/150%/75% zoom | 不同 DPI 的真实跨屏、实际呈现和真实 IME 跨屏仍未验收;恢复证据为同一 96-DPI 私有 desktop,目标字形/逐字形 fallback 未等价 | R2 | -| 配置与会话持久化 | **限定验收**:有界 v2、原子替换、v1 迁移、空会话与损坏/未知版本保护;[多会话恢复](../experiments/shell/results/2026-10-25-workspace-restore-multi/validation.json)覆盖 shell 类型/目录/标题/zoom/split/窗口,[迁移回退](../experiments/shell/results/2026-10-25-workspace-recovery-guards/validation.json)及[写失败与关闭策略](../experiments/shell/results/2026-10-25-workspace-write-failure/validation.json)均有真实验证。只重建白名单 shell,不恢复原进程/历史;保存失败保留窗口,显式放弃保存才跳过 | 仅当前单工作区、正常窗口和白名单本地会话;偏好、分组/搜索、最大化/多窗口与文字标记恢复归后续。联合基线默认配置出现恢复 stage1 超时,由 [#61](https://github.com/JavaZeroo/termforge/issues/61) 定位;真实 DPI、呈现和 #38 等独立回归不因此视为通过,R2 出口未宣告完成 | R2 / R3 / R4 | +| 配置与会话持久化 | **限定验收**:有界 v2、原子替换、v1 迁移、空会话与损坏/未知版本保护;[多会话恢复](../experiments/shell/results/2026-10-25-workspace-restore-multi/validation.json)覆盖 shell 类型/目录/标题/zoom/split/窗口,[迁移回退](../experiments/shell/results/2026-10-25-workspace-recovery-guards/validation.json)及[写失败与关闭策略](../experiments/shell/results/2026-10-25-workspace-write-failure/validation.json)均有真实验证。[#61 的合法编辑 BS 前缀](../experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json)已在默认/候选 system 限定修正;目录/版本完成按[独立输出与新提示符合同](research/workspace-restore-transport-oracle.md)核对;只重建白名单 shell,不恢复原进程/历史;保存失败保留窗口,显式放弃保存才跳过 | 仅当前单工作区、正常窗口和白名单本地会话;偏好、分组/搜索、最大化/多窗口与文字标记恢复归后续。真实 DPI、呈现和 #38 等独立回归不因此视为通过,R2 出口未宣告完成 | R2 / R3 / R4 | | 文本增强与辅助面板 | **未实现** | 高亮/时间戳/折叠/outline、补全与 Fish 增强、命令历史、Sender/QuickBar、Free Type Mode(输入模式,行为待取证)、vim 本地/远程键位、锁屏、Sysmon | R4 | ## 远程、文件与协议 diff --git a/docs/TRACKING.md b/docs/TRACKING.md index 12e213ee..7b74f909 100644 --- a/docs/TRACKING.md +++ b/docs/TRACKING.md @@ -35,7 +35,7 @@ | 工作 | 责任任务 | 完成条件的归属 | |---|---|---| | 持续输出中的 Ctrl-C 历史失败 | [原始 ETX 回归 #36](https://github.com/JavaZeroo/termforge/issues/36) | 有界定位历史因果与原生交付;后续通过不回填旧因 | -| 工作区恢复与显式后端回归 | [恢复 stage1 #61](https://github.com/JavaZeroo/termforge/issues/61) · [InputPump gate #62](https://github.com/JavaZeroo/termforge/issues/62) · [工作区 stage52 #63](https://github.com/JavaZeroo/termforge/issues/63) | 原完整回归和单项重放均保留;不放宽期限、负载或几何断言 | +| 新增实际回归 | [恢复 oracle 修正 #61](https://github.com/JavaZeroo/termforge/issues/61) · [InputPump gate #62](https://github.com/JavaZeroo/termforge/issues/62) · [工作区 stage52 #63](https://github.com/JavaZeroo/termforge/issues/63) · [hosted reverse #66](https://github.com/JavaZeroo/termforge/issues/66) | #61 有同字节因果与两配置限定修正;其余真实失败继续开放,不放宽期限、负载或几何断言 | | 原 10 秒纯夹具超时 | [IME inventory 定位 #40](https://github.com/JavaZeroo/termforge/issues/40) | 阶段与清理已补齐;下一步是固定源码/宿主的有界原因定位 | | controller 自然退出 | [close 前退出 #44](https://github.com/JavaZeroo/termforge/issues/44) | 保留 send/wait/pre-close 与清理差异;不能以 close 后结果当自然退出 | | 旧 normal-1 未知异常 | [事件转换边界 #57](https://github.com/JavaZeroo/termforge/issues/57) | 独立于已修合法空标题 #45;原事件 kind/载荷未知,不能回填根因 | diff --git a/docs/research/workspace-restore-transport-oracle.md b/docs/research/workspace-restore-transport-oracle.md index 6f17b292..7aeb7cfd 100644 --- a/docs/research/workspace-restore-transport-oracle.md +++ b/docs/research/workspace-restore-transport-oracle.md @@ -8,15 +8,18 @@ 仅剥离控制序列并要求 LF 仍不充分:新的真实默认构建运行中,cmd 的完整标记后直接出现 `ESC [ 6 ; 1 H`、显示光标序列和 `TF>`,没有运输层 CRLF。这份新失败(SHA-256 `339ca19fc01a0a188325fd6b4b594538cfdf6535e9b1e17ca398b6fa4472c8a5`)同样保留;最小反例先失败,再增加受限的 column-one CUP 记录边界。它不假设起始游标行号,也不声称 CUP 移到了新行。 +[#61](https://github.com/JavaZeroo/termforge/issues/61) 的两份默认/system 新失败具有不同原因:PowerShell 命令编辑重绘在实际输出之前带一个裸 BS(`0x08`)。旧 oracle 因它直接拒绝整个运输,即使随后已有真正 CRLF、完整目录/版本输出和新提示符。用原字节独立重放,两个 PowerShell 条件均为 false,而 cmd/底部条件为 true;仅移除前缀 BS 的诊断输入使旧条件转为 true,证实拒绝分支。修正不模拟退格或删除回显,而是把该前缀记录标记为不可信,直到真正 LF 才允许后续独立输出;[限定复验](../../experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json)保留原始失败及各配置身份。这是测试完成端点修正,不是产品输出丢失或恢复功能缺陷的修复。 + ## 限定 oracle 合同 - 独立扫描 7-bit ESC 形式的完整 OSC(BEL 或 ST 终止)和 CSI。OSC 内容不参与正文匹配,UTF-8 的所有高位正文 bytes 原样保留,包括 `目` 中的 `0x9b`。 - 标记必须匹配完整正文记录,前后不可附加命令、其它正文或任意子串。LF 结束记录;显式正行号、第 1 列 CUP/HVP 仅可结束完整标记或提示符,不能清除命令前缀或拼接分散的标记片段。其它非空内容遇 CUP 保持未知,直到真实 LF;SGR 和其它 CSI 不会凭空结束标记。 +- 在首次匹配输出之前,命令编辑中的 BS 只会污染当前记录;CUP 不能解除该污染,也不把退格两侧文字拼起来。真实 LF 后可验证新的完整记录。已经匹配输出后的 BS 仍拒绝,不能借编辑过的提示符证明命令完成;其它裸控制字符的拒绝保持不变。 - 该记录之后必须出现独立的 `TF>` 或 `TF> ` 记录,才证明 shell 返回;输入命令回显、标题内标记、旧提示符和输出行前缀都不满足条件。 - 完整 CSI 仅按语法封装剥离,不解释或验收其执行语义;其中受限 column-one CUP 的记录边界另按上条处理。不完整或格式错误的封装、未知 ESC 引导形式、DCS 等其它控制字符串保持未证明状态。这只覆盖固定持久化命令已输出和返回提示符的运输证据,不扩展为第二个通用 VT 核心;产品屏幕及恢复断言仍独立保留。 - 保留原有 8192-byte 观测上限、30 秒总期限、真实目录/版本、返回提示符、恢复成员/标题/缩放/分屏/几何及尺寸检查和真实 Job/desktop 回收断言。 -`transport_completion_test` 独立覆盖标题重复遮蔽、BEL/ST、SGR、CUP、UTF-8 以及命令回显、OSC/DCS payload、不完整控制、缺失或旧提示符等反例。它修正的是测试 oracle,不构成产品恢复故障修复或完整 R2 验收。 +`transport_completion_test` 独立覆盖标题重复遮蔽、BEL/ST、SGR、CUP、UTF-8、命令编辑前缀 BS,以及命令回显、BS 拼接/提示符编辑、OSC/DCS payload、不完整控制、缺失或旧提示符等反例。它修正的是测试 oracle,不构成产品恢复故障修复或完整 R2 验收。 ```powershell ctest --test-dir build/default -C Release -R '^transport_completion$' --output-on-failure @@ -27,4 +30,4 @@ python -m unittest experiments.shell.test_shell.ShellTests.test_workspace_v2_mul 候选构建使用相同测试命令和原场景两轮保存/新进程恢复,将 `TERMFORGE_BIN_DIR` 指向显式候选构建。两个构建使用私有非输入 desktop 和自有 Job;无须接触用户剪贴板或输入法。 -本地最终验证:默认与候选 Release 构建均通过独立 helper 回归和上述原测试方法;每种构建完成两轮保存、两轮新进程恢复和两轮超宽窗口裁剪恢复。原始两份失败字节分别经 C++ helper 重放,四个条件均成立。[公开复核摘要](../../experiments/shell/results/2026-10-26-workspace-transport-oracle/validation.json)记录源文件及输入身份、旧谓词、反例和 fresh 样本哈希;原始运输数据保留本地。此处不代替合并后 CI 或完整候选回归。 +此前固定源码的[公开复核摘要](../../experiments/shell/results/2026-10-26-workspace-transport-oracle/validation.json)保留 OSC/CUP 原失败、当时默认/候选的两轮保存/恢复/钳制结果,不回写为 #61 通过。新 BS 反例、默认/候选各配置的实际结果以 [#61 复验身份](../../experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json)为准。原始运输数据保留本地;局部方法通过不代替合并后 CI 或完整候选回归。 diff --git a/experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json b/experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json new file mode 100644 index 00000000..de680881 --- /dev/null +++ b/experiments/shell/results/2026-10-26-workspace-restore-bs/validation.json @@ -0,0 +1,623 @@ +{ + "schema": "termforge-workspace-restore-bs-v1", + "date": "2026-10-26", + "scope": "R61 test completion oracle correction only; no product restore behavior change", + "implementation_source_commit": "3775a8e6a93fb1e27e8df5b2055d4744d304aaa6", + "base_commit": "3d6d44d3b3e9a9a3e41da98e8711e9f19a211fd8", + "original_failure_source_commit": "3e5b96d0e0854b691b6dd41821ab10b6124b83eb", + "original_default_shell_sha256": "3807ed087dba1269c6a28e832dfbdfd50d9a5e5907645b2d6852d1b1bd509eeb", + "platform": "Windows-11-10.0.26200-SP0", + "source_git_blob_sha1": { + "CMakeLists.txt": "e864d4223fd1ef544045933c13c81f1e57faf822", + "experiments/shell/transport_completion.hpp": "cc56ce3abfe642aae66f5b3c21863a8c3901f75a", + "experiments/shell/transport_completion_test.cpp": "1707f4d42298ac8cf6d213bad582a5bee4e7da68", + "experiments/shell/workspace_persistence_scenario.hpp": "f7f73dbd771f559384d9b4e569d815d2ef0792da", + "experiments/shell/test_shell.py": "09782b1cae5afc938a67a6fbb783e6a103f0333f", + "experiments/shell/run.py": "271d8ac7b937027dd668dee0b3c3c66fc572aec5", + "experiments/shell/workspace.hpp": "f2411d64efac1d53fbdcbeafeb2f8df2e640c0b0", + "experiments/shell/shell.cpp": "b3681dd0a69dc013783c3d5872b4b48847e7d32e", + "experiments/shell/size_client.cpp": "251220197098b1b654b7554b0a727e4a2e85d7a9", + "experiments/shell/shell_launch.cpp": "b7414df96a8bb0c83701860410fc030bff7831b3", + "experiments/shell/shell_launch.hpp": "8a72ad1e23b3889740026db56e0b954ed1385542", + "experiments/core/candidate_terminal.cpp": "cc12246ee7e94382bf95835cd45e6ef6b8949a19", + "experiments/core/candidate_terminal.hpp": "55de3176ec220b2e404cc7e108f6d0063af3c004", + "experiments/candidates/alacritty/Cargo.lock": "6501189a98bfe0610ebcb5e3c31d3abc2ae3f132" + }, + "source_raw_sha256": { + "CMakeLists.txt": "f03543ecc465967750638fa51c84a4611c8415a7af362f094aa872259641d329", + "experiments/shell/transport_completion.hpp": "47f85232785ccade107123342019a81f013991e33cacffad7616ce8334fe69ab", + "experiments/shell/transport_completion_test.cpp": "1bad42507e43f3165bff0254822104ca29e15ac5ce476f8b7c54b3f325772af1", + "experiments/shell/workspace_persistence_scenario.hpp": "7f70d755549718d0fddb94b43cce12f3955727fcc51599a8bb124bf3e59ec263", + "experiments/shell/test_shell.py": "897971781f4e8c97fc601be3ebf41253eaaaa6125f2aac0d497c433cb9c2ffc1", + "experiments/shell/run.py": "d77454ce626be025e088e6adea9d5f12150c039296397161b2bb8e00e08c1d66", + "experiments/shell/workspace.hpp": "39fe452b5b5c47e863e50f29a6ca4c4dcc752a92417f4a58ac7ea786752d31de", + "experiments/shell/shell.cpp": "49703b00131d88b491f58287f43daa0a6f12fbf5b36a19a097d901af7d62ce44", + "experiments/shell/size_client.cpp": "cb11661bec9551053eec0dbb474bce2ac899a06ee2e50830a370af4816df39f4", + "experiments/shell/shell_launch.cpp": "03fac4122d8151539adf9e88c273e9fbaf632f2e80ba10ea77c3873f588db052", + "experiments/shell/shell_launch.hpp": "921724b7101c5146637a49b942c834d2860d0d426105c117354ddd8680ef50eb", + "experiments/core/candidate_terminal.cpp": "0d0eaabe5ef896aa7b263c8151622f579f37c421bf9473fc3d69ada010a64e61", + "experiments/core/candidate_terminal.hpp": "328411c1af6ddb509623ded8fe9c814cb6ac56cd502a2952e8cedd0a3bb2d997", + "experiments/candidates/alacritty/Cargo.lock": "896c6e60909d55d61fb26d277b1a98daf9245e8aee811caff37fc3e9d06ca627" + }, + "preserved_contract": { + "observation_cap_bytes": 8192, + "native_deadline_seconds": 30, + "outer_deadline_seconds": 40, + "real_commands_directories_versions": true, + "unicode_bytes_preserved": true, + "layout_geometry_resize_assertions": true, + "exact_independent_output_and_fresh_prompt": true, + "owned_job_and_desktop": true + }, + "causal_observation": { + "raw_control_byte": 8, + "location": "PowerShell command-editor record before real output", + "original_power_shell_predicates": [ + false, + false + ], + "corrected_predicates": [ + true, + true + ], + "diagnostic_old_predicates_after_removing_only_bs": [ + true, + true + ], + "removal_is_diagnostic_only": "The product and accepted inputs are unchanged; original raw bytes use the corrected oracle.", + "policy": "Taint the current pre-output record until a real LF. Never edit/concatenate its text; reject BS after output has matched." + }, + "original_failures": [ + { + "id": "clamped-1", + "original_exit_code": 2, + "original_stage": 1, + "outer_timed_out": false, + "original_job_after": { + "active": 0, + "total": 11, + "terminated": 0 + }, + "original_job_after_cleanup": { + "active": 0, + "total": 11, + "terminated": 0 + }, + "desktop_gone": true, + "private_files_sha256": { + "sample.json": "a66a8935c9dbd41ebf1703ea75fbc4720d5f1ff05565ee9873fb823cb1190c92", + "persistence-failure.json": "e19f964e2c540902af1885644607a8bda12594fe242a2868e224d6dfca7f3f25", + "workspace.layout": "c1e846eef56e4d4428844a186aa623bb4c3e61ac4f2047824b91dd6fe0d81a4c" + }, + "original_predicate_replay": [ + { + "case": "bottom", + "raw_bytes": 62, + "bs_count": 0, + "raw_sha256": "f950c09bbb93743570ae8f3fe9b26340784f7c7265583d622b819f9b0666d351", + "old": true, + "new": true, + "old_without_bs": true + }, + { + "case": "cmd-dir", + "raw_bytes": 96, + "bs_count": 0, + "raw_sha256": "be45faaf916b878e1887b4f7de0a3584ace8d3d719d7264d31c74250c35c0424", + "old": true, + "new": true, + "old_without_bs": true + }, + { + "case": "powershell-dir", + "raw_bytes": 941, + "bs_count": 1, + "raw_sha256": "4718a0c45843985139806efeda9eeb8d2137d6d60b8377d1a0e2d7bc0ca3c6b5", + "old": false, + "new": true, + "old_without_bs": true + }, + { + "case": "powershell-major", + "raw_bytes": 941, + "bs_count": 1, + "raw_sha256": "4718a0c45843985139806efeda9eeb8d2137d6d60b8377d1a0e2d7bc0ca3c6b5", + "old": false, + "new": true, + "old_without_bs": true + } + ] + }, + { + "id": "ordinary-restore-0", + "original_exit_code": 2, + "original_stage": 1, + "outer_timed_out": false, + "original_job_after": { + "active": 0, + "total": 11, + "terminated": 0 + }, + "original_job_after_cleanup": { + "active": 0, + "total": 11, + "terminated": 0 + }, + "desktop_gone": true, + "private_files_sha256": { + "sample.json": "ac100d816700411369eab2685c9575fe4199694aa32f29b97211e76b78eaf43b", + "persistence-failure.json": "1ddf4b253f3370f3becb92c95ba1003207a943bd350e50af30259bba14d448b1", + "workspace.layout": "8f7b7be7afcb15bdf7cd83f3707de6ed2ac8eadb350081ce11f9e026e021429e" + }, + "original_predicate_replay": [ + { + "case": "bottom", + "raw_bytes": 168, + "bs_count": 0, + "raw_sha256": "bafd1b1ab688a8c4365363f5565b35cffcced3fd6721d72517379cd8d3f751ee", + "old": true, + "new": true, + "old_without_bs": true + }, + { + "case": "cmd-dir", + "raw_bytes": 214, + "bs_count": 0, + "raw_sha256": "af4da726beb8542ab909ff57454500ddb26d3b3267cedf02c1ac1f6e4e79bf25", + "old": true, + "new": true, + "old_without_bs": true + }, + { + "case": "powershell-dir", + "raw_bytes": 409, + "bs_count": 1, + "raw_sha256": "4a2584d43a0537b420bf7e56f29ed6f3a3941659d6176ade88a0aed3f6910742", + "old": false, + "new": true, + "old_without_bs": true + }, + { + "case": "powershell-major", + "raw_bytes": 409, + "bs_count": 1, + "raw_sha256": "4a2584d43a0537b420bf7e56f29ed6f3a3941659d6176ade88a0aed3f6910742", + "old": false, + "new": true, + "old_without_bs": true + } + ] + } + ], + "pure_regression": { + "assertions": 41, + "positive": 8, + "negative": 33, + "new_positive": 2, + "new_negative": 7, + "original_32_assertions_preserved": true, + "red_test_exit_code": 1, + "red_test_exe_sha256": "7e3b9696b18273a76c48c70da9fac18ecb96fe077ddaeae633671a3509b5f755", + "failure": "command redraw BS before a fresh literal output line does not poison later records" + }, + "actual_configurations": [ + { + "configuration": "default-system", + "backend": "system", + "candidate_version": null, + "unittest": "experiments.shell.test_shell.ShellTests.test_workspace_v2_multiple_shells_split_and_geometry_restore", + "original_method_exit_code": 0, + "save_rounds": 2, + "ordinary_restore_rounds": 2, + "clamped_restore_rounds": 2, + "binaries_sha256": { + "termforge_shell.exe": "ffef472a0012a43c21fe128072d2dd81dcfda8893db619d9170ce48d530de055", + "shell_test_launcher.exe": "972ab25b8f5b06c918fed7e37774aa417aac9677a974bfd0d58537e2d71c6bd1", + "shell_size_client.exe": "51870fae82777c8d3d0a65bbb33cdd594893dc9ee7be730a2df43355671a5d85", + "transport_completion_test.exe": "e713b8dc2cf37b797b037e1a299b1ed23e15599f9b7d0e3156ce05767f98974e" + }, + "transport_completion_ctest": "passed", + "samples": [ + { + "case": "workspace-v2-clamped-0", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "5b48667bda036dc8f6385ddd619b6d6de43a5c545e39f5e3ee93ab2e0c011ecd", + "workspace.layout": "b26c65c5de416d70549c3f262cc5fe00fa299ad80a74005aff5e78ab045924cf", + "restore-geometry.json": "e1b9fdfa4c0be1cf1901dfc4678c2952f5be23b95ca1d2a434c92b17f5069ce0" + } + }, + { + "case": "workspace-v2-clamped-1", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "d79bbf3f82aa499d9add0ed00e7d428931b380b99466a4f745602cb896c33d7b", + "workspace.layout": "9743e97ffdc9a0cc64f966f480c4b471af92569521d653d36b82e41db5b8502b", + "restore-geometry.json": "e1b9fdfa4c0be1cf1901dfc4678c2952f5be23b95ca1d2a434c92b17f5069ce0" + } + }, + { + "case": "workspace-v2-restore-0", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "1ab571ddc093f89f800686ba9eaa85a2b7983bc5a70f156454c753c406083078", + "workspace.layout": "b299cd88e6ec1e9da762800e78fca546738cf79baedf5fcb40901ff3bdaf8250", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-restore-1", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "39242b3b65039a633d2474ebb22de41b7017bc3e1feae69db6fcf0498cb51fde", + "workspace.layout": "d057f9693c2951a4bf3a717a53e7659bbcb96bde73d44f5673e60cce2ddd4124", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-save-0", + "mode": "ui-workspace-save", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-save", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "d3804ae71053e3e56490c15f28001022e61d265aaa6915e4fe8e2fa5cd955540", + "workspace.layout": "b299cd88e6ec1e9da762800e78fca546738cf79baedf5fcb40901ff3bdaf8250", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-save-1", + "mode": "ui-workspace-save", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-save", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "ebccb03f4153098a861fea9af529a4a02621e0b30f57fea018709a020a5f3597", + "workspace.layout": "d057f9693c2951a4bf3a717a53e7659bbcb96bde73d44f5673e60cce2ddd4124", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + } + ] + }, + { + "configuration": "candidate-4-system", + "backend": "system", + "candidate_version": "0.26.1-dev+termforge.4", + "unittest": "experiments.shell.test_shell.ShellTests.test_workspace_v2_multiple_shells_split_and_geometry_restore", + "original_method_exit_code": 0, + "save_rounds": 2, + "ordinary_restore_rounds": 2, + "clamped_restore_rounds": 2, + "binaries_sha256": { + "termforge_shell.exe": "223976126a04d50cad4f36ab69460bada9bad0827f627757dbd0809c78dc0a1a", + "shell_test_launcher.exe": "31385f55ad58f4f06d612585aef10814a9b81c21965fafad1017b86f715890f2", + "shell_size_client.exe": "2f7534c90a3df0ea37f871a6bc98e2bf1ddc3977259981103a66c2eb5d0d30e8", + "transport_completion_test.exe": "0ca13d0796bd615250d4e72bc4b0096661d5c6464e23174ad26572d79eff49dc" + }, + "transport_completion_ctest": "passed", + "samples": [ + { + "case": "workspace-v2-clamped-0", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "a96b3f340137d08b4d8dcd49888d97f95ccc1a4b699649cefb2aa2cd865873b7", + "workspace.layout": "1fe13105e3d2072a5a839e0c255c7371eb477bb48a2dca1efe73a733b883e1dd", + "restore-geometry.json": "e1b9fdfa4c0be1cf1901dfc4678c2952f5be23b95ca1d2a434c92b17f5069ce0" + } + }, + { + "case": "workspace-v2-clamped-1", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "924eb800ab1b4c13aad787be0c75b2f599a8251b04bf2d1bef60916891bdc328", + "workspace.layout": "7345d3b657eb0ec7cc10a46c591a9466f153edbba30f63737deb088a6f1e0492", + "restore-geometry.json": "e1b9fdfa4c0be1cf1901dfc4678c2952f5be23b95ca1d2a434c92b17f5069ce0" + } + }, + { + "case": "workspace-v2-restore-0", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "dd318d72728496378d1689ef3059e1d175aed713a239873cdfc9b37383a0f0c9", + "workspace.layout": "2ee1643002d4b3a2f555e23f7b1508d618a5dfe119ee3da4101f4f65ed3ad27d", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-restore-1", + "mode": "ui-workspace-restore", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-restore", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "8c2e130cbee17ef6fde7debc881e9ccd9271d7cf25781bde11acdb9b3c504230", + "workspace.layout": "b46fda1fadf648e40302bfa41142be58d780eb01d1fa5bed9ff5acfcf2702b16", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-save-0", + "mode": "ui-workspace-save", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-save", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "03530bea16c31b7a1b56d88d17d02a5867cc7ca1b867cd64f011dfb1770d52a4", + "workspace.layout": "2ee1643002d4b3a2f555e23f7b1508d618a5dfe119ee3da4101f4f65ed3ad27d", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + }, + { + "case": "workspace-v2-save-1", + "mode": "ui-workspace-save", + "status": "passed", + "exit_code": 0, + "outer_timed_out": false, + "result": { + "valid": true, + "mode": "ui-workspace-save", + "sessions_closed": 3, + "active_children_after": 0, + "settings_save_failed": false, + "presented_done": null + }, + "job_after": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "job_after_cleanup": { + "active": 0, + "total": 14, + "terminated": 0 + }, + "desktop_gone": true, + "private_sha256": { + "sample.json": "207bbb2cc556e8299c8f8bc11a8b37110f44becf85123c14660af19bf7a5f38c", + "workspace.layout": "b46fda1fadf648e40302bfa41142be58d780eb01d1fa5bed9ff5acfcf2702b16", + "restore-geometry.json": "5cc777f65d580599f8bda2d3e7e3d246b63aba19dba55a27e65d358709402638" + } + } + ] + } + ], + "limitations": [ + "This fixes the independent test oracle, not lost output or product restoration logic.", + "The two original red samples remain failed; raw replays explain their predicate rejection and do not rewrite original process outcomes.", + "The fresh default/system and candidate/system methods are separate configurations; no result is borrowed by the experimental official backend.", + "The full R1/R2 workflows, actual presentation, real different DPI, performance and WindTerm parity are not accepted by this local method.", + "Raw transports, full screens, directories, desktop identities, layouts and frame artifacts stay private; only synthetic verdicts and hashes are published.", + "This record does not claim full local CTest or final GitHub CI success." + ] +} diff --git a/experiments/shell/transport_completion.hpp b/experiments/shell/transport_completion.hpp index 81541ce3..cc56ce3a 100644 --- a/experiments/shell/transport_completion.hpp +++ b/experiments/shell/transport_completion.hpp @@ -10,8 +10,10 @@ namespace tf_shell_test { // column-one CUP. This is a record boundary, without assuming the initial cursor // row or claiming that the marker still occupies a separate visible screen row. // Complete CSI envelopes are omitted without interpreting their semantics. -// Unknown ESC forms, malformed/incomplete envelopes and other control strings -// remain unproven. ShellWindow retains its independent 8192-byte observation cap. +// A command-editor BS before output invalidates that record until a real LF; +// it cannot erase an echo prefix or splice a marker. Unknown ESC forms, +// malformed/incomplete envelopes and other controls remain unproven. +// ShellWindow retains its independent 8192-byte observation cap. inline bool transport_output_completed(std::string_view raw, std::string_view marker) { if(marker.empty()) return false; for(const unsigned char byte:marker) if(byte<0x20 || byte==0x7f) return false; @@ -76,6 +78,12 @@ inline bool transport_output_completed(std::string_view raw, std::string_view ma if(i==raw.size() || raw[i]!='\n') return false; } else if(byte=='\n') { end_line(); + } else if(byte=='\b') { + // PSReadLine redraw can precede the command's real output. Do not + // interpret its editing or reuse any text from that record. Once + // output has matched, BS cannot manufacture an idle prompt. + if(output_seen) return false; + literal_line=false; } else { if(byte<0x20 || byte==0x7f) return false; line.push_back(static_cast(byte)); diff --git a/experiments/shell/transport_completion_test.cpp b/experiments/shell/transport_completion_test.cpp index d144335e..1707f4d4 100644 --- a/experiments/shell/transport_completion_test.cpp +++ b/experiments/shell/transport_completion_test.cpp @@ -26,6 +26,11 @@ int main() { "TF_RESTORE_BOTTOM\r\n\x1b]0;cmd.exe\a\r\nTF>","TF_RESTORE_BOTTOM"),"echo and output remain distinct"); check(transport_output_completed("for %I in (\"%CD%\") do @echo TF_CMD_DIR=%~nxI\r\n"+ marker+"\x1b[6;1H\x1b[?25hTF>",marker),"ConPTY column-one CUP frames an exact output line"); + check(transport_output_completed("\x1b[?25l\x1b[93mW\x1b[m\x1b[1;6H\x1b[?25h" + "\x1b[93m\bWrite-Outp\x1b[m\x1b[1;15H\x1b[?25h\r\n"+ + line+"TF>",marker),"command redraw BS before a fresh literal output line does not poison later records"); + check(transport_output_completed("echo old\b\r\n"+line+"TF_PS_MAJOR=5\r\nTF> ",marker), + "fresh output remains independent of a prior edited command record"); check(!transport_output_completed("TF>echo "+line+"TF>",marker),"command echo is not independent output"); check(!transport_output_completed(title+"\a\r\nTF>",marker),"OSC BEL payload is not output"); @@ -56,6 +61,14 @@ int main() { check(!transport_output_completed(line+"TF>\x1bPunknown\x1b\\",marker),"unsupported control strings remain unknown"); check(!transport_output_completed(line+"TF>",""),"empty marker rejected"); check(!transport_output_completed("TF>\r\nTF>","TF>\r\nTF>"),"marker cannot contain controls"); + check(!transport_output_completed("echo \b"+line+"TF>",marker),"BS cannot discard an echo prefix"); + check(!transport_output_completed("\b"+marker+"\x1b[6;1HTF>",marker),"CUP cannot reset a BS-tainted record"); + check(!transport_output_completed("TF_CMD_\bDIR=restore-cmd \xe7\x9b\xae\xe5\xbd\x95\r\nTF>",marker), + "BS-separated fragments cannot assemble an exact marker"); + check(!transport_output_completed(marker+"\b\r\nTF>",marker),"editing the marker does not prove literal output"); + check(!transport_output_completed(line+"\bTF>",marker),"BS after a marker cannot manufacture prompt"); + check(!transport_output_completed(line+"TF>\b",marker),"trailing prompt BS remains unknown"); + check(!transport_output_completed("\t\r\n"+line+"TF>",marker),"other bare controls remain rejected"); std::cout<<"transport completion checks passed\n"; } catch(const std::exception& error) { std::cerr<