Skip to content

Claude Code transcripts cannot be imported as Maka Sessions #3142

Closed

Description

@Joob1n

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 a tool_result. Folding only consecutive records imports one answer as several rows — 383 occurrences across 33 of 1236 local transcripts, 378 of them continuations carrying only tool_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 isSidechain files, 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 cwd can answer a project-scoped query.
  • isMeta records 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:

  1. Adapters emit turn_state only for facts the transcript carries — the interrupt notice and isApiErrorMessage — and let deriveTurnRecords label everything else inferred.
  2. The Ledger accepts an inferred terminal on the transcript-materialization path, where transcriptRunHeader derives 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 and tool_use in one record where 2.1 splits them.
  • Reuse claudeUserMessageText and the interrupt literal from @maka/core/foreign-session rather than re-implementing them; the scanner and the importer deciding "what did the user say" differently would be a real bug.

Activity

  1. Joob1n commented on Aug 17, 2026

    @Joob1n
    MemberAuthor

    Claiming this one.

    Planning to sequence it as two changes rather than one, since the second only makes sense once the first has landed:

    1. The Ledger side — let isTrustworthyRecoveredTerminal and terminalStatus accept an inferred terminal on the transcript-materialization path. That is where the pressure to forge a recorded status comes from, and it touches Runtime terminal-state authority, so it deserves review on its own rather than buried inside an adapter.
    2. The adapter, emitting turn_state only for the interrupt notice and isApiErrorMessage, with fixtures for both the 2.1 and 2.0 transcript shapes.

    Happy to reorder or split differently if a maintainer would rather see them together.

  2. Joob1n commented on Aug 17, 2026

    @Joob1n
    MemberAuthor

    Adding the provenance, since my claim above only described the plan.

    I filed this issue from the review on the closed #2954, which was my own attempt at this adapter. Everything in the description came out of that round rather than from reading the code fresh — the transcript-shape details (one response split across records sharing message.id, tool results arriving as type: 'user', sidechains being whole files), the measured counts, and above all the Ledger constraint, which took several passes to isolate.

    That last one is the part worth not rediscovering. isTrustworthyRecoveredTerminal is only half of it: terminalStatus in runtime-event-backfill.ts independently refuses to emit a terminal event without a recorded turn_state, so relaxing only the first leaves the Run at failed and looks like the fix did not work. I have a branch where both are open and the imports materialize correctly.

    So I would like to keep this one. If someone else is interested in the area, I would rather split by layer than hand over the whole thing — the Ledger change is genuinely separable and could go to a different pair of hands than the adapter.

  3. Astro-Han commented on Aug 23, 2026

    @Astro-Han
    Contributor

    Closing as completed by #3435. The merged adapter imports Claude Code transcripts through the existing source-agnostic import authority, handles split assistant records, tool-result records, sidechains and cwd identity, and derives terminal state from recorded evidence without weakening the Runtime ledger boundary.\n\nThe separate large-catalog usability consequence is tracked by #3438 and does not leave this adapter issue incomplete.\n\nTriage and wording were prepared with OpenAI Codex and reviewed by the maintainer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions