@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,114 +0,0 @@
1
- # Issue #13 Drain-Path PTY Probe Refresh — 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:** Stop `drainNextFollowUp` (reachable from the 700ms reconcile poll) from forcing a real `pty.spawn` probe on every cycle; switch it to cached-probe semantics and lock that in with a red-green test.
6
-
7
- **Architecture:** One-line production change (`ptySupport({ refresh: true })` → `ptySupport()` in `drainNextFollowUp`) + one new test in `test/service.test.mjs` driving the public `reconcile()` API with an injected `ptySupport` spy, modeled on the existing "ensureHost probes PTY support with TTL cache" test and the "busy replies queue and drain when idle" row-state pattern.
8
-
9
- **Tech Stack:** Node built-in test runner; existing `createService` DI seams (`ptySupport`, `launch`).
10
-
11
- ## Global Constraints
12
-
13
- - Production delta is exactly the one-line probe switch in `src/runtime/service.mjs` `drainNextFollowUp` (spec A4).
14
- - `dispatch` (L616) and `reply` (L649) keep `refresh: true` — explicit user actions.
15
- - No changes to `ptySpawnSupported` / `shouldProbePtySupport`.
16
- - Test command: `npm test` (= `node --test test/*.test.mjs`).
17
-
18
- **Execution mode:** inline (executing-plans) — single task, one-line production diff + one test.
19
-
20
- ---
21
-
22
- ### Task 1: Red-green test + one-line probe switch
23
-
24
- **Files:**
25
- - Modify: `src/runtime/service.mjs:496` (inside `drainNextFollowUp`)
26
- - Modify: `test/service.test.mjs` (append new test after "ensureHost probes PTY support with TTL cache, not forced refresh", ~L958)
27
-
28
- **Interfaces:**
29
- - Consumes: `createService` opts `{ ptySupport?: (opts?) => {ok, reason?}, launch?: typeof launchRun }`; public `svc.reconcile()`, `svc.queueFollowUp(viewId, text)`; helpers `freshRoot()`, `createView(root, {id, name, cwd})`, `readState`/`writeState`, `rmSync`.
30
- - Produces: nothing consumed by other tasks.
31
-
32
- - [ ] **Step 1: Write the failing test (red)**
33
-
34
- Append to `test/service.test.mjs` (after the "ensureHost probes PTY support with TTL cache, not forced refresh" test):
35
-
36
- ```js
37
- test("reconcile auto-drain uses the cached PTY probe, never forced refresh", () => {
38
- const root = freshRoot();
39
- try {
40
- createView(root, { id: "v1", name: "a", cwd: "/r" });
41
- const st = readState(root, "v1");
42
- st.semanticState = "idle";
43
- st.processState = "exited";
44
- writeState(root, st);
45
- const probeCalls = [];
46
- const svc = service(root, {
47
- ptySupport: (opts = {}) => {
48
- probeCalls.push(opts);
49
- return { ok: false, reason: "test" };
50
- },
51
- launch: () => ({ pid: null, configPath: "/no/config.json" }),
52
- });
53
- const queued = svc.queueFollowUp("v1", "next step");
54
- assert.equal(queued.ok, true);
55
- const fixed = svc.reconcile();
56
- assert.ok(fixed >= 1, "reconcile drained the queued follow-up");
57
- assert.ok(probeCalls.length >= 1, "drain path probed ptySupport");
58
- for (const opts of probeCalls) {
59
- assert.notEqual(opts?.refresh, true, "reconcile→drain must not force ptySupport refresh");
60
- }
61
- } finally {
62
- rmSync(root, { recursive: true, force: true });
63
- }
64
- });
65
- ```
66
-
67
- - [ ] **Step 2: Run it, verify it fails against current production code**
68
-
69
- Run: `node --test test/service.test.mjs`
70
- Expected: FAIL — `reconcile→drain must not force ptySupport refresh` (production still passes `{refresh: true}`).
71
-
72
- - [ ] **Step 3: The one-line fix (green)**
73
-
74
- In `src/runtime/service.mjs` `drainNextFollowUp` (~L496), change:
75
-
76
- ```js
77
- const pty = ptySupport({ refresh: true });
78
- ```
79
-
80
- to:
81
-
82
- ```js
83
- const pty = ptySupport();
84
- ```
85
-
86
- (Only inside `drainNextFollowUp`. The `dispatch` and `reply` occurrences keep `{ refresh: true }`.)
87
-
88
- - [ ] **Step 4: Run the test file, verify pass**
89
-
90
- Run: `node --test test/service.test.mjs`
91
- Expected: PASS including the new test.
92
-
93
- - [ ] **Step 5: Full suite (A2/A3)**
94
-
95
- Run: `npm test`
96
- Expected: all green.
97
-
98
- - [ ] **Step 6: Verify A4 — scope**
99
-
100
- Run: `git diff main -- src/ test/`
101
- Expected: exactly one line changed in `src/runtime/service.mjs`, one test appended in `test/service.test.mjs`.
102
-
103
- - [ ] **Step 7: Commit**
104
-
105
- ```bash
106
- git add src/runtime/service.mjs test/service.test.mjs
107
- git commit -m "perf: use cached PTY probe on the reconcile drain path (issue #13)"
108
- ```
109
-
110
- ## Self-Review
111
-
112
- - Spec coverage: A1 (Steps 1-2 red, 3-4 green), A2/A3 (Step 5), A4 (Step 6). Covered.
113
- - Placeholder scan: exact code shown for both edits.
114
- - Type consistency: spy signature matches `createService` opts type; `launch` fake matches the pattern used by the existing "busy replies queue and drain when idle" test.
@@ -1,73 +0,0 @@
1
- # Issue #38 Windows WezTerm IME Docs — 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:** Document that Windows WezTerm attach sessions need a visible hardware cursor (`PI_HARDWARE_CURSOR=1` / `"showHardwareCursor": true`) for IME candidate tracking.
6
-
7
- **Architecture:** One new Troubleshooting subsection in README.md, placed after "### Attach is slow or keeps reconnecting".
8
-
9
- **Tech Stack:** Markdown only.
10
-
11
- ## Global Constraints
12
-
13
- - Docs-only: README.md is the only non-process file touched (spec A2).
14
- - Both fixes documented with scope guidance; cosmetic trade-off stated.
15
- - No upstream comment on earendil-works/pi#5200 (owner's voice).
16
-
17
- **Execution mode:** inline (executing-plans).
18
-
19
- ---
20
-
21
- ### Task 1: Add the Troubleshooting subsection
22
-
23
- **Files:**
24
- - Modify: `README.md` (Troubleshooting section, after the "### Attach is slow or keeps reconnecting" block)
25
-
26
- - [ ] **Step 1: Insert the subsection**
27
-
28
- Insert after the "### Attach is slow or keeps reconnecting" paragraph, before "### Start & attach falls back to background":
29
-
30
- ````markdown
31
- ### IME candidate window is stuck at the window edge (Windows WezTerm)
32
-
33
- On Windows WezTerm with a WSL2 backend, the IME candidate window may stay
34
- pinned to the right edge instead of following the text cursor in an attached
35
- session. Windows WezTerm only tracks the IME candidate position from the
36
- visible hardware cursor, and Pi hides the hardware cursor by default — the
37
- block cursor you see in the editor is drawn content, not the hardware cursor.
38
- Linux terminals are not affected.
39
-
40
- Make the hardware cursor visible, either way:
41
-
42
- ```bash
43
- export PI_HARDWARE_CURSOR=1 # machine-local, e.g. ~/.zshrc.local
44
- ```
45
-
46
- or set `"showHardwareCursor": true` in Pi's `settings.json` (syncs across
47
- machines if the config is version-controlled; harmless on Linux).
48
-
49
- Trade-off: the real terminal cursor becomes visible inside the TUI. This is
50
- cosmetic only.
51
- ````
52
-
53
- - [ ] **Step 2: Verify A1 (content presence)**
54
-
55
- Run: `grep -n "PI_HARDWARE_CURSOR=1\|showHardwareCursor\|IME candidate window is stuck" README.md`
56
- Expected: 3+ hits inside the Troubleshooting section.
57
-
58
- - [ ] **Step 3: Verify A2 (scope)**
59
-
60
- Run: `git diff main --stat`
61
- Expected: README.md + process docs under docs/superpowers/ only.
62
-
63
- - [ ] **Step 4: Commit**
64
-
65
- ```bash
66
- git add README.md
67
- git commit -m "docs: Windows WezTerm IME needs a visible hardware cursor (issue #38)"
68
- ```
69
-
70
- ## Self-Review
71
-
72
- - Spec coverage: A1 (Step 2), A2 (Step 3). Covered.
73
- - Placeholder scan: full markdown shown.
@@ -1,143 +0,0 @@
1
- # Issue #39 Truncate Surrogate-Safe Cut — 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 `truncate()` in `src/core/heuristics.mjs` never split a surrogate pair (cut-point back-off within the UTF-16 unit budget) and never emit lone surrogates, eliminating the U+FFFD → width-mismatch → wrap → stacked-frames chain at its repo-side root.
6
-
7
- **Architecture:** Pure-function change inside `truncate` only: check whether the unit before the cut point is a high surrogate whose low surrogate is being dropped; if so, back the cut off by one unit. Strip any lone surrogates from the returned string on both paths via a small private regex helper.
8
-
9
- **Tech Stack:** Node built-in test runner; no new dependencies.
10
-
11
- ## Global Constraints
12
-
13
- - Keep the UTF-16 unit budget semantics of `n` (spec Decision 1) — do NOT switch to code-point counting.
14
- - Only `src/core/heuristics.mjs` (truncate + private helper) and `test/heuristics.test.mjs` change (spec A5).
15
- - Existing tests must pass unmodified (spec A3).
16
- - Test command: `npm test`.
17
-
18
- **Execution mode:** inline (executing-plans).
19
-
20
- ---
21
-
22
- ### Task 1: Red tests at the surrogate boundaries
23
-
24
- **Files:**
25
- - Modify: `test/heuristics.test.mjs` (extend the existing "truncate adds ellipsis" test block or add a new test after it)
26
-
27
- **Interfaces:**
28
- - Consumes: `truncate(s, n)` exported from `src/core/heuristics.mjs`.
29
- - Produces: nothing consumed by other tasks.
30
-
31
- - [ ] **Step 1: Write the failing tests (red)**
32
-
33
- Replace the existing block:
34
-
35
- ```js
36
- test("truncate adds ellipsis", () => {
37
- assert.equal(truncate("hello", 10), "hello");
38
- assert.equal(truncate("hello world", 5), "hell…");
39
- });
40
- ```
41
-
42
- with:
43
-
44
- ```js
45
- test("truncate adds ellipsis", () => {
46
- assert.equal(truncate("hello", 10), "hello");
47
- assert.equal(truncate("hello world", 5), "hell…");
48
- assert.equal(truncate("一二三四五六", 5), "一二三四…");
49
- });
50
-
51
- test("truncate never splits a surrogate pair", () => {
52
- // Cut point lands between the high and low surrogate of 👍: back off.
53
- assert.equal(truncate("a👍b", 3), "a…");
54
- // Long string whose 80-unit cut lands inside the emoji (issue #39 repro shape).
55
- const s = "x".repeat(59) + "完成 — LGTM " + "👍" + "y".repeat(10);
56
- const out = truncate(s, 80);
57
- assert.ok(!/[\uD800-\uDBFF]$/.test(out.replace(/…$/, "")), "no trailing lone high surrogate before ellipsis");
58
- // The pair survives whole when it fits the budget.
59
- assert.equal(truncate("👍", 2), "👍");
60
- assert.equal(truncate("a👍", 3), "a👍");
61
- });
62
-
63
- test("truncate strips lone surrogates from input", () => {
64
- assert.equal(truncate("ab\ud83d", 10), "ab"); // lone high surrogate, short path
65
- assert.equal(truncate("ab\ud83dcd", 4), "abc…"); // lone high surrogate, truncated path
66
- assert.equal(truncate("\udc4dab", 10), "ab"); // lone low surrogate, short path
67
- });
68
- ```
69
-
70
- Note on `"ab\ud83dcd"` with n=4: units are a,b,\ud83d,c,d (length 5 > 4) → cut end=3 → slice = "ab\ud83d" → the high surrogate is last-but-0 with no following unit in the *kept* slice; input's `\ud83d` is followed by `c` (not a low surrogate), so it is a lone surrogate in the input and must be stripped → expected "ab…". If the implementation instead computes end=3, sees charCodeAt(2) is a high surrogate and charCodeAt(3)="c" is not a low surrogate, no pair back-off applies (no pair to preserve) — the strip helper removes it → "ab…". Both reasonings converge; assert the observable "ab…".
71
-
72
- - [ ] **Step 2: Run, verify red**
73
-
74
- Run: `node --test test/heuristics.test.mjs`
75
- Expected: the two new tests FAIL (current implementation splits pairs / keeps lone surrogates), "truncate adds ellipsis" still passes.
76
-
77
- ### Task 2: Implement the surrogate-safe truncate
78
-
79
- **Files:**
80
- - Modify: `src/core/heuristics.mjs` (replace `truncate`)
81
-
82
- - [ ] **Step 3: New implementation**
83
-
84
- Replace the existing `truncate` function with:
85
-
86
- ```js
87
- const LONE_HIGH_SURROGATE_RE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/g;
88
- const LONE_LOW_SURROGATE_RE = /(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
89
-
90
- /** @param {string} s */
91
- function stripLoneSurrogates(s) {
92
- if (!s) return s;
93
- return s.replace(LONE_HIGH_SURROGATE_RE, "").replace(LONE_LOW_SURROGATE_RE, "");
94
- }
95
-
96
- /**
97
- * Truncate to `n` chars with an ellipsis (counts characters, not display width).
98
- * The budget `n` is in UTF-16 units. The cut never splits a surrogate pair
99
- * (backs off one unit when it would), and lone surrogates in the input are
100
- * stripped from the result — a lone surrogate renders as U+FFFD, whose
101
- * terminal width can disagree with the computed width and misalign rows.
102
- * @param {string} s
103
- * @param {number} n
104
- * @returns {string}
105
- */
106
- export function truncate(s, n) {
107
- const str = String(s ?? "");
108
- if (str.length <= n) return stripLoneSurrogates(str);
109
- let end = Math.max(0, n - 1);
110
- const lastUnit = str.charCodeAt(end - 1);
111
- const nextUnit = str.charCodeAt(end);
112
- if (lastUnit >= 0xd800 && lastUnit <= 0xdbff && nextUnit >= 0xdc00 && nextUnit <= 0xdfff) end -= 1;
113
- return `${stripLoneSurrogates(str.slice(0, end))}…`;
114
- }
115
- ```
116
-
117
- - [ ] **Step 4: Run the file, verify green**
118
-
119
- Run: `node --test test/heuristics.test.mjs`
120
- Expected: all pass, including the two new tests and the unchanged originals.
121
-
122
- - [ ] **Step 5: Full suite (A4)**
123
-
124
- Run: `npm test`
125
- Expected: all green.
126
-
127
- - [ ] **Step 6: Verify A5 scope**
128
-
129
- Run: `git diff main -- src/ test/`
130
- Expected: only `src/core/heuristics.mjs` and `test/heuristics.test.mjs`.
131
-
132
- - [ ] **Step 7: Commit**
133
-
134
- ```bash
135
- git add src/core/heuristics.mjs test/heuristics.test.mjs
136
- git commit -m "fix: truncate never splits surrogate pairs or emits lone surrogates (issue #39)"
137
- ```
138
-
139
- ## Self-Review
140
-
141
- - Spec coverage: A1 (Task 1 red + Task 2), A2 (lone-surrogate tests), A3 (unchanged originals + CJK case), A4 (Step 5), A5 (Step 6). Covered.
142
- - Placeholder scan: full code shown.
143
- - Type consistency: `stripLoneSurrogates` string→string; `charCodeAt` unit checks match the surrogate ranges used in the regexes.
@@ -1,226 +0,0 @@
1
- # Issue #61 Mention Fallback Guards — 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:** Stop the Rule 6 bare `#N` mention fallback from lifting placeholder numbers: left-boundary guard, inline-code-span exclusion, PR-context kind-aware skip, plus a documented providers.json example for internal-platform CLIs.
6
-
7
- **Architecture:** Three guards inside `mentionFallback`/`MENTION_RE` (pure regex logic), one README example. Tests go through the public `extractCodeRefs` seam like all existing mention tests.
8
-
9
- **Tech Stack:** Node built-in test runner; no new dependencies.
10
-
11
- ## Global Constraints
12
-
13
- - Issue-only fallback stays (no PR mention candidate fabrication) — spec non-goals.
14
- - Existing mention tests unchanged (spec A5).
15
- - Only `src/core/code-refs.mjs`, `README.md` (Evidence section), `test/code-refs-extract.test.mjs` change (spec A7).
16
- - Test command: `npm test`.
17
-
18
- **Execution mode:** inline (executing-plans).
19
-
20
- ---
21
-
22
- ### Task 1: Red tests for the three guards + composite repro
23
-
24
- **Files:**
25
- - Modify: `test/code-refs-extract.test.mjs` (after the existing "mention fallback yields no winner" test, ~L283)
26
-
27
- - [ ] **Step 1: Add failing tests**
28
-
29
- ```js
30
- test("mention fallback ignores ##N heading forms and word-prefixed #N (issue #61)", () => {
31
- const result = extractCodeRefs(
32
- { commands: [], assistantTexts: ["##1378", "##1378", "##1378", "a#1378", "b#99"], worktreePath: null, branch: null, repoUrl: null, host: null },
33
- github()
34
- );
35
- assert.equal(result.issue, null);
36
- assert.equal(result.pr, null);
37
- });
38
-
39
- test("mention fallback ignores #N inside inline code spans (issue #61)", () => {
40
- const result = extractCodeRefs(
41
- { commands: [], assistantTexts: ["`monitor pr #1378`", "`#1378` x3", "see `#1378`"], worktreePath: null, branch: null, repoUrl: null, host: null },
42
- github()
43
- );
44
- assert.equal(result.issue, null);
45
- });
46
-
47
- test("mention fallback does not count pr-context #N toward issue candidates (issue #61)", () => {
48
- const result = extractCodeRefs(
49
- { commands: [], assistantTexts: ["monitor pr #1378", "monitor pr #1378", "monitor pr #1378", "monitor pr #1378", "monitor pr #1378"], worktreePath: null, branch: null, repoUrl: null, host: null },
50
- github()
51
- );
52
- assert.equal(result.issue, null);
53
- });
54
-
55
- test("issue #61 repro shape: guarded placeholder loses, real issue prose wins", () => {
56
- const result = extractCodeRefs(
57
- {
58
- commands: [],
59
- assistantTexts: [
60
- "设置 IID=\"#1378\" 会让 \"#$IID\" 展开成 \"##1378\" → 404", // ##1378 heading-guard form
61
- "`monitor pr #1378` 文档示例", // code-span form
62
- "monitor pr #1378 用户独立调用", // pr-context form
63
- "正在处理 issue #18", "issue #18 的修复", "更新 #18 状态", // real issue prose ×3
64
- ],
65
- worktreePath: null, branch: null, repoUrl: null, host: null,
66
- },
67
- github()
68
- );
69
- assert.equal(result.issue.number, 18);
70
- assert.equal(result.issue.strength, "mention");
71
- });
72
- ```
73
-
74
- Note on the heading test: `##1378` — with the guard, the second `#` is preceded by `#` → no match; the first `#` is followed by `#` not digits → no match. `a#1378`/`b#99`: preceded by word char → no match. Expected result with OLD code: `#1378` counts 3× (from the `##1378` lines' second hash) + more → a winner exists → test fails (red).
75
-
76
- - [ ] **Step 2: Run, verify red**
77
-
78
- Run: `node --test test/code-refs-extract.test.mjs`
79
- Expected: the new tests fail against current code; existing tests green.
80
-
81
- ### Task 2: Implement the guards
82
-
83
- **Files:**
84
- - Modify: `src/core/code-refs.mjs` (MENTION_RE L549; mentionFallback L767)
85
-
86
- - [ ] **Step 3: Code changes**
87
-
88
- Replace:
89
-
90
- ```js
91
- /** Bare `#N` mentions. */
92
- const MENTION_RE = /#(\d{1,7})/g;
93
- ```
94
-
95
- with:
96
-
97
- ```js
98
- /**
99
- * Bare `#N` mentions. The lookbehind rejects a preceding `#` (markdown
100
- * headings, `##N` typos) or word character (`a#1`) — those are not issue
101
- * references (issue #61).
102
- */
103
- const MENTION_RE = /(?<![#\w])#(\d{1,7})/g;
104
- /** Inline code span (single-line) — excluded from mention counting. */
105
- const INLINE_CODE_SPAN_RE = /`[^`\n]*`/g;
106
- /** Explicit PR-context tokens immediately before a `#N` match. */
107
- const PR_CONTEXT_RE = /(?:^|\W)(?:pr|pull|mr|merge)s?(?:\W|$)/i;
108
- ```
109
-
110
- In `mentionFallback`, replace the counting loop:
111
-
112
- ```js
113
- for (let i = start; i < assistantTexts.length; i++) {
114
- const text = assistantTexts[i];
115
- if (typeof text !== "string") continue;
116
- for (const m of text.matchAll(MENTION_RE)) {
117
- const n = Number(m[1]);
118
- counts.set(n, (counts.get(n) ?? 0) + 1);
119
- lastIndex.set(n, baseIndex + i);
120
- }
121
- }
122
- ```
123
-
124
- with:
125
-
126
- ```js
127
- for (let i = start; i < assistantTexts.length; i++) {
128
- const text = assistantTexts[i];
129
- if (typeof text !== "string") continue;
130
- // Doc examples inside inline code spans are not real references; strip
131
- // them before counting (issue #61).
132
- const stripped = text.replace(INLINE_CODE_SPAN_RE, " ");
133
- for (const m of stripped.matchAll(MENTION_RE)) {
134
- // `monitor pr #N` is explicitly PR context: do not count it toward
135
- // the issue fallback (issue #61).
136
- const before = stripped.slice(Math.max(0, m.index - 16), m.index);
137
- if (PR_CONTEXT_RE.test(before)) continue;
138
- const n = Number(m[1]);
139
- counts.set(n, (counts.get(n) ?? 0) + 1);
140
- lastIndex.set(n, baseIndex + i);
141
- }
142
- }
143
- ```
144
-
145
- - [ ] **Step 4: Run test file, verify green**
146
-
147
- Run: `node --test test/code-refs-extract.test.mjs`
148
- Expected: all pass (new + existing).
149
-
150
- - [ ] **Step 5: Full suite (A5)**
151
-
152
- Run: `npm test`
153
- Expected: all green.
154
-
155
- ### Task 3: README providers.json example (G1/A6)
156
-
157
- **Files:**
158
- - Modify: `README.md` (Evidence and Code References section)
159
- - Modify: `test/code-refs-extract.test.mjs` (one JSON-validity test)
160
-
161
- - [ ] **Step 6: Append after the section's first paragraph**
162
-
163
- ```markdown
164
- For internal code platforms (non-github/gitlab hosts) or custom CLIs, add a
165
- per-store `providers.json` so claim/action rules exist and sessions stop
166
- depending on the low-confidence mention fallback. Example — an internal CLI
167
- (`acli`) with claim-strength issue rules:
168
-
169
- ```json
170
- {
171
- "providers": [
172
- {
173
- "name": "acode",
174
- "hosts": ["acode.internal.example.com"],
175
- "rules": [
176
- { "pattern": "acli\\s+issue\\s+update\\s+#?(\\d+)(?=[\\s\\S]*--assignee)", "kind": "issue", "strength": "claim" },
177
- { "pattern": "acli\\s+issue\\s+(?:note|comment|close)\\s+#?(\\d+)", "kind": "issue", "strength": "action" },
178
- { "pattern": "acli\\s+issue\\s+(?:show|view)\\s+#?(\\d+)", "kind": "issue", "strength": "view" }
179
- ]
180
- }
181
- ]
182
- }
183
- ```
184
-
185
- `hosts` matches the repo's remote host; rules follow the same
186
- `pattern`/`kind`/`strength` shape as the built-in `gh`/`glab` tables
187
- (`strength`: `claim` > `action` > `view`), and `#N` in the matched text
188
- captures the number. Validation errors from a broken file are surfaced in the
189
- diagnostics panel and never break extraction — the file is simply ignored.
190
- ```
191
-
192
- - [ ] **Step 7: JSON-validity test**
193
-
194
- ```js
195
- test("README providers.json example parses and validates (issue #61)", async () => {
196
- const readme = readFileSync(new URL("../README.md", import.meta.url), "utf8");
197
- const fence = readme.match(/```json\n(\{[\s\S]*?providers[\s\S]*?\})\n```/);
198
- assert.ok(fence, "README contains a json providers example");
199
- const parsed = JSON.parse(fence[1]);
200
- const { provider, errors } = validateProvider(parsed.providers[0]);
201
- assert.deepEqual(errors, []);
202
- assert.equal(provider.name, "acode");
203
- assert.equal(provider.rules.length, 3);
204
- });
205
- ```
206
-
207
- Add `validateProvider` to the import from `../src/core/code-refs.mjs` and
208
- `readFileSync` to the node:fs import if not already present.
209
-
210
- - [ ] **Step 8: Full suite + scope (A5/A7)**
211
-
212
- Run: `npm test` and `git diff main -- src/ README.md test/ --stat`
213
- Expected: all green; scope = code-refs.mjs, README.md, code-refs-extract.test.mjs.
214
-
215
- - [ ] **Step 9: Commit**
216
-
217
- ```bash
218
- git add src/core/code-refs.mjs README.md test/code-refs-extract.test.mjs
219
- git commit -m "fix: guard mention fallback against placeholders, code spans, and pr-context (issue #61)"
220
- ```
221
-
222
- ## Self-Review
223
-
224
- - Spec coverage: A1/A2/A3 (Task 1 red + Task 2), A4 (composite test), A5 (Step 5), A6 (Task 3), A7 (Step 8). Covered.
225
- - Placeholder scan: full code shown.
226
- - Type consistency: regexes module-level constants; matchAll index usage valid post-strip (indices refer to `stripped`, used only for the window check).
@@ -1,87 +0,0 @@
1
- # Issue #63 Flaky Manual-Completion Assert — 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:** Stop the runner.integration "manual completion" test from racing `markCompleted` against the status.json→state.json persist window (test-only fix).
6
-
7
- **Architecture:** Replace the one-shot `assert.deepEqual(createService({root}).markCompleted("view_1"), {ok:true})` with a `waitFor` poll of `markCompleted` until `{ok:true}`, then assert the exact shape. Durability asserts after runner exit stay unchanged.
8
-
9
- **Tech Stack:** Node built-in test runner (`node --test`), existing `waitFor(fn, timeoutMs = 15000, intervalMs = 50)` helper in the test file.
10
-
11
- ## Global Constraints
12
-
13
- - Test-only: modify `test/runner.integration.test.mjs` and nothing else (spec A4).
14
- - No production code change under `runner/` or `src/` (spec Non-goals).
15
- - Test command: `npm test` (= `node --test test/*.test.mjs`).
16
- - Commit style: conventional commits.
17
-
18
- **Execution mode:** inline (executing-plans) — single task, 3-line diff; the heavy part is the 10-run verification loop which stays in the controlling session.
19
-
20
- ---
21
-
22
- ### Task 1: Poll markCompleted to success in runner.integration test
23
-
24
- **Files:**
25
- - Modify: `test/runner.integration.test.mjs` (~L357-364, test "runner does not clobber a manual completion made during post-exit model passes")
26
-
27
- **Interfaces:**
28
- - Consumes: `waitFor(fn, timeoutMs = 15000, intervalMs = 50)` (defined at top of same file), `createService({ root }).markCompleted(viewId)` → `{ ok: boolean, error?: string }`.
29
- - Produces: nothing consumed by other tasks.
30
-
31
- - [ ] **Step 1: Replace the one-shot assertion with a poll**
32
-
33
- Replace:
34
-
35
- ```js
36
- // User marks the row done manually while the model passes are in flight.
37
- const { createService } = await import("../src/runtime/service.mjs");
38
- assert.deepEqual(createService({ root }).markCompleted("view_1"), { ok: true });
39
- ```
40
-
41
- with:
42
-
43
- ```js
44
- // User marks the row done manually while the model passes are in flight.
45
- // persist() writes status.json before state.json, so markCompleted can
46
- // transiently reject ("active run") after endedAt is already visible.
47
- // Poll it to success, then assert the exact shape; the race window is
48
- // millisecond-scale and 15s default timeout is ample.
49
- const { createService } = await import("../src/runtime/service.mjs");
50
- const svc = createService({ root });
51
- let manual = null;
52
- await waitFor(() => {
53
- manual = svc.markCompleted("view_1");
54
- return manual.ok ? manual : null;
55
- });
56
- assert.deepEqual(manual, { ok: true });
57
- ```
58
-
59
- Note: if `waitFor` times out it returns `null` and leaves `manual` falsy-or-`{ok:false}`; the trailing `assert.deepEqual` then fails — timeout is still a test failure, not a silent pass.
60
-
61
- - [ ] **Step 2: Run the single test file, expect pass**
62
-
63
- Run: `npm test` (full suite; there is no per-file filter in `node --test` glob mode — use `node --test test/runner.integration.test.mjs` for the quick check)
64
- Expected: PASS, no other test affected.
65
-
66
- - [ ] **Step 3: Verify A3 — 10 consecutive full `npm test` runs**
67
-
68
- Run: `for i in $(seq 1 10); do npm test > /tmp/issue63-run-$i.log 2>&1 || { echo "RUN $i FAILED"; break; }; echo "run $i ok"; done`
69
- Expected: `run 1 ok` … `run 10 ok`, zero failures (spec A3).
70
-
71
- - [ ] **Step 4: Verify A4 — only the test file changed**
72
-
73
- Run: `git diff main --stat`
74
- Expected: exactly one file: `test/runner.integration.test.mjs`.
75
-
76
- - [ ] **Step 5: Commit**
77
-
78
- ```bash
79
- git add test/runner.integration.test.mjs
80
- git commit -m "test: poll markCompleted to success instead of racing the persist window (issue #63)"
81
- ```
82
-
83
- ## Self-Review
84
-
85
- - Spec coverage: A1 (Step 1 diff shape), A2 (Step 2 + unchanged durability asserts), A3 (Step 3), A4 (Step 4). Covered.
86
- - Placeholder scan: no TBD/TODO; exact code shown.
87
- - Type consistency: `manual` is `{ok:boolean,error?:string}`; `waitFor` returns truthy-on-success, `null` on timeout. Consistent.