@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,106 +0,0 @@
1
- # Spec: cwd 常用目录在全量历史里搜索(issue #262)
2
-
3
- Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-11-03T08:21:00+08:00
4
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-11-03T08:30:00+08:00
5
-
6
- ## 1. 问题陈述
7
-
8
- cwd picker 的 favorites 候选池在**打开时**就被截断到 8 条(`src/ui/dashboard.ts:755`),后续 `filterCwdCandidates`(`src/core/launch-options.mjs:358`)只在这 8 条上做子串过滤。结果是:`cwd-stats.json` 里已记录、但排名 > 8 的目录,输入其名字片段完全搜不到;过滤为空还会回落 `browse`,browse 以 `launch.cwd` 为基准解析片段,必然找不到,界面只显示 `no matches`,用户误以为项目从未被记录。
9
-
10
- 排名是「使用强度」排序,不是「用户会去找」的排序;历史越长越容易命中这个窗口。
11
-
12
- ## 2. 根因(已对真实代码逐行核对)
13
-
14
- 1. `src/ui/dashboard.ts:755`:`launch.cwdRanked = existingCwdCandidates(rankedCwdCandidates(this.deps.root, 8))` —— 前置截断。
15
- 2. `src/core/cwd-stats.mjs:98`:`rankedCwdCandidates(root, limit = 8)` —— 默认值同源,`rows.slice(0, Math.max(1, limit))` 后才追加 home 兜底。
16
- 3. `src/core/launch-options.mjs:409`:`nextCwdPickerState` 在传入列表上过滤;无命中即 `browse`(`:413` 之后的 fallback),browse 的 `listDirectorySuggestions`(函数头 `:302`,以 `launch.cwd` 为基准解析在 `:303-305`)解析项目名片段,得到 `[]`。
17
- 4. 渲染层 `renderLaunchSuggestions`(`src/ui/dashboard.ts:1685`)本就只画 6 行 + `↓ N more`,候选数组变长不会撑高对话框——**不是**需要截断的原因。
18
-
19
- ## 3. 方案选择
20
-
21
- | 方案 | 内容 | 结论 |
22
- | --- | --- | --- |
23
- | A(选定) | 打开 picker 时加载**全量** ranked 历史;`rankedCwdCandidates` 默认不截断 | 一次性消除「记录存在但搜不到」,渲染层零改动 |
24
- | B | 8 → 20(或任意更大常量) | 只把窗口推后,记录继续增长会复发;且两处常量需同步,易改漏 |
25
- | C | 在 `nextCwdPickerState` 里先过滤再 slice | 状态机拿不到被截掉的条目,等同 B;且会破坏 `↓ N more` 的语义(slice 后总数失真) |
26
-
27
- 选定 A。`limit` 参数保留(默认 `Number.POSITIVE_INFINITY`),仅供测试或未来明确需要显式封顶的调用方使用;当前唯一生产调用方 dashboard 不传 limit。
28
-
29
- ## 4. 数据流与契约
30
-
31
- ```
32
- readCwdStats(root) // 全量 entries(已有)
33
- → rankedCwdCandidates(root) // 全量排序:count desc → lastUsed desc,末尾补 home(若缺)
34
- → existingCwdCandidates(...) // 丢弃已不存在的目录(已有,O(N) statSync)
35
- → launch.cwdRanked // picker 的搜索池
36
- → nextCwdPickerState(query, pool, cwd) // 纯函数:命中 → favorites;空/无命中 → browse(已有语义)
37
- ```
38
-
39
- 改动点:
40
-
41
- - `src/core/cwd-stats.mjs`:`rankedCwdCandidates(root, limit = Number.POSITIVE_INFINITY)`;更新 JSDoc,写明默认不封顶、显式 limit 仅用于调用方需要的封顶。
42
- - `src/ui/dashboard.ts:755`:去掉 `, 8`,改为 `existingCwdCandidates(rankedCwdCandidates(this.deps.root))`。
43
- - 其余(过滤、状态机、browse、渲染)**一行不改**。
44
-
45
- 行为契约(改后):
46
-
47
- | 输入 | 模式 | 候选 |
48
- | --- | --- | --- |
49
- | 空 query | favorites | 全量 ranked(按 count/lastUsed 排序,home 兜底) |
50
- | 非空 query,历史中任意一条路径含该子串(大小写不敏感) | favorites | 全部命中项,保持排名序 |
51
- | 非空 query,历史中无任何命中 | browse | 与现状一致(以 `launch.cwd` 为基准的文件系统浏览) |
52
- | stats 读取失败 | browse | 与现状一致(`openLaunchPicker` 的 try/catch 置空池) |
53
- | 池中目录全部已删除 | browse | `existingCwdCandidates` 过滤后为空 → 现状 fallback |
54
-
55
- ## 5. 非目标
56
-
57
- - 不修 browse 模式的软链目录枚举缺陷(`src/core/launch-options.mjs:317-318`,`Dirent.isDirectory()` 不跟随软链)——独立 issue 跟踪。
58
- - 不改渲染窗口(6 行)、不改 `↑/↓` 钳制语义、不加 fuzzy 匹配、不引入手动收藏、不改 `cwd-stats.json` 格式与排名规则。
59
- - 不改历史设计文档 `docs/superpowers/specs/2026-08-22-cwd-favorites-design.md` 中的 “Top 8” 记录(历史留档,由本 spec 取代其行为约定)。
60
-
61
- ## 6. 已知边界与风险
62
-
63
- - **O(N) statSync**:打开 picker 时对 N 条历史各做一次 `statSync`(`existingCwdCandidates`)。N = 记录目录数,本机 15、issue 机器 28,量级为微秒到毫秒;perf gate(A11)不覆盖此路径,且打开动作是每 picker 会话一次而非每帧。若未来 N 达到数千,需要另设懒校验/缓存方案,不在本 issue。
64
- - **home 兜底的滚动距离**:home 不在 stats 时仍追加在列表末尾;记录越多,空 query 下按 ↓ 到 home 的距离越长(旧实现固定在 9 位)。home 在真实使用中几乎总是排名第一(本机 187×),故接受;不额外改变排序语义。
65
- - **候选总数上界 = 历史条目数**:无人为 cap,故不存在「再次写死一个数字」的复发点。
66
-
67
- ## 7. 可测性拆分设计(必答)
68
-
69
- 保持既有分层,**不新增生产 helper、不在 dashboard 里加逻辑**:
70
-
71
- | 单元 | 位置 | 形态 | 测试层级 |
72
- | --- | --- | --- | --- |
73
- | 排名 + home 兜底 | `rankedCwdCandidates`(`src/core/cwd-stats.mjs`) | 读 stats(I/O 壳)+ 纯排序;不碰 fs 目录校验 | unit(临时 root 写 `cwd-stats.json`) |
74
- | stale 目录过滤 | `existingCwdCandidates`(`src/core/launch-options.mjs`) | 纯函数 + `statSync` 谓词 | unit(既有测试覆盖) |
75
- | 子串过滤 | `filterCwdCandidates` | 纯函数 | unit(既有) |
76
- | 模式状态机 | `nextCwdPickerState` | 纯函数 | unit(新增「>8 条列表、命中第 10 条」用例) |
77
- | dashboard 接线 | `openLaunchPicker`(`src/ui/dashboard.ts:749-762`) | 薄 I/O 壳:加载池 → 调状态机 | integration(TS 探针 + `handleInput`,沿用 `test-support/cwd-browse-tab-probe.ts` 模式) |
78
-
79
- 测试边界:排名/过滤/状态机的语义全部由纯函数单测钉住;「打开 picker 时池子是否被截断」只能由 dashboard 探针钉住(单测对传入列表一视同仁,无法发现调用方的 `, 8`)。渲染层不新增逻辑,故不加渲染断言。
80
-
81
- ## 8. 验收矩阵(必答)
82
-
83
- | ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
84
- | --- | --- | --- | --- | --- |
85
- | A1 | 排名默认返回全量历史 | 自动化验证(unit) | `node --test test/cwd-stats.test.mjs`:临时 root 写 10 条 stats,调 `rankedCwdCandidates(root)`(不传 limit) | 返回 10 条按 count desc / lastUsed desc 排列 + home 兜底;不出现 8 条截断 |
86
- | A2 | picker 能搜到排名 > 8 的已记录目录(issue 主场景) | 自动化验证(integration,探针) | `node --test test/dashboard-cwd-favorites-pool.test.mjs`:临时 root 建 10 个真实目录并写入 stats(目标为第 10 名);先 `dash.input = "go"`(`openLaunchDialog` 在空 prompt 时直接回 list,`launch` 为 null,见 `dashboard.ts:676-678`、`:750-751`),再 `openLaunchDialog()` → `openLaunchPicker("cwd", "")` → 逐字符 `handleInput("a"/"l"/"e"/"r")` | `cwdPickerMode === "favorites"`,`cwdSuggestions` 含目标路径(修复前为 `browse` + `[]`) |
87
- | A3 | 状态机在超 8 条列表上仍按子串匹配 | 自动化验证(unit) | `node --test test/launch-options.test.mjs`:12 条 ranked,query 命中第 10 条 | `mode === "favorites"`,suggestions 含该条且保持排名序 |
88
- | A4 | 既有行为不回归(空 query、无命中回 browse、stale 过滤、browse/favorites Tab) | 自动化验证(unit + integration) | `node --test test/launch-options.test.mjs test/cwd-stats.test.mjs test/dashboard-cwd-tab.test.mjs` | 全绿;空 query 仍 favorites、无命中仍 browse、Tab 语义不变 |
89
- | A5 | 类型与套件门禁 | 自动化验证(static + build) | `npm run typecheck`、`npm run verify` | 零类型错误;套件、覆盖率、perf gate、pack:dry 全过 |
90
- | A6 | 失败分支:stats 不可读 / 目录全失效 | 自动化验证(unit) | `node --test test/cwd-stats.test.mjs test/launch-options.test.mjs`(既有 corrupt stats 用例 + `existingCwdCandidates` + 空池回 browse) | 读取失败降级为空/仅 home;池为空时仍回落 browse,不抛异常 |
91
- | U1 | 真实 TUI 手感(本机 15 条 stats,目标排名第 11) | 用户实测 | 前置:更新 pi 的 git 安装副本并重启 pi——`git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board pull --ff-only origin main`,再 `git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board log -1 --oneline` 确认含修复 commit(若 pi 有自身更新机制如 `pi install`/update,以它为准)。步骤:重启后 board → `i` 输入 prompt → `Enter` → `cwd` 字段 `Enter` → 输入 `commit` | favorites 模式出现 `~/git-repo/github/pi-commit-lint 1×`(第 11 名);再输 `github-watching` 出现 `~/workspace/proj/github-watching/pi-agent-board 2×`(第 10 名);空 query 榜单顺序不变 |
92
-
93
- U1 可执行时机:合并 + 安装副本更新后。合并前以 A2 的探针作为等价自动化证据;U1 在合并后由用户执行,未执行前记为 `pending`,不得宣称全部验收完成。
94
-
95
- ## 9. 文件清单
96
-
97
- | 文件 | 改动 |
98
- | --- | --- |
99
- | `src/core/cwd-stats.mjs` | `rankedCwdCandidates` 默认 limit 改为 `Number.POSITIVE_INFINITY`;JSDoc 更新 |
100
- | `src/ui/dashboard.ts` | `openLaunchPicker` 去掉 `, 8`(仅 1 行) |
101
- | `test/cwd-stats.test.mjs` | 新增 A1 全量用例 |
102
- | `test/launch-options.test.mjs` | 新增 A3「>8 条列表命中第 10 条」用例 |
103
- | `test-support/cwd-favorites-pool-probe.ts` | 新增 A2 探针(沿用 #127 探针模式) |
104
- | `test/dashboard-cwd-favorites-pool.test.mjs` | 新增 A2 集成测试 |
105
-
106
- 命名遵循仓内约定:测试文件 kebab-case,探针放 `test-support/`,commit 用 conventional commits 并在 subject 末尾带 `(#<PR>)`。
@@ -1,135 +0,0 @@
1
- /**
2
- * Budget audit for test files (issue #95 F2).
3
- *
4
- * Mechanical discipline: a test's declared timeout must be able to contain
5
- * its waits (rule a), and an app-level deadline a test drives must be covered
6
- * by the test's own budget unless explicitly escaped (rule b). Pure functions
7
- * only — the gate lives in test/budget-audit.test.mjs, which runs these over
8
- * fixture sources AND the real test tree.
9
- */
10
-
11
- /** Strip escaped lines (`// budget: <reason>`) from a source. @param {string} source @returns {string} */
12
- export function stripEscapedLines(source) {
13
- return source.split("\n").filter((line) => !line.includes("// budget:")).join("\n");
14
- }
15
-
16
- /** @param {string} source @returns {number | null} */
17
- export function parseFileDefaultWaitMs(source) {
18
- // The default lives in the parameter list: `async function waitFor(predicate, timeoutMs = 15_000, ...) {`.
19
- const m = /async function waitFor\([^)]*?timeoutMs\s*=\s*([0-9_]+)[^)]*\)\s*\{/.exec(source);
20
- return m ? Number(m[1].replace(/_/g, "")) : null;
21
- }
22
-
23
- /** Balanced-paren argument text for the call whose `(` sits at fromIndex. @param {string} text @param {number} fromIndex @returns {string | null} */
24
- function callArgs(text, fromIndex) {
25
- let depth = 0;
26
- for (let i = fromIndex; i < text.length; i++) {
27
- if (text[i] === "(") depth++;
28
- else if (text[i] === ")") {
29
- depth--;
30
- if (depth === 0) return text.slice(fromIndex + 1, i);
31
- }
32
- }
33
- return null;
34
- }
35
-
36
- /** Top-level (depth-0) comma-separated arguments of a call's argument text. @param {string} args @returns {string[]} */
37
- function splitTopLevelArgs(args) {
38
- const parts = [];
39
- let depth = 0;
40
- let current = "";
41
- for (const ch of args) {
42
- if (ch === "(" || ch === "[" || ch === "{") depth += 1;
43
- else if (ch === ")" || ch === "]" || ch === "}") depth -= 1;
44
- if (ch === "," && depth === 0) {
45
- parts.push(current);
46
- current = "";
47
- } else current += ch;
48
- }
49
- parts.push(current);
50
- return parts;
51
- }
52
-
53
- /**
54
- * Distinct numeric budget literals a `waitFor(...)` call declares: the trailing
55
- * numeric argument (`waitFor(pred, 30_000)` / `waitFor(pred, 30_000, 50)`) plus
56
- * the first top-level numeric argument, which covers
57
- * `waitFor(pred, 30_000, capture)` shapes where the trailing regex only ever
58
- * sees the capture thunk — and the `(pred, timeout, interval)` shape, whose
59
- * trailing number is the interval, not the budget. Empty when the call omits
60
- * the budget and inherits the file default.
61
- * @param {string} args @returns {number[]}
62
- */
63
- function waitLiteralsIn(args) {
64
- const out = [];
65
- const add = (raw) => {
66
- const value = Number(raw.replace(/_/g, ""));
67
- if (!out.includes(value)) out.push(value);
68
- };
69
- const trailing = /,\s*([0-9_]+)\s*,?\s*$/.exec(args.trim());
70
- if (trailing) add(trailing[1]);
71
- const first = splitTopLevelArgs(args).map((a) => a.trim()).find((a) => /^[0-9_]+$/.test(a));
72
- if (first) add(first);
73
- return out;
74
- }
75
-
76
- /**
77
- * @param {string} source
78
- * @returns {{ fileDefaultWaitMs: number | null, tests: Array<{ name: string, declaredTimeoutMs: number | null, waitLiterals: number[], hasDefaultWaitCall: boolean, deadlineLiterals: number[] }> }}
79
- */
80
- export function parseTestBudgets(source) {
81
- const clean = stripEscapedLines(source);
82
- const fileDefaultWaitMs = parseFileDefaultWaitMs(clean);
83
- const tests = [];
84
- const starts = [];
85
- const re = /^(?:export )?test\(/gm;
86
- for (let m = re.exec(clean); m; m = re.exec(clean)) starts.push(m.index);
87
- for (let i = 0; i < starts.length; i++) {
88
- const chunk = clean.slice(starts[i], starts[i + 1] ?? clean.length);
89
- const nameM = /^(?:export )?test\("([^"]+)"/.exec(chunk);
90
- const toM = /timeout:\s*([0-9_]+)/.exec(chunk.slice(0, 400));
91
- const waitLiterals = [];
92
- let hasDefaultWaitCall = false;
93
- const waitRe = /waitFor\(/g;
94
- for (let w = waitRe.exec(chunk); w; w = waitRe.exec(chunk)) {
95
- const args = callArgs(chunk, w.index + "waitFor".length);
96
- if (args == null) continue;
97
- const literals = waitLiteralsIn(args);
98
- if (literals.length > 0) waitLiterals.push(...literals);
99
- else hasDefaultWaitCall = true;
100
- }
101
- const deadlineLiterals = [];
102
- const dlRe = /timeoutMs:\s*([0-9_]+)/g;
103
- for (let d = dlRe.exec(chunk); d; d = dlRe.exec(chunk)) deadlineLiterals.push(Number(d[1].replace(/_/g, "")));
104
- tests.push({ name: nameM ? nameM[1] : `<test ${i + 1}>`, declaredTimeoutMs: toM ? Number(toM[1].replace(/_/g, "")) : null, waitLiterals, hasDefaultWaitCall, deadlineLiterals });
105
- }
106
- return { fileDefaultWaitMs, tests };
107
- }
108
-
109
- /**
110
- * Rule (a): declared timeout >= max(single explicit wait, file default when a
111
- * call omits it) + margin, margin = max(5000, wait / 3).
112
- * Rule (b): an app deadline >= 30s needs a declared timeout >= it (escape via
113
- * `// budget:` lines, stripped upstream).
114
- * @param {Array<{ path: string, source: string }>} sources
115
- * @returns {Array<{ path: string, test: string, rule: string, message: string }>}
116
- */
117
- export function auditBudgets(sources) {
118
- const violations = [];
119
- for (const { path, source } of sources) {
120
- const parsed = parseTestBudgets(source);
121
- for (const t of parsed.tests) {
122
- const waits = [...t.waitLiterals];
123
- if (t.hasDefaultWaitCall && parsed.fileDefaultWaitMs != null) waits.push(parsed.fileDefaultWaitMs);
124
- if (t.declaredTimeoutMs != null && waits.length > 0) {
125
- const maxWait = Math.max(...waits);
126
- const margin = Math.max(5000, Math.round(maxWait / 3));
127
- if (t.declaredTimeoutMs < maxWait + margin) violations.push({ path, test: t.name, rule: "rule-a", message: `declared timeout ${t.declaredTimeoutMs}ms cannot contain max wait ${maxWait}ms + margin ${margin}ms` });
128
- }
129
- for (const d of t.deadlineLiterals) {
130
- if (d >= 30000 && (t.declaredTimeoutMs == null || t.declaredTimeoutMs < d)) violations.push({ path, test: t.name, rule: "rule-b", message: `app deadline ${d}ms needs a declared test timeout >= it (or a // budget: escape)` });
131
- }
132
- }
133
- }
134
- return violations;
135
- }