Ver Fonte

test(tui): stabilize first-run PTY snapshots

NI0317 há 1 mês atrás
pai
commit
261eb6b7ae

+ 19 - 3
apps/cli/tests/pty-harness.ts

@@ -55,6 +55,9 @@ while time.monotonic() < deadline:
                 os.write(fd, action["send"].encode())
         else:
             os.write(fd, action["send"].encode())
+            if "signalAfterMs" in action:
+                time.sleep(action["signalAfterMs"] / 1000)
+                os.kill(pid, signal.SIGTERM)
         action_index += 1
     waited, candidate = os.waitpid(pid, os.WNOHANG)
     if waited == pid:
@@ -76,7 +79,14 @@ if actual_exit != int(expected_exit):
 
 /** One terminal input or workspace mutation performed after its marker renders. */
 type TuiPtyAction =
-  | { readonly waitFor: string; readonly occurrence?: number; readonly send: string; readonly delayMs?: number }
+  | {
+    readonly waitFor: string
+    readonly occurrence?: number
+    readonly send: string
+    readonly delayMs?: number
+    /** Terminate the process this many milliseconds after sending input. */
+    readonly signalAfterMs?: number
+  }
   | { readonly waitFor: string; readonly occurrence?: number; readonly signal: 'SIGTERM'; readonly delayMs?: number }
   | {
     readonly waitFor: string
@@ -205,8 +215,14 @@ async function runWindowsPtySmoke(
             else setTimeout(() => { terminal.write(input) }, action.delayMs)
           }
         } else {
-          if (action.delayMs === undefined) terminal.write(action.send)
-          else setTimeout(() => { terminal.write(action.send) }, action.delayMs)
+          const send = (): void => {
+            terminal.write(action.send)
+            if (action.signalAfterMs !== undefined) {
+              setTimeout(() => { terminal.kill('SIGTERM') }, action.signalAfterMs)
+            }
+          }
+          if (action.delayMs === undefined) send()
+          else setTimeout(send, action.delayMs)
         }
         actionIndex += 1
       }

+ 0 - 39
apps/cli/tests/snapshots/tui-first-run-welcome/60-columns-low-height.expected.txt

@@ -1,39 +0,0 @@
-terminal 60x12 buffer=normal length=16 base=4 viewport=4
-lifecycle started=0 stopped=0 progress=inactive
-title ""
-cursor visible column=0 viewportRow=11 bufferRow=15
-viewport
-4| " DEEPSEEK HARNESS"
-  style 1-8 fg=bright-magenta bold
-  style 10-16 bold
-5| "╭──────────────────────────────────────────────────────────╮"
-  style 0-59 dim
-6| "│                     DeepSeek Harness                     │"
-  style 0-0 dim
-  style 22-37 fg=blue bold
-  style 59-59 dim
-7| "│                                                          │"
-  style 0-0 dim
-  style 59-59 dim
-8| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                    │"
-  style 0-0 dim
-  style 59-59 dim
-9| "│                                                          │"
-  style 0-0 dim
-  style 59-59 dim
-10| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验 │"
-  style 0-0 dim
-  style 59-59 dim
-11| "├──────────────────────────────────────────────────────────┤"
-  style 0-59 dim
-12| "│                       Enter  继续                        │"
-  style 0-0 dim
-  style 24-34 fg=bright-magenta bold
-  style 59-59 dim
-13| "│                         ↑/↓ 滚动 ↓                       │"
-  style 0-0 dim
-  style 24-35 dim
-  style 59-59 dim
-14| "╰──────────────────────────────────────────────────────────╯"
-  style 0-59 dim
-15| <blank>

+ 21 - 39
apps/cli/tests/snapshots/tui-first-run-welcome/120-columns.expected.txt → apps/cli/tests/tui-first-run-snapshots/120-columns.expected.txt

