@huseynsnmz/pi-board 0.15.3 → 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 (211) hide show
  1. package/CHANGELOG.md +13 -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 +5 -11
  8. package/src/index.ts +1 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/IMPLEMENTATION_PLAN.md +0 -920
  12. package/PRD.md +0 -484
  13. package/PROGRESS.md +0 -142
  14. package/VERIFY.md +0 -115
  15. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  16. package/docs/EXPLORATION.md +0 -187
  17. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  18. package/docs/RELEASE.md +0 -148
  19. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  20. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  21. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  22. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  23. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  24. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  25. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  26. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  27. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  28. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  29. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  30. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  31. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  32. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  33. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  34. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  35. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  36. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  37. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  38. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  39. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  40. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  41. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  42. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  43. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  44. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  45. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  46. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  47. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  48. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  49. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  50. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  51. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  52. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  53. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  54. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  55. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  56. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  57. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  58. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  59. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  60. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  61. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  62. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  63. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  64. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  65. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  66. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  67. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  68. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  69. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  70. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  71. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  72. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  73. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  74. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  75. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  76. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  77. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  78. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  79. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  80. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  81. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  82. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  83. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  84. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  85. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  86. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  87. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  88. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  89. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  90. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  91. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  92. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  93. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  94. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  95. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  96. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  97. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  98. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  99. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  100. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  101. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  102. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  103. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  104. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  105. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  106. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  107. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  108. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  109. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  110. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  111. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  112. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  113. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  114. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  115. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  116. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  117. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  118. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  119. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  120. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  121. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  122. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  123. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  124. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  125. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  126. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  127. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  128. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  129. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  130. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  131. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  132. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  133. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  134. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  135. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  136. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  137. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  138. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  139. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  140. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  141. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  142. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  143. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  144. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  145. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  146. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  147. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  148. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  149. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  150. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  151. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  152. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  153. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  154. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  155. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  156. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  157. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  158. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  159. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  160. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  161. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  162. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  163. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  164. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  165. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  166. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  167. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  168. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  169. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  170. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  171. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  172. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  173. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  174. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  175. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  176. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  177. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  178. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  179. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  180. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  181. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  182. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  183. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  184. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  185. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  186. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  187. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  188. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  189. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  190. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  191. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  192. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  193. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  194. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  195. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  196. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  197. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  198. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  199. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  200. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  201. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  202. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  203. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  204. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  205. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  206. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  207. package/scripts/budget-audit.mjs +0 -135
  208. package/scripts/release.mjs +0 -446
  209. package/scripts/release_helper.mjs +0 -309
  210. package/scripts/run-perf-gate.mjs +0 -40
  211. package/scripts/run-tests.mjs +0 -375
@@ -1,405 +0,0 @@
1
- # Selected Row Highlight 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:** Make the board's selected row identifiable at rest by painting its full line (indent through the trailing pad) with the theme's `selectedBg`.
6
-
7
- **Architecture:** One pure module-scope helper `applyRowHighlight(theme, selected, text)` decides whether to wrap a line; `renderRow` calls it on `indent + clipped body` so the highlight starts at column 0 and covers the right edge. The theme token (`selectedBg`) is pi's own selection background, so dark/light themes and 256/16-colour terminals are handled by pi. A new TS harness (`dashboard-selection-render.ts`) drives the real `DashboardComponent` and asserts the ANSI structure; the eight pre-existing `DashboardComponent` harnesses get an identity `bg` stub.
8
-
9
- **Tech Stack:** TypeScript (src/ui), Node 20+ `node:test` + `node --experimental-transform-types` harnesses, `@earendil-works/pi-tui` (`visibleWidth`, `truncateToWidth`), pi theme tokens (`ThemeBg = "selectedBg"`).
10
-
11
- **Spec:** `docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md`
12
-
13
- **Worktree:** `C:/Users/27499/proj/opensource/pi-agent-board/.pi/worktrees/issue-231-selected-row-highlight` — every path below is relative to it; never edit the main checkout.
14
-
15
- ## Global Constraints
16
-
17
- - Tabs for indentation; English comments; conventional commit subjects with the issue number (`(#231)` lands on the PR, not on local commits).
18
- - Do not change `select` mode's `◉/○`, the peek header, the flash banner, the folder-header `▌`, or `windowBody()` (open PR #226 owns the viewport).
19
- - Highlight colour comes **only** from `theme.bg("selectedBg", …)` — no hardcoded RGB, no per-stage background, no `muted → dim` demotion.
20
- - `\x1b[49m` (emitted by `theme.bg`) must stay inside the clipped string; the bg span must include the folder indent, the trailing pad, and the age column.
21
- - `npm run verify` (typecheck + perf + test + coverage + pack:dry) is the gate before claiming done.
22
- - Do not touch `research/`, `~/.github-issue-driven/`, or the main checkout; `git add <explicit files>` only.
23
-
24
- ## Review Focus
25
-
26
- 1. **Ultra-narrow terminals (`width < 24`)** — `contentWidth` has a floor of 20, so `indent + contentWidth > width` and `clip` truncates with `\x1b[0m`. The highlight may lose its closer there, but it must never leak into following rows. → A8 (Task 2).
27
- 2. **`selectedId` matching no visible row** (e.g. after a filter) — no row may be painted. → A5 zero-branch (Task 2).
28
- 3. **Folder-group indent (`indent = 4`)** — the bg opener must sit at column 0, before the indent, not after it. → A3 (Task 2).
29
- 4. **`select` mode** — the batch marker `◉/○` must remain visible and the highlight must still apply. → A6 (Task 2).
30
- 5. **Coexisting strong signals** — the flash banner keeps its own bg bar, the folder header keeps its `▌`, and the peek header stays unhighlighted. → A7 (Task 2) and U5 (manual).
31
-
32
- ---
33
-
34
- ### Task 1: Pure highlight helper
35
-
36
- **Files:**
37
- - Modify: `src/ui/dashboard.ts` (insert the exported helper next to `stageFg` / `ansiBg`, around the `STAGE_RGB` block at :1968-1996)
38
- - Create: `test-support/dashboard-selection-render.ts`
39
- - Create: `test/dashboard-selection.test.mjs`
40
-
41
- **Interfaces:**
42
- - Consumes: `ThemeLike` (`src/ui/dashboard.ts:50-56`, already declares `bg(color: string, text: string): string`).
43
- - Produces:
44
- - `export function applyRowHighlight(theme: Pick<ThemeLike, "bg">, selected: boolean, text: string): string`
45
- - Harness JSON contract: `{ ok: boolean, unit: { unselectedIdentical: boolean, selectedWrapped: boolean, themeCallCount: number } }` (integration fields are added in Task 2).
46
-
47
- - [ ] **Step 1: Write the failing test**
48
-
49
- Create `test/dashboard-selection.test.mjs`:
50
-
51
- ```js
52
- import assert from "node:assert/strict";
53
- import { execFileSync } from "node:child_process";
54
- import { fileURLToPath } from "node:url";
55
- import { join } from "node:path";
56
- import test from "node:test";
57
-
58
- const ROOT_DIR = fileURLToPath(new URL("../", import.meta.url));
59
- const SCRIPT = join(ROOT_DIR, "test-support", "dashboard-selection-render.ts");
60
-
61
- function runHarness() {
62
- const out = execFileSync(process.execPath, ["--experimental-transform-types", SCRIPT], {
63
- encoding: "utf8",
64
- timeout: 30_000,
65
- });
66
- return JSON.parse(out);
67
- }
68
-
69
- test("applyRowHighlight is a pure pass-through when unselected and wraps once when selected (issue #231)", () => {
70
- const parsed = runHarness();
71
- assert.equal(parsed.ok, true);
72
- assert.equal(parsed.unit.unselectedIdentical, true, "unselected input must come back byte-identical");
73
- assert.equal(parsed.unit.selectedWrapped, true, "selected input must be wrapped exactly once");
74
- assert.equal(parsed.unit.themeCallCount, 1, "the helper must call theme.bg once per selected row, never for unselected rows");
75
- });
76
- ```
77
-
78
- Create `test-support/dashboard-selection-render.ts` (unit part only for now):
79
-
80
- ```ts
81
- // Dashboard selected-row harness: renders real DashboardComponent frames with a
82
- // recording/ANSI-emitting theme stub and prints JSON flags the .mjs test asserts
83
- // on. Unit checks for the pure helper live here too so one process proves both.
84
- import { mkdtempSync, rmSync } from "node:fs";
85
- import { tmpdir } from "node:os";
86
- import { join } from "node:path";
87
- import { visibleWidth } from "@earendil-works/pi-tui";
88
- import { createView } from "../src/core/store.mjs";
89
- import { createService } from "../src/runtime/service.mjs";
90
- import { applyRowHighlight, DashboardComponent } from "../src/ui/dashboard.ts";
91
-
92
- const BG_OPEN = "\x1b[48;2;1;2;3m";
93
- const BG_CLOSE = "\x1b[49m";
94
-
95
- const root = mkdtempSync(join(tmpdir(), "agentview-selection-render-"));
96
- try {
97
- let bgCalls = 0;
98
- const theme = {
99
- fg: (token: string, text: string) => {
100
- fgCalls.push([token, text]);
101
- return text;
102
- },
103
- bold: (text: string) => text,
104
- italic: (text: string) => text,
105
- strikethrough: (text: string) => text,
106
- bg: (_color: string, text: string) => {
107
- bgCalls += 1;
108
- return `${BG_OPEN}${text}${BG_CLOSE}`;
109
- },
110
- };
111
- const fgCalls: Array<[string, string]> = [];
112
-
113
- // --- unit: the pure helper -------------------------------------------
114
- const identity = "plain line";
115
- const untouched = applyRowHighlight(theme, false, identity);
116
- const callsAfterUnselected = bgCalls;
117
- const wrapped = applyRowHighlight(theme, true, identity);
118
-
119
- console.log(
120
- JSON.stringify({
121
- ok: true,
122
- unit: {
123
- unselectedIdentical: untouched === identity && callsAfterUnselected === 0,
124
- selectedWrapped: wrapped === `${BG_OPEN}${identity}${BG_CLOSE}`,
125
- themeCallCount: bgCalls,
126
- },
127
- }),
128
- );
129
- } finally {
130
- rmSync(root, { recursive: true, force: true });
131
- }
132
- ```
133
-
134
- (Keep `createView`, `createService`, `DashboardComponent` and `visibleWidth` imported even though the unit part does not use them yet — Task 2 fills in the integration section, and the typecheck gate excludes `test-support/` anyway.)
135
-
136
- - [ ] **Step 2: Run the test to verify it fails**
137
-
138
- Run: `node --test test/dashboard-selection.test.mjs`
139
- Expected: FAIL — the harness throws because `applyRowHighlight` is not exported from `../src/ui/dashboard.ts` (or `parsed.ok` is missing).
140
-
141
- - [ ] **Step 3: Implement `applyRowHighlight` in `src/ui/dashboard.ts`**
142
-
143
- Place it next to `stageFg`:
144
-
145
- ```ts
146
- /**
147
- * Issue #231: the selected row's full-line background. Pure (the theme is
148
- * injected) so the wrap/close contract is unit-testable, and an unselected row
149
- * is returned byte-identical.
150
- */
151
- export function applyRowHighlight(
152
- theme: Pick<ThemeLike, "bg">,
153
- selected: boolean,
154
- text: string,
155
- ): string {
156
- return selected ? theme.bg("selectedBg", text) : text;
157
- }
158
- ```
159
-
160
- - [ ] **Step 4: Run the test to verify it passes**
161
-
162
- Run: `node --test test/dashboard-selection.test.mjs`
163
- Expected: PASS (1 test).
164
-
165
- - [ ] **Step 5: Commit**
166
-
167
- ```bash
168
- git add src/ui/dashboard.ts test/dashboard-selection.test.mjs test-support/dashboard-selection-render.ts
169
- git commit -m "feat(ui): add pure selected-row highlight helper (issue #231)"
170
- ```
171
-
172
- ---
173
-
174
- ### Task 2: Paint the row, cover the indent, and keep every existing harness green
175
-
176
- **Files:**
177
- - Modify: `src/ui/dashboard.ts:1459-1483` (`renderRow` return path)
178
- - Modify: `test-support/dashboard-selection-render.ts` (add the integration section and its JSON fields)
179
- - Modify: `test/dashboard-selection.test.mjs` (assert the integration fields)
180
- - Modify (stub only): `test-support/dashboard-delete-key-probe.ts`, `test-support/cwd-browse-tab-probe.ts`, `test-support/dashboard-shrink-render.ts`, `test-support/dashboard-refs-render.ts`, `test-support/dashboard-prewarm-smoke.ts`, `test-support/navigation-wrap.ts`, `test-support/hold-key-wrap.ts`, `test-support/ui-smoke.ts`, `test-support/attach-delete-flow-probe.ts`
181
-
182
- **Interfaces:**
183
- - Consumes: `applyRowHighlight` from Task 1.
184
- - Produces: `renderRow` output whose selected-row string begins with the theme's bg opener and ends with `\x1b[49m`; harness JSON gains `integration: { selectedStartsAtCol0, selectedWidth, unselectedHasNoOpener, selectedHasArrow, unselectedHasNoArrow, selectedCloserLast, selectedCloserCount, firstFrameBgCalls, zeroSelectionBgCalls, selectMarkerPresent, selectUnselectedCircle, selectHasBg, peekHasNoOpener, peekHeaderUsesAccent, narrowMaxWidth, narrowLeaked, unselectedUsesMuted, unselectedNameIsDim }` (`firstFrameBgCalls` is the per-frame `bgCalls` counter of the selected-row render; `zeroSelectionBgCalls` is the same counter with a `selectedId` that matches no visible row).
185
-
186
- - [ ] **Step 1: Write the failing integration assertions**
187
-
188
- Append to `test-support/dashboard-selection-render.ts`, inside the `try` block, after the unit section, and change the single `console.log` to emit both `unit` and `integration`:
189
-
190
- ```ts
191
- // --- integration: real render frames ---------------------------------
192
- createView(root, { id: "v1", name: "alpha session", cwd: join(root, "proj-a") });
193
- createView(root, { id: "v2", name: "beta session", cwd: join(root, "proj-b") });
194
- const service = createService({
195
- root,
196
- runnerScript: "/no/runner.mjs",
197
- piCommand: "pi",
198
- piArgsPrefix: [],
199
- defaultCwd: root,
200
- launch: () => ({ pid: null, configPath: "/no/config.json" }),
201
- launchHost: () => ({ pid: null, configPath: "/no/host-config.json" }),
202
- launchTitle: () => ({ pid: null, configPath: "/no/title-config.json" }),
203
- });
204
- const tui = {
205
- terminal: { rows: 40, cols: 160, columns: 160, write: () => {} },
206
- requestRender: () => {},
207
- };
208
- const makeDash = () =>
209
- new DashboardComponent(tui as never, theme as never, {} as never, () => {}, {
210
- service,
211
- root,
212
- defaultCwd: root,
213
- availableModels: [],
214
- currentModel: null,
215
- currentThinkingLevel: "off",
216
- });
217
-
218
- // Two cwds ⇒ showFolders=true ⇒ rows carry indent 4.
219
- bgCalls = 0;
220
- const dash = makeDash();
221
- const lines = dash.render(160);
222
- const selectedLine = lines.find((line) => line.includes("alpha session")) ?? "";
223
- const otherLine = lines.find((line) => line.includes("beta session")) ?? "";
224
-
225
- const selectedStartsAtCol0 = selectedLine.startsWith(BG_OPEN);
226
- const selectedWidth = visibleWidth(selectedLine);
227
- const unselectedHasNoOpener = !otherLine.includes(BG_OPEN);
228
- const selectedHasArrow = selectedLine.includes("›");
229
- const unselectedHasNoArrow = !otherLine.includes("›");
230
- const firstFrameBgCalls = bgCalls;
231
-
232
- // select mode: press `m`, then space to batch-mark the current row.
233
- dash.handleInput("m");
234
- dash.handleInput(" ");
235
- const selectLines = dash.render(160);
236
- const selectMarkerPresent = selectLines.some((line) => line.includes("◉"));
237
- const selectHasBg = selectLines.find((line) => line.includes("alpha session"))?.includes(BG_OPEN) ?? false;
238
- dash.handleInput("\x1b");
239
-
240
- // peek: space in list mode opens the peek header (must stay unhighlighted).
241
- const peekDash = makeDash();
242
- peekDash.handleInput(" ");
243
- const peekHasNoOpener = !peekDash.render(160).some((line) => line.includes(BG_OPEN));
244
-
245
- // ultra-narrow: width 23 with indent 4 overflows (4 + max(20, 19) = 24 > 23).
246
- const narrowLines = dash.render(23);
247
- const narrowMaxWidth = Math.max(...narrowLines.map((line) => visibleWidth(line)));
248
- const narrowLeaked = narrowLines.some((line) => !line.includes("alpha session") && line.includes(BG_OPEN));
249
-
250
- // D4: unselected rows keep `muted`, never `dim`, for name/summary.
251
- const unselectedUsesMuted = fgCalls.some(([token, text]) => token === "muted" && text.includes("beta session"));
252
- const unselectedNameIsDim = fgCalls.some(([token, text]) => token === "dim" && text.includes("beta session"));
253
- ```
254
-
255
- Then replace the `console.log(JSON.stringify({ ok: true, unit: {...} }))` with:
256
-
257
- ```ts
258
- console.log(
259
- JSON.stringify({
260
- ok: true,
261
- unit: {
262
- unselectedIdentical: untouched === identity && callsAfterUnselected === 0,
263
- selectedWrapped: wrapped === `${BG_OPEN}${identity}${BG_CLOSE}`,
264
- themeCallCount: bgCallsBeforeIntegration,
265
- },
266
- integration: {
267
- selectedStartsAtCol0,
268
- selectedWidth,
269
- unselectedHasNoOpener,
270
- selectedHasArrow,
271
- unselectedHasNoArrow,
272
- firstFrameBgCalls,
273
- selectMarkerPresent,
274
- selectHasBg,
275
- peekHasNoOpener,
276
- narrowMaxWidth,
277
- narrowLeaked,
278
- unselectedUsesMuted,
279
- unselectedNameIsDim,
280
- },
281
- }),
282
- );
283
- ```
284
-
285
- where `bgCallsBeforeIntegration` is captured right after the unit section (`const bgCallsBeforeIntegration = bgCalls;`).
286
-
287
- Add to `test/dashboard-selection.test.mjs`:
288
-
289
- ```js
290
- test("renderRow paints the selected row from column 0 through the padded right edge (issue #231)", () => {
291
- const parsed = runHarness();
292
- const it = parsed.integration;
293
- assert.equal(it.selectedStartsAtCol0, true, "bg must open before the folder indent");
294
- assert.equal(it.selectedWidth, 160, "bg span must reach the padded right edge");
295
- assert.equal(it.unselectedHasNoOpener, true, "unselected rows must have no bg");
296
- assert.equal(it.selectedHasArrow, true, "the › cursor marker stays on the selected row");
297
- assert.equal(it.unselectedHasNoArrow, true, "no › on unselected rows");
298
- assert.equal(it.firstFrameBgCalls, 1, "exactly one row is painted per frame");
299
- });
300
-
301
- test("selected-row highlight coexists with select mode, peek, and narrow terminals (issue #231)", () => {
302
- const parsed = runHarness();
303
- const it = parsed.integration;
304
- assert.equal(it.selectMarkerPresent, true, "select mode's ◉ batch marker survives");
305
- assert.equal(it.selectHasBg, true, "select mode keeps the row highlight");
306
- assert.equal(it.peekHasNoOpener, true, "peek header is not highlighted");
307
- assert.ok(it.narrowMaxWidth <= 23, "no line may exceed the requested width");
308
- assert.equal(it.narrowLeaked, false, "narrow clipping must not leak bg into other rows");
309
- });
310
-
311
- test("unselected rows keep muted name/summary, not dim (issue #231 decision D4)", () => {
312
- const parsed = runHarness();
313
- assert.equal(parsed.integration.unselectedUsesMuted, true);
314
- assert.equal(parsed.integration.unselectedNameIsDim, false);
315
- });
316
- ```
317
-
318
- - [ ] **Step 2: Run the tests to verify they fail**
319
-
320
- Run: `node --test test/dashboard-selection.test.mjs`
321
- Expected: FAIL — `selectedStartsAtCol0` is false (no bg is emitted yet) and `firstFrameBgCalls` is 0.
322
-
323
- - [ ] **Step 3: Wire `applyRowHighlight` into `renderRow`**
324
-
325
- Replace the last return of `renderRow` (`src/ui/dashboard.ts:1481-1483`):
326
-
327
- ```ts
328
- line = padTo(line, contentWidth - visibleWidth(ageRaw));
329
- line += t.fg("dim", ageRaw);
330
- const body = clip(line, contentWidth);
331
- const painted = applyRowHighlight(t, selected, `${" ".repeat(indent)}${body}`);
332
- return clip(painted, width);
333
- ```
334
-
335
- Add the identity `bg` stub to each pre-existing harness theme literal, e.g.:
336
-
337
- ```ts
338
- const theme = { fg: (_c: string, t: string) => t, bold: (t: string) => t, italic: (t: string) => t, strikethrough: (t: string) => t, bg: (_c: string, t: string) => t };
339
- ```
340
-
341
- Apply the same one-line addition to the nine files listed in **Files** above (grep `fg: (` in `test-support/*.ts` to find each literal).
342
-
343
- - [ ] **Step 4: Run the focused tests, then the full suite**
344
-
345
- Run: `node --test test/dashboard-selection.test.mjs`
346
- Expected: PASS (4 tests).
347
-
348
- Run: `node --test test/dashboard-*.test.mjs test/ui-smoke.test.mjs`
349
- Expected: PASS — this is where a missed `bg` stub would surface as `theme.bg is not a function`.
350
-
351
- Run: `npm test`
352
- Expected: PASS (no new failures; pre-existing environment failures, if any, must be identical to a clean `git stash` baseline).
353
-
354
- - [ ] **Step 5: Commit**
355
-
356
- ```bash
357
- git add src/ui/dashboard.ts test/dashboard-selection.test.mjs test-support/dashboard-selection-render.ts \
358
- test-support/dashboard-delete-key-probe.ts test-support/cwd-browse-tab-probe.ts \
359
- test-support/dashboard-shrink-render.ts test-support/dashboard-refs-render.ts \
360
- test-support/dashboard-prewarm-smoke.ts test-support/navigation-wrap.ts \
361
- test-support/hold-key-wrap.ts test-support/ui-smoke.ts test-support/attach-delete-flow-probe.ts
362
- git commit -m "feat(ui): paint the selected board row with selectedBg (issue #231)"
363
- ```
364
-
365
- ---
366
-
367
- ### Task 3: Full verification gate
368
-
369
- **Files:**
370
- - No source changes expected. If `npm run verify` fails, fix in the owning file from Task 1/2 and amend the Task 2 commit rather than adding a new fixup commit.
371
-
372
- **Interfaces:**
373
- - Consumes: both earlier tasks.
374
- - Produces: a branch whose `npm run verify` is green.
375
-
376
- - [ ] **Step 1: Run the repo gate**
377
-
378
- Run: `npm run verify`
379
- Expected: `typecheck` clean, `test:perf` 3 passed, `npm test` green, `test:coverage` above lines 85 / funcs 80 / branches 70, `pack:dry` lists `src/ui/dashboard.ts`.
380
-
381
- - [ ] **Step 2: Confirm the packed tarball still ships the touched file**
382
-
383
- Run: `npm pack --dry-run 2>&1 | grep -E "src/ui/dashboard.ts|test-support"`
384
- Expected: `src/ui/dashboard.ts` present; `test-support/` absent (it is not in `files`).
385
-
386
- - [ ] **Step 3: Record the manual-check handoff**
387
-
388
- Write the U1–U5 steps from the spec into the PR description (they need a live pi session and cannot run in CI). Do not claim them as passed.
389
-
390
- - [ ] **Step 4: Commit any gate fixes (only if needed)**
391
-
392
- ```bash
393
- git add -A -- src test test-support
394
- git commit -m "test(ui): keep the selection harness green under the full gate (issue #231)"
395
- ```
396
-
397
- ---
398
-
399
- ## Self-Review
400
-
401
- - **Spec coverage:** D1 → Task 1 + Task 2 A1/A2; D2 → A10 (Task 2); D3 → A3 (Task 2); D4 → A11 (Task 2). A1–A4, A6–A8, A10, A11 → Task 2; A5 → Task 1 (unit call count) + Task 2 (per-frame count); A9 → Task 3; U1–U5 → PR handoff (Task 3 Step 3). No spec requirement is unassigned.
402
- - **Step scan:** each step names one file and one checkable result; no "handle edge cases" placeholders.
403
- - **Type consistency:** `applyRowHighlight(theme, selected, text)` is defined and consumed with the same three parameters in both tasks; `BG_OPEN`/`BG_CLOSE` and the JSON field names used by the `.mjs` assertions match the harness emission.
404
- - **Review Focus:** each of the five lines has an owning assertion (A8, A5-zero, A3, A6, A7/A5) in Task 2, plus U5 in the PR handoff.
405
- - **Proportion:** the plan is shorter than the spec and contains only signatures, assertions and the one-line stub shape.