Repository navigation
fix(cli): verify process identity before killing tracked watch PIDs - #1665
Conversation
Fixes github#1641 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🛫 PR Readiness Check
PR Scope: 📦🔧 Mixed (product + infrastructure)
|
| Status | Check | Details |
|---|---|---|
| ❌ | Single commit | 2 commits — consider squashing before review |
| ✅ | Not in draft | Ready for review |
| ✅ | Branch up to date | Up to date with dev |
| ❌ | Copilot review | No Copilot review yet — it may still be processing |
| ✅ | Changeset present | Changeset file found |
| ✅ | Scope clean | No .squad/ or docs/proposals/ files |
| ✅ | No merge conflicts | No merge conflicts |
| ✅ | Copilot threads resolved | No Copilot review threads |
| ❌ | CI passing | 9 check(s) still running |
Files Changed (3 files, +176 −0)
| File | +/− |
|---|---|
.changeset/1641-pid-tracker-identity-check.md |
+5 −0 |
packages/squad-cli/src/cli/commands/watch/pid-tracker.ts |
+62 −0 |
test/cli/watch-pid-tracker.test.ts |
+109 −0 |
Total: +176 −0
This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.
🟡 Impact Analysis — PR #1665Risk tier: 🟡 MEDIUM 📊 Summary
🎯 Risk Factors
📦 Modules Affectedroot (1 file)
squad-cli (1 file)
tests (1 file)
This report is generated automatically for every PR. See #733 for details. |
|
the one CI test failure is pre-existing, unrelated to this PR — |
|
👋 Friendly nudge — this PR has had no activity for 8 days. What needs attention:
If this PR is abandoned, please close it. If it's blocked on something external, leave a comment so the team knows. |
What
squad watch'sPidTrackernow cross-checks a live process's real OS start time against the trackedspawnedAtbefore killing it incleanup()/cleanupStale(), instead of trusting a bare PID number.Why
Fixes #1641
isAlive()isprocess.kill(pid, 0)andkillTree()kills by PID alone —name/spawnedAtwere only ever used in the post-kill log line, never checked before the kill. If watch dies without cleaning up.watch-pidsand the OS reassigns that PID to something unrelated before the nextcleanupStale()run, the tracker kills whatever now owns that number. Repro is in the issue: a.watch-pidsentry claiming a month-old spawn gets killed against a process that actually started 90 seconds ago, no contradiction check anywhere in the path.How
getProcessStartTime(pid): looks up the OS's real start time for a PID —wmic process where ProcessId=X get CreationDateon Windows (parses the CIM_DATETIME UTC-offset field properly, not just the local wall-clock digits),ps -o lstart= -p Xelsewhere. Returnsnullif it can't be determined.matchesTrackedIdentity(proc): compares that againstproc.spawnedAtwith a 5-minute tolerance (spawn/scheduling delay, not the minutes-to-months drift a real PID reuse produces). Fails open (returnstrue) when the start time can't be determined, so normal cleanup isn't blocked whenwmic/psaren't available.cleanup()andcleanupStale(): both call the check right beforekillTree(); on mismatch they log a warning and skip the kill instead of proceeding.Testing
Added a
identity check (#1641)block to the existingtest/cli/watch-pid-tracker.test.tssuite, same mock style as the rest of the file (execFileSync mocked at module level,process.killspied per-test):wmic/ps)wmicCreationDateresponse and a real POSIXps -o lstart=response into the correct UTC instantRan the broader watch suite too —
test/cli/watch.test.tshit its known cold dynamic-import 5s timeout (3 tests), confirmed pre-existing by rerunning the same file with--testTimeout=20000, all 6 pass. Not caused by this change; same quirk documented on prior PRs against this repo.cd packages/squad-sdk && npm run buildthencd packages/squad-cli && npm run build(both package-local, no root prebuild churn) — clean,tsc --noEmit -p packages/squad-cli/tsconfig.jsonclean.eslinton both changed files: 0 errors, 12 warnings, alln/no-sync/no-consoleon the same categories the file already triggered before this change (execFileSync,console.log) — no new warning categories.Diff hygiene:
git diffvsgit diff -widentical,git diff --checkclean — hit the usual CRLF drift on this box (working copy silently went CRLF on both edited files despite LF-committed blobs), normalized back to LF and reverified againstgit show HEAD:<path>before staging..changeset/1641-pid-tracker-identity-check.md(patch@bradygaster/squad-cli)PR Readiness Checklist
Branch & Commit
dev(upstream, fresh fetch)devBuild & Test
npm run build(per-package) passesnpm test— targeted suite 16/16; broader watch suite checked, only the known pre-existing cold-start timeouttsc --noEmitcleaneslint— 0 errors, pre-existing warning categories onlyChangeset
Docs
Exports
Breaking Changes
None.
matchesTrackedIdentityis additive and private; the only externally visible change is that a kill can now be skipped (with a logged warning) in the specific case where the live process contradicts the tracked record.Waivers
None.