Skip to content

Instantly share code, notes, and snippets.

@lmmx
Last active September 15, 2026 10:40
Show Gist options
  • Select an option

  • Save lmmx/aebb4da67a503bcafe4b391f4df74702 to your computer and use it in GitHub Desktop.

Select an option

Save lmmx/aebb4da67a503bcafe4b391f4df74702 to your computer and use it in GitHub Desktop.
Session Goal Replay — a Skill for Claude Code
name session-goal-replay
description Replays a prior Claude Code session's turn sequence toward a newly-determined goal by resuming the actual session and live-synthesizing each prompt from the agent's real response. Falls back to a lower-fidelity subagent-based mode when isolation from the live session/environment is required. Use when you have a historical session transcript and want to redirect its procedural pattern toward a different objective than the one it originally pursued.

Session Goal Replay

Mode selection (default: resume)

Both modes operate in the same running environment (same filesystem, same working directory) — mode choice is about session mechanics, not environment isolation.

Resume mode (default). Drives the actual claude CLI via Bash, appending real turns to a real session via --resume <session-id>. Genuine conversational replay: full history and tool state are restored by the CLI itself, not reconstructed by you.

Subagent mode (opt-in). Dispatches via the Task tool instead of --resume. Pick this only when you specifically don't want the retargeted run to create or extend any session JSONL at all — it's a stateless one-shot dispatch with no session identity, not a lighter or more isolated version of resume mode. Fidelity is lower by construction (see caveats below), independent of environment.


Resume mode — procedure

Setup

  1. Locate the transcript. Session history is JSONL under ~/.claude/projects/<project-hash>/<session-id>.jsonl. Use Bash + jq to extract the ordered list of user-authored turns (filter out assistant/tool-result entries) along with the target session_id.
  2. Extract per-turn intent and goal_span, same as before:
    • intent: what this turn was trying to get the agent to do, stated goal-agnostically.
    • goal_span: the substring(s) carrying the original objective — these get replaced, never reused verbatim.
  3. State the new goal explicitly as a standing reference to restate internally at every turn — don't let it rely on surviving implicitly in the resumed context, since the resumed session's own history still contains the original goal framing (that's expected and fine — you're appending new turns pointed at a new goal onto a history that pursued a different one, not erasing the history).

Per-turn loop

For turn index t = 1..N:

  1. Compose the prompt from historical_turn[t].intent + the new goal + (if t>1) the agent's actual last response. Write it fresh — the historical turn tells you what this step was for, not what words to send.

  2. Send it, guarding the resume-dialog trap:

    echo "1" | claude -p --resume "$SESSION_ID" "$PROMPT_TEXT" --output-format json
    

    Use explicit --resume <id>, not bare --continue — the latter only works on a finished session and is ambiguous if there's more than one candidate. Parse session_id and result from the JSON output; confirm the returned session_id matches what you sent — silent mismatches mean you've drifted onto a different session.

  3. Score divergence against historical_turn[t]'s original response:

    • Scope match (same kind of work, new goal) → low
    • Unprompted branch (clarifying question, error, new consideration) → medium
    • Contradiction / dead end / scripted next step no longer makes sense → high
  4. Decide next-turn strategy:

    • Low → synthesize turn t+1 from historical_turn[t+1].intent as planned.
    • Medium → address what the agent just raised first, then fold in historical_turn[t+1].intent as a secondary beat, still pointed at the new goal.
    • High → drop the script from here; coordinate freely toward the new goal, using remaining historical intents only as loose pacing reference. Log this as a script exit at turn t.
  5. State/environment drift caveat. Even with true conversational history restored, tool calls (Read/Grep/Bash) inside the resumed session act on current filesystem/git state, not historical state. If files have changed since the original session, expect divergence from that alone, independent of the goal change — don't attribute all divergence to the retargeting.

  6. Log every turn: index, divergence score, strategy, composed prompt, response summary, script-exit point if any. This is the only audit trail for reconstructing why the retargeted run ended up where it did.

Failure modes

  • Goal leakage. Composing turn t+1 from the agent's actual last response risks re-importing old-goal framing if that response quoted or referenced the original objective. Check composed prompts before sending.
  • Over-steering. If every turn scores "high," the original session isn't structurally similar enough to the new goal to be worth replaying at all — say so rather than forcing continued turns.

Subagent mode — procedure (fallback only)

  1. Reconstruct a single prompt that inlines the prior turns as plain-text context (since there's no real history to inherit), followed by the new-goal-retargeted instruction for the current step.
  2. Dispatch via Task tool. Treat the response as a bounded worker result, not a conversational turn — there is no "next turn" to this subagent in the way resume mode has one; each dispatch is independent unless you manually carry forward a growing context string.
  3. Apply the same divergence scoring as resume mode if you're doing multiple dispatches in sequence, but expect systematically higher divergence rates than resume mode — the subagent has no memory of "being" the agent from the original session, only of the text you handed it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment