Sessions: let the pane say which conversation it is on - #23
Merged
Conversation
PR #17 taught the re-pin watcher to follow /clear onto the successor conversation, but it deliberately refuses in a folder shared by several live claude sessions — a new transcript there cannot be attributed to a pane by scanning. Folders like a busy project workspace ALWAYS have rivals, so /clear there was never followed and a restart still resumed the pre-/clear thread. Observed live: a pane /cleared on Aug 3, its successor absorbed a day of work, and the Aug 4 restart brought back the abandoned thread and orphaned the rest. Scanning cannot attribute, but the pane itself knows. A SessionStart hook (scripts/am-repin-hook.sh, registered idempotently into settings.json at boot) runs inside the pane's process tree, where $AM_ID names the pane and the payload carries the new conversation's id. It drops a breadcrumb on local disk; the watcher consumes it and re-pins with nothing to guess. The shared-folder refusal now applies only to the scan fallback, which stays for panes without a breadcrumb and for codex/opencode. Breadcrumbs are trusted only after checks, because SessionStart fires more widely than the docs admit (verified on 2.1.220 — print mode fires it too, contradicting the documentation): - $CLAUDE_PID must descend from the pane's tmux root. A nested `claude -p` run inside a pane inherits $AM_ID and would otherwise claim the pane with a throwaway conversation — worse than the bug. The hook's CLAUDE_CODE_ENTRYPOINT=cli filter drops most of these; the pid check is the backstop. - The payload cwd must be the pane's folder, the id must not be pinned by another session, and a no-matcher registration means source:"startup" also replaces the "--session-id not honoured" heuristic with a fact, while source:"resume" provably reports the unchanged id and stays a no-op. Consumed-on-read so a stale crumb can never flip a pin backwards past a later scan re-pin. Losing breadcrumbs at restart is harmless: the pin persists in sessions.json and the relaunch's own resume event writes a fresh crumb — which incidentally verifies the whole chain at every restart. The installer merges into settings.json (never replaces — it also holds permissions and model), is idempotent across boots, and refuses to touch a file it cannot parse. Verified against a live config dir: adding hooks to an already-trusted dir shows no trust prompt. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Only conflict was the runner.js import line in index.js: #24 (libghostty session model) dropped `copySelection`/`paneModes` and added `stopAll`, `ghosttyReady`, `ghosttyError`; this branch adds `installClaudeRepinHook`. Took both sides — main's list plus the hook installer — and kept main's TERM_CTRL sentinel. runner.js merged cleanly: all three capture schedulers are still wired in ensureRunning, and nothing references the two dropped exports any more. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
#17 taught the re-pin watcher to follow
/clearonto the successor conversation, but it deliberately refuses when several live claude sessions share a folder — a new transcript there cannot be attributed to a pane by scanning. Busy shared workspaces always have rivals, so/clearthere is never followed: observed live on Aug 4, a restart resumed a pane's pre-/clearthread and orphaned a day of work in the successor.Approach
Scanning cannot attribute — but the pane itself knows. A
SessionStarthook (scripts/am-repin-hook.sh, registered idempotently intosettings.jsonat server boot) runs inside the pane's process tree, where$AM_IDnames the pane and the payload carries the new conversation id. It drops a breadcrumb on local disk; the watcher consumes it and re-pins with nothing to guess. The shared-folder refusal now guards only the scan fallback (kept for hook-less panes and for codex/opencode).Why breadcrumbs need verifying
SessionStartfires more widely than documented (verified on Claude Code 2.1.220 — print mode fires it too, contrary to the docs):$CLAUDE_PIDmust descend from the pane's tmux root. A nestedclaude -pinside a pane inherits$AM_IDand would otherwise claim the pane with a throwaway conversation — worse than the bug. The hook'sCLAUDE_CODE_ENTRYPOINT=clifilter drops most; the pid check is the backstop.cwdmust equal the pane's folder; the id must not be pinned by another session.startupreplaces the "--session-idnot honoured" heuristic with a fact;resumeprovably reports the unchanged id (no-op) — and re-validates the chain at every restart.sessions.json.The installer merges (never replaces)
settings.json, is idempotent, and refuses to touch a file it cannot parse. Verified on a live established config dir: adding hooks shows no trust prompt.Tests
server/test/repin.test.mjsextended: 21 checks covering the verdict (attribution, nested-run rejection, claimed/no-op/garbage) and the installer (merge, idempotency, corrupt-file refusal). Full live validation notes (five experiments incl. two concurrent panes in one folder attributing correctly): produced during review with a second agent, available on request.🤖 Generated with Claude Code