@@ -1,94 +1,76 @@
-terminal 120x30 buffer=normal length=32 base=2 viewport=2
-lifecycle started=0 stopped=0 progress=inactive
-title ""
-cursor visible column=0 viewportRow=29 bufferRow=31
-viewport
-2| " DEEPSEEK HARNESS"
-  style 1-8 fg=bright-magenta bold
-  style 10-16 bold
-3| " scripted TUI ready."
-  style 1-19 dim
-4| " main-session-{{uuid}}"
-  style 1-49 dim
-5| <blank>
-6| "/tmp/dsh-tui-welcome  tui-scripted-model  ↑0 ↓0  0% conte"
-  style 0-82 fg=bright-magenta bold
-  style 85-102 dim
-  style 105-109 dim
-  style 112-119 dim
-7| "╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+overlay 120x30 rows=20
+0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
   style 0-119 dim
-8| "│                                    ▄                                     DeepSeek Harness                            │"
+1| "│                                    ▄                                     DeepSeek Harness                            │"
   style 0-0 dim
   style 10-37 fg=blue
   style 75-90 fg=blue bold
   style 119-119 dim
-9| "│               ▄▄▄▄▄▄▄▄▄▄███▀      ██▄                                                                                │"
+2| "│               ▄▄▄▄▄▄▄▄▄▄███▀      ██▄                                                                                │"
   style 0-0 dim
   style 9-38 fg=blue
   style 119-119 dim
-10| "│       ▄███████████████▄      ████▄  ▄▄▄▄██     感谢您愿意拨冗试用 DeepSeek Harness。                                 │"
+3| "│       ▄███████████████▄      ████▄  ▄▄▄▄██     感谢您愿意拨冗试用 DeepSeek Harness。                                 │"
   style 0-0 dim
   style 4-43 fg=blue
   style 119-119 dim
-11| "│     ▄███████████████████▄    ████████████▀                                                                           │"
+4| "│     ▄███████████████████▄    ████████████▀                                                                           │"
   style 0-0 dim
   style 4-43 fg=blue
   style 119-119 dim
-12| "│    ▄██████████████████████▄   ▀█████████▀      目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。    │"
+5| "│    ▄██████████████████████▄   ▀█████████▀      目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。    │"
   style 0-0 dim
   style 4-42 fg=blue
   style 119-119 dim
-13| "│     ▄███▀█████████████████████▄   ████▀▀                                                                             │"
+6| "│     ▄███▀█████████████████████▄   ████▀▀                                                                             │"
   style 0-0 dim
   style 6-41 fg=blue
   style 119-119 dim
-14| "│      ███       ▀▀█████████▀▀▀█████████▀        “如切如磋,如琢如磨。”                                                │"
+7| "│      ███       ▀▀█████████▀▀▀█████████▀        “如切如磋,如琢如磨。”                                                │"
   style 0-0 dim
   style 7-40 fg=blue
   style 49-70 bold
   style 119-119 dim
-15| "│      ███          ▀███████▀█  ▀███████                                                                               │"
+8| "│      ███          ▀███████▀█  ▀███████                                                                               │"
   style 0-0 dim
   style 7-39 fg=blue
   style 119-119 dim
-16| "│      ███▄           ▀███████▄  ▀█████▀         产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的  │"
+9| "│      ███▄           ▀███████▄  ▀█████▀         产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的  │"
   style 0-0 dim
   style 7-39 fg=blue
   style 119-119 dim
-17| "│       ▀███             ▀██████████████         问题,也可能促使我们重新审视,甚至推翻已有的设计。                    │"
+10| "│       ▀███             ▀██████████████         问题,也可能促使我们重新审视,甚至推翻已有的设计。                    │"
   style 0-0 dim
   style 8-39 fg=blue
   style 119-119 dim
-18| "│        ▀███▄            ▀███████████▀                                                                                │"
+11| "│        ▀███▄            ▀███████████▀                                                                                │"
   style 0-0 dim
   style 8-38 fg=blue
   style 119-119 dim
-19| "│          ▀███▄      ▄▄▄    ▀████████▀          我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能帮到您,甚至  │"
+12| "│          ▀███▄      ▄▄▄    ▀████████▀          我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能帮到您,甚至  │"
   style 0-0 dim
   style 9-38 fg=blue
   style 119-119 dim
-20| "│            █████▄    ███▄▄   ▀█████▄▄          反而为工作平添了麻烦,请在企业微信群中留言,将使用感受告诉我们。每一  │"
+13| "│            █████▄    ███▄▄   ▀█████▄▄          反而为工作平添了麻烦,请在企业微信群中留言,将使用感受告诉我们。每一  │"
   style 0-0 dim
   style 9-38 fg=blue
   style 119-119 dim
