@huseynsnmz/pi-board 0.15.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +24 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +55 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +277 -0
  7. package/package.json +5 -11
  8. package/src/commands/agent-board.ts +26 -2
  9. package/src/commands/attach-flow.ts +4 -0
  10. package/src/core/double-press.mjs +6 -2
  11. package/src/core/slash-commands.mjs +52 -0
  12. package/src/index.ts +1 -1
  13. package/src/runtime/service.mjs +12 -0
  14. package/src/ui/dashboard.ts +59 -25
  15. package/src/ui/pty-attach.ts +8 -3
  16. package/IMPLEMENTATION_PLAN.md +0 -920
  17. package/PRD.md +0 -484
  18. package/PROGRESS.md +0 -142
  19. package/VERIFY.md +0 -115
  20. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  21. package/docs/EXPLORATION.md +0 -187
  22. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  23. package/docs/RELEASE.md +0 -148
  24. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  25. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  26. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  27. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  28. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  29. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  30. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  31. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  32. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  33. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  34. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  35. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  36. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  37. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  38. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  39. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  40. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  41. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  42. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  43. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  44. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  45. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  46. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  47. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  48. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  49. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  50. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  51. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  52. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  53. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  54. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  55. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  56. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  57. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  58. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  59. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  60. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  61. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  62. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  63. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  64. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  65. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  66. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  67. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  68. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  69. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  70. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  71. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  72. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  73. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  74. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  75. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  76. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  77. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  78. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  79. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  80. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  81. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  82. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  83. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  84. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  85. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  86. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  87. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  88. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  89. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  90. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  91. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  92. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  93. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  94. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  95. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  96. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  97. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  98. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  99. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  100. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  101. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  102. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  103. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  104. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  105. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  106. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  107. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  108. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  109. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  110. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  111. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  112. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  113. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  114. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  115. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  116. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  117. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  118. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  119. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  120. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  121. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  122. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  123. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  124. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  125. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  126. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  127. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  128. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  129. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  130. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  131. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  132. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  133. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  134. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  135. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  136. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  137. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  138. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  139. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  140. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  141. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  142. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  143. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  144. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  146. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  147. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  148. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  149. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  150. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  151. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  152. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  153. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  154. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  155. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  156. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  157. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  158. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  159. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  160. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  161. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  162. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  163. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  164. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  165. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  166. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  167. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  168. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  169. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  170. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  171. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  172. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  173. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  174. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  175. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  176. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  177. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  178. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  179. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  180. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  181. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  182. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  183. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  184. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  185. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  186. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  187. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  188. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  189. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  190. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  191. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  192. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  193. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  194. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  195. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  196. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  197. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  198. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  199. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  200. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  201. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  202. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  203. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  204. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  205. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  206. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  207. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  208. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  209. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  210. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  211. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  212. package/scripts/budget-audit.mjs +0 -135
  213. package/scripts/release.mjs +0 -446
  214. package/scripts/release_helper.mjs +0 -309
  215. package/scripts/run-perf-gate.mjs +0 -40
  216. package/scripts/run-tests.mjs +0 -375
