diff --git a/.github/workflows/core-candidate.yml b/.github/workflows/core-candidate.yml index 09c15bac..96f18d02 100644 --- a/.github/workflows/core-candidate.yml +++ b/.github/workflows/core-candidate.yml @@ -84,7 +84,7 @@ jobs: - name: Build native measurement run: cmake --build build --config Release --target core_measure - name: Build and test candidate C++ adapter - run: cmake --build build --config Release --target candidate_core_test && ctest --test-dir build -C Release -R candidate_core --output-on-failure + run: cmake --build build --config Release --target candidate_core_test workflow_checkpoint_test && ctest --test-dir build -C Release -R "^(candidate_core|workflow_checkpoint)$" --no-tests=error --output-on-failure - name: Verify resize trace and actual replay input rejection run: cmake --build build --config Release --target candidate_resize_replay && ctest --test-dir build -C Release -R "^(resize_trace|candidate_resize_replay)$" --no-tests=error --output-on-failure - name: Build and test candidate shell on Windows diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e7b3df0..846f1e13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,6 +303,13 @@ if(BUILD_TESTING) add_executable(transport_completion_test experiments/shell/transport_completion_test.cpp) termforge_warnings(transport_completion_test) add_test(NAME transport_completion COMMAND transport_completion_test) + add_executable(workflow_checkpoint_test experiments/shell/workflow_checkpoint_test.cpp) + target_link_libraries(workflow_checkpoint_test PRIVATE termforge_core) + termforge_warnings(workflow_checkpoint_test) + if(TERMFORGE_BUILD_CANDIDATE_ADAPTER) + termforge_candidate_shell(workflow_checkpoint_test) + endif() + add_test(NAME workflow_checkpoint COMMAND workflow_checkpoint_test) add_executable(scenario_tick_gate_test experiments/shell/scenario_tick_gate_test.cpp) termforge_warnings(scenario_tick_gate_test) add_test(NAME scenario_tick_gate COMMAND scenario_tick_gate_test) diff --git a/docs/EVIDENCE.md b/docs/EVIDENCE.md index 095561a3..c2aa5356 100644 --- a/docs/EVIDENCE.md +++ b/docs/EVIDENCE.md @@ -56,7 +56,8 @@ R1 固定总出口的源码为 `ce9b24d`,早期 `2dc8d6d` / `9b16acc` 仍分 | 实验 UI 未完成粘贴(#53) | [机制与边界](research/ui-paste-fence.md)、[旧固定记录](../experiments/shell/results/2026-10-26-ui-paste-fence/validation.json)、[组合修复](research/resize-replay-cost.md)、[最终同源码记录](../experiments/shell/results/2026-10-26-replay-cost-and-close/validation.json) | `eae4b6a` 单窗口两轮、真实EOF/无GO负例及两轮原八会话data-only通过;行键复用及关闭期间不回复分别有诊断/因果回归,原completed/stage45/退出错误保留。完整CTest54/56仍有Vim59、resize43、main7/ui52与shell180秒期限;未计真实CF/IME、完整后端或性能通过。 | | Native Vim 捕获目录隔离 | [原因与复验](research/vim-fixture-isolation.md)、[固定原件审计](../experiments/conpty/results/2026-10-26-vim-fixture-isolation/validation.json) | 实验分支同旧 EXE 的兄弟运行一绿一红;修正新目录隔离后完整模块9/9。仅闭合 #52 native 夹具原因;GUI stage59 仍开放,未知与受扰动探针分列。 | | 原生 viewport resize 与默认策略隔离(#51) | [原因与复现](research/native-viewport-resize.md)、[固定验证及六组合成原件](../experiments/conpty/results/2026-10-27-native-viewport-resize/validation.json) | 原 `.4` GUI stage43 与独立 native/replay 红;`.5` 原 resize/Unicode、历史/空格复制、六组 native 对照通过。默认策略填充误判经实际红绿修复;最终完整回归 54/56、12 项内部 skip,#52/#88 及此前 #87 的失败独立保留,不计完整后端或 WindTerm 等价。 | -| 窄列命令回显与恢复完成(#88) | [独立判据合同](research/workspace-restore-transport-oracle.md)、[原件哈希/红绿/正式两轮验证](../experiments/shell/results/2026-10-27-workspace-echo-cr/validation.json) | 原 262 B cmd 运输中的回显裸 CR 使完成判据提前拒绝;修正仅使该记录保持不可信至真实 LF。原两轮保存/普通恢复/钳制及完整 shell 通过;官方完整回归 55/56、8 项内部 skip,#52 仍红,#63/#87 旧因未闭合。构建重叠结果明确排除。 | +| 窄列命令回显与恢复完成(#88) | [独立判据合同](research/workspace-restore-transport-oracle.md)、[原件哈希/红绿/正式两轮验证](../experiments/shell/results/2026-10-27-workspace-echo-cr/validation.json) | 原 262 B cmd 运输中的回显裸 CR 使完成判据提前拒绝;修正仅使该记录保持不可信至真实 LF。原两轮保存/普通恢复/钳制及完整 shell 通过;官方完整回归 55/56、8 项内部 skip,#52 当时仍红,#63/#87 旧因未闭合(#87 后续见下一行)。构建重叠结果明确排除。 | +| 替换会话完整记录检查点(#87) | [机制、因果与边界](research/workflow-record-checkpoint.md)、[固定源码及原两轮验证](../experiments/shell/results/2026-10-27-workflow-record-checkpoint/validation.json) | 旧判据在真实消费半行时采样而红,同 session 后续字节补齐;被动等待方案因输出结束而红。默认关闭的有界 LF 检查点受控通过,最终 c662011 两轮原八会话及关闭 guard 通过;完整官方 56/57、8 项内部 skip,Vim59 仍红。历史两份 native 后续缺口、诊断 trace 截断及未复现分别保留。 | | 工作区几何与交互的固定记录 | [#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、期限、负载及恢复断言。仅修夹具,不宣称修复产品恢复。 | diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 3cc69710..d7901714 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -14,7 +14,7 @@ |---|---|---| | 默认 main:自研核心 + 系统 ConPTY | 默认构建与已发布 Alpha 的基础路线,不需要 Rust | 已有 R1 限定验收;VT/Unicode 能力仍为有限子集,不能引用候选核心的字素或 TUI 结果 | | 显式候选 main:`termforge.4` + 系统 ConPTY | `TERMFORGE_BUILD_CANDIDATE_ADAPTER=ON`;按 [ADR-0003](adr/0003-core-reference-and-reuse.md)保留为可评估实验,正式核心选型待用户确认 | [固定来源](../experiments/candidates/alacritty/PROVENANCE.md)、[适配边界](../experiments/core/CANDIDATE.md)与 [#39 字素合同](research/streaming-graphemes.md);候选正确性通过不关闭系统 ConPTY 或 R2 父项 | -| 实验分支:`termforge.5` + 固定官方 ConPTY | [PR #50(Draft)](https://github.com/JavaZeroo/termforge/pull/50),`.4` 联合源码 `3e5b96d` 的[真实基线](research/joint-conpty-baseline.md)保留,尚未合并 main 或通过产品整体验收 | [#51 原生 viewport 修复](research/native-viewport-resize.md)保留历史并对齐宿主坐标,原 resize/Unicode 两轮及六组原生样本通过;[#88 回显完成判据](research/workspace-restore-transport-oracle.md)的原两轮保存/恢复/钳制及完整 shell 通过,最终官方完整回归 55/56、8 项内部 skip。[Vim #52](https://github.com/JavaZeroo/termforge/issues/52)仍红,此前[八会话半行 #87](https://github.com/JavaZeroo/termforge/issues/87)和 #63 旧工作区失败原因未闭合。[#53 UI fence](research/ui-paste-fence.md)与[行键复用/关闭回复](research/resize-replay-cost.md)、[#62 worker 夹具](research/sender-fence.md)各自限定通过。终端 margin 与原生状态分列,#47 及完整后端仍开放;旧残尾、超时和组合失败记录保留 | +| 实验分支:`termforge.5` + 固定官方 ConPTY | [PR #50(Draft)](https://github.com/JavaZeroo/termforge/pull/50),`.4` 联合源码 `3e5b96d` 的[真实基线](research/joint-conpty-baseline.md)保留,尚未合并 main 或通过产品整体验收 | [#51 原生 viewport 修复](research/native-viewport-resize.md)的原 resize/Unicode 及原生样本、[#88 回显完成判据](research/workspace-restore-transport-oracle.md)的原保存/恢复/钳制分别通过;[#87 完整记录检查点](research/workflow-record-checkpoint.md)有受控旧红/新绿及最终同源码两轮原八会话 data-only。最新官方完整回归 56/57、8 项内部 skip,[Vim #52](https://github.com/JavaZeroo/termforge/issues/52)仍红,#63 旧工作区失败原因未闭合。[#53 UI fence](research/ui-paste-fence.md)与[行键复用/关闭回复](research/resize-replay-cost.md)、[#62 worker 夹具](research/sender-fence.md)各自限定通过。终端 margin 与原生状态分列,#47 及完整后端仍开放;旧失败和历史运输缺口保留 | 以下能力默认指已合并实现;某个配置的通过结果不能转用于另一个配置。R1 的真实系统 CF/拼音验收属于固定自研核心配置,不外推到候选 `.4` 或实验后端联合配置。 diff --git a/docs/milestones/R2-local-terminal.md b/docs/milestones/R2-local-terminal.md index 9d13791d..52e6ff81 100644 --- a/docs/milestones/R2-local-terminal.md +++ b/docs/milestones/R2-local-terminal.md @@ -44,7 +44,7 @@ flowchart LR ## 下一批的任务边界 -1. **在已冻结基线上逐项闭合后端回归。** #55 已交付含红的真实基线;接续 #52、#63 和 [#87 八会话半行观测](https://github.com/JavaZeroo/termforge/issues/87)的独立失败。#62 的[真实 writer 持块、取消与停止夹具](../research/sender-fence.md)不降低原 4MiB/16KiB/期限,#53 已接入默认关闭的 fence,[单窗口 UI/3MB/768B 与控制故障切片](../research/ui-paste-fence.md)及[同源码两轮原八会话data-only](../research/resize-replay-cost.md)已通过;[#51 原生 viewport 修复](../research/native-viewport-resize.md)的原 resize 与 Unicode、[#88 回显完成判据](../research/workspace-restore-transport-oracle.md)的原保存/恢复/钳制限定回归通过。完整官方回归中的 Vim、此前 #87、历史工作区失败及聚合期限证据分别保留,真实 CF/IME 组合独立验收。各回归可用原冻结输入独立定位,最终修复验收须在联合配置重建;一次未复现不自动构成因果修复。 +1. **在已冻结基线上逐项闭合后端回归。** #55 已交付含红的真实基线;接续 #52、#63 的独立失败。#62 的[真实 writer 持块、取消与停止夹具](../research/sender-fence.md)不降低原 4MiB/16KiB/期限,#53 已接入默认关闭的 fence,[单窗口 UI/3MB/768B 与控制故障切片](../research/ui-paste-fence.md)及[同源码两轮原八会话data-only](../research/resize-replay-cost.md)已通过;[#51 原生 viewport 修复](../research/native-viewport-resize.md)的原 resize 与 Unicode、[#88 回显完成判据](../research/workspace-restore-transport-oracle.md)的原保存/恢复/钳制限定回归通过;[#87 完整记录检查点](../research/workflow-record-checkpoint.md)已具有受控因果红绿及同源码两轮原八会话 data-only。完整官方回归中的 Vim、历史工作区失败及聚合期限证据分别保留,#87 的历史运输缺口不追溯补造,真实 CF/IME 组合独立验收。各回归可用原冻结输入独立定位,最终修复验收须在联合配置重建;一次未复现不自动构成因果修复。 2. **并行处理原版对照、平台语义与性能。** [原生语义处理路径 #56](https://github.com/JavaZeroo/termforge/issues/56)先明确 #41 原合同需要什么、哪些路径可支持;保留必要的上游或独立实现工作,不用“平台不同”豁免缺口。#3/#4 优先建立原版 Windows 本地终端基线;#54 现在冻结重复方法与完成端点并定位吞吐成本,数值预算作为提案交用户确认,不等到 #34 才补性能。 3. **在主线推进独立行为合同。** #29 核对极窄/极短、软硬行、历史/淘汰、选择/Find、连续 resize 和 child ack;#30 核对 IME 组合/提交/取消与输出/resize/切换、应用键盘/粘贴接收、实际复制及同次八会话。已验收的 #39 只提供首批字素基础,最终配置的交叉验收仍需执行。 4. **补终端模式和应用矩阵。** #27 明确 DECOM/DECAWM、pending-wrap、保存恢复、异常序列与 margin×resize;#28 先固定应用版本/入口/动作及协议字节预期,再逐应用完成进入、操作、resize、退出、主屏恢复。Vim 的一个修复不替代完整 TUI 验收。 diff --git a/docs/research/workflow-record-checkpoint.md b/docs/research/workflow-record-checkpoint.md new file mode 100644 index 00000000..61ae64c5 --- /dev/null +++ b/docs/research/workflow-record-checkpoint.md @@ -0,0 +1,45 @@ +# 替换会话的完整记录检查点 + +原八会话 data-only 工作流的 `cycle-eight` 快照必须在替换会话仍持续输出时取得,并满足既有精确文字、身份及回收断言。[#87](https://github.com/JavaZeroo/termforge/issues/87) 修正的是合成测试的消费与采样协调;正常产品输出默认不启用检查点,Python oracle、生产输入和期限保持不变。 + +## 失败及因果证据 + +历史两份失败在 F5 第 127 条记录只得到 12 个点,独立 oracle 要求 35 个点;源已有 16384 B,队列还有 8217 B。原件仍可离线重现 `replacement core payload/role mismatch`。那两次没有保存后续 native 运输,不能追溯宣称已证明其完整性。 + +原 stage35 只要求 source≥4096 B 和任意编号前缀,而消费者一次最多取 8192 B。实际 READY 前缀 25 B 与两个 4096 B 源块可以合并为 8217 B;重分片回放在 8192 B 处产生相同的第 127 条半行,余下 25 B 补齐它。这证明了机制,不等于重现历史 GUI 调度。最初四次有界活体观察均未捕获半行,这些未复现结果单列保留。 + +随后用同一诊断 EXE 做受控旧判据/新判据对照:在第一轮 F5 替换输出前保持消费者,源到达 16384 B 后只放行一次真实 8192 B 消费;保存状态,再继续原工作流。没有改生产字节、4096 B 分块、20 ms 间隔、32000 条/2048000 B、五轮替换、精确 oracle 或期限。 + +- 旧判据在第 117 条中途采样,原 oracle 真红。相同 session 后续实际 39 B 补齐该条,运输前缀保持;独立候选核心回放得到完整原文字。 +- 仅被动等待空行的中间方案仍失败:持续消费跳过边界,直到生产结束才采样,原 oracle 报 `replacement output not active`。这不是可接受修复。 +- 新检查点在实际 LF 后持有快照,通过原 oracle;该检查点源为 241664 B,小于 2048000 B,`fixture_running=true`、`output_done=false`。同 session 的实际后续 41 B 补齐受控半行。两次运行各有自己的真实运输,未声称它们逐字节相同。 + +诊断记录上限为 128 KiB/1024 events。新方案的后续 collected 运输超出字节上限,但所需补齐字节在保存的前缀内;held/checkpoint 未溢出。这里只证明该前缀内的补齐,不声称完整后续运输已捕获。受控消费者调度仅用于因果实验,最终原场景回归移除诊断调度和记录。 + +## 最终机制与边界 + +`OutputRecordCheckpoint` 仅由 UI 线程显式启用。已有空行边界时立即保持;否则每次取一个真实队列字节并交给原核心,遇 LF 后保持,最多读取 256 B,并受原场景 70 秒总期限约束。两个替换会话各自就绪后才采集同一检查点,随即释放,恢复原 8192 B 消费块及 65536 B/4 ms tick 总预算。底层队列仍有界。 + +检查点不验证或修补正文:完整的 `BROKEN` 编号行也会就绪,再由原 Python oracle 拒绝。空启动、只有 READY、CR 无 LF、当前半行、只有其他角色等不会被完整记录判据接受。关闭会解除保持,关闭中或无有效 HWND 的对象拒绝重新启用,避免阻碍排空及回收。 + +`workflow_checkpoint_test` 在实际默认核心和候选 native-viewport 核心验证 8192/8217 分片、后续补齐、保持/释放及边界负例;最初红测试和最终测试保留相同触发用例,最终另增边界与状态用例,不能称整个测试源码相同。`runtime_observation` 用自有非输入 desktop 验证默认关闭、显式启用、关闭解除和关闭后拒绝重新启用。普通 cmd 回显不保证运输中有字面 LF,因此一个依赖此假设的临时关闭测试已排除;最终真实关闭测试只宣称关闭时已 armed,未宣称它必为 held。 + +## 正式验证与复现 + +固定源码、EXE/依赖哈希、旧红、新绿、完整回归、内部 skip 和回收结果见[公开白名单记录](../../experiments/shell/results/2026-10-27-workflow-record-checkpoint/validation.json)。原运输、窗口路径和 desktop 身份保留本地。最终原完整八会话测试包含两个新进程、每次五轮替换;它与受控诊断运行分别记录。 + +最终源码 `c662011`、候选 `.5`/固定官方后端 flags24 的完整 CTest 为 **56/57,402.66 秒**,两轮原八会话均通过,Job active=0、desktop 已回收。唯一失败为 Vim stage59,仍由 #52 定位;8 项内部 skip 逐项列出,不算真实 CF/IME 通过。关闭 guard 的实际新用例先红后绿;红例未独立保留 EXE 哈希,该字段明确为 null,源码、相同测试、构建日志和失败日志仍在。更早 `f89b0ac` 的 57/57 只属于关闭 guard 前版本,不能拼入最终源码,也不能解释 Vim 历史失败。候选 CI 另增加该跨平台核心回归;CI 的 system 后端与本机官方配置分列。 + +在依赖和全部 helper 已构建的候选/显式官方配置运行: + +```powershell +ctest --test-dir build/candidate -C Release -R '^(workflow_checkpoint|runtime_observation)$' --output-on-failure +$env:TERMFORGE_BIN_DIR = "$PWD/build/candidate/bin/Release" +$env:TERMFORGE_EXPECT_CONPTY_BACKEND = 'reviewed-side-by-side-1.25.260930003' +$env:TERMFORGE_TEST_ARTIFACTS = "$PWD/local/workflow-record-check-new" +$env:PYTHONUTF8 = '1' +python -m unittest experiments.shell.test_workflow_sessions.WorkflowSessionsTests.test_two_fresh_owned_eight_session_rounds -v +ctest --test-dir build/candidate -C Release --output-on-failure +``` + +完整回归还需固定 Vim 在当前进程 PATH 中。官方后端、system 后端、默认核心和候选核心分别记录身份,不能互相借用结论。data-only 通过不代表真实 CF/IME、实际呈现、性能或完整 R2;#52、#63 及 #47 的其他门槛仍独立处理。 diff --git a/experiments/shell/output_record_checkpoint.hpp b/experiments/shell/output_record_checkpoint.hpp new file mode 100644 index 00000000..d064f7ea --- /dev/null +++ b/experiments/shell/output_record_checkpoint.hpp @@ -0,0 +1,39 @@ +// Explicit synthetic-consumer coordination; disabled during ordinary use. +#pragma once +#include +#include +#include +#include + +namespace tf_shell_test { +class OutputRecordCheckpoint { +public: + using Clock=std::chrono::steady_clock; + static constexpr size_t byte_limit=256; +private: + bool armed_=false,held_=false; + size_t consumed_=0; + Clock::time_point deadline_{}; +public: + void arm(bool at_boundary,Clock::time_point deadline) { + if(armed_) return; + if(deadline<=Clock::now()) throw std::invalid_argument("output checkpoint deadline expired"); + deadline_=deadline;armed_=true;held_=at_boundary;consumed_=0; + } + size_t read_budget(size_t normal) const { + if(!armed_) return normal; + if(Clock::now()>=deadline_) throw std::runtime_error("output checkpoint deadline expired"); + if(!held_ && consumed_>=byte_limit) throw std::runtime_error("output checkpoint record bound exceeded"); + return held_?0:1; + } + void consumed(std::string_view bytes) { + if(!armed_) return; + if(held_ || bytes.size()!=1) throw std::runtime_error("output checkpoint read boundary violated"); + if(++consumed_>byte_limit) throw std::runtime_error("output checkpoint record bound exceeded"); + if(bytes.front()=='\n') held_=true; + } + bool held() const { return held_; } + bool active() const { return armed_; } + void release() noexcept { armed_=held_=false;consumed_=0; } +}; +} // namespace tf_shell_test diff --git a/experiments/shell/results/2026-10-27-workflow-record-checkpoint/validation.json b/experiments/shell/results/2026-10-27-workflow-record-checkpoint/validation.json new file mode 100644 index 00000000..02ba0d93 --- /dev/null +++ b/experiments/shell/results/2026-10-27-workflow-record-checkpoint/validation.json @@ -0,0 +1,1032 @@ +{ + "schema": 1, + "issue": 87, + "source_commit": "c662011616a0498e0fbd6af8019f5f3cc03fdcaa", + "base_commit": "6b365b3cae9fbbfbb25f19a716d4fe85bcb0c04b", + "configuration": "candidate termforge.5 / explicit official ConPTY 1.25.260930003 / flags24", + "windows": "10.0.26200", + "identity": { + "source_commit": "c662011616a0498e0fbd6af8019f5f3cc03fdcaa", + "after_successful_build_utc": "2026-10-26T22:29:38.710131+00:00", + "diagnostic_instrumentation": false, + "files_sha256": { + "CMakeLists.txt": "8a0e3f86981e91fd6482b590669ccc4adb571a359bd265b3dc98aee59a7b92f2", + "experiments/shell/shell.cpp": "b4f6fe98812d1d23fa5f5bbcc7376437404af6cdb67cd6cdcdf448b57cb31fe9", + "experiments/shell/output_record_checkpoint.hpp": "b4925aadf2e29d8bf44c1d0101143ead51dadf58fd1bb681ad4af8f3c008fa69", + "experiments/shell/workflow_checkpoint.hpp": "34ddd734ff6ab81ae39194d9231a162dbe2069eb2c7bad81a9f15d028be92369", + "experiments/shell/workflow_checkpoint_test.cpp": "f4d670707a251285be37546c7be1c8e6668bf5087414ae7e415cbb5b510bc8af", + "experiments/shell/runtime_observation_test.cpp": "3a262672b6e28d326d1765ba7488940aadd8edaffaee47314ec27368ba56f98b", + "experiments/shell/workspace_workflow_scenario.hpp": "d70ec033c24fc8763996181bb30da7acc7a069cba4f527440fb9356c21fb2d7b", + "experiments/shell/workflow_scenario_state.hpp": "2627160b91fb468616dd3ec1cee289bf021771b63c8708e1989b6a2964995e3f", + "experiments/shell/workflow_fixture.hpp": "332d73699320a6f503fc212417a4d3820b82cc2d3feb3d1fffe3930660d57453", + "experiments/shell/workflow_sessions_checks.py": "b976d3504837eef57ff13319daed254870727741f36f265412a6b14b4cb3faae", + "experiments/shell/test_workflow_sessions.py": "6c968a443c46b30e75a929f880228e6a23bf2b92d9221bbbf18816933babd6e7", + "experiments/shell/run.py": "45976b828eecb316881d698dedaef09ad304f72d991dfa360f1c552bbcbe9f1d", + "build/candidate/bin/Release/termforge_shell.exe": "941ef34db129282a26625d8bad4fd663c4284fe3307802907366299ab04f31c4", + "build/candidate/bin/Release/workflow_checkpoint_test.exe": "056047580e8ddd77224908667297a0b704f97ef57d1eadeb382b52c7c7875b86", + "build/candidate/bin/Release/runtime_observation_test.exe": "0049a0d863724bf8bd181c85868bd68a409eb5e12c755a5538add5f85bf8b0bf", + "build/candidate/bin/Release/shell_text_client.exe": "9350e53de74c9a6b78495a3b59788e3a31e0067a9c5ec1b8685a729ff18244d3", + "build/candidate/bin/Release/shell_test_launcher.exe": "7bd78cffd78ac66d651933c9614d6be937c99baa105ce04c1a533df5785c11e2", + "vim.exe": "2e0fda63a89171adfc46c9417d5105cc39ed778cc69a890b83a4eabc34e7646a" + } + }, + "source_hash_policy": "SHA256 of on-disk compilation inputs; Windows checkout line endings retained.", + "candidate_dependency_sha256": { + "experiments/candidates/alacritty/Cargo.lock": "f7b8906cc63b6a5d66943f15c50a78c51f773face60229fcddf24b2d02911736", + "experiments/candidates/alacritty/target/release/termforge_alacritty_core.lib": "f2461f024d8b3002ca6bb872b409e1dadb34eff6fc92892108cfd18b3aece807" + }, + "backend": { + "version": "1.25.260930003", + "source_commit": "dc8ae365847d41f62d1ebca93815bb00959bc12f", + "package_sha256": "6d6f8b008c655d814c498d8205d4012dcd4d0b6dfc1096461d0a185d1e3caf03", + "files": { + "conpty.dll": "feeef341d891643c62d30b6b07800bc70f0bc148f44cb8c3bee84aa557ae805a", + "OpenConsole.exe": "3d66b23d0a71bb8eed2b77edc8b9df9bf54ce6c8fb74c863a30e760997f80586" + }, + "configured_creation_flags": 24 + }, + "workload": { + "fixture": "r108-console-v1", + "records": 32000, + "source_bytes": 2048000, + "block_bytes": 4096, + "block_delay_ms": 20, + "replacement_cycles": 5, + "scenario_deadline_ms": 70000, + "runner_timeout_seconds": 90, + "ctest_timeout_seconds": 300, + "python_oracle_changed": false + }, + "original_failures": { + "prior_artifact": "experiments/conpty/results/2026-10-27-native-viewport-resize/validation.json", + "product_source": "57f24f5", + "shell_sha256": "4e23e277ea7984d85856b713b3af5b0545d68042c2bbd42a8422f8a6b422c28d", + "mismatches": [ + { + "run": "run-89580b0cae404944b12e03e031dab1bd", + "bad": [ + { + "cycle": 1, + "name": "F5", + "source_bytes": 16384, + "actual": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301............", + "expected": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301...................................", + "exact_prefix": true, + "output": { + "queued_bytes": 8217, + "high_water_bytes": 8217, + "stopped": false + } + }, + { + "cycle": 1, + "name": "F5", + "source_bytes": 16384, + "actual": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301............", + "expected": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301...................................", + "exact_prefix": true, + "output": { + "queued_bytes": 8217, + "high_water_bytes": 8217, + "stopped": false + } + } + ] + }, + { + "run": "run-8abb195e8f824858adbc0149ce1bbca6", + "bad": [ + { + "cycle": 2, + "name": "F5", + "source_bytes": 16384, + "actual": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301............", + "expected": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301...................................", + "exact_prefix": true, + "output": { + "queued_bytes": 8217, + "high_water_bytes": 8217, + "stopped": false + } + }, + { + "cycle": 2, + "name": "F5", + "source_bytes": 16384, + "actual": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301............", + "expected": "TF_R108_F5_00127 \u4e2d\u6587 e\u0301...................................", + "exact_prefix": true, + "output": { + "queued_bytes": 8217, + "high_water_bytes": 8217, + "stopped": false + } + } + ] + }, + { + "run": "run-9266bc270e244e05a0073d86f00ae8ae", + "bad": [] + } + ], + "offline_validator": [ + { + "run": "run-89580b0cae404944b12e03e031dab1bd", + "error": "replacement core payload/role mismatch", + "seconds": 0.6168144999537617 + }, + { + "run": "run-8abb195e8f824858adbc0149ce1bbca6", + "error": "replacement core payload/role mismatch", + "seconds": 0.5770988999865949 + } + ], + "original_native_continuation_available": false + }, + "initial_live_observation": { + "identity": { + "base_commit": "f2ea8d2f5851724bedeb13344ee4681c7b092691", + "diagnostic_only": true, + "build_completed_before_test_utc": "2026-10-26T21:36:40.942995+00:00", + "sha256": { + "experiments/shell/shell.cpp": "1154c7eeaa2ae32c36958ef3f04446778bb59ec3802c3fcfb9508ac9731fe0b4", + "experiments/shell/workspace_workflow_scenario.hpp": "7ec62dda236dbb189a6922d2c7d4ec1ba9eca379b2e76b816ac3d58731c252b5", + "build/candidate/bin/Release/termforge_shell.exe": "74efba3d16080fc74ae2a8793083d3612e445ebb91eab4006ee28d28b07cec0a" + } + }, + "runs": [ + { + "sample_sha256": "37dc6911b059f25c708e2c7beba6d37d9eb2a508e2dbd53e8fa69295f9c56884", + "sessions_sha256": "3846b6d9a1570dafd7cc13f302d6ce6a684765aa62a0685439c32b761b20da12", + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + } + }, + { + "sample_sha256": "f0af6eef429ef1f4f00d606916ce3995cc1dd42004c57fcc79fe4c22b3edaaff", + "sessions_sha256": "6b631bad7a48dd82fa5e50004d5b066db1f6d5af0bb3a193ec286e27093cf245", + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + } + }, + { + "sample_sha256": "a6d5d75585a618cfcd4e53cf034c744533f701689530f2f413844afa146a4640", + "sessions_sha256": "babe9f73ce179f11564225e9b06d9f564d14115f625e474628afc197ce8b7b9b", + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + } + }, + { + "sample_sha256": "ff1b5ecd829ca68959c881806681af160513a17cb6ffeb2d7b09e518d82be1d0", + "sessions_sha256": "1c41e39aa93e60a8c75f7aa88cc5579daea96f8d6f479ca7cbafffa2e0ca8156", + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + } + } + ], + "half_row_captured": false + }, + "repartition": { + "transport_sha256": "f7c5134e5779bd946460d01c0db43b4ee53819261da701b031bb9a17a901b725", + "same_real_bytes": true, + "partition_changed": true, + "gui_timing_reproduced": false, + "remaining_25_bytes_hex": "2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e0d0a", + "partial_dots": 12, + "final_dots": 35, + "prefix_state_offset": 11032, + "final_state_offset": 11057, + "raw_cell_lines": [ + "TF_R108_F5_00127 \u4e2d \u6587 e\u0301............", + "TF_R108_F5_00127 \u4e2d \u6587 e\u0301..................................." + ], + "note": "Screen JSON includes explicit width-0 spacer cells; these are not user text. GUI raw snapshots used canonical project_line." + }, + "core_regression": { + "red_test_source_sha256": "50b4c17d494695b50d65f3f52f0c2dfb458feb1509989579627f52225c2e0aeb", + "red_helper_sha256": "c5efe3d91748921292090f212e32f62a252053bb2ca7d5abe76be4c530e0e8f3", + "red_executable_sha256": "76a92dda8f6bfa025f3d50e1e43d65889c569c83688fc8be0d6b5124e47c546f", + "red_log_sha256": "df03de6cb3deec3b3df505c58e1f89347bddfcb9dda3b960e2fbf50af18438c3", + "red_error": "numbered prefix must not admit an unfinished consumer row", + "both_cores_failed_before": true, + "final_test_passed": true, + "note": "Original 8192/8217 trigger retained. Final test additionally covers active hold/release, readiness negatives and bounds; entire red/final test sources differ." + }, + "rejected_passive_wait": { + "identity": { + "source_commit": "51ad19dc5b4f829046b41f360b1428aa4ee0d37e", + "diagnostic_only": true, + "build_completed_before_test_utc": "2026-10-26T21:57:55.185445+00:00", + "files_sha256": { + "experiments/shell/shell.cpp": "b0c769769614fb56d91528a5cfdf96938f2557d4c1c591e43d971a151666e836", + "experiments/shell/workspace_workflow_scenario.hpp": "ecfa477b8b47e68f3c14782a885cb9f3ec217cae91bf33b0cca6a1d405674503", + "build/candidate/bin/Release/termforge_shell.exe": "6792f6eacb3100f2da98f8a6c29e9e2125becf29d0760a9e32a815ea02f89a8e" + } + }, + "results": [ + { + "policy": "old", + "error": "replacement core payload/role mismatch", + "checked": null, + "exit_code": 0, + "timed_out": false, + "desktop_gone": true, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + } + }, + { + "policy": "fixed", + "error": "replacement output not active", + "checked": null, + "exit_code": 0, + "timed_out": false, + "desktop_gone": true, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + } + } + ], + "log_sha256": "5a0f3c61bcdf00977bd26332e17758955282a96b34d6f4dd31145e14ba1d0102", + "error": "replacement output not active", + "usable_for_acceptance": false + }, + "controlled_gui": { + "identity": { + "source_commit": "51ad19dc5b4f829046b41f360b1428aa4ee0d37e", + "diagnostic_only": true, + "uncommitted_record_checkpoint": true, + "build_completed_before_test_utc": "2026-10-26T22:14:07.691106+00:00", + "files_sha256": { + "experiments/shell/shell.cpp": "eb8cd8a7f7548756fd529b8f6443b69151f605837760ca23e3d99198fe467438", + "experiments/shell/workspace_workflow_scenario.hpp": "757693dae0eff8eb093a16360136893bdfdce3b533d672eedc1de8f770ca2e7d", + "experiments/shell/output_record_checkpoint.hpp": "b4925aadf2e29d8bf44c1d0101143ead51dadf58fd1bb681ad4af8f3c008fa69", + "experiments/shell/workflow_checkpoint.hpp": "34ddd734ff6ab81ae39194d9231a162dbe2069eb2c7bad81a9f15d028be92369", + "experiments/shell/workflow_checkpoint_test.cpp": "f4d670707a251285be37546c7be1c8e6668bf5087414ae7e415cbb5b510bc8af", + "build/candidate/bin/Release/termforge_shell.exe": "8ca1957989d43941a67e38593f240a7bf651b145b78ca0918813d23de6c825bd" + } + }, + "same_diagnostic_executable": true, + "diagnostic_patch_sha256": "809c19056e30bc26dd810af81a65fe5220e5fc95ad8af96ddc156b20ec96d0de", + "cases": [ + { + "mode": "old", + "record": 117, + "partial": "TF_R108_F5_00117 \u4e2d\u6587 e", + "generation": 10, + "source_bytes_at_checkpoint": 32768, + "output_done": false, + "fixture_running": true, + "raw_core_cell_rows": [ + "TF_R108_F5_00117 \u4e2d \u6587 e", + "TF_R108_F5_00117 \u4e2d \u6587 e\u0301..................................." + ], + "same_session_continuation": true, + "native_prefix_preserved": true, + "actual_suffix_bytes": 39, + "held_cursor": [ + 6, + 23 + ], + "checkpoint_cursor": [ + 6, + 23 + ], + "held_transport_sha256": "49f4051e5f5f0c47fc955f2d0d94ac19a2224a2f5005dac34bed217a90374f7e", + "continued_transport_sha256": "d13c8002fabf72bccc837a892f546af898e924e7560f95c3d32ccc7e6f06425b", + "trace_overflow": { + "held": { + "events_overflow": false, + "transport_overflow": false + }, + "checkpoint": { + "events_overflow": false, + "transport_overflow": false + }, + "collected": { + "events_overflow": false, + "transport_overflow": false + } + }, + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "sample_error": "replacement core payload/role mismatch", + "source_sha256": "693dacfd941aefbb0b2379d44ec1d5492536611af9fd5b83c49642c65bcc56bb", + "sample_sha256": "faa3bea07e0092773dc66d925d85e644f36aefc1fc50eb1e9175885c3cb1568a", + "sessions_sha256": "b32e1e3d3ee3cb8164c124c9f42bbc635a25c8071ffd0b3fbb31068156a6e690", + "consumer_snapshots_sha256": { + "held": "7ddb00077095a97ae3950834fd4f432cd0fcd33d706b2b353a96a19e512dc04e", + "checkpoint": "1e979b605d3ef65ec7816409da7eb9fb2b80c2abda87047bcd77c45871d3a608", + "collected": "051f2a6058c23a03fe057a76f5078a87bd91ba74da2f918476f9621cfc321b31" + }, + "replay_sha256": "1b96eb77b7e7ec0c220349c7b4dc2496bd3bf2485d4610fa0cda04bccd929906" + }, + { + "mode": "new", + "record": 117, + "partial": "TF_R108_F5_00117 \u4e2d\u6587", + "generation": 10, + "source_bytes_at_checkpoint": 241664, + "output_done": false, + "fixture_running": true, + "raw_core_cell_rows": [ + "TF_R108_F5_00117 \u4e2d \u6587", + "TF_R108_F5_00117 \u4e2d \u6587 e\u0301..................................." + ], + "same_session_continuation": true, + "native_prefix_preserved": true, + "actual_suffix_bytes": 41, + "held_cursor": [ + 6, + 21 + ], + "checkpoint_cursor": [ + 6, + 0 + ], + "held_transport_sha256": "fe9c9fbb43fda939d2ea77e0c189dcc8ef6924aa0bf24b9ab8d733ccfb7bed99", + "continued_transport_sha256": "f31909f03efe242ab80365c59630cdcaf48612493a1e19ff0c64e600a43582ff", + "trace_overflow": { + "held": { + "events_overflow": false, + "transport_overflow": false + }, + "checkpoint": { + "events_overflow": false, + "transport_overflow": false + }, + "collected": { + "events_overflow": false, + "transport_overflow": true + } + }, + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "sample_error": null, + "source_sha256": "0c9ad5475733cff134f0372a4e9ef9348dec984b07d9e639741165750149cb90", + "sample_sha256": "7e0b9a8f32f40b64ac7d23d65f77757e759be53c053b65b29b6b860692adc9a7", + "sessions_sha256": "953c44c025631475b6cf8856fd632ec2d60f9409c4c9baf56bdc37e5c0b5ef9d", + "consumer_snapshots_sha256": { + "held": "f5806a2dd48560e4886b3dd7c8918259532781dd3ce19316c9a2d687aa273243", + "checkpoint": "e11551cb7501f13591f38a989f661e76b8feb69db9a8a9363797ba7b1d794d90", + "collected": "bf229d4ab212b4de45cc1082862705b3ce05fadb08889ea0a99b3f03540dbf63" + }, + "replay_sha256": "f97529bca6dbd1cbfd393ec92fa668e6bc06b7fdd88a4e73954c94a131cbbb72" + } + ], + "default_product_scheduling": false, + "schedule": "Pause F5 consumer before replacement output, wait source >=16384, consume one actual 8192B block, retain snapshot and resume. Producer input/rate/bytes and original oracle unchanged.", + "trace_limit_bytes": 131072, + "trace_limit_events": 1024, + "trace_limit_note": "New collected trace overflowed; needed suffix is present in its preserved prefix. No claim of complete subsequent transport." + }, + "close_guard": { + "red_log_sha256": "60504f0ee912974ade65cf8e573d774bcfe8370343f4da7ac96668f229fea49c", + "red_test_source_sha256": "3a262672b6e28d326d1765ba7488940aadd8edaffaee47314ec27368ba56f98b", + "red_product_source": "f89b0ac3a95f28e2b4ffa5bdfd368164848e8bde", + "red_executable_sha256": null, + "red_build_log_sha256": "e89d6ac2ad05e5807ee29cdcdf8b3a3d437b773407e560e70340f09426652d68", + "error": "closing window must reject a new output record checkpoint", + "green_log_sha256": "bbdc9e41c57357182dde0a239b4beaf2638e4bed38a656974ce7316b4fb442e4" + }, + "final_workflow_rounds": [ + { + "sample_sha256": "0e004559ca3b8bed704a3bf9cd4e6d54f92fc31db6576d2f2fc5cfc8090bfae2", + "sessions_sha256": "f437c524c63fa144d7389e7d38982d8f3b9568ff894dee08dd5e4609928fbe94", + "checked": { + "schema": "termforge-workflow-sessions-identity-validation-v1", + "live_sessions": 8, + "checkpoints": 26, + "rejected_ninth": true, + "load_foreground_verified": true, + "data_workflow_verified": true, + "complete_workflow_verified": false, + "system_clipboard_verified": false, + "ime_verified": false + }, + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + }, + "replacement_cycles": [ + { + "cycle": 1, + "source_bytes": [ + 20480, + 20480 + ], + "generations": [ + 9, + 10 + ], + "all_output_active": true + }, + { + "cycle": 2, + "source_bytes": [ + 16384, + 16384 + ], + "generations": [ + 11, + 12 + ], + "all_output_active": true + }, + { + "cycle": 3, + "source_bytes": [ + 12288, + 12288 + ], + "generations": [ + 13, + 14 + ], + "all_output_active": true + }, + { + "cycle": 4, + "source_bytes": [ + 12288, + 12288 + ], + "generations": [ + 15, + 16 + ], + "all_output_active": true + }, + { + "cycle": 5, + "source_bytes": [ + 16384, + 16384 + ], + "generations": [ + 17, + 18 + ], + "all_output_active": true + } + ] + }, + { + "sample_sha256": "18ab9afea089669bb30b6e69a401e5a8e7d8082aa9d49e5c0d28d2b1544cf12a", + "sessions_sha256": "ae6443ec74669dabed594afffdb99c94af6a022bb64582468e6bb80abe35c7da", + "checked": { + "schema": "termforge-workflow-sessions-identity-validation-v1", + "live_sessions": 8, + "checkpoints": 26, + "rejected_ninth": true, + "load_foreground_verified": true, + "data_workflow_verified": true, + "complete_workflow_verified": false, + "system_clipboard_verified": false, + "ime_verified": false + }, + "lifecycle": { + "exit_code": 0, + "timed_out": false, + "job_after": { + "active": 0, + "total": 77, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 77, + "terminated": 0 + } + }, + "replacement_cycles": [ + { + "cycle": 1, + "source_bytes": [ + 16384, + 16384 + ], + "generations": [ + 9, + 10 + ], + "all_output_active": true + }, + { + "cycle": 2, + "source_bytes": [ + 12288, + 12288 + ], + "generations": [ + 11, + 12 + ], + "all_output_active": true + }, + { + "cycle": 3, + "source_bytes": [ + 12288, + 12288 + ], + "generations": [ + 13, + 14 + ], + "all_output_active": true + }, + { + "cycle": 4, + "source_bytes": [ + 8192, + 12288 + ], + "generations": [ + 15, + 16 + ], + "all_output_active": true + }, + { + "cycle": 5, + "source_bytes": [ + 12288, + 12288 + ], + "generations": [ + 17, + 18 + ], + "all_output_active": true + } + ] + } + ], + "final_full_ctest": { + "log_sha256": "ea5283e3c04791877299e5e410d98fdbf695fd0c97ecc888e1e604dedc87f387", + "tests": [ + { + "name": "shell_launch", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "candidate_title", + "result": "passed", + "seconds": 0.75 + }, + { + "name": "candidate_reply_close", + "result": "passed", + "seconds": 0.37 + }, + { + "name": "runtime_observation", + "result": "passed", + "seconds": 1.45 + }, + { + "name": "ime_input_expectation", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "sent_message_wait", + "result": "passed", + "seconds": 0.28 + }, + { + "name": "find_bar", + "result": "passed", + "seconds": 0.36 + }, + { + "name": "renderer_metrics", + "result": "passed", + "seconds": 0.87 + }, + { + "name": "ime_context", + "result": "passed", + "seconds": 0.38 + }, + { + "name": "layout_state", + "result": "passed", + "seconds": 0.15 + }, + { + "name": "workspace_state", + "result": "passed", + "seconds": 0.37 + }, + { + "name": "transport_completion", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "workflow_checkpoint", + "result": "passed", + "seconds": 0.07 + }, + { + "name": "scenario_tick_gate", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "candidate_core", + "result": "passed", + "seconds": 1.23 + }, + { + "name": "resize_core", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "history_core", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "selection_core", + "result": "passed", + "seconds": 0.08 + }, + { + "name": "search_core", + "result": "passed", + "seconds": 0.12 + }, + { + "name": "paste", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "sender_fence", + "result": "passed", + "seconds": 1.02 + }, + { + "name": "sender_fence_pipe", + "result": "passed", + "seconds": 0.37 + }, + { + "name": "clipboard_policy", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "keyboard", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "text_action_policy", + "result": "passed", + "seconds": 0.01 + }, + { + "name": "candidate_resize_replay", + "result": "passed", + "seconds": 0.66 + }, + { + "name": "candidate_shell_tui", + "result": "failed", + "seconds": 31.29 + }, + { + "name": "core", + "result": "passed", + "seconds": 6.43 + }, + { + "name": "recovery", + "result": "passed", + "seconds": 10.07 + }, + { + "name": "measure", + "result": "passed", + "seconds": 4.24 + }, + { + "name": "binary_paths", + "result": "passed", + "seconds": 0.18 + }, + { + "name": "conpty_margin_contract", + "result": "passed", + "seconds": 0.28 + }, + { + "name": "benchmark_tools", + "result": "passed", + "seconds": 5.37 + }, + { + "name": "differential", + "result": "passed", + "seconds": 20.33 + }, + { + "name": "ime_inventory", + "result": "passed", + "seconds": 49.88 + }, + { + "name": "text_actions", + "result": "passed", + "seconds": 5.43 + }, + { + "name": "workflow_fixture", + "result": "passed", + "seconds": 22.84 + }, + { + "name": "workflow_sessions", + "result": "passed", + "seconds": 81.61 + }, + { + "name": "workflow_resources", + "result": "passed", + "seconds": 0.47 + }, + { + "name": "workflow_resource_phases", + "result": "passed", + "seconds": 4.37 + }, + { + "name": "workflow_system_content", + "result": "passed", + "seconds": 0.18 + }, + { + "name": "workflow_system_runner", + "result": "passed", + "seconds": 0.68 + }, + { + "name": "copy_error_frame", + "result": "passed", + "seconds": 0.14 + }, + { + "name": "paste_fence_evidence", + "result": "passed", + "seconds": 0.11 + }, + { + "name": "resize_trace", + "result": "passed", + "seconds": 0.13 + }, + { + "name": "research_guards", + "result": "passed", + "seconds": 4.93 + }, + { + "name": "receive", + "result": "passed", + "seconds": 0.2 + }, + { + "name": "conpty", + "result": "passed", + "seconds": 22.94 + }, + { + "name": "conpty_ctrl_c", + "result": "passed", + "seconds": 4.33 + }, + { + "name": "conpty_margin", + "result": "passed", + "seconds": 0.39 + }, + { + "name": "conpty_backend", + "result": "passed", + "seconds": 1.16 + }, + { + "name": "presentation", + "result": "passed", + "seconds": 4.11 + }, + { + "name": "shell", + "result": "passed", + "seconds": 94.93 + }, + { + "name": "selection_bytes", + "result": "passed", + "seconds": 3.64 + }, + { + "name": "selection_context", + "result": "passed", + "seconds": 3.94 + }, + { + "name": "raw_console_receiver", + "result": "passed", + "seconds": 0.39 + }, + { + "name": "paste_system", + "result": "passed", + "seconds": 8.93 + } + ], + "total": 57, + "passed": 56, + "failed": [ + "candidate_shell_tui" + ], + "seconds": 402.66, + "failure_stages": [ + 59 + ], + "internal_skips": [ + { + "test": "test_actual_system_copy_paste_and_shortcuts_twice (experiments.shell.test_actions.ActionWindowTest.test_actual_system_copy_paste_and_shortcuts_twice)", + "reason": "actual system Copy/Paste requires a disposable hosted VM" + }, + { + "test": "test_suspended_target_is_reclaimed_if_job_assignment_fails (test_ui_r1.EvidenceBoundaryTests.test_suspended_target_is_reclaimed_if_job_assignment_fails)", + "reason": "opt-in pinned reference failure injection" + }, + { + "test": "test_mouse_selection_to_real_system_clipboard (experiments.shell.test_shell.ShellTests.test_mouse_selection_to_real_system_clipboard)", + "reason": "system copy UI gate requires a disposable hosted VM" + }, + { + "test": "test_system_clipboard_product_read_errors_cancel_and_close (experiments.shell.test_shell.ShellTests.test_system_clipboard_product_read_errors_cancel_and_close)", + "reason": "product clipboard read gate requires a disposable hosted VM" + }, + { + "test": "test_system_clipboard_shortcut_to_window_confirmation_and_receiver (experiments.shell.test_shell.ShellTests.test_system_clipboard_shortcut_to_window_confirmation_and_receiver)", + "reason": "real system clipboard UI gate requires a disposable hosted VM" + }, + { + "test": "test_real_system_copy_shortcuts_for_styled_fixture_twice (experiments.shell.test_selection_bytes.SelectionBytesWindowTest.test_real_system_copy_shortcuts_for_styled_fixture_twice)", + "reason": "system CF requires a disposable hosted VM" + }, + { + "test": "test_real_system_copy_and_edit_context_twice (experiments.shell.test_selection_context.SelectionContextWindowTest.test_real_system_copy_and_edit_context_twice)", + "reason": "real CF/EDIT Copy requires a disposable hosted VM" + }, + { + "test": "test_real_system_clipboard (experiments.shell.test_paste.PasteTests.test_real_system_clipboard)", + "reason": "Private window station OpenClipboard denied (5); system API gate remains unverified." + } + ], + "full_acceptance": false, + "last_test_log_sha256": "c7728e3c25e030fe12e7e39652b7e01409661a34f3f5aa1c23f16e78b332afa1" + }, + "other_failures": [ + { + "mode": "candidate-tui", + "stage": 59, + "lifecycle": { + "exit_code": 2, + "timed_out": false, + "job_after": { + "active": 0, + "total": 7, + "terminated": 0 + }, + "desktop_gone": true, + "job_after_cleanup": { + "active": 0, + "total": 7, + "terminated": 0 + } + }, + "files_sha256": { + "candidate-input.bin": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "candidate-insert-log.txt": "e6de4daeba1841783e7e22f81dbf87154f341c309dc5b54405e5fc2c2f3550a6", + "candidate-output.bin": "fc43e5c356005f9c32847476d4bdba5fabaf51439083d8ac4e530d3e8bb5c883", + "candidate-resize-log.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "candidate-startup-log.txt": "3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5", + "candidate-vim.vim": "386d475e5297169cf7fcc425dcc0f6b0174c237ff86be03564b2d505c5d79797", + "sample.json": "115d07631f728fb3ed2cd0a0e0a6a183c39627c627b7c849a029bbeb6eb4b695" + } + } + ], + "earlier_pre_close_guard_ctest": { + "log_sha256": "c47eca6a71598128c07a98c65d9b975163177fe197d80a01a2720b120ca0f3ed", + "total": 57, + "passed": 57, + "failed": [], + "seconds": 382.56, + "failure_stages": [], + "full_acceptance": false, + "source_commit": "f89b0ac3a95f28e2b4ffa5bdfd368164848e8bde", + "usable_for_acceptance": false, + "internal_skip_count": 8 + }, + "limits": [ + "Original two historical failures had no native continuation capture; later controlled causal reproduction does not fabricate it.", + "Only synthetic sampling coordination changes; no demonstrated product text-loss fix, oracle weakening, performance or WindTerm-equivalence claim.", + "Four passive live probes missed the half-row; a passive readiness implementation later starved in a controlled run and is rejected.", + "An excluded generic cmd echo test assumed a literal LF that the transport need not emit; final live close test only claims close while armed.", + "Raw native bytes, personal paths and desktop identities remain local. Controlled diagnostic scheduling and final original uninstrumented runs are separate.", + "Real CF/IME, actual presentation, full official-backend acceptance, #52/#63 and R2 remain unverified." + ], + "presented_done": null, + "performance_acceptance": false, + "windterm_runtime_equivalence": false, + "full_r2_verified": false, + "excluded_cmd_lf_test_sha256": "7d2cafa5d70d57e69527f626f533995e8d8c71a77a6b4f55f7aae646af91be09" +} diff --git a/experiments/shell/runtime_observation_test.cpp b/experiments/shell/runtime_observation_test.cpp index c5ddaa00..a247acc2 100644 --- a/experiments/shell/runtime_observation_test.cpp +++ b/experiments/shell/runtime_observation_test.cpp @@ -124,11 +124,21 @@ static void runtime_default_off_case() { require(!window.runtime_observation_enabled(), "normal live window keeps observation disabled"); require(!window.operation_observation_enabled() && window.operation_observations().empty(), "normal live window keeps operation observation disabled"); + require(!window.output_record_checkpoint_active(),"ordinary live output has no record checkpoint"); + window.arm_output_record_checkpoint(std::chrono::steady_clock::now()+std::chrono::seconds(5)); + require(window.output_record_checkpoint_active(),"explicit output checkpoint must arm"); window.close(); + require(!window.output_record_checkpoint_active(),"close must release an armed output record checkpoint"); + bool closed_arm_rejected=false; + try { window.arm_output_record_checkpoint(std::chrono::steady_clock::now()+std::chrono::seconds(5)); } + catch(const std::exception&) { closed_arm_rejected=true; } + const bool rearmed=window.output_record_checkpoint_active(); + window.release_output_record_checkpoint(); runtime_wait(window, [&] { return IsWindow(window.handle()) == FALSE; }, "unobserved real shell closes deadline"); const auto result = window.collect(); require(result.complete && !result.runtime_before_close && !result.runtime_final && !window.runtime_before_close() && !window.runtime_final(), "normal lifecycle creates no runtime samples"); + require(closed_arm_rejected && !rearmed,"closing window must reject a new output record checkpoint"); } static void operation_state_case() { using R=tf_shell::OperationReason; diff --git a/experiments/shell/shell.cpp b/experiments/shell/shell.cpp index 9b3ec6ac..cec8f84f 100644 --- a/experiments/shell/shell.cpp +++ b/experiments/shell/shell.cpp @@ -6,6 +6,7 @@ #include "queues.hpp" #include "resize.hpp" #include "resize_trace.hpp" +#include "output_record_checkpoint.hpp" #include "keyboard.hpp" #include "ime.hpp" #include "hosted_ime_input.hpp" @@ -102,6 +103,7 @@ class ShellWindow { bool find_initial_ = false, find_terminal_visible_ = true; bool observe_output_ = false; std::string observed_output_; // Opt-in synthetic probe, capped independently of queue size. + tf_shell_test::OutputRecordCheckpoint output_record_checkpoint_; Clock::time_point start_ = Clock::now(), input_time_, flood_start_; bool frame_after_ = false; Result result_; @@ -354,6 +356,7 @@ class ShellWindow { if (closing_) return; if(runtime_enabled_) runtime_before_close_=runtime_snapshot(); closing_ = true; + output_record_checkpoint_.release(); // Closing must drain even a held synthetic checkpoint. operation_observations_.close(); selection_end(); find_.reset(); search_.close(); find_current_.reset(); clear_find_resize_anchor(); @@ -793,7 +796,9 @@ class ShellWindow { bool drained = false; // Check time between small feeds; also retain a hard byte budget. do { - const auto bytes = output_.take(8192); + const auto budget=output_record_checkpoint_.read_budget(8192); + if(!budget) break; + const auto bytes = output_.take(budget); if (bytes.empty()) { drained = true; break; } if(observe_output_) { observed_output_.append(bytes); @@ -801,9 +806,11 @@ class ShellWindow { } resize_trace_.observe(tf_shell_test::ResizeTrace::feed, parser_.screen.height(), parser_.screen.width(), 0, stage_, bytes); parser_.feed(bytes); batch += bytes.size(); + output_record_checkpoint_.consumed(bytes); #ifdef TERMFORGE_CANDIDATE_CORE route_terminal_events(); #endif + if(output_record_checkpoint_.held()) break; } while (batch < 65536 && ms(began) < 4); result_.max_consumed_per_tick = (std::max)(result_.max_consumed_per_tick, batch); if (batch) { result_.consumed += batch; search_changed(); publish(); } @@ -1191,6 +1198,17 @@ class ShellWindow { return lines; } int observed_tick_count() const { return result_.ticks; } + void arm_output_record_checkpoint(Clock::time_point deadline) { + require_ui_thread(); + require(!closing_ && hwnd_ && IsWindow(hwnd_),"output checkpoint target must be live"); + const auto& view=*snapshot_; + const bool empty=view.cursor_row>=0 && static_cast(view.cursor_row)(view.cursor_row)]).text.empty(); + output_record_checkpoint_.arm(empty,deadline); + } + bool output_record_checkpoint_held() const { require_ui_thread();return output_record_checkpoint_.held(); } + bool output_record_checkpoint_active() const { require_ui_thread();return output_record_checkpoint_.active(); } + void release_output_record_checkpoint() { require_ui_thread();output_record_checkpoint_.release(); } void set_search_work_budget(size_t units) { require(units>=1 && units<=4096,"search work budget outside testable bound"); search_work_budget_=units; } diff --git a/experiments/shell/workflow_checkpoint.hpp b/experiments/shell/workflow_checkpoint.hpp new file mode 100644 index 00000000..b1d8d78f --- /dev/null +++ b/experiments/shell/workflow_checkpoint.hpp @@ -0,0 +1,31 @@ +// Readiness for the fixed numbered-output fixture, separate from its oracle. +#pragma once +#include "../core/history_view.hpp" +#include "../core/text_document.hpp" +#include +#include + +namespace tf_shell_test { +inline bool workflow_numbered_output_started(const std::string& role, + const std::vector& document) { + const auto prefix="TF_R108_"+role+'_'; + for(const auto& row:document) { + const auto& text=row.projection.text; + if(text.rfind(prefix,0)==0 && text.size()>prefix.size() && + text[prefix.size()]>='0' && text[prefix.size()]<='9') return true; + } + return false; +} +template +inline bool workflow_checkpoint_ready(const std::string& role, + const std::vector& document, + const View& view) { + // Prefixes can be visible halfway through an output-queue read. The + // fixed CR/LF fixture is sampled only on its next empty live row; the + // independent oracle still validates every captured numbered payload. + return view.cursor_row>=0 && static_cast(view.cursor_row)(view.cursor_row)]).text.empty() && + workflow_numbered_output_started(role,document); +} +} // namespace tf_shell_test diff --git a/experiments/shell/workflow_checkpoint_test.cpp b/experiments/shell/workflow_checkpoint_test.cpp new file mode 100644 index 00000000..56e73c20 --- /dev/null +++ b/experiments/shell/workflow_checkpoint_test.cpp @@ -0,0 +1,116 @@ +#include "workflow_checkpoint.hpp" +#include "output_record_checkpoint.hpp" +#include "../core/terminal.hpp" +#ifdef TERMFORGE_CANDIDATE_CORE +#include "../core/candidate_terminal.hpp" +#endif +#include +#include +#include +#include + +namespace { +void check(bool value,const char* message) { + if(!value) throw std::runtime_error(message); +} +template bool ready(Terminal& terminal) { + termforge::TextDocument document(terminal.screen); + std::vector lines; + for(size_t i=0;i void chunk_boundary(Terminal& terminal) { + // The fixture emits READY before signaling its side-channel readiness. + // Its 25 bytes can share an output-queue read with numbered source data. + std::ostringstream source; + source<<"\x1b[2J\x1b[HTF_R108_F5_READY\r\n"; + for(unsigned i=0;i<128;++i) + source<<"TF_R108_F5_"< void boundary_states(Terminal& terminal) { + terminal.feed("\x1b[2J\x1b[H"); + check(!ready(terminal),"empty startup must not qualify as numbered output"); + terminal.feed("TF_R108_F5_READY\r\n"); + check(!ready(terminal),"READY is not a numbered payload"); + terminal.feed("TF_R108_F5_00000 中文 é...................................\r"); + check(!ready(terminal),"CR alone leaves the completed row under the cursor"); + terminal.feed("\n"); + check(ready(terminal),"CR/LF completes the record boundary"); + terminal.feed("TF_R108_F5_"); + check(!ready(terminal),"an earlier complete row must not hide a new partial prefix"); + terminal.feed("00001 中文 é...................................\r\n"); + check(ready(terminal),"later fragmented record must reach its boundary"); + terminal.feed("\x1b[2J\x1b[HTF_R108_F4_00000 中文 é...................................\r\n"); + check(!ready(terminal),"another role alone must not make this role ready"); + terminal.feed("TF_R108_F5_00000 BROKEN\r\n"); + check(ready(terminal),"readiness must expose completed corrupt rows to the independent oracle"); +} +} +int main() { + int failures=0; + try { checkpoint_bounds(); } catch(const std::exception& error) { std::cerr<observed_document_lines()) { - const auto& text=row.projection.text; - if(text.rfind(prefix,0)==0 && text.size()>prefix.size() && text[prefix.size()]>='0' && text[prefix.size()]<='9') consumed=true; - } - if(!consumed) return; + if(std::filesystem::file_size(gate(name).path.wstring()+L".source.bin")<4096) {held=false;continue;} + auto* shell=workflow_shell(name); + const auto document=shell->observed_document_lines(); + if(!tf_shell_test::workflow_numbered_output_started(name,document)) {held=false;continue;} + shell->arm_output_record_checkpoint(started_+std::chrono::milliseconds(70000)); + if(!shell->output_record_checkpoint_held()) {held=false;continue;} + require(tf_shell_test::workflow_checkpoint_ready(name,document,shell->visible_snapshot()),"workflow checkpoint did not reach an empty record boundary"); } + if(!held) return; workflow_checkpoint("cycle-eight",evidence.cycle); + for(const std::string name:{"F4","F5"}) workflow_shell(name)->release_output_record_checkpoint(); if(evidence.cycle<5) {++evidence.cycle;stage_=30;} else stage_=40; } else if(stage_==40) { paste_scenario_.start_receiver(std::filesystem::path(image_).parent_path()/"close.bin","close");