@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,490 +0,0 @@
1
- # Windows Process Identity Token Implementation Plan
2
-
3
- > **For agentic workers:** REQUIRED SUB-SKILL: Use subagent-driven-development (recommended) or executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
-
5
- **Goal:** Unify the four duplicate Linux-only `captureStartToken` implementations into one, give win32 a real comparable token (`ct:<unix-ms>` from the existing identity probe), and make host-role observation compare tokens — without touching the lease layer.
6
-
7
- **Architecture:** `src/core/pid.mjs` keeps the only synchronous token reader; `src/core/process-identity-probe.mjs` gains the async, platform-dispatching reader (`readProcessStartToken`) plus a pure `creationTimeToken`. `runner/pty-runner.mjs` writes the probed token into `host.json`'s `runnerIdentity`/`childIdentity`. `src/runtime/service.mjs` assembles an observation snapshot (alive + observed token, memoized per pid) and lets the pure `processIdentityState` decide; `revalidateProbeOwned` re-reads before any signal. Lease identities (`host-start`/`host-meta`/coordinator) keep using the synchronous reader, so `locks.mjs` is untouched.
8
-
9
- **Tech Stack:** Node ESM (`.mjs` + one `.ts` module), `node:test` + `assert/strict`, c8 coverage, no new dependencies.
10
-
11
- **Spec:** `docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md`
12
-
13
- ## Global Constraints
14
-
15
- - Tabs for indentation; English comments and commit messages.
16
- - Pure functions first, thin I/O shell; the spec's §5 split is a hard constraint — do not re-couple assembly and judgment.
17
- - Tests: `node:test` + `assert/strict`; anything touching fs/git uses a temp-dir fixture, never the host repository.
18
- - No new runtime dependencies.
19
- - Token format is exactly `ct:<positive-safe-integer-ms>` (`creationTimeToken()`), and only win32 produces it.
20
- - `readProcessStartToken` must never throw: every failure path returns `null` (callers read `null` as "unverifiable", never "foreign").
21
- - Commit subjects: conventional (`feat`/`fix`/`refactor`/`test`/`docs`/`chore`).
22
-
23
- ## Review Focus
24
-
25
- - **An old `host.json` (Windows, `startToken: null`) must behave exactly as today** — observation returns `unknown` immediately and the #217 probe fallback runs unchanged, with zero added probes.
26
- - **A hung probe (PowerShell disabled / 3 s timeout) must not consume the recovery budget** — `HOST_RECOVERY_POLL_MS`/`HOST_RECOVERY_GRACE_MS` still bound the loop, and a probe failure degrades to `unknown`, never `foreign`.
27
- - **A malformed record (identity present, `pid` or `spawnedAt` missing) must never become a releasable state** — `not_started` requires `identity == null && spawnedAt == null`; identity-bearing records are judged by observation.
28
- - **A reused pid must never receive a signal** — `revalidateProbeOwned` re-reads the token and refuses on mismatch or read failure; the `tokenMemo` may not shortcut this.
29
- - **Concurrent/bypassed memo must not leak** — each `recoverHost`/resolver invocation owns its own `tokenMemo`; a stale successful token must not be cached beyond one invocation.
30
-
31
- ---
32
-
33
- ### Task 1: `creationTimeToken` pure function
34
-
35
- **Files:**
36
- - Modify: `src/core/process-identity-probe.mjs`
37
- - Test: `test/process-identity-probe.test.mjs`
38
-
39
- **Interfaces:**
40
- - Produces: `creationTimeToken(creationTimeMs: number): string | null` — `"ct:<ms>"` for a positive safe integer, `null` otherwise.
41
-
42
- - [ ] **Step 1: Write the failing test**
43
-
44
- Add to `test/process-identity-probe.test.mjs`:
45
-
46
- ```js
47
- test("creationTimeToken formats only positive safe integers", () => {
48
- assert.equal(creationTimeToken(1792811668715), "ct:1792811668715");
49
- assert.equal(creationTimeToken(1), "ct:1");
50
- assert.equal(creationTimeToken(0), null);
51
- assert.equal(creationTimeToken(-1), null);
52
- assert.equal(creationTimeToken(1.5), null);
53
- assert.equal(creationTimeToken(NaN), null);
54
- assert.equal(creationTimeToken(Infinity), null);
55
- assert.equal(creationTimeToken(Number.MAX_SAFE_INTEGER + 1), null);
56
- assert.equal(creationTimeToken("1792811668715"), null);
57
- assert.equal(creationTimeToken(null), null);
58
- assert.equal(creationTimeToken(undefined), null);
59
- });
60
- ```
61
-
62
- - [ ] **Step 2: Run test to verify it fails**
63
-
64
- Run: `node --test test/process-identity-probe.test.mjs`
65
- Expected: FAIL — `creationTimeToken is not a function` (or import error).
66
-
67
- - [ ] **Step 3: Implement `creationTimeToken` in `src/core/process-identity-probe.mjs`**
68
-
69
- Signature and contract:
70
-
71
- ```js
72
- export function creationTimeToken(creationTimeMs) { /* positive safe integer → `ct:${n}`; else null */ }
73
- ```
74
-
75
- Use `Number.isSafeInteger(creationTimeMs) && creationTimeMs > 0`; no string coercion.
76
-
77
- - [ ] **Step 4: Run test to verify it passes**
78
-
79
- Run: `node --test test/process-identity-probe.test.mjs`
80
- Expected: PASS (all existing probe tests plus the new one).
81
-
82
- - [ ] **Step 5: Commit**
83
-
84
- ```bash
85
- git add src/core/process-identity-probe.mjs test/process-identity-probe.test.mjs
86
- git commit -m "feat: add creationTimeToken for comparable win32 process tokens"
87
- ```
88
-
89
- ---
90
-
91
- ### Task 2: `readProcessStartToken` cross-platform reader
92
-
93
- **Files:**
94
- - Modify: `src/core/process-identity-probe.mjs`
95
- - Test: `test/process-identity-probe.test.mjs`
96
-
97
- **Interfaces:**
98
- - Consumes: `captureStartToken(pid)` from `src/core/pid.mjs`; `readProcessIdentityProbe(pid, opts)` (same module); `creationTimeToken(ms)` from Task 1.
99
- - Produces: `readProcessStartToken(pid, { platform?, probe? }): Promise<string|null>` — linux → `captureStartToken`, win32 → `probe()` → `creationTimeToken(probe.creationTimeMs)`, every other platform → `null` **without calling `probe`**.
100
-
101
- - [ ] **Step 1: Write the failing tests**
102
-
103
- Add to `test/process-identity-probe.test.mjs`:
104
-
105
- ```js
106
- test("readProcessStartToken: linux delegates to captureStartToken", async () => {
107
- let probed = false;
108
- const token = await readProcessStartToken(process.pid, { platform: "linux", probe: async () => { probed = true; return null; } });
109
- assert.equal(probed, false);
110
- assert.equal(token, captureStartToken(process.pid));
111
- });
112
-
113
- test("readProcessStartToken: win32 converts the probe creation time", async () => {
114
- let seenPid = null;
115
- const token = await readProcessStartToken(4242, {
116
- platform: "win32",
117
- probe: async (pid) => { seenPid = pid; return { creationTimeMs: 1792811668715, commandLine: "node x" }; },
118
- });
119
- assert.equal(seenPid, 4242);
120
- assert.equal(token, "ct:1792811668715");
121
- });
122
-
123
- test("readProcessStartToken: darwin never probes and returns null", async () => {
124
- let probed = false;
125
- const token = await readProcessStartToken(4242, { platform: "darwin", probe: async () => { probed = true; return { creationTimeMs: 1, commandLine: null }; } });
126
- assert.equal(probed, false);
127
- assert.equal(token, null);
128
- });
129
-
130
- test("readProcessStartToken: probe failure and bad pids degrade to null", async () => {
131
- assert.equal(await readProcessStartToken(4242, { platform: "win32", probe: async () => null }), null);
132
- assert.equal(await readProcessStartToken(4242, { platform: "win32", probe: async () => ({ creationTimeMs: null, commandLine: null }) }), null);
133
- assert.equal(await readProcessStartToken(0, { platform: "win32", probe: async () => { throw new Error("must not probe"); } }), null);
134
- assert.equal(await readProcessStartToken(null, { platform: "win32", probe: async () => { throw new Error("must not probe"); } }), null);
135
- });
136
- ```
137
-
138
- - [ ] **Step 2: Run tests to verify they fail**
139
-
140
- Run: `node --test test/process-identity-probe.test.mjs`
141
- Expected: FAIL — `readProcessStartToken is not a function`.
142
-
143
- - [ ] **Step 3: Implement `readProcessStartToken` in `src/core/process-identity-probe.mjs`**
144
-
145
- Signature and dispatch exactly as the Interfaces block. Validate `pid` first (`Number.isSafeInteger(pid) && pid > 0` → else `null`, no probe). Linux path is synchronous inside the async function. Win32 path: `const probe = await probeFn(pid); return probe ? creationTimeToken(probe.creationTimeMs) : null;`. Any thrown probe error must be caught and become `null`. Update the module header comment: linux is supported via `/proc`, win32 via creation time, **darwin is deliberately unsupported** (ps elapsed time is second-resolution).
146
-
147
- - [ ] **Step 4: Run tests to verify they pass**
148
-
149
- Run: `node --test test/process-identity-probe.test.mjs`
150
- Expected: PASS.
151
-
152
- - [ ] **Step 5: Commit**
153
-
154
- ```bash
155
- git add src/core/process-identity-probe.mjs test/process-identity-probe.test.mjs
156
- git commit -m "feat: read process start tokens across platforms (win32 creation time)"
157
- ```
158
-
159
- ---
160
-
161
- ### Task 3: Collapse the four token copies into one
162
-
163
- **Files:**
164
- - Modify: `runner/pty-runner.mjs` (delete private `captureStartToken` at ~1613; import from `../src/core/pid.mjs`)
165
- - Modify: `runner/state-coordinator.mjs` (delete private `captureStartToken` at ~494; import from `../src/core/pid.mjs`)
166
- - Modify: `src/runtime/service.mjs` (delete private `readProcStartToken` at ~2546; import `captureStartToken`)
167
-
168
- **Interfaces:**
169
- - Consumes: `captureStartToken(pid)` from `src/core/pid.mjs`.
170
- - Produces: no new API — this task only removes duplicates. All three call sites keep their current argument and behaviour (lease identities stay `null` on non-Linux).
171
-
172
- - [ ] **Step 1: Replace the runner copies**
173
-
174
- In `runner/pty-runner.mjs` add `import { captureStartToken } from "../src/core/pid.mjs";` and delete the function at ~1613. In `runner/state-coordinator.mjs` add the same import (path `../src/core/pid.mjs`) and delete the function at ~494.
175
-
176
- - [ ] **Step 2: Replace the service copy**
177
-
178
- In `src/runtime/service.mjs`, delete `readProcStartToken` (~2546) and change `serviceIdentity()` and `defaultObserveProcess` to call `captureStartToken` (import it from `../core/pid.mjs`).
179
-
180
- - [ ] **Step 3: Verify the static convergence and types**
181
-
182
- Run: `Get-ChildItem src,runner -Recurse -Filter *.mjs | Select-String -Pattern 'function captureStartToken|function readProcStartToken'`
183
- Expected: exactly one hit — `src/core/pid.mjs`. (On POSIX: `grep -rn "function captureStartToken\|function readProcStartToken" src runner`.)
184
-
185
- Run: `npm run typecheck`
186
- Expected: clean.
187
-
188
- - [ ] **Step 4: Run the affected suites**
189
-
190
- Run: `node --test test/pid.test.mjs test/pty-runner.integration.test.mjs test/state-coordinator.integration.test.mjs test/service.test.mjs`
191
- Expected: no new failures versus the recorded baseline (`research/baseline-test.log`); the `/proc` token assertions in `pty-runner.integration.test.mjs` still pass on Linux.
192
-
193
- - [ ] **Step 5: Commit**
194
-
195
- ```bash
196
- git add runner/pty-runner.mjs runner/state-coordinator.mjs src/runtime/service.mjs
197
- git commit -m "refactor: collapse the four start-token copies into src/core/pid.mjs"
198
- ```
199
-
200
- ---
201
-
202
- ### Task 4: pty-runner writes real tokens into `host.json`
203
-
204
- **Files:**
205
- - Modify: `runner/pty-runner.mjs`
206
-
207
- **Interfaces:**
208
- - Consumes: `readProcessStartToken(pid, opts)` from `../src/core/process-identity-probe.mjs`.
209
- - Produces: `host.json` records whose `runnerIdentity.startToken` / `childIdentity.startToken` are `/proc` tokens on Linux and `ct:<ms>` on win32; `null` when the probe is unavailable.
210
-
211
- - [ ] **Step 1: Start the self probe before the start lease**
212
-
213
- At the very top of `async function ownedMain(config)` (before the `acquireOwnedViewLock(..., "host-start", ...)` call at ~768):
214
-
215
- ```js
216
- const selfTokenPromise = readProcessStartToken(process.pid);
217
- ```
218
-
219
- Do **not** await it here — it runs in parallel with lease acquisition, the yield check and socket binding.
220
-
221
- - [ ] **Step 2: Use it for `runnerIdentity`**
222
-
223
- At the "Publish runner identity" `ownedUpdate` (~933), await before the update and use the result:
224
-
225
- ```js
226
- const selfToken = await selfTokenPromise;
227
- const claimedSelf = ownedUpdate((cur) => ({ ...cur, runnerPid: process.pid, runnerIdentity: { pid: process.pid, startToken: selfToken }, ... }));
228
- ```
229
-
230
- Leave the `host-start` lease identity at ~771 on the synchronous `captureStartToken` (lease layer unchanged).
231
-
232
- - [ ] **Step 3: Use it for `childIdentity`**
233
-
234
- At the post-spawn `ownedUpdate` (~1023), read the child token first:
235
-
236
- ```js
237
- const childToken = await readProcessStartToken(childPid);
238
- const ready = ownedUpdate((cur) => ({ ...cur, childPid, childIdentity: { pid: childPid, startToken: childToken }, ... }));
239
- ```
240
-
241
- - [ ] **Step 4: Run the integration suite**
242
-
243
- Run: `node --test test/pty-runner.integration.test.mjs`
244
- Expected: PASS on Linux — `runnerIdentity.startToken` and `childIdentity.startToken` still match `/^\d+$/` (the `/proc` value); on a Windows host the same assertions read `ct:` and the run/ready timing stays within the suite's existing tolerances.
245
-
246
- - [ ] **Step 5: Commit**
247
-
248
- ```bash
249
- git add runner/pty-runner.mjs
250
- git commit -m "feat(host): publish a real start token on win32 host records"
251
- ```
252
-
253
- ---
254
-
255
- ### Task 5: `processIdentityState` — narrow `not_started`
256
-
257
- **Files:**
258
- - Modify: `src/core/host-coordination.mjs:49-54`
259
- - Test: `test/host-coordination.test.mjs`
260
-
261
- **Interfaces:**
262
- - Produces: `processIdentityState(identity, observed, spawnedAt)` now returns `not_started` only when `spawnedAt == null && identity == null`; an identity-bearing record with a missing `spawnedAt` is judged by `observed`.
263
-
264
- - [ ] **Step 1: Write the failing tests**
265
-
266
- Add to `test/host-coordination.test.mjs`:
267
-
268
- ```js
269
- test("processIdentityState: an identity-bearing record is never not_started", () => {
270
- assert.equal(processIdentityState({ pid: 1, startToken: "t" }, { alive: true, startToken: "t" }, null), "owned");
271
- assert.equal(processIdentityState({ pid: 1, startToken: "t" }, { alive: true, startToken: "u" }, null), "foreign");
272
- assert.equal(processIdentityState({ pid: 1, startToken: "t" }, { alive: false }, null), "dead");
273
- assert.equal(processIdentityState({ pid: 1, startToken: "t" }, { alive: true, startToken: null }, null), "unknown");
274
- });
275
-
276
- test("processIdentityState: not_started needs both identity and spawnedAt to be absent", () => {
277
- assert.equal(processIdentityState(null, null, null), "not_started");
278
- assert.equal(processIdentityState(undefined, null, null), "not_started");
279
- });
280
- ```
281
-
282
- - [ ] **Step 2: Run tests to verify they fail**
283
-
284
- Run: `node --test test/host-coordination.test.mjs`
285
- Expected: FAIL — the identity-bearing `null`-`spawnedAt` cases return `not_started`.
286
-
287
- - [ ] **Step 3: Change the guard**
288
-
289
- In `src/core/host-coordination.mjs`:
290
-
291
- ```js
292
- if (spawnedAt == null && identity == null) return "not_started";
293
- ```
294
-
295
- Update the JSDoc sentence that explains the degradation rule (identity present ⇒ spawn happened ⇒ judge by observation; suggesting `not_started` there would mark a possibly-live host releasable).
296
-
297
- - [ ] **Step 4: Run tests to verify they pass**
298
-
299
- Run: `node --test test/host-coordination.test.mjs`
300
- Expected: PASS, including the pre-existing `(null, null, null) → not_started` case.
301
-
302
- - [ ] **Step 5: Commit**
303
-
304
- ```bash
305
- git add src/core/host-coordination.mjs test/host-coordination.test.mjs
306
- git commit -m "fix: never classify an identity-bearing record as not_started"
307
- ```
308
-
309
- ---
310
-
311
- ### Task 6: Service observation assembly + `tokenMemo`
312
-
313
- **Files:**
314
- - Modify: `src/runtime/service.mjs` (`observeHostRole` ~983, `defaultObserveProcess` ~2624, `createService` ~136-161, six call sites)
315
- - Test: `test/service.test.mjs`
316
-
317
- **Interfaces:**
318
- - Consumes: `captureStartToken` (Task 3), `readProcessStartToken` (Task 2), `processIdentityState` (Task 5).
319
- - Produces:
320
- - `createService(opts)` accepts `opts.isAlive?: (pid) => boolean` and `opts.readStartToken?: (pid) => Promise<string|null>` (defaults `isAlive`, `readProcessStartToken`).
321
- - `observeHostRole(host, role, { sessionFile, memo, tokenMemo })` — `tokenMemo: Map<number, Promise<string|null>>`.
322
- - Default observation: `identity == null && spawnedAt == null → "not_started"`; `pid == null → "unknown"`; dead → `"dead"`; record token `null` → `"unknown"` **without probing**; otherwise compare the memoized observed token and return `processIdentityState(...)`.
323
-
324
- - [ ] **Step 1: Write the failing tests**
325
-
326
- Add to `test/service.test.mjs` (follow the file's existing `createService` fixture pattern):
327
-
328
- ```js
329
- test("observe: a matching win32 token is owned and a mismatching one is foreign", async () => {
330
- // inject isAlive → true and readStartToken → "ct:111" for a stored identity whose token is "ct:111" / "ct:222"
331
- });
332
-
333
- test("observe: a null record token never probes and stays unknown", async () => {
334
- // readStartToken must not be called; observation must be "unknown"
335
- });
336
-
337
- test("observe: tokenMemo probes each pid once per recovery", async () => {
338
- // counting stub: two observations of the same pid → exactly 1 read
339
- });
340
- ```
341
-
342
- Write real assertions in the body; the three comments name the cases the tests must pin.
343
-
344
- - [ ] **Step 2: Run tests to verify they fail**
345
-
346
- Run: `node --test test/service.test.mjs`
347
- Expected: FAIL — observations fall back to the old behaviour / the injected seams do not exist.
348
-
349
- - [ ] **Step 3: Implement the assembly and wire the memo**
350
-
351
- Move the observation assembly into the `createService` closure as `observeProcessIdentity(identity, { spawnedAt, pid, tokenMemo })` (spec D4 pseudo-code). Keep `opts.observeProcess` as the outer override; default to the new closure. Add `opts.isAlive` / `opts.readStartToken`. Thread `tokenMemo` through `observeHostRole` and create one per invocation at all six call sites (`~1109`, `~1110`, `~1152`, `~1155`, `~1416`, `~1417`), next to the existing `probeMemo`.
352
-
353
- - [ ] **Step 4: Run tests to verify they pass**
354
-
355
- Run: `node --test test/service.test.mjs test/host-recovery.test.mjs test/host-resolver.test.mjs`
356
- Expected: PASS; existing injected `observeProcess` stubs keep working (they may stay synchronous).
357
-
358
- - [ ] **Step 5: Commit**
359
-
360
- ```bash
361
- git add src/runtime/service.mjs test/service.test.mjs
362
- git commit -m "feat(service): compare start tokens when observing host roles"
363
- ```
364
-
365
- ---
366
-
367
- ### Task 7: `revalidateProbeOwned` covers the token path
368
-
369
- **Files:**
370
- - Modify: `src/runtime/service.mjs:1005-1018`
371
- - Test: `test/service.test.mjs`
372
-
373
- **Interfaces:**
374
- - Produces: `revalidateProbeOwned(host, role, sessionFile, memo, tokenMemo)` — with a recorded token it re-reads via `readStartTokenImpl(pid)` **bypassing `tokenMemo`**, compares, and returns `false` on mismatch or `null`.
375
-
376
- - [ ] **Step 1: Write the failing tests**
377
-
378
- Add to `test/service.test.mjs`:
379
-
380
- ```js
381
- test("revalidate: a reused pid with a different token never receives a signal", async () => {
382
- // first read "ct:111" (observation, memoized), then re-read "ct:222" → no SIGTERM recorded
383
- });
384
-
385
- test("revalidate: a failing re-read refuses the signal", async () => {
386
- // observation "ct:111", re-read null → no signal
387
- });
388
- ```
389
-
390
- - [ ] **Step 2: Run tests to verify they fail**
391
-
392
- Run: `node --test test/service.test.mjs`
393
- Expected: FAIL — the token path currently returns `true` unconditionally.
394
-
395
- - [ ] **Step 3: Implement the revalidation**
396
-
397
- Replace the `identity?.startToken != null → return true` shortcut: re-read with `readStartTokenImpl(pid)` (not the memo), compare against `identity.startToken`, return `false` on mismatch/`null`. Keep the probe branch unchanged (it still writes back to `probeMemo`). Do not write token results into `probeMemo` (its values are probe objects).
398
-
399
- - [ ] **Step 4: Run the recovery suites**
400
-
401
- Run: `node --test test/service.test.mjs test/host-recovery.test.mjs`
402
- Expected: PASS.
403
-
404
- - [ ] **Step 5: Commit**
405
-
406
- ```bash
407
- git add src/runtime/service.mjs test/service.test.mjs
408
- git commit -m "fix(service): re-read the token before signalling an owned process"
409
- ```
410
-
411
- ---
412
-
413
- ### Task 8: Degradation-path acceptance test
414
-
415
- **Files:**
416
- - Test: `test/service.test.mjs`
417
-
418
- **Interfaces:**
419
- - Consumes: everything from Tasks 6–7; no production change expected.
420
-
421
- - [ ] **Step 1: Write the test**
422
-
423
- ```js
424
- test("degradation: unreadable token and unavailable probe stay unknown and pend recovery", async () => {
425
- // record token "ct:111"; readStartToken → null; probeProcessIdentity → null
426
- // expect observation "unknown" and the recovery result "recovery_pending"
427
- // assert no signal was sent to any pid
428
- });
429
- ```
430
-
431
- - [ ] **Step 2: Run it**
432
-
433
- Run: `node --test test/service.test.mjs`
434
- Expected: PASS. If it fails because production behaviour differs, fix production (the spec's D7 row is the contract), then re-run.
435
-
436
- - [ ] **Step 3: Commit**
437
-
438
- ```bash
439
- git add test/service.test.mjs
440
- git commit -m "test: pin the unverifiable-identity degradation path"
441
- ```
442
-
443
- ---
444
-
445
- ### Task 9: Full verification and PR preparation
446
-
447
- **Files:**
448
- - Modify: none (verification only; PR body goes to `~/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/pr-body.md`, outside the repo).
449
-
450
- - [ ] **Step 1: Typecheck and full test run (Windows host)**
451
-
452
- Run: `npm run typecheck` then `npm test *> "$HOME/baseline-after.log"`
453
- Expected: typecheck clean; failure set equals the recorded baseline (`research/baseline-test.log` / `baseline-failures.txt`, 1298 tests / 66 fail / 5 skip) — diff the `✖` lines and require an empty difference.
454
-
455
- - [ ] **Step 2: Coverage and package sanity**
456
-
457
- Run: `npm run test:coverage` then `npm run pack:dry`
458
- Expected: coverage thresholds hold (lines 85 / funcs 80 / branches 70) for the changed files; `pack:dry` lists the same file set plus no strays.
459
-
460
- - [ ] **Step 3: Record the acceptance matrix**
461
-
462
- Write the A1–A11 results (command → outcome) and U1 as `pending (user acceptance, needs Windows host + reload)` into the issue-238 ledger; do not claim U1 as passed.
463
-
464
- - [ ] **Step 4: Write the PR body**
465
-
466
- `pr-body.md` must contain: summary, the spec link, per-gap mapping (A-IDs), the two Windows-only behaviours that CI cannot cover, and the local-baseline evidence.
467
-
468
- - [ ] **Step 5: Push and open the PR**
469
-
470
- ```bash
471
- git push -u origin issue-238-process-identity-token
472
- gh pr create --repo zhuxixi/pi-agent-board --base main --head issue-238-process-identity-token --title "fix(host): unify process identity tokens and produce them on win32 (#238)" --body-file "$HOME/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/pr-body.md"
473
- gh pr edit <PR> --repo zhuxixi/pi-agent-board --add-label zima:needs-review
474
- ```
475
-
476
- Then block on Zima CR (skill `zima-pr-monitor`) in the same turn.
477
-
478
- ---
479
-
480
- ## Self-Review
481
-
482
- **Spec coverage:** A1→T1, A2→T2, A3/A6→T6, A4→T5 (+T6), A5→T7, A7→T3, A8→T4, A9→T8, A10/A11→T9, U1→T9 Step 3 (recorded pending). D1→T3, D2→T2, D3→T4, D4→T6, D5→T5, D6→T7, D7→T8. No spec section without a task.
483
-
484
- **Step scan:** every step names one action and its checkable result; bodies are limited to signatures, dispatch rules and the exact guards the spec pins.
485
-
486
- **Type consistency:** `creationTimeToken(number) → string|null` (T1) is consumed by `readProcessStartToken` (T2) and both live in `process-identity-probe.mjs`; `tokenMemo: Map<pid, Promise<string|null>>` is introduced in T6 and consumed by T7; `processIdentityState(identity, observed, spawnedAt)` keeps its three-argument shape in T5 and T6.
487
-
488
- **Review Focus:** old-record zero-probe path → T6 Step 1 case 2; hung probe degradation → T8; malformed record not releasable → T5 Step 1; reused-pid signal refusal → T7 Step 1; memo isolation → T6 Step 1 case 3.
489
-
490
- **Proportion:** ~9 tasks against a 286-line spec; code blocks are signatures and test assertions, not implementations.