fix: capture OpenCode sessions that predate the session-tracker (#402) - #403
Merged
Conversation
save-session refused to capture an OpenCode session started before the session-tracker plugin was installed, because captureOpenCodeSession required a recorded <id>.json state entry (via findOpenCodeSessionID, which threw "No state recorded for session ..."). But `opencode export` returns the transcript whether or not the SessionStart hook ever fired, so the tracker state was never a real prerequisite for the capture. Split findOpenCodeSessionID into two intents: - openCodeSessionIDFromEnv — reads $OPENCODE_SESSION_ID, the only hard prerequisite for a capture; captureOpenCodeSession now uses this and always shells out to export. - isOpenCodeSessionTracked — a never-throwing boolean, consulted only as tie-break evidence in captureSession when both host session-id variables are set (unchanged behaviour: an untracked OpenCode id still yields to Claude Code). Bundle artifacts are intentionally not rebuilt here — the shipped bundle is refreshed at release time (scripts/release.sh), and the CI freshness guard only runs on version-bump PRs.
An untracked OpenCode session no longer errors on tracker state; it falls through to `opencode export`. Isolate the OpenCode dispatch by unsetting CLAUDE_CODE_SESSION_ID and accept the missing-CLI error as proof the env var was read.
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
enchiridion save-sessionrefused to capture an OpenCode session that was started before the session-tracker plugin was installed, exiting non-zero with:The transcript then had to be reconstructed by hand. Fixes #402.
Root cause
captureOpenCodeSessionrequired a recorded<id>.jsonstate entry (viafindOpenCodeSessionID, which threw). Butopencode export <id>returns the transcript whether or not theSessionStarthook ever fired — so tracker state was never a real prerequisite for the capture, only tie-break evidence.Fix (issue option 2)
Split
findOpenCodeSessionIDinto two intents:openCodeSessionIDFromEnv— reads$OPENCODE_SESSION_ID, the only hard prerequisite for a capture.captureOpenCodeSessionnow uses this and always shells out toopencode export, tracked or not.isOpenCodeSessionTracked— a never-throwing boolean, consulted only as tie-break evidence incaptureSessionwhen both host session-id variables are set. Behaviour there is unchanged: an untracked OpenCode id still yields to Claude Code (an untracked id can be a leaked variable from an unrelated project).Tests
captureOpenCodeSessioncaptures a session that predates the tracker (no.opencode/state at all) via the injected export seam.openCodeSessionIDFromEnv(throws unset / returns id) andisOpenCodeSessionTracked(all four false branches + the true branch).$OPENCODE_SESSION_ID-set dispatch test now asserts the export path succeeds without tracker state, rather than failing.npm run typecheck,npm run lintclean. Full suite passes except pre-existing Windows-only failures (subprocess.bin/tsxspawn; tests assuming no.claude/ancestor) — CI runs on Linux and is unaffected.Bundle artifacts are intentionally not rebuilt here — the shipped bundle is refreshed at release time (
scripts/release.sh); the CI freshness guard only runs on version-bump PRs.