Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/EVIDENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ R1 固定总出口的源码为 `ce9b24d`,早期 `2dc8d6d` / `9b16acc` 仍分
| 候选 GUI 合法空标题(#45) | [真实 GUI 前后对照](../experiments/shell/results/2026-10-26-empty-title/validation.json)、[转换入口](../experiments/presentation/render.hpp)、[持续回归](../experiments/shell/candidate_title_test.cpp) | 真实空 OSC 标题使旧 UTF 转换抛异常;修后两 fresh GUI 正常,非法非空反例保留。不回填旧 normal-1 未知异常的根因。 |
| More 尾部 ECH 与捕获时序(#42 限定修复) | [合同及来源](research/more-capture-ech.md)、[修复前后与同输入对照](../experiments/differential/results/2026-10-26-more-ech/validation.json)、[历史触发记录](../experiments/shell/results/2026-10-26-margin-state/validation.json) | Recovery ECH 缺失和旧捕获过早分开修正,同原件 whole/byte 红→绿;完整 More 提示、单 q/自然退出/EOF/Job0 独立核对。候选 pending-wrap 差异仍保留。 |

[#52 native 捕获目录隔离](research/vim-fixture-isolation.md)保留同旧 EXE 的兄弟运行红例、修后完整 cmd/More/Vim 两轮和真实 stale-fixture 反例;GUI stage59 仍有本批真实红例,不能据此关闭 #52。

## 核心路线、参考与历史验证

| 范围 | 结果与复现入口 | 可以支持的结论与限制 |
Expand Down
29 changes: 29 additions & 0 deletions docs/research/vim-fixture-isolation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Vim 捕获的运行目录隔离

[#52](https://github.com/JavaZeroo/termforge/issues/52) 的 native `new-buffer` 和 GUI stage59 是两条独立入口。本页只解释 native 捕获复用旧文件的原因;GUI 仍有真实失败,不能由本修正关闭 #52。

## native 反例与修正

原 `real_app_capture` 在新建输出目录的父目录运行应用。连续两个兄弟 capture 因而共享 `editor.txt`、`editor-startup.txt` 和 `editor-insert.txt`。第一轮 Vim 写盘后,第二轮打开已有的 12 字节文件,实际输出为 `"editor.txt" [unix] 1L, 12B`,原 `[New]` 断言必然失败。旧联合基线失败原件也具有这段输出。失败诊断中的 `editor_exit=91` 来自随后清理,不是 Vim 自然退出故障的证明。

同一旧 EXE、官方 `1.25.260930003`、Console width flags24、同一父目录的两次完整 cmd/More/Vim 捕获已复现第一绿、第二红 `new-buffer`;每次独立父目录的对照均绿。因此,这条失败来自夹具路径复用,不能认定为 Console width 兼容缺陷。

修正将应用工作目录设为本轮刚新建的 capture 目录。相对文件名、事件文件握手、输入字节、`[New]`、写盘内容、退出、真实 reader EOF 与外层 Job 检查均保留。兄弟运行和父目录已有文件不再影响本轮,也不会删除或覆盖父目录的文件。

新增真实回归在父目录预置旧编辑文件和 READY/INSERT,然后执行两个兄弟 capture,检查各自新文件、事件及父目录原件未变。该回归对旧 EXE 真实失败,对本树重建的修正 EXE 通过;完整真实应用模块 9 项通过。详细身份、原期限、原件哈希和回收结果见[脱敏证据](../../experiments/conpty/results/2026-10-26-vim-fixture-isolation/validation.json)。

## 复验

先构建 `real_app_capture`,在当前进程 PATH 提供固定 Vim;设置 `TERMFORGE_REAL_APP_CAPTURE` 指向所验配置的实际 EXE。

```powershell
python -m unittest discover -s experiments/differential -p test_real_app_matrix.py -v
```

完整模块另需按 differential README 构建两方核心 trace。不要用系统 ConPTY 的成功代替显式官方配置。

## 尚未闭合

原 GUI `candidate-tui` 两轮本批仍为一绿一红;红例在 resize 后没有产生 InsertLeave,`:call writefile(...)` 成为插入内容,stage59 到原期限失败。两轮都回收为 Job0、私有 desktop 消失。目录隔离不影响此入口,尚无根因或修复结论。历史冻结基线、完整 TUI、呈现端点和后端整体回归仍分别保留。

有停止条件的 GUI 探针也未形成修复:strace 包装的两次运行在 Vim 启动时触发另一种 `c0000005`,不计原症状;临时观察真实 WriteFile 成功字节的三轮均绿,其中两轮 Ctrl-O 命令先于 CPR,否定了“这个先后关系必然导致红例”的假设。观察代码已移除,重新构建的原入口两轮均绿,事件/写盘/回复原检查保持;这仍不能解释已保存的真实 stage59 红例。探针二进制未在还原前记录哈希,证据明确保留该身份缺口并排除为验收。下一次需定位真实控制台输入到 Vim resize/按键处理的边界,不能用这批绿例关闭 GUI 问题。
5 changes: 4 additions & 1 deletion experiments/conpty/real_app_capture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ int main(int argc, char** argv) {
if (!IsProcessInJob(GetCurrentProcess(), nullptr, &contained) || !contained) return 2;
const std::filesystem::path out = argv[1], vim = argv[2];
require(std::filesystem::create_directories(out), "output directory already exists");
const auto root = out.parent_path();
// Each newly created capture owns its application fixtures as well as
// its traces. Sibling captures must not reuse editor contents or event
// files from an earlier run in their shared parent directory.
const auto root = out;
unsigned long long shell_bytes = 0, pager_bytes = 0, editor_bytes = 0;
{
tf_conpty::Session session;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
{
"schema": "termforge-vim-fixture-isolation-v1",
"scope": "native capture fixture isolation only; GUI stage59 remains open",
"base_source": "af563ba15aa5abf90a09ca55eb9c177ac873ecf0",
"fixed_source": "aa5fbe5f61d771b5ac7716ce4e77cb143baec3ef",
"backend": {
"identity": "reviewed-side-by-side-1.25.260930003",
"flags": 24
},
"application": {
"version": "Git for Windows MSYS Vim 9.1 patches 1-532 compiled 2024-07-09",
"exe_sha256": "2e0fda63a89171adfc46c9417d5105cc39ed778cc69a890b83a4eabc34e7646a"
},
"binary_sha256": {
"old_capture": "6203c2a5ddaebf756d959456265aa64d21f9286c59730123709211120dee7941",
"fixed_capture": "9d0d8d186f024993915465fc52d4f9e4cba34a014baee831a96d5611b0c7e10d",
"gui_original": "54105cbfc5ac7ab34fb6d18ba2672d06ed5c187bebeaee803322084e323a9c72"
},
"source_files": {
"experiments/conpty/real_app_capture.cpp": "14579e51ab1f11967634e491b0e430f4cb29d370e2be6d6a67d7f3bc77655075",
"experiments/differential/test_real_app_matrix.py": "7182266c031682f422529a851726ac653b85ef6f9ca05aaab3cd21559e2764fb"
},
"unchanged_contract": {
"capture_outer_seconds": 60,
"gate": "GO newline after Job assignment",
"real_cmd_more_vim": true,
"new_buffer_marker": "[New]",
"editor_text": "TF_VIM_REAL",
"reader_eof_required": 109,
"editor_normal_exit_required": 0,
"startup_milliseconds": 10000,
"new_buffer_milliseconds": 10000,
"insert_milliseconds": 10000,
"editor_exit_milliseconds": 15000
},
"same_parent_original": [
{
"supervision": {
"timed_out": false,
"returncode": 0,
"job_after": {
"active": 0,
"total": 8,
"terminated": 0
},
"cleanup_errors": []
},
"editor.bin": {
"bytes": 2099,
"sha256": "698a05e6e3d717fac41e72e7af4a0eb85ba946faa35c030e5e54b7687cf97e2c"
},
"shell.bin": {
"bytes": 64,
"sha256": "9c74f9134043ae20a6dccb278951365f8f65e4d13c2eef491737b5095da4538c"
},
"pager.bin": {
"bytes": 508,
"sha256": "f51912329ea7e8f696057afc48703ceced3d2e8d582f891239ebfcc2b08b1dc0"
},
"pager-lifecycle.json": {
"bytes": 287,
"sha256": "5f9cb64fee5d7f5edcf86005ab1c73c04f4dedd9a4e8975f61495235cf40c7ef"
},
"manifest.json": {
"bytes": 244,
"sha256": "dbd4eb828fa17de859963fd736d529d888aec64510b9d6523dd684d8852d3e61"
}
},
{
"supervision": {
"timed_out": false,
"returncode": 1,
"job_after": {
"active": 0,
"total": 8,
"terminated": 0
},
"cleanup_errors": []
},
"editor.bin": {
"bytes": 1999,
"sha256": "93adf25f57b7b31eacf242fe3229786b47e5148741c8074b74e775d43254f215"
},
"shell.bin": {
"bytes": 64,
"sha256": "9c74f9134043ae20a6dccb278951365f8f65e4d13c2eef491737b5095da4538c"
},
"pager.bin": {
"bytes": 508,
"sha256": "f51912329ea7e8f696057afc48703ceced3d2e8d582f891239ebfcc2b08b1dc0"
},
"pager-lifecycle.json": {
"bytes": 287,
"sha256": "5f9cb64fee5d7f5edcf86005ab1c73c04f4dedd9a4e8975f61495235cf40c7ef"
},
"editor-failure.json": {
"bytes": 270,
"sha256": "a2a39c3f6e40ee1b56a44a2b352abd725676727d56bbe176958ff0550fc699b4"
},
"failure": {
"stage": "new-buffer",
"error": "editor new buffer was not displayed",
"editor_exit": 91,
"child_signaled": true,
"editor_bytes": 1999,
"active_children": 0,
"cleanup_complete": true,
"capture_overflow": false
}
}
],
"same_parent_fixed_with_stale_parent_fixtures": [
{
"supervision": {
"timed_out": false,
"returncode": 0,
"job_after": {
"active": 0,
"total": 8,
"terminated": 0
},
"cleanup_errors": []
},
"editor.bin": {
"bytes": 2099,
"sha256": "698a05e6e3d717fac41e72e7af4a0eb85ba946faa35c030e5e54b7687cf97e2c"
},
"shell.bin": {
"bytes": 64,
"sha256": "9c74f9134043ae20a6dccb278951365f8f65e4d13c2eef491737b5095da4538c"
},
"pager.bin": {
"bytes": 508,
"sha256": "f51912329ea7e8f696057afc48703ceced3d2e8d582f891239ebfcc2b08b1dc0"
},
"pager-lifecycle.json": {
"bytes": 287,
"sha256": "5f9cb64fee5d7f5edcf86005ab1c73c04f4dedd9a4e8975f61495235cf40c7ef"
},
"manifest.json": {
"bytes": 244,
"sha256": "dbd4eb828fa17de859963fd736d529d888aec64510b9d6523dd684d8852d3e61"
}
},
{
"supervision": {
"timed_out": false,
"returncode": 0,
"job_after": {
"active": 0,
"total": 8,
"terminated": 0
},
"cleanup_errors": []
},
"editor.bin": {
"bytes": 2099,
"sha256": "698a05e6e3d717fac41e72e7af4a0eb85ba946faa35c030e5e54b7687cf97e2c"
},
"shell.bin": {
"bytes": 64,
"sha256": "9c74f9134043ae20a6dccb278951365f8f65e4d13c2eef491737b5095da4538c"
},
"pager.bin": {
"bytes": 508,
"sha256": "f51912329ea7e8f696057afc48703ceced3d2e8d582f891239ebfcc2b08b1dc0"
},
"pager-lifecycle.json": {
"bytes": 287,
"sha256": "5f9cb64fee5d7f5edcf86005ab1c73c04f4dedd9a4e8975f61495235cf40c7ef"
},
"manifest.json": {
"bytes": 244,
"sha256": "dbd4eb828fa17de859963fd736d529d888aec64510b9d6523dd684d8852d3e61"
}
}
],
"regression_test": {
"original_binary": "failed RuntimeError real application capture failed in 10.561s",
"fixed_binary": "passed in 0.688s",
"native_gate_module": "2 tests passed in 0.784s",
"full_real_app_matrix_module": "9 tests passed in 11.994s",
"full_real_app_log_sha256": "f721167b7bfd5f971b11396fa7bda3d0e2694142682c835bd17a91b35b2f152b"
},
"gui_original_two_runs": [
{
"passed": true,
"outer_timeout": false,
"job_after": {
"active": 0,
"total": 9,
"terminated": 0
},
"desktop_gone": true,
"evidence_sha256": {
"candidate-output.bin": "c8b801ffa250aa2360d19cb671feb6540d6f652a47e3801f50f7ed1ae23c9f60",
"candidate-insert-log.txt": "3d07108bcc44f0831b31530265fac711c1d1e69194aa9309897e4440c26de65a",
"candidate-resize-log.txt": "2d3db728e657a0e1ab99b23acb6b2d08c586c4eaba6e65a66ab65e7fb47693b7",
"candidate-startup-log.txt": "3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5"
}
},
{
"passed": false,
"outer_timeout": false,
"job_after": {
"active": 0,
"total": 7,
"terminated": 0
},
"desktop_gone": true,
"evidence_sha256": {
"candidate-output.bin": "fc43e5c356005f9c32847476d4bdba5fabaf51439083d8ac4e530d3e8bb5c883",
"candidate-insert-log.txt": "e6de4daeba1841783e7e22f81dbf87154f341c309dc5b54405e5fc2c2f3550a6",
"candidate-resize-log.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"candidate-startup-log.txt": "3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5"
}
}
],
"limitations": [
"GUI stage59 has a fresh genuine red and is not fixed by fixture-directory isolation",
"Historical frozen joint baseline is unchanged",
"Capture exit_code91 is forced cleanup after new-buffer assertion failed; it is not a natural Vim exit",
"No full current official CTest matrix, actual presentation or complete TUI acceptance is claimed"
],
"full_real_app_matrix": {
"candidate": "alacritty_terminal",
"version": "0.26.1-dev+termforge.4",
"capture_sha256": "9d0d8d186f024993915465fc52d4f9e4cba34a014baee831a96d5611b0c7e10d",
"core_trace_sha256": "44265fcaf1d54103eff1731c5c49cd5cca39c635ff303f2a6659c6b4ac63048e",
"candidate_trace_sha256": "9fd9c962e9837f2e764d91973f192f8ad77110020656d44350a9329d521828b4",
"runs": 3,
"counts": {
"match": 3
},
"execution_passed": true,
"real_application_covered": true,
"semantic_acceptance": false,
"presented_done": null,
"summary_sha256": "cced694562a16395e0d25cbad4a70ea639aabc9df49188351cae782f05d20c47"
},
"gui_after_native_fixture_fix": {
"runtime_source": "aa5fbe5f61d771b5ac7716ce4e77cb143baec3ef",
"binary_sha256": "ac7998c5c6b89f251e1fc83a773dcad31d63b55c10f69f04408dc1e4b2b97499",
"temporary_instrumentation_removed": true,
"same_runtime_as_gui_original_except_native_capture_fix": true,
"runs": [
{
"status": "passed",
"timed_out": false,
"exit_code": 0,
"job_after": {
"active": 0,
"total": 9,
"terminated": 0
},
"desktop_gone": true,
"raw_sha256": {
"candidate-output.bin": "7976fddf498096e0fa1ce90318ec6fb618d6d267ddbf1e89503bfd717471e832",
"candidate-insert-log.txt": "3d07108bcc44f0831b31530265fac711c1d1e69194aa9309897e4440c26de65a",
"candidate-resize-log.txt": "2d3db728e657a0e1ab99b23acb6b2d08c586c4eaba6e65a66ab65e7fb47693b7",
"candidate-startup-log.txt": "3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5"
}
},
{
"status": "passed",
"timed_out": false,
"exit_code": 0,
"job_after": {
"active": 0,
"total": 9,
"terminated": 0
},
"desktop_gone": true,
"raw_sha256": {
"candidate-output.bin": "4b415c29f16225a8c9df5d1fec37a128dfd91f405bf42878fdbd2d08bdeaa027",
"candidate-insert-log.txt": "3d07108bcc44f0831b31530265fac711c1d1e69194aa9309897e4440c26de65a",
"candidate-resize-log.txt": "2d3db728e657a0e1ab99b23acb6b2d08c586c4eaba6e65a66ab65e7fb47693b7",
"candidate-startup-log.txt": "3dfa3fb239f56c778a1e9b33eb31328349edd155a95806dac4378904e88527e5"
}
}
],
"interpretation": "two current passes do not explain or fix the original genuine stage59 failure"
},
"bounded_gui_diagnostics": {
"accepted_as_fix": false,
"strace_wrapper": {
"runs": 2,
"failure": "different startup crash c0000005, GUI stage50, not original stage59",
"real_input_version_and_oracle_retained": true
},
"temporary_WriteFile_observer": {
"runs": [
{
"status": "passed",
"actual_write_order": [
[
"cursor-position-reply",
89
],
[
"resize-command",
90
]
],
"all_reported_writes_successful": true,
"write_trace_sha256": "edcc9fe614bff32d753da8ab212362cf1101a5648ffbfe1fb701199688517a31"
},
{
"status": "passed",
"actual_write_order": [
[
"resize-command",
89
],
[
"cursor-position-reply",
90
]
],
"all_reported_writes_successful": true,
"write_trace_sha256": "7738ce6719efc0f08b82117118e8efce118702132aa40f939774b0e09fdca0ba"
},
{
"status": "passed",
"actual_write_order": [
[
"resize-command",
89
],
[
"cursor-position-reply",
90
]
],
"all_reported_writes_successful": true,
"write_trace_sha256": "cad8e5598daf7dc1ad1110b56ef675d6c5d1e21033d2b7ed02146dcd7bf5e9c2"
}
],
"binary_sha256": null,
"binary_identity_limitation": "probe executable not hashed before restoring original source; excluded from acceptance",
"local_patch_sha256": "1b6da35b9be9ff84babecb60e36c46588306eac6df10ac2fe54ccb3dc2d87bb8",
"falsified_hypothesis": "Ctrl-O preceding CPR necessarily causes stage59 failure",
"remaining_unknown": "Where between console host input processing and Vim resize/key processing Ctrl-O ceased to act in the original failure"
},
"cleanup": "temporary source edits removed; final working tree rebuilt without observers"
}
}
Loading
Loading