Skip to content

Redesigning session history to set the stage for parallel subagentsΒ #109

Description

@jpia

Hey all πŸ‘‹ β€” I've got a working prototype of this running locally and I'm still in the testing/refining phase. Before I start opening PRs, I wanted to put the shape of it out here and see if the community thinks I'm on the right path. Happy to course-correct based on your feedback.

Why I'm doing this

  1. I'm using late to learn local models. I've been running it against oMLX to build intuition for how they behave, with the eventual goal of fine-tuning my own. A big part of that learning is replaying conversations β€” including subagents. Today subagent conversations are in-memory only and vanish the moment the subagent finishes. I want every conversation persisted so I can inspect, replay, and eventually use them as training data.

  2. I want parallel research subagents someday. Imagine spawning 3–5 research subagents in parallel, each with its own isolated history, then consolidating. For that to be safe, each subagent needs a stable, collision-free path before any goroutine spawns. The current session store can't guarantee that, so this redesign sets the stage without actually implementing parallel execution yet.

The latent problems

To make subagent history land safely, two existing issues need fixing first:

# Problem Why it matters
1 ID race. Subagent IDs are minted from len(parent.Children()) β€” a read under no lock that races the append in AddChild under another lock. Two concurrent spawns both see count = 0, both mint coder-subagent-0, and the second rename silently overwrites the first. Count-based IDs collide under concurrent spawns. Parallel subagents are unsafe until this is gone.
2 Flat-dir clutter. One loose .json per session in a shared top-level dir. Add subagent history and every subagent drops another indistinguishable file. Grouping, listing, and deleting a session's artifacts all become ad-hoc. Happens even with sequential subagents. No parent-child relationship on disk.

Rather than patch each symptom, one layout change takes care of both.

The high-level design

One folder per session. Subagent histories nest as subfolders inside the parent β€” so late session list (top-level walk) never sees them, late session delete <id> cascades for free via rm -r semantics, and each session folder is owned by exactly one writer.

~/.local/share/late/sessions/
β”œβ”€β”€ session-20260803-192905/        # parent β€” date-based ID unchanged
β”‚   β”œβ”€β”€ history.json
β”‚   β”œβ”€β”€ meta.json
β”‚   └── subagents/
β”‚       β”œβ”€β”€ coder-subagent-0_01J8Q3PT…/   # ULID-suffixed (new)
β”‚       β”‚   └── history.json
β”‚       └── coder-subagent-1_01J8Q3Q0…/
β”‚           └── history.json
└── session-20260803-185121/
    └── …

The ID race is fixed internally (not a breaking change): subagent IDs are minted under the parent mutex with a ULID suffix via NextChildID(agentType). Two concurrent spawns can't construct the same path. Parent session IDs stay date-based; only subagents gain the ULID suffix β€” and since subagent history is brand-new, nothing on disk breaks.

Migration is one-shot and idempotent. Existing flat session-*.json + session-*.meta.json files move into a folder named after the ID (the ID itself is preserved β€” pure layout move). Runs automatically on startup;

Making the legacy code easy to eventually remove

For backwards compat, the old flat-layout read/migrate code can stay for a while (maybe a few weeks/months?). To keep that cleanup trivial, I'm isolating all of it in a dedicated internal/session/oldsesh package and lifting the fallback decision up to cmd/late, so internal/session itself is already folder-only with zero legacy branches.

The practical payoff: when the deprecation window closes, it's rm -r internal/session/oldsesh, drop the two TODO(deprecate) glue lines in cmd/late, and go mod tidy. internal/session doesn't change at all.

What I'd love feedback on

I'm mostly looking for a gut check β€” does any of this feel off, or is there a simpler approach I'm overlooking? If you've wrestled with subagent history or persistence before, I'd love to hear what worked, what didn't, and what I probably got wrong.

Beyond all the design questions β€” I just want to say that late is just really fun to run and work with. I'm grateful it exists. Thank you!!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions