@huseynsnmz/pi-board 0.15.3 → 0.17.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.
- package/CHANGELOG.md +24 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +55 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +277 -0
- package/package.json +5 -11
- package/src/commands/agent-board.ts +26 -2
- package/src/commands/attach-flow.ts +4 -0
- package/src/core/double-press.mjs +6 -2
- package/src/core/slash-commands.mjs +52 -0
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +59 -25
- package/src/ui/pty-attach.ts +8 -3
- package/IMPLEMENTATION_PLAN.md +0 -920
- package/PRD.md +0 -484
- package/PROGRESS.md +0 -142
- package/VERIFY.md +0 -115
- package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
- package/docs/EXPLORATION.md +0 -187
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
- package/docs/RELEASE.md +0 -148
- package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
- package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
- package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
- package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
- package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
- package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
- package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
- package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
- package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
- package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
- package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
- package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
- package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
- package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
- package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
- package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
- package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
- package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
- package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
- package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
- package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
- package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
- package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
- package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
- package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
- package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
- package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
- package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
- package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
- package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
- package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
- package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
- package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
- package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
- package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
- package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
- package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
- package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
- package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
- package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
- package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
- package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
- package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
- package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
- package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
- package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
- package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
- package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
- package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
- package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
- package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
- package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
- package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
- package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
- package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
- package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
- package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
- package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
- package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
- package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
- package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
- package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
- package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
- package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
- package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
- package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
- package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
- package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
- package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
- package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
- package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
- package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
- package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
- package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
- package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
- package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
- package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
- package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
- package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
- package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
- package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
- package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
- package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
- package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
- package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
- package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
- package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
- package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
- package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
- package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
- package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
- package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
- package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
- package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
- package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
- package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
- package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
- package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
- package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
- package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
- package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
- package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
- package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
- package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
- package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
- package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
- package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
- package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
- package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
- package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
- package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
- package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
- package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
- package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
- package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
- package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
- package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
- package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
- package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
- package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
- package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
- package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
- package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
- package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
- package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
- package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
- package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
- package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
- package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
- package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
- package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
- package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
- package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
- package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
- package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
- package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
- package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
- package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
- package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
- package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
- package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
- package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
- package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
- package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
- package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
- package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
- package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
- package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
- package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
- package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
- package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
- package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
- package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
- package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
- package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
- package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
- package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
- package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
- package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
- package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
- package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
- package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
- package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
- package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
- package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
- package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
- package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
- package/scripts/budget-audit.mjs +0 -135
- package/scripts/release.mjs +0 -446
- package/scripts/release_helper.mjs +0 -309
- package/scripts/run-perf-gate.mjs +0 -40
- package/scripts/run-tests.mjs +0 -375
package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
# issue #238 — 设计:把进程身份 token 统一为一处跨平台实现,让 Windows 也能产出并比对 token
|
|
2
|
-
|
|
3
|
-
Drafted: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T11:52:00+08:00
|
|
4
|
-
Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T12:05:00+08:00 · 依据 R1 复核(R1-G1 darwin 秒级抖动排除 token 路径、R1-G2 观测侧 token memo、R1-G3 U1 pid 复用步骤重写、R1-D1…D10 全部接受)
|
|
5
|
-
Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T12:20:00+08:00 · 依据 R2 复核(R2-G1 U1④ resolver 双角色 ended 门槛、R2-D1 probe 账 6 次、R2-D2 not_started 接线与纯函数条件、R2-D3 darwin 行号、R2-D4 CI 分步命令、R2-D5 U1③ 毫秒换算、R2-D6 重验降级描述;全部接受)
|
|
6
|
-
Revised: deepseek/deepseek-flash (selected) · deepseek/deepseek-flash (physical) @ 2026-10-24T12:45:00+08:00 · 依据 R3 复核(收敛轮:0 真缺口;R3-D1 ci.yml 步骤行号、R3-D2 pid fallback 与 D4 签名统一、R3-D3 U1① 安装形态与升级路径;全部接受)
|
|
7
|
-
|
|
8
|
-
仓库:zhuxixi/pi-agent-board · issue:#238 · 基线:`main` @ 0a31485 · 状态:已收敛(第 3 轮 0 真缺口),待用户确认设计
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 1. 问题与根因(已核实)
|
|
13
|
-
|
|
14
|
-
**现象**:Windows 上每一处进程身份判定都退化为「pid 死活」——`captureStartToken()` 读 `/proc`,非 Linux 恒 `null`。`host.json` 的 `runnerIdentity` / `childIdentity` 在 Windows 上永远写着 `startToken: null`,观测侧只能靠 pid 存活判断,pid 一被复用就永远判不出身份。
|
|
15
|
-
|
|
16
|
-
**根因(两层)**:
|
|
17
|
-
|
|
18
|
-
| 层 | 事实 | 位置 |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| 能力层 | token 采集实现有 **四份**(issue 说三份,漏了 service 私有副本),且都只支持 Linux | `src/core/pid.mjs:53`、`runner/pty-runner.mjs:1613`、`runner/state-coordinator.mjs:494`、`src/runtime/service.mjs:2546 readProcStartToken` |
|
|
21
|
-
| 语义层 | host-role 判定散在三处:运行时走 `service.mjs:2624 defaultObserveProcess`(同步),纯函数 `host-coordination.mjs:49 processIdentityState` 运行时未被引用,`observeHostRole`(`service.mjs:983`)自带一套平行判定 | 见左 |
|
|
22
|
-
|
|
23
|
-
**已修复的部分(本设计的前提,不要重做)**:issue 正文的 consequence 1(#217 卡在 `stopping`)已由 **PR #240**(`b743ef6`)修复——`src/core/process-identity-probe.mjs` + `classifyUnverifiableProcess`(`host-coordination.mjs:109`)用「命令行优先 / 创建时间 5 s 容差兜底」把 `unknown` 细化为 `owned`/`foreign`。本 issue 不做第二套兜底逻辑,只补「token 本身」。
|
|
24
|
-
|
|
25
|
-
**剩余缺口(本设计要修的)**:
|
|
26
|
-
|
|
27
|
-
1. 记录侧在 Windows 上写 `null` → 观测侧无法直接比对,只能依赖命令行可读性与 5 s 创建时间窗口。
|
|
28
|
-
2. 四份采集实现漂移风险(注释已不一致)。
|
|
29
|
-
3. 判定未收敛:`processIdentityState` 是死代码,`observeHostRole` 自己实现了一遍 `not_started`/`dead` 语义。
|
|
30
|
-
|
|
31
|
-
**可行性实测(本机 Windows 11,2026-10-24,脚本 `research/verify-probe-self.mjs`)**:
|
|
32
|
-
|
|
33
|
-
| 观测 | 结果 |
|
|
34
|
-
|---|---|
|
|
35
|
-
| 同一 pid 连续两次 probe 的 `creationTimeMs` | 完全相同(`1792811668715`)→ 稳定,可作 token |
|
|
36
|
-
| 自查询 `creationTimeMs` vs `Date.now() - process.uptime()*1000` | 差 **-10 ms** → 同一时钟语义,但记录侧仍应统一用 probe 值 |
|
|
37
|
-
| 刚 `spawn` 的子进程立即可 probe? | **可以**,`creationTimeMs - spawnedAt = -1 ms` → 无竞态窗口 |
|
|
38
|
-
| 单次 probe 耗时 | **356–387 ms**(`Get-CimInstance`) |
|
|
39
|
-
| 命令行可读 | 可读(`node.exe <script> ...`) |
|
|
40
|
-
|
|
41
|
-
**darwin 未实测且机制上不可用**:`resolveProbeCommand` 的 darwin 分支(`process-identity-probe.mjs:132-134`)用 `ps -p <pid> -o etime=,command=`,`parsePsElapsedMs`(`:69` 正则,`MM:SS`/`HH:MM:SS`/`D-HH:MM:SS`)只有**秒级分辨率**,`parsePsProbeLine`(`:86-101`)以 `nowMs - elapsedMs` 反推创建时间 —— 两次读取在不同时刻进行时估算值可相差约 1 s。若把它当 token 做字符串相等比对,会把活着的 owned host 判成 `foreign`(→ 释放记录 + 新起宿主 + 双写 session 文件)。**故 token 路径只给 win32;darwin 维持 `null`**,继续走 #217 的 5 s 容差兜底(容差恰好吸收秒级抖动)。
|
|
42
|
-
|
|
43
|
-
**下游价值**:#239(`replaceStaleCoordinator` 发 SIGTERM 前不校验身份)正文自述依赖本 issue 的底座。
|
|
44
|
-
|
|
45
|
-
## 2. 范围
|
|
46
|
-
|
|
47
|
-
### 做
|
|
48
|
-
|
|
49
|
-
1. **单一采集实现**(4 → 1):`src/core/pid.mjs` 为唯一同步 token 实现;删除 pty-runner / state-coordinator / service 三份私有副本。
|
|
50
|
-
2. **跨平台异步 token 读取**:新增 `readProcessStartToken(pid, opts)`(`src/core/process-identity-probe.mjs`)——Linux 用 `/proc`;**win32 用已有 probe 的创建时间,格式化为 `ct:<unix-ms>`**;darwin 与其他平台 `null`;失败 `null`。
|
|
51
|
-
3. **记录侧写入真实 token(只限 `host.json` 身份)**:`pty-runner` 的 `runnerIdentity` 与 `childIdentity` 在 Windows 上写 `ct:<ms>`。
|
|
52
|
-
4. **观测侧 token 比对**:`service` 的 host-role 观测在「记录 token 非空」时异步读取观测 token 并比对;相等 `owned`、不等 `foreign`;记录或观测任一为 `null` → 维持 `unknown` 并走 #217 的 probe 兜底。观测侧 token 读取**按 pid memo 化**(单次恢复 ≤2 次 probe)。
|
|
53
|
-
5. **判定收敛**:`observeHostRole` 的 `not_started`/`dead`/`owned`/`foreign` 判定改由 `host-coordination.mjs` 的 `processIdentityState` 产出(纯函数),`observeHostRole` 只负责组装观测快照与兜底。
|
|
54
|
-
6. **发信号前重验覆盖 token 路径**:`revalidateProbeOwned` 对「记录 token 非空」不再无条件放行,而是实时重读比对(绕过 memo),读失败即拒绝发信号(见 D6)。
|
|
55
|
-
|
|
56
|
-
### 不做(显式非目标)
|
|
57
|
-
|
|
58
|
-
| # | 非目标 | 理由 |
|
|
59
|
-
|---|---|---|
|
|
60
|
-
| N1 | `locks.mjs classifyLeaseOwner` 的 token 比对 | 锁获取全同步(`tryAcquireOwnedViewLock`);Windows 读 token 需 ~390 ms 异步 probe,实现只能把锁层异步化。且 #224(PR #236)已用心跳覆盖租约回收语义。 |
|
|
61
|
-
| N2 | 租约 identity(`host-start` / `host-meta` / coordinator lease)写 Windows token | 同上:`hasIdentity` 切换会把租约推进 identity 分支(`locks.mjs:305`),而判定仍只问 pid 死活 —— #224 spec 已论证这比现状更糟。本 issue 只改 `host.json`,锁层行为零变化。 |
|
|
62
|
-
| N3 | `service` 的 `currentProcessIdentity()` / `hostMetaIdentity()` 跨平台异步化 | 同步高频路径(每次 claim/recover),probe 会阻塞 dashboard 事件循环 390 ms。保持 `null`(= 现状)。 |
|
|
63
|
-
| N4 | 重写 #217 的命令行佐证逻辑;darwin 的 token 化 | `classifyUnverifiableProcess` 保留为 token 不可用时的兜底;darwin 的 `ps` 只有秒级分辨率(见 §1),不能当 token。 |
|
|
64
|
-
| N5 | #239 的发信号前身份校验 | 依赖本底座,另立。 |
|
|
65
|
-
| N6 | legacy(`instanceId == null`)记录的 token 补齐 | 既有 `canFinalizeLegacyHost` 覆盖,不受影响。 |
|
|
66
|
-
|
|
67
|
-
## 3. 设计决策
|
|
68
|
-
|
|
69
|
-
### D1 — 单一采集实现(同步)
|
|
70
|
-
|
|
71
|
-
`src/core/pid.mjs` 保留唯一 `captureStartToken(pid)`:Linux 读 `/proc/<pid>/stat` 第 22 字段,其他平台 `null`。删除:
|
|
72
|
-
|
|
73
|
-
- `runner/pty-runner.mjs:1613` → `import { captureStartToken } from "../src/core/pid.mjs"`
|
|
74
|
-
- `runner/state-coordinator.mjs:494` → 同上
|
|
75
|
-
- `src/runtime/service.mjs:2546 readProcStartToken` → 用 `captureStartToken`
|
|
76
|
-
|
|
77
|
-
契约不变:`test/pid.test.mjs:69` 的「非 Linux 返回 null」继续成立。
|
|
78
|
-
|
|
79
|
-
### D2 — 跨平台异步 token(仅 win32 产出 token)
|
|
80
|
-
|
|
81
|
-
`src/core/process-identity-probe.mjs` 新增:
|
|
82
|
-
|
|
83
|
-
```js
|
|
84
|
-
/** `ct:<unix-ms>` — a comparable, stable process token for win32 only. */
|
|
85
|
-
export function creationTimeToken(creationTimeMs) {
|
|
86
|
-
// pure: positive safe integer → `ct:<ms>`; anything else → null
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Cross-platform start token.
|
|
91
|
-
* - linux : captureStartToken(pid) (/proc 第 22 字段,jiffies)
|
|
92
|
-
* - win32 : probe → creationTimeToken(ms) (CIM 毫秒级,两次读取严格相等)
|
|
93
|
-
* - 其他 : null (darwin 的 ps etime 只有秒级,不能当 token)
|
|
94
|
-
*/
|
|
95
|
-
export async function readProcessStartToken(pid, {
|
|
96
|
-
platform = process.platform,
|
|
97
|
-
probe = readProcessIdentityProbe, // injectable for tests
|
|
98
|
-
} = {}) { /* linux → captureStartToken; win32 → probe → creationTimeToken; else null */ }
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
- 非法 pid / `probe == null` / `creationTimeMs == null` → `null`(= 不可验证,绝不猜测)。
|
|
102
|
-
- `ct:` 前缀保证与 Linux 的 jiffies 字符串永不相等;跨平台/跨来源记录不会被误判 `owned`。
|
|
103
|
-
- **darwin 显式返回 `null`,且不调用 probe**(省一次 ~`ps` 往返,语义上也不需要)。
|
|
104
|
-
|
|
105
|
-
### D3 — 记录侧(仅 `host.json`)
|
|
106
|
-
|
|
107
|
-
`runner/pty-runner.mjs`(注意:`main()` `:110` 是**同步**入口,只读 config 并分发;第 1–7 步都在 `async function ownedMain(config)` `:545` 内):
|
|
108
|
-
|
|
109
|
-
| 位置 | 现在 | 改为 |
|
|
110
|
-
|---|---|---|
|
|
111
|
-
| `:771` start lease identity | `captureStartToken(process.pid)` | **不变**(锁层保持 `null` on Windows,见 N2) |
|
|
112
|
-
| `:936 runnerIdentity` | `captureStartToken(process.pid)` | `await selfTokenPromise` |
|
|
113
|
-
| `:1026 childIdentity` | `captureStartToken(childPid)` | `await readProcessStartToken(childPid)` |
|
|
114
|
-
|
|
115
|
-
**发起时点**:在 `ownedMain(config)` **开头、第 1 步(`acquireOwnedViewLock`,`:768`)之前**:
|
|
116
|
-
|
|
117
|
-
```js
|
|
118
|
-
const selfTokenPromise = readProcessStartToken(process.pid); // 与 lease/yield/listen 并行
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
第 4 步(`:933`)`await selfTokenPromise` 后再 `ownedUpdate`。
|
|
122
|
-
|
|
123
|
-
**时序量化(Windows 实测)**:
|
|
124
|
-
|
|
125
|
-
- `runnerIdentity`:前三步(lease/yield/listen)是本地快速操作(通常 <100 ms),probe 需 356–387 ms → 锁内等待 `≈ 390 − 前三步 ≈ 300 ms`。`HOST_RUNNER_LOCK_WAIT_MS = 5_000`(`pty-runner.mjs:58`)足以吸收;start lease 的持锁窗口因此比现状长约 0.3 s(现状本来就含 spawn/ready 等毫秒—百毫秒级操作)。
|
|
126
|
-
- `childIdentity`:`state: "alive"` 的 publish 延后 ≤ ~390 ms(仅 Windows;Linux 上两个 await 都是 `/proc` 同步零成本)。
|
|
127
|
-
- probe 失败(PowerShell 被禁/超时)→ `null`,写盘结果与现状一致。
|
|
128
|
-
|
|
129
|
-
`runner/state-coordinator.mjs:134`:改为 import `captureStartToken`(语义不变,Windows 仍 `null`,见 N2)。
|
|
130
|
-
|
|
131
|
-
### D4 — 观测侧(`service`):观测层与判定层分离 + token memo
|
|
132
|
-
|
|
133
|
-
现状 `defaultObserveProcess`(`service.mjs:2624`)把「读 token」和「判定」揉在一个模块级函数里,且 `opts` 注入到不了它(`createService` 之外)。改为两层:
|
|
134
|
-
|
|
135
|
-
1. **纯判定**:`host-coordination.mjs` 的 `processIdentityState(identity, observed, spawnedAt)`——`not_started` → `dead` → 任一 token `null` → `unknown` → 相等 `owned` / 不等 `foreign`。
|
|
136
|
-
**`not_started` 的判定条件本次修订为 `identity == null && spawnedAt == null`**(R2-D2):旧条件是 `spawnedAt == null`,会把「identity 已写但 spawnedAt 缺失」的畸形记录判成 `not_started`(属于 `SAFE_TO_RELEASE`)→ 可能释放一个**仍然活着**的 host → 新起宿主双写 session 文件。`identity` 存在即证明该角色 spawn 已发生,此时必须按观测判定(`owned`/`dead`/`unknown`),不能宣布未启动。既有用例 `(null, null, null) → not_started` 不受影响。
|
|
137
|
-
2. **观测组装**(`createService` 闭包内,可注入):`observeProcessIdentity(identity, { spawnedAt, pid, tokenMemo })`。`pid` 由 `observeHostRole` 按现状 `identity?.pid ?? host?.[`${role}Pid`] ?? null`(`service.mjs:990`)算出后传入——**保留 pid fallback**,identity 已写但 pid 字段缺失的畸形记录仍能走 #217 probe 兜底:
|
|
138
|
-
|
|
139
|
-
```js
|
|
140
|
-
// 组装 observed 快照,判定一律交给 processIdentityState(无魔法值参数)
|
|
141
|
-
if (identity == null && spawnedAt == null) return "not_started"; // 保留既有 :987-989 语义
|
|
142
|
-
if (pid == null) return "unknown";
|
|
143
|
-
const alive = isAliveImpl(pid);
|
|
144
|
-
const observed = { alive, startToken: null };
|
|
145
|
-
if (alive && identity.startToken != null) {
|
|
146
|
-
// 只在记录 token 非空时才读观测 token:旧记录/服务侧仍是 0 次新增 probe
|
|
147
|
-
if (!tokenMemo.has(pid)) tokenMemo.set(pid, readStartTokenImpl(pid));
|
|
148
|
-
observed.startToken = await tokenMemo.get(pid);
|
|
149
|
-
}
|
|
150
|
-
return processIdentityState(identity, observed, spawnedAt);
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
`opts.observeProcess` 的注入点保留(测试优先,接管「观测 + 判定」);默认值改为上述闭包实现;新增 `opts.isAlive` 与 `opts.readStartToken`(**均为待新增注入点**,默认 `pid.isAlive` / `readProcessStartToken`)。
|
|
154
|
-
|
|
155
|
-
3. **token 读取 memo 化**(R1-G2):`observeHostRole(host, role, { sessionFile, memo, tokenMemo })`,新增 `tokenMemo: Map<pid, Promise<string|null>>`。`recoverHost` 的一次调用内对每个 pid 只 probe 一次:
|
|
156
|
-
|
|
157
|
-
```js
|
|
158
|
-
if (!tokenMemo.has(pid)) tokenMemo.set(pid, readStartTokenImpl(pid));
|
|
159
|
-
const startToken = await tokenMemo.get(pid);
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
调用点(`service.mjs:1109/1110/1152/1155` 与 resolver 的 `:1416/1417`)各创建一个 `tokenMemo`(与既有 `probeMemo` 并列,互不混用:`probeMemo` 的值是 probe 对象,`tokenMemo` 的值是字符串/null)。
|
|
163
|
-
|
|
164
|
-
**probe 次数账(Windows,单次恢复)**:
|
|
165
|
-
|
|
166
|
-
| 路径 | probe 次数 |
|
|
167
|
-
|---|---|
|
|
168
|
-
| 记录 token 为 `null`(旧记录/service 侧) | **0 次新增**(立即 `unknown` → #217 兜底,兜底本身 memo 化 ≤2 次) |
|
|
169
|
-
| 记录 token 非空(本 issue 新路径) | 首次 2 次(runner+child,各 ~390 ms)+ 重验 ≤4 次(≤2 角色 × 2 信号,见 D6/§8)= 总计 ≤6 次 |
|
|
170
|
-
| 其中 probe 挂起(3 s 超时) | 首次两角色最多 6 s;已计入 grace 预算,见 §8 |
|
|
171
|
-
|
|
172
|
-
### D5 — 兜底顺序不变(#70 契约)
|
|
173
|
-
|
|
174
|
-
观测判定顺序固定为:**token 比对优先,probe 佐证兜底,仍不可验证 → `unknown`**。任何读取失败都不得猜测成 `foreign`(`foreign` 会触发释放/新起宿主)。
|
|
175
|
-
|
|
176
|
-
### D6 — 发信号前重验(覆盖 token 路径)
|
|
177
|
-
|
|
178
|
-
`service.mjs:1005 revalidateProbeOwned` 现在对「记录 token 非空」直接 `return true`(`:1007-1008`,理由是 Linux 的 `/proc` 读取本身实时)。Windows 上 token 路径的观测来自 ~390 ms 前的 probe,与发信号之间存在 pid 复用窗口,因此:
|
|
179
|
-
|
|
180
|
-
- 记录 token 非空 → **实时重读** `readStartTokenImpl(pid)`(**绕过 `tokenMemo`**)并比对;相等才放行;
|
|
181
|
-
- 重读失败(`null`)→ **返回 `false`**(拒绝发信号)。实际后果(R2-D6 修正):观察值仍由 `tokenMemo` 的既有 promise 供出,后续轮次仍可能得 `owned`,重验持续失败直到 grace 耗尽 → 最终观察仍 `owned` → `recovery_pending`;**全程不发信号、不释放**(安全,但不会自动退化为 #217 的 probe 兜底);
|
|
182
|
-
- 记录 token 为 `null` → 保持现有 probe 重验(含写回 `probeMemo`);
|
|
183
|
-
- **token 路径重验结果不写回 memo**(避免把字符串塞进 probe 对象的 Map)。
|
|
184
|
-
|
|
185
|
-
Linux 上重读 `/proc` 是同步零成本;Windows 上每次重验 +~390 ms,只发生在即将发信号的恢复轮次(每角色最多 2 次:SIGTERM + SIGKILL,最坏 4 次)。
|
|
186
|
-
|
|
187
|
-
### D7 — 降级与失败
|
|
188
|
-
|
|
189
|
-
| 失败点 | 行为 | 验收 |
|
|
190
|
-
|---|---|---|
|
|
191
|
-
| probe 不可用(PowerShell 被禁、超时、pid 已死) | `readProcessStartToken` → `null`;记录侧写 `null`(= 现状);观测侧走 #217 兜底 | A2 / A9 |
|
|
192
|
-
| 记录 token 为 `ct:` 但观测读不到 | `unknown` → 兜底 → 仍 `unknown` 则 `recovery_pending`(不变) | A9 |
|
|
193
|
-
| 旧记录(token `null`) | 完全维持现状路径,零行为变化 | A3 |
|
|
194
|
-
| darwin / 其他平台 | `null` → 现状 | A2 |
|
|
195
|
-
| Linux 无 `/proc` 权限 | `captureStartToken` → `null` → 兜底 | 既有测试 |
|
|
196
|
-
|
|
197
|
-
## 4. 组件契约
|
|
198
|
-
|
|
199
|
-
| 模块 | 导出 | 语义 |
|
|
200
|
-
|---|---|---|
|
|
201
|
-
| `src/core/pid.mjs` | `captureStartToken(pid)` | 同步;Linux `/proc` → 字符串;其他/失败 → `null` |
|
|
202
|
-
| `src/core/process-identity-probe.mjs` | `creationTimeToken(ms)` | 纯函数;正安全整数 → `"ct:<ms>"`;否则 `null` |
|
|
203
|
-
| 同上 | `readProcessStartToken(pid, opts)` | 异步;linux → `captureStartToken`;**win32 → probe → `creationTimeToken`**;**darwin 及其他 → `null`(不调 probe)**;失败 `null`;`opts.platform` / `opts.probe` 可注入 |
|
|
204
|
-
| `src/core/host-coordination.mjs` | `processIdentityState(identity, observed, spawnedAt)` | 纯函数,签名不变(`observed.startToken` 现在是跨平台 token) |
|
|
205
|
-
| `src/runtime/service.mjs` | `createService(opts)` | `opts.observeProcess` 允许返回 Promise(`await` 兼容同步返回值);**新增** `opts.isAlive`(默认 `pid.mjs` 的 `isAlive`)与 `opts.readStartToken`(默认 `readProcessStartToken`) |
|
|
206
|
-
| 同上 | `observeHostRole(host, role, { sessionFile, memo, tokenMemo })` | `tokenMemo` 新增;`probeMemo` 语义不变 |
|
|
207
|
-
| 同上 | `revalidateProbeOwned(...)` | token 路径实时重读、不写 memo、读失败拒绝 |
|
|
208
|
-
|
|
209
|
-
## 5. 可测性拆分设计(必答)
|
|
210
|
-
|
|
211
|
-
| 拆分点 | 形态 | 测试边界 |
|
|
212
|
-
|---|---|---|
|
|
213
|
-
| `creationTimeToken(ms)` | 纯函数 | 单测:正整数/0/负数/NaN/Infinity/字符串/超大值 → `null` |
|
|
214
|
-
| `readProcessStartToken(pid, opts)` | 异步 + 注入 `platform` + `probe` | 单测:linux → `/proc` 值;win32 → `ct:`;**darwin → `null` 且 `probe` 未被调用**;probe `null` → `null`;非法 pid → 不调 probe |
|
|
215
|
-
| `processIdentityState` | 纯函数 | 既有 5 态单测;补 `ct:` token 相等/不等 |
|
|
216
|
-
| `observeProcessIdentity`(service 闭包) | 异步 + 注入 `isAlive` / `readStartToken` | 单测(经 `createService({isAlive, readStartToken})`):记录 token `null` → **不调** `readStartToken` 且 `unknown`;相等 → `owned`;不等 → `foreign`;读失败 → `unknown`;alive=false → `dead` |
|
|
217
|
-
| `observeHostRole` + `tokenMemo` | 异步编排 | 单测:同一 pid 两轮观测只 probe 一次(计数桩);`probeMemo` 与 `tokenMemo` 互不污染 |
|
|
218
|
-
| `revalidateProbeOwned` | 异步编排 | 单测:记录 token 非空 + 重读返回不同值 → 拒绝信号;重读返回 `null` → 拒绝;Linux 路径零额外开销 |
|
|
219
|
-
| `pty-runner` 记录侧 | 集成 | 现有 `pty-runner.integration.test.mjs:886` 断言非空 token(Linux `/^\d+$/`);win32 `ct:` 分支由 `readProcessStartToken` 单测 + U1 实机覆盖 |
|
|
220
|
-
|
|
221
|
-
## 6. 数据流
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
spawn 时(记录侧) 恢复时(观测侧,service)
|
|
225
|
-
───────────────────── ──────────────────────────
|
|
226
|
-
ownedMain 开头: observeHostRole(host, role)
|
|
227
|
-
selfTokenPromise = readProcessStartToken(self)
|
|
228
|
-
→ host.json.runnerIdentity.startToken │ identity == null && spawnedAt == null ──▶ not_started
|
|
229
|
-
spawn child 后: │(identity 存在则绝不判 not_started,见 D4)
|
|
230
|
-
readProcessStartToken(childPid) ▼
|
|
231
|
-
→ host.json.childIdentity.startToken pid = identity?.pid ?? host[rolePid]
|
|
232
|
-
│ null ──▶ unknown
|
|
233
|
-
▼
|
|
234
|
-
isAlive(pid)? ──no──▶ dead
|
|
235
|
-
│yes
|
|
236
|
-
▼
|
|
237
|
-
identity.startToken == null? ──yes──▶ unknown
|
|
238
|
-
│no │
|
|
239
|
-
▼ ▼
|
|
240
|
-
tokenMemo(pid) = readProcessStartToken(pid) (#217 probe 兜底)
|
|
241
|
-
│
|
|
242
|
-
▼
|
|
243
|
-
processIdentityState(...) = equal? owned : foreign
|
|
244
|
-
│
|
|
245
|
-
▼
|
|
246
|
-
发信号前 revalidate(D6,绕过 tokenMemo 重读)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## 7. 验收矩阵
|
|
250
|
-
|
|
251
|
-
| ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
|
|
252
|
-
|---|---|---|---|---|
|
|
253
|
-
| A1 | `creationTimeToken` 纯函数 | 自动化(unit) | `node --test test/process-identity-probe.test.mjs` | 合法 ms → `ct:<ms>`;0/负/非整数/非数字/超大 → `null` |
|
|
254
|
-
| A2 | 跨平台 token 读取 | 自动化(unit) | 同上,注入 `platform` + `probe` 桩 | linux 走 `/proc`;win32 返回 `ct:`;**darwin → `null` 且 probe 未被调用**;probe `null` → `null`;非法 pid 不调 probe |
|
|
255
|
-
| A3 | 观测侧 token 比对 | 自动化(unit) | `node --test test/service.test.mjs`(`createService({isAlive, readStartToken})` 注入) | 相等 → `owned`;不等 → `foreign`;记录 `null` → 不调 `readStartToken` 且 `unknown`;读失败 → `unknown` |
|
|
256
|
-
| A4 | 判定收敛 | 自动化(unit) | `test/host-coordination.test.mjs` + `test/host-recovery.test.mjs` | 既有 5 态用例全过(`(null,null,null) → not_started` 保持);新增 `(identity, {alive:true,token 匹配}, null) → owned`(不被误判 not_started);配置用例覆盖 not_started/dead/foreign |
|
|
257
|
-
| A5 | 发信号前重验 | 自动化(unit) | `test/service.test.mjs` 新增:token 非空 + 重读返回不同 token / `null` | 不发信号、流程 finalize 或 pending(按 observation 而定) |
|
|
258
|
-
| A6 | token memo 生效 | 自动化(unit) | `test/service.test.mjs` 新增:同一 pid 两轮观测 + 计数桩 | `readStartToken` 对该 pid 只调 1 次;`probeMemo` 不受影响 |
|
|
259
|
-
| A7 | 四份拷贝收敛 | 自动化(static) | `Get-ChildItem src,runner -Recurse -Filter *.mjs \| Select-String -Pattern 'captureStartToken'`(等价 `grep -rn captureStartToken src runner`) | 只有 `src/core/pid.mjs` 有定义;其余为 import/调用;`npm run typecheck` 通过 |
|
|
260
|
-
| A8 | 记录侧接线(Linux 行为不变) | 自动化(integration) | `node --test test/pty-runner.integration.test.mjs` | `runnerIdentity/childIdentity.startToken` 仍匹配 `/^\d+$/`(既有 :886 断言) |
|
|
261
|
-
| A9 | 降级链路 | 自动化(unit) | `test/service.test.mjs`:注入 `readStartToken → null` 且 `probeProcessIdentity → null` | observation 最终 `unknown` → 恢复返回 `recovery_pending`,不误判 `foreign`、不发信号 |
|
|
262
|
-
| A10 | CI 全量回归 | 自动化(build/test) | **CI(ubuntu-latest)分步全绿**:`npm run typecheck` / `npm run test:perf` / `npm test` / `npm run test:coverage` / `npm run pack:dry`(`.github/workflows/ci.yml:32-44` 的五个 `run:` 步骤,等价于 `package.json:60` 的 `npm run verify`;CI job 不直接调 verify) | 五步全绿,含 c8 门槛(lines 85 / funcs 80 / branches 70) |
|
|
263
|
-
| A11 | 本机 Windows 回归对照 | 自动化(build/test,本机) | `npm test *> baseline-after.log`,与 `research/baseline-test.log` / `research/baseline-failures.txt` 逐条对照 | 不新增失败测试(口径:`✖` 行集合差集为空;基线 1298 tests / 66 fail / 5 skip)。**不要求本机 `npm run verify` 全绿**——既有 66 个失败会让链在 test 步骤中断 |
|
|
264
|
-
| U1 | Windows 实机 token 与 pid 复用 | 用户实测 | ① 本机 `git pull` 到含 fix 的版本并重载 Pi(确认 `runner/pty-runner.mjs` 含 `readProcessStartToken`);**确认失败说明是 npm 安装形态**(`README.md:45` 的 `pi install npm:@zhuxixi/pi-agent-board` 不受本地 checkout 影响)→ 改按 `README.md:53-54` 执行 `npm install` + `pi install "$(pwd)"`(或安装含 fix 的已发布版本)后重载,再次确认;② **用新 session 或先结束旧 host 再 attach**,确保走一次全新 host 启动(新 `instanceId` 记录);③ 读 `~/.pi/agent/agent-board/views/<view>/host.json`,用 `[long]([datetimeoffset](Get-CimInstance Win32_Process -Filter "ProcessId=<pid>").CreationDate).ToUnixTimeMilliseconds()` 换算毫秒值比对(或直接跑 `research/verify-probe-self.mjs` 对照);④ 造复用(resolver 需**双角色都 ended** 才进 `recoverHost`,`service.mjs:1423`):先 `stopping` 化——把 `state` 置 `stopping`、`stopRequestedAt` 改为 `now - 6000`(≥ `HOST_RECOVERY_GRACE_MS=5000` 才过 `staleStop`,`service.mjs:1413`);再把 `runnerPid` 与 `runnerIdentity` 都改成**已死的 pid**(如 `99999999`,`startToken` 随便写 `ct:1`)→ runner 侧判 `dead`;最后把 `childPid` 与 `childIdentity` 指向**当前存活的无关进程**(`startToken` 写不匹配值如 `ct:1`)→ child 侧 token 比对不等判 `foreign`;然后 attach | ③ `runnerIdentity.startToken` / `childIdentity.startToken` 以 `ct:` 开头且与 CIM 换算值一致;④ 恢复判 `foreign` 并正常收尾:`host.json` 出现新 `instanceId` 或 state 推进、不永久 `recovery_pending`、无信号发给该无关进程(该进程仍存活);验收后把 `host.json` 改回或删掉该 view 记录,并清理失去引用的真实 child 进程(若成为孤儿) |
|
|
265
|
-
|
|
266
|
-
## 8. 风险与缓解
|
|
267
|
-
|
|
268
|
-
| 风险 | 影响 | 缓解 |
|
|
269
|
-
|---|---|---|
|
|
270
|
-
| Windows 上 `childIdentity` 使 `alive` publish 延迟 ~390 ms | 首次 attach 慢 0.4 s | 仅 Windows 新记录;Linux 零成本;CR 可评估改「先 publish 后补写」 |
|
|
271
|
-
| 锁内等待 ~300 ms(`runnerIdentity`) | start lease 持有变长 | probe 在拿锁前发起;`HOST_RUNNER_LOCK_WAIT_MS = 5 s` 兜底;实测前三步 <100 ms |
|
|
272
|
-
| **观测侧每轮 probe** | 若无 memo:每轮 2×390 ms,5 s grace 内约 10 次 PowerShell;probe 挂起(3 s 超时)时单轮 >6 s | **D4 的 `tokenMemo`**:单次恢复 ≤2 次;重验每次 1 次(≤2 角色 × 2 信号);probe 挂起最坏 2×3 s = 6 s,仍受 `HOST_RECOVERY_GRACE_MS=5 s` 轮次约束(最终观察在循环外补一次,见 `:1152-1155`) |
|
|
273
|
-
| `observeProcess` 改 async 破坏注入点 | 既有测试/调用方 | 仅一处调用(`service.mjs:986`);`await` 兼容同步返回值;`opts.observeProcess` 类型注释改为「可返回 Promise」 |
|
|
274
|
-
| `defaultObserveProcess` 是模块级函数,`opts` 到不了 | A3 的注入落空 | D4:观测组装移入 `createService` 闭包,新增 `opts.isAlive` / `opts.readStartToken` |
|
|
275
|
-
| 记录 token 非空改变 `hasIdentity` | 租约分支切换 | N2:只改 `host.json`,租约 identity 仍走 `captureStartToken`(Windows `null`) |
|
|
276
|
-
| CI 无 Windows | 平台分支无端到端 | 注入式单测(A1–A3/A5/A6/A9)+ U1 实机 |
|
|
277
|
-
|
|
278
|
-
## 9. 参考
|
|
279
|
-
|
|
280
|
-
- #217 设计:`docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md`(probe / `classifyUnverifiableProcess` 的来源;其 U1 用「手改 host.json」造复用)
|
|
281
|
-
- #224 设计:`docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md:42,52`(本 issue 的范围划界)
|
|
282
|
-
- #70 契约:`docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md:131-146`
|
|
283
|
-
- 调研留档:`~/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/research/01-census-and-scope.md`
|
|
284
|
-
- 实测脚本:`~/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/research/verify-probe-self.mjs`
|
|
285
|
-
- **本机基线**(A11 对照基准):`~/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/research/baseline-test.log`(1298 tests / 1227 pass / 66 fail / 5 skip)与 `baseline-failures.txt`(133 条 `✖` 行,含子测试)
|
|
286
|
-
- 第 1 轮复核报告:`~/.github-issue-driven/zhuxixi/pi-agent-board/issue-238/research/spec-review-round-1.md`
|