You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The owner's words: "why do users have to know about sessions? why can't it just be seamless? when a user starts engaging with the agent it resurrects last everything as if the session never died" — and then: "can't it be fully persisted and work via DB completely, instead of relying on ephemeral sessions?"
The answer to the second question is where the first one is actually decided, so this issue is about ownership of conversation state. The user-facing consequences are filed separately (#695, #696, #697, #698) and are all cheap; this is the one with design in it.
The durable half already exists
coding_timeline is append-only in D1, 100 000 characters per entry (lib/coding-timeline.ts:59 — args.content.slice(0, 100_000)), and interleaves chat_user · chat_assistant · terminal · command · brain · outcome · system (:14-21). It is readable per session and per repo, the repo-scoped read tagging each row with its sessionId so the UI can draw boundaries (:31-35).
It works. The console reloads history from it, and #257 already uses the repo-scoped read so a finished run does not leave an empty screen.
It is read only ever FOR THE HUMAN. Nothing feeds it back INTO an engine.
The half that is not ours
Continuity today is a pointer, not a record. coding/headless.ts:266:
readState (:938) reads a JSON file on the runner's disk (statePath: defaultStatePath(this.reposBaseDir), coding/runtime.ts:238) mapping our coding_sessions.id → Claude Code's own session id. The conversation itself lives inside ~/.claude, owned by another program, on one machine.
Not ours to keep. Clearing ~/.claude, or Claude Code changing its store format, ends continuity and no D1 row can help.
Opaque. We cannot read, compact, or measure it. This is whyRESUME_WINDOW_MS (lib/coding-session-continuity.ts:47) is four days: the header argues it from a long weekend, which is honest reasoning about a quantity we cannot observe.
Claude-only.headless.ts:260 returns resumedConversation false "for a raw (non-Claude) engine under every circumstance". Codex and Grok have no conversation at all — for them seamlessness does not degrade, it has never existed.
Proposal: invert the dependency
The platform owns the conversation. An engine's own memory becomes an optimisation, not the source of truth. On wake, seed the engine from coding_timeline instead of relying on --resume.
Slices, ordered so the first cannot regress anything:
Seed-from-timeline as the FALLBACK, used exactly where resolveSessionContinuity returns fresh today for a reason other than forceFresh: past the window, a different engine, a missing last_activity_at. Every one of those starts stone cold now, so any brief is an improvement and there is nothing to regress. The reason strings already distinguish these cases.
Make it primary for raw engines. They gain multi-turn memory they have never had. Again nothing to regress — see headless.ts:256-260.
Only once proven, decide whether Claude keeps preferring --resume (cheaper, higher fidelity when present) or moves over too.
Slice 1 also makes RESUME_WINDOW_MS a policy dial rather than a constraint, which is what the owner actually asked for ("night time or for a few days"). Filing a separate "make the window configurable" issue would be treating the symptom — a configurable window still falls off a cliff, it just falls later.
What this cannot buy — state plainly, do not let it surface later
Reconstruction, not restoration. Claude Code's context is not a message list; it holds tool state, file caches and its own compaction. Seeding gives continuity of understanding, not a byte-identical context. Often better — a curated brief beats a bloated transcript — but the claim must not be "as if it never died".
A process is not data. You cannot persist a running child process, only reconstitute one fast enough that nobody notices. Measured: a wake with --resume answered a memory question in 8 seconds (2026-08-17, instance bd43f4de).
The machine must be on. Unmovable, and it should be reported as "your Mac mini is offline", never in session vocabulary.
Token cost is unchanged. Re-sending context every turn is an LLM property, not a storage choice. Compaction matters either way — the platform already summarises chat after 20 messages and the same trick applies here.
Verified end-to-end, 2026-08-17
Instance bd43f4de (Chess coder 2), repo apps/chess-academy. Previous session csess_21b8d1b9 reaped at 6h idle. POST /v1/instances/:id/coding/sessions returned:
"resumed": true,
"continuity": { "mode": "resume", "resumeFrom": "csess_21b8d1b9-…",
"reason": "the previous conversation on this repo was last touched 11 hours ago" }
The engine then recalled its previous closing question verbatim against coding_timeline seq 9227, and the correct 11-of-12 figure, with no tool use. So the mechanism works today — on one machine, for one engine, within four days, reachable from one surface. This issue is about removing those four qualifiers.
The owner's words: "why do users have to know about sessions? why can't it just be seamless? when a user starts engaging with the agent it resurrects last everything as if the session never died" — and then: "can't it be fully persisted and work via DB completely, instead of relying on ephemeral sessions?"
The answer to the second question is where the first one is actually decided, so this issue is about ownership of conversation state. The user-facing consequences are filed separately (#695, #696, #697, #698) and are all cheap; this is the one with design in it.
The durable half already exists
coding_timelineis append-only in D1, 100 000 characters per entry (lib/coding-timeline.ts:59—args.content.slice(0, 100_000)), and interleaveschat_user·chat_assistant·terminal·command·brain·outcome·system(:14-21). It is readable per session and per repo, the repo-scoped read tagging each row with itssessionIdso the UI can draw boundaries (:31-35).It works. The console reloads history from it, and #257 already uses the repo-scoped read so a finished run does not leave an empty screen.
It is read only ever FOR THE HUMAN. Nothing feeds it back INTO an engine.
The half that is not ours
Continuity today is a pointer, not a record.
coding/headless.ts:266:readState(:938) reads a JSON file on the runner's disk (statePath: defaultStatePath(this.reposBaseDir),coding/runtime.ts:238) mapping ourcoding_sessions.id→ Claude Code's own session id. The conversation itself lives inside~/.claude, owned by another program, on one machine.Four consequences follow, all verified in code:
~/.claude, or Claude Code changing its store format, ends continuity and no D1 row can help.RESUME_WINDOW_MS(lib/coding-session-continuity.ts:47) is four days: the header argues it from a long weekend, which is honest reasoning about a quantity we cannot observe.headless.ts:260returnsresumedConversationfalse "for a raw (non-Claude) engine under every circumstance". Codex and Grok have no conversation at all — for them seamlessness does not degrade, it has never existed.Proposal: invert the dependency
The platform owns the conversation. An engine's own memory becomes an optimisation, not the source of truth. On wake, seed the engine from
coding_timelineinstead of relying on--resume.Slices, ordered so the first cannot regress anything:
resolveSessionContinuityreturnsfreshtoday for a reason other thanforceFresh: past the window, a different engine, a missinglast_activity_at. Every one of those starts stone cold now, so any brief is an improvement and there is nothing to regress. The reason strings already distinguish these cases.headless.ts:256-260.--resume(cheaper, higher fidelity when present) or moves over too.Slice 1 also makes
RESUME_WINDOW_MSa policy dial rather than a constraint, which is what the owner actually asked for ("night time or for a few days"). Filing a separate "make the window configurable" issue would be treating the symptom — a configurable window still falls off a cliff, it just falls later.What this cannot buy — state plainly, do not let it surface later
--resumeanswered a memory question in 8 seconds (2026-08-17, instancebd43f4de).Verified end-to-end, 2026-08-17
Instance
bd43f4de(Chess coder 2), repoapps/chess-academy. Previous sessioncsess_21b8d1b9reaped at 6h idle.POST /v1/instances/:id/coding/sessionsreturned:The engine then recalled its previous closing question verbatim against
coding_timelineseq 9227, and the correct 11-of-12 figure, with no tool use. So the mechanism works today — on one machine, for one engine, within four days, reachable from one surface. This issue is about removing those four qualifiers.