fix(pi): clear the feed's mine-race timer, so pi -p exits when the mine returns
feedPalace() raced mempalace_mine against a bare setTimeout(reject, feedMineTimeoutMs) and never cleared it. When the mine won (the normal case) the losing 300 000 ms timer stayed referenced. Interactive and RPC mode call process.exit() and never showed it. Print mode (`pi -p`, `--mode json`) returns and waits for an empty event loop, so every `pi -p` on a devbox printed its answer and then idled for the rest of the five minutes. Measured on tor-ms22 (pi 1.1.0, NODE_OPTIONS=--import probe): - before: PONG at 4 s, exit at 315.1 s. The 300 000 ms timer from mempalace.ts:1144 (created +15.1 s) was the ONLY active resource from +50 s on. - after: PONG at 3 s, exit at 45.2 s, the moment the mine's socket closed. - MEMPALACE_FEED=0: 3.8 s. pi 1.0.0 behaved the same as 1.1.0, so this is not a pi regression. The timer is now cleared in a finally once the race settles. It stays referenced while it runs because it is the liveness guard. scripts/test-feed-exit.sh loads the type-stripped SHIPPED extension under a stub pi with a fake palace in a separate process and fires session_shutdown: - A: the mine answers in 1 s with a 20 s deadline. Node must exit within 3 s of the handler resolving. The pre-fix source FAILS here (19 000 ms). - B: the mine never answers, 2 s deadline. The deadline still ends the wait, and the failure is logged. All five scripts/test-*.sh pass. README Debugging section: the remaining expected wait (one catch-up feed per session, ~45 s here) and MEMPALACE_FEED=0 for scripts that want an instant return.
This commit is contained in:
@@ -567,6 +567,33 @@ the short default; the override is honoured and is itself a deadline). Seeing
|
||||
this message on a fixed build means a mine exceeded *five* minutes — treat it as
|
||||
the previous section says.
|
||||
|
||||
### `pi -p` prints its answer, then takes minutes to exit
|
||||
|
||||
Print mode (`pi -p`, `--mode json`) does not call `process.exit()` the way
|
||||
interactive and RPC mode do: it returns and waits for node's event loop to
|
||||
empty. Until 2026-10-13 the feed raced its mine against a bare `setTimeout`
|
||||
that nothing cleared, so when the mine won (the normal case) the losing
|
||||
`MEMPALACE_FEED_MINE_TIMEOUT_MS` timer kept node alive for the rest of its
|
||||
five minutes. Measured on tor-ms22, a `pi -p` printed `PONG` at 4 s and exited
|
||||
at 315 s, which is the timer's creation at 15 s plus 300 s. From 35 s after the
|
||||
mine returned until exit, that timer was the only live resource. Interactive
|
||||
sessions were never affected. The hang predates pi 1.1.0 (pi 1.0.0 behaved the
|
||||
same), and `pi-task`/`fork` children were never affected because they run
|
||||
`--no-extensions`.
|
||||
|
||||
Fixed 2026-10-13: the timer is cleared when the race settles.
|
||||
`scripts/test-feed-exit.sh` pins it. Node must exit within 3 s of
|
||||
`session_shutdown` resolving, both when the mine answers and when it times
|
||||
out. Before the fix the first case held node for the whole deadline.
|
||||
|
||||
**What is still expected.** The first settled turn of every session runs a
|
||||
catch-up feed (`lastFeedAt` starts at 0), and `session_shutdown` waits for it.
|
||||
A `pi -p` therefore still exits only after one full feed: measured at 45 s on
|
||||
tor-ms22, of which `--prepare` took 11 s and the mine 31 s. A script that
|
||||
wants `pi -p` to return at once, and does not need this run's transcript
|
||||
filed now, sets `MEMPALACE_FEED=0`. The next interactive session's feed picks
|
||||
up the transcript, if it qualifies.
|
||||
|
||||
## The `Type.Unsafe` gotcha
|
||||
|
||||
Earlier versions of this extension registered every MCP tool with
|
||||
|
||||
Reference in New Issue
Block a user