instar 1.3.409 → 1.3.411
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +13 -3
- package/dist/commands/server.js.map +1 -1
- package/dist/lifeline/MessageQueue.d.ts +22 -2
- package/dist/lifeline/MessageQueue.d.ts.map +1 -1
- package/dist/lifeline/MessageQueue.js +41 -1
- package/dist/lifeline/MessageQueue.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +2 -2
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/messaging/MessageProcessingLedger.d.ts +22 -0
- package/dist/messaging/MessageProcessingLedger.d.ts.map +1 -1
- package/dist/messaging/MessageProcessingLedger.js +58 -6
- package/dist/messaging/MessageProcessingLedger.js.map +1 -1
- package/dist/messaging/ingressDedup.d.ts +3 -1
- package/dist/messaging/ingressDedup.d.ts.map +1 -1
- package/dist/messaging/ingressDedup.js +1 -1
- package/dist/messaging/ingressDedup.js.map +1 -1
- package/dist/messaging/stuckMessageRecovery.d.ts +13 -2
- package/dist/messaging/stuckMessageRecovery.d.ts.map +1 -1
- package/dist/messaging/stuckMessageRecovery.js +20 -4
- package/dist/messaging/stuckMessageRecovery.js.map +1 -1
- package/dist/monitoring/SessionWatchdog.d.ts +1 -1
- package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
- package/dist/monitoring/SessionWatchdog.js +55 -25
- package/dist/monitoring/SessionWatchdog.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +5 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/upgrades/1.3.411.md +93 -0
- package/upgrades/side-effects/stuck-recovery-replay-dedupe.md +88 -0
- package/upgrades/side-effects/watchdog-nonblocking-scans.md +81 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Side-Effects Review — SessionWatchdog non-blocking process scans
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `watchdog-nonblocking-scans`
|
|
4
|
+
**Date:** `2026-06-07`
|
|
5
|
+
**Author:** `Echo`
|
|
6
|
+
**Tier:** 1 (internal monitor refactor; no API/route/config/migration surface)
|
|
7
|
+
**Second-pass reviewer:** `Echo (self) — Tier-1; the behavior-equivalence + ordering analysis below is load-bearing`
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
`SessionWatchdog` polled every 30s over EVERY running session, running several
|
|
12
|
+
`ps`/`pgrep` probes per session via **synchronous** `spawnSync` (5s timeout each).
|
|
13
|
+
Each probe blocked the single Node event loop for its full duration; under load
|
|
14
|
+
(dozens of sessions, a busy box) the cumulative stall made the server miss its own
|
|
15
|
+
`/health` window → false "server temporarily down" + restart loop (2026-06-07 topic
|
|
16
|
+
21816 incident). This converts the watchdog's process scans to async (`execFile`) and
|
|
17
|
+
makes the poll yield the loop between sessions. File: `src/monitoring/SessionWatchdog.ts`.
|
|
18
|
+
|
|
19
|
+
- `shellExec` (spawnSync) → `shellExecAsync` (`promisify(execFile)`); returns captured
|
|
20
|
+
stdout or '' on non-zero exit/timeout — identical to the prior `.stdout ?? ''`.
|
|
21
|
+
- `getFrameworkPid`, `getClaudePid`, `getChildProcesses`, `hasActivePipelineSibling`,
|
|
22
|
+
`checkCompactionIdle` are now `async`; all internal callers (`checkSession`,
|
|
23
|
+
`checkCompactionIdle`) `await` them.
|
|
24
|
+
- `poll()` `await new Promise(setImmediate)` between sessions so a scan over many
|
|
25
|
+
sessions can never monopolize the loop.
|
|
26
|
+
|
|
27
|
+
## Decision-point inventory
|
|
28
|
+
|
|
29
|
+
- The watchdog's escalation/kill decisions are UNCHANGED — only the I/O mechanism of
|
|
30
|
+
the read-only process probes changed (sync → async). Same commands, same parsing,
|
|
31
|
+
same thresholds, same LLM gate.
|
|
32
|
+
- No message block/allow surface. No new route/config/migration.
|
|
33
|
+
|
|
34
|
+
## 1. Over-block (escalating a session that shouldn't be)
|
|
35
|
+
|
|
36
|
+
Behavior is preserved: the probe commands, output parsing, stuck-thresholds, pipeline
|
|
37
|
+
guard, and LLM gate are byte-for-byte the same. `execFile`'s non-zero-exit throw is
|
|
38
|
+
caught and mapped to the captured stdout (or '') — matching `spawnSync(...).stdout ??
|
|
39
|
+
''`, which never threw. So a pgrep/egrep no-match still reads as "no PID / no children",
|
|
40
|
+
exactly as before. No new false escalation path.
|
|
41
|
+
|
|
42
|
+
## 2. Under-block (missing a genuinely stuck session)
|
|
43
|
+
|
|
44
|
+
The scans now interleave with other event-loop work, but each poll still visits every
|
|
45
|
+
session and runs the same probes; nothing is skipped. The 5s per-probe timeout is
|
|
46
|
+
preserved (passed to execFile). A probe that times out returns '' (treated as
|
|
47
|
+
"unknown / no match") exactly as the sync version did. The `running` re-entrancy guard
|
|
48
|
+
still prevents overlapping polls.
|
|
49
|
+
|
|
50
|
+
## 3. Level-of-abstraction fit
|
|
51
|
+
|
|
52
|
+
Correct layer. This is the same OS-process read, moved off the synchronous path so it
|
|
53
|
+
shares the loop cooperatively. No LLM involved in the scan itself.
|
|
54
|
+
|
|
55
|
+
## 4. Ordering / concurrency
|
|
56
|
+
|
|
57
|
+
The methods became async, so within a single `checkSession` the probes now `await`
|
|
58
|
+
(yield) between steps. A session could be killed between two awaited probes — but the
|
|
59
|
+
existing code already tolerates "process gone" (empty output → null/empty list), so a
|
|
60
|
+
mid-scan disappearance degrades to the same no-op as a dead pid. The per-session
|
|
61
|
+
`await` + the inter-session `setImmediate` only ADD yield points; they do not reorder
|
|
62
|
+
the decisions within a session.
|
|
63
|
+
|
|
64
|
+
## 5. Blast radius
|
|
65
|
+
|
|
66
|
+
Single file. The watchdog is opt-in (`monitoring.watchdog.enabled`). The async
|
|
67
|
+
conversion does not change what the watchdog DOES, only that it stops starving the
|
|
68
|
+
event loop while doing it. Tests for compaction/pipeline/mcp-exclusion/rate-limit all
|
|
69
|
+
pass unchanged (their sync `mockReturnValue` stubs work under `await`).
|
|
70
|
+
|
|
71
|
+
## 6. Rollback
|
|
72
|
+
|
|
73
|
+
Pure code revert. No state/format/config change.
|
|
74
|
+
|
|
75
|
+
## 7. Tests
|
|
76
|
+
|
|
77
|
+
`tests/unit/SessionWatchdog-nonblocking.test.ts` (new): the scanning helpers return
|
|
78
|
+
Promises (async), a 0ms timer fires during an in-flight probe (loop stays live), and
|
|
79
|
+
source guards assert no `spawnSync(` call, `execFileAsync` present, the three helpers
|
|
80
|
+
are async, and `poll()` yields via `setImmediate`. Existing watchdog suites updated to
|
|
81
|
+
`await` the now-async methods. 72 watchdog unit tests green; `tsc --noEmit` clean.
|