Skip to content

Pin Claude, Codex, and OpenCode conversations from lifecycle events - #41

Merged
lvwerra merged 2 commits into
mainfrom
codex/deterministic-conversation-pins
Aug 7, 2026
Merged

Pin Claude, Codex, and OpenCode conversations from lifecycle events#41
lvwerra merged 2 commits into
mainfrom
codex/deterministic-conversation-pins

Conversation

@lvwerra

@lvwerra lvwerra commented Aug 6, 2026

Copy link
Copy Markdown
Member

Summary

Replace newest-file/newest-row guessing with exact lifecycle events for the three harnesses that expose an integration point:

  • Claude: keep the existing SessionStart hook, but bind every breadcrumb to a fresh per-launch nonce and the top-level pane process.
  • Codex: install a root-owned managed SessionStart hook for startup, resume, and clear; persist the exact session_id and validated transcript_path it reports.
  • OpenCode: install a global plugin that reports root session.created events and the exact session selected on the next chat.message; validate that id, cwd, process, and root status against SQLite before pinning it.

Every PTY launch now gets an AM_RUN_ID and an AM_PANE_PID. The server accepts a breadcrumb only when its pane id, launch nonce, harness, cwd, id shape, process identity, and ownership all agree. Delayed events from an old process and lifecycle events from nested agents therefore cannot re-pin the top-level pane, and same-folder panes do not pick each other conversations.

The existing transcript/rollout/database discovery remains as a fallback when an adapter is unavailable. Gemini, Hermes, OpenClaw, shell, files, and trace behavior is unchanged.

Clear/new-session behavior

  • Claude /clear emits SessionStart(source=clear).
  • Codex /clear emits SessionStart(source=clear).
  • OpenCode /new (/clear) emits a root session.created event.

The exact breadcrumb is polled from local disk every 250 ms and once more on PTY exit, while expensive storage discovery keeps its existing sparse cadence. Transient Codex rollout/OpenCode database visibility failures retain the exact event briefly and retry rather than discarding it.

Reliability and safety details

  • Nested Claude, Codex, and OpenCode processes are filtered before they can overwrite the pane breadcrumb and independently rejected by the server process-tree check.
  • OpenCode generic event callbacks are fire-and-forget upstream, so the tiny local breadcrumb write is synchronous; /clear followed immediately by quit cannot lose it at the first await.
  • Codex uses /etc/codex/requirements.toml and a root-owned managed hook, so no user /hooks trust prompt or global hook-trust bypass is required.
  • Codex transcript paths must stay under $CODEX_HOME/sessions, accept lexical or canonical paths for symlinked storage, and encode the reported UUID. The nullable transcript_path case resolves only an exact-ID rollout.
  • OpenCode child/subagent sessions and cwd mismatches are rejected from the database row.
  • Existing user Claude hooks/settings and OpenCode plugins/config are merged or left untouched. The app-owned OpenCode plugin is installed without a FUSE rename.

Tests

  • npm test --prefix server
  • JS and shell syntax checks for the runner and all adapters
  • TOML parse and managed-hook assertions
  • git diff --check

The full server suite passes, including 54 conversation re-pin checks, 32 OpenCode resume/plugin checks, and the Ghostty terminal migration/scrollback/resize integration suites.

Relationship to existing work

This is the deterministic follow-up to #36 and is based on current main, including the watcher lifecycle fixes merged from #35.

@lvwerra
lvwerra marked this pull request as ready for review August 6, 2026 16:58
@lvwerra
lvwerra merged commit dce99f0 into main Aug 7, 2026
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