@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.
- package/CHANGELOG.md +13 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +53 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +276 -0
- package/package.json +5 -11
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +14 -8
- 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
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
# Issue #217 — Precise Host Identity Corroboration 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:** On platforms where `captureStartToken()` is `null` (Windows, macOS), refine an `unknown` host-role observation with deterministic evidence — role-specific command-line match, falling back to process creation time vs `*SpawnedAt` — so a reused pid no longer strands a `stopping` host, and `recovery_pending` is never silent.
|
|
6
|
-
|
|
7
|
-
**Architecture:** The decision is a pure function in `src/core/host-coordination.mjs`. A new I/O shell `src/core/process-identity-probe.mjs` runs one async `execFile` per pid (Windows CIM JSON, macOS `ps`) behind injectable seams. `service.mjs` keeps all wiring: `observeHostRole` becomes async, receives `{ sessionFile, memo }`, and only probes when the fast token path returns `unknown`. Diagnostics use a new `appendDiagnosticOnce`.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** Node ESM `.mjs`, `node:test` + `assert/strict`, `node:child_process.execFile` (async, `windowsHide: true`), PowerShell `Get-CimInstance Win32_Process`, POSIX `ps -p <pid> -o etime=,command=`.
|
|
10
|
-
|
|
11
|
-
**Spec:** `docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md`
|
|
12
|
-
|
|
13
|
-
**Worktree:** `C:/Users/27499/proj/opensource/pi-agent-board/.pi/worktrees/issue-217-windows-pid-reuse-stopping` — every path below is relative to it; never edit the main checkout.
|
|
14
|
-
|
|
15
|
-
## Global Constraints
|
|
16
|
-
|
|
17
|
-
- Tabs for indentation; English comments; conventional commit subjects (`(#217)` lands on the PR, not on local commits).
|
|
18
|
-
- `host-coordination.mjs` stays pure: no fs/net/process I/O, no `resolveTestMs` import.
|
|
19
|
-
- `probe == null` MUST return `unknown` — never `foreign`. This is the #70 contract.
|
|
20
|
-
- Role-specific fragment: child → `meta.sessionFile`, runner → host `configPath`. Never use `sessionFile` for the runner.
|
|
21
|
-
- Do not change `processIdentityState`, `captureStartToken` (all copies), lock identity, or `serviceIdentity()`.
|
|
22
|
-
- `recoverHost(viewId, expectedInstanceId, sessionFile?)` — third parameter optional so existing two-arg calls keep working.
|
|
23
|
-
- Probe memo is created by the caller and shared across the loop and the final re-check; the resolver stopping branch creates its own local memo.
|
|
24
|
-
- Every spawn/exec uses `windowsHide: true` and a `timeout`.
|
|
25
|
-
- `git add <explicit files>` only.
|
|
26
|
-
- Gate: `npm run typecheck` + the listed test files; `npm run verify` if the local node-pty environment allows it (known Windows `AttachConsole failed` limitation).
|
|
27
|
-
|
|
28
|
-
## Review Focus
|
|
29
|
-
|
|
30
|
-
1. **Process dies between `isAlive` and the probe** — `probe == null` must stay `unknown` for that round; the next poll's `defaultObserveProcess` classifies `dead`. → A8 (Task 3).
|
|
31
|
-
2. **Path prefix trap** (`session.jsonl` inside `session-2.jsonl`, `view_abc` inside `view_abcd`) — matching must be boundary-aware, not a raw `includes`. → A1 (Task 1).
|
|
32
|
-
3. **Mixed slash/case/quoting in command lines** — Windows paths arrive as `C:\…` inside `"…"`; comparison normalizes slashes and case. → A1 (Task 1).
|
|
33
|
-
4. **Command line unreadable (permission / system process)** — fall back to the creation-time window; a missing `spawnedAt` yields `unknown`. → A2 (Task 1).
|
|
34
|
-
5. **Clock step between spawn and recovery** — creation-time fallback uses a 5 s tolerance and only applies when the command-line signal is unavailable. → A2/A15 (Task 1).
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
### Task 1: Pure decision functions + tolerance knob
|
|
39
|
-
|
|
40
|
-
**Files:**
|
|
41
|
-
- Modify: `src/core/host-coordination.mjs` (append after `processIdentityState`)
|
|
42
|
-
- Modify: `src/runtime/service.mjs` (add the constant + reader next to `resolveHostRecoveryPollMs` at :90)
|
|
43
|
-
- Test: `test/host-coordination.test.mjs`, `test/test-knobs.test.mjs`
|
|
44
|
-
|
|
45
|
-
**Interfaces:**
|
|
46
|
-
- Produces:
|
|
47
|
-
- `export function commandLineMentions(commandLine: string, fragment: string): boolean`
|
|
48
|
-
- `export function classifyUnverifiableProcess({ spawnedAt, probe, expectedCommandFragment, toleranceMs }): "owned" | "foreign" | "unknown"`
|
|
49
|
-
- `export const HOST_IDENTITY_CREATION_TOLERANCE_MS = 5_000` (in `service.mjs`)
|
|
50
|
-
- `export function resolveIdentityCreationToleranceMs(env = process.env): number` (in `service.mjs`)
|
|
51
|
-
- `probe` shape: `{ creationTimeMs: number|null, commandLine: string|null } | null`.
|
|
52
|
-
|
|
53
|
-
- [ ] **Step 1: Write the failing truth-table tests**
|
|
54
|
-
|
|
55
|
-
In `test/host-coordination.test.mjs`, add:
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
test("commandLineMentions normalizes slashes/case and respects path boundaries", () => {
|
|
59
|
-
assert.equal(commandLineMentions('node C:\\x\\cli.js --session "C:\\a b\\s.jsonl"', "C:/a b/s.jsonl"), true);
|
|
60
|
-
assert.equal(commandLineMentions("NODE --session c:\\A\\S.JSONL", "C:\\a\\s.jsonl"), true);
|
|
61
|
-
// prefix trap: view_abc must not match view_abcd
|
|
62
|
-
assert.equal(commandLineMentions("node x --session C:\\v\\view_abcd.jsonl", "C:\\v\\view_abc.jsonl"), false);
|
|
63
|
-
assert.equal(commandLineMentions("node x --session C:\\v\\session-2.jsonl", "C:\\v\\session.jsonl"), false);
|
|
64
|
-
assert.equal(commandLineMentions("", "C:\\v\\s.jsonl"), false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("classifyUnverifiableProcess truth table", () => {
|
|
68
|
-
const probe = (creationTimeMs, commandLine) => ({ creationTimeMs, commandLine });
|
|
69
|
-
const base = { spawnedAt: 1_000_000, expectedCommandFragment: "C:\\v\\s.jsonl", toleranceMs: 5_000 };
|
|
70
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: null }), "unknown");
|
|
71
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: probe(9_999_999, "node --session C:\\v\\s.jsonl") }), "owned");
|
|
72
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: probe(9_999_999, "node other.js") }), "foreign");
|
|
73
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: probe(1_002_000, null) }), "owned");
|
|
74
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: probe(900_000, null) }), "foreign");
|
|
75
|
-
assert.equal(classifyUnverifiableProcess({ ...base, probe: probe(1_020_000, null) }), "foreign");
|
|
76
|
-
// command line wins over a null spawnedAt
|
|
77
|
-
assert.equal(classifyUnverifiableProcess({ ...base, spawnedAt: null, probe: probe(5, "node --session C:\\v\\s.jsonl") }), "owned");
|
|
78
|
-
// no signal at all
|
|
79
|
-
assert.equal(classifyUnverifiableProcess({ ...base, spawnedAt: null, probe: probe(1_000_000, null) }), "unknown");
|
|
80
|
-
assert.equal(classifyUnverifiableProcess({ ...base, expectedCommandFragment: null, probe: probe(1_000_000, "node x") }), "owned");
|
|
81
|
-
});
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
In `test/test-knobs.test.mjs`, extend both reader tests:
|
|
85
|
-
|
|
86
|
-
```js
|
|
87
|
-
assert.equal(HOST_IDENTITY_CREATION_TOLERANCE_MS, 5_000);
|
|
88
|
-
assert.equal(resolveIdentityCreationToleranceMs({}), 5_000);
|
|
89
|
-
assert.equal(resolveIdentityCreationToleranceMs({ AGENT_BOARD_TEST_IDENTITY_CREATION_TOLERANCE_MS: "25" }), 25);
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
- [ ] **Step 2: Run to verify they fail**
|
|
93
|
-
|
|
94
|
-
Run: `node --test test/host-coordination.test.mjs test/test-knobs.test.mjs`
|
|
95
|
-
Expected: FAIL — `commandLineMentions is not a function` / import error for the new reader.
|
|
96
|
-
|
|
97
|
-
- [ ] **Step 3: Implement the pure functions**
|
|
98
|
-
|
|
99
|
-
`host-coordination.mjs`: `commandLineMentions` normalizes `\`→`/` and lowercases both sides, then scans every occurrence and accepts only when both neighbours are start/end or whitespace/quote (boundary-aware). `classifyUnverifiableProcess` follows the spec D1 pseudo-code exactly: `probe == null → unknown`; command line + fragment → `owned`/`foreign`; else creation window `|creation - spawnedAt| <= tolerance → owned`, otherwise `foreign`; missing `spawnedAt`/`creation` → `unknown`.
|
|
100
|
-
|
|
101
|
-
`service.mjs`: add the constant and reader (uses `resolveTestMs`) beside the existing readers.
|
|
102
|
-
|
|
103
|
-
- [ ] **Step 4: Run to verify they pass**
|
|
104
|
-
|
|
105
|
-
Run: `node --test test/host-coordination.test.mjs test/test-knobs.test.mjs`
|
|
106
|
-
Expected: PASS.
|
|
107
|
-
|
|
108
|
-
- [ ] **Step 5: Commit**
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
git add src/core/host-coordination.mjs src/runtime/service.mjs test/host-coordination.test.mjs test/test-knobs.test.mjs
|
|
112
|
-
git commit -m "feat(core): add identity corroboration pure functions and tolerance knob (issue #217)"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
**Acceptance:** A1, A2, A15.
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
### Task 2: Process identity probe module
|
|
120
|
-
|
|
121
|
-
**Files:**
|
|
122
|
-
- Create: `src/core/process-identity-probe.mjs`
|
|
123
|
-
- Create: `test/process-identity-probe.test.mjs`
|
|
124
|
-
|
|
125
|
-
**Interfaces:**
|
|
126
|
-
- Produces:
|
|
127
|
-
- `export const PROBE_TIMEOUT_MS = 3_000`
|
|
128
|
-
- `export function parseUnixMillis(text: string): number|null`
|
|
129
|
-
- `export function parseProbeJson(text: string): {creationTimeMs:number|null, commandLine:string|null}|null`
|
|
130
|
-
- `export function parsePsElapsedMs(text: string): number|null`
|
|
131
|
-
- `export function parsePsProbeLine(text: string, nowMs: number): {creationTimeMs:number|null, commandLine:string|null}|null`
|
|
132
|
-
- `export function resolveProbeCommand(platform: string, pid: number): {command:string, args:string[]}|null`
|
|
133
|
-
- `export async function readProcessIdentityProbe(pid, { platform = process.platform, execFileFn = execFile, timeoutMs = PROBE_TIMEOUT_MS, nowMs = Date.now } = {}): Promise<{creationTimeMs:number|null, commandLine:string|null}|null>`
|
|
134
|
-
- Consumes nothing from Task 1.
|
|
135
|
-
|
|
136
|
-
- [ ] **Step 1: Write the failing parser/command tests**
|
|
137
|
-
|
|
138
|
-
In `test/process-identity-probe.test.mjs`:
|
|
139
|
-
|
|
140
|
-
```js
|
|
141
|
-
test("parseUnixMillis accepts positive integers only", () => {
|
|
142
|
-
assert.equal(parseUnixMillis("1792718500139"), 1_792_718_500_139);
|
|
143
|
-
assert.equal(parseUnixMillis(""), null);
|
|
144
|
-
assert.equal(parseUnixMillis("abc"), null);
|
|
145
|
-
assert.equal(parseUnixMillis("-5"), null);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
test("parseProbeJson parses the CIM object and rejects junk", () => {
|
|
149
|
-
assert.deepEqual(parseProbeJson('{"creation":1792718500139,"cmd":"node x"}'),
|
|
150
|
-
{ creationTimeMs: 1_792_718_500_139, commandLine: "node x" });
|
|
151
|
-
assert.deepEqual(parseProbeJson('{"creation":null,"cmd":null}'), { creationTimeMs: null, commandLine: null });
|
|
152
|
-
assert.equal(parseProbeJson("not json"), null);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test("parsePsElapsedMs handles every etime shape", () => {
|
|
156
|
-
assert.equal(parsePsElapsedMs("05:03"), 303_000);
|
|
157
|
-
assert.equal(parsePsElapsedMs("01:02:03"), 3_723_000);
|
|
158
|
-
assert.equal(parsePsElapsedMs("2-01:02:03"), 176_523_000);
|
|
159
|
-
assert.equal(parsePsElapsedMs("nope"), null);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test("parsePsProbeLine splits elapsed from the command", () => {
|
|
163
|
-
assert.deepEqual(parsePsProbeLine("05:03 node /x --session y z", 1_000_000),
|
|
164
|
-
{ creationTimeMs: 697_000, commandLine: "node /x --session y z" });
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test("resolveProbeCommand is platform- and pid-aware", () => {
|
|
168
|
-
assert.match(resolveProbeCommand("win32", 123).args.at(-1), /ProcessId=123/);
|
|
169
|
-
assert.deepEqual(resolveProbeCommand("darwin", 123), { command: "ps", args: ["-p", "123", "-o", "etime=,command="] });
|
|
170
|
-
assert.equal(resolveProbeCommand("linux", 123), null);
|
|
171
|
-
assert.equal(resolveProbeCommand("win32", 0), null);
|
|
172
|
-
assert.equal(resolveProbeCommand("win32", 1.5), null);
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
- [ ] **Step 2: Run to verify they fail**
|
|
177
|
-
|
|
178
|
-
Run: `node --test test/process-identity-probe.test.mjs`
|
|
179
|
-
Expected: FAIL — module not found.
|
|
180
|
-
|
|
181
|
-
- [ ] **Step 3: Implement the parsers and command resolver**
|
|
182
|
-
|
|
183
|
-
Windows command: `powershell.exe -NoProfile -NonInteractive -Command "$p = Get-CimInstance Win32_Process -Filter \"ProcessId=<pid>\" -ErrorAction SilentlyContinue; if ($p) { [pscustomobject]@{creation=[long]([datetimeoffset]$p.CreationDate).ToUnixTimeMilliseconds(); cmd=$p.CommandLine} | ConvertTo-Json -Compress }"`. macOS: `ps -p <pid> -o etime=,command=`. Other platforms: `null`.
|
|
184
|
-
|
|
185
|
-
- [ ] **Step 4: Write the failing execution-layer tests**
|
|
186
|
-
|
|
187
|
-
```js
|
|
188
|
-
test("readProcessIdentityProbe parses stdout and honors exec options", async () => {
|
|
189
|
-
const calls = [];
|
|
190
|
-
const execFileFn = (cmd, args, opts, cb) => {
|
|
191
|
-
calls.push({ cmd, args, opts });
|
|
192
|
-
cb(null, '{"creation":1792718500139,"cmd":"node x --session y"}\n', "");
|
|
193
|
-
};
|
|
194
|
-
const probe = await readProcessIdentityProbe(123, { platform: "win32", execFileFn });
|
|
195
|
-
assert.deepEqual(probe, { creationTimeMs: 1_792_718_500_139, commandLine: "node x --session y" });
|
|
196
|
-
assert.equal(calls[0].opts.windowsHide, true);
|
|
197
|
-
assert.equal(calls[0].opts.timeout, 3_000);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test("readProcessIdentityProbe returns null on failure/empty output", async () => { /* non-zero exit, timeout, empty stdout → null */ });
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
- [ ] **Step 5: Run to verify they fail, then implement the I/O shell**
|
|
204
|
-
|
|
205
|
-
Run: `node --test test/process-identity-probe.test.mjs` → FAIL.
|
|
206
|
-
Implement `readProcessIdentityProbe` with `execFile(command, args, { timeout, windowsHide: true, maxBuffer: 64 * 1024 }, cb)` wrapped in a promise; non-zero exit / error / empty trimmed stdout → `null`.
|
|
207
|
-
|
|
208
|
-
- [ ] **Step 6: Run to verify they pass + commit**
|
|
209
|
-
|
|
210
|
-
Run: `node --test test/process-identity-probe.test.mjs` → PASS.
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
git add src/core/process-identity-probe.mjs test/process-identity-probe.test.mjs
|
|
214
|
-
git commit -m "feat(core): add cross-platform process identity probe (issue #217)"
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
**Acceptance:** A3, A4, A5.
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
### Task 3: Wire the probe into recovery
|
|
222
|
-
|
|
223
|
-
**Files:**
|
|
224
|
-
- Modify: `src/runtime/service.mjs` (`createService` opts at :100-160; `observeHostRole` :961; `recoverHost` :982-1050; resolver stopping branch :1304-1318; final re-check :1046)
|
|
225
|
-
- Test: `test/host-recovery.test.mjs`, `test/host-resolver.test.mjs`
|
|
226
|
-
|
|
227
|
-
**Interfaces:**
|
|
228
|
-
- Consumes: `classifyUnverifiableProcess`, `resolveIdentityCreationToleranceMs` (Task 1); `readProcessIdentityProbe` (Task 2).
|
|
229
|
-
- Produces: `createService({ probeProcessIdentity })` injection point; `observeHostRole(host, role, { sessionFile, memo })` async.
|
|
230
|
-
|
|
231
|
-
- [ ] **Step 1: Update existing unknown→pending tests to inject a null probe**
|
|
232
|
-
|
|
233
|
-
In `test/host-recovery.test.mjs:179` and `test/host-resolver.test.mjs:350`, add `probeProcessIdentity: async () => null` to the `service(...)` overrides. Run both files; the existing assertions must still pass (they will fail after Step 3 without this).
|
|
234
|
-
|
|
235
|
-
- [ ] **Step 2: Write the failing recovery tests**
|
|
236
|
-
|
|
237
|
-
Add to `test/host-recovery.test.mjs`:
|
|
238
|
-
|
|
239
|
-
```js
|
|
240
|
-
test("recoverHost releases an unverifiable reused pid when the command line is foreign", async () => {
|
|
241
|
-
// host record: stopping, runnerIdentity/childIdentity startToken null, runnerSpawnedAt/childSpawnedAt set
|
|
242
|
-
// observeProcess: () => "unknown"; probeProcessIdentity: async () => ({ creationTimeMs: 9, commandLine: "node other.js" })
|
|
243
|
-
// expect: res.ok === true, res.recovered === true, signals.length === 0, new instance claimed
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
test("recoverHost signals an unverifiable owned pid when the command line matches", async () => {
|
|
247
|
-
// same record, probe commandLine includes the view's sessionFile
|
|
248
|
-
// expect: exactly one SIGTERM to the owned identity, replacement claimed
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test("recoverHost probes at most twice per call (shared memo)", async () => {
|
|
252
|
-
// probeProcessIdentity counts calls; runner+child both unknown
|
|
253
|
-
// expect probeCalls <= 2
|
|
254
|
-
});
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
Add to `test/host-resolver.test.mjs`:
|
|
258
|
-
|
|
259
|
-
```js
|
|
260
|
-
test("resolver advances a stranded stopping host when the child command line is foreign (issue #217)", async () => {
|
|
261
|
-
// stopping host, staleStop true, observeProcess unknown, probe foreign, probeHost scripted ["missing","ready"]
|
|
262
|
-
// expect result.kind === "pty" (no recovery_pending)
|
|
263
|
-
});
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
- [ ] **Step 3: Run to verify they fail**
|
|
267
|
-
|
|
268
|
-
Run: `node --test test/host-recovery.test.mjs test/host-resolver.test.mjs`
|
|
269
|
-
Expected: FAIL — the foreign cases still return `recovery_pending`; memo count unbounded.
|
|
270
|
-
|
|
271
|
-
- [ ] **Step 4: Implement the wiring**
|
|
272
|
-
|
|
273
|
-
Follow spec D3 exactly:
|
|
274
|
-
- `const probeProcessIdentityImpl = opts.probeProcessIdentity ?? readProcessIdentityProbe;`
|
|
275
|
-
- `async function observeHostRole(host, role, { sessionFile, memo })` — fast path unchanged; on `unknown` with a pid, `memo.set(pid, await probeProcessIdentityImpl(pid))` once, then `classifyUnverifiableProcess({ spawnedAt, probe, expectedCommandFragment: role === "child" ? sessionFile : host?.configPath ?? null, toleranceMs: resolveIdentityCreationToleranceMs() })`.
|
|
276
|
-
- `recoverHost(viewId, expectedInstanceId, sessionFile = loadRow(root, viewId)?.meta.sessionFile ?? null)` creates `const probeMemo = new Map()` and awaits every `observeHostRole(..., { sessionFile, memo: probeMemo })` (loop + final re-check).
|
|
277
|
-
- Resolver stopping branch creates its own local memo and passes its existing `sessionFile`; the resolver's `recoverHost` calls also pass `sessionFile`.
|
|
278
|
-
|
|
279
|
-
- [ ] **Step 5: Run to verify they pass**
|
|
280
|
-
|
|
281
|
-
Run: `node --test test/host-recovery.test.mjs test/host-resolver.test.mjs`
|
|
282
|
-
Expected: PASS (including the pre-existing unknown→pending assertions).
|
|
283
|
-
|
|
284
|
-
- [ ] **Step 6: Commit**
|
|
285
|
-
|
|
286
|
-
```bash
|
|
287
|
-
git add src/runtime/service.mjs test/host-recovery.test.mjs test/host-resolver.test.mjs
|
|
288
|
-
git commit -m "fix(host): corroborate unverifiable process identity before stranding recovery (issue #217)"
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
**Acceptance:** A6, A7, A8, A9, A11, A13.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
### Task 4: Recovery-pending diagnostics
|
|
296
|
-
|
|
297
|
-
**Files:**
|
|
298
|
-
- Modify: `src/core/diagnostics.mjs` (add `appendDiagnosticOnce`)
|
|
299
|
-
- Modify: `src/runtime/service.mjs` (`:1018`, `:1047`, `:1308`)
|
|
300
|
-
- Test: `test/diagnostics.test.mjs`, `test/host-recovery.test.mjs`
|
|
301
|
-
|
|
302
|
-
**Interfaces:**
|
|
303
|
-
- Produces: `export function appendDiagnosticOnce(root, viewId, patch, opts = {}): DiagnosticEvent|null` — `opts.match` (array of `details` keys, default `[]`), `opts.limit` (default 20); returns `null` when a matching recent event exists.
|
|
304
|
-
|
|
305
|
-
- [ ] **Step 1: Write the failing unit test**
|
|
306
|
-
|
|
307
|
-
```js
|
|
308
|
-
test("appendDiagnosticOnce writes once per match key", () => {
|
|
309
|
-
const root = mkdtempSync(join(tmpdir(), "agentview-diag-"));
|
|
310
|
-
createView(root, { id: "v1", name: "a", cwd: "/r" });
|
|
311
|
-
const patch = { source: "service", level: "warn", code: "host_recovery_pending", details: { instanceId: "i1", role: "child" } };
|
|
312
|
-
assert.ok(appendDiagnosticOnce(root, "v1", patch, { match: ["instanceId", "role"] }));
|
|
313
|
-
assert.equal(appendDiagnosticOnce(root, "v1", patch, { match: ["instanceId", "role"] }), null);
|
|
314
|
-
assert.ok(appendDiagnosticOnce(root, "v1", { ...patch, details: { instanceId: "i2", role: "child" } }, { match: ["instanceId", "role"] }));
|
|
315
|
-
});
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
- [ ] **Step 2: Run to verify it fails**
|
|
319
|
-
|
|
320
|
-
Run: `node --test test/diagnostics.test.mjs` → FAIL (no export).
|
|
321
|
-
|
|
322
|
-
- [ ] **Step 3: Implement and wire**
|
|
323
|
-
|
|
324
|
-
`appendDiagnosticOnce` reads the tail (default 20) via `readJsonlTail` + `normalizeDiagnostic`, returns `null` on a same-code same-key match, otherwise appends. In `service.mjs`, before the three producing returns, call it with `code: "host_recovery_pending"`, `details: { instanceId, role, observation, probe: probe ? "available" : "unavailable", path: "recover" }` (or `"attach"` at :1308), `{ match: ["instanceId", "role"] }`. Do not add it to the two relay returns (:1315, :1383).
|
|
325
|
-
|
|
326
|
-
- [ ] **Step 4: Write the failing integration assertion**
|
|
327
|
-
|
|
328
|
-
In `test/host-recovery.test.mjs`, extend the unknown→pending test: assert `readDiagnostics(root, "v1").some((e) => e.code === "host_recovery_pending")`, then call `recoverHost` again and assert the count did not grow.
|
|
329
|
-
|
|
330
|
-
- [ ] **Step 5: Run to verify they pass + commit**
|
|
331
|
-
|
|
332
|
-
Run: `node --test test/diagnostics.test.mjs test/host-recovery.test.mjs` → PASS.
|
|
333
|
-
|
|
334
|
-
```bash
|
|
335
|
-
git add src/core/diagnostics.mjs src/runtime/service.mjs test/diagnostics.test.mjs test/host-recovery.test.mjs
|
|
336
|
-
git commit -m "feat(diagnostics): record host recovery pending with dedupe (issue #217)"
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
**Acceptance:** A10.
|
|
340
|
-
|
|
341
|
-
---
|
|
342
|
-
|
|
343
|
-
### Task 5: Verification and manual-verification record
|
|
344
|
-
|
|
345
|
-
**Files:** none (verification only)
|
|
346
|
-
|
|
347
|
-
- [ ] **Step 1: Typecheck**
|
|
348
|
-
|
|
349
|
-
Run: `npm run typecheck` → 0 errors.
|
|
350
|
-
|
|
351
|
-
- [ ] **Step 2: Run the targeted suites**
|
|
352
|
-
|
|
353
|
-
Run: `node --test test/host-recovery.test.mjs test/host-resolver.test.mjs test/host-coordination.test.mjs test/process-identity-probe.test.mjs test/diagnostics.test.mjs test/test-knobs.test.mjs` → all pass.
|
|
354
|
-
|
|
355
|
-
- [ ] **Step 3: Attempt the full gate and record the environment verdict**
|
|
356
|
-
|
|
357
|
-
Run: `npm run verify`. If it fails only on the known Windows `AttachConsole failed` / node-pty paths (JFox `202609051440498495`), record the failing files and the reason in the PR body; otherwise fix.
|
|
358
|
-
|
|
359
|
-
- [ ] **Step 4: Record U1/U2 status honestly**
|
|
360
|
-
|
|
361
|
-
Write the U1/U2 results (or `pending` with the exact blocker) into the PR body. Do not claim manual verification without observed evidence.
|
|
362
|
-
|
|
363
|
-
- [ ] **Step 5: Commit any verification-only documentation, if needed**
|
|
364
|
-
|
|
365
|
-
`git add <explicit files> && git commit -m "test: record issue #217 verification results"` only if a file changed.
|
|
366
|
-
|
|
367
|
-
**Acceptance:** A12, A14, U1, U2.
|