-21| "│             ▀█████████████▄▄▄▄█▀█████▀         条反馈,都会帮助我们把它打磨得更好。                                  │"
+14| "│             ▀█████████████▄▄▄▄█▀█████▀         条反馈,都会帮助我们把它打磨得更好。                                  │"
   style 0-0 dim
   style 8-39 fg=blue
   style 119-119 dim
-22| "│                   ▀▀███████████▀▀                                                                                    │"
+15| "│                   ▀▀███████████▀▀                                                                                    │"
   style 0-0 dim
   style 12-34 fg=blue
   style 119-119 dim
-23| "├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"
+16| "├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"
   style 0-119 dim
-24| "│                                                     Enter  继续                                                      │"
+17| "│                                                     Enter  继续                                                      │"
   style 0-0 dim
   style 54-64 fg=bright-magenta bold
   style 119-119 dim
-25| "│                                                                                                                      │"
+18| "│                                                                                                                      │"
   style 0-0 dim
   style 119-119 dim
-26| "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+19| "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
   style 0-119 dim
-27-31| <blank>

+ 21 - 39
apps/cli/tests/snapshots/tui-first-run-welcome/160-columns.expected.txt → apps/cli/tests/tui-first-run-snapshots/160-columns.expected.txt

@@ -1,94 +1,76 @@
-terminal 160x30 buffer=normal length=32 base=2 viewport=2
-lifecycle started=0 stopped=0 progress=inactive
-title ""
-cursor visible column=0 viewportRow=29 bufferRow=31
-viewport
-2| " DEEPSEEK HARNESS"
-  style 1-8 fg=bright-magenta bold
-  style 10-16 bold
-3| " scripted TUI ready."
-  style 1-19 dim
-4| " main-session-{{uuid}}"
-  style 1-49 dim
-5| <blank>
-6| "/tmp/dsh-tui-welcome  tui-scripted-model  ↑0 ↓0  0% context"
-  style 0-82 fg=bright-magenta bold
-  style 85-102 dim
-  style 105-109 dim
-  style 112-121 dim
-7| "╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
+overlay 160x30 rows=20
+0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
   style 0-159 dim
-8| "│                                    ▄                                                         DeepSeek Harness                                                │"
+1| "│                                    ▄                                                         DeepSeek Harness                                                │"
   style 0-0 dim
   style 10-37 fg=blue
   style 95-110 fg=blue bold
   style 159-159 dim
-9| "│               ▄▄▄▄▄▄▄▄▄▄███▀      ██▄                                                                                                                        │"
+2| "│               ▄▄▄▄▄▄▄▄▄▄███▀      ██▄                                                                                                                        │"
   style 0-0 dim
   style 9-38 fg=blue
   style 159-159 dim
-10| "│       ▄███████████████▄      ████▄  ▄▄▄▄██     感谢您愿意拨冗试用 DeepSeek Harness。                                                                         │"
+3| "│       ▄███████████████▄      ████▄  ▄▄▄▄██     感谢您愿意拨冗试用 DeepSeek Harness。                                                                         │"
   style 0-0 dim
   style 4-43 fg=blue
   style 159-159 dim
-11| "│     ▄███████████████████▄    ████████████▀                                                                                                                   │"
+4| "│     ▄███████████████████▄    ████████████▀                                                                                                                   │"
   style 0-0 dim
   style 4-43 fg=blue
   style 159-159 dim
-12| "│    ▄██████████████████████▄   ▀█████████▀      目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。                                            │"
+5| "│    ▄██████████████████████▄   ▀█████████▀      目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。                                            │"
   style 0-0 dim
   style 4-42 fg=blue
   style 159-159 dim
-13| "│     ▄███▀█████████████████████▄   ████▀▀                                                                                                                     │"
+6| "│     ▄███▀█████████████████████▄   ████▀▀                                                                                                                     │"
   style 0-0 dim
   style 6-41 fg=blue
   style 159-159 dim
-14| "│      ███       ▀▀█████████▀▀▀█████████▀        “如切如磋,如琢如磨。”                                                                                        │"
+7| "│      ███       ▀▀█████████▀▀▀█████████▀        “如切如磋,如琢如磨。”                                                                                        │"
   style 0-0 dim
   style 7-40 fg=blue
   style 49-70 bold
   style 159-159 dim
-15| "│      ███          ▀███████▀█  ▀███████                                                                                                                       │"
+8| "│      ███          ▀███████▀█  ▀███████                                                                                                                       │"
   style 0-0 dim
   style 7-39 fg=blue
   style 159-159 dim
-16| "│      ███▄           ▀███████▄  ▀█████▀         产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的问题,也可能促使我们重新审视,甚至推翻已  │"
+9| "│      ███▄           ▀███████▄  ▀█████▀         产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的问题,也可能促使我们重新审视,甚至推翻已  │"
   style 0-0 dim
   style 7-39 fg=blue
   style 159-159 dim
-17| "│       ▀███             ▀██████████████         有的设计。                                                                                                    │"
+10| "│       ▀███             ▀██████████████         有的设计。                                                                                                    │"
   style 0-0 dim
   style 8-39 fg=blue
   style 159-159 dim
-18| "│        ▀███▄            ▀███████████▀                                                                                                                        │"
+11| "│        ▀███▄            ▀███████████▀                                                                                                                        │"
   style 0-0 dim
   style 8-38 fg=blue
   style 159-159 dim
-19| "│          ▀███▄      ▄▄▄    ▀████████▀          我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能帮到您,甚至反而为工作平添了麻烦,请在企业微信群中留  │"
+12| "│          ▀███▄      ▄▄▄    ▀████████▀          我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能帮到您,甚至反而为工作平添了麻烦,请在企业微信群中留  │"
   style 0-0 dim
   style 9-38 fg=blue
   style 159-159 dim
-20| "│            █████▄    ███▄▄   ▀█████▄▄          言,将使用感受告诉我们。每一条反馈,都会帮助我们把它打磨得更好。                                              │"
+13| "│            █████▄    ███▄▄   ▀█████▄▄          言,将使用感受告诉我们。每一条反馈,都会帮助我们把它打磨得更好。                                              │"
   style 0-0 dim
   style 9-38 fg=blue
   style 159-159 dim
-21| "│             ▀█████████████▄▄▄▄█▀█████▀                                                                                                                       │"
+14| "│             ▀█████████████▄▄▄▄█▀█████▀                                                                                                                       │"
   style 0-0 dim
   style 8-39 fg=blue
   style 159-159 dim
-22| "│                   ▀▀███████████▀▀                                                                                                                            │"
+15| "│                   ▀▀███████████▀▀                                                                                                                            │"
   style 0-0 dim
   style 12-34 fg=blue
   style 159-159 dim
-23| "├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"
+16| "├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤"
   style 0-159 dim
-24| "│                                                                         Enter  继续                                                                          │"
+17| "│                                                                         Enter  继续                                                                          │"
   style 0-0 dim
   style 74-84 fg=bright-magenta bold
   style 159-159 dim
-25| "│                                                                                                                                                              │"
+18| "│                                                                                                                                                              │"
   style 0-0 dim
   style 159-159 dim
-26| "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+19| "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
   style 0-159 dim
-27-31| <blank>

+ 31 - 0
apps/cli/tests/tui-first-run-snapshots/60-columns-low-height.expected.txt

@@ -0,0 +1,31 @@
+overlay 60x12 rows=10
+0| "╭──────────────────────────────────────────────────────────╮"
+  style 0-59 dim
+1| "│                     DeepSeek Harness                     │"
+  style 0-0 dim
+  style 22-37 fg=blue bold
+  style 59-59 dim
+2| "│                                                          │"
+  style 0-0 dim
+  style 59-59 dim
+3| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                    │"
+  style 0-0 dim
+  style 59-59 dim
+4| "│                                                          │"
+  style 0-0 dim
+  style 59-59 dim
+5| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验 │"
+  style 0-0 dim
+  style 59-59 dim
+6| "├──────────────────────────────────────────────────────────┤"
+  style 0-59 dim
+7| "│                       Enter  继续                        │"
+  style 0-0 dim
+  style 24-34 fg=bright-magenta bold
+  style 59-59 dim
+8| "│                         ↑/↓ 滚动 ↓                       │"
+  style 0-0 dim
+  style 24-35 dim
+  style 59-59 dim
+9| "╰──────────────────────────────────────────────────────────╯"
+  style 0-59 dim

+ 28 - 36
apps/cli/tests/snapshots/tui-first-run-welcome/60-columns.expected.txt → apps/cli/tests/tui-first-run-snapshots/60-columns.expected.txt

@@ -1,98 +1,90 @@
-terminal 60x30 buffer=normal length=34 base=4 viewport=4
-lifecycle started=0 stopped=0 progress=inactive
-title ""
-cursor visible column=0 viewportRow=29 bufferRow=33
-viewport
-4| " DEEPSEEK HARNESS"
-  style 1-8 fg=bright-magenta bold
-  style 10-16 bold
-5| "╭──────────────────────────────────────────────────────────╮"
+overlay 60x30 rows=27
+0| "╭──────────────────────────────────────────────────────────╮"
   style 0-59 dim
-6| "│                         ▄▄▄▄▄▄   ▄▄                      │"
+1| "│                         ▄▄▄▄▄▄   ▄▄                      │"
   style 0-0 dim
   style 23-36 fg=blue
   style 59-59 dim
-7| "│                     ▄████████▄ ▀████▀                    │"
+2| "│                     ▄████████▄ ▀████▀                    │"
   style 0-0 dim
   style 21-38 fg=blue
   style 59-59 dim
-8| "│                     █▀▀▀▀███████▄██▀                     │"
+3| "│                     █▀▀▀▀███████▄██▀                     │"
   style 0-0 dim
   style 22-37 fg=blue
   style 59-59 dim
-9| "│                     █▄    ▀███ ▀███                      │"
+4| "│                     █▄    ▀███ ▀███                      │"
   style 0-0 dim
   style 22-36 fg=blue
   style 59-59 dim
-10| "│                      ▀█▄     ▀█████                      │"
+5| "│                      ▀█▄     ▀█████                      │"
   style 0-0 dim
   style 23-36 fg=blue
   style 59-59 dim
-11| "│                       ▀█▄▄ █▄▄▀███▄                      │"
+6| "│                       ▀█▄▄ █▄▄▀███▄                      │"
   style 0-0 dim
   style 23-36 fg=blue
   style 59-59 dim
-12| "│                            ▀▀▀▀▀▀                        │"
+7| "│                            ▀▀▀▀▀▀                        │"
   style 0-0 dim
   style 25-34 fg=blue
   style 59-59 dim
-13| "│                                                          │"
+8| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-14| "│                     DeepSeek Harness                     │"
+9| "│                     DeepSeek Harness                     │"
   style 0-0 dim
   style 22-37 fg=blue bold
   style 59-59 dim
-15| "│                                                          │"
+10| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-16| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                    │"
+11| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                    │"
   style 0-0 dim
   style 59-59 dim
-17| "│                                                          │"
+12| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-18| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验 │"
+13| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验 │"
   style 0-0 dim
   style 59-59 dim
-19| "│ 难免粗粝。                                               │"
+14| "│ 难免粗粝。                                               │"
   style 0-0 dim
   style 59-59 dim
-20| "│                                                          │"
+15| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-21| "│ “如切如磋,如琢如磨。”                                   │"
+16| "│ “如切如磋,如琢如磨。”                                   │"
   style 0-0 dim
   style 2-23 bold
   style 59-59 dim
-22| "│                                                          │"
+17| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-23| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实 │"
+18| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实 │"
   style 0-0 dim
   style 59-59 dim
-24| "│ 使用中暴露的问题,也可能促使我们重新审视,甚至推翻已有的 │"
+19| "│ 使用中暴露的问题,也可能促使我们重新审视,甚至推翻已有的 │"
   style 0-0 dim
   style 59-59 dim
-25| "│ 设计。                                                   │"
+20| "│ 设计。                                                   │"
   style 0-0 dim
   style 59-59 dim
-26| "│                                                          │"
+21| "│                                                          │"
   style 0-0 dim
   style 59-59 dim
-27| "│ 我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能 │"
+22| "│ 我们尤其希望听见那些失败、困惑与不顺手的时刻——如果它未能 │"
   style 0-0 dim
   style 59-59 dim
-28| "├──────────────────────────────────────────────────────────┤"
+23| "├──────────────────────────────────────────────────────────┤"
   style 0-59 dim
-29| "│                       Enter  继续                        │"
+24| "│                       Enter  继续                        │"
   style 0-0 dim
   style 24-34 fg=bright-magenta bold
   style 59-59 dim
-30| "│                         ↑/↓ 滚动 ↓                       │"
+25| "│                         ↑/↓ 滚动 ↓                       │"
   style 0-0 dim
   style 24-35 dim
   style 59-59 dim
-31| "╰──────────────────────────────────────────────────────────╯"
+26| "╰──────────────────────────────────────────────────────────╯"
   style 0-59 dim
-32-33| <blank>

+ 28 - 36
apps/cli/tests/snapshots/tui-first-run-welcome/80-columns.expected.txt → apps/cli/tests/tui-first-run-snapshots/80-columns.expected.txt

@@ -1,101 +1,93 @@
-terminal 80x30 buffer=normal length=33 base=3 viewport=3
-lifecycle started=0 stopped=0 progress=inactive
-title ""
-cursor visible column=0 viewportRow=29 bufferRow=32
-viewport
-3| " DEEPSEEK HARNESS"
-  style 1-8 fg=bright-magenta bold
-  style 10-16 bold
-4| "╭──────────────────────────────────────────────────────────────────────────────╮"
+overlay 80x30 rows=27
+0| "╭──────────────────────────────────────────────────────────────────────────────╮"
   style 0-79 dim
-5| "│                              ▄▄▄▄▄▄▄██▀    █▄      ▄                         │"
+1| "│                              ▄▄▄▄▄▄▄██▀    █▄      ▄                         │"
   style 0-0 dim
   style 26-53 fg=blue
   style 79-79 dim
-6| "│                           ▄███████████▄▄   ███▄▄████                         │"
+2| "│                           ▄███████████▄▄   ███▄▄████                         │"
   style 0-0 dim
   style 26-53 fg=blue
   style 79-79 dim
-7| "│                          ████████████████▄ ▀██████▀                          │"
+3| "│                          ████████████████▄ ▀██████▀                          │"
   style 0-0 dim
   style 26-52 fg=blue
   style 79-79 dim
-8| "│                           ██▀▀▀▀▀████████████▄▄██▀                           │"
+4| "│                           ██▀▀▀▀▀████████████▄▄██▀                           │"
   style 0-0 dim
   style 28-51 fg=blue
   style 79-79 dim
-9| "│                           ██       ▀█████▄ ▀█████                            │"
+5| "│                           ██       ▀█████▄ ▀█████                            │"
   style 0-0 dim
   style 28-50 fg=blue
   style 79-79 dim
-10| "│                           ██▄        ▀████▄ ▄████                            │"
+6| "│                           ██▄        ▀████▄ ▄████                            │"
   style 0-0 dim
   style 28-50 fg=blue
   style 79-79 dim
-11| "│                             ██▄         ████████▀                            │"
+7| "│                             ██▄         ████████▀                            │"
   style 0-0 dim
   style 29-50 fg=blue
   style 79-79 dim
-12| "│                              ██▄    ▄▄   ▀█████▀                             │"
+8| "│                              ██▄    ▄▄   ▀█████▀                             │"
   style 0-0 dim
   style 29-49 fg=blue
   style 79-79 dim
-13| "│                               ▀███▄▄▄███▄  ████▄▄                            │"
+9| "│                               ▀███▄▄▄███▄  ████▄▄                            │"
   style 0-0 dim
   style 29-50 fg=blue
   style 79-79 dim
-14| "│                                   ▀▀▀███████▀▀                               │"
+10| "│                                   ▀▀▀███████▀▀                               │"
   style 0-0 dim
   style 31-47 fg=blue
   style 79-79 dim
-15| "│                                                                              │"
+11| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-16| "│                               DeepSeek Harness                               │"
+12| "│                               DeepSeek Harness                               │"
   style 0-0 dim
   style 32-47 fg=blue bold
   style 79-79 dim
-17| "│                                                                              │"
+13| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-18| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                                        │"
+14| "│ 感谢您愿意拨冗试用 DeepSeek Harness。                                        │"
   style 0-0 dim
   style 79-79 dim
-19| "│                                                                              │"
+15| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-20| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。           │"
+16| "│ 目前的版本仍处于内部测试阶段,有些功能仍待完善,有些体验难免粗粝。           │"
   style 0-0 dim
   style 79-79 dim
-21| "│                                                                              │"
+17| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-22| "│ “如切如磋,如琢如磨。”                                                       │"
+18| "│ “如切如磋,如琢如磨。”                                                       │"
   style 0-0 dim
   style 2-23 bold
   style 79-79 dim
-23| "│                                                                              │"
+19| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-24| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的问题,也 │"
+20| "│ 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中暴露的问题,也 │"
   style 0-0 dim
   style 79-79 dim
-25| "│ 可能促使我们重新审视,甚至推翻已有的设计。                                   │"
+21| "│ 可能促使我们重新审视,甚至推翻已有的设计。                                   │"
   style 0-0 dim
   style 79-79 dim
-26| "│                                                                              │"
+22| "│                                                                              │"
   style 0-0 dim
   style 79-79 dim
-27| "├──────────────────────────────────────────────────────────────────────────────┤"
+23| "├──────────────────────────────────────────────────────────────────────────────┤"
   style 0-79 dim
-28| "│                                 Enter  继续                                  │"
+24| "│                                 Enter  继续                                  │"
   style 0-0 dim
   style 34-44 fg=bright-magenta bold
   style 79-79 dim
-29| "│                                   ↑/↓ 滚动 ↓                                 │"
+25| "│                                   ↑/↓ 滚动 ↓                                 │"
   style 0-0 dim
   style 34-45 dim
   style 79-79 dim
-30| "╰──────────────────────────────────────────────────────────────────────────────╯"
+26| "╰──────────────────────────────────────────────────────────────────────────────╯"
   style 0-79 dim
-31-32| <blank>

+ 30 - 17
apps/cli/tests/tui-keyless-smoke.e2e.ts

@@ -25,7 +25,7 @@ const dshBinScript = fileURLToPath(new URL('../src/bin.ts', import.meta.url))
 // needs no config argument at all; these are the overlays under test.
 const scriptedConfigPath = fileURLToPath(new URL('./fixtures/tui-scripted.cordis.yml', import.meta.url))
 const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