@@ -1,204 +0,0 @@
1
- # Issue #222:Attach 首屏就绪设计(spec)
2
-
3
- Drafted: callyouai/gpt-6.1-sol (selected) · callyouai/gpt-6.1-sol (physical) @ 2026-10-21T14:44:19.875Z
4
- Revised: callyouai/gpt-6.1-sol (selected) · callyouai/gpt-6.1-sol (physical) @ 2026-10-22T14:56:20.840Z
5
- Revised: callyouai/gpt-6.1-sol (selected) · callyouai/gpt-6.1-sol (physical) @ 2026-10-22T15:13:30Z
6
- Revised: callyouai/gpt-6.1-sol (selected) · callyouai/gpt-6.1-sol (physical) @ 2026-10-22T15:57:13.939Z
7
-
8
- - Issue:[zhuxixi/pi-agent-board#222](https://github.com/zhuxixi/pi-agent-board/issues/222)
9
- - 状态:两轮异模型设计复核已收敛;第 2 轮无真缺口,父已吸收 1 项定时器细化,等待用户审批。尚未批准或实现;复核不等于实测通过。
10
- - 调查/既有测量基线:`main@9a197f4bc2216e63cbda12f0cd9890ec9b3b6b34`。2026-10-22 回收复核时实际 HEAD 已为 `d41a16bbd3e55c6df1d1cdd60c43a7c2e2492482`;该范围只含 #221 的 events 源码/测试与设计文档,所引用 attach 文件无差异。旧测量仍只归属原基线;P1 必须重新记录实际检出、核对依赖变化,不把不同 commit 当同一性能样本。
11
- - 实施计划:[issue-222-attach-ready-check.md](../plans/2026-10-23-issue-222-attach-ready.md)。本文定义行为、状态和验收契约;计划负责任务、修改文件与执行顺序。
12
- - 落位例外:本轮 Plannotator 规划模式只允许在当前工作目录内写 Markdown,因此临时放在 `plans/`。不写 `~/.github-issue-driven/`、不改配置、不创建 worktree;规划解除并获准后按 issue-driven 归档到隔离 worktree 的 `docs/superpowers/specs/2026-10-21-issue-222-attach-ready-design.md`,调研/复核留痕迁至 issue 归档目录。不得把临时路径当作已完成归档。
13
-
14
- ## 1. 问题与目标
15
-
16
- 忙碌会话持续输出时,attach 等待约 2.5 秒才显示内容;快照本身约 5ms 已到。已确认的原因是每个输出解析回调重置 250ms 安静期,最后由 2500ms 硬上限结束 loading。旧逻辑负责遮挡日志回放和 resize-jiggle;现代协议仍复用了这段等待。
17
-
18
- 用户要求同时解决切入速度和过渡画面:第一个会话画面直接包含最新回复尾部及输入区域;活动提问/选择界面保持当前状态;之后不得露半帧、旧内容、错误尺寸、残影或双光标。用户主动向上滚动后不抢回底部。体验参照 Claude Code 会话恢复/子代理切换,不推测其实现。
19
-
20
- 既有单次记录为持续输出首屏 2512ms、安静首屏 268ms。这些数字只证明现状延迟;半帧泄露仍是假设,候选性能与成功率均未测量。
21
-
22
- ## 2. 调研依据与边界
23
-
24
- 已复用前轮代码与历史调查,并读取 issue #222 正文、PR #122 的实现说明和 CR:序号连续性、旧尺寸、协议降级、重启空基线和旧日志污染都已有兼容性契约。`finishCollecting()` 的快照校验与本地 `whenIdle()` 不能证明子 TUI(终端界面)已完成整帧;resize applied 也不证明重绘完成。
25
-
26
- JFox 永久笔记中的 attach 输入分段、就地删除和安装路径材料用于确认输入/命令入口/部署边界,没有找到可直接证明本问题闪屏根因的笔记。最近查询结果和历史依据记入本轮调研留痕,不将历史安装版本当作当前加载版本。
27
-
28
- 本轮只设计;不运行新实验、不改源码或测试、不提交或发布。既有 `screen.log`、legacy settle/jiggle、底部投影、主题桥、恢复协议和 detach 契约均为本次设计的约束。
29
-
30
- ## 3. 范围与非目标
31
-
32
- - 现代 snapshot+subscribe 路径改用有证据的画面就绪条件;正常忙碌会话不再等待全局安静期。
33
- - 首屏和后续绘制共用画面完整性契约,避免控制消息绕过输出合并器。
34
- - 同尺寸、尺寸变化、空基线、恢复和 legacy 分开定义、测量及验收。
35
- - 不升级依赖、不修改协议版本、不增加 End 注入、不增大全屏清理次数;不夹带输入拦截或延迟显示 loading 的体验改动。
36
- - 缺少可证明的完成信号时不猜测;以诊断证据为依据决定是否需要另审协议扩展。
37
-
38
- ## 4. 组件与数据流
39
-
40
- ### 4.1 职责与权威来源
41
-
42
- `createTerminalAttachClient()` 继续拥有订阅、序号校验、补发、恢复和 generation(子进程代次)判断;`snapshotReady` 仅表示传输基线已校验,不直接触发显示。`PtyAttachComponent` 拥有本地解析、当前目标尺寸、视口意图和可发布投影;TUI 继续拥有调度、差分绘制和终端写入。测试观测器只观察实际输出,不能成为生产就绪逻辑的一部分。
43
-
44
- ```mermaid
45
- flowchart LR
46
- Host[Runner 快照与输出] --> Client[现有协议客户端:校验与恢复]
47
- Client --> Parser[当前代次本地 xterm:持续解析]
48
- Parser --> Gate[就绪决策:基线/完整边界/尺寸/代次]
49
- Gate --> View[完整投影:行/光标/视口来源]
50
- View --> TUI[现有 TUI 调度与 Terminal.write]
51
- TUI --> Observe[测试:外层 xterm 与逐帧断言]
52
- ```
53
-
54
- ### 4.2 本地状态与最小数据模型
55
-
56
- 以下为待新增的设计接口,名称不是现存 API。纯状态定义在 `src/core/pty-attach-render.mjs`;组件中的适配器读取现有事件并执行作用。保持现有 `createAttachOutputRenderScheduler()` 的 40ms 合并,不把每个 chunk 都变成全屏渲染。
57
-
58
- | 状态数据 | 定义与失效条件 |
59
- | --- | --- |
60
- | `connectionToken` | 当前 socket 连接标识;旧 socket 消息不可改动当前状态。 |
61
- | `baselineToken` | 每次权威基线替换递增;与 `generation`、parser 实例共同围住异步解析队列。 |
62
- | `targetSize`/`geometryRevision` | 当前实际 body 列/行及尺寸修订号;真实 resize 或目标变化就使旧尺寸投影失效。 |
63
- | `baselineState` | `unverified`/`hydrating`/`valid-empty`/`valid-content`;日志回放永远不能赋值为协议有效基线。 |
64
- | `frameState` | `unknown`/`open`/`complete`;来自真实 xterm 同步更新模式与已验证的帧边界。 |
65
- | `deadlineState` | `pending`/`expired`;组件适配器持有现有保护定时器,按本次连接/等待身份写入到期事件。纯决策只消费该输入,不读时钟、不启动 timer;到期仅改变等待原因/有界恢复动作,不能把未完成画面升级为 ready。旧身份到期回调和 dispose 后回调无效。 |
66
- | `committedView` | 对有同步标记的现代路径无条件保留最小完整视图:当前可见完整投影行、光标、来源 token、尺寸修订号、视口意图及可见范围交互所需元数据;不得引用会继续变化的 xterm 行对象。 |
67
- | `transitionFinished` | 只从 false 变 true;只管首次 attach 副作用,不被恢复重置。 |
68
- | `closed` | dispose 后永久 true;所有回调、调度、探测与监听器都必须服从。 |
69
-
70
- `decideAttachPresentation(state)` 是拟新增纯函数,返回 `wait`(附原因)/`publish`/`legacy`/`closed`,不读时钟、终端或文件。`canPublishAttachView(viewIdentity, currentIdentity)` 是拟新增来源比较纯函数,用于拒绝旧 token、尺寸、generation 或 parser 的投影。当前 `receivedOutput` 只表示缓冲区有内容,不再单独承担现代协议就绪语义。
71
-
72
- ### 4.3 解析隔离与基线替换
73
-
74
- 在新的权威基线进入时,组件替换本地 xterm 实例,保留构造参数、现有历史上限与投影规则;将 `term` 的 readonly 绑定改为受组件管理的当前 parser。新 parser 先安装同样的边界观察,再接收权威帧和有序补发;旧 parser 的回调即使后来完成也无法污染新 buffer,且必须同时通过 token 与实例身份检查才能发布。旧 parser 和 CSI 注册句柄被 dispose;不能只给回调加 token,却让旧 write 留在同一个活动解析队列中。
75
-
76
- 替换发生在 `snapshotReady`,因为此前序号校验尚未完成;`snapshotBegin` 只保存尺寸和当前 collecting 身份,不把未校验内容发布。新基线使历史 floor、selection 与缓存的来源同步更新;滚动意图按既有行为保留,只有内容身份确实变更或旧坐标不再有效时才清除,不因普通 live 输出回到底部。非空水合后继续使用 `historyFloorAfterHydrate()`;空基线 floor 为 0。
77
-
78
- 水合生成的 ANSI(控制终端的转义序列)本身含模式恢复和光标停车,不能把其中任何一次 CSI 关闭当作用户画面已经完成。`hydrating` 期间禁止边界发布;水合回调完成后才能读取恢复后的模式、光标和有效投影。`finishCollecting()` 随后同步 emit 的 catch-up flush 必须按原有顺序排队;每个完整边界上的投影需当时复制,不能等整个 chunk 回调时才读取已经进入下一帧的网格。
79
-
80
- ### 4.4 完整投影与交互
81
-
82
- 对有同步标记的现代路径,帧边界发布、最小 `committedView` 和 `render()` 投影选择入口的门控是无条件机制:首屏和后续绘制共用同一规则,status、ticker、鼠标和主题渲染都不能绕过。同步块打开期间只显示同来源、同尺寸的上一张完整视图;不存在时显示 loading。关闭边界通过 `parser.registerCsiHandler()` 观察并返回 `false`,保持默认解析;先验证 handler 时刻能读取的网格/光标状态,再取不可变投影。此机制不以 P2 是否重现用户现场闪屏为开关;P2 用于定位现场原因并验证机制接缝,不能用“本实验未复现”免除 A3。
83
-
84
- 帧尾之后的 Pi 光标停车通常在同步块外;这类光标/模式变化按现有 cursor coalescing 处理,只能更新完整网格对应的光标,不能解锁正在写入的新网格。测试分别检查硬件光标和字符属性形成的软光标。最小完整视图只在有效完整边界/可验证的完整投影变更时提交,继续使用现有 40ms 调度;不逐 chunk 复制历史、不反复序列化终端。超出可见范围的额外历史缓存或通用缓存框架不属默认方案,只有具体证据表明现有接缝不足时才另行修订。无同步标记和 legacy 的保证边界仍由 §5.3/A7 单独定义。
85
-
86
- 滚动、选择、复制和链接命中必须使用与当前呈现一致的行/坐标来源,不能显示缓存 A 却从解析中 B 复制。优先只缓存可见范围及现有命中测试所需的文本、属性和链接信息;不复制整个历史。同步更新期间用户触发超出缓存范围的滚动请求时,记录视口意图,在下一完整边界用既有 `project()` 应用,期间保持当前完整画面;新输出不得取消该意图。若实测所需的选择/命中信息无法局限在这个接缝内,诊断 gate 要求修订缓存设计,不能默默退化交互。
87
-
88
- ## 5. 状态、事件与就绪规则
89
-
90
- ### 5.1 正常现代协议路径
91
-
92
- 正常首次 publish 必须同时满足:模式为 protocol、当前非空权威基线已校验并解析、没有已知未完成同步更新、完整画面属于当前连接/基线/generation、目标尺寸有效且有该尺寸画面的完成证据、组件未关闭。水合后恢复模式为 closed 的同尺寸非空快照可以作为已提交屏幕;恢复为 open 的快照必须等其真正帧尾,不能把传输层 `snapshot_end` 当帧尾。
93
-
94
- 现代协议 publish 不调用 `deferAttachSettle()`;输出持续更新也不重置就绪。首次满足规则后通过现有 `finishAttachTransition()` 完成 timer cleanup、一次强制替换、健康探测、主题桥和颜色探测。已完成之后的普通更新只发布完整投影,不重开 loading,不重做首次副作用。
95
-
96
- 现有 connect 回调会在模式判定前 `startAttachSettle()`,因此仅删除输出中的 defer 不足以改变转换时序。确认 `mode=protocol` 时组件必须解除已武装的 250ms settle,并将现有 2500ms hard timeout 收编为 `deadlineState` 的到期输入;已经排队的旧 timer 回调仍须核对模式、等待身份与 closed。模式尚为 undecided 时两类 timer 均不得直接调用 `finishAttachTransition()`;现代路径只有合格 publish 决策能执行首次转换。若客户端选择 legacy,则恢复既有 settle/jiggle/hard-timeout 路径和期限,不重置原等待起点来额外增加等待,协议 probe 期限不变。这保留 #128 的“首次转换单点翻转、hooks 至多一次”约束,并防止 S/A4/A5/C 等待超过 250ms 时提前启动主题桥、清屏或健康探测。
97
-
98
- | 事件/前置 | 画面状态与动作 | 验收 ID |
99
- | --- | --- | --- |
100
- | 初始 undecided | 保持 loading;日志/探针广播可按现有路径解析但不能成为 protocol 首屏。 | A2、A6 |
101
- | `snapshotBegin` 同尺寸 | 保存 collecting 身份,不发 resize、不发布。 | A1、A4 |
102
- | `snapshotBegin` 不同尺寸 | 记录目标修订号,只发一次真实目标尺寸;遮住旧尺寸内容。 | A4 |
103
- | 校验失败/`resubscribing` | 延用客户端既有有界恢复;本地基线标无效,不发布未校验内容。 | A2、A6 |
104
- | 非空 `snapshotReady` | 替换 parser,进入 hydrating,权威帧后接原顺序补发;解析完成再判完整模式与尺寸。 | A1–A3、A6 |
105
- | 空 `snapshotReady` | 替换 parser、清 floor 与旧内容,状态 valid-empty;等待本代 child 的有效完整编辑画面。 | A5、A6 |
106
- | 同步块开启/关闭 | 开启只解析;关闭且满足 gate 才复制投影并请求绘制。同 chunk 关开必须保住关帧瞬间。 | A3、A8 |
107
- | 同 generation 断连 | 可保留同尺寸完整画面并显示重连状态;停止新连接身份未确认的发布。 | A6 |
108
- | 同 generation 连续重放恢复 | 保留 parser 与连续缓存;依客户端连续性事件恢复发布,不要求必有新快照。 | A6 |
109
- | epoch 更换/新的空基线 | 立即使旧画面失效;只允许新 generation 的权威基线或完整 live 画面。 | A5、A6 |
110
- | protocol→legacy fallback | 关闭现代发布门控,保留既有 legacy settle/jiggle/heal,不借现代就绪事件提前放行。 | A7 |
111
- | dispose/detach | 撤销 listener、定时器、CSI handler、parser 与缓存;延迟回调零发布;detach 行为沿用现有协议。 | A9 |
112
-
113
- ### 5.2 尺寸变化和冷启动的诊断 gate
114
-
115
- 当前协议没有“本次 resize 的 child 全面重绘已完成”的权威事件,`applied`、下一输出 chunk、任意一次同步块关闭都不足以替代。因此 A4 是**实施前诊断 gate**:批准后先用可控 child 和真实 TUI 记录时序,确认能否仅用现有生产信号归属当前目标尺寸的完整重绘;fixture 的帧号/INPUT 标记只能作为独立测试真值,不能混进生产判定。
116
-
117
- A5 同样要求区分 boot 文本、raw-mode 信号和完整编辑画面。当前 `childInputReady` 只用于颜色报告安全,不单独证明首屏就绪;生产不能硬编码搜索 `INPUT` 或中文界面文本。诊断阶段必须验证真实 Pi 完整渲染边界与其启动序列能否形成充分条件。
118
-
119
- 诊断结果必须是可审查的事件条件和反例测试。如果 A4/A5 缺少充分条件,则**不进入生产修复 P4**:记录无法区分的最小两条事件轨迹,给出所需最小信号并修订本 spec 后重新复核/审阅。不能仅优化同尺寸路径后声称 #222 全部解决,也不能为这些场景添加新的固定等待。
120
-
121
- ### 5.3 超时和兼容输出
122
-
123
- 2500ms 在现代协议中只是诊断/恢复保护线,不能强制 publish 已知半帧、空基线或错误尺寸。到期保留可 detach 的等待画面,记录等待原因;只复用现有客户端恢复预算,不添加无界重订阅或反复重置 2500ms 的死循环。协议错误最终按既有预算进入 legacy;纯粹没有完整帧的场景不能谎报成功,记试验失败。
124
-
125
- 无同步标记的输出走既有保守策略,单列为兼容场景,不宣称具备现代整帧保证。legacy 的 250ms/2500ms 与 jiggle 行为保持,既有 `AGENT_BOARD_TERMINAL_SNAPSHOT=0` 仍是显式回滚开关。probe 超时等兼容性期限不因本 issue 改变。
126
-
127
- ## 6. 可测性拆分
128
-
129
- 以下模块接口是拟新增设计,不作为当前仓库能力对外陈述;函数的最终导出名可在不改变边界和验收 ID 的前提下调整,并更新计划。
130
-
131
- | 接缝/位置 | 独立输入与输出 | 最低足够测试层级/覆盖边界 | 验收 ID |
132
- | --- | --- | --- | --- |
133
- | `decideAttachPresentation()`,现有 render helper | 含 `deadlineState` 的纯状态 → wait 原因/publish/legacy/closed;无 timer、I/O | unit:枚举门禁组合、空基线、尺寸 unknown、保护线 pending/expired、过期身份到期无效且超时不可强制就绪 | A1、A4、A5、A7、A9 |
134
- | `canPublishAttachView()`,同 helper | 投影身份与当前身份 → boolean | unit:旧连接、基线、parser、尺寸、generation、closed 均拒绝 | A2、A6、A9 |
135
- | `observeAttachFrameBoundary()`,组件薄适配(待新增) | xterm 公开 CSI hook/模式,输出 immutable 完整边界;返回 dispose | component:真 xterm、分片 CSI、同 chunk 关开、水合屏蔽、帧尾光标;禁止并行 ANSI 正则解析器 | A3、A8、A9 |
136
- | `commitAttachView()`,组件内部方法(待新增) | 通过 gate 的真实 parser 投影 → 当前可见缓存与 requestRender | integration:实际 TUI 输出、所有渲染入口、滚动与命中测试;不把调用次数当屏幕正确 | A1–A3、A8 |
137
- | 现有 `handleAttachEvent()`/`pushOutput()`/`finishAttachTransition()` | 协议事件 → parser 隔离、一次副作用、恢复和主题 I/O | integration:真实客户端与 runner;独立 fixture 事件提供时序真值 | A2、A4–A7、A9 |
138
- | `attach-presentation-fixture.ts`(待新增) | 原样 Terminal.write → 外层 xterm 中逐帧网格/光标/时间 | unit+component:先注入单帧旧内容、空屏、上下半屏错号、第二光标,确认全能检出;块外写也可见 | A10 |
139
- | 重复实验统计 helper(scenario 测量文件内,待新增) | JSON 试验记录 → 分位数/成功率/Wilson 区间/失败清单 | unit:已知样本、超时 `+∞`、基础设施失败单列、固定分位数算法;opt-in E2E 测真实性能 | A10、A11 |
140
-
141
- 新增 `test/attach-ready-report.test.mjs` 对统计纯函数进行已知输入断言;普通 `npm test` 只跑确定性用例。重复 100/30 次实验显式开启,不加入每次 CI。真实时间试验与 fake clock 回归分开,父进程统一单调时钟标记;跨进程只比较父侧收到事件的时刻,并报告测量边界。
142
-
143
- ## 7. 验收矩阵
144
-
145
- 自动化证明门禁、事件顺序和写给终端的画面;用户实测核对当前终端中的实际呈现。每项都必须有计划任务及实际证据,自动化通过不能关闭实测 pending。
146
-
147
- 本矩阵的 A1–A12/U1–U3 仅限 issue #222 命名空间;报告、检查记录与 issue 评论使用 `#222-A11` 等全称。仓库 README/VERIFY.md 中 #121/#91 的裸 A11 是 `npm run test:perf` 的既有三项 perf-gate,不是本设计的 1660 次重复试验;两者分别留证,不能互相替代。
148
-
149
- | ID | 功能点/决策 | 验收方式 | 命令/操作与通过标准 | 计划任务 |
150
- | --- | --- | --- | --- | --- |
151
- | A1 | 同尺寸完整基线立即发布,忙碌/安静均不等 settle | 自动化(unit+integration) | `node --test test/pty-attach-render.test.mjs test/pty-attach-ready.test.mjs`;完整条件已成立时未到 250ms 即正确首屏,零 resize/jiggle;完整帧被推迟到 >250ms 且 <2500ms 时不得提前转换 | P3、P4 |
152
- | A2 | 权威来源、校验失败、catch-up 和水合不露中间状态 | 自动化(integration) | 上述 ready 测试+`node --test test/pty-attach-protocol-e2e.test.mjs test/terminal-snapshot.integration.test.mjs`;毒日志、未校验帧、旧队列均不发布,原序号恢复不退化 | P2–P4 |
153
- | A3 | 同步块内所有绘制入口都看完整画面 | 自动化(component+integration) | ready 测试;分片、同 chunk 关开、status/ticker/主题各触发绘制;完整目标前零半帧,后续也无错误帧 | P2–P4 |
154
- | A4 | 目标尺寸的完成证据、零/一次真实 resize | 自动化(integration;实施前 gate) | ready+protocol 测试,G1–G3;旧尺寸帧与 ack 不可发布;先证明确切生产条件或停止 P4 修订设计 | P2–P4 |
155
- | A5 | 空基线/冷启动不会显示旧屏、boot 或半个编辑器 | 自动化(integration;实施前 gate) | ready 测试,C;boot、raw-mode、完整 live 帧分阶段;仅充分条件成立后发布,否则停止 P4 | P2–P4 |
156
- | A6 | reconnect、epoch reset、resnapshot 来源正确 | 自动化(integration) | ready+protocol 测试驱动既有 restart fixture,R 两子格;无新快照续播可恢复,新代次不回流旧画面 | P3、P4 |
157
- | A7 | unframed/legacy fallback、保护期限与异常等待 | 自动化(unit+integration) | ready 测试+`node --test test/pty-attach-desync-health-e2e.test.mjs test/pty-attach-desync-heal.test.mjs`;legacy settle/jiggle 保留,现代超时不露已知坏屏,detach 可用 | P3、P4 |
158
- | A8 | 底部、活动界面、长历史、宽字符与光标;上滚及复制命中一致 | 自动化(component+integration) | ready 测试,L 与附加交互用例;首屏 TAIL+INPUT 或当前选择界面,滚动意图保留,复制/链接与可见内容一致 | P3、P4 |
159
- | A9 | 一次就绪副作用、颜色延迟、dispose 清理 | 自动化(unit+integration) | ready 测试+`node --test test/pty-attach-scheme-readiness.test.mjs test/pty-attach-settle-scheme.test.mjs test/pty-attach-startup-color.test.mjs`;D/T 与旧回调零发布;延迟完整帧前 hooks 为 0、首次合格 publish 后为 1,迟到 timer 不再触发;现有 pending scheme 语义保留 | P3、P4 |
160
- | A10 | 观察器和统计本身能检错、失败留痕完整 | 自动化(unit+static) | ready 测试含观察器自测+`node --test test/attach-ready-report.test.mjs`;人为错误帧不能被最终正确帧抹掉,超时不从分布消失 | P2、P3、P5 |
161
- | A11 | 延迟与成功率达到分场景目标 | 自动化(opt-in E2E) | 计划中的真实入口重复 CLI;B/Q/L 总延迟 P95≤200ms、最大≤500ms,S/C/R 完整画面后 P95≤100ms、最大≤200ms;G 总延迟 P95≤500ms、最大≤1000ms,完整目标帧后 P95≤100ms;每格零产品画面错误 | P5 |
162
- | A12 | 类型、性能、全仓回归、coverage、打包 | 自动化(static+build+unit/integration) | `npm run verify` 全通过;不得只用 `npm test` 替代 | P6 |
163
- | U1 | 实际终端切入当前工作区、无可见闪烁/残影/双光标 | 用户实测 | 候选明确加载后,隔离会话 fullscreen B/Q/L 各10次、regular B/Q各5次,录屏逐帧核对;每次首屏正确、过程无错误画面 | P6 |
164
- | U2 | 实际终端尺寸变化、冷启动、重连/重启 | 用户实测 | 同候选环境,各5次隔离试验;首屏尺寸与来源正确,无历史位置再跳底、无旧代次回流;冷启动耗时单列 | P6 |
165
- | U3 | 交互及退出仍正常 | 用户实测 | U1/U2 每类至少1次继续输入、主动上滚后让 child 输出、选择复制已知文本、detach/再 attach;输出不抢回、复制一致、退出正常 | P6 |
166
-
167
- A11 的具体子格、样本量和统一统计口径在计划 Verification 中固定:B/Q/S 与 L 两子格各100次,G1–G3/C/R两子格/T两子格/U两子格/D各30次,两版共1660次正式试验。每格5次预热排除且单列,候选/基线交错,首屏后观察至少3秒。P50/P95 为排序后第 `ceil(p×n)` 项;产品超时按 `+∞`;所有失败保留、95% Wilson 区间不外推未测环境。每次上限10秒,基础设施失败超过5%整格无效。
168
-
169
- ## 8. 部署、回滚与保证边界
170
-
171
- U1–U3 在代码实现、自动化完成、候选可加载后执行;不以录屏替代精确自动化时序,也不把外层 xterm 当作真实终端像素证据。终端渲染/图形合成与输入法行为不适合由进程内模拟完全证明,所以保留实测;60fps 录屏无法排除更短的像素变化,报告须写明这一限制。
172
-
173
- 实际检查使用独立 Pi 测试会话、临时 agent root 和明确的本地扩展入口,不修改全局设置、不对活跃用户会话发测试输入或 kill。启动前后记录父 Pi、child Pi、TUI、xterm 的实际加载路径/版本、终端名/版本及尺寸;现有仓库 Pi TUI 1.1.0 与宿主安装目录 1.3.0 必须分清,目录名不是解析证明。
174
-
175
- 候选加载前只读检查 `~/.pi/agent/extensions/agent-board` 的存在与真实指向,并核对候选入口的 realpath/commit/文件哈希。U 项固定使用隔离进程的一次性显式入口,不执行 `pi install`、不改全局 symlink:待实现获准后,以临时 `PI_CODING_AGENT_DIR`、`--no-extensions` 关闭自动发现,再用 `--extension` 指向候选 worktree 的绝对 `index.ts`;显式入口在关闭自动发现后仍会加载。以下只是已从宿主 Pi 的 `docs/cli.md`、`docs/packages.md` 与 `docs/environment-variables.md` 核实的拟执行命令,当前不启动新 Pi:
176
-
177
- ```bash
178
- PI_CODING_AGENT_DIR="$ATTACH_222_AGENT_DIR" pi --no-extensions --no-mcp --extension "$ATTACH_222_CANDIDATE_ENTRY" --tui-mode fullscreen --verbose
179
- ```
180
-
181
- `ATTACH_222_AGENT_DIR` 是获准后准备的临时配置目录,不复用真实会话;`ATTACH_222_CANDIDATE_ENTRY` 是经 realpath 核对的候选 `index.ts` 绝对路径。regular 验收只改变 `--tui-mode`。执行前核对实际 Pi 版本对应的 CLI 支持和安全认证前置;不复制/回显凭据,不因配置缺失改用全局安装。全新试验进程启动后,再从实际资源加载记录与模块解析证据核对入口和依赖;`--verbose` 或启动命令本身不是证明。不能证明运行的是候选、发生双重加载或无法在隔离环境启动时,U 项保持 pending。
182
-
183
- 发布与全局安装不在本轮授权内;临时 `AGENT_BOARD_TERMINAL_SNAPSHOT=0` 仅在隔离进程验证 legacy,不持久更改配置。正式回退可按既有 PR/版本流程撤销改动;该开关只恢复 legacy 路径,不等于新的完整帧保证。
184
-
185
- A4/A5 没有充分生产信号、完整投影需要越界改协议、解析 API 不可用、重复实验出现未归因错误,均停止生产修复并留证修订。单次 guard 失败不以增加等待掩盖;同架构连续三次针对性假设失败转架构复盘。人工验收环境不可用时 U 项保持 pending,不能宣称“快且不闪”全部验收。
186
-
187
- ## 9. 流程交接
188
-
189
- 当前处于 issue-driven 步 4(设计/根因报告),不能把先前实施计划替代设计 gate。设计完成后按 `spec-cross-review` 异模型复核,默认最多 4 轮,每轮只读 fresh-context reviewer,由主会话统一修订和写留痕;然后在 Plannotator 提交同一计划及本 spec。
190
-
191
- 实际授权归档不存在,默认 `stepwise`。当前用户明确要求使用 issue-driven 并补 spec,覆盖设计与该流程要求的只读复核;不升格为自动 push/PR/合并授权。认领/issue 评论、仓外归档、worktree、代码实现和提交等后续动作受当前规划限制及既有授权约束,未执行的步骤明确标为 pending。
192
-
193
- ## 10. 交叉复核状态
194
-
195
- 2026-10-22:第 1 轮从运行产物收回完整 structuredOutput,提出 1 个真缺口和 4 项细化,父逐条裁定并修订。第 2 轮重新核对五视角与真实代码,确认前轮修订有效,返回 0 个真缺口、1 项细化、6 项核对通过。原报告把 passes 误写为 5,父保存原始版后只请求计数勘误;已核验 findings/lenses/被审版本均未变化,未重跑审查、未新增轮次。
196
-
197
- 两轮 child 实际模型均为 `zai-coding-cn/glm-5.3`,主会话最近 assistant physical 为 `callyouai/gpt-6.1-sol`,异构审计通过。父按第 2 轮建议吸收 R2-D1:明确初始 settle 和 hard timer 的模式门控/处置,并补 A1/A9 的延迟完整帧时序断言。按技能规则,零真缺口且细化吸收后进入设计待确认,不启动第三轮;此最终小修订不是又一轮独立复核结果。
198
-
199
- 哈希、运行 ID、原始回报、计数勘误和逐条裁定统一记在 [spec-cross-review-state.md](/home/elling/.github-issue-driven/zhuxixi/pi-agent-board/issue-222/research/spec-cross-review-state.md)、[第 1 轮报告](/home/elling/.github-issue-driven/zhuxixi/pi-agent-board/issue-222/research/spec-review-round-1.md) 与 [第 2 轮报告](/home/elling/.github-issue-driven/zhuxixi/pi-agent-board/issue-222/research/spec-review-round-2.md),不把本文件的被审哈希写回本文件。设计复核已收敛,但审批 gate 仍关闭;必须经用户确认并解除规划限制后,才可进入 worktree、实施或 Step 6 正式计划。
200
-
201
-
202
- ## Execution Handoff — 2026-10-23
203
-
204
- Design approved by the user at 2026-10-23T08:20:19+08:00. Planning restrictions are explicitly ended for this approved execution. This is the approved design with only link relocation and this handoff note; archived spec SHA-256 remains `10c0a158ff4ac222d0c11a394f14b275292e96ee44b047cf17f1be45f949a64e`. Diagnostic #222-A4/A5 and manual U1–U3 gates remain binding. No commit/push/publication authorization is implied.
@@ -1,90 +0,0 @@
1
- # spec — issue #229:vim-style j/k/l in the board list
2
-
3
- Drafted: kimi-coding/k3 (selected) · kimi-coding/k3 (physical) @ 2026-10-23T22:50:00+08:00
4
- Revised: kimi-coding/k3 (selected) · kimi-coding/k3 (physical) @ 2026-10-23T23:00:30+08:00 — cross-review R1:R1-G1 接受(§4.1 In evidence 行补 l attach、A7 改逐行断言);R1-G2 接受(新增 A9 负向断言);R1-D1 接受(U1 部署前置改为按 `pi list` 原源同形移除/恢复);R1-D2 接受(§7 先例引用改 dashboard-prewarm-smoke.ts)
5
- Revised: kimi-coding/k3 (selected) · kimi-coding/k3 (physical) @ 2026-10-23T23:05:30+08:00 — cross-review R2:R2-D1 接受(evidence 帮助行手势串改 `→/l attach` 与 peek 口径对称);R2-D2 接受(A7 枚举补齐 4 处 j/k 行断言 + 负向断言扩展到 In peek/session/evidence 旧措辞)
6
-
7
- 基线:main @ 759d659。调研留档:`research/01-keybinding-audit-and-history.md`(issue 测量全部验证属实)。
8
-
9
- ## 1. 背景与问题
10
-
11
- board 的 `list` 模式是用户停留最久的视图,却是唯一不接受 `j`/`k` 的导航视图:`j`/`k` 落到 printable fallback 弹错误的 INSERT toast;`l` 全模式空闲,而它的箭头孪生 `→` 是主 attach 手势。两个只读视图(session/evidence)已绑 `j`/`k` 滚动。目标:让 list/select/peek 的导航与 attach 键位符合 vim 肌肉记忆,且不破坏任何文本输入模式与既定键位契约(`h` = Hold,PR #151)。
12
-
13
- ## 2. 设计决策表
14
-
15
- | # | 决策 | 依据 |
16
- |---|---|---|
17
- | D1 | 采纳 issue 的 Proposed direction 全部内容 | 测量经调研验证属实,与历史决策无冲突 |
18
- | D2 | `j`/`k` **复用** `moveSelection()` / `peekStep()` —— 继承**环绕(wrap)**语义,与 `↑`/`↓` 逐字节一致;**不**实现 issue Verification 措辞里的 "clamp" | wrap 是 issue #52 的既定行为,`test/dashboard-navigation.test.mjs` 锁定;`j`/`k` 与 `↑`/`↓` 行为分裂(一个 wrap 一个 clamp)比不环绕更糟。这是与 issue 措辞的唯一偏差 |
19
- | D3 | 顺带清理 `handleListKey` 的两处重复行(`data === "d"` ×2、`ctrl("x")` ×2,merge 残留死代码) | 零行为变化;改动同函数,顺手清理成本最低 |
20
- | D4 | select 不绑 `l`(无对应动作,`space` 才是 toggle);session 不绑 `l`(无 `→` 可镜像,已有 `enter`/`a` 两条 attach 路径) | issue 已论证 |
21
- | D5 | 补齐**已有** `j`/`k` 在 Transcript/Evidence 的文档欠账(help 面板与 README 四表均未写) | 调研发现:欠账不止新键,既有键也未写 |
22
- | D6 | 不动 `h`(Hold)、不动 dispatch/filter/rename/reply(`j`/`k`/`l` 保持字面字符)、不动 attach 侧键位 | `h` = Hold 是 PR #151 契约;文本模式字母必须字面;attach 是裸 PTY 透传的另一套面(JFox `202610182229584028`) |
23
-
24
- ## 3. 逐模式键位变更(src/ui/dashboard.ts,行号为 main @ 759d659 参考值)
25
-
26
- | 模式 | handler | 新增绑定 | 行为(复用现有路径) |
27
- |---|---|---|---|
28
- | list | `handleListKey` :359 | `j` / `k` | `moveSelection(+1 / -1)`,环绕,与 `↑`/`↓` 同一路径 |
29
- | list | `handleListKey` | `l` | `attachSelected()`,镜像 `→` / `>` |
30
- | select | `handleSelectKey` :400 | `j` / `k` | `moveSelection(±1)` |
31
- | peek | `handlePeekKey` :427 | `j` / `k` | `peekStep(±1)`,环绕 |
32
- | peek | `handlePeekKey` | `l` | `attachPeek()`,镜像 `→` / `>` |
33
- | evidence | `handleEvidenceKey` :484 | `l` | `attachSelected()`,镜像 `→`(`j`/`k` 已有) |
34
- | session | — | 无变更 | `j`/`k` 已有;不绑 `l`(D4) |
35
- | dispatch / filter / rename / reply | `handleTextMode` | 无变更 | `j`/`k`/`l` 保持字面字符(D6) |
36
-
37
- 插入位置约定:各 handler 内紧贴对应箭头键分支之后,保持「先功能键后字母」的既有阅读顺序。list 模式的新分支须位于 printable fallback(:394)之前。
38
-
39
- ## 4. 文案同步(五处,调研发现的三处纪律 + 两处欠账)
40
-
41
- 1. `renderHelp` 帮助面板(:1699-1730):`↑/↓` 行 → `↑/↓ or j/k`;`→ or >` 行 → `→ / > / l`;"In select" 行补 `j/k`;"In peek" 行 `↑↓` → `↑↓/j/k`、`→ attach` → `→/l attach`;"In session"/"In evidence" 行补已有的 `j/k` 滚动(D5);**"In evidence" 行另补 attach 手势串 `→/l attach`**(§3 给 evidence 新增了 `l`,帮助面板必须同步,否则 README/帮助/行为三处不一致;手势串保留主手势 `→` 与 peek 行口径对称,不只写新增的 `l`)。
42
- 2. `listHints()` NORMAL 行(:1362):`→ attach` → `→/l attach`。
43
- 3. `listHints()` SELECT 行(:1350):补 `j/k move`。
44
- 4. README Main list 表:`Up/Down` 行补 `j`/`k`;`Right / >` 行补 `l`。
45
- 5. README Peek / Transcript / Evidence 三表:Peek 补 `j`/`k` 与 `l`;Transcript/Evidence 补已有的 `j`/`k`(D5),Evidence 补 `l`。
46
-
47
- ## 5. 非目标
48
-
49
- - 不绑 `h` 的任何新义(Hold 契约不动,不「补全 hjkl 四键」)。
50
- - 不给 session 模式加 `l`;不给 select 模式加 `l`。
51
- - 不动 attach 视图键位、不动文本输入模式。
52
- - 不改变 wrap 语义本身(想改 clamp 需另开 issue 并影响 `↑`/`↓`)。
53
-
54
- ## 6. 验收矩阵(必答)
55
-
56
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
57
- |----|--------|----------|----------|----------|
58
- | A1 | list 模式 `j`/`k` 移动选择 | 自动化(unit,探针) | `node --test test/dashboard-vim-keys.test.mjs`(新探针 `test-support/vim-keys-wrap.ts`,复用 navigation-wrap 模式) | 3 行 fixture 上 `j`/`k` 序列的 `selectedId` 轨迹与 `↑`/`↓` 同序列完全一致(含两端环绕) |
59
- | A2 | select 模式 `j`/`k` | 自动化(unit,同探针) | 同上 | 进入 select 后 `j`/`k` 移动选中且环绕 |
60
- | A3 | peek 模式 `j`/`k` → `peekStep` | 自动化(unit,同探针) | 同上 | `peekId`/`selectedId` 轨迹与 `↑`/`↓` 一致 |
61
- | A4 | `l` 在 list/peek/evidence 触发 attach | 自动化(unit,同探针) | 构造器注入 fake `done` 回调捕获 `{action:"attach", viewId}` | 三模式各捕获一次 attach 且 viewId 正确;fixture 用非 busy row(时序约束见 §7) |
62
- | A5 | dispatch/filter/rename/reply 的 `j`/`k`/`l` 仍为字面字符 | 自动化(unit,同探针) | 各文本模式下按 `j`/`k`/`l`,白盒读 `this.input` | input 含字面 `jkl`,无模式跳出 |
63
- | A6 | session/evidence `j`/`k` 滚动回归保护 | 自动化(unit,同探针) | 断言 `sessionScrollTop`/`evidenceScrollTop` | `j`↓`k`↑ 与 `↓`↑ 行为一致(既有行为不回归) |
64
- | A7 | help/hints 文案更新 | 自动化(unit,同探针渲染路径) | 探针调用渲染输出,**逐行断言**:① 主 `↑/↓ or j/k` 行存在;② `→ / > / l` 行存在;③ "In select" 行含 `j/k`;④ "In peek" 行含 `↑↓/j/k` 与 `→/l attach`;⑤ "In session" 行含 `j/k` 滚动;⑥ "In evidence" 行含 `j/k` 滚动与 `→/l attach`;⑦ NORMAL hint 含 `→/l attach`;⑧ SELECT hint 含 `j/k`;⑨ 负向:无 j/k 的旧 `↑↓` 措辞在主行与 In peek/In session/In evidence 行均不再出现 | 逐行断言全过(不用子串级 `含 l 字样`,防单行遗漏被其它行掩护;①-⑧ 覆盖 §4.1 全部变更点含 D5 欠账项) |
65
- | A9 | 负向契约:select/session **不**绑 `l`(D4) | 自动化(unit,同探针) | select 模式 `handleInput("l")` 后断言 mode 仍为 select 且 fake `done` 零调用;session 模式同理 | 两模式按 `l` 均无副作用(不 attach、不切模式、input 不增长) |
66
- | A8 | 全量门槛 | 自动化(build/test) | `npm run verify` | typecheck + perf gate + 全测试 + coverage 阈值(lines 85/funcs 80/branches 70)+ pack:dry 全过 |
67
- | U1 | 真实面板手感 | 用户实测 | 部署前置:① `pi list` 记录当前安装源标识(本机维护者环境为 git 源 `git:github.com/zhuxixi/pi-agent-board`,其它环境可能是 npm 源)② `pi remove <该源标识>`(命令出处:pi docs cli.md `pi remove <source>`,源标识与安装时同形;避免原源与 worktree 双 identity 并存)③ `pi install <worktree 绝对路径>` → `pi /agent-board`;步骤:① list 按 `j`/`k` 走行含两端环绕 ② 按 `j` 不再弹 INSERT toast ③ `l` attach 选中会话 ④ detach 后 `?` 帮助显示新绑定(含 evidence 行的 `l`) ⑤ `i` 进 INSERT 后 `j`/`k`/`l` 仍是字面字符 | 五项全部符合预期;验完 `pi remove <worktree 路径>` 并按 ① 记录的原源 `pi install <原源标识>` 恢复 |
68
- | U2 | README 四表与行为一致 | 用户实测 | 对照 README 四个表格逐行核对实际行为 | 表格无遗漏、无虚构 |
69
-
70
- ## 7. 可测性拆分设计(必答)
71
-
72
- **不新增函数、不改现有拆分**。各 handler 已是独立方法;本次每个 handler 只加 1–2 个早返分支,复用现有纯路径:`moveSelection`(:266)、`peekStep`(:1163)、`attachSelected`(:1174)、`attachPeek`(:1180)。副作用本就隔离在这四个方法 + `done` 回调 + `input` buffer 内,本次改动不引入新耦合。
73
-
74
- **测试边界**:`DashboardComponent.handleInput()` 公开入口。观测点:
75
-
76
- - `selectedId` / `peekId` / `sessionScrollTop` / `evidenceScrollTop` / `input` —— 白盒读(TS-private 运行时可访问,`navigation-wrap.ts` 已有先例);
77
- - attach 意图 —— 构造器第 4 参 `done` 注入 fake 捕获(先例:`test-support/dashboard-prewarm-smoke.ts` 第 42 行 `(r) => doneCapture?.push(r)` 与第 82-92 行断言 `done[0].stopFirst === false`;`done` 为第 4 参见 dashboard.ts:164,`DashboardResult` 含 `{action:"attach"; viewId; stopFirst}` 见 :58)。
78
-
79
- **探针形态**:新建 `test-support/vim-keys-wrap.ts`(`node --experimental-transform-types` 运行,tsconfig 排除 test-support,与既有探针一致),fixture 用 `createService` + `mkdtemp` 三行 view(navigation-wrap 模式),`handleInput` 直调。一个探针覆盖 A1–A7 的全部按键序列,输出 JSON 供 `test/dashboard-vim-keys.test.mjs` 断言。
80
-
81
- **触发时序约束(A4)**:`requestAttach` 内 `planDashboardAttach` 对 busy row 走 confirm 分支(进 `pending` 而非 `done`)。探针 fixture 固定使用 `idle/exited` 非 busy row,测直接 attach 路径;confirm 分支的 `pending` 机制由既有测试(如 `dashboard-delete-key.test.mjs`)覆盖,不在本 issue 重复。
82
-
83
- **D3 死代码清理**:零行为变化,不单设验收项,由 A1–A8 回归兜底。
84
-
85
- ## 8. 保证边界
86
-
87
- - 只保证 `j`/`k` 与 `↑`/`↓` 行为**完全一致**(含环绕);不承诺 vim 语义里的 clamp。
88
- - 只保证 list/select/peek 三模式新增导航、`l` 在 list/peek/evidence 三模式 attach;其它模式零变更。
89
- - 文案同步覆盖 §4 五处;不保证仓内其它文档(如 demo 视频、外部博客)同步。
90
- - U1 的部署前置(先 remove git 源再 install 本地路径)若被跳过,可能加载到旧代码,验收结论无效。
@@ -1,146 +0,0 @@
1
- # Issue #232 — attach drops trailing background-only cells (design)
2
-
3
- Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-23T22:58:00+08:00
4
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-23T23:20:00+08:00 — R1-G1, R1-D1, R1-D2, R1-D3
5
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-23T23:35:00+08:00 — R2-G1, R2-D1, R2-D2
6
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-23T23:45:00+08:00 — R3-D1, R3-D2, R3-D3
7
-
8
- Repo: `zhuxixi/pi-agent-board` · base `main` @ `759d659` · issue #232 (bug)
9
-
10
- ## 1. Problem
11
-
12
- In attach mode a full-screen TUI whose theme background differs from the host
13
- terminal's default renders with the host background showing through every
14
- character-less region (nvim's empty buffer, tab-bar gaps). Colours survive into
15
- the xterm buffer; they are destroyed when the buffer is serialized back to ANSI.
16
-
17
- ## 2. Root cause (verified against `759d659`)
18
-
19
- `lineToAnsi()` (`src/ui/pty-attach.ts:1962`) computes `last` = last cell whose
20
- `getChars()` is non-empty, returns `""` when `last < 0`, and renders
21
- `x <= last`. Background-only cells therefore never reach the output:
22
-
23
- - blank line + painted bg → `""` (whole line's bg dropped);
24
- - content line → everything right of the last character dropped.
25
-
26
- ## 3. Design
27
-
28
- Three surgical changes inside `lineToAnsi()`, no signature/module changes.
29
-
30
- **C1 — track the painted extent.** During the existing per-line scan, also
31
- record `lastBg` = last cell with `!cell.isBgDefault()`. Compute
32
- `renderEnd = Math.max(last, lastBg)`. The scan keeps skipping
33
- `getWidth() === 0` continuation cells and never reads `extended`, so the
34
- reusable-cell contract from issue #158 stays satisfied.
35
-
36
- **C2 — render through `renderEnd`.** Replace both bounds derived from `last`:
37
- the main render loop (`x <= renderEnd`) and the empty-line guard
38
- (`if (renderEnd < 0)`). Blank default-bg lines still return `""`, so ordinary
39
- shell output stays byte-identical. Blank painted lines now enter the normal
40
- loop, which emits their cells' SGR bg plus `" "` padding; content lines pad
41
- through their last painted column.
42
-
43
- **C3 — cursor bound.** The post-loop "cursor past content" check compares
44
- `cursor.col > renderEnd` instead of `> last`, so a cursor inside the newly
45
- painted region is drawn exactly once by the in-loop `isCursor` path.
46
-
47
- ### Why this is sufficient
48
-
49
- pi-tui renders a component line as-is; the SGR run in the line paints the row's
50
- bg. Padding exists only to give the SGR run cells to apply to. For an nvim
51
- screen the bg covers the full width, so each line serializes to full width.
52
-
53
- ## 4. Decisions
54
-
55
- | Decision | Rationale |
56
- | --- | --- |
57
- | Follow the reporter's patch shape (3 changes) | Matches the verified root cause; smallest diff; reported as visually fixed. |
58
- | Selection over padded cells becomes inverse-video | Correction to the issue comment: `pointWithinSelection` is evaluated per column, not per char. Drawing a selection over blank area matches real terminal behaviour; copied text is built from the selection model, not from padding, so copy output is unchanged. |
59
- | `isBgDefault()` only — `isInverse()` deliberately excluded | Keeps the fix inside the reported class (explicit bg). Reversed-video-only blank regions are a separate, unmeasured case; see follow-up. |
60
- | No pure-helper extraction | The defect is the emission bound, not the arithmetic; extracted-helper unit tests would not observe it. See §6. |
61
- | Byte cost accepted | Painted screens now emit full-width lines, but the attach renderer is differential and only rewrites changed lines. |
62
-
63
- ## 5. Non-goals
64
-
65
- - No separate legacy/replay acceptance item: legacy attach is the **same**
66
- `PtyAttachComponent` and the same renderer (`replayScreenLog()` feeds the same
67
- `this.term`, `project()` always calls `lineToAnsi`), so C1–C3 apply to it
68
- automatically — that is an expected benefit, not a boundary. The byte-cost
69
- trade-off applies there too; A1–A5 exercise the same serialization path.
70
- - Inverse-video-only blank regions (`SGR 7` with default bg) — same bug class,
71
- different trigger; file a follow-up if a real app hits it.
72
- - Capping padding at a viewport edge instead of the last painted column.
73
- - Dashboard peek (text view) and web dashboard (real xterm.js frontend).
74
-
75
- ## 6. Testability split (design constraint)
76
-
77
- **Test boundary:** `PtyAttachComponent.render(width)` — the public surface
78
- already used by `test-support/hyperlink-cell-smoke.ts`. Height comes from the
79
- stub TUI (`tui.terminal.rows`), not from the argument. The fixture
80
- constructs terminal state by writing ANSI into the component's real
81
- `@xterm/headless` terminal, isolates all side effects (socket path
82
- `/no/such/socket`, stub TUI, `dispose()` in `finally`), and returns the raw
83
- rendered lines as JSON. Assertions live in `node:test` and never touch the
84
- terminal directly.
85
-
86
- **Pieces:**
87
-
88
- | Piece | Kind | Test |
89
- | --- | --- | --- |
90
- | `test-support/attach-bg-paint-smoke.ts` | fixture (side effects isolated here) | driven by the .mjs test via `--experimental-transform-types` |
91
- | `test/pty-attach-bg-paint.test.mjs` | assertions | `npm test` |
92
-
93
- **Why no pure function is split out:** `lineToAnsi` is already pure given
94
- `(line, reusable, term, selection, cursor)`; the only thing that can regress is
95
- *which cells reach the output*. A helper returning `renderEnd` would be unit
96
- tested while the actual loop bound could still be reverted to `last`, i.e. the
97
- test would pass with the bug present. The render-boundary test fails if any of
98
- C1/C2/C3 is reverted. If a future change makes this loop hot enough to justify
99
- extraction, the same smoke test remains valid.
100
-
101
- **Red-green proof (A8):** the fixture must produce cells with
102
- `getChars() === ""` **and** a non-default bg — only erase sequences (EL `\x1b[K`
103
- / ED `\x1b[2J`) under an active bg create those. Writing literal spaces would
104
- make `getChars()` non-empty and the unpatched code would pass. Before
105
- committing, revert C2's bound to `last` temporarily and confirm A1/A2 fail, then
106
- restore; record both runs in the PR description.
107
-
108
- ## 7. Acceptance matrix
109
-
110
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
111
- | --- | --- | --- | --- | --- |
112
- | A1 | 空白行的非默认背景不再被丢弃 | 自动化验证(integration,组件 render 输出) | `node --test test/pty-attach-bg-paint.test.mjs`;fixture 先发 `\x1b[48;2;30;30;46m` 再对空行发 `\x1b[K`(EL擦除,生成 `getChars()===""` 的 bg-only 单元格) | 该行渲染结果非空且含 `48;2;30;30;46` |
113
- | A2 | 内容行尾部背景渲染到最后被涂色的列 | 同上 | 写 `AB` → `\x1b[41G`(1-based 第 41 列 = 0-based col 40)→ `\x1b[K` | `AB` 后有 38 个默认背景空格(0-based col 2–39),其后 40 个空格带 `48;2;30;30;46`(col 40–79);尾部 `\x1b[0m` 不计 |
114
- | A3 | 默认背景的空白行保持空串(零字节开销路径不回退) | 同上 | fixture 中纯文本屏幕的空白行 | 对应行 `=== ""` |
115
- | A4 | 宽字符行的背景填充列数正确 | 同上 | 写 `中`(宽字符)→ 在 bg 激活下 `\x1b[K` 擦到行尾 | 渲染行显示宽度 === 40,且 `中` 之后紧跟 38 个涂色空格(续列 `getWidth()===0` 不计入输出) |
116
- | A5 | 光标落在填充区内只绘制一次 | 同上 | 光标置于涂色空白行的中部,`cursorHidden=false`(fixture 形状照抄 `test-support/cursor-visibility-smoke.ts`) | 该行恰含一个 `CURSOR_MARKER` 与一个反显块 |
117
- | A6 | 类型与模块契约不破 | 自动化验证(static) | `npm run typecheck`(注意:tsconfig 排除 `test`/`test-support`,fixture 类型由 A1 运行时暴露) | 退出码 0 |
118
- | A7 | 全量门禁 | 自动化验证(build/test suite/coverage/pack) | `npm run verify` | 全部通过(覆盖率阈值 lines 85 / funcs 80 / branches 70) |
119
- | A8 | 测试对修复有判别力(防「未修复也绿」) | 自动化验证(mutation,一次性,非 CI) | 临时把 C2 的边界改回 `x <= last` → 跑 A1/A2 → 还原 | 回退时 A1/A2 均失败,还原后均通过;两次输出记入 PR 描述 |
120
- | A9 | 填充区参与选区时仍可反显 | 自动化验证(integration,组件 render 输出) | fixture 通过私有字段 `selection = { anchor: { line, col: 20 }, focus: { line, col: 60 } }` 选中涂色区(形状照抄 `MouseSelection`,`src/ui/pty-attach.ts:159`) | 该行在 col 20–60 的渲染片段含反显属性 `7`;复制文本(`selectionText()`)不受 padding 影响 |
121
- | U1 | 真实 nvim/catppuccin attach 观感 | 用户实测 | 见下方「U1 执行前置」,时机:PR 分支可安装之后、合并之前 | 空区域为 `#1e1e2e`;光标行高亮横贯整行 |
122
-
123
- **U1 执行前置**(否则 attach 的是 npm 旧包,看不到修复):
124
-
125
- 0. 确认 payload 是 PR head:`git log -1` 与 PR 的 head commit 对齐(worktree 就是 PR 分支时天然满足)。
126
- 1. 在 worktree 内 `npm install`。
127
- 2. 让 Pi 加载该 checkout:`pi install "$(pwd)"`(README「Local checkout」)或临时软链
128
- `ln -s "$PWD" ~/.pi/agent/extensions/agent-board`(README「Development auto-discovery」)。
129
- 3. **重启 pi** —— 扩展只在启动时加载。
130
- 4. `/agent-board` → attach 到 nvim/catppuccin 会话,观察空缓冲区与光标行。
131
- 5. 收尾:`pi remove "$(pwd)"` 或 `rm ~/.pi/agent/extensions/agent-board`。
132
-
133
- > 记录:报告者已在 issue 评论中对该补丁做过一次视觉验证;U1 在合并前按上述步骤
134
- > 在 PR 构建上复确认一次即可,不必重复造场景。A5 的 `CURSOR_MARKER`
135
- > (`\x1b_pi:c\x07`)路径已由 `test-support/cursor-visibility-smoke.ts` 证明稳定。
136
-
137
- ## 8. Risks
138
-
139
- | Risk | Mitigation |
140
- | --- | --- |
141
- | Full-width serialization increases bytes for painted screens | Differential renderer; A2/A3 pin the cheap path. |
142
- | Selection highlight now covers padding | Intentional and documented; copy path unaffected. |
143
- | Cursor double-block regression | C3 + A5. |
144
- | Wide-char padding misalignment | A4. |
145
- | Test fixture drifts from the component's private API | Same pattern as existing smoke fixtures (`hyperlink-cell-smoke.ts`); only `term`, `receivedOutput`, `finishAttachTransition` are touched. `test-support/` is outside `tsconfig` (`exclude: ["node_modules", "test", "test-support"]`), so fixture type errors surface at A1's test run, not at A6 — same as every existing smoke fixture. |
146
- | Test passes on unpatched code | A1/A2 use EL/ED-erased bg-only cells (literal spaces would keep `getChars()` non-empty); A8 records the red-green proof. |