Skip to content

feat(claude-bridge): a resumed session grows forever and nothing compacts it — pet will 413 permanently (#666 follow-up) #667

Description

@vibechoom

Deferred scope from PR #666, filed immediately rather than left in a PR body. The bridge as shipped has a failure mode that is certain, not conditional — it only takes long enough.

The mechanism

The bridge derives a session title from a stable hash of the transcript prefix, then resumes that session on every subsequent turn. That is exactly what makes prompt caching work, and it is why the persisted-session shape was chosen over Attach::OneOff (see #584's thread — a fresh session per turn re-pays the whole prefix every time).

But it means a plugin with a stable persona and no history rewriting — pet, precisely — maps to one session that grows forever and that nothing ever compacts. Each quip appends a turn. Nothing prunes.

So the bridge works, gets cheaper per turn thanks to cache reads, and then one day starts returning PromptTooLong permanently, for that plugin, with no recovery short of deleting on-disk session state by hand. The order-of-magnitude estimate in the PR is ~10³ quips; the exact number does not matter, the shape does.

This is not a regression and nothing is broken today — but it is the difference between "ships and works" and "ships and keeps working."

Why it was deferred rather than built

The scope agreed on #584 was Claude::run + Attach + Config + Error + Sink from hive-claude, deliberately excluding SessionStore / InfiniteSession / CompactionPolicy as surplus for a stateless bridge. That scoping was made before @kaesaecracker's caching argument turned the session layer from surplus into load-bearing, and I corrected the scope on-thread at the time — but only as far as "use a persisted session", not as far as "and therefore inherit the compaction problem too."

Self-healing compaction also wants its own live verification, and bundling it into the initial PR would have meant shipping two unverified mechanisms at once.

Options

  1. Archive-and-recreate on PromptTooLong. hive-claude already classifies that as a typed Error variant, so the bridge can catch it, retire the session under its current title, create a fresh one, and replay only the system/persona prefix. Smallest change; loses conversational continuity at the cut point, which for pet's poke-quips is close to free and for caw's briefing is irrelevant (it is stateless per day anyway).
  2. Adopt hive-claude's InfiniteSession / CompactionPolicy. More faithful, keeps continuity across the boundary, and is code that already exists and is presumably exercised in hyperhive. Larger dependency surface on a crate we currently use about a third of, and its semantics need reading before adopting — see the note in hytte-claude-bridge: local OpenAI-compatible shim so the LLM plugins can ride a Claude subscription (#575 follow-up) #584 about not shaping a shared crate around a use case that is not real.
  3. Cap turns per session by construction — fold a generation counter into the title so a session rotates every N turns. Crude, but entirely local to the bridge and needs no new hive-claude surface. Rotation is then predictable rather than triggered by an error, which makes it much easier to live-verify.

I lean 1, with 3 as a cheap belt-and-braces if rotation turns out to be hard to observe: option 1's trigger is the exact error the crate already types, so there is no new classification to get wrong, and the recovery is visible in the journal.

Whichever is picked, the recovery must log — a silent session rotation would make the cache-hit rate drop mysteriously with nothing to correlate it against, which is the same observability trap #609/#634 argued about on the networkd/wifi side.

Also worth a test

Nothing currently pins that a session ever rotates. Whatever lands here should come with a test that drives the title derivation past the rotation condition, so the mechanism cannot silently stop working.

Severity

Low now, certain later. No user impact until a plugin accumulates enough turns, and the bridge is not yet deployed. Worth doing before it runs unattended for a month, not before it merges.

Refs #666, #584, #575.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions