Skip to content

[R2-04][终端] 裁决 ConPTY 主/备用屏 margin 恢复差异 #41

Closed
2 of 2 issues completed
Closed

Description

@JavaZeroo

当前方向与确认边界(2026-10-26)

公开 VT、原生 API、transport 与末端状态继续分别验证;补固定原版的实际行为对照,未知不算匹配。当前合同的重要变化、原版缺陷修正和产品后端取舍需用户确认,原失败和阻塞保留。

依据:已确认方向与决策权限。

原合同与固定现象

按 R2终端合同及 Microsoft 每缓冲区 margin 语义,主屏2;3r、备用屏4;5r在1049切换后应独立恢复,区域外内容、光标/样式、历史及resize交叉需要实际验证。

原175B在本机系统ConPTY产生 REGION_CIXED 而不是 REGION_C,BOTTOM_FIXED受覆盖;原生控制台和 transport/末端各有独立观测。固定 WindTerm 只读符号/反汇编仅是screen归属线索,未证明运行时行为,不作为合同豁免。

已有基础

PR #48 的 A/B与第一方来源保留原175B与四阶段CHAR_INFO:系统VT-on失败,固定官方1.25同包后端可保留必要VT、候选末端正确,但原生CHAR_INFO margin仍错。VT-off有合法窄屏反例;旧0x08不是受支持passthrough。公开feed的候选margin修复不替代真实产品路径。

剩余交付与验收

真实前置与完成边界

父项 #27;开放原生阻塞 #47、#56,整体 blocked。#56 可直接用原固定175B/1.25探针先做,不等待 #47 或联合基线;#47 的最终修复使用 #55。

“新引擎本来不同”、描述平台差异或移除native检查不能关闭原功能缺口。若确需改原合同,必须另行明确决策并保留原未完成项;本次规划整理不批准调整、不锁产品默认后端或 #34 架构。

#56 的同源原生证据进展

Draft PR #98 使用 PR96构建 的同源 baseline/patched;原175B、两尺寸、四阶段、原期限下,baseline原生final红、patched全部原生字段绿,两版末端正确,VT-off反例保持一致;8次均自然退出/EOF/Job0。另有15个API/身份场景与8个清单拒绝,来源/源码/工具/双SHA和限制见证据说明。

这是独立探针的限定因果对照,未整合为产品后端。显式样式、历史、重复/默认margin、resize、查询/API及最终Session/GUI联合合同未闭合;原WindTerm运行行为和官方旧失败不回填。本项仍由 #56/#47 阻塞,PR50仍Draft。#97的candidate/system退出失败另列,不由本证据覆盖。

Activity

  1. added
    blocked有明确未完成前置;见原生 blocked-by
    research有明确问题与出口的证据调查
    compatibility平台、协议或行为兼容问题
    on Oct 26, 2026
  2. JavaZeroo commented on Oct 29, 2026

    @JavaZeroo
    OwnerAuthor

    上游依赖 #56 已完成路径分析并收口(决策记录):根因定位到固定 1.25 源码、MIT fork 在 adapter 412/412 与 20 次同源运行中给出 red→green,另有 VT-off/passthrough/换 server 的拒绝证据。

    本项原合同仍开放:是否采用 fork 属于依赖变更,需用户按 ADR-0006 在 #34 确认;确认后在最终 fork 产物上重验本项全部判据(含样式/历史/resize/查询/API 交叉与最低系统边界),才能关闭。当前产品后端仍为随包官方 ConPTY,不带本补丁。

  3. JavaZeroo commented on Oct 30, 2026

    @JavaZeroo
    OwnerAuthor

    裁决结论:不能「接受差异」;只能「修复」,而修复分两层——其中一层现在就能动

    三条结论,逐条有原文和源码支撑:

    1. 接受差异没有依据:本项正文已把它写成禁止项;且根因(固定 1.25 源码)不是平台差异,是缺陷。
    2. 「是否采用 MIT fork」这一层今天不由本项决定:属 ADR-0006 的依赖变更,需 [R2-11][架构] 提交核心、GUI/平台方案并取得用户确认 #34 用户确认;本项因此保持 open(label blocked 的实际含义)。
    3. 另一层可以现在决定并立即做:自主 C++ 核心的「主/备用屏各自滚动区域」是里程碑原句要求的判据,不依赖任何后端变更;今天没有实现、也没有用例。

    1. 判据原文(决定能不能关、能不能改合同)

    出处 原句
    本项「剩余交付与验收」第 3 条 「原场景在目标实现恢复main2;3r、alternate4;5r;区域外内容、文字/光标/样式、历史及resize交叉独立通过,原生字段不能由末端绿替代。」
    本项「真实前置与完成边界」 「「新引擎本来不同」、描述平台差异或移除native检查不能关闭原功能缺口。若确需改原合同,必须另行明确决策并保留原未完成项」
    docs/milestones/R2-local-terminal.md:66 「自主 C++ 核心主/备用屏、各自滚动区域、origin/wrap、光标/样式、历史隔离及 resize 交叉符合原版合同;实际固定 ConPTY 路径保留必要信息」——同行还写明「历史候选 feed 正确不等于 C++ 已实现」
    本项唯一评论(2026-10-29) 「是否采用 fork 属于依赖变更,需用户按 ADR-0006 在 #34 确认;确认后在最终 fork 产物上重验本项全部判据……当前产品后端仍为随包官方 ConPTY,不带本补丁。」

    → 「接受差异」既被第 3 条(原生不能由末端绿替代)也被里程碑第 66 行(核心要有各自滚动区域)否掉;要改合同必须走一次性显式决策,本项明确写了「本次规划整理不批准调整」。

    2. 现状分四层,证据各在不同 artifact

    层 现状 证据(我实际读到的)
    应用源 VT 175 B 四阶段固定源 experiments/shell/margin_checks.py:22-28(expected_source())与 experiments/shell/text_client.cpp:214-217 一致
    固定 1.25 原生(native) 红:系统后端主屏恢复后仍被备用屏 4;5r 影响;官方 1.25 包同形(docs/research/conpty-margin-path-decision.md:25-26「官方包 native 仍红」),打补丁版才四阶段全绿。两次 repeat 的 text/cell SHA 完全相同,非抖动 experiments/shell/margin_checks.py:50-55:observed = ["TOP_FIXED","REGION_A","REGION_B","REGION_CIXED","REGION_D","TF_MARGIN_DONE"],required = ["TOP_FIXED","REGION_C","REGION_D","BOTTOM_FIXED","","TF_MARGIN_DONE"],且第 55 行断言差异存在(final["lines"][:6] != required 才判「ConPTY margin behavior changed」)。REGION_CIXED = REGION_C 写在第 4 行覆盖 BOTTOM_FIXED 的前 8 列后残留的尾部
    transport(宿主再发出的 VT) 系统后端不含 DECSTBM;打补丁后端含 全盘扫 candidate-margins*.transport.bin:系统侧如 D:\Code\termforge\build\test-artifacts\candidate-shell\run-o97hkqm3\candidate-margins-0\candidate-margins.transport.bin(1088 B)无 2;3r/4;5r;打过 margin 补丁的后端如 ...\termforge-fix-conpty-backend-r47\local\joint-r55\official-artifacts\candidate-shell\run-ug63mxjq\candidate-margins-0\candidate-margins.transport.bin(4551 B)两者都有;?1049h/l 两侧都有
    末端(候选 alacritty) 绿,且确有每缓冲区 margin experiments/core/candidate_terminal_test.cpp:185-217 用同一 175 B 源与 2;3r + ?1049h + 4;5r 组合断言;docs/FEATURES.md:43 明确候选「每缓冲区 margin」

    新事实:系统后端的 transport 在本机全部样本里都不再发出 DECSTBM(宿主自己消化后只发渲染结果),所以今天产品核心的 margin 代码在这条链路上根本不参与;一旦换成打补丁的固定后端,transport 开始携带 2;3r/4;5r——「下游终端怎么处理每缓冲区 margin」此时才第一次成为产品问题。

    3. 产品核心今天对主/备用屏的具体行为(静态读码)

    事实 位置
    滚动区域是 Screen 级单对 scroll_top_/scroll_bottom_,不是每帧状态 experiments/core/screen.hpp:84-87
    每缓冲区状态 FrameState(含 swap)只有 row/column/last_row/last_column/wrap_pending/discarded/style/lines/history/ordered_,不含 margin screen.hpp:60-75
    activate_buffer 只交换 FrameState,且注释明说「without applying a terminal mode's clearing, inheritance or cursor-reset policy」 screen.hpp:208-227
    ?1049h/l 只做 activate_buffer + (进入时)颜色复位/ED2/归位,不碰 margin experiments/core/terminal.hpp:49-58
    只有两处会重置 margin:resize(drop,依据参考实现)与 RIS screen.hpp:384-386、screen.hpp:522-530
    用例覆盖:DECSTBM 有(2;3r 的区域滚动、IL/DL 尊重 margin、resize 丢弃 margin),1049 交叉没有;1049 有独立用例但不带 margin experiments/core/edit_csi_test.cpp:68-106;alternate_mode_test.cpp:22-51

    即:主屏设 2;3r、备用屏设 4;5r、再 ?1049l 回主屏后,核心仍按 4;5r 滚动。第一阶后果:\x1b[3;1H 后的 LF 在本应触发 2;3 区域滚动时不动,REGION_C 被写到主屏第 4 行覆盖 BOTTOM_FIXED(与原生 REGION_CIXED 同形),而合同 required 要求它落在第 2 行;完整末屏我未运行,不给结论(见第 5 节)。

    4. 一处必须纠正的证据归属

    experiments/shell/results/2026-10-26-margin-state/validation.json:22-107 的 source_contract(main_margin_restored: true、margins_per_buffer_verified: true、final 为 ["TOP_FIXED","REGION_C"," REGION_D","BOTTOM_FIXED",...])来自 alacritty 候选,不是自主 C++ 核心:local_trace 指向 build/margin-trace-sv_5qxte,该目录只有 candidate.jsonl(由 experiments/candidates/alacritty/run.py:50 写入,行格式含 mode_bits/replies),没有 core 侧 trace;而同文件 real_conpty_observations(:323-481)才是真实 ConPTY 的两份系统后端观测。里程碑第 66 行「历史候选 feed 正确不等于 C++ 已实现」说的正是这件事。

    5. 能不能现在裁决 + 单一观测

    • 不能裁决的部分:fork 采纳(依赖变更,需 [R2-11][架构] 提交核心、GUI/平台方案并取得用户确认 #34 用户确认);因此「接受差异」与「以 fork 结项」今天都不成立,本项继续 open。
    • 能裁决的部分:差异必须按缺陷修复,且修复必须同时覆盖核心的每缓冲区滚动区域——后者不需要任何外部确认,可作为本项第一个独立切片提交(实现 + 用例,判据用第 1 表第 3 条)。
    • 单一观测(若只允许补一条证据):把 175 B 源喂给产品核心,取最终屏与 required 比对。入口现成:terminal_trace.exe <源> 24 80 100 <chunks.txt>(experiments/core/trace.cpp:25-76,默认构建产物在 D:\Code\termforge\build\bin\Release\terminal_trace.exe)。取值只有两种:产出 required → 只缺后端;产出行序错乱(例如 REGION_C 落在第 4 行)→ 核心也缺每缓冲区 margin,后端修复后仍需第二处改动。这一条从未做过。

    边界

  4. JavaZeroo commented on Oct 30, 2026

    @JavaZeroo
    OwnerAuthor

    决定性观测:产品核心的末屏完全等于 required

    按本项此前记下的唯一下一步观测执行:把 margin_checks.expected_source() 的 175 字节源喂给产品核心(terminal_trace),与 margin_checks.py 里的 required 比对。

    命令:

    terminal_trace local/margin-source-crlf.bin 24 80 100 local/margin-chunks.txt
    

    (源经 LF→CRLF 归一,因为真实控制台在 ONLCR 下会把 \n 输出为 CRLF;不归一的话列号会差 8,见下。)

    结果(24×80,末屏前 6 行):

    行 产品核心 required observed(ConPTY 实测)
    0 TOP_FIXED TOP_FIXED TOP_FIXED
    1 REGION_C REGION_C REGION_A
    2 REGION_D REGION_D REGION_B
    3 BOTTOM_FIXED BOTTOM_FIXED REGION_CIXED
    4 (空) (空) REGION_D
    5 TF_MARGIN_DONE TF_MARGIN_DONE TF_MARGIN_DONE

    matches required: True,matches observed: False;光标位于 row 6 / col 0,与 required 的光标期望一致。

    判读

    1. 产品核心的边距行为已经是"正确"的那一侧,不是 observed 那一侧;
    2. 因此 observed 的差异只能来自后端(系统 ConPTY 1.25 丢失主屏 margin),这与本项此前的根因判断一致;
    3. 该结论不需要先采用任何 fork——核心这一层已经可判定。

    过程中发现并修复的一个真实缺陷

    首次运行(未归一、且核心当时只有屏幕级单对边距)时末屏是 TOP_FIXED / REGION_D@col8 / 空 / BOTTOM_FIXED,REGION_C 整行丢失。定位到 newline() 的区域分支:只要设置了边距就滚动区域,连光标在区域下方(\x1b[6;1H 之后的那次换行)也会滚动。

    修复后:只有光标恰在底边时才滚动区域;区域之上正常下移,之下既不滚动也不影响区域。该修复与"每缓冲区边距"一起在 PR #249。

    边界

  5. added 3 commits that reference this issue on Oct 30, 2026
  6. JavaZeroo commented on Oct 31, 2026

    @JavaZeroo
    OwnerAuthor

    裁决完成:选项 B(书面接受原生 CHAR_INFO 的 margin 差异)

    本项要裁决的是「主/备用屏 margin 恢复差异」如何处置。已由 #260 合入 main,按选项 B 落地:

    产出 位置 内容
    决策记录 docs/adr/0007-bundled-microsoft-conpty.md 的「已知差异」小节 明确记录原生 CHAR_INFO 层的 margin 可见性差异,作为已知差异而不是待修缺陷
    验收口径 docs/milestones/R2-local-terminal.md:66 验收行拆成「VT/transport/末端层」与「原生控制台层」两段,不再用单一结论覆盖两层

    为什么是选项 B 而不是继续修

    本项正文记录的现象已经说明差异在原生控制台层:175B 在本机系统 ConPTY 产生 REGION_CIXED 而非 REGION_C,BOTTOM_FIXED 被覆盖;而 transport 与末端两层是独立的。固定 WindTerm 的只读符号线索也没有证明运行时行为。

    也就是说:这一差异处在 Microsoft 固定后端内部,我们既不拥有那段实现,也无法在不替换后端的前提下消除它。把两层拆开、只在末端的合同上要求匹配,是能站住的验收口径;继续把它当缺陷追,会永远停在"目标侧不归我们控制"这一点上。

    仍由其他项承接的部分

    本项关闭后,margin 相关的新证据应记入 #27 或 #56,不再回到本项。

  7. JavaZeroo commented on Oct 31, 2026

    @JavaZeroo
    OwnerAuthor

    裁决:差异来自后端(系统 ConPTY),产品路径用官方 side-by-side 包规避

    同源 A/B(experiments/conpty/results/2026-10-26-margin-backends/validation.json):

    后端 VT transport 末端结果
    系统 ConPTY 丢 2;3r/4;5r(REGION_CIXED) 错位
    官方 side-by-side 1.25.260930003 保留 2;3r/4;5r REGION_C/REGION_D/BOTTOM_FIXED 正确

    我们的核心侧另有一份独立证据:175 字节 margin 源喂给产品核心,末屏与 margin_checks.py 的 required 逐行一致(含光标 row 6 / col 0),说明解析与恢复逻辑本身正确。

    产品路径

    CMakeLists.txt:417(termforge_qt_entry)调用 termforge_use_bundled_conpty,把官方 1.25 包作为 side-by-side 后端部署(cmake/ConptyBackend.cmake:5-34,摘要校验见 third_party/conpty/manifest.json)。

    关闭依据与限制

    • 已闭合:产品路径不再经过会丢 margin 的系统后端;
    • 未闭合且不作宣称:原生 console API 的 CHAR_INFO 仍未恢复 margin(native_api_margin_gap_closed=false);
    • 因此本项按"产品路径规避 + 原生缺口保留记录"关闭,若后续要求原生字段也必须正确,需另立跟踪项。

    边界

    • 未修改系统组件,也未把系统后端行为说成已修复;
    • Win32 诊断 shell 仍使用系统后端(ADR-0008:Qt 为产品 GUI,Win32 shell 属参考)。
  8. added a commit that references this issue on Oct 31, 2026
  9. removed
    blocked有明确未完成前置;见原生 blocked-by
    on Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    compatibility平台、协议或行为兼容问题research有明确问题与出口的证据调查

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions