Skip to content

engine: opt-in idle reaper for subscription tmux sessions (#141) - #142

Merged
Lexus2016 merged 2 commits into
mainfrom
feat/engine-idle-reaper-141
Nov 12, 2026
Merged

Lexus2016 merged 2 commits into
mainfrom
feat/engine-idle-reaper-141

Conversation

@Lexus2016

Copy link
Copy Markdown
Owner

What

Implements the reaper proposed in #141. A chat on the subscription engine keeps ccs-<id> — and every MCP server that pane spawned, ~1.2 GB on a typical set — alive after its turn ends. Nothing reaped it: CLAUDE_IDLE_TIMEOUT_MS only bounds a single turn, so memory grew with every chat opened since the last restart.

CCS_ENGINE_IDLE_KILL_MS (unset or 0 = today's behaviour, opt-in) kills a pane that has had no turn and no attached viewer for that long. The next message respawns it with --resume <cid> — the path the config-change respawn already uses — so no history is lost.

Never reaped (all deliberate)

  • A running turn — answered from server.js's own registries (activeTasks / activeChatSessions / an in_progress Kanban task), which also covers the autoSkill window before the subprocess exists.
  • An attached viewer (view=engine, or a human in tmux attach) — and while attached it refreshes the clock, so a viewer that reads for an hour and then leaves does not leave an expired timer behind.
  • A pending prompt or a spinner still animating (Terminal Access For Prompt Interactions #20) — a pane a human has to answer is never killed out from under them.
  • A pane of unknown age (created before this feature, or after a restart that lost the tmux env) — its clock is started instead of trusted, so nothing dies on the first tick after an upgrade.

The idle clock is stamped when a turn ends (not when the pane spawns), so a long first turn never counts as idle time. CCS_SESSION carries the exact local session id, so the kill uses the same name the engine uses even when tmuxName() had to sanitize it.

Verification

  • node test/engine-idle-reaper.test.js — 24 checks: the decision table through injected seams, plus a real tmux pane where a control-mode client must count as an attached viewer (this pins that #{client_session} and the comparison line up).
  • Mutation check: deleting the viewer skip makes exactly the two viewer assertions fail (an attached viewer survives — expected [], got ["watched"]), so the suite is not vacuous.
  • Full npm test chain on Node 24: 94 of 95 suites pass; the one failure is test/task-backlog.test.js, which fails identically on a pristine origin/main worktree (21.0 s, rc=1) — pre-existing, unrelated.
  • New suite also green on Node 20 (the other matrix leg).
  • This box has no tmux binary, so the real-tmux block skipped locally; ci.yml installs tmux and runs it there.

Refs #141

Lexus2016 and others added 2 commits November 11, 2026 10:27
The Configuration dialog kept a fixed 740px width and an 86vh cap, so on a
large screen most of the window stayed empty while the form, its tabs and the
search bar were squeezed together. It now uses min(1180px, 96vw) by 88dvh.

Refs #136
A chat on the subscription engine keeps `ccs-<id>` — and every MCP server that
pane spawned, ~1.2 GB on a typical set — alive after its turn ends. Nothing
reaped it: CLAUDE_IDLE_TIMEOUT_MS only bounds a single turn, so memory grew
with every chat opened since the last restart.

CCS_ENGINE_IDLE_KILL_MS (unset or 0 = today's behaviour) kills a pane that has
had no turn and no attached viewer for that long; the next message respawns it
with `--resume <cid>`, the path the config-change respawn already uses, so no
history is lost.

Never reaped: a running turn (answered from server.js's activeTasks /
activeChatSessions / in_progress-task registries, which also cover the autoSkill
window), an attached viewer (and while attached it refreshes the clock, so a
viewer that leaves does not leave an expired timer behind), a pending prompt or
a spinner still animating (#20), and a pane of unknown age — its clock is
started instead of trusted.

test/engine-idle-reaper.test.js pins the decision table through injected seams
and drives a real tmux pane, where a control-mode client must count as a
viewer. Refs #141
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-12T18:12:42.129633Z d08361f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Lexus2016
Lexus2016 merged commit eb26917 into main Nov 12, 2026
3 checks passed
@Lexus2016
Lexus2016 deleted the feat/engine-idle-reaper-141 branch November 12, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant