@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,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.