@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,381 +0,0 @@
1
- # Issue #241 — detached state-coordinator never exits on its own
2
-
3
- Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T17:48:00+08:00
4
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T17:59:00+08:00 (spec-cross-review round 1, degraded — findings R1-G1..G3, R1-D1..D8)
5
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T18:04:00+08:00 (spec-cross-review round 2, degraded — converged; R2-D1..D3)
6
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T18:35:00+08:00 (task-2 review I1: the root-missing step-down must not write a diagnostic — `appendLine` recreates the root skeleton; stderr attribution only. A3/A4 updated, §3.2/§6/§7 corrected)
7
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T19:05:00+08:00 (task-3 verification ruling: A7 is a CI acceptance — the dev Windows box has 62 pre-existing environmental failures; local gate = 0 survivors + no new failure. U1 reworded to measure residue, not exit codes; §7 baseline row added)
8
- Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T19:20:00+08:00 (final-review MUST-FIX I1: writes recreate a deleted root, so existence-only detection is insufficient — add the boot-time dev/ino root-identity guard, A3c integration test, correct §7/G1; wrapper trailing-separator false-green gets a code+unit fix in the same wave)
9
-
10
- Status: step-4 root-cause report / spec draft (github-issue-driven). Cross-review:
11
- `research/spec-review-round-1.md` (round 1 not converged → this revision; round 2
12
- reviews this version). Research: `research/root-cause.md`. Issue comments: #241
13
- (6382598306, 6382598816, 6382611575).
14
-
15
- ## 1. Problem statement
16
-
17
- A detached `runner/state-coordinator.mjs` outlives every short-lived caller that
18
- lazily started it (tests, probe child processes, detached runner children,
19
- one-shot CLI/repro scripts) because it only ever exits on SIGTERM/SIGINT, a
20
- server error, or three consecutive heartbeat failures. There is no idle timeout
21
- and no "my root is gone" check; `server.listen()` keeps the event loop alive
22
- forever. Live audit on the reporting box: 26 orphans, all roots under
23
- `%TEMP%\agentview-*`, all parents dead, ~3.75 GB RSS, 68 `agentview-pty-*`
24
- directories.
25
-
26
- On win32 the throwaway root is also **undeletable while the coordinator lives**,
27
- because the coordinator's own **cwd is the root** — verified: `rmSync(root)`
28
- fails with `EPERM` (experiment in research §3). Therefore the issue's option 1
29
- (`statSync(root)` ⇒ ENOENT ⇒ exit) cannot fire as written: the root is still
30
- there. On Linux deleting a process's cwd is allowed, which is why CI stays green.
31
-
32
- Root cause chain (four links):
33
-
34
- 1. `launchCoordinator` spawns `detached, stdio:ignore, cwd: root` — outliving
35
- the caller is by design (a real board's coordinator must survive the
36
- dashboard Pi), so the spawn shape itself is not the bug.
37
- 2. The coordinator has no root-vanished and no idle exit.
38
- 3. Short-lived callers lazily `ensureCoordinator` on throwaway roots and never
39
- learn the spawned pid (`ensureCoordinator` returns it, but only
40
- `coordinator-client.test.mjs` tracks it; probes and runner children do not).
41
- 4. On win32 the coordinator's `cwd: root` keeps the root present, so even the
42
- "root is gone" signal the reporter proposed is shadowed by the process's own
43
- handle — the test's `rmSync` fails, the directory and the process both stay.
44
-
45
- ## 2. Goals / non-goals
46
-
47
- Goals:
48
-
49
- - G1 A coordinator whose root was deleted exits on its own within a bounded,
50
- small window, releasing its lease. Covers test/probe roots and one-shot CLI
51
- roots, not just this repo's suite. A concurrent write that recreates the
52
- directory is detected by the boot-time dev/ino identity guard and steps down on
53
- the next tick (residual: a skeleton directory, never a surviving process).
54
- - G2 Throwaway roots become deletable on win32 while a coordinator serves them
55
- (the cwd handle is released), so test/probe cleanup stops leaving directories.
56
- - G3 The test suite fails loudly if it leaks a coordinator: after `node --test`
57
- finished, any coordinator spawned from this checkout on a temp root is killed
58
- and the run exits nonzero.
59
- - G4 Production behavior is unchanged while the board root exists: no extra
60
- startup cost, no restart, no protocol bump, no data migration.
61
-
62
- Non-goals:
63
-
64
- - Idle timeout ("root still exists but nobody talks to me"). No acceptance
65
- criterion in the issue requires it; it needs a conservative knob and could
66
- thrash a live board. Follow-up candidate.
67
- - Changing the detached spawn design (the coordinator must outlive the
68
- dashboard Pi).
69
- - Cleaning the 330 empty `agentview-cclient-*` directories (fake-server roots
70
- that are simply never removed in `coordinator-client.test.mjs`; no live
71
- process, separate hygiene item).
72
- - Auto-cleaning orphans that predate this change (they have no self-exit code).
73
- The wrapper only snapshots-and-warns about pre-existing ones.
74
-
75
- ## 3. Design
76
-
77
- ### 3.1 Release the root handle (launchCoordinator)
78
-
79
- `src/core/launch.mjs` (add `import { dirname, resolve } from "node:path";` — the
80
- file currently imports only `spawn`, `atomicWriteJson`, `resolveNode`, `paths.mjs`,
81
- `writePid`, see `launch.mjs:8-12`):
82
-
83
- ```js
84
- export function coordinatorSpawnPlan(root) {
85
- const absoluteRoot = resolve(root);
86
- return { root: absoluteRoot, cwd: dirname(absoluteRoot) };
87
- }
88
-
89
- export function launchCoordinator(root, opts) {
90
- const node = opts.node ?? resolveNode();
91
- const plan = coordinatorSpawnPlan(root);
92
- const child = spawnDetached(node, [opts.runnerScript, plan.root], plan.cwd);
93
- return { pid: child.pid ?? null };
94
- }
95
- ```
96
-
97
- - The coordinator derives every path from the absolute root argument and never
98
- touches `process.cwd()` (verified line by line: `runner/state-coordinator.mjs`
99
- uses `root = process.argv[2]` at `:91` plus `src/core/paths.mjs`; no `chdir`).
100
- cwd is purely a process-level side effect, so there is no behavior change.
101
- - Passing `resolve(root)` keeps relative-root callers working and makes the argv
102
- canonical. Pipe-name isolation is unaffected: `coordinatorEndpointPathFor`
103
- already hashes `path.win32.resolve(root)` (`src/core/paths.mjs:120-139`,
104
- issue #124), so every spelling maps to the same endpoint.
105
- - `dirname(root)` exists whenever the root's parent exists; for `C:\`/`/` the
106
- parent is the root itself. The parent of a board root (`~/.pi/agent`) and the
107
- parent of a temp root (`os.tmpdir()`) are stable, so the process still has a
108
- valid cwd and no longer pins a throwaway root.
109
-
110
- ### 3.2 Root-vanished self-exit (state-coordinator)
111
-
112
- `runner/state-coordinator.mjs`, inside the existing supervised heartbeat
113
- interval (started in the `server.listen` callback, `:237-272`, default 1000 ms,
114
- `AGENT_BOARD_TEST_COORDINATOR_HEARTBEAT_MS` compressible per
115
- `src/core/test-knobs.mjs` and read at `main()` start, `:123`):
116
-
117
- ```js
118
- heartbeatTimer = setInterval(() => {
119
- if (rootVanished()) {
120
- // No diagnostic: appendLine ensureDirs its parent and would recreate
121
- // <root>/views/_coordinator, leaving the skeleton dir U1 measures.
122
- process.stderr.write(`state-coordinator: board root missing (${root}); stepping down\n`);
123
- shutdown(0);
124
- return;
125
- }
126
- // ... existing touch/failure logic unchanged
127
- }, heartbeatMs);
128
- ```
129
-
130
- `rootVanished()` is a module-scope helper using the already-imported `statSync`
131
- (`:39`):
132
-
133
- ```js
134
- function rootVanished() {
135
- try { statSync(root); return false; }
136
- catch (err) { return err?.code === "ENOENT"; }
137
- }
138
- ```
139
-
140
- **Root-replacement guard (final-review I1).** An existence-only check is not
141
- enough: a write that lands after the delete (`appendCommand` / `writeState` →
142
- `ensureDir`) *succeeds by recreating* the directory, so the next tick sees
143
- "present" and the coordinator serves a dead board — or, in the boot-window race
144
- (guard passes, then the root is deleted before lease acquisition `ensureDir`),
145
- it acquires a lease on the recreated root and serves forever. Capture the
146
- directory identity once at boot and step down when it changes:
147
-
148
- ```js
149
- const rootIdentity = captureRootIdentity(); // {dev, ino} | null (null when the FS reports ino 0)
150
- function rootVanishedOrReplaced() {
151
- try {
152
- const st = statSync(root);
153
- if (!rootIdentity) return null;
154
- return st.dev !== rootIdentity.dev || st.ino !== rootIdentity.ino ? "replaced" : null;
155
- } catch (err) {
156
- return err?.code === "ENOENT" ? "missing" : null;
157
- }
158
- }
159
- ```
160
-
161
- The boot guard captures the identity before lease acquisition and compares the
162
- same way. Step-down stderr: `state-coordinator: board root missing (<root>);
163
- stepping down` or `state-coordinator: board root replaced (<root>); stepping
164
- down`. When `ino` is unavailable/0 the check degrades to existence-only (never a
165
- false step-down). Residual accepted: a write in the delete window may leave a
166
- skeleton directory (data, not a process); the guard bounds its lifetime to one
167
- tick.
168
-
169
- - `ENOENT` ⇒ step down. Any other error (EACCES/EBUSY) or success ⇒ keep
170
- serving — a transient access failure must not kill a healthy coordinator.
171
- - **No diagnostic is written on the root-missing path.** Every append helper
172
- `ensureDir`s its parent (`src/core/atomic.mjs:107-109`), so writing
173
- `<root>/views/_coordinator/diagnostics.jsonl` would *succeed* by recreating the
174
- deleted directory chain — reintroducing the skeleton-dir residue U1 measures.
175
- The step-down is logged to stderr instead
176
- (`state-coordinator: board root missing (<root>); stepping down`): detached
177
- callers ignore stdio, while a test can spawn the runner with a stderr pipe and
178
- attribute the exit deterministically. The boot-time guard (`rootVanished()`
179
- before lease acquisition — lease acquisition also `ensureDir`s and would
180
- recreate the root, so "fails lease acquisition" is not a viable boot path)
181
- logs the same stderr line and writes nothing either.
182
- - `shutdown(0)` already clears the interval, closes the server, best-effort
183
- unlinks the POSIX socket, releases the lease, and exits — all cleanup is
184
- try/catch guarded (`:278-293`). `shutdown` is idempotent via its `shuttingDown`
185
- flag, and `lease.release()` no-ops when `owner.json` is gone (token-fenced).
186
- - A command in flight at the instant the root disappears fails its journal/write
187
- and the client observes the documented ambiguous outcome
188
- (`connection_reset`/`timeout`); it must not retry with a fresh commandId
189
- (`coordinator-client.mjs:11-25`). This is an accepted edge, see §7.
190
-
191
- Bounds: detection latency ≤ one heartbeat tick (1 s production) after the root
192
- disappears. No new timer, no new startup cost. The test knob also shortens the
193
- lease touch interval, but the stale-lease threshold stays
194
- `max(60s, 3 × heartbeatMs)` = 60 s (existing #224 behavior in
195
- `src/core/locks.mjs`), so A3/A4 never depend on the knob changing stale
196
- semantics.
197
-
198
- ### 3.3 Suite gate (new `scripts/run-tests.mjs`)
199
-
200
- `package.json` (`:56-57`):
201
-
202
- ```json
203
- "test": "node scripts/run-tests.mjs",
204
- "test:coverage": "c8 node scripts/run-tests.mjs",
205
- ```
206
-
207
- The wrapper (Node, no shell glob, no new dependency):
208
-
209
- 1. Enumerate test files: `readdirSync("test")` filtered to `*.test.mjs`, sorted,
210
- passed to `node --test <files>`; child stdio inherited.
211
- 2. **Before** the run, enumerate coordinator processes and keep their pids as a
212
- *snapshot* (pre-existing orphans must not fail this run; print a one-line
213
- warning with the count instead).
214
- 3. Spawn `process.execPath --test <files...>`, wait for exit. `exitCode === null`
215
- (killed by a signal) is treated as failure and the signal is printed — it is
216
- never a success passthrough.
217
- 4. **After** the run, poll every 100 ms up to 10 s for every coordinator above
218
- the snapshot to be gone (the 1 s self-exit window plus load slack).
219
- 5. Any survivor: print pid + root per process, `SIGTERM` (on win32 that is
220
- TerminateProcess), wait 2 s, `SIGKILL` leftovers, and exit 1. Otherwise exit
221
- with the test run's own exit code (test failures keep priority; residue is
222
- also reported if both happen).
223
-
224
- **Import safety (required):** the module must be side-effect free on import —
225
- `main()` runs only when the file is the entry point, e.g.
226
- `if (import.meta.url === pathToFileURL(resolve(process.argv[1])).href) main();`
227
- otherwise the wrapper's own unit tests (A5/A6) would recursively run the suite.
228
-
229
- Process enumeration, split for testability:
230
-
231
- ```js
232
- export function parseProcessList(raw, platform) // pure
233
- export function selectOrphanCoordinators({ processes, repoRoot, tmpRoot, platform }) // pure
234
- export async function runSuite({ spawn, listProcesses, kill, sleep, now, ... }) // injectable shell
235
- export function main() // real shell, guarded entry
236
- ```
237
-
238
- - win32 enumeration: `pwsh`/`powershell` `-NoProfile -NonInteractive -Command`
239
- with `[Console]::OutputEncoding = [Text.Encoding]::UTF8; $OutputEncoding = ...;`
240
- prefix and `Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
241
- Select-Object ProcessId,CommandLine | ConvertTo-Json -Compress`. The JSON may
242
- be a single object when only one process matches — `parseProcessList` must
243
- accept both object and array.
244
- - POSIX enumeration: `ps -e -ww -o pid=,args=` (`-ww` = unlimited width; without
245
- it macOS/Linux may truncate the command line before the root argument and the
246
- process silently escapes selection).
247
- - Selection predicate: command line contains this checkout's
248
- `runner/state-coordinator.mjs` absolute path (case-insensitive on win32,
249
- separator-normalized) **and** its root argument resolves under `os.tmpdir()`.
250
- Production roots (`~/.pi/agent/agent-board`, `$AGENT_BOARD_ROOT`) never match,
251
- so a live board coordinator is never touched; another checkout's test
252
- coordinator never matches because the script path differs.
253
- - Root extraction from the command line: the token after
254
- `state-coordinator.mjs`, quotes stripped (spawned argv is not quoted, CIM may
255
- quote paths with spaces).
256
- - Enumeration unavailable (command missing/permission error): print a warning
257
- and skip the residue check (fail-open). CI (`ps`, ubuntu) and the reporting
258
- Windows box (`pwsh`) both support it; this avoids turning an enumeration
259
- environment issue into a red suite.
260
-
261
- `.c8rc.json` (`:4-9`) gains `"scripts/run-tests.mjs"` in `exclude` (CLI shell +
262
- process enumeration; the pure parts are unit-tested but the wrapper itself is a
263
- dev runner). Coverage collection is unaffected: c8 exports `NODE_V8_COVERAGE` to
264
- the whole child tree.
265
-
266
- ### 3.4 Ordering constraint
267
-
268
- The wrapper gate (3.3) can only land green **after** 3.1 + 3.2 make the existing
269
- suite stop leaking. Implementation order: 3.1 → 3.2 → integration tests → wire
270
- package.json + wrapper → run the full suite. If the gate reports survivors that
271
- come from a path not fixed by 3.1/3.2, that is a real finding to investigate
272
- (possibly an additional lazy-spawn path), not something to silence.
273
-
274
- ## 4. Component contracts
275
-
276
- | Symbol | Module | Contract |
277
- |---|---|---|
278
- | `coordinatorSpawnPlan(root)` | `src/core/launch.mjs` | `{ root: string (absolute), cwd: string }`; `cwd === dirname(resolve(root))`; idempotent for absolute roots. |
279
- | `parseProcessList(raw, platform)` | `scripts/run-tests.mjs` | `Array<{pid:number, commandLine:string}>`; `win32` accepts CIM JSON array **or** single object, `linux`/`darwin` parse `ps` lines; unparsable lines skipped, never throws. |
280
- | `selectOrphanCoordinators({processes, repoRoot, tmpRoot, platform})` | `scripts/run-tests.mjs` | `Array<{pid:number, root:string}>`; only this checkout's `runner/state-coordinator.mjs`; root under `tmpRoot`; deterministic order by pid. |
281
- | `runSuite(deps)` | `scripts/run-tests.mjs` | Injectable `spawn/listProcesses/kill/sleep`; returns `{exitCode, survivors}`; no `process.exit` inside, no `main()` side effect on import. |
282
- | root-vanished check | `runner/state-coordinator.mjs` | `ENOENT` on `statSync(root)` ⇒ step down; other errors/OK ⇒ keep serving. |
283
-
284
- ## 5. Acceptance matrix
285
-
286
- Verification commands assume repo root. `WT` = worktree absolute path (step 5).
287
-
288
- | ID | Functional point | Verification | Concrete check | Pass criterion |
289
- |---|---|---|---|---|
290
- | A1 | spawn plan: absolute argv + parent-dir cwd | Automated (unit) — `test/coordinator-lifecycle.test.mjs` | `coordinatorSpawnPlan` on absolute, trailing-slash, dot-segment roots | `root` absolute, `cwd === dirname(resolve(root))`, no throw |
291
- | A2 | `launchCoordinator` actually spawns with that plan | Automated (integration, cross-platform) — same file | spawn a temp fake runner via `launchCoordinator(root, {runnerScript: fake})`; fake writes `{cwd: process.cwd(), arg: process.argv[2]}` under root; poll for the file | `arg === root` (absolute); both sides normalized with `path.resolve` and compared case-insensitively on win32 ⇒ `cwd === dirname(root)`; fails if cwd reverts to `root` |
292
- | A3 | root-vanished self-exit releases the process; root becomes and stays deletable | Automated (integration) — same file | set `AGENT_BOARD_TEST_COORDINATOR_HEARTBEAT_MS=100` before spawn (restore in `finally`); spawn the real runner directly with the `coordinatorSpawnPlan` argv/cwd and `stdio: ["ignore", "pipe", "pipe"]`; wait until the endpoint answers; assert lease `owner.json` exists at `<root>/views/_coordinator/state-coordinator.lock/owner.json` (`P.viewLockPath`, `paths.mjs:171`) and pid alive; `rmSync(root, {maxRetries: 5, retryDelay: 50})`; wait for exit | rmSync does not throw; root gone and stays absent after exit; exit code 0; stderr matches `board root missing`; "exists before gone" asserted first (win32 rmSync success is the cwd evidence, stderr the self-exit attribution) |
293
- | A3c | root recreated in the delete window does not defeat step-down | Automated (integration) — same file | start the real coordinator (direct spawn, stderr piped, 100 ms knob); wait ready; `rmSync(root)`; immediately send one valid `state_command` that is rejected but journaled (e.g. `patch_fields` on an unknown view) so the reply path recreates the root skeleton; await exit | exit code 0 within 2 s; stderr matches `/board root (missing\|replaced)/`; the directory may remain as a skeleton (data residue accepted) but no process survives |
294
- | A4 | root present ⇒ no step-down (no false positive) | Automated (integration) — same file | same knob; start via the same direct spawn (stderr piped), wait ≥ 3 ticks (≈ 500 ms), then `rawPing` the endpoint and assert pid alive | still alive and answering ping; stderr contains no `board root missing` line (the root-missing path is observable only on stderr — it writes no diagnostic by design) |
295
- | A5 | wrapper selection purity | Automated (unit) — `test/run-tests-wrapper.test.mjs` | win32 CIM array **and** single-object JSON samples + POSIX `ps` sample incl. a long (>200 char) command line; this-checkout vs other-checkout script path; temp vs production root; slash/case variants | exact pid sets, no throws, long line not truncated |
296
- | A6 | wrapper orchestration + import safety | Automated (unit, injected fakes) — same file | survivors present → kill called for each, exit 1; clean → no kill, test exit code passthrough; snapshot survivors warn-only; `exitCode === null` → failure; `import` of the module performs no I/O and starts nothing | behavior as specified; importing twice is safe |
297
- | A7 | suite is wired through the wrapper | Automated (build/suite) — CI `npm test` (ubuntu-latest) + local Windows baseline diff | in CI: `npm test` uses the wrapper and exits 0, printing `run-tests: no leaked coordinators`; `package.json` points at it. On the dev Windows box the full suite has a known pre-existing failure baseline (62 failures, separated by environment: AF_UNIX/EACCES `.sock`, AttachConsole, separator expectations — all reproduce with plain `node --test` on `main`); there the requirement is "failure set ⊆ baseline and 0 survivors", not exit 0 | CI: exit 0, no survivors; Windows: no new failures vs `main`, 0 survivors |
298
- | A8 | coverage path still works with the wrapper | Automated (build) — `npm run test:coverage` (part of `npm run verify`) | c8 + wrapper + `.c8rc.json` exclude | coverage collected, thresholds lines 85 / funcs 80 / branches 70 hold |
299
- | U1 | Windows box stops accumulating | User measurement — after merge, local Windows, before/after `npm test` twice (one-time manual cleanup of pre-existing orphans first) | count coordinators (`Get-CimInstance ... state-coordinator`) and `%TEMP%\agentview-pty-*` dirs before/after each run; also run `npm run typecheck`/the wrapper subset. The Windows-local suite is NOT expected to be green (pre-existing baseline above); U1 is about residue, not exit codes | 0 new coordinators, 0 new `agentview-pty-*` (or `agentview-del-flow-*`) dirs per run |
300
-
301
- Automated-first: A1/A5/A6 are `unit`, A2/A3/A4 are `integration` (a real spawn
302
- is the point; A2 is deliberately the cross-platform one so the cwd fix has a
303
- CI-observable regression lock, since Linux cannot detect it via `rmSync`), A7/A8
304
- are the repo's own commands. U1 is the only user measurement: it observes
305
- process/OS state across runs on the reporter's box and is the direct
306
- reproduction of the report.
307
-
308
- ## 6. Testability split (design constraint for implementation)
309
-
310
- - `coordinatorSpawnPlan` is a pure function in `launch.mjs`; `launchCoordinator`
311
- is its thin spawn shell. A1 tests the pure part; A2 proves the shell's actual
312
- cwd/argv end to end on every platform (the fake runner writes its own
313
- `process.cwd()` and `argv[2]`); A3 proves the end-to-end consequence.
314
- - The root-vanished decision is intentionally *not* extracted into a new module:
315
- it is a one-line predicate whose correctness is the timing/step-down sequence,
316
- so A3 (exit on delete) and A4 (no exit while present) are integration tests
317
- over the real runner. A pure predicate unit test would be tautological.
318
- Because the step-down must not write inside the root (see §3.2), its
319
- attribution is the **stderr line**: A3/A4 spawn the runner directly with
320
- `stdio: [..., "pipe", "pipe"]` instead of going through the detached
321
- `launchCoordinator` (whose stdio is ignored); A1/A2 already lock the
322
- `launchCoordinator` spawn plan, so no production-path coverage is lost.
323
- - `scripts/run-tests.mjs` keeps two pure functions (`parseProcessList`,
324
- `selectOrphanCoordinators`) and one injectable orchestrator (`runSuite`) apart
325
- from `main()`'s real `spawn`/`execFileSync`/`process.kill`/timer shell, and is
326
- import-side-effect free by contract. A5 covers the pure functions; A6 covers
327
- `runSuite` with fakes (no real process is killed in unit tests) plus the
328
- import-safety contract.
329
- - No test may assert "clean" without first asserting the precondition (lease
330
- file exists / pid alive before the delete; a survivor exists before the kill
331
- path; the fake runner's file appears before reading it).
332
- - A2's fake runner lives under the temp root, is tiny, and never binds a socket;
333
- the test asserts the written file appears (bounded poll), then the values.
334
-
335
- ## 7. Risks / edge cases
336
-
337
- | Risk | Handling |
338
- |---|---|
339
- | Root-missing step-down | writes no diagnostic and no state: `appendLine` recreates the parent chain, so a write would resurrect the deleted root skeleton; the step-down logs one stderr line and `shutdown(0)` stays unblocked. |
340
- | `statSync` EACCES/EBUSY (transient) | treat as "root present", keep serving; only ENOENT steps down. |
341
- | Root removed while a command is being journaled | the write may **succeed by recreating the root** (`ensureDir`), so existence-only detection is insufficient; the boot-time dev/ino identity guard detects the replacement on the next tick and steps down, leaving at most a skeleton directory. The client still sees the ambiguous `connection_reset`/`timeout` and must not retry with a fresh commandId (existing contract). |
342
- | Wrapper kills another checkout's test coordinator | impossible: selection requires this checkout's absolute `runner/state-coordinator.mjs` path. |
343
- | Wrapper runs while a real board coordinator serves a temp root (user set `AGENT_BOARD_ROOT` to a temp dir) | temp predicate would match; acceptable known limit — such a setup is itself a throwaway. Documented, not guarded. |
344
- | Enumeration unavailable | warn + fail-open (skip residue check); A6 covers the failed-enumeration branch. |
345
- | POSIX command line truncation | `ps -e -ww`; A5 covers a long line. |
346
- | win32 CIM single-object JSON / non-UTF8 output | object-or-array parse; forced UTF-8 in the command prefix; A5 covers both shapes. |
347
- | `pwsh` launch cost on every test run (~0.5–1 s) | acceptable for a dev/suite gate; POSIX path uses `ps`. |
348
- | Windows-local full suite has pre-existing failures (AF_UNIX/EACCES `.sock`, AttachConsole, separator expectations) | A7 is a CI acceptance; locally the gate must show 0 survivors and no NEW failure vs `main`. Never loosen the gate (fail-open or skip) to make the local suite green. |
349
- | Gate exposes a test that never deletes its root | after 3.1 the root is removable, so fix that test's cleanup in the same PR; no exemption list. |
350
- | Existing orphans predating the fix | not auto-cleaned; wrapper warns with their count on every run; U1 includes one manual cleanup command. |
351
- | npm test on Node 20 | wrapper uses only `readdirSync`/`spawn`/`process.kill`; no `--test-global-setup` (Node 22+ only), no new deps. |
352
- | Coverage thresholds after adding the wrapper | `.c8rc.json` excludes the wrapper; A8 proves thresholds still hold. |
353
-
354
- ## 8. Deployment / execution path
355
-
356
- - **No protocol version bump.** Every newly spawned coordinator runs the new
357
- `runner/state-coordinator.mjs` from the installed package — the spawn path is
358
- `opts.runnerScript` (`coordinator-client.mjs:197`), a file-path constant, not a
359
- protocol-negotiated one. Test/probe roots are freshly created per run, so they
360
- never collide with a pre-existing old-build instance. On a production root an
361
- already-running old-build coordinator keeps serving until it is next replaced
362
- or the process restarts; that is correct, because its root still exists and
363
- the new behavior is specifically "step down when the root is gone". (A
364
- protocol bump would force-reelect every user's coordinator on upgrade for no
365
- gain — explicitly out of scope.)
366
- - No data migration. Lease/journal formats untouched.
367
- - Already-leaked processes from older builds stay until manually killed; U1
368
- includes the one-time cleanup (`Get-CimInstance ... state-coordinator` filtered
369
- to `%TEMP%\agentview-*`, plus the temp dirs). The new suite gate prevents new
370
- ones.
371
- - `npm run verify` (typecheck + perf gate + tests + coverage + pack:dry) stays
372
- the completion gate; `npm test` now also covers the residue assertion.
373
-
374
- ## 9. Open questions for the design gate
375
-
376
- 1. Approve 3.1 + 3.2 + 3.3 (+ tests), i.e. the issue's preferred "1 + 3" with
377
- the cwd correction, with idle timeout kept out of scope?
378
- 2. Is the 1 s root check / <=1 s detection latency acceptable in production, or
379
- should the cadence be a separate (slower) knob?
380
- 3. Is the fail-open behavior when process enumeration is unavailable acceptable
381
- (warn + skip), or should it fail the run?