@huseynsnmz/pi-board 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +26 -4
  2. package/CONTRIBUTING.md +7 -0
  3. package/README.md +53 -419
  4. package/SECURITY.md +19 -0
  5. package/docs/how-it-works.md +31 -0
  6. package/docs/reference.md +276 -0
  7. package/package.json +18 -19
  8. package/src/index.ts +9 -1
  9. package/src/runtime/service.mjs +12 -0
  10. package/src/ui/dashboard.ts +14 -8
  11. package/src/ui/double-left.ts +5 -1
  12. package/src/ui/focus.ts +7 -1
  13. package/src/ui/waiting.ts +10 -5
  14. package/IMPLEMENTATION_PLAN.md +0 -920
  15. package/PRD.md +0 -484
  16. package/PROGRESS.md +0 -142
  17. package/VERIFY.md +0 -115
  18. package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
  19. package/docs/EXPLORATION.md +0 -187
  20. package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
  21. package/docs/RELEASE.md +0 -148
  22. package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
  23. package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
  24. package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
  25. package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
  26. package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
  27. package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
  28. package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
  29. package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
  30. package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
  31. package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
  32. package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
  33. package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
  34. package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
  35. package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
  36. package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
  37. package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
  38. package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
  39. package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
  40. package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
  41. package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
  42. package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
  43. package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
  44. package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
  45. package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
  46. package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
  47. package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
  48. package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
  49. package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
  50. package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
  51. package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
  52. package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
  53. package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
  54. package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
  55. package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
  56. package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
  57. package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
  58. package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
  59. package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
  60. package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
  61. package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
  62. package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
  63. package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
  64. package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
  65. package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
  66. package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
  67. package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
  68. package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
  69. package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
  70. package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
  71. package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
  72. package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
  73. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
  74. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
  75. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
  76. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
  77. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
  78. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
  79. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
  80. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
  81. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
  82. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
  83. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
  84. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
  85. package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
  86. package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
  87. package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
  88. package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
  89. package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
  90. package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
  91. package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
  92. package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
  93. package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
  94. package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
  95. package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
  96. package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
  97. package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
  98. package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
  99. package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
  100. package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
  101. package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
  102. package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
  103. package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
  104. package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
  105. package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
  106. package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
  107. package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
  108. package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
  109. package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
  110. package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
  111. package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
  112. package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
  113. package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
  114. package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
  115. package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
  116. package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
  117. package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
  118. package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
  119. package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
  120. package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
  121. package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
  122. package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
  123. package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
  124. package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
  125. package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
  126. package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
  127. package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
  128. package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
  129. package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
  130. package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
  131. package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
  132. package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
  133. package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
  134. package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
  135. package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
  136. package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
  137. package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
  138. package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
  139. package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
  140. package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
  141. package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
  142. package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
  143. package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
  144. package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
  145. package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
  146. package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
  147. package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
  148. package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
  149. package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
  150. package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
  151. package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
  152. package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
  153. package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
  154. package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
  155. package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
  156. package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
  157. package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
  158. package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
  159. package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
  160. package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
  161. package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
  162. package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
  163. package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
  164. package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
  165. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
  166. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
  167. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
  168. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
  169. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
  170. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
  171. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
  172. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
  173. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
  174. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
  175. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
  176. package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
  177. package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
  178. package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
  179. package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
  180. package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
  181. package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
  182. package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
  183. package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
  184. package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
  185. package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
  186. package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
  187. package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
  188. package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
  189. package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
  190. package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
  191. package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
  192. package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
  193. package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
  194. package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
  195. package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
  196. package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
  197. package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
  198. package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
  199. package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
  200. package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
  201. package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
  202. package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
  203. package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
  204. package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
  205. package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
  206. package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
  207. package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
  208. package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
  209. package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
  210. package/scripts/budget-audit.mjs +0 -135
  211. package/scripts/patch-vulns.mjs +0 -59
  212. package/scripts/release.mjs +0 -446
  213. package/scripts/release_helper.mjs +0 -309
  214. package/scripts/run-perf-gate.mjs +0 -40
  215. package/scripts/run-tests.mjs +0 -375
@@ -1,341 +0,0 @@
1
- # Windows Lease Publish EPERM Reclaim 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:** 让 `attemptAcquireLease` 在 Windows 的 publish-rename EPERM(目标目录已存在)下进入 reclaim 判定,使 coordinator / host 的租约孤锁可被自动回收(issue #114)。
6
-
7
- **Architecture:** 把 rename 冲突码判定抽成导出的纯函数 `isPublishConflictCode(code)`(`EEXIST`/`ENOTEMPTY`/`EPERM`),`attemptAcquireLease` 的 catch 改用它;回收安全性完全由既有 `classifyLeaseOwner` 决定,本次不放宽任何回收条件。测试分三层:纯函数 unit → 注入 fs 模拟 Windows EPERM 的接管 unit/integration → spawn 真 coordinator 的端到端回归。
8
-
9
- **Tech Stack:** Node 24 ESM、`node:test`、零新依赖。
10
-
11
- **Spec:** `docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md`
12
-
13
- ## Global Constraints
14
-
15
- - 不改 `classifyLeaseOwner` 的判定契约、不改 quarantine / inspectedToken 核对机制、不改 `ORPHAN_LEASE_AGE_MS`(5min)。
16
- - 不新增依赖;不新增生产文件。
17
- - 生产改动仅限 `src/core/locks.mjs`。
18
- - 所有测试命令在 worktree 根目录执行(`node --test test/<file>.test.mjs`)。
19
- - Windows 真机既有失败基线(修复前):`node --test test/locks.test.mjs` = 20 tests / **4 fail**(全部为租约接管路径),修复后必须 0 fail。
20
- - commit 用 conventional commits;每个 task 独立 commit。
21
-
22
- ---
23
-
24
- ### Task 1: `isPublishConflictCode` 纯函数 + catch 接线
25
-
26
- **Files:**
27
- - Modify: `src/core/locks.mjs`(新增导出函数;改造 `attemptAcquireLease` catch)
28
- - Test: `test/locks.test.mjs`
29
-
30
- **Interfaces:**
31
- - Produces: `isPublishConflictCode(code: string|undefined): boolean`(导出)——`EEXIST`/`ENOTEMPTY`/`EPERM` → true,其余 → false。
32
- - Consumes: 既有 `attemptAcquireLease` / `reclaimOrBlock`(不动其签名)。
33
-
34
- - [ ] **Step 1: Write the failing test**
35
-
36
- 在 `test/locks.test.mjs` 的 import 块(第 6 行)把 `isPublishConflictCode` 加入解构列表:
37
-
38
- ```js
39
- import { acquireOwnedViewLock, classifyLeaseOwner, defaultLocksFs, isPublishConflictCode, releaseWithToken, tryAcquireOwnedViewLock, withFileLockSync, withViewLockSync } from "../src/core/locks.mjs";
40
- ```
41
-
42
- 在文件末尾追加测试:
43
-
44
- ```js
45
- // ---- publish-rename conflict codes (issue #114) -----------------------------
46
-
47
- test("isPublishConflictCode: rename conflict codes including Windows EPERM", () => {
48
- assert.equal(isPublishConflictCode("EEXIST"), true);
49
- assert.equal(isPublishConflictCode("ENOTEMPTY"), true, "POSIX rename onto an existing dir");
50
- assert.equal(isPublishConflictCode("EPERM"), true, "Windows rename onto an existing dir (errno -4048)");
51
- assert.equal(isPublishConflictCode("ENOENT"), false);
52
- assert.equal(isPublishConflictCode("EACCES"), false);
53
- assert.equal(isPublishConflictCode(undefined), false);
54
- assert.equal(isPublishConflictCode(null), false);
55
- });
56
- ```
57
-
58
- - [ ] **Step 2: Run test to verify it fails**
59
-
60
- Run: `node --test test/locks.test.mjs`
61
- Expected: FAIL —— `isPublishConflictCode is not a function`(或 import 报错),其余用例不受影响。
62
-
63
- - [ ] **Step 3: Write minimal implementation**
64
-
65
- 在 `src/core/locks.mjs` 的 `attemptAcquireLease` **之前**插入:
66
-
67
- ```js
68
- /**
69
- * Whether a publish-rename failure means "the lock path already exists"
70
- * (contention) rather than a genuine filesystem error.
71
- *
72
- * POSIX reports EEXIST/ENOTEMPTY when renaming a directory onto an existing
73
- * one; Windows reports EPERM (errno -4048) for the same situation — this op's
74
- * platform equivalent of EEXIST (issue #114: a crashed owner's lease could
75
- * never be reclaimed because EPERM was rethrown before reclaimOrBlock).
76
- * Routing a genuine permission error here stays safe: reclaimability is still
77
- * decided solely by `classifyLeaseOwner`, so it resolves `blocked`/`busy`.
78
- * @param {string|undefined|null} code
79
- * @returns {boolean}
80
- */
81
- export function isPublishConflictCode(code) {
82
- return code === "EEXIST" || code === "ENOTEMPTY" || code === "EPERM";
83
- }
84
- ```
85
-
86
- 把 `attemptAcquireLease` catch 中的:
87
-
88
- ```js
89
- const code = err && err.code;
90
- if (code !== "EEXIST" && code !== "ENOTEMPTY") throw err;
91
- ```
92
-
93
- 替换为:
94
-
95
- ```js
96
- const code = /** @type {NodeJS.ErrnoException|undefined} */ (err)?.code;
97
- if (!isPublishConflictCode(code)) throw err;
98
- ```
99
-
100
- - [ ] **Step 4: Run test to verify it passes**
101
-
102
- Run: `node --test test/locks.test.mjs`
103
- Expected: 新用例 PASS;其余用例与修复前一致(Windows 真机此时 4 个接管用例仍 fail —— 它们在 Task 2 的注入用例之外,属既有基线,Task 4 复验)。
104
-
105
- - [ ] **Step 5: Commit**
106
-
107
- ```bash
108
- git add src/core/locks.mjs test/locks.test.mjs
109
- git commit -m "fix(locks): treat Windows publish-rename EPERM as lease contention (issue #114)"
110
- ```
111
-
112
- ---
113
-
114
- ### Task 2: 注入 fs 的 EPERM 接管测试(reclaim / busy / blocked)
115
-
116
- **Files:**
117
- - Test: `test/locks.test.mjs`
118
-
119
- **Interfaces:**
120
- - Consumes: `isPublishConflictCode` 接线后的 `tryAcquireOwnedViewLock`(Task 1)。
121
- - Produces: 无生产接口;测试辅助 `windowsPublishFs(lockPath)`。
122
-
123
- - [ ] **Step 1: Write the failing tests**
124
-
125
- 在 `test/locks.test.mjs` 顶部 import 增加 `renameSync as realRenameSync`(保留既有名字):
126
-
127
- ```js
128
- import { existsSync, mkdirSync, mkdtempSync, renameSync as realRenameSync, rmSync, writeFileSync } from "node:fs";
129
- ```
130
-
131
- 在文件末尾追加:
132
-
133
- ```js
134
- /** Windows-style publish fs: renaming onto an existing lock path throws EPERM
135
- * (errno -4048) instead of POSIX's ENOTEMPTY; all other ops are real. */
136
- function windowsPublishFs(lockPath) {
137
- return {
138
- ...LOCK_FS,
139
- renameSync: (from, to, ...rest) => {
140
- if (String(to) === lockPath && existsSync(to)) {
141
- const e = new Error("EPERM: operation not permitted, rename");
142
- e.code = "EPERM";
143
- throw e;
144
- }
145
- return realRenameSync(from, to, ...rest);
146
- },
147
- };
148
- }
149
-
150
- const ME = { pid: process.pid, startToken: "me" };
151
- const DEAD = { pid: 99999999, startToken: "dead" };
152
-
153
- test("EPERM publish contention: dead owner with full identity is reclaimed", () => {
154
- const root = freshRoot();
155
- try {
156
- const lockPath = P.viewLockPath(root, "v1", "coordinator");
157
- mkdirSync(lockPath, { recursive: true });
158
- writeFileSync(join(lockPath, "owner.json"), JSON.stringify({ token: "t", pid: DEAD.pid, identity: DEAD, startedAt: Date.now() }));
159
- const got = tryAcquireOwnedViewLock(root, "v1", "coordinator", { identity: ME, fs: windowsPublishFs(lockPath) });
160
- assert.equal(got.acquired, true, "EPERM contention must reach reclaimOrBlock");
161
- assert.equal(got.lease.isOwner(), true);
162
- got.lease.release();
163
- } finally {
164
- rmSync(root, { recursive: true, force: true });
165
- }
166
- });
167
-
168
- test("EPERM publish contention: stale identity-less owner (Windows startToken:null) is reclaimed", () => {
169
- const root = freshRoot();
170
- try {
171
- const lockPath = P.viewLockPath(root, "v1", "coordinator");
172
- mkdirSync(lockPath, { recursive: true });
173
- writeFileSync(join(lockPath, "owner.json"), JSON.stringify({
174
- token: "t", pid: DEAD.pid,
175
- identity: { pid: DEAD.pid, startToken: null },
176
- startedAt: Date.now() - 10 * 60_000,
177
- }));
178
- const got = tryAcquireOwnedViewLock(root, "v1", "coordinator", { identity: ME, fs: windowsPublishFs(lockPath) });
179
- assert.equal(got.acquired, true, "issue #112 orphan reclaim must be reachable through EPERM contention");
180
- got.lease.release();
181
- } finally {
182
- rmSync(root, { recursive: true, force: true });
183
- }
184
- });
185
-
186
- test("EPERM publish contention: live owner stays busy (no steal)", () => {
187
- const root = freshRoot();
188
- try {
189
- const lockPath = P.viewLockPath(root, "v1", "coordinator");
190
- mkdirSync(lockPath, { recursive: true });
191
- writeFileSync(join(lockPath, "owner.json"), JSON.stringify({ token: "t", pid: process.pid, identity: { pid: process.pid, startToken: "live" }, startedAt: Date.now() }));
192
- const got = tryAcquireOwnedViewLock(root, "v1", "coordinator", { identity: ME, fs: windowsPublishFs(lockPath) });
193
- assert.equal(got.acquired, false);
194
- assert.equal(got.reason, "busy");
195
- } finally {
196
- rmSync(root, { recursive: true, force: true });
197
- }
198
- });
199
-
200
- test("EPERM publish contention: fresh identity-less owner stays blocked", () => {
201
- const root = freshRoot();
202
- try {
203
- const lockPath = P.viewLockPath(root, "v1", "coordinator");
204
- mkdirSync(lockPath, { recursive: true });
205
- writeFileSync(join(lockPath, "owner.json"), JSON.stringify({ token: "t", pid: DEAD.pid, identity: null, startedAt: Date.now() }));
206
- const got = tryAcquireOwnedViewLock(root, "v1", "coordinator", { identity: ME, fs: windowsPublishFs(lockPath) });
207
- assert.equal(got.acquired, false);
208
- assert.equal(got.reason, "blocked");
209
- } finally {
210
- rmSync(root, { recursive: true, force: true });
211
- }
212
- });
213
- ```
214
-
215
- - [ ] **Step 2: Run tests to verify they pass**
216
-
217
- Run: `node --test test/locks.test.mjs`
218
- Expected: 4 个新用例 PASS。
219
-
220
- - [ ] **Step 3: Prove the tests actually pin the fix (revert check)**
221
-
222
- 临时把 Task 1 的 catch 判定改回旧形态(`if (code !== "EEXIST" && code !== "ENOTEMPTY") throw err;`),运行:
223
-
224
- Run: `node --test test/locks.test.mjs`
225
- Expected: 4 个新用例 FAIL(`EPERM` 被 throw)。确认后恢复 Task 1 的实现,再跑一次确认 PASS。
226
-
227
- - [ ] **Step 4: Commit**
228
-
229
- ```bash
230
- git add test/locks.test.mjs
231
- git commit -m "test(locks): cover EPERM publish contention reclaim/busy/blocked (issue #114)"
232
- ```
233
-
234
- ---
235
-
236
- ### Task 3: `ensureCoordinator` 端到端孤锁接管(spawn 真 coordinator)
237
-
238
- **Files:**
239
- - Test: `test/coordinator-client.test.mjs`
240
-
241
- **Interfaces:**
242
- - Consumes: 既有 `ensureCoordinator(root, { runnerScript })`、文件内辅助 `track(pid)` / `cleanupRoot(root)`、`COORDINATOR_SCRIPT` 常量。
243
- - Produces: 无生产接口。
244
-
245
- - [ ] **Step 1: Write the failing test**
246
-
247
- 在 `test/coordinator-client.test.mjs` 的 node:fs import 中加入 `mkdirSync`:
248
-
249
- ```js
250
- import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
251
- ```
252
-
253
- 在 `ensureCoordinator spawns the real coordinator; ...` 测试之后追加:
254
-
255
- ```js
256
- test("ensureCoordinator reclaims a stale identity-less orphan lease (issue #114)", async (t) => {
257
- const root = freshRoot();
258
- t.after(async () => {
259
- await cleanupRoot(root);
260
- });
261
-
262
- // Residue of a killed coordinator on a platform without startToken (Windows):
263
- // dead pid, identity-less, past the 5min orphan age.
264
- const lockPath = P.viewLockPath(root, "_coordinator", "state-coordinator");
265
- mkdirSync(lockPath, { recursive: true });
266
- writeFileSync(join(lockPath, "owner.json"), JSON.stringify({
267
- token: "orphan-lease",
268
- pid: 99999999,
269
- identity: { pid: 99999999, startToken: null },
270
- startedAt: Date.now() - 10 * 60_000,
271
- }), "utf8");
272
-
273
- const ensured = await ensureCoordinator(root, { runnerScript: COORDINATOR_SCRIPT });
274
- assert.equal(ensured.ok, true, "the orphan lease must be reclaimed, not block startup");
275
- assert.match(ensured.instanceId, /^[0-9a-f]+$/);
276
- track(ensured.pid);
277
- });
278
- ```
279
-
280
- - [ ] **Step 2: Run test to verify it passes**
281
-
282
- Run: `node --test test/coordinator-client.test.mjs`
283
- Expected: 新用例 PASS;同文件其余用例不受影响。
284
-
285
- - [ ] **Step 3: Commit**
286
-
287
- ```bash
288
- git add test/coordinator-client.test.mjs
289
- git commit -m "test(coordinator): e2e orphan lease takeover via ensureCoordinator (issue #114)"
290
- ```
291
-
292
- ---
293
-
294
- ### Task 4: 真机 Windows 验证 + 全量回归(验收 A5/A6/A7)
295
-
296
- **Files:** 无代码改动(验证任务)。
297
-
298
- - [ ] **Step 1: Windows 真机:锁测试转绿(A5)**
299
-
300
- Run: `node --test test/locks.test.mjs`
301
- Expected: **0 fail**(基线 20 tests / 4 fail → 修复后 25 tests / 0 fail,含 Task 1-2 新增用例)。
302
-
303
- - [ ] **Step 2: Windows 真机:coordinator 集成套件(A7)**
304
-
305
- Run: `node --test test/coordinator-client.test.mjs test/coordinator-journal.test.mjs`
306
- Expected: 0 fail(含 Task 3 新增用例)。
307
-
308
- - [ ] **Step 3: 类型检查(A6)**
309
-
310
- Run: `npm run typecheck`
311
- Expected: 零错误。
312
-
313
- - [ ] **Step 4: 全量回归(A6)**
314
-
315
- Run: `npm test 2>&1 | tail -40`
316
- Expected: 失败集不新增。与修复前基线对比:在**同一 worktree、未含本次改动**的 `a129b09` 上先记录一次基线失败清单(`git stash` 或另存输出),修复后按名字逐项比对,仅允许减少、不允许新增。(Windows 既有环境类失败照旧记录。)
317
-
318
- - [ ] **Step 5: 记录验收结果到 issue**
319
-
320
- ```bash
321
- gh issue comment 114 --repo zhuxixi/pi-agent-board --body "## 实现完成:验收结果 A1–A7
322
- <贴各命令与结果摘要>"
323
- ```
324
-
325
- - [ ] **Step 6: Commit(如有验证脚本或文档微调)**
326
-
327
- 若 Step 1-4 全部通过且无文件改动,本任务无 commit;若为跑基线在 worktree 内产生了临时产物,确认未 add 后删除。
328
-
329
- ---
330
-
331
- ## Self-Review
332
-
333
- - **Spec coverage**:A1 → Task 1 Step 1;A2/A3/A4 → Task 2;A5/A6 → Task 4;A7 → Task 3 + Task 4 Step 2;U1(用户实测)→ 合并部署后由用户执行(spec §3 已标 pending 规则)。
334
- - **Placeholder scan**:无 TBD/TODO;所有测试与实现代码已给出完整文本。
335
- - **Type consistency**:`isPublishConflictCode(code)` 签名在 Task 1 定义、Task 2 测试辅助通过行为依赖;`windowsPublishFs` 仅测试内使用;`track` / `cleanupRoot` / `COORDINATOR_SCRIPT` 均为既有名字。
336
-
337
- ---
338
-
339
- ## Execution notes
340
-
341
- - `b29ca2d` — Task 3 fix round 1 (controller-ruled): test-fixture robustness fix in `test/coordinator-client.test.mjs` — condition-based wait for SIGKILLed pids plus bounded EPERM/EBUSY retry on root removal. It also flipped 4 pre-existing spawn-real test failures green; no plan task was rewritten.