-const firstRunSnapshots = fileURLToPath(new URL('./snapshots/tui-first-run-welcome/', import.meta.url))
+const firstRunSnapshots = fileURLToPath(new URL('./tui-first-run-snapshots/', import.meta.url))
 const synchronizedFrameEnd = '\x1b[?2026l'
 
 /**
@@ -166,7 +166,24 @@ function smoke(overrides: Partial<TuiPtySmokeOptions> & {
 
 const firstRunCopy = TUI_FIRST_RUN_WELCOME_NOTICE_COPY[TUI_FIRST_RUN_WELCOME_NOTICE_LOCALE]
 
-/** Project the first synchronized PTY frame containing `marker` into the stable terminal snapshot format. */
+/** Keep only the overlay rows, excluding platform-specific scrollback and the underlying TUI. */
+function overlaySnapshot(snapshot: string, columns: number, rows: number): string {
+  const blocks: string[][] = []
+  for (const line of snapshot.split('\n')) {
+    if (/^\d+(?:-\d+)?~?\| /u.test(line)) blocks.push([line])
+    else if (line.startsWith('  style ') && blocks.length > 0) blocks.at(-1)?.push(line)
+  }
+  const first = blocks.findIndex(block => block[0]?.includes('╭') === true)
+  const last = blocks.findIndex((block, index) => index >= first && block[0]?.includes('╰') === true)
+  if (first < 0 || last < first) throw new Error('first-run PTY snapshot has no complete overlay frame')
+  const overlay = blocks.slice(first, last + 1).flatMap((block, index) => [
+    block[0]!.replace(/^\d+(?:-\d+)?(~)?\|/u, `${String(index)}$1|`),
+    ...block.slice(1),
+  ])
+  return [`overlay ${String(columns)}x${String(rows)} rows=${String(last - first + 1)}`, ...overlay, ''].join('\n')
+}
+
+/** Project the first synchronized PTY frame containing `marker` into an overlay-only snapshot. */
 async function firstRunFrameSnapshot(
   output: string,
   marker: string,
@@ -180,9 +197,7 @@ async function firstRunFrameSnapshot(
   const terminal = new HeadlessTerminal(columns, rows)
   try {
     terminal.write(output.slice(0, frameEnd + synchronizedFrameEnd.length))
-    return (await terminal.snapshot())
-      .replace(/main-session-[0-9a-f-]{36}/gu, 'main-session-{{uuid}}')
-      .replace(/\/[^"\s]*dsh-tui-welcome-\d+-[A-Za-z0-9]+/gu, '/tmp/dsh-tui-welcome')
+    return overlaySnapshot(await terminal.snapshot(), columns, rows)
   } finally {
     await terminal.dispose()
   }
@@ -212,10 +227,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
       columns,
       rows: 30,
       actions: [
-        { waitFor: firstRunCopy.paragraphs[0]!, send: '\r' },
-        columns >= 120
-          ? { waitFor: firstRunCopy.saving, signal: 'SIGTERM', delayMs: 500 }
-          : { waitFor: 'main-session-', occurrence: 2, signal: 'SIGTERM' },
+        { waitFor: `Enter  ${firstRunCopy.continueLabel}`, send: '\r', signalAfterMs: 2_000 },
       ],
       inspect: async (cwd) => {
         expect(await hasTuiFirstRunWelcomeAcknowledgement(join(cwd, '.dsh'))).toBe(true)
@@ -244,8 +256,12 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
       rows: 12,
       actions: [
         { waitFor: firstRunCopy.paragraphs[0]!, send: '\x1b[F' },
-        { waitFor: '企业微信群', send: '\r' },
-        { waitFor: 'main-session-', occurrence: 2, signal: 'SIGTERM' },
+        {
+          waitFor: `Enter  ${firstRunCopy.continueLabel}`,
+          occurrence: 2,
+          send: '\r',
+          signalAfterMs: 2_000,
+        },
       ],
     })
     await expect(await firstRunFrameSnapshot(output, firstRunCopy.paragraphs[0]!, 60, 12))
@@ -268,8 +284,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
         showFirstRunWelcome: true,
         expectedExitCode: process.platform === 'win32' ? 0 : -15,
         actions: [
-          { waitFor: firstRunCopy.paragraphs[0]!, send: '\r' },
-          { waitFor: firstRunCopy.saving, signal: 'SIGTERM', delayMs: 500 },
+          { waitFor: `Enter  ${firstRunCopy.continueLabel}`, send: '\r', signalAfterMs: 2_000 },
         ],
       })
       expect(first).toContain(firstRunCopy.title)
@@ -314,8 +329,7 @@ describe('dsh TUI keyless smoke (real Loader tree in a PTY)', () => {
         showFirstRunWelcome: true,
         expectedExitCode: -15,
         actions: [
-          { waitFor: firstRunCopy.paragraphs[0]!, send: '\r' },
-          { waitFor: firstRunCopy.saving, signal: 'SIGTERM', delayMs: 500 },
+          { waitFor: `Enter  ${firstRunCopy.continueLabel}`, send: '\r', signalAfterMs: 2_000 },
         ],
       })
       expect(next).toContain(firstRunCopy.paragraphs[0])
@@ -515,8 +529,7 @@ describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
         originalLineCount = before.split('\n').filter(Boolean).length
       },
       actions: [
-        { waitFor: firstRunCopy.paragraphs[0]!, send: '\r' },
-        { waitFor: firstRunCopy.saving, signal: 'SIGTERM', delayMs: 500 },
+        { waitFor: `Enter  ${firstRunCopy.continueLabel}`, send: '\r', signalAfterMs: 2_000 },
       ],
       inspect: async (cwd) => {
         const after = await readFile(logPath(