You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
geometry-96 grid [111,7] top 75 maximum 75 selected ok
geometry-zoom75 grid [149,10] top 75 maximum 75 selected ok
geometry-failure grid [111,7] top 78 maximum 78 selected invalidated
Activity
JavaZeroo commentedon Oct 29, 2026
新证据:移位量可以精确算出来,且与"重绘带尾部空行"吻合
失败轮(run 40916697774 /
run-dtb8lw0m/system-1)的观测:ESC[K CR LFTF_FIND_078变为TF_FIND_075(正好上移 3)算术完全对得上:7 行屏幕 + 6 行内容 + 3 个换行 = 9 行输出,超出 7 行 2 行 → 滚动,且末尾空行又追加,最终净增 3 行;历史前进 3 行,于是 index 78 的内容变成原来 index 75 的
TF_FIND_075。由此得到的假设
重绘很可能是按旧几何(7 行)生成的:ConPTY 在 resize 生效前发出"重打可见区 + 补空行"的序列,我们的屏幕仍按旧尺寸接收,于是这些补空行被当作滚动输出,缓冲区增长、内容整体位移;随后新尺寸生效,位移被固化。
这解释了为什么:
下一步(仍不改产品)
Parser(7,111)回放"重绘 + 3 空行",确认能得到同样的 +3 行与 index 78 位移(纯核心验证);JavaZeroo commentedon Oct 29, 2026
机制闭合:重绘是按旧几何生成的,被我们按新几何应用
本地核心实验(
Parser(7,111,recovery),81 行后喂"重绘 075–080 + 3 空行")只得到 +3 行、索引 78 不变——说明单纯重放不足以产生位移。把失败帧的视口数据代进去就吻合了:078变075也就是:ConPTY 的重绘是按 resize 前(10 行、top=75)的几何发出的;我们的屏幕已经切到 resize 后(7 行、top=78)才消费这批字节,于是整体错位 3 行,正好是 10−7。
这同时解释了:
结论与建议
这不是选区逻辑、也不是裁剪策略问题,而是 resize 期间"新尺寸"与"旧尺寸重绘字节"的次序问题。可选方向(都属重要行为,需用户确认):
ESC[?25l ESC[H起的重绘序列,在尺寸切换瞬间丢弃/重放。第 3 条风险最高(启发式),第 1 条最保守。需要你决定方向后再动产品代码;本轮仍只做调查。
JavaZeroo commentedon Oct 29, 2026
新增样本(PR #195 的 PR 回归,只改诊断代码):run 40918632787、job 123605523470;
text_actions8.77s 失败:test_actual_local_actions_twice_without_clipboard(repeat=1)→actions geometry lost terminal selection;与本项已有结论一致:resize 期间 ConPTY 尾部重绘按旧几何生成、被按新几何消费,错位量 = 高度差,导致选区行内容等长互换而失效。
合并 PR #195(输出消费轨迹)后,下一次命中即可用
output-trace.jsonl直接确认"重绘块消费时 rows 已是新值",把机制从推断升级为确证。Merge pull request #195 from JavaZeroo/diag/output-trace
Merge pull request #197 from JavaZeroo/research/repaint-design
Merge pull request #198 from JavaZeroo/diag/repaint-flag
Merge pull request #201 from JavaZeroo/ci/repeat-five
Merge pull request #202 from JavaZeroo/research/trace-analyzer
JavaZeroo commentedon Oct 30, 2026
新样本(system 变体,带完整轨迹)
run 40979768991、
text-actions/.../system-1:actions geometry lost terminal selection。阶段时间线
选区丢弃
{"schema":"termforge-selection-discard-v1","reason":"row-text-changed","row":78, "saved_bytes":29,"current_bytes":29,"saved_columns":111,"current_columns":111,"t_ms":789468}丢弃发生在
geometry-zoom75之后 15 ms、geometry-96之后 47 ms。期间的输出块
终端 1 在这一段收到的重绘批次
columns/rows全部是 112×7(t=789265/789343/789406…),终端 2 为 74×17;也就是说消费时刻的网格在这几次几何切换中没有随阶段变化。与既有结论的一致性
仍未决定
修法方向仍需确认(先排空 / 尺寸世代 / 识别吸收),本文档 conpty-resize-repaint-timing.md 已列出三个方向的取舍。
Merge pull request #221 from JavaZeroo/diag/resize-trace
Merge pull request #222 from JavaZeroo/research/analyzer-resize
JavaZeroo commentedon Oct 30, 2026
本地 repeat14 的核对结果:错配已可从字节+消费网格证明,缺的只是 resize 时刻
样本:
local/repeat14/text-actions/run-kkdmmpu8/system-1(sample.json:status=failed、exit_code=2、stderr="actions geometry lost terminal selection")。同一 repeat14 的 12 个样本中唯一失败(system 6 个中 1 个;另 3 个 run 的 system-0/system-1 与 local-0/local-1 全部 passed)。源身份:
identity.json的experiments/shell/shell.cpp = 6aebf4088ef0b42342e9ad2368a8938c8b857cb134f7c0207bd06033eb70efce,与提交5ab207c^的 CRLF 检出形式逐字节相同(5ab207c"diag(shell): record geometry switches in the output trace" 提交时间 2026-10-30 09:43 +0800)。即该样本早于termforge-resize-trace-v1,本来就不会有 resize 记录。1. 精确阶段序列(
actions-progress.jsonl,t_ms 为 GetTickCount64 开机毫秒)2. 重绘批次与消费网格(
output-trace.jsonl的columns/rows是喂入前记录的消费网格,shell.cpp:861-869)output.bin是两终端按写入顺序拼接,16 段字节数合计 9202 = 文件长度,可逐段定位。bottom 终端共 5 批:ESC[2;4H195d02780cf6…2f9e22d1b637…2f9e22d1b637…2f9e22d1b637…ESC[7;1H收束) + 210B(7 行)89a7c1fca166…441B 一批里含两条重绘(2 个
ESC[?25l ESC[H):前段 10 行形状 = 为 149×10 生成,后段 7 行形状 = 为 111×7 生成;两段在同一读里被以 111×7 消费。3. 关键差分:同一 1071B 传输,消费网格不同 → 结果不同
五段 210/210/210/231/210 = 1071B,SHA-256
c1d7cb18d5da399a838c997e0abe9375fd4db037e9f8f609ac02e1a58878cb84,与docs/research/resize-repaint-witness-plan.md:20固定的原失败传输完全相同。这是"按旧几何生成、被新几何消费"的直接证据,不再只是推测:同一字节串只在消费网格为 111×7 时出错。
4. 丢弃记录与行位移
selection-discard.jsonl:{"reason":"row-text-changed","row":78,"saved_bytes":29,"current_bytes":29,"saved_columns":111,"current_columns":111,"t_ms":789468}—— 与 progress 的geometry-failure同刻、同行号;top/max 75→78的 +3 恰等于 10 行 − 7 行。5. 从这些产物能证到哪一步
ESC[7;1H)+ 通过样本同字节在 149×10 消费结论:"错配"可由本产物证明,"错配的时序成因"不能。后者正是
termforge-resize-trace-v1的用途(shell.cpp:228-234记录 old/new 网格与 t_ms,与 output-trace 同一 GetTickCount64 时钟):本 run 的output-trace.jsonl692 行仅有 output/heartbeat/input 三类 schema、0 条 resize 记录,且源身份已证明早于该提交,故不能也不需要在旧样本上要求它。6. 三个方向各自还缺什么证据
Session读取路径状态模型评审(架构级)ESC[?25l ESC[H重绘的反例语料与误伤率;丢弃后可见内容仍正确的呈现校验三者共同的必要前置:用
docs/research/resize-repaint-witness-plan.md:26-37的四个切点(A/B/C/D)确认该交错是否足以独立产生同形坏状态——目前该 witness 未运行,本评论不作因果裁定。边界
local/repeat14(gitignored 本地产物)与仓库文档,未改任何产品/测试代码、未重放 transport、未运行 witness、未 dispatch 任何作业。t_ms是 GetTickCount64 相对开机毫秒,不能换算墙钟;本评论不给出任何绝对时间。JavaZeroo commentedon Oct 30, 2026
独立复核:441B/111×7 这一段属实
我按子代理给出的路径重新读了
local/repeat14/text-actions/run-kkdmmpu8/system-1/output-trace.jsonl,逐块确认(terminal 1,共 16 块):同一轮的兄弟样本(local-0/local-1/system-0)里,对应的 231B 十行重绘被消费在 149×10;只有失败样本把它消费在 111×7。
这使 #194 的机制从"推断"变成可从字节与记录的消费网格直接证明:一段按 10 行生成的尾部重绘,被按 7 行的网格消费。
仍缺的只是精确的 resize 时刻(该样本早于
termforge-resize-trace-v1,轨迹里 0 条 resize 记录),因此"+3 行的来源"(top 75→78)还需下一次带 resize 记录的命中。修法方向仍等确认(先排空 / 尺寸世代 / 识别吸收)。
6 remaining items
test(core): 锁定重绘的几何相关性 (#194)
Merge pull request #265 from JavaZeroo/feat/resize-replay
JavaZeroo commentedon Oct 31, 2026
又一条纯文档 PR 的 CI 命中了同一症状
PR #273(只改
docs/research/r2-performance-budget.md与docs/milestones/R2-local-terminal.md)的 Windows 回归失败:要点:
text_actions用例);与已有结论的关系
本项此前的证据是"同一轮四个样本中,失败样本把 231B 十行重绘消费在 111×7,通过样本消费在 149×10"。本条补充的是:在纯文档 PR 上也能复现同一句错误,进一步说明它与业务改动无关。
边界
selection-discard.jsonl具体字段;JavaZeroo commentedon Oct 31, 2026
新的命中:
ui-actions的几何丢失,带上了每标签页计数器run 41192442670,
text_actions用例 8.17 s:判读
input_rejected=0input_high_water=1(两个会话)output_bytes == consumed(4267/4267、4971/4971)read_error=0child_signaled=false、exit_code=259这条样本把本项与"读取/输入侧的停滞"彻底分开:几何丢失发生时,两个会话的输出都已全部消费、输入无积压、子进程存活。也就是说,问题出在几何变化与已记录选区之间的判定,而不是数据还没到。
与既有证据的关系
此前本项的证据是:失败样本把 231B 十行重绘消费在 111×7,而三个通过样本消费在 149×10(字节形状 + 消费网格)。本样本补充的是同一时刻的队列与读取状态,两者不冲突:重绘确实到了、也确实被消费了,只是消费时的网格与重绘的形状不匹配,于是选区被判失效。
边界
selection-discard.jsonl(该诊断需TERMFORGE_SELECTION_DIAG才写出,本次作业未设置);stage=0属 Workspace 场景已知局限;JavaZeroo commentedon Oct 31, 2026
找到了丢弃记录:全部指向「行文本变短」,不是尺寸变化
此前我一直以为失败样本里没有
selection-discard.jsonl。实际是有的——run.py:470默认就把它写到每个样本目录(setdefault("TERMFORGE_SELECTION_DIAG", …)),只是我只 grep 了 stdout/stderr。下载 run 41192442670 的 artifact 后统计(
local/a194/actions-repeat-evidence):text-actionsrow-text-changed12shellanchor-missing-or-shrunk40、row-text-changed30selection-contextrow-text-changed20记录字段:
reason、row、saved_bytes、current_bytes、saved_columns、current_columns、t_ms。典型记录:
{"schema":"termforge-selection-discard-v1","reason":"row-text-changed", "row":0,"saved_bytes":23,"current_bytes":17,"saved_columns":54,"current_columns":54,"t_ms":586093}判读
saved_columns == current_columns(54/54)→ 列数没变,不是 resize 造成的;saved_bytes 23 → current_bytes 17→ 选中的那一行内容变短了 6 字节;row-text-changed说明判定依据是"行文本与保存时不同";anchor-missing-or-shrunk(锚点行消失或变短),同属"行内容在重绘过程中短暂不完整"这一类。结合此前样本(输出全部消费、输入无积压、子进程存活),这组数据把成因收窄为:几何变化触发重绘时,选中的行在某一时刻只被写入了部分内容(23→17 字节),判定逻辑据此认为选中文本已失效。
对三个候选方向的意义
边界
text-actions/shell/selection-context套件;失败的那个ui-actions样本目录本身没有记录,说明它的丢弃走的是另一条路径(场景自身的断言),需进一步核对;JavaZeroo commentedon Oct 31, 2026
按样本分组的精确结果(更正我上一条的字节方向)
上一条我引用的是
selection-context目录里的 23→17(变短)。把shell套件的 70 条记录按样本分组后,实际分布是:(saved_bytes, current_bytes)copy-system-0copy-system-1find-0/find-1(0, 0)selection-0/selection-1全量字节差值分布:
(saved_bytes, current_bytes)(0, 0)anchor-missing-or-shrunk(11, 29)row-text-changed因此正确的描述是
row-text-changed(30 条):保存时该行 11 字节,判定时 29 字节 —— 行变长了,即重绘把更多内容写了进去;anchor-missing-or-shrunk(40 条):行内容与列数都是 0 —— 锚点行在那一刻不存在(被清空或在另一屏);row-text-changed组是 54 → 54(未变),anchor组是 0 → 0。这对三个方向的意义(在更正后重新评估)
row-text-changed组列数完全没变(54→54),世代变化识别不了这种部分重绘边界
shell套件(run 41192442670);selection-context套件另有 20 条row-text-changed,字节方向与shell组不同(23→17),说明变长与变短都会触发,即判定只比较"内容相等",不区分方向;diag(core): record the differing row text on a discarded selection refs
Merge pull request #288 from JavaZeroo/diag/discard-text
JavaZeroo commentedon Oct 31, 2026
已合入 main 的新诊断(可用于判定修法方向)
saved_text/current_text(各 48 字节,JSON 转义)selection-discard.jsonl(#288)rejected/drained/childrun.py(#287)stageworkspace.hpp(#284)closed/closer_joined/frame_at_timeoutshell.cpp、run.py(#285/#283)对三个方向的意义
决策的关键是:几何变化导致的行文本变化,是“写了一半”还是“真的换了内容”。
current_text是saved_text的前缀(如ONETWO→ONETWO+新增)ONETWO→XXXXXX)(0,0)且 anchor 原因边界
JavaZeroo commentedon Oct 31, 2026
新字段已生效,本机跑出两条真实记录
本机执行
run.execute(termforge_shell, "ui-selection")(通过的场景),selection-discard.jsonl现在带上了文本:{"reason":"anchor-missing-or-shrunk","row":0,"saved_bytes":0,"current_bytes":0,"saved_columns":0,"current_columns":0,"saved_text":"","current_text":""} {"reason":"row-text-changed","row":0,"saved_bytes":11,"current_bytes":29,"saved_columns":74,"current_columns":74, "saved_text":"OTHER text","current_text":"TF_FIND_002 needle 中文 e\u0301"}判读
saved_text="OTHER text"→current_text="TF_FIND_002 needle 中文 e◌́"saved_columns == current_columns(74/74)对修法方向的影响(重要)
此前我推断"三种原因都指向判定发生在重绘中间态",因此倾向"先排空"。这条实测数据说明:至少在本机这条路径上,
row-text-changed是内容真的被替换,先排空不会、也不应该阻止它。所以现在有三种可能:
0/0)占主导结论
我需要一次带新字段的 CI 失败样本才能给出确定方向;本机数据已经推翻了"一定是中间态"的假设。
JavaZeroo commentedon Oct 31, 2026
用 #288 的文本字段做了统计:40 条
row-text-changed里 0 条是部分重绘数据来源:run 41200844601 的
actions-repeat-evidence(80 条带saved_text/current_text的记录)。row-text-changedanchor-missing-or-shrunk其中
row-text-changed的判定:current_text以saved_text开头的记录数 = 0。典型记录:
{"reason":"row-text-changed","row":0,"saved_bytes":23,"current_bytes":17, "saved_columns":54,"current_columns":54, "saved_text":"TF_STREAM_TICK_00000001","current_text":"TF_STREAM_STOPPED"}即:行内容被真的替换了(
TICK_00000001→STOPPED),不是"写了一半"。这类丢弃是正确行为。对本项的影响
我之前倾向的"先排空"方向,在这批数据里不会、也不应该阻止这些丢弃——因为没有任何一条是中间态。
因此需要重新收敛问题:
actions场景在 DPI 变化后要求 Find 匹配与选区存活saved_text/current_text要区分这两者,需要一次
ui-actions几何阶段的失败样本(本批 80 条都来自 stream/selection 场景,不是几何阶段)。边界
ui-actions几何阶段(stage 80-84)的记录,因此不能直接外推到本项的失败点。JavaZeroo commentedon Oct 31, 2026
本机复现 #194 的可行性:不可行
我按上一轮的计划试了三条路,结论都是否定的。
1. 本机能跑
ui-actions,但跑不到几何阶段test_actions.ActionWindowTest.test_actual_local_actions_twice_without_clipboard不要求 hosted,本机可执行:但把它单独跑 10 次:
一次完整执行里,
ui-actions产出的snapshots是 0 条、geometry-*阶段 0 个(本机无受控 DPI 切换能力,那几段直接不产生记录)。actions geometry lost terminal selection2. 因此本机既拿不到样本,也无法做修前/修后对照
要验证任何针对本项的改动,都需要在有真实 DPI 切换能力的环境里跑,而我这里不具备。
3. 也不该继续盲目加采样轮次
上一轮 3 次定向采样全绿(序列 18 绿 / 1 红),命中率约 1/9。继续派发只是把成本线性堆高。
结论
本项需要「一次
ui-actions几何阶段失败样本」才能定论;该样本只能在 hosted 或真实异 DPI 环境产生。这不是我能在本机补齐的数据。边界
JavaZeroo commentedon Nov 1, 2026
按用户决定:本项关闭(不可复现,不再追)
决定
用户明确:「无法复现 就不管了。可以先 close」。因此本项按“不可复现”关闭,不是根因修复。
本会话试过的复现路径
ui-actionsonly=windows-2025-actions-repeat)CUP cannot discard an echo prefix…),已回退已落地的诊断(若再次命中可据此定位)
stageworkspace.hpp失败行input_high_water/input_rejected/output_bytes/consumed/closed/closer_joinedshell.cpp的diagnostic_line()saved_text/current_textselection-discard.jsonlrejected/drained/child)run.py关闭时的已知事实(供以后参考)
row-text-changed中,没有一条是部分重绘(current_text不以saved_text开头);anchor-missing-or-shrunk的字节与列数均为 0(行在切换中);workspace_actions_scenario.hpp:202-204,要求 DPI/zoom 变化后 Find 匹配与选区存活。重开条件
若再次命中该形态,凭上述诊断字段可直接判读“行文本是否真的变了”,无需重做本轮的探索。