Claude Code transcripts cannot be imported as Maka Sessions
#3 142 ouverte le 17 août 2026
Métriques du dépôt
- Stars
- (1 étoile)
- Métriques de merge PR
- (Aucune PR mergée en 30 j)
Description
Problem
ExternalSessionAdapter is source-agnostic and Codex uses it today, but there is no adapter for Claude Code. A user with history in ~/.claude/projects/<mangled-cwd>/<uuid>.jsonl has no way to bring it into Maka.
Part of #2499. An earlier attempt is at #2954 (closed); this issue records the problem and what that attempt established, so the next one does not rediscover it.
What the transcript format requires
- One model response is split across several records sharing
message.id, and the records of one response can be separated by atool_result. Folding only consecutive records imports one answer as several rows — 383 occurrences across 33 of 1236 local transcripts, 378 of them continuations carrying onlytool_use. - Tool results arrive as
type: 'user'records. They are the harness replying, not the human, and must not import as user Turns. - Sub-agent transcripts are whole
isSidechainfiles, not interleaved records (verified: 0 mixed across 1231 files), so exclusion is per-file. - The directory name mangles path separators, so only a record's own
cwdcan answer a project-scoped query. isMetarecords are context Claude Code injected on the user's behalf and were never typed by a human.
The load-bearing constraint
This is the part worth knowing before starting.
isTrustworthyRecoveredTerminal (runtime-ledger-repair.ts) discards a reconstructed terminal RuntimeEvent unless a recorded turn_state corroborates it. Every imported Run is then repaired to failed/missing_terminal_event, and the whole conversation renders as errors.
A transcript does not record that a Turn ended — only that another one began. So an importer that wants its history to render has to assert a recorded terminal it never observed. That assertion is wrong in a way that matters: 50 of 376 real root transcripts end with a prompt that has no answer after it (a killed process, a crash, or a Session still running), and each would import as a Turn that finished normally.
The honest shape is two changes that only work together:
- Adapters emit
turn_stateonly for facts the transcript carries — the interrupt notice andisApiErrorMessage— and letderiveTurnRecordslabel everything elseinferred. - The Ledger accepts an inferred terminal on the transcript-materialization path, where
transcriptRunHeaderderives the header from the same messages, so there is no independent record for the transcript to contradict.
Note that (2) needs both isTrustworthyRecoveredTerminal and terminalStatus in runtime-event-backfill.ts to open — the latter also refuses to emit a terminal event without a turn_state, so relaxing only the first leaves the Run at failed.
Since this touches Runtime terminal-state authority, it may be worth landing as its own change before the adapter, rather than inside it.
Suggested scope
- The adapter, registered alongside Codex.
- Fixtures for both the 2.1 and 2.0 transcript shapes — 2.0 predates
ai-title/last-prompt, never emits thinking blocks, and puts text andtool_usein one record where 2.1 splits them. - Reuse
claudeUserMessageTextand the interrupt literal from@maka/core/foreign-sessionrather than re-implementing them; the scanner and the importer deciding "what did the user say" differently would be a real bug.