@huseynsnmz/pi-board 0.15.1 → 0.16.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 (215) hide show
  1. package/CHANGELOG.md +26 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +53 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +276 -0
  7. package/package.json +18 -19
  8. package/src/index.ts +9 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/src/ui/double-left.ts +5 -1
  12. package/src/ui/focus.ts +7 -1
  13. package/src/ui/waiting.ts +10 -5
  14. package/IMPLEMENTATION_PLAN.md +0 -920
  15. package/PRD.md +0 -484
  16. package/PROGRESS.md +0 -142
  17. package/VERIFY.md +0 -115
  18. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  19. package/docs/EXPLORATION.md +0 -187
  20. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  21. package/docs/RELEASE.md +0 -148
  22. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  23. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  24. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  25. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  26. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  27. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  28. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  29. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  30. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  31. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  32. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  33. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  34. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  35. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  36. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  37. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  38. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  39. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  40. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  41. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  42. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  43. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  44. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  45. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  46. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  47. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  48. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  49. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  50. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  51. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  52. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  53. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  54. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  55. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  56. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  57. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  58. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  59. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  60. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  61. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  62. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  63. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  64. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  65. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  66. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  67. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  68. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  69. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  70. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  71. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  72. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  73. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  74. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  75. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  76. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  77. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  78. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  79. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  80. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  81. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  82. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  83. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  84. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  85. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  86. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  87. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  88. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  89. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  90. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  91. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  92. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  93. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  94. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  95. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  96. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  97. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  98. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  99. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  100. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  101. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  102. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  103. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  104. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  105. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  106. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  107. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  108. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  109. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  110. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  111. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  112. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  113. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  114. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  115. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  116. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  117. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  118. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  119. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  120. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  121. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  122. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  123. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  124. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  125. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  126. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  127. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  128. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  129. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  130. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  131. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  132. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  133. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  134. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  135. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  136. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  137. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  138. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  139. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  140. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  141. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  142. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  143. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  144. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  146. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  147. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  148. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  149. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  150. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  151. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  152. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  153. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  154. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  155. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  156. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  157. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  158. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  159. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  160. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  161. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  162. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  163. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  164. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  165. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  166. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  167. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  168. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  169. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  170. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  171. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  172. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  173. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  174. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  175. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  176. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  177. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  178. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  179. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  180. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  181. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  182. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  183. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  184. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  185. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  186. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  187. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  188. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  189. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  190. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  191. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  192. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  193. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  194. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  195. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  196. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  197. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  198. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  199. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  200. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  201. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  202. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  203. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  204. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  205. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  206. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  207. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  208. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  209. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  210. package/scripts/budget-audit.mjs +0 -135
  211. package/scripts/patch-vulns.mjs +0 -59
  212. package/scripts/release.mjs +0 -446
  213. package/scripts/release_helper.mjs +0 -309
  214. package/scripts/run-perf-gate.mjs +0 -40
  215. package/scripts/run-tests.mjs +0 -375
@@ -1,244 +0,0 @@
1
- # Key-Hint Doc Sync Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Sync three stale key hints with the bindings already implemented in `src/ui/dashboard.ts` (issue #245): the peek footer's key list, the README Peek/Transcript tables' missing `!`, and the README's undocumented multi-select keys.
6
-
7
- **Architecture:** One string literal in `renderPeek` plus three README edits; one render assertion added to the existing `test-support/vim-keys-wrap.ts` probe so the peek footer cannot drift again. No handler logic, no new files, no new test infrastructure.
8
-
9
- **Tech Stack:** TypeScript (runtime), `node --test` + `--experimental-transform-types` probe (test), Markdown (docs).
10
-
11
- **Spec:** `docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md` (decisions D1–D5; spec §6 option **A** — multi-select gets its own 9-row table — confirmed by the user)
12
-
13
- ## Global Constraints
14
-
15
- - Tabs for indentation in `.ts`; English comments and commit messages.
16
- - **Do not touch any handler logic** (spec D5): no changes to `handleListKey` / `handleSelectKey` / `handlePeekKey` / `handleSessionKey` / `handleEvidenceKey`.
17
- - **Do not touch the `?` help overlay or the NORMAL/SELECT hint lines** — they are already correct (spec D5).
18
- - The multi-select table is **exactly 9 rows**, one per `handleSelectKey` binding, including `?` and `!` (spec D3, hardened by cross-review R1-D1).
19
- - Do **not** add a `!` row to the Evidence table: `handleEvidenceKey` has no `!` binding (spec §7).
20
- - `npm run verify` must pass before the PR (AGENTS.md).
21
- - Worktree discipline: every path under `/home/elling/work/git-repo/pi-agent-board/.pi/worktrees/issue-245-key-hints-doc-sync` (= `$WT`); git via `git -C $WT`. Branch `issue-245-key-hints-doc-sync`, commit subjects end with `(issue #245)` **and** the PR-number suffix `(#N)` once known.
22
-
23
- ## Review Focus
24
-
25
- These are the conditions the spec implies but no task's tests can cover; each is pinned in the task noted.
26
-
27
- 1. **Peek footer is the last line of `renderPeek`, and `fitToHeight` slices the tail** (`src/ui/dashboard.ts:1280`). A tall peek body silently truncates the footer, making the new assertion fail for the wrong reason. Pinned by the `rows` bump in Task 1, Step 1 (cross-review R1-D3).
28
- 2. **The multi-select table count** — 9 rows, not 7. Dropping `?`/`!` re-creates the very debt this issue fixes. Pinned by Task 2, Step 3's `rg` count check (cross-review R1-D1).
29
- 3. **The Evidence table must still have no `!` row.** Pinned by Task 2, Step 3's negative check.
30
- 4. **`README.md:120`'s prose must not duplicate the new table's content** — it should point at the table instead. Pinned by Task 2, Step 4 (manual read).
31
-
32
- ---
33
-
34
- ### Task 1: Peek footer copy + render assertion
35
-
36
- **Files:**
37
- - Modify: `src/ui/dashboard.ts` (`renderPeek` footer, currently line 1543 — locate by the string `↑↓ prev/next · →/> attach`)
38
- - Modify: `test-support/vim-keys-wrap.ts` (`out.copy` block, currently lines ~119-140)
39
- - Modify: `test/dashboard-vim-keys.test.mjs` (A7 test, currently lines 38-63)
40
-
41
- **Interfaces:**
42
- - Consumes: existing `renderPeek(width)` and the probe's white-box `dash.mode` / `dash.peekId` / `dash.selectedId` handles (already used at `vim-keys-wrap.ts:100-110`).
43
- - Produces: probe field `out.copy.peekRender` (string) that Task 3's verification relies on indirectly.
44
-
45
- **Spec acceptance IDs:** A1, A2, A3, A4 (+ Review Focus 1).
46
-
47
- - [ ] **Step 1: Add the failing assertion and the probe capture**
48
-
49
- In `test-support/vim-keys-wrap.ts`, inside the `out.copy` block, after the `evidenceRender` capture and before `dash.mode = "list";`, add:
50
-
51
- ```ts
52
- // Peek footer: raise the height first — fitToHeight slices the tail, and the
53
- // footer is renderPeek's last line (cross-review R1-D3).
54
- tui.terminal.rows = 40;
55
- dash.mode = "peek"; dash.peekId = ids[0]; dash.selectedId = ids[0];
56
- const peekRender = dash.render(120).join("\n");
57
- tui.terminal.rows = 24;
58
- ```
59
-
60
- and extend the `out.copy` initializer on the following line to include `peekRender`:
61
-
62
- ```ts
63
- out.copy = { help120, listRender, selectRender, sessionRender, evidenceRender, peekRender, help80Lines: help80.length };
64
- ```
65
-
66
- In `test/dashboard-vim-keys.test.mjs`, in the A7 test: add `peekRender` to the destructuring line, and append before the closing `});`:
67
-
68
- ```js
69
- // #245 A1/A3: the peek footer advertises j/k and l, and the old wording is gone.
70
- assert.match(peekRender, /↑↓\/j\/k prev\/next/);
71
- assert.match(peekRender, /→\/l attach/);
72
- assert.doesNotMatch(peekRender, /↑↓ prev\/next · →\/> attach/);
73
- ```
74
-
75
- - [ ] **Step 2: Run the test to verify it fails**
76
-
77
- Run: `cd $WT && node --test test/dashboard-vim-keys.test.mjs`
78
- Expected: FAIL — `/↑↓\/j\/k prev\/next/` does not match, because the footer still reads `↑↓ prev/next`.
79
-
80
- - [ ] **Step 3: Change the footer copy**
81
-
82
- In `src/ui/dashboard.ts`, `renderPeek`'s else-branch (currently line 1543), replace:
83
-
84
- ```ts
85
- out.push(clip(t.fg("dim", "↑↓ prev/next · →/> attach · d done · v transcript · e evidence · r reply · esc back"), width));
86
- ```
87
-
88
- with:
89
-
90
- ```ts
91
- out.push(clip(t.fg("dim", "↑↓/j/k prev/next · →/l attach · d done · v transcript · e evidence · r reply · esc back"), width));
92
- ```
93
-
94
- (Matches the `↑↓/j/k … · →/l …` house style that #237 established for the session/evidence footers at `:1565` / `:1600`.)
95
-
96
- - [ ] **Step 4: Run the test to verify it passes**
97
-
98
- Run: `cd $WT && node --test test/dashboard-vim-keys.test.mjs`
99
- Expected: PASS — both tests, including the three new assertions.
100
-
101
- - [ ] **Step 5: Commit**
102
-
103
- ```bash
104
- git -C $WT add src/ui/dashboard.ts test-support/vim-keys-wrap.ts test/dashboard-vim-keys.test.mjs
105
- git -C $WT commit -m "docs(ui): advertise j/k and l in the peek footer (issue #245)"
106
- ```
107
-
108
- ---
109
-
110
- ### Task 2: README — `!` rows and the multi-select table
111
-
112
- **Files:**
113
- - Modify: `README.md` (multi-select prose `:120`; Peek table `:152-167`; Transcript table `:169-183`; new `### Multi-select` section after the Main list table)
114
-
115
- **Interfaces:**
116
- - Consumes: Task 1's copy as the source of truth for the peek footer wording (the README Peek table must not contradict it).
117
- - Produces: the documentation surface Task 3's U1 checklist walks.
118
-
119
- **Spec acceptance IDs:** U1 (the `!` rows + 9-row table), Review Focus 2, 3, 4.
120
-
121
- - [ ] **Step 1: Add `!` to the Peek table**
122
-
123
- In `README.md`'s Peek table, insert after the `h` row and before the `Up` / `Down` / `j` / `k` row:
124
-
125
- ```markdown
126
- | `!` | Open node-pty diagnostics and repair hints. |
127
- ```
128
-
129
- - [ ] **Step 2: Add `!` to the Transcript table**
130
-
131
- In the Transcript table, insert after the `h` row and before the `Left` / `Esc` / `<` row:
132
-
133
- ```markdown
134
- | `!` | Open node-pty diagnostics and repair hints. |
135
- ```
136
-
137
- (Wording is copied verbatim from the Main list table's `!` row so the same feature reads the same everywhere.)
138
-
139
- - [ ] **Step 3: Add the `### Multi-select` section**
140
-
141
- Insert this section immediately after the Main list table and before `### Peek`:
142
-
143
- ```markdown
144
- ### Multi-select
145
-
146
- Press `m` in the main list to act on several sessions at once.
147
-
148
- | Key | Action |
149
- | --- | --- |
150
- | `Up` / `Down` / `j` / `k` | Move the selection. |
151
- | `Space` | Toggle the current row. |
152
- | `a` | Select all visible rows. |
153
- | `u` | Clear the selection. |
154
- | `d` | Confirm marking the selected inactive rows Done. |
155
- | `Ctrl+X` | Delete the selected Done rows (confirmation prompt). |
156
- | `m` / `Esc` / `Ctrl+C` | Leave multi-select mode. |
157
- | `?` | Open the help overlay. |
158
- | `!` | Open node-pty diagnostics and repair hints. |
159
- ```
160
-
161
- - [ ] **Step 4: Point the prose at the new table instead of duplicating it**
162
-
163
- Replace the existing `README.md:120` line:
164
-
165
- ```markdown
166
- - `m` enters multi-select mode. Use `Space` to toggle rows, `a` to select all visible rows, `u` to clear the selection, `d` to mark inactive rows Done, or `Ctrl+X` to delete selected Done rows.
167
- ```
168
-
169
- with:
170
-
171
- ```markdown
172
- - `m` enters multi-select mode; see [Multi-select](#multi-select) for its keys.
173
- ```
174
-
175
- - [ ] **Step 5: Verify the tables by counting, not by eye**
176
-
177
- Run each check and confirm the stated output:
178
-
179
- ```bash
180
- cd $WT
181
- # 9 rows in the multi-select table (excluding header + separator)
182
- sed -n '/^### Multi-select/,/^### Peek/p' README.md | grep -c '^| `'
183
- # expect: 9
184
- # `!` documented four times: main list, multi-select, peek, transcript — and NOT in evidence
185
- grep -c '^| `!`' README.md
186
- # expect: 4
187
- sed -n '/^### Evidence/,/^### PTY attach/p' README.md | grep -c '^| `!`'
188
- # expect: 0
189
- ```
190
-
191
- - [ ] **Step 6: Commit**
192
-
193
- ```bash
194
- git -C $WT add README.md
195
- git -C $WT commit -m "docs: document ! and multi-select keys in the README (issue #245)"
196
- ```
197
-
198
- ---
199
-
200
- ### Task 3: Full gate + manual verification checklist
201
-
202
- **Files:** none modified — this task runs the gate and records results.
203
-
204
- **Interfaces:**
205
- - Consumes: Tasks 1–2.
206
- - Produces: the A5 evidence and the U1/U2 record posted to issue #245.
207
-
208
- **Spec acceptance IDs:** A5, U1, U2.
209
-
210
- - [ ] **Step 1: Run the full verification gate**
211
-
212
- Run: `cd $WT && npm run verify`
213
- Expected: PASS — typecheck, perf gate, all tests (currently 1404: 1401 pass / 3 opt-in skips), coverage thresholds (lines 85 / funcs 80 / branches 70), `pack:dry`.
214
-
215
- - [ ] **Step 2: Re-read the diff for the spec's non-goals**
216
-
217
- Run: `cd $WT && git diff --stat main...HEAD && git diff main...HEAD -- src/ui/dashboard.ts`
218
- Expected: `src/ui/dashboard.ts` shows **exactly one changed line** (the peek footer). If any handler, the help overlay, or the NORMAL/SELECT hint lines appear in the diff, revert that hunk — it violates spec D5.
219
-
220
- - [ ] **Step 3: U1 — walk the README against the live dashboard (after merge)**
221
-
222
- Deploy prelude (spec R1-D2): merge the PR, `git pull` on the main checkout, restart pi (the local checkout loads via auto-discovery, README "Development auto-discovery"), then `pi /agent-board`. Note: the `!` bindings themselves already ship in v0.13.0, so steps ① and ② can be sanity-checked on an older install — only the README text must be the merged version.
223
-
224
- ① Open Peek, press `!`, confirm the node-pty diagnostics view opens.
225
- ② Open the transcript (`v`), press `!`, same.
226
- ③ Enter multi-select (`m`) and walk all 9 rows of the new table: movement (`↑`/`↓`/`j`/`k`), `Space` toggle, `a`, `u`, `d`, `Ctrl+X`, `m`/`Esc`/`Ctrl+C` exit, `?`, `!`.
227
-
228
- - [ ] **Step 4: U2 — the peek footer on screen (after merge)**
229
-
230
- `space` to open Peek; read the footer.
231
- Expected: `↑↓/j/k prev/next · →/l attach · d done · v transcript · e evidence · r reply · esc back`.
232
-
233
- - [ ] **Step 5: Record results on issue #245**
234
-
235
- Post the A5 command + result, then the U1/U2 observations. If any U item cannot run yet, mark it `pending` — do not claim full acceptance.
236
-
237
- ---
238
-
239
- ## Self-Review Notes
240
-
241
- - **Spec coverage:** D1 → Task 1; D2 → Task 2 Steps 1-2; D3 → Task 2 Steps 3-4 (option A, 9 rows); D4 → Task 1 Step 1; D5 → Task 3 Step 2's revert check. A1/A3 → Task 1; A2 → Task 1 Steps 2/4; A4 → Task 3 Step 1; A5 → Task 3 Step 1; U1/U2 → Task 3 Steps 3-5. Every acceptance ID has an owning task; no task lacks an ID.
242
- - **Placeholder scan:** none — every step carries the literal text to write or the exact command with its expected output.
243
- - **Type consistency:** `peekRender` is named identically in the probe (`out.copy.peekRender`), the test destructuring, and the assertions.
244
- - **Unresolved spec item:** §6 asked A vs B; the user chose **A**, recorded in `authorization.md` (`Scope: design-approved (D3 = option A…)`). The plan implements A only.
@@ -1,344 +0,0 @@
1
- # Fullscreen selection highlight clear — Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** In the fullscreen attach view, drop pi-tui's text selection as soon as copy-on-release has run, so the inverse-video highlight does not outlive the copy (issue #172).
6
-
7
- **Architecture:** Reuse the existing #174 deferred hook (`handleMouse` → `process.nextTick` after a left `release`/`click`) to read pi's final selection, mirror it into X11 PRIMARY as today, then clear pi's selection via a feature-detected private method and request one repaint. The predicate that schedules the hook becomes env-free; the kill switch stays where it already lives (`writePrimarySelection`). The clear is gated on `getCopyOnSelect() !== false` so users who disabled copy-on-select keep their selection for `Ctrl+X`.
8
-
9
- **Tech Stack:** TypeScript component (`src/ui/pty-attach.ts`), a pure decision predicate in `src/core/pty-scroll.mjs`, `node:test` unit tests, a component-level scenario harness with a fake fullscreen TUI and a PATH-stubbed `xclip`.
10
-
11
- ## Global Constraints
12
-
13
- - Spec: `docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md` (v3).
14
- - `clearTextSelection()` is private in pi-tui's typings → always feature-detect; degrade, never throw.
15
- - `resetTextSelection()` must not be used (it clears `lastClick` and would break double-click word / triple-click line selection).
16
- - Regular/legacy mode is out of scope (issue #250); do not touch `handleLocalMouseEvent`.
17
- - Tabs for indentation, English comments, conventional commit subjects, no `git add -A`.
18
-
19
- ---
20
-
21
- ### Task 1: Replace the mirror predicate with an env-free selection-end predicate
22
-
23
- **Files:**
24
- - Modify: `src/core/pty-scroll.mjs:271-282`
25
- - Test: `test/pty-scroll.test.mjs:20`, `test/pty-scroll.test.mjs:244-262`
26
-
27
- **Interfaces:**
28
- - Produces: `isFullscreenSelectionEnd(event) → boolean` (exported from `src/core/pty-scroll.mjs`).
29
- - Removes: `shouldMirrorSelectionToPrimary(event, { nativePasteEnabled })`.
30
-
31
- - [ ] **Step 1: Update the unit test first**
32
-
33
- In `test/pty-scroll.test.mjs` replace the import name and the test block:
34
-
35
- ```js
36
- test("isFullscreenSelectionEnd: only the left button's release/click ends a fullscreen selection", () => {
37
- assert.equal(isFullscreenSelectionEnd(ev("release", "left")), true);
38
- assert.equal(isFullscreenSelectionEnd(ev("click", "left")), true);
39
- const cases = [
40
- ["press", "left"],
41
- ["move", "left"],
42
- ["drag", "left"],
43
- ["wheel", "left"],
44
- ["release", "middle"],
45
- ["release", "right"],
46
- ["release", "none"],
47
- ["click", "middle"],
48
- ["click", "none"],
49
- ["press", "middle"],
50
- ];
51
- for (const [type, button] of cases) {
52
- assert.equal(isFullscreenSelectionEnd(ev(type, button)), false, `${type}/${button}`);
53
- }
54
- });
55
- ```
56
-
57
- - [ ] **Step 2: Run the test to verify it fails**
58
-
59
- Run: `node --test test/pty-scroll.test.mjs`
60
- Expected: FAIL — `isFullscreenSelectionEnd is not a function`.
61
-
62
- - [ ] **Step 3: Replace the helper**
63
-
64
- ```js
65
- /**
66
- * Issue #174 (B2) / #172: whether a normalized mouse event ends a fullscreen text
67
- * selection gesture the attach overlay must settle — mirror the selection into X11
68
- * PRIMARY (#174) and drop pi's highlight once copy-on-select has run (#172). Only
69
- * release/click of the left button ends a selection in TuiAltScreen; pi owns the
70
- * gesture, so the overlay observes without consuming. Pure: the native-paste kill
71
- * switch is applied by writePrimarySelection, not here.
72
- */
73
- export function isFullscreenSelectionEnd(event) {
74
- return (event.type === "release" || event.type === "click") && event.button === "left";
75
- }
76
- ```
77
-
78
- - [ ] **Step 4: Run the test to verify it passes**
79
-
80
- Run: `node --test test/pty-scroll.test.mjs`
81
- Expected: PASS (all cases, no import errors).
82
-
83
- - [ ] **Step 5: Commit**
84
-
85
- ```bash
86
- git add src/core/pty-scroll.mjs test/pty-scroll.test.mjs
87
- git commit -m "refactor(attach): selection-end predicate for fullscreen settle (issue #172)"
88
- ```
89
-
90
- ---
91
-
92
- ### Task 2: Clear pi's selection in the deferred settle hook
93
-
94
- **Files:**
95
- - Modify: `src/ui/pty-attach.ts:17` (import), `:521-530` (schedule branch), `:1376-1381` (settle method)
96
- - Test: `test-support/pty-attach-selection-mirror-scenario.ts`, `test/pty-attach-selection-mirror.test.mjs`
97
-
98
- **Interfaces:**
99
- - Consumes: `isFullscreenSelectionEnd(event)` from Task 1.
100
- - Produces: `private settleFullscreenSelection(nativePasteEnabled: boolean): void`; type `FullscreenSelectionHost`.
101
-
102
- - [ ] **Step 1: Extend the scenario with clear hooks and cases**
103
-
104
- In `test-support/pty-attach-selection-mirror-scenario.ts`:
105
-
106
- 1. Add module-level knobs next to `selectionText`:
107
-
108
- ```ts
109
- let copyOnSelect = true;
110
- let clearAvailable = true;
111
- let clearCalls = 0;
112
- ```
113
-
114
- 2. Give the fake TUI the two new members (the conditional spread must be inside the object literal):
115
-
116
- ```ts
117
- const tui = {
118
- mode: "fullscreen",
119
- getActiveSelectionText: () => selectionText,
120
- getCopyOnSelect: () => copyOnSelect,
121
- ...(clearAvailable
122
- ? {
123
- clearTextSelection: () => {
124
- clearCalls += 1;
125
- selectionText = undefined; // prove read-before-clear: a wrong order yields no PRIMARY write
126
- },
127
- }
128
- : {}),
129
- terminal: { rows: 24, cols: 80, columns: 80, write: () => {} },
130
- requestRender: () => {},
131
- };
132
- ```
133
-
134
- 3. Add result fields and one reset helper:
135
-
136
- ```ts
137
- releaseClears: -1,
138
- clickClears: -1,
139
- emptyClears: -1,
140
- killSwitchClears: -1,
141
- nonEndingClears: -1,
142
- copyOnSelectOffWrites: -1,
143
- copyOnSelectOffClears: -1,
144
- fallbackError: null as string | null,
145
- fallbackWrites: -1,
146
- ```
147
-
148
- ```ts
149
- const resetFixture = (text: string | undefined = "mirror-fixture-text") => {
150
- selectionText = text;
151
- copyOnSelect = true;
152
- clearAvailable = true;
153
- clearCalls = 0;
154
- };
155
- ```
156
-
157
- 4. Call `resetFixture()` at the start of every existing case, and record the clear deltas:
158
-
159
- - case 1 (release): `result.releaseClears = clearCalls;`
160
- - case 2 (click): `result.clickClears = clearCalls;`
161
- - case 3 (empty): set `resetFixture(undefined)`, then `result.emptyClears = clearCalls;`
162
- - case 4 (kill switch): `resetFixture()`, then `result.killSwitchClears = clearCalls;`
163
- - case 5 (press/move): `resetFixture()`, then `result.nonEndingClears = clearCalls;`
164
- - new case 6 (`copyOnSelect = false`): `resetFixture(); copyOnSelect = false;` handle release; assert writes 1 and clears 0.
165
- - new case 7 (no `clearTextSelection`): `resetFixture(); clearAvailable = false;` wrap the call in try/catch, capture `fallbackError`, and record `fallbackWrites`.
166
-
167
- 5. The middle-click paste case stays last and must also call `resetFixture()`.
168
-
169
- - [ ] **Step 2: Run the scenario test to verify it fails**
170
-
171
- Run: `node --test test/pty-attach-selection-mirror.test.mjs`
172
- Expected: FAIL on the new assertions (`releaseClears` is 0, because the component never clears yet).
173
-
174
- - [ ] **Step 3: Wire the component**
175
-
176
- Import (line 17): replace `shouldMirrorSelectionToPrimary` with `isFullscreenSelectionEnd`.
177
-
178
- Replace the schedule branch:
179
-
180
- ```ts
181
- const nativePasteEnabled = process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE !== "0";
182
- if (isFullscreenSelectionEnd(event)) {
183
- // Observe only — pi's fullscreen selection keeps the event. Overlays are
184
- // dispatched before TuiAltScreen finalizes (and copies) the selection for
185
- // this event, so settling must run deferred (issue #174).
186
- process.nextTick(() => this.settleFullscreenSelection(nativePasteEnabled));
187
- return undefined;
188
- }
189
- ```
190
-
191
- Add the structural type near the other top-level types in the file:
192
-
193
- ```ts
194
- /** The pi-tui members the attach view reads without a public typing contract:
195
- * getActiveSelectionText/clearTextSelection are private in TuiAltScreen's d.ts
196
- * (runtime-reachable), getCopyOnSelect is public. All are feature-detected. */
197
- interface FullscreenSelectionHost {
198
- getActiveSelectionText?: () => string | undefined;
199
- getCopyOnSelect?: () => boolean;
200
- clearTextSelection?: () => void;
201
- }
202
- ```
203
-
204
- Rename `mirrorFullscreenSelectionToPrimary` to `settleFullscreenSelection` and extend it:
205
-
206
- ```ts
207
- /** Issue #174 (B2) + #172: a selection gesture ended inside this overlay and pi has
208
- * finalized (and copied) its selection. Mirror the text into X11 PRIMARY so
209
- * middle-click paste closes the loop, then drop pi's selection so the inverse-video
210
- * highlight does not outlive the copy. Clearing is skipped when copy-on-select is
211
- * off (the user copies later with pi's Ctrl+X) and when the pi-tui method is absent
212
- * (older/newer runtime) — that degrades to the previous behaviour instead of
213
- * throwing. clearTextSelection preserves pi's multi-click history; resetTextSelection
214
- * would reset lastClick and break double-click word / triple-click line selection. */
215
- private settleFullscreenSelection(nativePasteEnabled: boolean): void {
216
- if (this.closed) return;
217
- const alt = this.tui as FullscreenSelectionHost;
218
- const text = alt.getActiveSelectionText?.();
219
- if (typeof text !== "string" || text.length === 0) return;
220
- if (nativePasteEnabled) this.writePrimarySelection(text);
221
- if (alt.getCopyOnSelect?.() === false) return;
222
- if (typeof alt.clearTextSelection !== "function") return;
223
- alt.clearTextSelection();
224
- this.scheduleRender();
225
- }
226
- ```
227
-
228
- - [ ] **Step 4: Update the test assertions**
229
-
230
- In `test/pty-attach-selection-mirror.test.mjs`, keep every existing assertion and add:
231
-
232
- ```js
233
- assert.equal(parsed.releaseClears, 1, "the highlight must be cleared once the copy has run");
234
- assert.equal(parsed.clickClears, 1, "a click ending with a live selection clears too");
235
- assert.equal(parsed.emptyClears, 0, "an empty selection must not clear");
236
- assert.equal(parsed.killSwitchClears, 1, "the highlight clear is not governed by NATIVE_PASTE");
237
- assert.equal(parsed.nonEndingClears, 0, "press/move must not clear");
238
- assert.equal(parsed.copyOnSelectOffWrites, 1, "the PRIMARY mirror still runs with copy-on-select off");
239
- assert.equal(parsed.copyOnSelectOffClears, 0, "copy-on-select off keeps the selection for Ctrl+X");
240
- assert.equal(parsed.fallbackError, null, "a missing clearTextSelection must not throw");
241
- assert.equal(parsed.fallbackWrites, 1, "the PRIMARY mirror still runs without the clear method");
242
- ```
243
-
244
- - [ ] **Step 5: Run the scenario test to verify it passes**
245
-
246
- Run: `node --test test/pty-attach-selection-mirror.test.mjs`
247
- Expected: PASS.
248
-
249
- - [ ] **Step 6: Commit**
250
-
251
- ```bash
252
- git add src/ui/pty-attach.ts test-support/pty-attach-selection-mirror-scenario.ts test/pty-attach-selection-mirror.test.mjs
253
- git commit -m "fix(attach): clear the fullscreen selection highlight after copy-on-release (issue #172)"
254
- ```
255
-
256
- ---
257
-
258
- ### Task 2b: Guard the settle against the next gesture (local CR F1/F3/F4)
259
-
260
- **Files:**
261
- - Modify: `src/ui/pty-attach.ts` (field + `handleMouse` bump + serial check)
262
- - Test: `test-support/pty-attach-selection-mirror-scenario.ts`, `test/pty-attach-selection-mirror.test.mjs`, `test/pty-scroll.test.mjs`
263
-
264
- **Interfaces:**
265
- - Produces: `private selectionGestureSerial = 0` (bumped on left press).
266
-
267
- - [ ] **Step 1: Add the failing scenario case**
268
-
269
- ```ts
270
- // 8. A release and the next left press coalesced into one tick (pi-tui emits batched
271
- // reports synchronously): the queued settle must not read/clear the new gesture.
272
- await withComponent(undefined, async (c) => {
273
- resetFixture();
274
- const before = writes().length;
275
- (c as any).handleMouse(leftEvent("release"));
276
- (c as any).handleMouse({ ...leftEvent("press"), x: 9, screenX: 9 });
277
- await sleep(80);
278
- result.coalescedWrites = writes().length - before;
279
- result.coalescedClears = clearCalls;
280
- });
281
- ```
282
-
283
- Also pin the deferral in case 1: `result.releaseSyncWrites = writes().length - before;` immediately after `handleMouse` returns.
284
-
285
- - [ ] **Step 2: Run the scenario test to verify it fails**
286
-
287
- Run: `node --test test/pty-attach-selection-mirror.test.mjs`
288
- Expected: FAIL — `coalescedWrites` is 1 (the queued settle mirrored the new gesture).
289
-
290
- - [ ] **Step 3: Implement the guard**
291
-
292
- Field next to `private selection`:
293
-
294
- ```ts
295
- private selectionGestureSerial = 0;
296
- ```
297
-
298
- In `handleMouse`, before the settle branch:
299
-
300
- ```ts
301
- if (event.type === "press" && event.button === "left") this.selectionGestureSerial += 1;
302
- ```
303
-
304
- In the settle branch, capture and compare:
305
-
306
- ```ts
307
- const serial = this.selectionGestureSerial;
308
- process.nextTick(() => {
309
- if (serial !== this.selectionGestureSerial) return;
310
- this.settleFullscreenSelection(nativePasteEnabled);
311
- });
312
- ```
313
-
314
- - [ ] **Step 4: Complete the predicate table (F4) and fix the import order (F5)**
315
-
316
- Add `["click", "right"]` to the false-cases list in `test/pty-scroll.test.mjs` and move `isFullscreenSelectionEnd` into alphabetical position.
317
-
318
- - [ ] **Step 5: Run the tests to verify they pass**
319
-
320
- Run: `node --test test/pty-scroll.test.mjs test/pty-attach-selection-mirror.test.mjs`
321
- Expected: PASS (35 tests).
322
-
323
- - [ ] **Step 6: Commit**
324
-
325
- ```bash
326
- git add src/ui/pty-attach.ts test-support/pty-attach-selection-mirror-scenario.ts test/pty-attach-selection-mirror.test.mjs test/pty-scroll.test.mjs
327
- git commit -m "fix(attach): keep a queued settle from consuming the next gesture (issue #172)"
328
- ```
329
-
330
- ---
331
-
332
- ### Task 3: Full verification
333
-
334
- **Files:** none.
335
-
336
- - [ ] **Step 1: Run the repository gate**
337
-
338
- Run: `npm run verify`
339
- Expected: typecheck, perf gate, tests, coverage thresholds, pack:dry all pass.
340
-
341
- - [ ] **Step 2: Confirm no scope drift**
342
-
343
- Run: `git diff --stat origin/main`
344
- Expected: only the spec, the plan, `src/ui/pty-attach.ts`, `src/core/pty-scroll.mjs`, `test/pty-scroll.test.mjs`, `test-support/pty-attach-selection-mirror-scenario.ts`, `test/pty-attach-selection-mirror.test.mjs`. `handleLocalMouseEvent` and `test-support/pty-attach-legacy-drag-scenario.ts` untouched.