diff --git a/CMakeLists.txt b/CMakeLists.txt index 010bb7db..76238089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,7 +141,8 @@ if(WIN32) if(TERMFORGE_BUILD_SOURCE_CONPTY_EXPERIMENT) add_executable(conpty_source_margin_probe experiments/conpty/margin_probe.cpp) add_executable(conpty_source_backend_probe experiments/conpty/backend_probe.cpp) - foreach(target conpty_source_margin_probe conpty_source_backend_probe) + add_executable(conpty_source_margin_controls experiments/conpty/margin_controls_probe.cpp) + foreach(target conpty_source_margin_probe conpty_source_backend_probe conpty_source_margin_controls) termforge_warnings(${target}) target_compile_definitions(${target} PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 TERMFORGE_CONPTY_SOURCE_EXPERIMENT=1) @@ -370,6 +371,7 @@ if(BUILD_TESTING) termforge_add_python_test(measure -m unittest experiments.measure.test_measure experiments.measure.test_r54_contract.CostPlanTests -v) termforge_add_python_test(binary_paths -m unittest experiments.test_build_paths -v) termforge_add_python_test(conpty_margin_contract -m unittest experiments.conpty.test_margin_contract -v) + termforge_add_python_test(conpty_margin_controls -m unittest experiments.conpty.test_margin_controls -v) termforge_add_python_test(benchmark_tools -m unittest discover -s benchmarks/tests -v) termforge_add_python_test(differential -m unittest discover -s experiments/differential -p test_*.py -v) termforge_add_python_test(ime_inventory -m unittest research.ime.test_hosted_inventory research.ime.test_prepare_hosted research.ime.test_product_ime -v) diff --git a/docs/EVIDENCE.md b/docs/EVIDENCE.md index 11c12d3f..657ef767 100644 --- a/docs/EVIDENCE.md +++ b/docs/EVIDENCE.md @@ -52,6 +52,7 @@ R1 固定总出口的源码为 `ce9b24d`,早期 `2dc8d6d` / `9b16acc` 仍分 | 纯 AST 夹具的生命周期记录 | [#40](https://github.com/JavaZeroo/termforge/issues/40)、[计时、进度与回收验证](../research/ime/results/2026-10-26-inventory-lifecycle/validation.json)、[专用 CI 40253727745](https://github.com/JavaZeroo/termforge/actions/runs/40253727745) | 旧 10 秒 AST 夹具及专用 CI 记录启动、AST 和 Job0;原超时根因未确定。诊断增强见 #40 阶段保留,不由一次 CI 成功结项。 | | 原生 margin 逐字段合同与历史修复候选(#56 仍开放) | [合同、源码与路径](research/conpty-native-margin-path.md)、[独立 oracle](../experiments/conpty/margin_contract.py)、[2026-10-26固定运行](../experiments/conpty/results/2026-10-26-native-margin-path/validation.json) | 当时系统与官方原生 final 仍红,官方80列两次原5秒生命周期失败保留;补丁当时仅可应用。后续真实同源构建/运行另列,不改写旧失败。 | | 固定源码 ConPTY margin 运行对照(#56 限定实验) | [构建身份、运行与复验](research/conpty-source-margin-runtime.md)、[白名单验证](../experiments/conpty/results/2026-10-27-source-margin-runtime/validation.json) | 同源baseline与patched,原175B两尺寸native red→green;四阶段文本/格属性/光标、末端文本和VT-off反例独立检查,共8次自然退出/EOF/Job0。15个API/身份场景及8个清单拒绝通过;显式样式、历史、resize、完整产品未验,未选择后端。 | +| 源码 ConPTY 主屏与重复切屏对照(#56 限定实验) | [输入、预期与结果](research/conpty-source-margin-controls.md)、[白名单验证](../experiments/conpty/results/2026-10-27-source-margin-controls/validation.json) | 三场景×两尺寸×同源两版共12次;主屏均正确,默认备用屏/重复h/l为具体旧错误→原生全部正确,两版末端最终文字均正确;全部自然退出/EOF/Job0。独立输入不替代原175B,样式/历史/resize及产品未验。 | | ConPTY 系统/固定官方后端的同源 margin A/B(#41、#47 未关闭) | [合同与第一方来源](research/conpty-margin-backends.md)、[固定 175B 与 16 case 结果](../experiments/conpty/results/2026-10-26-margin-backends/validation.json) | 相同 175B、两网格/两 mode/两 fresh:系统末端 margin 错,固定官方后端的末端正确;CHAR_INFO 仍错。禁用 VT 由窄屏反例拒绝;仅诊断可行性,不证明 PR #50 或 main 联合整合通过。 | | 实验输入 worker 持块与停止(#62) | [sender fence 合同](research/sender-fence.md)、[固定源码与实际字段](../experiments/shell/results/2026-10-26-sender-fence/validation.json) | `519a3f3` 的系统/官方各两进程、每次 stop/cancel/竞争,共 12 条实际 worker 记录,均 join/Job0;另有各一次原 focus 单测和各 2/2 纯 CTest。明确修正旧 never-read/output-first 不能保证 16KiB 的夹具假设,同 EXE 旧顺序仍红。实现仅在 PR #50 实验整合,main 未包含;probe 未链接候选 VT,纯 EOF 为模拟,#53 UI/3MB/768B/八会话及完整官方矩阵未验收。 | | 实验 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、完整后端或性能通过。 | diff --git a/docs/FEATURES.md b/docs/FEATURES.md index b6bc7f8a..2e3401b0 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -16,7 +16,7 @@ | 显式候选 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)的原保存/恢复/钳制分别通过;[#87 完整记录检查点](research/workflow-record-checkpoint.md)有受控旧红/新绿及最终同源码两轮原八会话 data-only。最新官方完整回归 56/57、8 项内部 skip,[Vim #52](https://github.com/JavaZeroo/termforge/issues/52)仍红,[MSYS输入边界](research/vim-msys-input-boundary.md)已定位捕获样本的Ctrl-O丢失位置,后续[隔离运行库补丁](research/vim-msys-runtime-prototype.md)在重建基点red后原方法四轮通过;正式固定运行库验收未完成,[#63 工作区残尾](research/workspace-native-viewport.md)已由同EXE单变量与运输回放确认现有viewport修复覆盖,原UI/主区三轮重复两批及完整shell通过;更早缺运输样本保留未知。[#53 UI fence](research/ui-paste-fence.md)与[行键复用/关闭回复](research/resize-replay-cost.md)、[#62 worker 夹具](research/sender-fence.md)各自限定通过。终端 margin 与原生状态分列,#47 及完整后端仍开放;旧失败和历史运输缺口保留 | -另有默认关闭的[源码 ConPTY margin 探针实验](research/conpty-source-margin-runtime.md):固定同源 baseline/patched 对照在原175B两尺寸取得原生四阶段 red→green,VT-off反例保持一致。它只构建独立探针,尚未成为产品后端;样式、历史、resize及完整合同仍由 #56/#41 跟踪。 +另有默认关闭的[源码 ConPTY margin 探针实验](research/conpty-source-margin-runtime.md):固定同源 baseline/patched 对照在原175B两尺寸取得原生四阶段 red→green,VT-off反例保持一致。后续[主屏与重复切屏对照](research/conpty-source-margin-controls.md)以独立输入完成12次原生/末端验证,补充默认备用屏区域和重复h/l的限定证据。它只构建独立探针,尚未成为产品后端;样式、历史、resize及完整合同仍由 #56/#41 跟踪。 以下能力默认指已合并实现;某个配置的通过结果不能转用于另一个配置。R1 的真实系统 CF/拼音验收属于固定自研核心配置,不外推到候选 `.4` 或实验后端联合配置。 diff --git a/docs/research/conpty-source-margin-controls.md b/docs/research/conpty-source-margin-controls.md new file mode 100644 index 00000000..7805f167 --- /dev/null +++ b/docs/research/conpty-source-margin-controls.md @@ -0,0 +1,44 @@ +# 源码 ConPTY:主屏与重复切屏的原生对照 + +同源 margin 补丁在三个补充场景中符合预先固定的结果:主屏单独滚动保持正确;新建或再次进入备用屏使用默认全屏滚动区域;返回主屏和重复离开备用屏后恢复原来的 `2;3r`。未修补版在后两个场景出现具体预期错误,修补版通过全部原生阶段。实际运输的最终主屏在两版中均正确,不能用末端正确掩盖原生错误。 + +这是 [#56](https://github.com/JavaZeroo/termforge/issues/56) 的独立补充实验。预期依据是既有 R2 每缓冲区 margin 合同和固定上游状态路径,运行前已记录在 issue;WindTerm 2.7.0 对这些场景的实际行为仍未知。本结果不验收 WindTerm 等价、不选择产品后端,也不关闭 #56/#41。 + +## 固定输入与观测边界 + +沿用[原 175B 对照](conpty-source-margin-runtime.md)的同源 baseline/patched:Terminal `dc8ae365847d41f62d1ebca93815bb00959bc12f`、构建 run `40449292015` 和已审核的双文件 SHA、variant/summary/patch receipt。本次未修改上游补丁或重新构建上游。 + +三个场景各在 `80×24`、`12×7` 下运行 baseline/patched 一次,共 12 次,只开 VT;无自动重试。输入由独立 controls 探针发送,原 175B、四阶段 probe/oracle 和既有期限不变。新场景使用 `CTRL_DONE`,字节数与原负载分列。 + +| 场景 | 80×24 / 12×7 字节数 | 运行前固定的原生预期 | 未修补版实际结果 | 修补版实际结果 | +|---|---:|---|---|---| +| `main-only` | 99 / 99 | 无备用屏;主屏 `2;3r` 滚动,区域外不变 | before/final 均正确 | before/final 均正确 | +| `default-alt` | 150 / 149 | 主屏区域不传给新备用屏;物理底部换行应全屏滚动;返回后仍为主屏区域 | alt 未全屏滚动:保留 `ALT_TOP`,`ALT_LAST` 被覆盖;其余阶段正确 | before/alt/restored/final 全部正确 | +| `repeat-alt` | 221 / 220 | alt1 的 `4;5r` 不传给再次创建的 alt2;两次离开后主屏仍为 `2;3r` | alt2 同样未全屏滚动;final 出现 `REGION_CIXED`;其余阶段正确 | before/alt1/alt2/restored/final 全部正确 | + +每个阶段分别检查 `ReadConsoleOutputCharacterW` 文本、完整 `CHAR_INFO` 字符/属性、光标及 viewport top。预期为字面值,不使用候选 parser 生成;baseline 必须符合上述具体旧状态,异常、生命周期失败或其他文字错误均不计为有效反例。没有设置 SGR,属性检查只证明初始属性保持。 + +12 次都自然退出 37、读端 EOF 109,creation flags=0,内层子进程清零,外层 Job 为 active0/total4/terminated0,无强制取消。每次仍为 5 秒 child wait、15 秒外期限、共享 5 秒 cleanup;离线候选回放另由既有 trace 入口管理。两版实际运输的最终文字和 alternate=false 均符合预期;没有观测末端逐阶段光标、属性或实际呈现。 + +## 源码、构建与验证身份 + +- 本地实际运行源码 `edec26bb3c4d04858a667a96debf69c139476172`,工作树干净;参与文件 SHA、探针、trace、输入、运输、每阶段捕获及监督记录 SHA 见[脱敏清单](../../experiments/conpty/results/2026-10-27-source-margin-controls/validation.json)。 +- Windows 11 x64 build 26200;VS17 / MSVC 19.42.34433 / SDK 26100,Release x64。controls 探针 SHA `a71c94795806a40062ace7788ee69cfbac0cd8a274765dce6b28aa6d7f33c7ee`;`.5` trace SHA `5941d4569d0ae146eb005f12ecfb15014bc06c18ee48d1c9460824ab65df93d3`。 +- 新旧 margin 判据与 GO/回收回归 17/17,通过耗时 0.375 秒;原 loader/API/清理回归 14/14、零跳过,1.020 秒。后者复用前一批相同源码的原探针 EXE,运行本次修改后的 Python supervisor;不冒充重新编译原探针。两个实际 stdout 日志均保存在本地并记录 SHA,时长仅为测试诊断。 +- `TERMFORGE_BUILD_SOURCE_CONPTY_EXPERIMENT` 仍默认 OFF;新增 `conpty_source_margin_controls` 独立目标。仅 supervisor 增加可选参数和期限,既有 API 调用默认仍为 20 秒。纯 controls 判据纳入 Windows/Linux CTest,不依赖 native DLL;原生矩阵需显式运行。 + +私有原件位于 `local/r56-margin-controls/matrix-1`;公开清单仅含合成场景结果、必要身份和文件哈希,不包含绝对路径、原生二进制、原始运输或用户状态。 + +## 复验与剩余范围 + +先按[源码构建说明](../../experiments/conpty/upstream/README.md)取得并审核固定 run 的 baseline/patched。使用全新的输出目录: + +```powershell +cmake -S . -B local/source-controls-build -A x64 -DTERMFORGE_BUILD_SOURCE_CONPTY_EXPERIMENT=ON -DTERMFORGE_BUILD_CANDIDATE_ADAPTER=OFF -DTERMFORGE_CONPTY_BACKEND_DIR= -DBUILD_TESTING=OFF +cmake --build local/source-controls-build --config Release --target conpty_source_margin_controls --parallel 2 +cargo +1.92.0 build --manifest-path experiments/candidates/alacritty/Cargo.toml --locked --release --bin termforge-alacritty-trace +python -m experiments.conpty.source_margin_controls --baseline local/source-baseline --patched local/source-patched --probe local/source-controls-build/bin/Release/conpty_source_margin_controls.exe --candidate-trace experiments/candidates/alacritty/target/release/termforge-alacritty-trace.exe --output local/source-controls-new +python -m unittest experiments.conpty.test_margin_controls -v +``` + +本次只补无备用屏、默认备用屏区域、重复 h/l 的真实交叉。显式样式、非零光标保存恢复、历史隔离/压力、resize、查询/API 写入、完整产品工作流、性能及维护升级仍未验证;原 175B 的 VT-off 证据属于前一批,不能合并成同批更大矩阵。原系统/官方失败、#52、#97 和 #99 的独立失败继续保留。正式方案仍由 #34 提交证据及取舍,经用户确认后生效。 diff --git a/docs/research/conpty-source-margin-runtime.md b/docs/research/conpty-source-margin-runtime.md index 9b6e7f95..fcd05c64 100644 --- a/docs/research/conpty-source-margin-runtime.md +++ b/docs/research/conpty-source-margin-runtime.md @@ -24,7 +24,7 @@ ## 隔离入口与身份拒绝 -`TERMFORGE_BUILD_SOURCE_CONPTY_EXPERIMENT` 默认 OFF,仅建立 `conpty_source_margin_probe` 与 `conpty_source_backend_probe`,不能与产品 `TERMFORGE_CONPTY_BACKEND_DIR` 同时启用。只有这两个目标编译新的 source factory;默认及官方 factory 仍严格接受各自原身份。产品 bundle、默认后端和 PR #50 的固定官方配置均未切换。 +`TERMFORGE_BUILD_SOURCE_CONPTY_EXPERIMENT` 默认 OFF,本次使用 `conpty_source_margin_probe` 与 `conpty_source_backend_probe`;后续[补充对照](conpty-source-margin-controls.md)另增加 `conpty_source_margin_controls`。只有这三个独立目标编译 source factory,不能与产品 `TERMFORGE_CONPTY_BACKEND_DIR` 同时启用;默认及官方 factory 仍严格接受各自原身份。产品 bundle、默认后端和 PR #50 的固定官方配置均未切换。 source factory 只允许表中两个 SHA 对,分别报告 `source-dc8-r56-baseline-run40449292015` / `source-dc8-r56-patched-run40449292015`。Python 入口另核对原始 summary、patch、精确 variant 字段与类型、文件大小和 SHA;布尔值不能以 0/1 代替。DLL 与 EXE 在加载期间保持只读共享句柄;create/resize/close 使用同一模块,模块存活至会话和子进程回收之后。 @@ -63,6 +63,6 @@ python -m experiments.conpty.source_margin_run --baseline local/source-baseline python -m experiments.conpty.source_backend_check --baseline local/source-baseline --patched local/source-patched --official local/official-conpty-1.25 --probe-directory local/source-margin-build/bin/Release --output local/source-backend-new ``` -待补无备用屏、重复 h/l、默认 margin、非零光标和显式样式、历史隔离、resize、查询及 API 写入的真实交叉;上游 focused 测试虽覆盖重复 h/l,不能代替这些原生运行。之后仍需实际产品联合源码、支持平台、资源与维护/升级验证,并由 #34 提出正式方案供用户确认。 +无备用屏、重复 h/l、默认 margin 的后续 12 次[原生补充对照](conpty-source-margin-controls.md)已单独记录,不更改或扩充本批 175B 结果。非零光标和显式样式、历史隔离、resize、查询及 API 写入仍待真实交叉;之后还需实际产品联合源码、支持平台、资源与维护/升级验证,并由 #34 提出正式方案供用户确认。 独立的 [#97 candidate-scroll 退出超时](https://github.com/JavaZeroo/termforge/issues/97)发生于 parent `5c073e4` 的 candidate/system push CI:正确末帧已经产生,但原 20 秒期限未自然结束。它不是本 175B 输入,同 HEAD 的其他 CI 成功不能覆盖该失败;此处的源构建修补也不构成其因果修复。原官方 80 列 5 秒失败、Vim #52、完整 R2、WindTerm 行为等价、性能与实际呈现均没有由本实验验收。 diff --git a/experiments/conpty/margin_controls.py b/experiments/conpty/margin_controls.py new file mode 100644 index 00000000..bcd9c106 --- /dev/null +++ b/experiments/conpty/margin_controls.py @@ -0,0 +1,74 @@ +"""Literal native outcomes for independent source ConPTY control workloads.""" +from __future__ import annotations + + +def expected_source(case, columns, rows): + expected_native(case, columns, rows) # Fixed workload/grid validation. + parts = [b"\x1b[2J\x1b[HTOP_FIXED\r\nREGION_A\r\nREGION_B\r\nBOTTOM_FIXED\r\n\x1b[2;3r"] + if case != "main-only": + if case == "repeat-alt": + parts.append(b"\x1b[?1049hALT_OUTSIDE\r\n\x1b[4;1HALT_A\r\nALT_B\x1b[4;5r\x1b[5;1H\nALT_C\nALT_D") + parts.append(b"\x1b[?1049h\x1b[HALT_TOP\x1b[" + str(rows).encode("ascii") + b";1HALT_LAST\r\nALT_DONE") + parts.append(b"\x1b[?1049l\x1b[?1049l" if case == "repeat-alt" else b"\x1b[?1049l") + parts.append(b"\x1b[3;1H\nREGION_C\nREGION_D\x1b[6;1HCTRL_DONE\r\n") + return b"".join(parts) + + +def expected_native(case, columns, rows): + if (columns, rows) not in ((80, 24), (12, 7)) or case not in ("main-only", "default-alt", "repeat-alt"): + raise ValueError("fixed native control and grid required") + + def state(lines, cursor): + return {"lines": lines + [""] * (rows - len(lines)), "cursor": cursor} + + before = state(["TOP_FIXED", "REGION_A", "REGION_B", "BOTTOM_FIXED"], [0, 0]) + result = {"before": before} + if case != "main-only": + if case == "repeat-alt": + result["alt1"] = state(["ALT_OUTSIDE", "", "", "ALT_C", "ALT_D"], [4, 5]) + result["alt2" if case == "repeat-alt" else "alt"] = state( + [""] * (rows - 2) + ["ALT_LAST", "ALT_DONE"], [rows - 1, 8]) + result["restored"] = before + result["final"] = state(["TOP_FIXED", "REGION_C", "REGION_D", "BOTTOM_FIXED", "", "CTRL_DONE"], [6, 0]) + return result + + +def _evaluate(stages, expected, columns, rows): + if set(stages) != set(expected): + raise ValueError("native control stages differ") + for capture in stages.values(): + if (capture["columns"], capture["rows"]) != (columns, rows): + raise ValueError("native control grid changed") + # No control selects SGR. Compare all cells against the initial attribute; + # this checks preservation, without choosing a portable default color. + attribute = stages["before"]["cells"][0][0][1] + results = {} + for name, target in expected.items(): + actual = stages[name] + wanted = [[(ord(character), attribute) for character in line.ljust(columns)] + for line in target["lines"]] + fields = {"text": actual["lines"] == target["lines"], + "cell_text": actual["cell_lines"] == target["lines"], + "cells_and_attributes": actual["cells"] == wanted, + "cursor": [actual["cursor_row"], actual["cursor_column"]] == target["cursor"], + "viewport": actual["top"] == 0} + results[name] = dict(fields, passed=all(fields.values())) + return {"stages": results, "passed": all(row["passed"] for row in results.values()), + "initial_native_attribute": attribute, "full_r2_contract_verified": False} + + +def evaluate_native(stages, case, columns, rows): + return _evaluate(stages, expected_native(case, columns, rows), columns, rows) + + +def matches_unpatched_prediction(stages, case, columns, rows): + expected = expected_native(case, columns, rows) + if case != "main-only": + # With the main region still active, LF at the physical bottom clamps + # instead of scrolling. ALT_DONE overwrites ALT_LAST; ALT_TOP remains. + expected["alt2" if case == "repeat-alt" else "alt"] = { + "lines": ["ALT_TOP"] + [""] * (rows - 2) + ["ALT_DONE"], "cursor": [rows - 1, 8]} + if case == "repeat-alt": + expected["final"] = {"lines": ["TOP_FIXED", "REGION_A", "REGION_B", "REGION_CIXED", "REGION_D", "CTRL_DONE"] + [""] * (rows - 6), + "cursor": [6, 0]} + return _evaluate(stages, expected, columns, rows)["passed"] diff --git a/experiments/conpty/margin_controls_probe.cpp b/experiments/conpty/margin_controls_probe.cpp new file mode 100644 index 00000000..6e6b6069 --- /dev/null +++ b/experiments/conpty/margin_controls_probe.cpp @@ -0,0 +1,103 @@ +// Independent native controls for the fixed-source ConPTY margin experiment. MIT. +#include +#include +#include +#include +#include +#include +#include +#include "session.hpp" +#include "../shell/workflow_fixture.hpp" + +static void require(bool ok, const char* message) { + if (!ok) throw std::runtime_error(message); +} +static void save(const std::filesystem::path& path, const std::string& bytes) { + std::ofstream stream(path, std::ios::binary); + stream.write(bytes.data(), bytes.size()); stream.close(); + require(bool(stream), "control artifact write failed"); +} +static void write(const std::string& bytes) { + DWORD count = 0; + require(WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), bytes.data(), + static_cast(bytes.size()), &count, nullptr) && count == bytes.size(), + "control output failed"); +} +static std::vector> stages(const std::wstring& name, int rows) { + require(name == L"main-only" || name == L"default-alt" || name == L"repeat-alt", "fixed control required"); + std::vector> result{{"before", + "\x1b[2J\x1b[HTOP_FIXED\r\nREGION_A\r\nREGION_B\r\nBOTTOM_FIXED\r\n\x1b[2;3r"}}; + if (name != L"main-only") { + if (name == L"repeat-alt") result.emplace_back("alt1", + "\x1b[?1049hALT_OUTSIDE\r\n\x1b[4;1HALT_A\r\nALT_B\x1b[4;5r\x1b[5;1H\nALT_C\nALT_D"); + result.emplace_back(name == L"repeat-alt" ? "alt2" : "alt", + "\x1b[?1049h\x1b[HALT_TOP\x1b[" + std::to_string(rows) + ";1HALT_LAST\r\nALT_DONE"); + result.emplace_back("restored", name == L"repeat-alt" ? "\x1b[?1049l\x1b[?1049l" : "\x1b[?1049l"); + } + result.emplace_back("final", "\x1b[3;1H\nREGION_C\nREGION_D\x1b[6;1HCTRL_DONE\r\n"); + return result; +} +static void client(const std::wstring& name, const std::filesystem::path& directory, int rows) { + require(SetConsoleOutputCP(CP_UTF8) && SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), + ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING), + "control mode failed"); + std::string source; + for (const auto& stage : stages(name, rows)) { + write(stage.second); source += stage.second; + tf_shell_test::WorkflowOptions options; + options.capture = directory / stage.first; options.generation = 1; + tf_shell_test::workflow_capture_console(options, stage.first); + } + save(directory / "source.bin", source); +} + +int wmain(int argc, wchar_t** argv) { + try { + require(argc == 6, "control arguments required"); + const bool wide = std::wstring(argv[4]) == L"80" && std::wstring(argv[5]) == L"24"; + const bool narrow = std::wstring(argv[4]) == L"12" && std::wstring(argv[5]) == L"7"; + require(wide || narrow, "fixed control grid required"); + const COORD size{static_cast(wide ? 80 : 12), static_cast(wide ? 24 : 7)}; + const bool child = std::wstring(argv[1]) == L"--client"; + const std::wstring name = argv[child ? 2 : 1]; + (void)stages(name, size.Y); // Reject an unknown workload before acquiring resources. + BOOL owned = FALSE; + const auto directory = std::filesystem::absolute(argv[3]); + if (child) { + require(IsProcessInJob(GetCurrentProcess(), nullptr, &owned) && owned, "owned Job required"); + client(name, directory, size.Y); return 37; + } + + // The owner assigns this host to its Job before allowing any backend + // load or child creation. This retains the original bounded GO gate. + char go[4]{}; size_t used = 0; + while (used < sizeof(go)) { + DWORD count = 0; + if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), go + used, 1, &count, nullptr) || count != 1) break; + if (go[used++] == '\n') break; + } + require(std::string(go, used) == "GO\n" || std::string(go, used) == "GO\r\n", "GO required"); + require(IsProcessInJob(GetCurrentProcess(), nullptr, &owned) && owned, "owned Job required"); + auto backend = tf_conpty::Backend::reviewed_source_side_by_side(argv[2]); + wchar_t self[32768]{}; + const DWORD length = GetModuleFileNameW(nullptr, self, 32768); + require(length && length < 32768, "control executable path failed"); + tf_conpty::Session session(backend); + session.start(self, L"--client " + name + L" \"" + directory.wstring() + L"\" " + + argv[4] + L" " + argv[5], {}, {}, size); + const bool waited = session.wait_exit(5000); + save(directory / "transport.prefix.bin", session.output()); + session.close(); + save(directory / "transport.bin", session.output()); + std::cout << std::boolalpha << "{\"waited\":" << waited << ",\"side_by_side\":true" + << ",\"creation_flags\":" << session.backend_creation_flags() + << ",\"exit_code\":" << session.exit_code << ",\"read_error\":" << session.read_error + << ",\"capture_overflow\":" << session.capture_overflow + << ",\"forced_reader_cancel\":" << session.forced_reader_cancel + << ",\"forced_process_cancel\":" << session.forced_process_cancel + << ",\"cleanup_complete\":" << session.cleanup_complete + << ",\"active_children_after\":" << session.active_after + << ",\"backend_identity\":\"" << session.backend_identity() << "\"}\n"; + return waited && session.exit_code == 37 && session.cleanup_complete ? 0 : 1; + } catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 2; } +} diff --git a/experiments/conpty/results/2026-10-27-source-margin-controls/validation.json b/experiments/conpty/results/2026-10-27-source-margin-controls/validation.json new file mode 100644 index 00000000..5fb7e04f --- /dev/null +++ b/experiments/conpty/results/2026-10-27-source-margin-controls/validation.json @@ -0,0 +1,1667 @@ +{ + "schema": "termforge-source-margin-controls-v1", + "upstream_source": "dc8ae365847d41f62d1ebca93815bb00959bc12f", + "generated_utc": "2026-10-27T05:46:36.962112+00:00", + "build_run": "40449292015", + "source": "edec26bb3c4d04858a667a96debf69c139476172", + "working_tree_dirty": false, + "source_files": { + "CMakeLists.txt": "9afd4590855f90d0e467824a49fc75a6f24b0d88ba7f4ff4ad92764239b682b3", + "experiments/conpty/backend.hpp": "af672718af8c05e90a26576e317b6ac9295fbee76f3f0298371060a9d167f5bb", + "experiments/conpty/session.hpp": "242ac8aa13f3fd56675b2ddefaaf207e5f4c8d3ef8e703260aaed2f0c41677aa", + "experiments/conpty/test_backend.py": "ab371ff7fbf196061451b96009a8260afb35473c1d05b2f99dda66e2b6a9bbba", + "experiments/conpty/margin_controls_probe.cpp": "bf4193ed091b83b50f095623c9bbc406b63646e92dfac552cae1818fd9fc1a18", + "experiments/conpty/margin_controls.py": "7bab11200188a30eaeac4de56b2f35a365def83651fe19e17e54e43e2740060c", + "experiments/conpty/source_margin_controls.py": "b1159026480e52fde97f7b0e36824bded9fb394f70846c94bffa767e063b3df6", + "experiments/conpty/margin_run.py": "99e6caf18224ce7b41307355066db70eacb810bc0626b15d3af09c9a89439ed2", + "experiments/conpty/source_margin_run.py": "26d07c9f912b1bb68b89d33a989ee80189230002869df83a572a22e7b2f6f657", + "experiments/conpty/run.py": "c8eef1e36b6d200b2d508cef91eaa5ca79a91fa87c5daf6265b2475c31dce329", + "experiments/candidates/alacritty/run.py": "2d83240506bf03b4da92f7b2dc85adf40446aff25cd0f0dbe08132b611c2627e", + "experiments/shell/workflow_fixture.hpp": "332d73699320a6f503fc212417a4d3820b82cc2d3feb3d1fffe3930660d57453", + "experiments/shell/workflow_checks.py": "f186ff0f5335c26fc5c4f0286961981dfd6af8fbaafc789e2bc50058486efa44" + }, + "platform": "Windows-11-10.0.26200-SP0", + "probe_sha256": "a71c94795806a40062ace7788ee69cfbac0cd8a274765dce6b28aa6d7f33c7ee", + "trace_sha256": "5941d4569d0ae146eb005f12ecfb15014bc06c18ee48d1c9460824ab65df93d3", + "pair_receipts": { + "baseline": { + "schema": "termforge-upstream-conpty-variant-v1", + "upstream_source": "dc8ae365847d41f62d1ebca93815bb00959bc12f", + "workflow_run": "40449292015", + "production_patch_applied": false, + "focused_adapter_result": "expected-failure", + "full_adapter_verified": false, + "patch_sha256": "dc27377e399bd0217a0b76e16cc89f087c1b781929e4c958ad30c3a475322b81", + "files": { + "OpenConsole.exe": { + "sha256": "fabdc934d9755c405fe848251905e8c0e70a2bf7702b8ed7ccd104eec6591588", + "bytes": 1146880 + }, + "conpty.dll": { + "sha256": "9d165745cce4b29a5818a45c23f68e962b3a3140c2e00566bd17fee970fdd9d1", + "bytes": 108544 + } + } + }, + "patched": { + "schema": "termforge-upstream-conpty-variant-v1", + "upstream_source": "dc8ae365847d41f62d1ebca93815bb00959bc12f", + "workflow_run": "40449292015", + "production_patch_applied": true, + "focused_adapter_result": "passed", + "full_adapter_verified": true, + "patch_sha256": "dc27377e399bd0217a0b76e16cc89f087c1b781929e4c958ad30c3a475322b81", + "files": { + "OpenConsole.exe": { + "sha256": "1c8f071f6e4779ec297fb041af4eaea39be29b6920cef625ab91d1cbc20c79cb", + "bytes": 1147392 + }, + "conpty.dll": { + "sha256": "9d165745cce4b29a5818a45c23f68e962b3a3140c2e00566bd17fee970fdd9d1", + "bytes": 108544 + } + } + } + }, + "deadlines": { + "child_seconds": 5, + "outer_seconds": 15, + "cleanup_seconds": 5 + }, + "planned_executions": 12, + "comparisons": [ + { + "case": "main-only", + "columns": 80, + "rows": 24, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + }, + { + "case": "default-alt", + "columns": 80, + "rows": 24, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + }, + { + "case": "repeat-alt", + "columns": 80, + "rows": 24, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + }, + { + "case": "main-only", + "columns": 12, + "rows": 7, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + }, + { + "case": "default-alt", + "columns": 12, + "rows": 7, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + }, + { + "case": "repeat-alt", + "columns": 12, + "rows": 7, + "baseline_matches_prediction": true, + "patched_native_passed": true, + "both_terminal_finals_passed": true, + "passed": true + } + ], + "passed": true, + "full_r2_contract_verified": false, + "product_backend_selected": false, + "presented_done": null, + "unmeasured": [ + "styled_source", + "history_stress", + "resize", + "queries_and_api_writes", + "terminal_stage_cursors", + "full_product", + "performance", + "WindTerm_equivalence" + ], + "raw_summary_sha256": "49a51e9fe31591c689203b54eedf5a7b75c93db5fb74a4062d1ddbdf00732004", + "build": { + "generator": "Visual Studio 17 2022", + "compiler": "MSVC 19.42.34433.0", + "sdk": "10.0.26100.0", + "configuration": "Release x64", + "source_experiment": true, + "candidate_adapter": false, + "product_backend_dir": "", + "build_testing": false, + "target": "conpty_source_margin_controls", + "configure_log_sha256": "80e9aa25bb3337065aa412f06d5a7e652814a181df9128b8d2fbf86db9b493f6", + "build_log_sha256": "20716d99571de46a12aedb7431f6eff87ed76668e0c444e7adc30780c5daf56c", + "cmake_cache_sha256": "45df1ee5960f8231049fc23a5eb3cd84297c696c747005ff8b5af60a3ed65be6" + }, + "checks": { + "margin_regressions": { + "tests": 17, + "passed": 17, + "skipped": 0, + "seconds": 0.375, + "stdout_log_sha256": "7090c04a72c484bcf76445e60d14136db7b247111a69552ae0abce8135e2f030" + }, + "loader_regressions": { + "tests": 14, + "passed": 14, + "skipped": 0, + "seconds": 1.02, + "stdout_log_sha256": "6b49c108d84db78da015c7b6f95d8691216ee7d50510f7f4bf80157942dc244e" + }, + "original_margin_probe_sha256": "060abdbb7e4c885f77e2617fe0ca1f5b7e0550d6c3a851f918d1836b3afb6b83", + "original_backend_probe_sha256": "2d921cc7d82f912ab87a9a6d0c73d9e6e7acfcde00d0d88020d3997d5732d1c7", + "binary_source_files_unchanged_since_parent": true, + "original_probe_build_scope": "Reused the preceding build; the native probe source and shared headers are unchanged. The current Python harness was executed." + }, + "samples": { + "baseline-80x24-main-only": { + "status": "completed", + "case": "main-only", + "columns": 80, + "rows": 24, + "source_bytes": 99, + "source_sha256": "562efe17a5fc38ef1f6c0d2178909529d410a564153af2096fd65385774b7d78", + "transport_bytes": 140, + "transport_sha256": "298ef170a31e6b682bd03e6df50fa63501bba6a4d50b93068d238de5b11c98d6", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "44791e13f43dc0e3d8f5f85e3a0fe43d0efd140d92c44874183ae37ba75b6805", + "console.bin": "b397c51fdcda4e4c05dd0f99994518461c063798e3485b36f1fa7922902116d8", + "cells.bin": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + } + }, + "transport_prefix_sha256": "bfc803917123cd135ebef7399d2eaa9a15617916a02f52548cb612df01e954a3", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-80x24-main-only": { + "status": "completed", + "case": "main-only", + "columns": 80, + "rows": 24, + "source_bytes": 99, + "source_sha256": "562efe17a5fc38ef1f6c0d2178909529d410a564153af2096fd65385774b7d78", + "transport_bytes": 140, + "transport_sha256": "298ef170a31e6b682bd03e6df50fa63501bba6a4d50b93068d238de5b11c98d6", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "44791e13f43dc0e3d8f5f85e3a0fe43d0efd140d92c44874183ae37ba75b6805", + "console.bin": "b397c51fdcda4e4c05dd0f99994518461c063798e3485b36f1fa7922902116d8", + "cells.bin": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + } + }, + "transport_prefix_sha256": "bfc803917123cd135ebef7399d2eaa9a15617916a02f52548cb612df01e954a3", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + }, + "baseline-80x24-default-alt": { + "status": "completed", + "case": "default-alt", + "columns": 80, + "rows": 24, + "source_bytes": 150, + "source_sha256": "486f50c60cfcaac7c591c4cd943583cc547c94f3ba1d3b8a8b316772d6af12da", + "transport_bytes": 191, + "transport_sha256": "b1f24ee51249c3d8a3e6a5d354f61f69f9e23605256b7191d44f91be5f7c9224", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": false, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt": [ + 23, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "alt": "9061f9f82c6518dc6a37a4ba79a7d03517dfe401fab8bca6d11d7e2ae4710221", + "restored": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "alt": { + "console.json": "afb696fe20079f214ebe0f3daf9fccbeab3dbfd56219d6d9b68271f5e850b70e", + "console.bin": "65e6b3bccf2aed76d811477456d87c1a0bdacdb8c956dbf292fbab220c874097", + "cells.bin": "9061f9f82c6518dc6a37a4ba79a7d03517dfe401fab8bca6d11d7e2ae4710221" + }, + "restored": { + "console.json": "5c4c6f2da1d5464f63e7d5b5cc5156e681e00febf620fcc7bed5767947d0b1b4", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "44791e13f43dc0e3d8f5f85e3a0fe43d0efd140d92c44874183ae37ba75b6805", + "console.bin": "b397c51fdcda4e4c05dd0f99994518461c063798e3485b36f1fa7922902116d8", + "cells.bin": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + } + }, + "transport_prefix_sha256": "1d5d72c9e266d4bc59901ea1361c5c5be4a19191d67ea6283b74ff722e587982", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-80x24-default-alt": { + "status": "completed", + "case": "default-alt", + "columns": 80, + "rows": 24, + "source_bytes": 150, + "source_sha256": "486f50c60cfcaac7c591c4cd943583cc547c94f3ba1d3b8a8b316772d6af12da", + "transport_bytes": 191, + "transport_sha256": "b1f24ee51249c3d8a3e6a5d354f61f69f9e23605256b7191d44f91be5f7c9224", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": false, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt": [ + 23, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "alt": "ec772cf0abe15840349a8fe5cbe9e18c4746b4fda2c32f17062178386d4b6273", + "restored": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "alt": { + "console.json": "0c5fd107f5887d96aab42e833da5d377634051fdb81dcaca9ede1c17f5bb80e4", + "console.bin": "3b80a0147a3c299def30a9956a2fe30be0e769c3eaf6d345c099803e511d4920", + "cells.bin": "ec772cf0abe15840349a8fe5cbe9e18c4746b4fda2c32f17062178386d4b6273" + }, + "restored": { + "console.json": "5c4c6f2da1d5464f63e7d5b5cc5156e681e00febf620fcc7bed5767947d0b1b4", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "44791e13f43dc0e3d8f5f85e3a0fe43d0efd140d92c44874183ae37ba75b6805", + "console.bin": "b397c51fdcda4e4c05dd0f99994518461c063798e3485b36f1fa7922902116d8", + "cells.bin": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + } + }, + "transport_prefix_sha256": "1d5d72c9e266d4bc59901ea1361c5c5be4a19191d67ea6283b74ff722e587982", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + }, + "baseline-80x24-repeat-alt": { + "status": "completed", + "case": "repeat-alt", + "columns": 80, + "rows": 24, + "source_bytes": 221, + "source_sha256": "1e618a0c2d70bf177e8c58afba8aba50242da0eeff03cebf2f24a417cc31d83d", + "transport_bytes": 264, + "transport_sha256": "5db1cc1c0ebc73c00368561e5b9a3b7aa49587c1970dc0fdcb9ffd42aee6941f", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt1": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt2": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + } + }, + "passed": false, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt1": [ + 4, + 5 + ], + "alt2": [ + 23, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "alt1": "0fd582bf6115b38b1dbe57084658f686392c74b4d6b1340f945cfcbca4ff7635", + "alt2": "9061f9f82c6518dc6a37a4ba79a7d03517dfe401fab8bca6d11d7e2ae4710221", + "restored": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "27bb6f98b11712daa002382b58f0157f1b4b1026e97b48460f65422986b01c73" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "alt1": { + "console.json": "23d40e6afe96877aebb9506bfa4b14f09047e0c68c861d2a19b20dd96baaf993", + "console.bin": "f6492387ed4760be9851cccd504ac8f6f21624d55dce9671b3be4560ada0b5ac", + "cells.bin": "0fd582bf6115b38b1dbe57084658f686392c74b4d6b1340f945cfcbca4ff7635" + }, + "alt2": { + "console.json": "90e7b9d0dbbdaca888d20bb89c3599578e2c894eab5b797580776f5b04e0b7fc", + "console.bin": "65e6b3bccf2aed76d811477456d87c1a0bdacdb8c956dbf292fbab220c874097", + "cells.bin": "9061f9f82c6518dc6a37a4ba79a7d03517dfe401fab8bca6d11d7e2ae4710221" + }, + "restored": { + "console.json": "5c4c6f2da1d5464f63e7d5b5cc5156e681e00febf620fcc7bed5767947d0b1b4", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "bed3585f74d24e6466a540ba266c247da766211b7fdda0c97c4a82986582ba9c", + "console.bin": "d80cd19d088c244593a32ae4eac1d3b66bb0bf6c93841c208535cec6f29fed55", + "cells.bin": "27bb6f98b11712daa002382b58f0157f1b4b1026e97b48460f65422986b01c73" + } + }, + "transport_prefix_sha256": "3f35d4d6e53642c5763e1627b717fe49c47efd6633f6a8890aa78351cc2d9b34", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-80x24-repeat-alt": { + "status": "completed", + "case": "repeat-alt", + "columns": 80, + "rows": 24, + "source_bytes": 221, + "source_sha256": "1e618a0c2d70bf177e8c58afba8aba50242da0eeff03cebf2f24a417cc31d83d", + "transport_bytes": 264, + "transport_sha256": "5db1cc1c0ebc73c00368561e5b9a3b7aa49587c1970dc0fdcb9ffd42aee6941f", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt1": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt2": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": false, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt1": [ + 4, + 5 + ], + "alt2": [ + 23, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "alt1": "0fd582bf6115b38b1dbe57084658f686392c74b4d6b1340f945cfcbca4ff7635", + "alt2": "ec772cf0abe15840349a8fe5cbe9e18c4746b4fda2c32f17062178386d4b6273", + "restored": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651", + "final": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "2cc9905011e52755ad390a7ef9ea8c84ced519ae8272f6806e0d6c0c0801d328", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "alt1": { + "console.json": "23d40e6afe96877aebb9506bfa4b14f09047e0c68c861d2a19b20dd96baaf993", + "console.bin": "f6492387ed4760be9851cccd504ac8f6f21624d55dce9671b3be4560ada0b5ac", + "cells.bin": "0fd582bf6115b38b1dbe57084658f686392c74b4d6b1340f945cfcbca4ff7635" + }, + "alt2": { + "console.json": "170bfa243bf9568e843cd045a5577ff17055cd73282eeb70657e9758867cd387", + "console.bin": "3b80a0147a3c299def30a9956a2fe30be0e769c3eaf6d345c099803e511d4920", + "cells.bin": "ec772cf0abe15840349a8fe5cbe9e18c4746b4fda2c32f17062178386d4b6273" + }, + "restored": { + "console.json": "5c4c6f2da1d5464f63e7d5b5cc5156e681e00febf620fcc7bed5767947d0b1b4", + "console.bin": "4279b6605b0fb08728ce75e50e7b24eaf2d75e39e3d44b12b0f41d2e6ba8343b", + "cells.bin": "fe9016196e03b264cce22f628f67b778fe1e6a3aeb160ed53effc153b1ff6651" + }, + "final": { + "console.json": "44791e13f43dc0e3d8f5f85e3a0fe43d0efd140d92c44874183ae37ba75b6805", + "console.bin": "b397c51fdcda4e4c05dd0f99994518461c063798e3485b36f1fa7922902116d8", + "cells.bin": "cd50199bb35507012250c82e89282232990e58c8dfa12d014002837520780135" + } + }, + "transport_prefix_sha256": "3f35d4d6e53642c5763e1627b717fe49c47efd6633f6a8890aa78351cc2d9b34", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + }, + "baseline-12x7-main-only": { + "status": "completed", + "case": "main-only", + "columns": 12, + "rows": 7, + "source_bytes": 99, + "source_sha256": "562efe17a5fc38ef1f6c0d2178909529d410a564153af2096fd65385774b7d78", + "transport_bytes": 140, + "transport_sha256": "298ef170a31e6b682bd03e6df50fa63501bba6a4d50b93068d238de5b11c98d6", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "7e0a4e914d3163ba1eed10904852c8fb8a392c585f53612d2e96395c65cb5931", + "console.bin": "ae750de80c88d1345983e4ff3af5c96f9d83f550efe08480233b9d4f593b0465", + "cells.bin": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + } + }, + "transport_prefix_sha256": "bfc803917123cd135ebef7399d2eaa9a15617916a02f52548cb612df01e954a3", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-12x7-main-only": { + "status": "completed", + "case": "main-only", + "columns": 12, + "rows": 7, + "source_bytes": 99, + "source_sha256": "562efe17a5fc38ef1f6c0d2178909529d410a564153af2096fd65385774b7d78", + "transport_bytes": 140, + "transport_sha256": "298ef170a31e6b682bd03e6df50fa63501bba6a4d50b93068d238de5b11c98d6", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "7e0a4e914d3163ba1eed10904852c8fb8a392c585f53612d2e96395c65cb5931", + "console.bin": "ae750de80c88d1345983e4ff3af5c96f9d83f550efe08480233b9d4f593b0465", + "cells.bin": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + } + }, + "transport_prefix_sha256": "bfc803917123cd135ebef7399d2eaa9a15617916a02f52548cb612df01e954a3", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + }, + "baseline-12x7-default-alt": { + "status": "completed", + "case": "default-alt", + "columns": 12, + "rows": 7, + "source_bytes": 149, + "source_sha256": "f454182447e992f9384e00b8754b1650068dad69304ea0d61815b049f1e1e212", + "transport_bytes": 190, + "transport_sha256": "f0241ef4bb6c24d2ab43143d810209559fff24a66461e4321113588857f3d243", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": false, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt": [ + 6, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "alt": "2bb287dce6b1d142599046e30596417ed0ea1abdff31f81471cb2c03fe46e34a", + "restored": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "alt": { + "console.json": "a80a93e5a1c401273bd9ec9776fa022aad8b3dd409d3ec0285aafb5126bed46e", + "console.bin": "be8621c984e3f28209bbc97d377e78b8ecd01ca4f2600129c72181f0230f7744", + "cells.bin": "2bb287dce6b1d142599046e30596417ed0ea1abdff31f81471cb2c03fe46e34a" + }, + "restored": { + "console.json": "d846fba64eedf13310f9b819d2ef8d5cec418b79617be1aaab3ebee496de85c2", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "7e0a4e914d3163ba1eed10904852c8fb8a392c585f53612d2e96395c65cb5931", + "console.bin": "ae750de80c88d1345983e4ff3af5c96f9d83f550efe08480233b9d4f593b0465", + "cells.bin": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + } + }, + "transport_prefix_sha256": "6f36d5c80521c6b16edeb5255b5ee7c76140ac9b6e953ff0af7f0256fd01b325", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-12x7-default-alt": { + "status": "completed", + "case": "default-alt", + "columns": 12, + "rows": 7, + "source_bytes": 149, + "source_sha256": "f454182447e992f9384e00b8754b1650068dad69304ea0d61815b049f1e1e212", + "transport_bytes": 190, + "transport_sha256": "f0241ef4bb6c24d2ab43143d810209559fff24a66461e4321113588857f3d243", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": false, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt": [ + 6, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "alt": "17469e5eb0c1cfa2577d55e2f5870e4d478f650f2d4a9d72c330094bec23ccc6", + "restored": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "alt": { + "console.json": "2ee82c69922b50787b528fae0134f67dfa2e50226f4c5d14ffef428873fb9976", + "console.bin": "127f85391d796833c9f5e9a6ac970ab4fa3fc369ebfb8df5214291504940c6a1", + "cells.bin": "17469e5eb0c1cfa2577d55e2f5870e4d478f650f2d4a9d72c330094bec23ccc6" + }, + "restored": { + "console.json": "d846fba64eedf13310f9b819d2ef8d5cec418b79617be1aaab3ebee496de85c2", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "7e0a4e914d3163ba1eed10904852c8fb8a392c585f53612d2e96395c65cb5931", + "console.bin": "ae750de80c88d1345983e4ff3af5c96f9d83f550efe08480233b9d4f593b0465", + "cells.bin": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + } + }, + "transport_prefix_sha256": "6f36d5c80521c6b16edeb5255b5ee7c76140ac9b6e953ff0af7f0256fd01b325", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + }, + "baseline-12x7-repeat-alt": { + "status": "completed", + "case": "repeat-alt", + "columns": 12, + "rows": 7, + "source_bytes": 220, + "source_sha256": "2b68cea085a519f73306b02b702c685aa0dba44d780c8619e0e3ef7b202d1dcd", + "transport_bytes": 263, + "transport_sha256": "7859bdb124e0b8ed7858a5d090648dc30948d81c56948c0942313cead0e79161", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt1": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt2": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": false, + "cell_text": false, + "cells_and_attributes": false, + "cursor": true, + "viewport": true, + "passed": false + } + }, + "passed": false, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": true, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt1": [ + 4, + 5 + ], + "alt2": [ + 6, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "alt1": "c7b9b5456a0566673db0a45765f1de63ac3ae76501da1be8cc3f0aadf848b3b7", + "alt2": "2bb287dce6b1d142599046e30596417ed0ea1abdff31f81471cb2c03fe46e34a", + "restored": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "ca4f1e7fdd65663604cfe2eb8c50a838839adac9ac7bcf10fd22a342058efba7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-baseline-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "alt1": { + "console.json": "c9144171f486448d88de93fc4f6f4d0bc602a77b5b748136f34757b4d638bdc8", + "console.bin": "c2e8954d37be9bfc2b600951255d9410a9873e9259b7980ed41785de97e863c3", + "cells.bin": "c7b9b5456a0566673db0a45765f1de63ac3ae76501da1be8cc3f0aadf848b3b7" + }, + "alt2": { + "console.json": "2253a902f36fc1d407ab5d72faf6d928f6ee8df10f5c947a0d27a24d2e6259de", + "console.bin": "be8621c984e3f28209bbc97d377e78b8ecd01ca4f2600129c72181f0230f7744", + "cells.bin": "2bb287dce6b1d142599046e30596417ed0ea1abdff31f81471cb2c03fe46e34a" + }, + "restored": { + "console.json": "d846fba64eedf13310f9b819d2ef8d5cec418b79617be1aaab3ebee496de85c2", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "f318dcc781710db4d4695dd2906b401edf6d44710d30d5d5a59574b29b4fb58c", + "console.bin": "ad734bcaaac7c400337e93737f2a3c0f99993bbd7e0bdd8ca80278b1e0ef386a", + "cells.bin": "ca4f1e7fdd65663604cfe2eb8c50a838839adac9ac7bcf10fd22a342058efba7" + } + }, + "transport_prefix_sha256": "ef90cd86ca36da979fe06820f50ec8695a0ed86b46288f6de74ab8a07bef8e6e", + "supervision_summary_sha256": "661250217a88ffca8b79f59e301f5d89766fde825a36a274ab0090f5ec7552b5" + }, + "patched-12x7-repeat-alt": { + "status": "completed", + "case": "repeat-alt", + "columns": 12, + "rows": 7, + "source_bytes": 220, + "source_sha256": "2b68cea085a519f73306b02b702c685aa0dba44d780c8619e0e3ef7b202d1dcd", + "transport_bytes": 263, + "transport_sha256": "7859bdb124e0b8ed7858a5d090648dc30948d81c56948c0942313cead0e79161", + "native_contract": { + "stages": { + "before": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt1": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "alt2": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "restored": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + }, + "final": { + "text": true, + "cell_text": true, + "cells_and_attributes": true, + "cursor": true, + "viewport": true, + "passed": true + } + }, + "passed": true, + "initial_native_attribute": 7, + "full_r2_contract_verified": false + }, + "unpatched_prediction_matches": false, + "native_cursor": { + "before": [ + 0, + 0 + ], + "alt1": [ + 4, + 5 + ], + "alt2": [ + 6, + 8 + ], + "restored": [ + 0, + 0 + ], + "final": [ + 6, + 0 + ] + }, + "native_cell_sha256": { + "before": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "alt1": "c7b9b5456a0566673db0a45765f1de63ac3ae76501da1be8cc3f0aadf848b3b7", + "alt2": "17469e5eb0c1cfa2577d55e2f5870e4d478f650f2d4a9d72c330094bec23ccc6", + "restored": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3", + "final": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + }, + "terminal_alternate": false, + "terminal_final_passed": true, + "lifecycle": { + "status": "completed", + "failure_code": null, + "returncode": 0, + "outer_job_after": { + "active": 0, + "total": 4, + "terminated": 0 + }, + "outer_job_after_forced_cleanup": null, + "forced_cleanup": false, + "job_assigned": true, + "process_reaped": true, + "streams_drained": true, + "cleanup_errors": [], + "native": { + "waited": true, + "side_by_side": true, + "creation_flags": 0, + "exit_code": 37, + "read_error": 109, + "capture_overflow": false, + "forced_reader_cancel": false, + "forced_process_cancel": false, + "cleanup_complete": true, + "active_children_after": 0, + "backend_identity": "source-dc8-r56-patched-run40449292015" + }, + "error": null + }, + "native_stage_files": { + "before": { + "console.json": "0ff49b4ca941a2569ba6b6e88be65a2d371bdeebb1ec02c1028c720d84a74e6e", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "alt1": { + "console.json": "c9144171f486448d88de93fc4f6f4d0bc602a77b5b748136f34757b4d638bdc8", + "console.bin": "c2e8954d37be9bfc2b600951255d9410a9873e9259b7980ed41785de97e863c3", + "cells.bin": "c7b9b5456a0566673db0a45765f1de63ac3ae76501da1be8cc3f0aadf848b3b7" + }, + "alt2": { + "console.json": "1d0cce7996e41de52f5399433caa8a2397c19a8ab5caff37a072fce707e7d695", + "console.bin": "127f85391d796833c9f5e9a6ac970ab4fa3fc369ebfb8df5214291504940c6a1", + "cells.bin": "17469e5eb0c1cfa2577d55e2f5870e4d478f650f2d4a9d72c330094bec23ccc6" + }, + "restored": { + "console.json": "d846fba64eedf13310f9b819d2ef8d5cec418b79617be1aaab3ebee496de85c2", + "console.bin": "f3f613e2347734671af75bb4747b55ebc89ea0c2b1994b5efd7a86152aa80b10", + "cells.bin": "323107891dd2f32c2be1fecd7ae03379c96f8fe38eff938ece8f19eeebe6dbf3" + }, + "final": { + "console.json": "7e0a4e914d3163ba1eed10904852c8fb8a392c585f53612d2e96395c65cb5931", + "console.bin": "ae750de80c88d1345983e4ff3af5c96f9d83f550efe08480233b9d4f593b0465", + "cells.bin": "3143631df305f659111e37d5a4a81be0b4c1ad63957cc73c3a162f7ec1c782d7" + } + }, + "transport_prefix_sha256": "ef90cd86ca36da979fe06820f50ec8695a0ed86b46288f6de74ab8a07bef8e6e", + "supervision_summary_sha256": "53233abedb9bc62a5f7bbdc241403891ed825b16572c6ea9c37c89ffe3189cb5" + } + } +} diff --git a/experiments/conpty/source_margin_controls.py b/experiments/conpty/source_margin_controls.py new file mode 100644 index 00000000..30955187 --- /dev/null +++ b/experiments/conpty/source_margin_controls.py @@ -0,0 +1,149 @@ +"""One bounded source-pair comparison for three additional native controls.""" +from __future__ import annotations + +import argparse +from datetime import datetime, timezone +import json +from pathlib import Path +import platform +import subprocess + +from experiments.candidates.alacritty.run import run_trace +from experiments.conpty.margin_controls import ( + evaluate_native, expected_native, expected_source, matches_unpatched_prediction, +) +from experiments.conpty.margin_run import sha256, validate_lifecycle +from experiments.conpty.source_margin_run import identity, reviewed_pair, ROOT, RUN, UPSTREAM +from experiments.conpty.test_backend import BackendTests +from experiments.shell.workflow_checks import validate_console_window + +CASES = ("main-only", "default-alt", "repeat-alt") +GRIDS = ((80, 24), (12, 7)) + + +def inspect_case(directory, trace, case, columns, rows): + source = (directory / "source.bin").read_bytes() + if source != expected_source(case, columns, rows): + raise ValueError("fixed control source changed") + transport = (directory / "transport.bin").read_bytes() + if not 0 < len(transport) <= 8192: + raise ValueError("control transport bound") + expected = expected_native(case, columns, rows) + states = {stage: validate_console_window(directory, expected_role=stage, + expected_generation=1, prefix=stage) + for stage in expected} + report, records = run_trace(trace, transport, rows, columns, 100, + [len(transport)], directory / "replay") + if report["passed"] is not True: + raise ValueError("control replay rejected") + final = records[-1] + lines = ["".join(cell["text"] for cell in line if cell.get("width", 1)).rstrip() + for line in final["screen"]["cells"]] + alternate = bool(final["mode_bits"] & (1 << 12)) + return {"source_bytes": len(source), "source_sha256": sha256(directory / "source.bin"), + "transport_bytes": len(transport), "transport_sha256": sha256(directory / "transport.bin"), + "native_contract": evaluate_native(states, case, columns, rows), + "unpatched_prediction_matches": matches_unpatched_prediction(states, case, columns, rows), + "native_lines": {stage: row["lines"] for stage, row in states.items()}, + "native_cursor": {stage: [row["cursor_row"], row["cursor_column"]] for stage, row in states.items()}, + "native_cell_sha256": {stage: sha256(directory / (stage + ".cells.bin")) for stage in expected}, + "terminal_lines": lines, "terminal_alternate": alternate, + "terminal_final_passed": lines == expected["final"]["lines"] and not alternate} + + +def compare(samples): + results = [] + for columns, rows in GRIDS: + for case in CASES: + baseline = samples[f"baseline-{columns}x{rows}-{case}"] + patched = samples[f"patched-{columns}x{rows}-{case}"] + completed = all(row.get("status") == "completed" for row in (baseline, patched)) + qualified = (completed and baseline["unpatched_prediction_matches"] and + baseline["native_contract"]["passed"] == (case == "main-only")) + repaired = completed and patched["native_contract"]["passed"] + terminal = completed and all(row["terminal_final_passed"] for row in (baseline, patched)) + results.append({"case": case, "columns": columns, "rows": rows, + "baseline_matches_prediction": qualified, + "patched_native_passed": repaired, "both_terminal_finals_passed": terminal, + "passed": qualified and repaired and terminal}) + return results + + +def run(baseline, patched, probe, trace, output): + pairs = {name: reviewed_pair(folder, name) for name, folder in + (("baseline", baseline), ("patched", patched))} + probe, trace = Path(probe).resolve(strict=True), Path(trace).resolve(strict=True) + output = Path(output).resolve() + output.mkdir(parents=True, exist_ok=False) + subject = BackendTests() + subject.out, subject.serial = output / "_private" / "supervision", 0 + sources = ["CMakeLists.txt", "experiments/conpty/backend.hpp", "experiments/conpty/session.hpp", + "experiments/conpty/test_backend.py", "experiments/conpty/margin_controls_probe.cpp", + "experiments/conpty/margin_controls.py", "experiments/conpty/source_margin_controls.py", + "experiments/conpty/margin_run.py", "experiments/conpty/source_margin_run.py", + "experiments/conpty/run.py", "experiments/candidates/alacritty/run.py", + "experiments/shell/workflow_fixture.hpp", "experiments/shell/workflow_checks.py"] + summary = {"schema": "termforge-source-margin-controls-v1", "upstream_source": UPSTREAM, + "generated_utc": datetime.now(timezone.utc).isoformat(), "build_run": RUN, + "source": subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip(), + "working_tree_dirty": bool(subprocess.check_output(["git", "status", "--porcelain"], cwd=ROOT)), + "source_files": {name: sha256(ROOT / name) for name in sources}, + "platform": platform.platform(), "probe_sha256": sha256(probe), "trace_sha256": sha256(trace), + "pair_receipts": {name: receipt for name, (_, receipt) in pairs.items()}, + "deadlines": {"child_seconds": 5, "outer_seconds": 15, "cleanup_seconds": 5}, + "planned_executions": 12, "samples": {}, "comparisons": [], "passed": False, + "full_r2_contract_verified": False, "product_backend_selected": False, + "presented_done": None, + "unmeasured": ["styled_source", "history_stress", "resize", "queries_and_api_writes", + "terminal_stage_cursors", "full_product", "performance", "WindTerm_equivalence"]} + + def save(): + (output / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8") + + try: + # Exactly one execution per planned cell; failed cells remain failed. + for columns, rows in GRIDS: + for case in CASES: + for variant, (directory, _) in pairs.items(): + name = f"{variant}-{columns}x{rows}-{case}" + folder = output / "_private" / name + folder.mkdir(parents=True) + sample = {"status": "failed", "case": case, "columns": columns, "rows": rows, + "supervision_case": subject.serial} + stage = "native_execution" + try: + lifecycle = subject.invoke(directory, case, executable=probe, + arguments=(folder, columns, rows), timeout=15) + sample["lifecycle"] = lifecycle + stage = "lifecycle_validation" + job = lifecycle["outer_job_after"] + if (lifecycle["returncode"] != 0 or lifecycle["error"] is not None or + job["active"] != 0 or job["total"] < 1 or job["terminated"] != 0): + raise ValueError("control native probe failed") + validate_lifecycle(lifecycle["native"], True, backend_identity=identity(variant)) + stage = "capture_and_replay_validation" + sample.update(inspect_case(folder, trace, case, columns, rows), status="completed") + except Exception as error: + sample["error_type"] = type(error).__name__ + sample["failure_stage"] = stage + summary["samples"][name] = sample + save() + summary["comparisons"] = compare(summary["samples"]) + summary["passed"] = all(row["passed"] for row in summary["comparisons"]) + finally: + save() + return summary + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + for name in ("baseline", "patched", "probe", "candidate-trace", "output"): + parser.add_argument("--" + name, type=Path, required=True) + args = parser.parse_args() + result = run(args.baseline, args.patched, args.probe, args.candidate_trace, args.output) + print(json.dumps({"passed": result["passed"], "comparisons": result["comparisons"]})) + return int(not result["passed"]) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/experiments/conpty/test_backend.py b/experiments/conpty/test_backend.py index 47b64cdd..eedd7888 100644 --- a/experiments/conpty/test_backend.py +++ b/experiments/conpty/test_backend.py @@ -21,7 +21,7 @@ def setUp(self): self.out = Path(tempfile.mkdtemp(prefix="run-", dir=base)) self.serial = 0 - def invoke(self, directory, mode="--host", *, executable=None): + def invoke(self, directory, mode="--host", *, executable=None, arguments=(), timeout=20): job = process = None stdout = stderr = b"" result = {"status": "failed", "failure_code": None, "returncode": None, @@ -42,14 +42,15 @@ def cleanup_fault(code): try: job = OwnedJob() stage = "process_start" - process = subprocess.Popen([str(executable or binary("conpty_backend_probe")), mode, str(directory)], + process = subprocess.Popen([str(executable or binary("conpty_backend_probe")), mode, str(directory), + *map(str, arguments)], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, creationflags=subprocess.CREATE_NO_WINDOW) stage = "job_assign" job.assign(process.pid) result["job_assigned"] = True stage = "process_communicate" - stdout, stderr = process.communicate(b"GO\n", timeout=20) + stdout, stderr = process.communicate(b"GO\n", timeout=timeout) result["streams_drained"] = True stage = "stream_evidence" (private / "host.stdout.bin").write_bytes(stdout) diff --git a/experiments/conpty/test_margin_controls.py b/experiments/conpty/test_margin_controls.py new file mode 100644 index 00000000..1a72c8ac --- /dev/null +++ b/experiments/conpty/test_margin_controls.py @@ -0,0 +1,78 @@ +"""Independent literal captures and corruption gates for native margin controls.""" +import copy +import unittest + +from experiments.conpty.margin_controls import evaluate_native, matches_unpatched_prediction + + +def capture(lines, cursor, columns=12, rows=7): + padded = lines + [""] * (rows - len(lines)) + return {"columns": columns, "rows": rows, "top": 0, + "lines": padded, "cell_lines": padded.copy(), + "cells": [[(ord(c), 7) for c in line.ljust(columns)] for line in padded], + "cursor_row": cursor[0], "cursor_column": cursor[1]} + + +class NativeMarginControlTests(unittest.TestCase): + def main_only(self): + return {"before": capture(["TOP_FIXED", "REGION_A", "REGION_B", "BOTTOM_FIXED"], (0, 0)), + "final": capture(["TOP_FIXED", "REGION_C", "REGION_D", "BOTTOM_FIXED", "", "CTRL_DONE"], (6, 0))} + + def test_main_region_preserves_outside_and_rejects_corrupted_fields(self): + original = self.main_only() + self.assertTrue(evaluate_native(original, "main-only", 12, 7)["passed"]) + self.assertTrue(matches_unpatched_prediction(original, "main-only", 12, 7)) + for field in ("lines", "cell_lines", "cells", "cursor_row", "cursor_column", "top"): + changed = copy.deepcopy(original) + if field in ("lines", "cell_lines"): + changed["final"][field][0] = "REGION_A" + elif field == "cells": + changed["final"][field][0][0] = (ord("T"), 4) + else: + changed["final"][field] += 1 + with self.subTest(field=field): + self.assertFalse(evaluate_native(changed, "main-only", 12, 7)["passed"]) + self.assertFalse(matches_unpatched_prediction(changed, "main-only", 12, 7)) + + def test_new_alternate_has_full_screen_margins_and_qualifies_specific_old_failure(self): + sample = self.main_only() + sample["alt"] = capture(["", "", "", "", "", "ALT_LAST", "ALT_DONE"], (6, 8)) + sample["restored"] = copy.deepcopy(sample["before"]) + self.assertTrue(evaluate_native(sample, "default-alt", 12, 7)["passed"]) + self.assertFalse(matches_unpatched_prediction(sample, "default-alt", 12, 7)) + old = copy.deepcopy(sample) + old["alt"] = capture(["ALT_TOP", "", "", "", "", "", "ALT_DONE"], (6, 8)) + result = evaluate_native(old, "default-alt", 12, 7) + self.assertFalse(result["passed"]) + self.assertEqual([name for name, stage in result["stages"].items() if not stage["passed"]], ["alt"]) + self.assertTrue(matches_unpatched_prediction(old, "default-alt", 12, 7)) + old["before"]["cursor_row"] = 1 + self.assertFalse(matches_unpatched_prediction(old, "default-alt", 12, 7)) + + def test_repeated_alternate_and_main_requests_keep_the_hidden_main_region(self): + sample = self.main_only() + sample["alt1"] = capture(["ALT_OUTSIDE", "", "", "ALT_C", "ALT_D"], (4, 5)) + sample["alt2"] = capture(["", "", "", "", "", "ALT_LAST", "ALT_DONE"], (6, 8)) + sample["restored"] = copy.deepcopy(sample["before"]) + self.assertTrue(evaluate_native(sample, "repeat-alt", 12, 7)["passed"]) + self.assertFalse(matches_unpatched_prediction(sample, "repeat-alt", 12, 7)) + old = copy.deepcopy(sample) + old["alt2"] = capture(["ALT_TOP", "", "", "", "", "", "ALT_DONE"], (6, 8)) + old["final"] = capture(["TOP_FIXED", "REGION_A", "REGION_B", "REGION_CIXED", "REGION_D", "CTRL_DONE"], (6, 0)) + result = evaluate_native(old, "repeat-alt", 12, 7) + self.assertEqual([name for name, stage in result["stages"].items() if not stage["passed"]], ["alt2", "final"]) + self.assertTrue(matches_unpatched_prediction(old, "repeat-alt", 12, 7)) + old["restored"]["cells"][0][0] = (ord("T"), 4) + self.assertFalse(matches_unpatched_prediction(old, "repeat-alt", 12, 7)) + + def test_missing_stage_and_wrong_grid_are_rejected(self): + with self.assertRaises(ValueError): + evaluate_native({"before": self.main_only()["before"]}, "main-only", 12, 7) + with self.assertRaises(ValueError): + evaluate_native(self.main_only(), "main-only", 80, 24) + with self.assertRaises(ValueError): + evaluate_native(self.main_only(), "unknown", 12, 7) + + +if __name__ == "__main__": + unittest.main()