Skip to content

bug(pi): gentle-engram session_shutdown never closes the store-side session β€” Pi rows leak ended_at NULL (1.1% closed)Β #1149

Description

@ElCaaarnal

πŸ“ Bug Description

The Pi adapter (plugin/pi/index.ts, published as gentle-engram) creates a sessions row on session_start but never closes it. Its session_shutdown handler clears three in-memory maps and returns; it never calls POST /sessions/{id}/end.

On main today (f60fd0b6):

// plugin/pi/index.ts:1331
pi.on("session_shutdown", async (_event: unknown, ctx: SessionContext) => {
  const sessionId = observeRuntimeSessionID(ctx);
  if (!sessionId) return;
  toolCounts.delete(sessionId);
  forgetKnownSession(sessionId);
  forgetSelfHealContext(sessionId);
});

Grepping /end across that file returns exactly one hit β€” line 1180, inside the mem_session_end tool handler. So closure is reachable only when the model voluntarily calls the tool; there is no lifecycle path.

This is the same defect class as #1083 (OpenCode), #1118 (Claude Code) and #48, and it produces the failure in #1101. Pi is the client that every one of those audits missed:

Issue Clients in scope Pi included?
#48 Claude Code, OpenCode, Gemini CLI, Codex No β€” absent from the table
#1118 plugin/claude-code vs plugin/codex No
#1131 (open) OpenCode No β€” Agent / Client: OpenCode

This repeats the pattern #1031 called out: a defect fixed for one client while another keeps reproducing it.

Pi's own extension documentation states the contract being violated: "Register an idempotent session_shutdown handler to close any session-scoped resources you start." The adapter registers the handler, so it is aware of the contract, and then closes nothing store-side.

Side note: this form's Agent / Client dropdown has no Pi option, which is arguably the same blind spot in reporting form.

πŸ”„ Steps to Reproduce

  1. Use Pi with the gentle-engram package (npm:gentle-engram@0.1.12, current latest).
  2. Start a Pi session in a project directory, then exit it cleanly so session_shutdown fires.
  3. Repeat at least twice in the same directory.
  4. Query the store:
    SELECT id, started_at, ended_at FROM sessions
    WHERE directory = '<project-dir>' AND ended_at IS NULL;
  5. Observe every Pi row still has ended_at IS NULL.
  6. Call mem_save without an explicit session_id from that directory.

Pi session ids are UUIDv7 (01a0…), which makes them easy to separate from Claude Code's UUIDv4 rows when auditing.

βœ… Expected Behavior

The session_shutdown handler should close the store-side session, mirroring what plugin/codex/scripts/session-end.sh does for Codex β€” i.e. POST /sessions/{id}/end for the shutting-down session id, idempotent as Pi's docs require, before the in-memory cleanup.

❌ Actual Behavior

Pi rows accumulate with ended_at = NULL indefinitely. Measured closure rate by client on one store, for sessions started after 2026-08-01, bucketed by session-id shape:

Client (id shape) Sessions Closed
Claude Code (UUIDv4) 196 61.2%
Manually named 195 45.1%
Pi / OpenCode (UUIDv7 01a0*) 176 1.1%
engram-generated (ses_*) 69 0%

Store-wide this reached 1996 of 2380 rows open (84%), and a single project directory accumulated 16 open rows, which made mem_save fail closed until the rows were closed manually via POST /sessions/{id}/end.

Operating System

macOS

Engram Version

2.0.0-rc.9

Agent / Client

Other

πŸ“‹ Relevant Logs

multiple active runtime sessions match the current project and directory; provide session_id or end other active matching sessions before retrying

πŸ’‘ Additional Context

Verified the defect is in the published package, not in one local install:

  • Installed gentle-engram is 0.1.12, which is npm latest.
  • The installed index.ts is byte-identical to a pristine npm pack gentle-engram@0.1.12 tarball β€” SHA256 96dc8896c4dbdc5433eacb8c4a519df63fcab14f21d6398b96bcb87f015b006a for both.
  • The same handler is present in the pristine tarball, in plugin/pi/index.ts at ebf1228, and on main at f60fd0b6. It is not fixed upstream.
  • gentle-pi (main) contains no reference to closing engram sessions, so nothing else compensates.

Two related observations, both consistent with #1131's expected-behavior points 1 and 2 rather than new claims:

  • ActiveRuntimeSessions applies no recency bound and no live-process check, so a single unclosed row is permanent. That is documented intent ("rejects ambiguity rather than selecting by recency"), but it means any client that leaks one row eventually hard-fails mem_save for that directory.
  • engram doctor does not flag orphaned ended_at IS NULL rows whose owning process is gone. During this investigation it reported the affected project as ok on every session check while mem_save was failing.

One further data point for whoever picks this up: two rows could not be closed through the HTTP route at all and still hold real observations (46 and 3). One has an empty id, the other has id manual-save-/ with project /; both break the /sessions/{id}/end path. These look like the residue of #630.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions