@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,366 +0,0 @@
1
- # Dashboard Keypress Lag Fix 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:** Eliminate perceptible ↑/↓ selection lag on the dashboard by moving prewarm off the keypress path, skipping artifact loads for archived views, and stopping forced PTY probes (issue #9).
6
-
7
- **Architecture:** Three independent fixes: (A) a debounced prewarm scheduler so arrow keys only mutate selection and trigger a render; (B) an archived short-circuit in `listRows` so the 700ms poll (`reconcile()` + `refresh()` + `render()` each call it) stops reading ~10 artifact files per archived view; (C) `ensureHost` no longer forces `ptySupport({refresh:true})`, which spawned a probe process on every keypress when PTY support is broken.
8
-
9
- **Tech Stack:** Node 20+, TypeScript (`.ts` UI) + ESM JavaScript (`.mjs` core), `node --test` test runner, no new dependencies.
10
-
11
- ## Global Constraints
12
-
13
- - No new npm dependencies.
14
- - All commits in this worktree branch `issue-9-dashboard-keypress-lag`; never touch `main` checkout.
15
- - `git add` per-file; never `git add -A`.
16
- - Commit messages in English, conventional-commits format, reference `issue #9`.
17
- - `npm run verify` (typecheck + tests + pack dry-run) must pass before the PR.
18
- - Core modules are `.mjs` with JSDoc types; UI is `.ts`. Follow existing file conventions.
19
-
20
- ---
21
-
22
- ### Task 1: `listRows` archived short-circuit (Fix B)
23
-
24
- **Files:**
25
- - Modify: `src/core/store.mjs` (function `listRows`, around line 220)
26
- - Test: `test/store.test.mjs` (append new test)
27
-
28
- **Interfaces:**
29
- - Consumes: existing `readMeta(root, viewId)`, `loadRow(root, viewId)` from `src/core/store.mjs`.
30
- - Produces: unchanged `listRows(root, opts)` signature and return type. Callers (`service.mjs` `reconcile()`/`rows()`/`pruneWarmHosts()`, `dashboard.ts` render) need no changes.
31
-
32
- - [ ] **Step 1: Write the safety-net test**
33
-
34
- Note: `readJson` swallows missing/corrupt files with a null fallback (`src/core/atomic.mjs`), so the archived short-circuit has no black-box failure mode — pre-fix and post-fix output is identical; the difference is pure IO volume, proven by the Task 4 real-store measurement. This test instead pins the behavioral contract that makes the short-circuit safe: archived views whose artifact files are absent/stale must not break `listRows`, and `includeArchived` semantics stay unchanged.
35
-
36
- Append to `test/store.test.mjs` (imports `createView`, `listRows`, `writeMeta`, `P` are already at top; add `rmSync`/`writeFileSync`/`mkdirSync` to the existing `node:fs` import if missing):
37
-
38
- ```js
39
- test("listRows tolerates archived views with only meta.json", () => {
40
- const root = freshRoot();
41
- try {
42
- const live = createView(root, { id: "live1", name: "live", cwd: "/r" });
43
- for (const id of ["arch1", "arch2"]) {
44
- const meta = createView(root, { id, name: id, cwd: "/r" });
45
- meta.archived = true;
46
- writeMeta(root, meta);
47
- // Simulate the worst case the short-circuit must handle: archived view
48
- // dirs containing nothing but meta.json (no state/evidence/host files).
49
- rmSync(P.viewDir(root, id), { recursive: true, force: true });
50
- mkdirSync(P.viewDir(root, id), { recursive: true });
51
- writeFileSync(P.metaPath(root, id), JSON.stringify(meta));
52
- }
53
- const rows = listRows(root);
54
- assert.equal(rows.length, 1);
55
- assert.equal(rows[0].meta.id, live.id);
56
- const all = listRows(root, { includeArchived: true });
57
- assert.equal(all.length, 3);
58
- assert.equal(all.filter((r) => r.meta.archived).length, 2);
59
- } finally {
60
- rmSync(root, { recursive: true, force: true });
61
- }
62
- });
63
- ```
64
-
65
- - [ ] **Step 2: Run test to confirm green baseline**
66
-
67
- Run: `cd <worktree> && npm install --no-audit --no-fund 2>/dev/null; node --test test/store.test.mjs`
68
- Expected: PASS pre-fix (safety net — see Step 1 note). This test must stay green after the implementation change.
69
-
70
- - [ ] **Step 3: Write minimal implementation**
71
-
72
- In `src/core/store.mjs`, change `listRows`:
73
-
74
- ```js
75
- export function listRows(root, opts = {}) {
76
- const roster = readRoster(root);
77
- /** @type {Row[]} */
78
- const rows = [];
79
- for (const viewId of roster.views) {
80
- // Archived short-circuit: archived rows are invisible on the dashboard, so
81
- // never pay for their artifact files (state/evidence/host/diagnostics...).
82
- // meta.json is the single authoritative source of the archived flag.
83
- const meta = readMeta(root, viewId);
84
- if (!meta) continue;
85
- if (meta.archived && !opts.includeArchived) continue;
86
- const row = loadRow(root, viewId);
87
- if (!row) continue;
88
- if (row.meta.archived && !opts.includeArchived) continue;
89
- rows.push(row);
90
- }
91
- return rows;
92
- }
93
- ```
94
-
95
- (The second archived check is belt-and-suspenders in case `loadRow` re-reads a meta that changed between reads.)
96
-
97
- - [ ] **Step 4: Run test to verify it passes**
98
-
99
- Run: `node --test test/store.test.mjs`
100
- Expected: PASS, all tests in file green.
101
-
102
- - [ ] **Step 5: Commit**
103
-
104
- ```bash
105
- git add src/core/store.mjs test/store.test.mjs
106
- git commit -m "perf: skip artifact loading for archived views in listRows (issue #9)"
107
- ```
108
-
109
- ---
110
-
111
- ### Task 2: Debounced prewarm off the keypress path (Fix A)
112
-
113
- **Files:**
114
- - Create: `src/core/prewarm-schedule.mjs`
115
- - Create: `test/prewarm-schedule.test.mjs`
116
- - Modify: `src/ui/dashboard.ts` (fields near line 127, `moveSelection` ~L237, `refresh` ~L176, `dispose` ~L1086)
117
-
118
- **Interfaces:**
119
- - Produces: `createPrewarmScheduler(prewarm: () => void, delayMs?: number): { schedule(): void; cancel(): void }` — single-flight debounce; repeated `schedule()` within `delayMs` fire `prewarm()` exactly once after the last call; `cancel()` clears the pending timer.
120
- - Consumes (dashboard.ts): existing private `prewarmSelected()`.
121
-
122
- - [ ] **Step 1: Write the failing test**
123
-
124
- `test/prewarm-schedule.test.mjs`:
125
-
126
- ```js
127
- import assert from "node:assert/strict";
128
- import { test } from "node:test";
129
- import { createPrewarmScheduler } from "../src/core/prewarm-schedule.mjs";
130
-
131
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
132
-
133
- test("rapid schedules fire prewarm once after quiet period", async () => {
134
- let fired = 0;
135
- const s = createPrewarmScheduler(() => { fired += 1; }, 15);
136
- s.schedule();
137
- s.schedule();
138
- s.schedule();
139
- await sleep(5);
140
- assert.equal(fired, 0); // still debouncing
141
- await sleep(25);
142
- assert.equal(fired, 1); // exactly once
143
- });
144
-
145
- test("cancel prevents prewarm entirely", async () => {
146
- let fired = 0;
147
- const s = createPrewarmScheduler(() => { fired += 1; }, 10);
148
- s.schedule();
149
- s.cancel();
150
- await sleep(30);
151
- assert.equal(fired, 0);
152
- });
153
-
154
- test("schedule after cancel works again", async () => {
155
- let fired = 0;
156
- const s = createPrewarmScheduler(() => { fired += 1; }, 10);
157
- s.schedule();
158
- s.cancel();
159
- s.schedule();
160
- await sleep(30);
161
- assert.equal(fired, 1);
162
- });
163
-
164
- test("prewarm errors do not crash the scheduler", async () => {
165
- let fired = 0;
166
- const s = createPrewarmScheduler(() => { fired += 1; throw new Error("boom"); }, 5);
167
- s.schedule();
168
- await sleep(20);
169
- assert.equal(fired, 1);
170
- s.schedule();
171
- await sleep(20);
172
- assert.equal(fired, 2);
173
- });
174
- ```
175
-
176
- - [ ] **Step 2: Run test to verify it fails**
177
-
178
- Run: `node --test test/prewarm-schedule.test.mjs`
179
- Expected: FAIL — cannot find module `../src/core/prewarm-schedule.mjs`.
180
-
181
- - [ ] **Step 3: Write minimal implementation**
182
-
183
- `src/core/prewarm-schedule.mjs`:
184
-
185
- ```js
186
- /**
187
- * Single-flight debounce for dashboard prewarm.
188
- *
189
- * Arrow-key navigation must move the selection and repaint immediately; host
190
- * prewarm (which may spawn a PTY host and re-scan rows) is deferred so bursts
191
- * of keypresses trigger exactly one prewarm for the final resting selection.
192
- */
193
-
194
- /**
195
- * @param {() => void} prewarm Invoked (with errors swallowed) once scheduling
196
- * goes quiet for `delayMs`. Re-reads current state at fire time.
197
- * @param {number} [delayMs=200]
198
- * @returns {{ schedule: () => void, cancel: () => void }}
199
- */
200
- export function createPrewarmScheduler(prewarm, delayMs = 200) {
201
- /** @type {ReturnType<typeof setTimeout> | null} */
202
- let timer = null;
203
- const fire = () => {
204
- timer = null;
205
- try {
206
- prewarm();
207
- } catch {
208
- /* prewarm is best-effort; never break navigation */
209
- }
210
- };
211
- return {
212
- schedule() {
213
- if (timer !== null) clearTimeout(timer);
214
- timer = setTimeout(fire, delayMs);
215
- },
216
- cancel() {
217
- if (timer !== null) {
218
- clearTimeout(timer);
219
- timer = null;
220
- }
221
- },
222
- };
223
- }
224
- ```
225
-
226
- - [ ] **Step 4: Run test to verify it passes**
227
-
228
- Run: `node --test test/prewarm-schedule.test.mjs`
229
- Expected: PASS (4 tests).
230
-
231
- - [ ] **Step 5: Wire into DashboardComponent**
232
-
233
- In `src/ui/dashboard.ts`:
234
-
235
- 1. Import: add near the other `../core/` imports:
236
- `import { createPrewarmScheduler } from "../core/prewarm-schedule.mjs";`
237
- 2. Add field (near `private prewarmedId: string | null = null;`):
238
- `private readonly prewarmScheduler = createPrewarmScheduler(() => this.prewarmSelected(), 200);`
239
- 3. `moveSelection()`: replace `this.prewarmSelected();` with `this.prewarmScheduler.schedule();`
240
- 4. `refresh()` (line ~176): replace `if (this.selectedId && this.selectedId !== previousSelected) this.prewarmSelected();` with `if (this.selectedId && this.selectedId !== previousSelected) this.prewarmScheduler.schedule();`
241
- 5. `prewarmSelected()`: add a mode guard as the first line so a timer firing after the user entered peek/session/launch modes does not prewarm the wrong target:
242
- `if (this.mode !== "list" && this.mode !== "select") return;`
243
- 6. `dispose()`: replace the comment-only body with `this.prewarmScheduler.cancel();` (keep the existing comment about poll interval ownership).
244
-
245
- - [ ] **Step 6: Typecheck + full tests**
246
-
247
- Run: `npx tsc --noEmit && node --test test/*.test.mjs`
248
- Expected: no type errors; all tests green.
249
-
250
- - [ ] **Step 7: Commit**
251
-
252
- ```bash
253
- git add src/core/prewarm-schedule.mjs test/prewarm-schedule.test.mjs src/ui/dashboard.ts
254
- git commit -m "perf: debounce dashboard prewarm off the keypress path (issue #9)"
255
- ```
256
-
257
- ---
258
-
259
- ### Task 3: `ensureHost` PTY probe TTL instead of forced refresh (Fix C)
260
-
261
- **Files:**
262
- - Modify: `src/runtime/service.mjs` (`ensureHost`, ~L674: `const pty = ptySupport({ refresh: true });`)
263
- - Test: `test/service.test.mjs` (append new test)
264
-
265
- **Interfaces:**
266
- - Consumes: existing injected `ptySupport(opts)` option of `createService`; existing test helper `service(root, overrides)` in `test/service.test.mjs`.
267
- - Produces: unchanged `ensureHost` return contract; only probe option semantics change (default = cached/TTL instead of forced refresh).
268
-
269
- - [ ] **Step 1: Write the failing test**
270
-
271
- Append to `test/service.test.mjs` (uses `createView`, `writeState`, `readState` imports already present; add `writeFileSync` if not imported):
272
-
273
- ```js
274
- test("ensureHost probes PTY support with TTL cache, not forced refresh", () => {
275
- const root = freshRoot();
276
- try {
277
- process.env.AGENT_BOARD_FORCE_PTY = "1";
278
- const meta = createView(root, { id: "v1", name: "a", cwd: "/r" });
279
- writeFileSync(meta.sessionFile, JSON.stringify({ type: "session", id: "s1", cwd: "/r" }) + "\n");
280
- const probeCalls = [];
281
- const svc = service(root, {
282
- ptySupport: (opts = {}) => { probeCalls.push(opts); return { ok: true }; },
283
- launchHost: () => ({ pid: process.pid, configPath: "/no/host-config.json" }),
284
- });
285
- const res = svc.ensureHost("v1");
286
- assert.equal(res.ok, true);
287
- assert.ok(probeCalls.length >= 1);
288
- for (const opts of probeCalls) {
289
- assert.notEqual(opts?.refresh, true, "ensureHost must not force ptySupport refresh");
290
- }
291
- } finally {
292
- delete process.env.AGENT_BOARD_FORCE_PTY;
293
- rmSync(root, { recursive: true, force: true });
294
- }
295
- });
296
- ```
297
-
298
- - [ ] **Step 2: Run test to verify it fails**
299
-
300
- Run: `node --test test/service.test.mjs`
301
- Expected: new test FAILS — `ensureHost` currently calls `ptySupport({ refresh: true })` so `opts.refresh === true`.
302
-
303
- - [ ] **Step 3: Write minimal implementation**
304
-
305
- In `src/runtime/service.mjs` `ensureHost`, change:
306
-
307
- ```js
308
- const pty = ptySupport({ refresh: true });
309
- ```
310
-
311
- to:
312
-
313
- ```js
314
- // Default probe semantics: success is cached for the process lifetime and a
315
- // failed probe retries on a short TTL. Forcing refresh here would spawn a
316
- // probe process on every keypress-driven prewarm when PTY support is broken.
317
- const pty = ptySupport();
318
- ```
319
-
320
- - [ ] **Step 4: Run test to verify it passes**
321
-
322
- Run: `node --test test/service.test.mjs`
323
- Expected: PASS, all tests in file green.
324
-
325
- - [ ] **Step 5: Commit**
326
-
327
- ```bash
328
- git add src/runtime/service.mjs test/service.test.mjs
329
- git commit -m "fix: stop forcing PTY probe refresh on every ensureHost (issue #9)"
330
- ```
331
-
332
- ---
333
-
334
- ### Task 4: Full verification + performance evidence
335
-
336
- **Files:**
337
- - No source changes expected (fix-only task; measurement script is throwaway, not committed).
338
-
339
- **Interfaces:**
340
- - Consumes: all previous tasks merged on the branch.
341
-
342
- - [ ] **Step 1: Install deps and run full verify**
343
-
344
- Run: `cd <worktree> && npm install --no-audit --no-fund && npm run verify`
345
- Expected: `tsc --noEmit` clean, all 27+ test files pass, `npm pack --dry-run` succeeds.
346
-
347
- - [ ] **Step 2: Measure real-store improvement (read-only)**
348
-
349
- Run against the user's real store (read-only, no mutation):
350
-
351
- ```bash
352
- node --input-type=module -e "
353
- import { listRows } from './src/core/store.mjs';
354
- import os from 'node:os'; import path from 'node:path';
355
- const root = path.join(os.homedir(), '.pi/agent/agent-board');
356
- listRows(root);
357
- const t0 = performance.now(); listRows(root); const t1 = performance.now();
358
- console.log('listRows post-fix:', (t1 - t0).toFixed(1), 'ms');
359
- "
360
- ```
361
-
362
- Expected: ≤ 40ms on the 168-view store (was 180–204ms). Record the number.
363
-
364
- - [ ] **Step 3: Report numbers**
365
-
366
- Paste before/after into the PR body and as an issue #9 comment. No commit needed for this task.