DEV Community

QuoLu
QuoLu

Posted on

Why I Gave Up on Automatic Detection for Resuming Sessions in Claude Code

In my previous article, I released Throughline. It is a tool that offloads tool I/O, which usually occupies the majority of the context.

At that time, it was "working." At least, in my own environment.

However, right after publishing the article, I started noticing some strange behavior.

When I opened another window in parallel, the new session would autonomously pick up the memory of the previous session. Every time I restarted VSCode, it would be treated as "continuing from the previous session." Even though I had never performed a /clear command.

The Cause: Unable to detect /clear

Claude Code's hook includes an event called SessionStart, and I was supposed to be able to distinguish between startup (a new start) and clear (after a /clear command) using the source field.

However, with the VSCode extension, even if I perform a /clear, the source is overwritten as startup. This is a known issue tracked in GitHub issue #49937. It works if you use the CLI alone, but it cannot be identified when using the extension.

I am using it via the VSCode extension. In other words, the design premise of "distinguishing between startup and clear" was fundamentally broken.

Attempting to compensate with heuristics

So, I thought about determining it based on time differences. Like, if it's within 10 seconds of the last activity of the previous session, treat it as a clear; if longer, treat it as a startup.

This also broke.

  • When two windows are open in parallel, both appear as "recently active," making both candidates for inheritance.
  • Even when restarting VSCode, the transcript remains, making it look "recent."
  • I tried to trace the process tree, but the process structure differs between the CLI and the extension.

I realized that "there are no conditions to detect it in the first place."

Changing the approach

I failed because I was trying to detect it. If the user declares it, detection becomes unnecessary.

What I created is a slash command called /tl. Users type it only when they want to carry over their memory to the next session. When typed, that session ID is written to a table called handoff_batons. Imagine placing a baton.

When the next session starts, if a baton was placed within the last hour, it inherits the memory of that session. If not, it does nothing and starts as a new session.

This principle guarantees that parallel windows and VSCode restarts "will not misfire unless a baton is placed."

Being explicit might seem troublesome at first glance, but "accidentally inheriting and causing trouble" is far more problematic. Having zero misfires was more valuable.

But, this alone wasn't enough

With the baton in place, the next session could read the conversation logs of the previous session. However, after actually using it, I felt it was "just reading logs."

There is a difference in the visceral experience between an AI that reads past logs and an AI that continues from the point of interruption.

The former asks, "Okay, I've grasped the situation. So, what shall we do now?" The latter proceeds by saying, "Continuing from earlier, we should check X next, right?"

So I added two things here.

In-flight memo. The moment /tl is typed, I have the currently running Claude itself write down "the next move, current hypotheses, unresolved issues, and ongoing TODOs" in Markdown. That is attached to the baton.

Saving thinking. I also save Claude's extended thinking blocks as L3. When injecting into the next session, I place the thinking from the final turn at the very top. What the previous Claude was thinking is passed on to the next Claude.

As a result, the injected text for the next session looks like this:

You are resuming an interrupted task.

[In-flight memo written by the previous Claude]
Next steps: Write tests for X. Hypothesis: I think Y is the cause. Unresolved: Z.

[What the previous Claude was thinking at the end]
I'm curious about the behavior of Z. Maybe...

[Conversation from the last 20 turns]
...
Enter fullscreen mode Exit fullscreen mode

From "reading" to "continuing"

This changed the feel of the experience.

When I perform a /clear and then a /tl to start a new session, the next Claude begins immediately with, "Alright, I'll start writing those tests for X from earlier." It’s not reading; it’s continuing.

Even between humans, when handing off work to someone, it is faster to hand over a memo saying "What to do next. The reason. One thing I'm concerned about" rather than having them read the entire log. It was the same.

I actually wanted it to be automatic

I don’t want to be misunderstood, but I believe the ideal is for it to "work automatically in the background." Having the user explicitly type something is, in truth, a compromise.

In this case, I "escaped to an explicit declaration because I couldn't detect it." If the source issue in the VSCode extension is fixed, I want to return to automatic detection, and I will. Until then, I am just substituting it with an explicit baton.

However, even if it is a compromise, there is almost no practical harm. With automatic detection, you would end up typing /clear anyway; now that is just replaced by /tl. The keystrokes are the same, and I’ve been able to reduce misfires to zero.

It is not that "explicit is better," but rather "I settled for a declaration because I couldn't detect it." That is the honest truth.


Throughline is published on npm as v0.3.2. Node.js 22.5+, zero dependencies, MIT.

Throughline — GitHub

npm install -g throughline
throughline install
Enter fullscreen mode Exit fullscreen mode

If you are interested, please take a look.

Top comments (0)